ImageExpand.coffee
This commit is contained in:
parent
bc67db84af
commit
4089d56720
@ -10886,7 +10886,8 @@
|
||||
|
||||
ImageExpand = {
|
||||
init: function() {
|
||||
if (g.VIEW === 'catalog' || !Conf['Image Expansion']) {
|
||||
var _ref;
|
||||
if (!(((_ref = g.VIEW) === 'index' || _ref === 'thread') && Conf['Image Expansion'])) {
|
||||
return;
|
||||
}
|
||||
this.EAI = $.el('a', {
|
||||
@ -10896,6 +10897,13 @@
|
||||
href: 'javascript:;'
|
||||
});
|
||||
$.on(this.EAI, 'click', ImageExpand.cb.toggleAll);
|
||||
$.on(d, 'scroll visibilitychange', this.cb.playVideos);
|
||||
this.videoControls = $.el('span', {
|
||||
className: 'video-controls'
|
||||
});
|
||||
$.extend(this.videoControls, {
|
||||
innerHTML: " <a href=\"javascript:;\" title=\"You can also contract the video by dragging it to the left.\">contract</a>"
|
||||
});
|
||||
Header.addShortcut(this.EAI, true);
|
||||
return Post.callbacks.push({
|
||||
name: 'Image Expansion',
|
||||
@ -10903,37 +10911,32 @@
|
||||
});
|
||||
},
|
||||
node: function() {
|
||||
var clone, thumb, _ref, _ref1;
|
||||
if (!(((_ref = this.file) != null ? _ref.isImage : void 0) || ((_ref1 = this.file) != null ? _ref1.isVideo : void 0))) {
|
||||
var _ref;
|
||||
if (!(this.file && (this.file.isImage || this.file.isVideo))) {
|
||||
return;
|
||||
}
|
||||
thumb = this.file.thumb;
|
||||
$.on(thumb.parentNode, 'click', ImageExpand.cb.toggle);
|
||||
$.on(this.file.thumb.parentNode, 'click', ImageExpand.cb.toggle);
|
||||
if (this.isClone) {
|
||||
if ($.hasClass(thumb, 'expanding')) {
|
||||
if (this.file.isExpanding) {
|
||||
ImageExpand.contract(this);
|
||||
ImageExpand.expand(this);
|
||||
return ImageExpand.expand(this);
|
||||
} else if (this.file.isExpanded && this.file.isVideo) {
|
||||
clone = this;
|
||||
ImageExpand.setupVideoControls(clone);
|
||||
if (!clone.origin.file.fullImage.paused) {
|
||||
$.queueTask(function() {
|
||||
return Video.start(clone.file.fullImage);
|
||||
});
|
||||
}
|
||||
ImageExpand.setupVideoCB(this);
|
||||
return ImageExpand.setupVideo(this, !((_ref = this.origin.file.fullImage) != null ? _ref.paused : void 0) || this.origin.file.wasPlaying, this.file.fullImage.controls);
|
||||
}
|
||||
} else if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler) && (Conf['Expand videos'] || !this.file.isVideo)) {
|
||||
return ImageExpand.expand(this, null, true);
|
||||
} else if (ImageExpand.on && !this.isHidden && !this.isFetchedQuote && (Conf['Expand spoilers'] || !this.file.isSpoiler) && (Conf['Expand videos'] || !this.file.isVideo)) {
|
||||
return ImageExpand.expand(this);
|
||||
}
|
||||
},
|
||||
cb: {
|
||||
toggle: function(e) {
|
||||
var post, _ref;
|
||||
var file, post;
|
||||
if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
post = Get.postFromNode(this);
|
||||
if (post.file.isExpanded && ((_ref = post.file.fullImage) != null ? _ref.controls : void 0)) {
|
||||
file = post.file;
|
||||
if (file.isExpanded && file.isVideo && file.fullImage.controls) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
@ -10956,9 +10959,7 @@
|
||||
if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) {
|
||||
ImageExpand.EAI.className = 'contract-all-shortcut a-icon';
|
||||
ImageExpand.EAI.title = 'Contract All Images';
|
||||
func = function(post) {
|
||||
return ImageExpand.expand(post, null, true);
|
||||
};
|
||||
func = ImageExpand.expand;
|
||||
} else {
|
||||
ImageExpand.EAI.className = 'expand-all-shortcut a-icon';
|
||||
ImageExpand.EAI.title = 'Expand All Images';
|
||||
@ -10973,122 +10974,207 @@
|
||||
}
|
||||
});
|
||||
},
|
||||
playVideos: function(e) {
|
||||
return g.posts.forEach(function(post) {
|
||||
var file, video, visible, _i, _len, _ref;
|
||||
_ref = [post].concat(__slice.call(post.clones));
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
post = _ref[_i];
|
||||
file = post.file;
|
||||
if (!(file && file.isVideo && file.isExpanded)) {
|
||||
continue;
|
||||
}
|
||||
video = file.fullImage;
|
||||
visible = Header.isNodeVisible(video);
|
||||
if (visible && file.wasPlaying) {
|
||||
delete file.wasPlaying;
|
||||
video.play();
|
||||
} else if (!visible && !video.paused) {
|
||||
file.wasPlaying = true;
|
||||
video.pause();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
setFitness: function() {
|
||||
return (this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-'));
|
||||
return $[this.checked ? 'addClass' : 'rmClass'](doc, this.name.toLowerCase().replace(/\s+/g, '-'));
|
||||
}
|
||||
},
|
||||
toggle: function(post) {
|
||||
var headRect, left, root, top, x, y, _ref;
|
||||
if (!(post.file.isExpanded || $.hasClass(post.file.thumb, 'expanding'))) {
|
||||
var next;
|
||||
if (!(post.file.isExpanding || post.file.isExpanded)) {
|
||||
post.file.scrollIntoView = Conf['Scroll into view'];
|
||||
ImageExpand.expand(post);
|
||||
return;
|
||||
}
|
||||
root = post.nodes.root;
|
||||
_ref = (Conf['Advance on contract'] ? (function() {
|
||||
var next;
|
||||
next = root;
|
||||
ImageExpand.contract(post);
|
||||
if (Conf['Advance on contract']) {
|
||||
next = post.nodes.root;
|
||||
while (next = $.x("following::div[contains(@class,'postContainer')][1]", next)) {
|
||||
if ($('.stub', next) || next.offsetHeight === 0) {
|
||||
continue;
|
||||
if (!($('.stub', next) || next.offsetHeight === 0)) {
|
||||
break;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
return root;
|
||||
})() : root).getBoundingClientRect(), top = _ref.top, left = _ref.left;
|
||||
if (top < 0) {
|
||||
y = top;
|
||||
if (Conf['Fixed Header'] && !Conf['Bottom Header']) {
|
||||
headRect = Header.bar.getBoundingClientRect();
|
||||
y -= headRect.top + headRect.height;
|
||||
if (next) {
|
||||
return Header.scrollTo(next);
|
||||
}
|
||||
}
|
||||
if (left < 0) {
|
||||
x = -window.scrollX;
|
||||
}
|
||||
if (x || y) {
|
||||
window.scrollBy(x, y);
|
||||
}
|
||||
return ImageExpand.contract(post);
|
||||
},
|
||||
contract: function(post) {
|
||||
var cb, eventName, thumb, video, _ref;
|
||||
thumb = post.file.thumb;
|
||||
if (post.file.isVideo && (video = post.file.fullImage)) {
|
||||
video.pause();
|
||||
TrashQueue.add(video, post);
|
||||
thumb.parentNode.href = video.src;
|
||||
thumb.parentNode.target = '_blank';
|
||||
_ref = ImageExpand.videoCB;
|
||||
for (eventName in _ref) {
|
||||
cb = _ref[eventName];
|
||||
$.off(video, eventName, cb);
|
||||
}
|
||||
$.rm(post.file.videoControls);
|
||||
delete post.file.videoControls;
|
||||
var bottom, cb, el, eventName, file, oldHeight, scrollY, top, x, _i, _len, _ref, _ref1;
|
||||
file = post.file;
|
||||
if (el = file.fullImage) {
|
||||
top = Header.getTopOf(el);
|
||||
bottom = top + el.getBoundingClientRect().height;
|
||||
oldHeight = d.body.clientHeight;
|
||||
scrollY = window.scrollY;
|
||||
}
|
||||
$.rmClass(post.nodes.root, 'expanded-image');
|
||||
$.rmClass(post.file.thumb, 'expanding');
|
||||
delete post.file.isExpanding;
|
||||
delete post.file.isExpanded;
|
||||
if (post.file.isVideo && post.file.fullImage) {
|
||||
return post.file.fullImage.pause();
|
||||
$.rmClass(file.thumb, 'expanding');
|
||||
if (file.videoControls) {
|
||||
$.rm(file.videoControls);
|
||||
}
|
||||
file.thumb.parentNode.href = file.URL;
|
||||
file.thumb.parentNode.target = '_blank';
|
||||
_ref = ['isExpanding', 'isExpanded', 'videoControls', 'wasPlaying', 'scrollIntoView'];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
x = _ref[_i];
|
||||
delete file[x];
|
||||
}
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
if (doc.contains(el)) {
|
||||
if (bottom <= 0) {
|
||||
window.scroll(0, scrollY + d.body.clientHeight - oldHeight);
|
||||
} else {
|
||||
Header.scrollToIfNeeded(post.nodes.root);
|
||||
}
|
||||
if (window.scrollX > 0) {
|
||||
window.scroll(0, window.scrollY);
|
||||
}
|
||||
}
|
||||
$.off(el, 'error', ImageExpand.error);
|
||||
ImageCommon.pushCache(el);
|
||||
if (file.isVideo) {
|
||||
el.pause();
|
||||
_ref1 = ImageExpand.videoCB;
|
||||
for (eventName in _ref1) {
|
||||
cb = _ref1[eventName];
|
||||
$.off(el, eventName, cb);
|
||||
}
|
||||
}
|
||||
if (Conf['Restart when Opened']) {
|
||||
ImageCommon.rewind(file.thumb);
|
||||
}
|
||||
delete file.fullImage;
|
||||
return $.queueTask(function() {
|
||||
if (file.isExpanding || file.isExpanded) {
|
||||
return;
|
||||
}
|
||||
$.rmClass(el, 'full-image');
|
||||
if (el.id) {
|
||||
return;
|
||||
}
|
||||
return $.rm(el);
|
||||
});
|
||||
},
|
||||
expand: function(post, src, disableAutoplay) {
|
||||
var el, isVideo, thumb, _ref;
|
||||
_ref = post.file, thumb = _ref.thumb, isVideo = _ref.isVideo;
|
||||
if (post.isHidden || post.file.isExpanded || $.hasClass(thumb, 'expanding')) {
|
||||
expand: function(post, src) {
|
||||
var el, file, isVideo, thumb, _ref;
|
||||
file = post.file;
|
||||
thumb = file.thumb, isVideo = file.isVideo;
|
||||
if (post.isHidden || file.isExpanding || file.isExpanded) {
|
||||
return;
|
||||
}
|
||||
$.addClass(thumb, 'expanding');
|
||||
if (el = post.file.fullImage) {
|
||||
el.className = 'full-image';
|
||||
el.style.cssText = '';
|
||||
TrashQueue.remove(el);
|
||||
} else {
|
||||
el = post.file.fullImage = $.el((isVideo ? 'video' : 'img'), {
|
||||
className: 'full-image'
|
||||
});
|
||||
file.isExpanding = true;
|
||||
if (file.fullImage) {
|
||||
el = file.fullImage;
|
||||
} else if (((_ref = ImageCommon.cache) != null ? _ref.dataset.fullID : void 0) === post.fullID) {
|
||||
el = file.fullImage = ImageCommon.popCache();
|
||||
$.on(el, 'error', ImageExpand.error);
|
||||
el.src = src || post.file.URL;
|
||||
if (isVideo) {
|
||||
el.loop = true;
|
||||
if (Conf['Restart when Opened'] && el.id !== 'ihover') {
|
||||
ImageCommon.rewind(el);
|
||||
}
|
||||
el.removeAttribute('id');
|
||||
} else {
|
||||
el = file.fullImage = $.el((isVideo ? 'video' : 'img'));
|
||||
el.dataset.fullID = post.fullID;
|
||||
$.on(el, 'error', ImageExpand.error);
|
||||
el.src = src || file.URL;
|
||||
}
|
||||
if (el !== thumb.nextSibling) {
|
||||
$.after(thumb, el);
|
||||
el.className = 'full-image';
|
||||
$.after(thumb, el);
|
||||
if (isVideo) {
|
||||
if (Conf['Show Controls'] && !file.videoControls) {
|
||||
file.videoControls = ImageExpand.videoControls.cloneNode(true);
|
||||
$.add(file.text, file.videoControls);
|
||||
}
|
||||
thumb.parentNode.removeAttribute('href');
|
||||
thumb.parentNode.removeAttribute('target');
|
||||
el.loop = true;
|
||||
ImageExpand.setupVideoCB(post);
|
||||
}
|
||||
if (!isVideo) {
|
||||
return $.asap((function() {
|
||||
return el.naturalHeight;
|
||||
}), function() {
|
||||
return ImageExpand.completeExpand(post);
|
||||
});
|
||||
} else if (el.readyState >= el.HAVE_METADATA) {
|
||||
return ImageExpand.completeExpand(post);
|
||||
} else {
|
||||
return $.on(el, 'loadedmetadata', function() {
|
||||
return ImageExpand.completeExpand(post);
|
||||
});
|
||||
}
|
||||
return $.asap((function() {
|
||||
return el.videoHeight || el.naturalHeight;
|
||||
}), function() {
|
||||
return ImageExpand.completeExpand(post, disableAutoplay);
|
||||
});
|
||||
},
|
||||
completeExpand: function(post, disableAutoplay) {
|
||||
var bottom;
|
||||
if (!$.hasClass(post.file.thumb, 'expanding')) {
|
||||
completeExpand: function(post) {
|
||||
var bottom, file, imageBottom, oldHeight, scrollY;
|
||||
file = post.file;
|
||||
if (!file.isExpanding) {
|
||||
return;
|
||||
}
|
||||
if (!post.nodes.root.parentNode) {
|
||||
ImageExpand.completeExpand2(post);
|
||||
return;
|
||||
}
|
||||
bottom = post.nodes.root.getBoundingClientRect().bottom;
|
||||
return $.queueTask(function() {
|
||||
ImageExpand.completeExpand2(post, disableAutoplay);
|
||||
if (!(bottom <= 0)) {
|
||||
return;
|
||||
}
|
||||
return window.scrollBy(0, post.nodes.root.getBoundingClientRect().bottom - bottom);
|
||||
});
|
||||
},
|
||||
completeExpand2: function(post, disableAutoplay) {
|
||||
bottom = Header.getTopOf(file.thumb) + file.thumb.getBoundingClientRect().height;
|
||||
oldHeight = d.body.clientHeight;
|
||||
scrollY = window.scrollY;
|
||||
$.addClass(post.nodes.root, 'expanded-image');
|
||||
$.rmClass(post.file.thumb, 'expanding');
|
||||
post.file.isExpanded = true;
|
||||
if (post.file.isVideo) {
|
||||
ImageExpand.setupVideoControls(post);
|
||||
return Video.configure(post.file.fullImage, disableAutoplay);
|
||||
$.rmClass(file.thumb, 'expanding');
|
||||
file.isExpanded = true;
|
||||
delete file.isExpanding;
|
||||
if (doc.contains(post.nodes.root) && bottom <= 0) {
|
||||
window.scroll(window.scrollX, scrollY + d.body.clientHeight - oldHeight);
|
||||
}
|
||||
if (file.scrollIntoView) {
|
||||
delete file.scrollIntoView;
|
||||
imageBottom = Header.getBottomOf(file.fullImage) - 25;
|
||||
if (imageBottom < 0) {
|
||||
window.scrollBy(0, Math.min(-imageBottom, Header.getTopOf(file.fullImage)));
|
||||
}
|
||||
}
|
||||
if (file.isVideo) {
|
||||
return ImageExpand.setupVideo(post, Conf['Autoplay'], Conf['Show Controls']);
|
||||
}
|
||||
},
|
||||
setupVideo: function(post, playing, controls) {
|
||||
var fullImage;
|
||||
fullImage = post.file.fullImage;
|
||||
if (!playing) {
|
||||
fullImage.controls = controls;
|
||||
return;
|
||||
}
|
||||
fullImage.controls = false;
|
||||
$.asap((function() {
|
||||
return doc.contains(fullImage);
|
||||
}), function() {
|
||||
if (!d.hidden && Header.isNodeVisible(fullImage)) {
|
||||
return fullImage.play();
|
||||
} else {
|
||||
return post.file.wasPlaying = true;
|
||||
}
|
||||
});
|
||||
if (controls) {
|
||||
return ImageCommon.addControls(fullImage);
|
||||
}
|
||||
},
|
||||
videoCB: (function() {
|
||||
@ -11110,117 +11196,58 @@
|
||||
},
|
||||
mouseout: function(e) {
|
||||
if (mousedown && e.clientX <= this.getBoundingClientRect().left) {
|
||||
return ImageExpand.contract(Get.postFromNode(this));
|
||||
return ImageExpand.toggle(Get.postFromNode(this));
|
||||
}
|
||||
},
|
||||
click: function(e) {
|
||||
if (this.paused && !this.controls) {
|
||||
this.play();
|
||||
return e.preventDefault();
|
||||
return e.stopPropagation();
|
||||
}
|
||||
}
|
||||
};
|
||||
})(),
|
||||
setupVideoControls: function(post) {
|
||||
var cb, contract, eventName, file, thumb, video, _ref;
|
||||
setupVideoCB: function(post) {
|
||||
var cb, eventName, file, _ref;
|
||||
file = post.file;
|
||||
thumb = file.thumb;
|
||||
video = file.fullImage;
|
||||
file.thumb.parentNode.removeAttribute('href');
|
||||
file.thumb.parentNode.removeAttribute('target');
|
||||
_ref = ImageExpand.videoCB;
|
||||
for (eventName in _ref) {
|
||||
cb = _ref[eventName];
|
||||
$.on(video, eventName, cb);
|
||||
$.on(file.fullImage, eventName, cb);
|
||||
}
|
||||
file.videoControls = $.el('span', {
|
||||
className: 'video-controls'
|
||||
});
|
||||
if (Conf['Show Controls']) {
|
||||
contract = $.el('a', {
|
||||
textContent: 'contract',
|
||||
href: 'javascript:;',
|
||||
title: 'You can also contract the video by dragging it to the left.'
|
||||
if (file.videoControls) {
|
||||
return $.on(file.videoControls.firstElementChild, 'click', function() {
|
||||
return ImageExpand.toggle(post);
|
||||
});
|
||||
$.on(contract, 'click', function(e) {
|
||||
return ImageExpand.contract(post);
|
||||
});
|
||||
$.add(file.videoControls, [$.tn('\u00A0'), contract]);
|
||||
}
|
||||
return $.add(file.text, file.videoControls);
|
||||
},
|
||||
error: function() {
|
||||
var URL, error, post, src, timeoutID;
|
||||
var post;
|
||||
post = Get.postFromNode(this);
|
||||
$.rm(this);
|
||||
delete post.file.isReady;
|
||||
delete post.file.fullImage;
|
||||
if (!($.hasClass(post.file.thumb, 'expanding') || $.hasClass(post.nodes.root, 'expanded-image'))) {
|
||||
if (!(post.file.isExpanding || post.file.isExpanded)) {
|
||||
return;
|
||||
}
|
||||
ImageExpand.contract(post);
|
||||
if (this.error && this.error.code !== this.error.MEDIA_ERR_NETWORK) {
|
||||
error = (function() {
|
||||
switch (this.error.code) {
|
||||
case 1:
|
||||
return 'MEDIA_ERR_ABORTED';
|
||||
case 3:
|
||||
return 'MEDIA_ERR_DECODE';
|
||||
case 4:
|
||||
return 'MEDIA_ERR_SRC_NOT_SUPPORTED';
|
||||
case 5:
|
||||
return 'MEDIA_ERR_ENCRYPTED';
|
||||
}
|
||||
}).call(this);
|
||||
post.file.error = $.el('div', {
|
||||
textContent: "Playback error: " + error,
|
||||
className: 'warning'
|
||||
});
|
||||
$.after(post.file.thumb, post.file.error);
|
||||
return;
|
||||
if (ImageCommon.decodeError(this, post)) {
|
||||
return ImageExpand.contract(post);
|
||||
}
|
||||
src = this.src.split('/');
|
||||
if (src[2] === 'i.4cdn.org') {
|
||||
URL = Redirect.to('file', {
|
||||
boardID: src[3],
|
||||
filename: src[4].replace(/\?.+$/, '')
|
||||
});
|
||||
if (URL) {
|
||||
setTimeout(ImageExpand.expand, 10000, post, URL);
|
||||
return;
|
||||
}
|
||||
if (g.DEAD || post.isDead || post.file.isDead) {
|
||||
return;
|
||||
}
|
||||
if (this.src.split('/')[2] !== 'i.4cdn.org') {
|
||||
return ImageExpand.contract(post);
|
||||
}
|
||||
timeoutID = setTimeout(ImageExpand.expand, 10000, post);
|
||||
return $.ajax("//a.4cdn.org/" + post.board + "/thread/" + post.thread + ".json", {
|
||||
onload: function() {
|
||||
var postObj, _i, _len, _ref;
|
||||
if (this.status !== 200) {
|
||||
return;
|
||||
}
|
||||
_ref = this.response.posts;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
postObj = _ref[_i];
|
||||
if (postObj.no === post.ID) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (postObj.no !== post.ID) {
|
||||
clearTimeout(timeoutID);
|
||||
return post.kill();
|
||||
} else if (postObj.filedeleted) {
|
||||
clearTimeout(timeoutID);
|
||||
return post.kill(true);
|
||||
return ImageCommon.error(this, post, 10 * $.SECOND, function(URL) {
|
||||
if (post.file.isExpanding || post.file.isExpanded) {
|
||||
ImageExpand.contract(post);
|
||||
if (URL) {
|
||||
return ImageExpand.expand(post, URL);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
menu: {
|
||||
init: function() {
|
||||
var conf, createSubEntry, el, name, subEntries, _ref;
|
||||
if (g.VIEW === 'catalog' || !Conf['Image Expansion']) {
|
||||
var conf, createSubEntry, el, name, subEntries, _ref, _ref1;
|
||||
if (!(((_ref = g.VIEW) === 'index' || _ref === 'thread') && Conf['Image Expansion'])) {
|
||||
return;
|
||||
}
|
||||
el = $.el('span', {
|
||||
@ -11229,9 +11256,9 @@
|
||||
});
|
||||
createSubEntry = ImageExpand.menu.createSubEntry;
|
||||
subEntries = [];
|
||||
_ref = Config.imageExpansion;
|
||||
for (name in _ref) {
|
||||
conf = _ref[name];
|
||||
_ref1 = Config.imageExpansion;
|
||||
for (name in _ref1) {
|
||||
conf = _ref1[name];
|
||||
subEntries.push(createSubEntry(name, conf[1]));
|
||||
}
|
||||
return Header.menu.addEntry({
|
||||
@ -11242,15 +11269,12 @@
|
||||
},
|
||||
createSubEntry: function(name, desc) {
|
||||
var input, label;
|
||||
label = $.el('label', {
|
||||
innerHTML: "<input type=checkbox name='" + name + "'> " + name,
|
||||
title: desc
|
||||
});
|
||||
label = UI.checkbox(name, name);
|
||||
label.title = desc;
|
||||
input = label.firstElementChild;
|
||||
if (name === 'Fit width' || name === 'Fit height') {
|
||||
$.on(input, 'change', ImageExpand.cb.setFitness);
|
||||
}
|
||||
input.checked = Conf[name];
|
||||
$.event('change', null, input);
|
||||
$.on(input, 'change', $.cb.checked);
|
||||
return {
|
||||
|
||||
@ -10908,7 +10908,8 @@
|
||||
|
||||
ImageExpand = {
|
||||
init: function() {
|
||||
if (g.VIEW === 'catalog' || !Conf['Image Expansion']) {
|
||||
var _ref;
|
||||
if (!(((_ref = g.VIEW) === 'index' || _ref === 'thread') && Conf['Image Expansion'])) {
|
||||
return;
|
||||
}
|
||||
this.EAI = $.el('a', {
|
||||
@ -10918,6 +10919,13 @@
|
||||
href: 'javascript:;'
|
||||
});
|
||||
$.on(this.EAI, 'click', ImageExpand.cb.toggleAll);
|
||||
$.on(d, 'scroll visibilitychange', this.cb.playVideos);
|
||||
this.videoControls = $.el('span', {
|
||||
className: 'video-controls'
|
||||
});
|
||||
$.extend(this.videoControls, {
|
||||
innerHTML: " <a href=\"javascript:;\" title=\"You can also contract the video by dragging it to the left.\">contract</a>"
|
||||
});
|
||||
Header.addShortcut(this.EAI, true);
|
||||
return Post.callbacks.push({
|
||||
name: 'Image Expansion',
|
||||
@ -10925,37 +10933,32 @@
|
||||
});
|
||||
},
|
||||
node: function() {
|
||||
var clone, thumb, _ref, _ref1;
|
||||
if (!(((_ref = this.file) != null ? _ref.isImage : void 0) || ((_ref1 = this.file) != null ? _ref1.isVideo : void 0))) {
|
||||
var _ref;
|
||||
if (!(this.file && (this.file.isImage || this.file.isVideo))) {
|
||||
return;
|
||||
}
|
||||
thumb = this.file.thumb;
|
||||
$.on(thumb.parentNode, 'click', ImageExpand.cb.toggle);
|
||||
$.on(this.file.thumb.parentNode, 'click', ImageExpand.cb.toggle);
|
||||
if (this.isClone) {
|
||||
if ($.hasClass(thumb, 'expanding')) {
|
||||
if (this.file.isExpanding) {
|
||||
ImageExpand.contract(this);
|
||||
ImageExpand.expand(this);
|
||||
return ImageExpand.expand(this);
|
||||
} else if (this.file.isExpanded && this.file.isVideo) {
|
||||
clone = this;
|
||||
ImageExpand.setupVideoControls(clone);
|
||||
if (!clone.origin.file.fullImage.paused) {
|
||||
$.queueTask(function() {
|
||||
return Video.start(clone.file.fullImage);
|
||||
});
|
||||
}
|
||||
ImageExpand.setupVideoCB(this);
|
||||
return ImageExpand.setupVideo(this, !((_ref = this.origin.file.fullImage) != null ? _ref.paused : void 0) || this.origin.file.wasPlaying, this.file.fullImage.controls);
|
||||
}
|
||||
} else if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler) && (Conf['Expand videos'] || !this.file.isVideo)) {
|
||||
return ImageExpand.expand(this, null, true);
|
||||
} else if (ImageExpand.on && !this.isHidden && !this.isFetchedQuote && (Conf['Expand spoilers'] || !this.file.isSpoiler) && (Conf['Expand videos'] || !this.file.isVideo)) {
|
||||
return ImageExpand.expand(this);
|
||||
}
|
||||
},
|
||||
cb: {
|
||||
toggle: function(e) {
|
||||
var post, _ref;
|
||||
var file, post;
|
||||
if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
post = Get.postFromNode(this);
|
||||
if (post.file.isExpanded && ((_ref = post.file.fullImage) != null ? _ref.controls : void 0)) {
|
||||
file = post.file;
|
||||
if (file.isExpanded && file.isVideo && file.fullImage.controls) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
@ -10978,9 +10981,7 @@
|
||||
if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) {
|
||||
ImageExpand.EAI.className = 'contract-all-shortcut a-icon';
|
||||
ImageExpand.EAI.title = 'Contract All Images';
|
||||
func = function(post) {
|
||||
return ImageExpand.expand(post, null, true);
|
||||
};
|
||||
func = ImageExpand.expand;
|
||||
} else {
|
||||
ImageExpand.EAI.className = 'expand-all-shortcut a-icon';
|
||||
ImageExpand.EAI.title = 'Expand All Images';
|
||||
@ -10995,122 +10996,207 @@
|
||||
}
|
||||
});
|
||||
},
|
||||
playVideos: function(e) {
|
||||
return g.posts.forEach(function(post) {
|
||||
var file, video, visible, _i, _len, _ref;
|
||||
_ref = [post].concat(__slice.call(post.clones));
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
post = _ref[_i];
|
||||
file = post.file;
|
||||
if (!(file && file.isVideo && file.isExpanded)) {
|
||||
continue;
|
||||
}
|
||||
video = file.fullImage;
|
||||
visible = Header.isNodeVisible(video);
|
||||
if (visible && file.wasPlaying) {
|
||||
delete file.wasPlaying;
|
||||
video.play();
|
||||
} else if (!visible && !video.paused) {
|
||||
file.wasPlaying = true;
|
||||
video.pause();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
setFitness: function() {
|
||||
return (this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-'));
|
||||
return $[this.checked ? 'addClass' : 'rmClass'](doc, this.name.toLowerCase().replace(/\s+/g, '-'));
|
||||
}
|
||||
},
|
||||
toggle: function(post) {
|
||||
var headRect, left, root, top, x, y, _ref;
|
||||
if (!(post.file.isExpanded || $.hasClass(post.file.thumb, 'expanding'))) {
|
||||
var next;
|
||||
if (!(post.file.isExpanding || post.file.isExpanded)) {
|
||||
post.file.scrollIntoView = Conf['Scroll into view'];
|
||||
ImageExpand.expand(post);
|
||||
return;
|
||||
}
|
||||
root = post.nodes.root;
|
||||
_ref = (Conf['Advance on contract'] ? (function() {
|
||||
var next;
|
||||
next = root;
|
||||
ImageExpand.contract(post);
|
||||
if (Conf['Advance on contract']) {
|
||||
next = post.nodes.root;
|
||||
while (next = $.x("following::div[contains(@class,'postContainer')][1]", next)) {
|
||||
if ($('.stub', next) || next.offsetHeight === 0) {
|
||||
continue;
|
||||
if (!($('.stub', next) || next.offsetHeight === 0)) {
|
||||
break;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
return root;
|
||||
})() : root).getBoundingClientRect(), top = _ref.top, left = _ref.left;
|
||||
if (top < 0) {
|
||||
y = top;
|
||||
if (Conf['Fixed Header'] && !Conf['Bottom Header']) {
|
||||
headRect = Header.bar.getBoundingClientRect();
|
||||
y -= headRect.top + headRect.height;
|
||||
if (next) {
|
||||
return Header.scrollTo(next);
|
||||
}
|
||||
}
|
||||
if (left < 0) {
|
||||
x = -window.scrollX;
|
||||
}
|
||||
if (x || y) {
|
||||
window.scrollBy(x, y);
|
||||
}
|
||||
return ImageExpand.contract(post);
|
||||
},
|
||||
contract: function(post) {
|
||||
var cb, eventName, thumb, video, _ref;
|
||||
thumb = post.file.thumb;
|
||||
if (post.file.isVideo && (video = post.file.fullImage)) {
|
||||
video.pause();
|
||||
TrashQueue.add(video, post);
|
||||
thumb.parentNode.href = video.src;
|
||||
thumb.parentNode.target = '_blank';
|
||||
_ref = ImageExpand.videoCB;
|
||||
for (eventName in _ref) {
|
||||
cb = _ref[eventName];
|
||||
$.off(video, eventName, cb);
|
||||
}
|
||||
$.rm(post.file.videoControls);
|
||||
delete post.file.videoControls;
|
||||
var bottom, cb, el, eventName, file, oldHeight, scrollY, top, x, _i, _len, _ref, _ref1;
|
||||
file = post.file;
|
||||
if (el = file.fullImage) {
|
||||
top = Header.getTopOf(el);
|
||||
bottom = top + el.getBoundingClientRect().height;
|
||||
oldHeight = d.body.clientHeight;
|
||||
scrollY = window.scrollY;
|
||||
}
|
||||
$.rmClass(post.nodes.root, 'expanded-image');
|
||||
$.rmClass(post.file.thumb, 'expanding');
|
||||
delete post.file.isExpanding;
|
||||
delete post.file.isExpanded;
|
||||
if (post.file.isVideo && post.file.fullImage) {
|
||||
return post.file.fullImage.pause();
|
||||
$.rmClass(file.thumb, 'expanding');
|
||||
if (file.videoControls) {
|
||||
$.rm(file.videoControls);
|
||||
}
|
||||
file.thumb.parentNode.href = file.URL;
|
||||
file.thumb.parentNode.target = '_blank';
|
||||
_ref = ['isExpanding', 'isExpanded', 'videoControls', 'wasPlaying', 'scrollIntoView'];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
x = _ref[_i];
|
||||
delete file[x];
|
||||
}
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
if (doc.contains(el)) {
|
||||
if (bottom <= 0) {
|
||||
window.scroll(0, scrollY + d.body.clientHeight - oldHeight);
|
||||
} else {
|
||||
Header.scrollToIfNeeded(post.nodes.root);
|
||||
}
|
||||
if (window.scrollX > 0) {
|
||||
window.scroll(0, window.scrollY);
|
||||
}
|
||||
}
|
||||
$.off(el, 'error', ImageExpand.error);
|
||||
ImageCommon.pushCache(el);
|
||||
if (file.isVideo) {
|
||||
el.pause();
|
||||
_ref1 = ImageExpand.videoCB;
|
||||
for (eventName in _ref1) {
|
||||
cb = _ref1[eventName];
|
||||
$.off(el, eventName, cb);
|
||||
}
|
||||
}
|
||||
if (Conf['Restart when Opened']) {
|
||||
ImageCommon.rewind(file.thumb);
|
||||
}
|
||||
delete file.fullImage;
|
||||
return $.queueTask(function() {
|
||||
if (file.isExpanding || file.isExpanded) {
|
||||
return;
|
||||
}
|
||||
$.rmClass(el, 'full-image');
|
||||
if (el.id) {
|
||||
return;
|
||||
}
|
||||
return $.rm(el);
|
||||
});
|
||||
},
|
||||
expand: function(post, src, disableAutoplay) {
|
||||
var el, isVideo, thumb, _ref;
|
||||
_ref = post.file, thumb = _ref.thumb, isVideo = _ref.isVideo;
|
||||
if (post.isHidden || post.file.isExpanded || $.hasClass(thumb, 'expanding')) {
|
||||
expand: function(post, src) {
|
||||
var el, file, isVideo, thumb, _ref;
|
||||
file = post.file;
|
||||
thumb = file.thumb, isVideo = file.isVideo;
|
||||
if (post.isHidden || file.isExpanding || file.isExpanded) {
|
||||
return;
|
||||
}
|
||||
$.addClass(thumb, 'expanding');
|
||||
if (el = post.file.fullImage) {
|
||||
el.className = 'full-image';
|
||||
el.style.cssText = '';
|
||||
TrashQueue.remove(el);
|
||||
} else {
|
||||
el = post.file.fullImage = $.el((isVideo ? 'video' : 'img'), {
|
||||
className: 'full-image'
|
||||
});
|
||||
file.isExpanding = true;
|
||||
if (file.fullImage) {
|
||||
el = file.fullImage;
|
||||
} else if (((_ref = ImageCommon.cache) != null ? _ref.dataset.fullID : void 0) === post.fullID) {
|
||||
el = file.fullImage = ImageCommon.popCache();
|
||||
$.on(el, 'error', ImageExpand.error);
|
||||
el.src = src || post.file.URL;
|
||||
if (isVideo) {
|
||||
el.loop = true;
|
||||
if (Conf['Restart when Opened'] && el.id !== 'ihover') {
|
||||
ImageCommon.rewind(el);
|
||||
}
|
||||
el.removeAttribute('id');
|
||||
} else {
|
||||
el = file.fullImage = $.el((isVideo ? 'video' : 'img'));
|
||||
el.dataset.fullID = post.fullID;
|
||||
$.on(el, 'error', ImageExpand.error);
|
||||
el.src = src || file.URL;
|
||||
}
|
||||
if (el !== thumb.nextSibling) {
|
||||
$.after(thumb, el);
|
||||
el.className = 'full-image';
|
||||
$.after(thumb, el);
|
||||
if (isVideo) {
|
||||
if (Conf['Show Controls'] && !file.videoControls) {
|
||||
file.videoControls = ImageExpand.videoControls.cloneNode(true);
|
||||
$.add(file.text, file.videoControls);
|
||||
}
|
||||
thumb.parentNode.removeAttribute('href');
|
||||
thumb.parentNode.removeAttribute('target');
|
||||
el.loop = true;
|
||||
ImageExpand.setupVideoCB(post);
|
||||
}
|
||||
if (!isVideo) {
|
||||
return $.asap((function() {
|
||||
return el.naturalHeight;
|
||||
}), function() {
|
||||
return ImageExpand.completeExpand(post);
|
||||
});
|
||||
} else if (el.readyState >= el.HAVE_METADATA) {
|
||||
return ImageExpand.completeExpand(post);
|
||||
} else {
|
||||
return $.on(el, 'loadedmetadata', function() {
|
||||
return ImageExpand.completeExpand(post);
|
||||
});
|
||||
}
|
||||
return $.asap((function() {
|
||||
return el.videoHeight || el.naturalHeight;
|
||||
}), function() {
|
||||
return ImageExpand.completeExpand(post, disableAutoplay);
|
||||
});
|
||||
},
|
||||
completeExpand: function(post, disableAutoplay) {
|
||||
var bottom;
|
||||
if (!$.hasClass(post.file.thumb, 'expanding')) {
|
||||
completeExpand: function(post) {
|
||||
var bottom, file, imageBottom, oldHeight, scrollY;
|
||||
file = post.file;
|
||||
if (!file.isExpanding) {
|
||||
return;
|
||||
}
|
||||
if (!post.nodes.root.parentNode) {
|
||||
ImageExpand.completeExpand2(post);
|
||||
return;
|
||||
}
|
||||
bottom = post.nodes.root.getBoundingClientRect().bottom;
|
||||
return $.queueTask(function() {
|
||||
ImageExpand.completeExpand2(post, disableAutoplay);
|
||||
if (!(bottom <= 0)) {
|
||||
return;
|
||||
}
|
||||
return window.scrollBy(0, post.nodes.root.getBoundingClientRect().bottom - bottom);
|
||||
});
|
||||
},
|
||||
completeExpand2: function(post, disableAutoplay) {
|
||||
bottom = Header.getTopOf(file.thumb) + file.thumb.getBoundingClientRect().height;
|
||||
oldHeight = d.body.clientHeight;
|
||||
scrollY = window.scrollY;
|
||||
$.addClass(post.nodes.root, 'expanded-image');
|
||||
$.rmClass(post.file.thumb, 'expanding');
|
||||
post.file.isExpanded = true;
|
||||
if (post.file.isVideo) {
|
||||
ImageExpand.setupVideoControls(post);
|
||||
return Video.configure(post.file.fullImage, disableAutoplay);
|
||||
$.rmClass(file.thumb, 'expanding');
|
||||
file.isExpanded = true;
|
||||
delete file.isExpanding;
|
||||
if (doc.contains(post.nodes.root) && bottom <= 0) {
|
||||
window.scroll(window.scrollX, scrollY + d.body.clientHeight - oldHeight);
|
||||
}
|
||||
if (file.scrollIntoView) {
|
||||
delete file.scrollIntoView;
|
||||
imageBottom = Header.getBottomOf(file.fullImage) - 25;
|
||||
if (imageBottom < 0) {
|
||||
window.scrollBy(0, Math.min(-imageBottom, Header.getTopOf(file.fullImage)));
|
||||
}
|
||||
}
|
||||
if (file.isVideo) {
|
||||
return ImageExpand.setupVideo(post, Conf['Autoplay'], Conf['Show Controls']);
|
||||
}
|
||||
},
|
||||
setupVideo: function(post, playing, controls) {
|
||||
var fullImage;
|
||||
fullImage = post.file.fullImage;
|
||||
if (!playing) {
|
||||
fullImage.controls = controls;
|
||||
return;
|
||||
}
|
||||
fullImage.controls = false;
|
||||
$.asap((function() {
|
||||
return doc.contains(fullImage);
|
||||
}), function() {
|
||||
if (!d.hidden && Header.isNodeVisible(fullImage)) {
|
||||
return fullImage.play();
|
||||
} else {
|
||||
return post.file.wasPlaying = true;
|
||||
}
|
||||
});
|
||||
if (controls) {
|
||||
return ImageCommon.addControls(fullImage);
|
||||
}
|
||||
},
|
||||
videoCB: (function() {
|
||||
@ -11132,106 +11218,58 @@
|
||||
},
|
||||
mouseout: function(e) {
|
||||
if (mousedown && e.clientX <= this.getBoundingClientRect().left) {
|
||||
return ImageExpand.contract(Get.postFromNode(this));
|
||||
return ImageExpand.toggle(Get.postFromNode(this));
|
||||
}
|
||||
},
|
||||
click: function(e) {
|
||||
if (this.paused && !this.controls) {
|
||||
this.play();
|
||||
return e.preventDefault();
|
||||
return e.stopPropagation();
|
||||
}
|
||||
}
|
||||
};
|
||||
})(),
|
||||
setupVideoControls: function(post) {
|
||||
var cb, contract, eventName, file, thumb, video, _ref;
|
||||
setupVideoCB: function(post) {
|
||||
var cb, eventName, file, _ref;
|
||||
file = post.file;
|
||||
thumb = file.thumb;
|
||||
video = file.fullImage;
|
||||
file.thumb.parentNode.removeAttribute('href');
|
||||
file.thumb.parentNode.removeAttribute('target');
|
||||
_ref = ImageExpand.videoCB;
|
||||
for (eventName in _ref) {
|
||||
cb = _ref[eventName];
|
||||
$.on(video, eventName, cb);
|
||||
$.on(file.fullImage, eventName, cb);
|
||||
}
|
||||
file.videoControls = $.el('span', {
|
||||
className: 'video-controls'
|
||||
});
|
||||
if (Conf['Show Controls']) {
|
||||
contract = $.el('a', {
|
||||
textContent: 'contract',
|
||||
href: 'javascript:;',
|
||||
title: 'You can also contract the video by dragging it to the left.'
|
||||
if (file.videoControls) {
|
||||
return $.on(file.videoControls.firstElementChild, 'click', function() {
|
||||
return ImageExpand.toggle(post);
|
||||
});
|
||||
$.on(contract, 'click', function(e) {
|
||||
return ImageExpand.contract(post);
|
||||
});
|
||||
$.add(file.videoControls, [$.tn('\u00A0'), contract]);
|
||||
}
|
||||
return $.add(file.text, file.videoControls);
|
||||
},
|
||||
error: function() {
|
||||
var URL, error, post, src, timeoutID;
|
||||
var post;
|
||||
post = Get.postFromNode(this);
|
||||
$.rm(this);
|
||||
delete post.file.isReady;
|
||||
delete post.file.fullImage;
|
||||
if (!($.hasClass(post.file.thumb, 'expanding') || $.hasClass(post.nodes.root, 'expanded-image'))) {
|
||||
if (!(post.file.isExpanding || post.file.isExpanded)) {
|
||||
return;
|
||||
}
|
||||
ImageExpand.contract(post);
|
||||
if (this.error && this.error.code !== this.error.MEDIA_ERR_NETWORK) {
|
||||
error = (function() {
|
||||
switch (this.error.code) {
|
||||
case 1:
|
||||
return 'MEDIA_ERR_ABORTED';
|
||||
case 3:
|
||||
return 'MEDIA_ERR_DECODE';
|
||||
case 4:
|
||||
return 'MEDIA_ERR_SRC_NOT_SUPPORTED';
|
||||
case 5:
|
||||
return 'MEDIA_ERR_ENCRYPTED';
|
||||
}
|
||||
}).call(this);
|
||||
post.file.error = $.el('div', {
|
||||
textContent: "Playback error: " + error,
|
||||
className: 'warning'
|
||||
});
|
||||
$.after(post.file.thumb, post.file.error);
|
||||
return;
|
||||
if (ImageCommon.decodeError(this, post)) {
|
||||
return ImageExpand.contract(post);
|
||||
}
|
||||
src = this.src.split('/');
|
||||
if (src[2] === 'i.4cdn.org') {
|
||||
URL = Redirect.to('file', {
|
||||
boardID: src[3],
|
||||
filename: src[4].replace(/\?.+$/, '')
|
||||
});
|
||||
if (URL) {
|
||||
setTimeout(ImageExpand.expand, 10000, post, URL);
|
||||
return;
|
||||
}
|
||||
if (g.DEAD || post.isDead || post.file.isDead) {
|
||||
return;
|
||||
}
|
||||
if (this.src.split('/')[2] !== 'i.4cdn.org') {
|
||||
return ImageExpand.contract(post);
|
||||
}
|
||||
timeoutID = setTimeout(ImageExpand.expand, 10000, post);
|
||||
return $.ajax(this.src, {
|
||||
onloadend: function() {
|
||||
if (this.status !== 404) {
|
||||
return;
|
||||
return ImageCommon.error(this, post, 10 * $.SECOND, function(URL) {
|
||||
if (post.file.isExpanding || post.file.isExpanded) {
|
||||
ImageExpand.contract(post);
|
||||
if (URL) {
|
||||
return ImageExpand.expand(post, URL);
|
||||
}
|
||||
clearTimeout(timeoutID);
|
||||
return post.kill(true);
|
||||
}
|
||||
}, {
|
||||
type: 'head'
|
||||
});
|
||||
},
|
||||
menu: {
|
||||
init: function() {
|
||||
var conf, createSubEntry, el, name, subEntries, _ref;
|
||||
if (g.VIEW === 'catalog' || !Conf['Image Expansion']) {
|
||||
var conf, createSubEntry, el, name, subEntries, _ref, _ref1;
|
||||
if (!(((_ref = g.VIEW) === 'index' || _ref === 'thread') && Conf['Image Expansion'])) {
|
||||
return;
|
||||
}
|
||||
el = $.el('span', {
|
||||
@ -11240,9 +11278,9 @@
|
||||
});
|
||||
createSubEntry = ImageExpand.menu.createSubEntry;
|
||||
subEntries = [];
|
||||
_ref = Config.imageExpansion;
|
||||
for (name in _ref) {
|
||||
conf = _ref[name];
|
||||
_ref1 = Config.imageExpansion;
|
||||
for (name in _ref1) {
|
||||
conf = _ref1[name];
|
||||
subEntries.push(createSubEntry(name, conf[1]));
|
||||
}
|
||||
return Header.menu.addEntry({
|
||||
@ -11253,15 +11291,12 @@
|
||||
},
|
||||
createSubEntry: function(name, desc) {
|
||||
var input, label;
|
||||
label = $.el('label', {
|
||||
innerHTML: "<input type=checkbox name='" + name + "'> " + name,
|
||||
title: desc
|
||||
});
|
||||
label = UI.checkbox(name, name);
|
||||
label.title = desc;
|
||||
input = label.firstElementChild;
|
||||
if (name === 'Fit width' || name === 'Fit height') {
|
||||
$.on(input, 'change', ImageExpand.cb.setFitness);
|
||||
}
|
||||
input.checked = Conf[name];
|
||||
$.event('change', null, input);
|
||||
$.on(input, 'change', $.cb.checked);
|
||||
return {
|
||||
|
||||
@ -1,15 +1,20 @@
|
||||
ImageExpand =
|
||||
init: ->
|
||||
return if g.VIEW is 'catalog' or !Conf['Image Expansion']
|
||||
return unless g.VIEW in ['index', 'thread'] and Conf['Image Expansion']
|
||||
|
||||
@EAI = $.el 'a',
|
||||
id: 'img-controls'
|
||||
id: 'img-controls'
|
||||
className: 'expand-all-shortcut a-icon'
|
||||
title: 'Expand All Images'
|
||||
href: 'javascript:;'
|
||||
|
||||
$.on @EAI, 'click', ImageExpand.cb.toggleAll
|
||||
|
||||
$.on d, 'scroll visibilitychange', @cb.playVideos
|
||||
@videoControls = $.el 'span', className: 'video-controls'
|
||||
$.extend @videoControls,
|
||||
<%= html('\u00A0<a href="javascript:;" title="You can also contract the video by dragging it to the left.">contract</a>') %>
|
||||
|
||||
Header.addShortcut @EAI, true
|
||||
|
||||
Post.callbacks.push
|
||||
@ -17,33 +22,31 @@ ImageExpand =
|
||||
cb: @node
|
||||
|
||||
node: ->
|
||||
return unless @file?.isImage or @file?.isVideo
|
||||
{thumb} = @file
|
||||
$.on thumb.parentNode, 'click', ImageExpand.cb.toggle
|
||||
return unless @file and (@file.isImage or @file.isVideo)
|
||||
$.on @file.thumb.parentNode, 'click', ImageExpand.cb.toggle
|
||||
|
||||
if @isClone
|
||||
if $.hasClass thumb, 'expanding'
|
||||
if @file.isExpanding
|
||||
# If we clone a post where the image is still loading,
|
||||
# make it loading in the clone too.
|
||||
ImageExpand.contract @
|
||||
ImageExpand.expand @
|
||||
|
||||
else if @file.isExpanded and @file.isVideo
|
||||
clone = @
|
||||
ImageExpand.setupVideoControls clone
|
||||
unless clone.origin.file.fullImage.paused
|
||||
$.queueTask -> Video.start clone.file.fullImage
|
||||
ImageExpand.setupVideoCB @
|
||||
ImageExpand.setupVideo @, !@origin.file.fullImage?.paused or @origin.file.wasPlaying, @file.fullImage.controls
|
||||
|
||||
return
|
||||
|
||||
else if ImageExpand.on and !@isHidden and
|
||||
else if ImageExpand.on and !@isHidden and !@isFetchedQuote and
|
||||
(Conf['Expand spoilers'] or !@file.isSpoiler) and
|
||||
(Conf['Expand videos'] or !@file.isVideo)
|
||||
ImageExpand.expand @, null, true
|
||||
ImageExpand.expand @
|
||||
|
||||
cb:
|
||||
toggle: (e) ->
|
||||
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
|
||||
post = Get.postFromNode @
|
||||
return if post.file.isExpanded and post.file.fullImage?.controls
|
||||
{file} = post
|
||||
return if file.isExpanded and file.isVideo and file.fullImage.controls
|
||||
e.preventDefault()
|
||||
ImageExpand.toggle post
|
||||
|
||||
@ -62,7 +65,7 @@ ImageExpand =
|
||||
if ImageExpand.on = $.hasClass ImageExpand.EAI, 'expand-all-shortcut'
|
||||
ImageExpand.EAI.className = 'contract-all-shortcut a-icon'
|
||||
ImageExpand.EAI.title = 'Contract All Images'
|
||||
func = (post) -> ImageExpand.expand post, null, true
|
||||
func = ImageExpand.expand
|
||||
else
|
||||
ImageExpand.EAI.className = 'expand-all-shortcut a-icon'
|
||||
ImageExpand.EAI.title = 'Expand All Images'
|
||||
@ -72,96 +75,170 @@ ImageExpand =
|
||||
toggle post for post in [post, post.clones...]
|
||||
return
|
||||
|
||||
playVideos: (e) ->
|
||||
g.posts.forEach (post) ->
|
||||
for post in [post, post.clones...]
|
||||
{file} = post
|
||||
continue unless file and file.isVideo and file.isExpanded
|
||||
|
||||
video = file.fullImage
|
||||
visible = Header.isNodeVisible video
|
||||
if visible and file.wasPlaying
|
||||
delete file.wasPlaying
|
||||
video.play()
|
||||
else if !visible and !video.paused
|
||||
file.wasPlaying = true
|
||||
video.pause()
|
||||
return
|
||||
|
||||
setFitness: ->
|
||||
(if @checked then $.addClass else $.rmClass) doc, @name.toLowerCase().replace /\s+/g, '-'
|
||||
$[if @checked then 'addClass' else 'rmClass'] doc, @name.toLowerCase().replace /\s+/g, '-'
|
||||
|
||||
toggle: (post) ->
|
||||
unless post.file.isExpanded or $.hasClass post.file.thumb, 'expanding'
|
||||
unless post.file.isExpanding or post.file.isExpanded
|
||||
post.file.scrollIntoView = Conf['Scroll into view']
|
||||
ImageExpand.expand post
|
||||
return
|
||||
|
||||
# Scroll back to the thumbnail when contracting the image
|
||||
# to avoid being left miles away from the relevant post.
|
||||
{root} = post.nodes
|
||||
{top, left} = (if Conf['Advance on contract'] then do ->
|
||||
next = root
|
||||
while next = $.x "following::div[contains(@class,'postContainer')][1]", next
|
||||
continue if $('.stub', next) or next.offsetHeight is 0
|
||||
return next
|
||||
root
|
||||
else
|
||||
root
|
||||
).getBoundingClientRect()
|
||||
|
||||
if top < 0
|
||||
y = top
|
||||
if Conf['Fixed Header'] and not Conf['Bottom Header']
|
||||
headRect = Header.bar.getBoundingClientRect()
|
||||
y -= headRect.top + headRect.height
|
||||
|
||||
if left < 0
|
||||
x = -window.scrollX
|
||||
window.scrollBy x, y if x or y
|
||||
ImageExpand.contract post
|
||||
|
||||
if Conf['Advance on contract']
|
||||
next = post.nodes.root
|
||||
while next = $.x "following::div[contains(@class,'postContainer')][1]", next
|
||||
break unless $('.stub', next) or next.offsetHeight is 0
|
||||
if next
|
||||
Header.scrollTo next
|
||||
|
||||
contract: (post) ->
|
||||
{thumb} = post.file
|
||||
if post.file.isVideo and video = post.file.fullImage
|
||||
video.pause()
|
||||
TrashQueue.add video, post
|
||||
thumb.parentNode.href = video.src
|
||||
thumb.parentNode.target = '_blank'
|
||||
for eventName, cb of ImageExpand.videoCB
|
||||
$.off video, eventName, cb
|
||||
$.rm post.file.videoControls
|
||||
delete post.file.videoControls
|
||||
{file} = post
|
||||
|
||||
if el = file.fullImage
|
||||
top = Header.getTopOf el
|
||||
bottom = top + el.getBoundingClientRect().height
|
||||
oldHeight = d.body.clientHeight
|
||||
{scrollY} = window
|
||||
|
||||
$.rmClass post.nodes.root, 'expanded-image'
|
||||
$.rmClass post.file.thumb, 'expanding'
|
||||
delete post.file.isExpanding
|
||||
delete post.file.isExpanded
|
||||
post.file.fullImage.pause() if post.file.isVideo and post.file.fullImage
|
||||
$.rmClass file.thumb, 'expanding'
|
||||
$.rm file.videoControls if file.videoControls
|
||||
file.thumb.parentNode.href = file.URL
|
||||
file.thumb.parentNode.target = '_blank'
|
||||
for x in ['isExpanding', 'isExpanded', 'videoControls', 'wasPlaying', 'scrollIntoView']
|
||||
delete file[x]
|
||||
|
||||
expand: (post, src, disableAutoplay) ->
|
||||
# Do not expand images of hidden/filtered replies, or already expanded pictures.
|
||||
{thumb, isVideo} = post.file
|
||||
return if post.isHidden or post.file.isExpanded or $.hasClass thumb, 'expanding'
|
||||
$.addClass thumb, 'expanding'
|
||||
if el = post.file.fullImage
|
||||
# Expand already-loaded/ing picture.
|
||||
el.className = 'full-image'
|
||||
el.style.cssText = ''
|
||||
TrashQueue.remove el
|
||||
else
|
||||
el = post.file.fullImage = $.el (if isVideo then 'video' else 'img'),
|
||||
className: 'full-image'
|
||||
$.on el, 'error', ImageExpand.error
|
||||
el.src = src or post.file.URL
|
||||
if isVideo
|
||||
el.loop = true
|
||||
$.after thumb, el unless el is thumb.nextSibling
|
||||
$.asap (-> el.videoHeight or el.naturalHeight), ->
|
||||
ImageExpand.completeExpand post, disableAutoplay
|
||||
return unless el
|
||||
|
||||
completeExpand: (post, disableAutoplay) ->
|
||||
return unless $.hasClass post.file.thumb, 'expanding' # contracted before the image loaded
|
||||
unless post.nodes.root.parentNode
|
||||
# Image might start/finish loading before the post is inserted.
|
||||
# Don't scroll when it's expanded in a QP for example.
|
||||
ImageExpand.completeExpand2 post
|
||||
return
|
||||
{bottom} = post.nodes.root.getBoundingClientRect()
|
||||
if doc.contains el
|
||||
if bottom <= 0
|
||||
# For images entirely above us, scroll to remain in place.
|
||||
window.scroll 0, scrollY + d.body.clientHeight - oldHeight
|
||||
else
|
||||
# For images not above us that would be moved above us, scroll to the thumbnail.
|
||||
Header.scrollToIfNeeded post.nodes.root
|
||||
if window.scrollX > 0
|
||||
# If we have scrolled right viewing an expanded image, return to the left.
|
||||
window.scroll 0, window.scrollY
|
||||
|
||||
$.off el, 'error', ImageExpand.error
|
||||
ImageCommon.pushCache el
|
||||
if file.isVideo
|
||||
el.pause()
|
||||
for eventName, cb of ImageExpand.videoCB
|
||||
$.off el, eventName, cb
|
||||
ImageCommon.rewind file.thumb if Conf['Restart when Opened']
|
||||
delete file.fullImage
|
||||
$.queueTask ->
|
||||
ImageExpand.completeExpand2 post, disableAutoplay
|
||||
return unless bottom <= 0
|
||||
window.scrollBy 0, post.nodes.root.getBoundingClientRect().bottom - bottom
|
||||
# XXX Work around Chrome/Chromium not firing mouseover on the thumbnail.
|
||||
return if file.isExpanding or file.isExpanded
|
||||
$.rmClass el, 'full-image'
|
||||
return if el.id
|
||||
$.rm el
|
||||
|
||||
expand: (post, src) ->
|
||||
# Do not expand images of hidden/filtered replies, or already expanded pictures.
|
||||
{file} = post
|
||||
{thumb, isVideo} = file
|
||||
return if post.isHidden or file.isExpanding or file.isExpanded
|
||||
|
||||
$.addClass thumb, 'expanding'
|
||||
file.isExpanding = true
|
||||
|
||||
if file.fullImage
|
||||
el = file.fullImage
|
||||
else if ImageCommon.cache?.dataset.fullID is post.fullID
|
||||
el = file.fullImage = ImageCommon.popCache()
|
||||
$.on el, 'error', ImageExpand.error
|
||||
ImageCommon.rewind el if Conf['Restart when Opened'] and el.id isnt 'ihover'
|
||||
el.removeAttribute 'id'
|
||||
else
|
||||
el = file.fullImage = $.el (if isVideo then 'video' else 'img')
|
||||
el.dataset.fullID = post.fullID
|
||||
$.on el, 'error', ImageExpand.error
|
||||
el.src = src or file.URL
|
||||
|
||||
el.className = 'full-image'
|
||||
$.after thumb, el
|
||||
|
||||
if isVideo
|
||||
# add contract link to file info
|
||||
if Conf['Show Controls'] and !file.videoControls
|
||||
file.videoControls = ImageExpand.videoControls.cloneNode true
|
||||
$.add file.text, file.videoControls
|
||||
|
||||
# disable link to file so native controls can work
|
||||
thumb.parentNode.removeAttribute 'href'
|
||||
thumb.parentNode.removeAttribute 'target'
|
||||
|
||||
el.loop = true
|
||||
ImageExpand.setupVideoCB post
|
||||
|
||||
if !isVideo
|
||||
$.asap (-> el.naturalHeight), -> ImageExpand.completeExpand post
|
||||
else if el.readyState >= el.HAVE_METADATA
|
||||
ImageExpand.completeExpand post
|
||||
else
|
||||
$.on el, 'loadedmetadata', -> ImageExpand.completeExpand post
|
||||
|
||||
completeExpand: (post) ->
|
||||
{file} = post
|
||||
return unless file.isExpanding # contracted before the image loaded
|
||||
|
||||
bottom = Header.getTopOf(file.thumb) + file.thumb.getBoundingClientRect().height
|
||||
oldHeight = d.body.clientHeight
|
||||
{scrollY} = window
|
||||
|
||||
completeExpand2: (post, disableAutoplay) ->
|
||||
$.addClass post.nodes.root, 'expanded-image'
|
||||
$.rmClass post.file.thumb, 'expanding'
|
||||
post.file.isExpanded = true
|
||||
if post.file.isVideo
|
||||
ImageExpand.setupVideoControls post
|
||||
Video.configure post.file.fullImage, disableAutoplay
|
||||
$.rmClass file.thumb, 'expanding'
|
||||
file.isExpanded = true
|
||||
delete file.isExpanding
|
||||
|
||||
# Scroll to keep our place in the thread when images are expanded above us.
|
||||
if doc.contains(post.nodes.root) and bottom <= 0
|
||||
window.scroll window.scrollX, scrollY + d.body.clientHeight - oldHeight
|
||||
|
||||
# Scroll to display full image.
|
||||
if file.scrollIntoView
|
||||
delete file.scrollIntoView
|
||||
imageBottom = Header.getBottomOf(file.fullImage) - 25
|
||||
if imageBottom < 0
|
||||
window.scrollBy 0, Math.min(-imageBottom, Header.getTopOf file.fullImage)
|
||||
|
||||
if file.isVideo
|
||||
ImageExpand.setupVideo post, Conf['Autoplay'], Conf['Show Controls']
|
||||
|
||||
setupVideo: (post, playing, controls) ->
|
||||
{fullImage} = post.file
|
||||
unless playing
|
||||
fullImage.controls = controls
|
||||
return
|
||||
fullImage.controls = false
|
||||
$.asap (-> doc.contains fullImage), ->
|
||||
if !d.hidden and Header.isNodeVisible fullImage
|
||||
fullImage.play()
|
||||
else
|
||||
post.file.wasPlaying = true
|
||||
if controls
|
||||
ImageCommon.addControls fullImage
|
||||
|
||||
videoCB: do ->
|
||||
# dragging to the left contracts the video
|
||||
@ -169,98 +246,41 @@ ImageExpand =
|
||||
mouseover: -> mousedown = false
|
||||
mousedown: (e) -> mousedown = true if e.button is 0
|
||||
mouseup: (e) -> mousedown = false if e.button is 0
|
||||
mouseout: (e) -> ImageExpand.contract(Get.postFromNode @) if mousedown and e.clientX <= @getBoundingClientRect().left
|
||||
mouseout: (e) -> ImageExpand.toggle(Get.postFromNode @) if mousedown and e.clientX <= @getBoundingClientRect().left
|
||||
click: (e) ->
|
||||
if @paused and not @controls
|
||||
@play()
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
setupVideoControls: (post) ->
|
||||
{file} = post
|
||||
{thumb} = file
|
||||
video = file.fullImage
|
||||
|
||||
# disable link to file so native controls can work
|
||||
file.thumb.parentNode.removeAttribute 'href'
|
||||
file.thumb.parentNode.removeAttribute 'target'
|
||||
|
||||
# setup callbacks on video element
|
||||
$.on video, eventName, cb for eventName, cb of ImageExpand.videoCB
|
||||
|
||||
# setup controls in file info
|
||||
file.videoControls = $.el 'span',
|
||||
className: 'video-controls'
|
||||
if Conf['Show Controls']
|
||||
contract = $.el 'a',
|
||||
textContent: 'contract'
|
||||
href: 'javascript:;'
|
||||
title: 'You can also contract the video by dragging it to the left.'
|
||||
$.on contract, 'click', (e) -> ImageExpand.contract post
|
||||
$.add file.videoControls, [$.tn('\u00A0'), contract]
|
||||
$.add file.text, file.videoControls
|
||||
setupVideoCB: (post) ->
|
||||
{file} = post
|
||||
for eventName, cb of ImageExpand.videoCB
|
||||
$.on file.fullImage, eventName, cb
|
||||
if file.videoControls
|
||||
$.on file.videoControls.firstElementChild, 'click', -> ImageExpand.toggle post
|
||||
|
||||
error: ->
|
||||
post = Get.postFromNode @
|
||||
$.rm @
|
||||
delete post.file.isReady
|
||||
delete post.file.fullImage
|
||||
# Images can error:
|
||||
# - before the image started loading.
|
||||
# - after the image started loading.
|
||||
unless $.hasClass(post.file.thumb, 'expanding') or $.hasClass post.nodes.root, 'expanded-image'
|
||||
# Don't try to re-expend if it was already contracted.
|
||||
return
|
||||
ImageExpand.contract post
|
||||
|
||||
if @error and @error.code isnt @error.MEDIA_ERR_NETWORK # video
|
||||
error = switch @error.code
|
||||
when 1 then 'MEDIA_ERR_ABORTED'
|
||||
when 3 then 'MEDIA_ERR_DECODE'
|
||||
when 4 then 'MEDIA_ERR_SRC_NOT_SUPPORTED'
|
||||
when 5 then 'MEDIA_ERR_ENCRYPTED'
|
||||
post.file.error = $.el 'div',
|
||||
textContent: "Playback error: #{error}"
|
||||
className: 'warning'
|
||||
$.after post.file.thumb, post.file.error
|
||||
return
|
||||
|
||||
src = @src.split '/'
|
||||
if src[2] is 'i.4cdn.org'
|
||||
URL = Redirect.to 'file',
|
||||
boardID: src[3]
|
||||
filename: src[4].replace /\?.+$/, ''
|
||||
if URL
|
||||
setTimeout ImageExpand.expand, 10000, post, URL
|
||||
return
|
||||
if g.DEAD or post.isDead or post.file.isDead
|
||||
return
|
||||
|
||||
timeoutID = setTimeout ImageExpand.expand, 10000, post
|
||||
<% if (type === 'crx') { %>
|
||||
$.ajax @src,
|
||||
onloadend: ->
|
||||
return if @status isnt 404
|
||||
clearTimeout timeoutID
|
||||
post.kill true
|
||||
,
|
||||
type: 'head'
|
||||
<% } else { %>
|
||||
# XXX CORS for i.4cdn.org WHEN?
|
||||
$.ajax "//a.4cdn.org/#{post.board}/thread/#{post.thread}.json", onload: ->
|
||||
return if @status isnt 200
|
||||
for postObj in @response.posts
|
||||
break if postObj.no is post.ID
|
||||
if postObj.no isnt post.ID
|
||||
clearTimeout timeoutID
|
||||
post.kill()
|
||||
else if postObj.filedeleted
|
||||
clearTimeout timeoutID
|
||||
post.kill true
|
||||
<% } %>
|
||||
# Don't try to re-expand if it was already contracted.
|
||||
return unless post.file.isExpanding or post.file.isExpanded
|
||||
if ImageCommon.decodeError @, post
|
||||
return ImageExpand.contract post
|
||||
# Don't autoretry images from the archive.
|
||||
unless @src.split('/')[2] is 'i.4cdn.org'
|
||||
return ImageExpand.contract post
|
||||
ImageCommon.error @, post, 10 * $.SECOND, (URL) ->
|
||||
if post.file.isExpanding or post.file.isExpanded
|
||||
ImageExpand.contract post
|
||||
ImageExpand.expand post, URL if URL
|
||||
|
||||
menu:
|
||||
init: ->
|
||||
return if g.VIEW is 'catalog' or !Conf['Image Expansion']
|
||||
return unless g.VIEW in ['index', 'thread'] and Conf['Image Expansion']
|
||||
|
||||
el = $.el 'span',
|
||||
textContent: 'Image Expansion'
|
||||
@ -277,13 +297,11 @@ ImageExpand =
|
||||
subEntries: subEntries
|
||||
|
||||
createSubEntry: (name, desc) ->
|
||||
label = $.el 'label',
|
||||
innerHTML: "<input type=checkbox name='#{name}'> #{name}"
|
||||
title: desc
|
||||
label = UI.checkbox name, name
|
||||
label.title = desc
|
||||
input = label.firstElementChild
|
||||
if name in ['Fit width', 'Fit height']
|
||||
$.on input, 'change', ImageExpand.cb.setFitness
|
||||
input.checked = Conf[name]
|
||||
$.event 'change', null, input
|
||||
$.on input, 'change', $.cb.checked
|
||||
el: label
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user