From 4089d56720bd5607625f92f5887a62d399b6d681 Mon Sep 17 00:00:00 2001 From: Zixaphir Date: Fri, 9 Jan 2015 18:19:43 -0700 Subject: [PATCH] ImageExpand.coffee --- builds/appchan-x.user.js | 416 ++++++++++++++++++---------------- builds/crx/script.js | 405 ++++++++++++++++++--------------- src/Images/ImageExpand.coffee | 362 +++++++++++++++-------------- 3 files changed, 630 insertions(+), 553 deletions(-) diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js index 7d494f7dc..7ec9a870a 100644 --- a/builds/appchan-x.user.js +++ b/builds/appchan-x.user.js @@ -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: " contract" + }); 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: " " + 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 { diff --git a/builds/crx/script.js b/builds/crx/script.js index 07b05489c..86528efd6 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -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: " contract" + }); 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: " " + 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 { diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee index 8fde698fb..b46d879ef 100644 --- a/src/Images/ImageExpand.coffee +++ b/src/Images/ImageExpand.coffee @@ -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('\u00A0contract') %> + 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: " #{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