Merge branch 'ccd0' into v3

Rebase ImageExpand implemention off ccd0's

Conflicts:
	CHANGELOG.md
	builds/4chan-X.user.js
	builds/crx.crx
	builds/crx/script.js
	src/Images/ImageExpand.coffee
This commit is contained in:
Zixaphir 2014-04-08 14:46:15 -07:00
commit b00390b980
12 changed files with 258 additions and 271 deletions

View File

@ -1,3 +1,19 @@
<<<<<<< HEAD
=======
### v1.7.3
*2014-04-07*
**ccd0**
- Fix behavior of .webm videos expanded within inline quotes.
- Contract thumbnails in quoted previews to avoid crashes caused by videos in quoted previews on some systems.
- Change interface when both `Autoplay` and `Show Controls` are unchecked. In this mode, videos are now activated by clicking on them. The first click expands the video, the second click plays the video, and the third click contracts it.
- Add item `Expand videos` in `Image Expansion` menu, which enables expansion of videos by `Expand All Images`. Disabled by default. Previously videos were expanded.
- Disable autoplay for videos expanded by `Expand All Images`.
### v1.7.2
*2014-04-07*
>>>>>>> ccd0
**ccd0** **ccd0**
- Restore thread expansion with JSON navigation disabled. - Restore thread expansion with JSON navigation disabled.
@ -36,13 +52,15 @@
**ccd0**: **ccd0**:
- Support hover for .webm videos. - Support hover for .webm videos.
- Add .webm to supported posting types. - Add .webm to supported posting types.
- Add option to enable/disable sound. - Add option `Allow Sound` to enable/disable sound. Enabled by default.
## v1.5.0 ## v1.5.0
*2014-04-04* *2014-04-04*
**ccd0**: **ccd0**:
- Support expansion of .webm videos. - Support expansion of .webm videos.
- New setting: `Autoplay`, enabled by default. Causes videos to play immediately when expanded.
- New setting: `Show Controls`, enabled by default. Shows native controls on videos.
### v1.4.7 ### v1.4.7
*2014-04-03* *2014-04-03*

View File

@ -1,5 +1,5 @@
/* /*
* 4chan X - Version 1.7.2 - 2014-04-07 * 4chan X - Version 1.7.3 - 2014-04-08
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE * https://github.com/ccd0/4chan-x/blob/master/LICENSE

View File

@ -1,6 +1,6 @@
// ==UserScript== // ==UserScript==
// @name 4chan X // @name 4chan X
// @version 1.7.2 // @version 1.7.3
// @minGMVer 1.14 // @minGMVer 1.14
// @minFFVer 26 // @minFFVer 26
// @namespace 4chan-X // @namespace 4chan-X

View File

@ -1,7 +1,7 @@
// Generated by CoffeeScript // Generated by CoffeeScript
// ==UserScript== // ==UserScript==
// @name 4chan X // @name 4chan X
// @version 1.7.2 // @version 1.7.3
// @minGMVer 1.14 // @minGMVer 1.14
// @minFFVer 26 // @minFFVer 26
// @namespace 4chan-X // @namespace 4chan-X
@ -24,7 +24,7 @@
// ==/UserScript== // ==/UserScript==
/* /*
* 4chan X - Version 1.7.2 - 2014-04-07 * 4chan X - Version 1.7.3 - 2014-04-08
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE * https://github.com/ccd0/4chan-x/blob/master/LICENSE
@ -244,6 +244,7 @@
'Fit width': [false, ''], 'Fit width': [false, ''],
'Fit height': [false, ''], 'Fit height': [false, ''],
'Expand spoilers': [true, 'Expand all images along with spoilers.'], 'Expand spoilers': [true, 'Expand all images along with spoilers.'],
'Expand videos': [false, 'Expand all images also expands videos (no autoplay).'],
'Expand from here': [false, 'Expand all images only from current position to thread end.'], 'Expand from here': [false, 'Expand all images only from current position to thread end.'],
'Advance on contract': [false, 'Advance to next post when contracting an expanded image.'] 'Advance on contract': [false, 'Advance to next post when contracting an expanded image.']
}, },
@ -373,7 +374,7 @@
doc = d.documentElement; doc = d.documentElement;
g = { g = {
VERSION: '1.7.2', VERSION: '1.7.3',
NAMESPACE: '4chan X.', NAMESPACE: '4chan X.',
boards: {} boards: {}
}; };
@ -7928,7 +7929,7 @@
ImageExpand = { ImageExpand = {
init: function() { init: function() {
if (!Conf['Image Expansion']) { if (g.VIEW === 'catalog' || !Conf['Image Expansion']) {
return; return;
} }
this.EAI = $.el('a', { this.EAI = $.el('a', {
@ -7937,34 +7938,33 @@
title: 'Expand All Images', title: 'Expand All Images',
href: 'javascript:;' href: 'javascript:;'
}); });
$.on(this.EAI, 'click', this.cb.toggleAll); $.on(this.EAI, 'click', ImageExpand.cb.toggleAll);
Header.addShortcut(this.EAI, 3); Header.addShortcut(this.EAI, 3);
$.on(d, 'scroll visibilitychange', this.cb.playVideos);
return Post.callbacks.push({ return Post.callbacks.push({
name: 'Image Expansion', name: 'Image Expansion',
cb: this.node cb: this.node
}); });
}, },
node: function() { node: function() {
var thumb; var clone, thumb, _ref, _ref1;
if (!(this.file && (this.file.isImage || this.file.isVideo))) { if (!(((_ref = this.file) != null ? _ref.isImage : void 0) || ((_ref1 = this.file) != null ? _ref1.isVideo : void 0))) {
return; return;
} }
thumb = this.file.thumb; thumb = this.file.thumb;
$.on(thumb.parentNode, 'click', ImageExpand.cb.toggle); $.on(thumb.parentNode, 'click', ImageExpand.cb.toggle);
if (this.isClone) { if (this.isClone && $.hasClass(thumb, 'expanding')) {
if (this.file.isImage && this.file.isExpanding) { ImageExpand.contract(this);
ImageExpand.contract(this);
ImageExpand.expand(this);
return;
}
if (this.file.isExpanded && this.file.isVideo) {
ImageExpand.setupVideoControls(this);
return;
}
}
if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler)) {
return ImageExpand.expand(this); return ImageExpand.expand(this);
} else if (this.isClone && this.file.isExpanded && this.file.isVideo) {
clone = this;
ImageExpand.setupVideoControls(clone);
if (!clone.origin.file.fullImage.paused) {
return $.queueTask(function() {
return ImageExpand.startVideo(clone);
});
}
} else if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler) && (Conf['Expand videos'] || !this.file.isVideo)) {
return ImageExpand.expand(this, null, true);
} }
}, },
cb: { cb: {
@ -7981,53 +7981,40 @@
return ImageExpand.toggle(post); return ImageExpand.toggle(post);
}, },
toggleAll: function() { toggleAll: function() {
var func; var func, toggle;
$.event('CloseMenu'); $.event('CloseMenu');
toggle = function(post) {
var file;
file = post.file;
if (!(file && (file.isImage || file.isVideo) && doc.contains(post.nodes.root))) {
return;
}
if (ImageExpand.on && (!Conf['Expand spoilers'] && file.isSpoiler || !Conf['Expand videos'] && file.isVideo || Conf['Expand from here'] && Header.getTopOf(file.thumb) < 0)) {
return;
}
return $.queueTask(func, post);
};
if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) { if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) {
ImageExpand.EAI.className = 'contract-all-shortcut fa fa-compress'; ImageExpand.EAI.className = 'contract-all-shortcut fa fa-compress';
ImageExpand.EAI.title = 'Contract All Images'; ImageExpand.EAI.title = 'Contract All Images';
func = ImageExpand.expand; func = function(post) {
return ImageExpand.expand(post, null, true);
};
} else { } else {
ImageExpand.EAI.className = 'expand-all-shortcut fa fa-expand'; ImageExpand.EAI.className = 'expand-all-shortcut fa fa-expand';
ImageExpand.EAI.title = 'Expand All Images'; ImageExpand.EAI.title = 'Expand All Images';
func = ImageExpand.contract; func = ImageExpand.contract;
} }
return g.posts.forEach(function(post) { return g.posts.forEach(function(post) {
var file, _i, _len, _ref; var _i, _len, _ref;
_ref = [post].concat(post.clones); toggle(post);
_ref = post.clones;
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
post = _ref[_i]; post = _ref[_i];
file = post.file; toggle(post);
if (!(file && (file.isImage || file.isVideo) && doc.contains(post.nodes.root))) {
return;
}
if (ImageExpand.on && (post.isHidden || !Conf['Expand spoilers'] && post.file.isSpoiler || !doc.contains(post.nodes.root) || Conf['Expand from here'] && Header.getTopOf(post.file.thumb) < 0)) {
return;
}
$.queueTask(func, post);
} }
}); });
}, },
playVideos: function(e) {
var fullID, play, post, _i, _len, _ref, _ref1;
_ref = g.posts;
for (fullID in _ref) {
post = _ref[fullID];
if (!(post.file && post.file.isVideo && post.file.isExpanded)) {
continue;
}
_ref1 = [post].concat(post.clones);
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
post = _ref1[_i];
play = !d.hidden && !post.isHidden && doc.contains(post.nodes.root) && Header.isNodeVisible(post.nodes.root);
if (play) {
post.file.fullImage.play();
} else {
post.file.fullImage.pause();
}
}
}
},
setFitness: function() { 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, '-'));
} }
@ -8035,7 +8022,7 @@
toggle: function(post) { toggle: function(post) {
var headRect, left, root, thumb, top, x, y, _ref; var headRect, left, root, thumb, top, x, y, _ref;
thumb = post.file.thumb; thumb = post.file.thumb;
if (!(post.file.isExpanded || post.file.isExpanding)) { if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) {
ImageExpand.expand(post); ImageExpand.expand(post);
return; return;
} }
@ -8083,10 +8070,9 @@
} }
$.rmClass(post.nodes.root, 'expanded-image'); $.rmClass(post.nodes.root, 'expanded-image');
$.rmClass(post.file.thumb, 'expanding'); $.rmClass(post.file.thumb, 'expanding');
delete post.file.isExpanding;
return post.file.isExpanded = false; return post.file.isExpanded = false;
}, },
expand: function(post, src) { expand: function(post, src, disableAutoplay) {
var el, isVideo, thumb, _ref; var el, isVideo, thumb, _ref;
_ref = post.file, thumb = _ref.thumb, isVideo = _ref.isVideo; _ref = post.file, thumb = _ref.thumb, isVideo = _ref.isVideo;
if (post.isHidden || post.file.isExpanded || $.hasClass(thumb, 'expanding')) { if (post.isHidden || post.file.isExpanded || $.hasClass(thumb, 'expanding')) {
@ -8109,42 +8095,49 @@
$.after(thumb, el); $.after(thumb, el);
} }
return $.asap((function() { return $.asap((function() {
return el.videoHeight || el.naturalHeight; if (isVideo) {
return el.videoHeight;
} else {
return el.naturalHeight;
}
}), function() { }), function() {
return ImageExpand.completeExpand(post); return ImageExpand.completeExpand(post, disableAutoplay);
}); });
}, },
completeExpand: function(post) { completeExpand: function(post, disableAutoplay) {
var bottom, complete, thumb; var bottom, thumb;
thumb = post.file.thumb; thumb = post.file.thumb;
if (!$.hasClass(thumb, 'expanding')) { if (!$.hasClass(thumb, 'expanding')) {
return; return;
} }
delete post.file.isExpanding;
post.file.isExpanded = true;
complete = function() {
$.addClass(post.nodes.root, 'expanded-image');
$.rmClass(post.file.thumb, 'expanding');
if (post.file.isVideo) {
return ImageExpand.setupVideo(post);
}
};
if (!post.nodes.root.parentNode) { if (!post.nodes.root.parentNode) {
complete(); ImageExpand.completeExpand2(post);
return; return;
} }
if (post.file.isVideo && !d.hidden && Header.isNodeVisible(post.nodes.root)) {
post.file.fullImage.play();
}
bottom = post.nodes.root.getBoundingClientRect().bottom; bottom = post.nodes.root.getBoundingClientRect().bottom;
return $.queueTask(function() { return $.queueTask(function() {
complete(); ImageExpand.completeExpand2(post, disableAutoplay);
if (!(bottom <= 0)) { if (!(bottom <= 0)) {
return; return;
} }
return window.scrollBy(0, post.nodes.root.getBoundingClientRect().bottom - bottom); return window.scrollBy(0, post.nodes.root.getBoundingClientRect().bottom - bottom);
}); });
}, },
completeExpand2: function(post, disableAutoplay) {
var thumb;
thumb = post.file.thumb;
$.addClass(post.nodes.root, 'expanded-image');
$.rmClass(post.file.thumb, 'expanding');
post.file.isExpanded = true;
if (post.file.isVideo) {
ImageExpand.setupVideoControls(post);
post.file.fullImage.muted = !Conf['Allow Sound'];
post.file.fullImage.controls = Conf['Show Controls'];
if (Conf['Autoplay'] && !disableAutoplay) {
return ImageExpand.startVideo(post);
}
}
},
videoCB: { videoCB: {
click: function(e) { click: function(e) {
if (this.paused && !this.controls) { if (this.paused && !this.controls) {
@ -8199,34 +8192,29 @@
} }
return $.add(file.text, file.videoControls); return $.add(file.text, file.videoControls);
}, },
setupVideo: function(post) { startVideo: function(post) {
var file, video; var controls, file, video;
ImageExpand.setupVideoControls(post);
file = post.file; file = post.file;
video = file.fullImage; video = file.fullImage;
video.muted = !Conf['Allow Sound']; controls = video.controls;
video.controls = Conf['Show Controls']; video.controls = false;
if (Conf['Autoplay']) { video.play();
video.controls = false; if (controls) {
video.play(); return $.asap((function() {
if (Conf['Show Controls']) { return (video.readyState >= 3 && video.currentTime <= Math.max(0.1, video.duration - 0.5)) || !file.isExpanded;
return $.asap((function() { }), function() {
return (video.readyState >= 3 && video.currentTime <= Math.max(0.1, video.duration - 0.5)) || !file.isExpanded; if (file.isExpanded) {
}), function() { return video.controls = true;
if (file.isExpanded) { }
return video.controls = true; }, 500);
}
}, 500);
}
} }
}, },
error: function() { error: function() {
var URL, post, src, timeoutID; var URL, post, src, timeoutID;
post = Get.postFromNode(this); post = Get.postFromNode(this);
post.file.isReady = false;
$.rm(this); $.rm(this);
delete post.file.fullImage; delete post.file.fullImage;
if (!(post.file.isExpanding || post.file.isExpanded)) { if (!($.hasClass(post.file.thumb, 'expanding') || $.hasClass(post.nodes.root, 'expanded-image'))) {
return; return;
} }
ImageExpand.contract(post); ImageExpand.contract(post);
@ -8271,7 +8259,7 @@
menu: { menu: {
init: function() { init: function() {
var conf, createSubEntry, el, name, subEntries, _ref; var conf, createSubEntry, el, name, subEntries, _ref;
if (!Conf['Image Expansion']) { if (g.VIEW === 'catalog' || !Conf['Image Expansion']) {
return; return;
} }
el = $.el('span', { el = $.el('span', {
@ -12964,7 +12952,7 @@
return; return;
} }
$.event('CloseMenu'); $.event('CloseMenu');
html = "<nav><div class=sections-list></div><p class='imp-exp-result warning'></p><div class=credits><a class=export>Export</a>&nbsp|&nbsp<a class=import>Import</a>&nbsp|&nbsp<a class=reset>Reset Settings</a>&nbsp|&nbsp<input type=file hidden><a href='https://github.com/ccd0/4chan-x' target=_blank>4chan X</a> |<a href='https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md' target=_blank>" + g.VERSION + "</a> |<a href='https://github.com/ccd0/4chan-x/blob/master/README.md#reporting-bugs-and-suggestions' target=_blank>Issues</a> |<a href=javascript:; class='close fa fa-times' title=Close></a></div></nav><div class=section-container><section></section></div>"; html = "<nav><div class=sections-list></div><p class='imp-exp-result warning'></p><div class=credits><a class=export>Export</a>&nbsp|&nbsp<a class=import>Import</a>&nbsp|&nbsp<a class=reset>Reset Settings</a>&nbsp|&nbsp<input type=file hidden><a href='https://github.com/ccd0/4chan-x' target=_blank>4chan X</a>&nbsp|&nbsp<a href='https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md' target=_blank>" + g.VERSION + "</a>&nbsp|&nbsp<a href='https://github.com/ccd0/4chan-x/issues' target=_blank>Issues</a>&nbsp|&nbsp<a href=javascript:; class='close fa fa-times' title=Close></a></div></nav><div class=section-container><section></section></div>";
Settings.overlay = overlay = $.el('div', { Settings.overlay = overlay = $.el('div', {
id: 'overlay' id: 'overlay'
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "4chan X", "name": "4chan X",
"version": "1.7.2", "version": "1.7.3",
"manifest_version": 2, "manifest_version": 2,
"description": "Cross-browser userscript for maximum lurking on 4chan.", "description": "Cross-browser userscript for maximum lurking on 4chan.",
"icons": { "icons": {

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript // Generated by CoffeeScript
/* /*
* 4chan X - Version 1.7.2 - 2014-04-07 * 4chan X - Version 1.7.3 - 2014-04-08
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/ccd0/4chan-x/blob/master/LICENSE * https://github.com/ccd0/4chan-x/blob/master/LICENSE
@ -220,6 +220,7 @@
'Fit width': [false, ''], 'Fit width': [false, ''],
'Fit height': [false, ''], 'Fit height': [false, ''],
'Expand spoilers': [true, 'Expand all images along with spoilers.'], 'Expand spoilers': [true, 'Expand all images along with spoilers.'],
'Expand videos': [false, 'Expand all images also expands videos (no autoplay).'],
'Expand from here': [false, 'Expand all images only from current position to thread end.'], 'Expand from here': [false, 'Expand all images only from current position to thread end.'],
'Advance on contract': [false, 'Advance to next post when contracting an expanded image.'] 'Advance on contract': [false, 'Advance to next post when contracting an expanded image.']
}, },
@ -349,7 +350,7 @@
doc = d.documentElement; doc = d.documentElement;
g = { g = {
VERSION: '1.7.2', VERSION: '1.7.3',
NAMESPACE: '4chan X.', NAMESPACE: '4chan X.',
boards: {} boards: {}
}; };
@ -7967,7 +7968,7 @@
ImageExpand = { ImageExpand = {
init: function() { init: function() {
if (!Conf['Image Expansion']) { if (g.VIEW === 'catalog' || !Conf['Image Expansion']) {
return; return;
} }
this.EAI = $.el('a', { this.EAI = $.el('a', {
@ -7976,34 +7977,33 @@
title: 'Expand All Images', title: 'Expand All Images',
href: 'javascript:;' href: 'javascript:;'
}); });
$.on(this.EAI, 'click', this.cb.toggleAll); $.on(this.EAI, 'click', ImageExpand.cb.toggleAll);
Header.addShortcut(this.EAI, 3); Header.addShortcut(this.EAI, 3);
$.on(d, 'scroll visibilitychange', this.cb.playVideos);
return Post.callbacks.push({ return Post.callbacks.push({
name: 'Image Expansion', name: 'Image Expansion',
cb: this.node cb: this.node
}); });
}, },
node: function() { node: function() {
var thumb; var clone, thumb, _ref, _ref1;
if (!(this.file && (this.file.isImage || this.file.isVideo))) { if (!(((_ref = this.file) != null ? _ref.isImage : void 0) || ((_ref1 = this.file) != null ? _ref1.isVideo : void 0))) {
return; return;
} }
thumb = this.file.thumb; thumb = this.file.thumb;
$.on(thumb.parentNode, 'click', ImageExpand.cb.toggle); $.on(thumb.parentNode, 'click', ImageExpand.cb.toggle);
if (this.isClone) { if (this.isClone && $.hasClass(thumb, 'expanding')) {
if (this.file.isImage && this.file.isExpanding) { ImageExpand.contract(this);
ImageExpand.contract(this);
ImageExpand.expand(this);
return;
}
if (this.file.isExpanded && this.file.isVideo) {
ImageExpand.setupVideoControls(this);
return;
}
}
if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler)) {
return ImageExpand.expand(this); return ImageExpand.expand(this);
} else if (this.isClone && this.file.isExpanded && this.file.isVideo) {
clone = this;
ImageExpand.setupVideoControls(clone);
if (!clone.origin.file.fullImage.paused) {
return $.queueTask(function() {
return ImageExpand.startVideo(clone);
});
}
} else if (ImageExpand.on && !this.isHidden && (Conf['Expand spoilers'] || !this.file.isSpoiler) && (Conf['Expand videos'] || !this.file.isVideo)) {
return ImageExpand.expand(this, null, true);
} }
}, },
cb: { cb: {
@ -8020,53 +8020,40 @@
return ImageExpand.toggle(post); return ImageExpand.toggle(post);
}, },
toggleAll: function() { toggleAll: function() {
var func; var func, toggle;
$.event('CloseMenu'); $.event('CloseMenu');
toggle = function(post) {
var file;
file = post.file;
if (!(file && (file.isImage || file.isVideo) && doc.contains(post.nodes.root))) {
return;
}
if (ImageExpand.on && (!Conf['Expand spoilers'] && file.isSpoiler || !Conf['Expand videos'] && file.isVideo || Conf['Expand from here'] && Header.getTopOf(file.thumb) < 0)) {
return;
}
return $.queueTask(func, post);
};
if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) { if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) {
ImageExpand.EAI.className = 'contract-all-shortcut fa fa-compress'; ImageExpand.EAI.className = 'contract-all-shortcut fa fa-compress';
ImageExpand.EAI.title = 'Contract All Images'; ImageExpand.EAI.title = 'Contract All Images';
func = ImageExpand.expand; func = function(post) {
return ImageExpand.expand(post, null, true);
};
} else { } else {
ImageExpand.EAI.className = 'expand-all-shortcut fa fa-expand'; ImageExpand.EAI.className = 'expand-all-shortcut fa fa-expand';
ImageExpand.EAI.title = 'Expand All Images'; ImageExpand.EAI.title = 'Expand All Images';
func = ImageExpand.contract; func = ImageExpand.contract;
} }
return g.posts.forEach(function(post) { return g.posts.forEach(function(post) {
var file, _i, _len, _ref; var _i, _len, _ref;
_ref = [post].concat(post.clones); toggle(post);
_ref = post.clones;
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (_i = 0, _len = _ref.length; _i < _len; _i++) {
post = _ref[_i]; post = _ref[_i];
file = post.file; toggle(post);
if (!(file && (file.isImage || file.isVideo) && doc.contains(post.nodes.root))) {
return;
}
if (ImageExpand.on && (post.isHidden || !Conf['Expand spoilers'] && post.file.isSpoiler || !doc.contains(post.nodes.root) || Conf['Expand from here'] && Header.getTopOf(post.file.thumb) < 0)) {
return;
}
$.queueTask(func, post);
} }
}); });
}, },
playVideos: function(e) {
var fullID, play, post, _i, _len, _ref, _ref1;
_ref = g.posts;
for (fullID in _ref) {
post = _ref[fullID];
if (!(post.file && post.file.isVideo && post.file.isExpanded)) {
continue;
}
_ref1 = [post].concat(post.clones);
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
post = _ref1[_i];
play = !d.hidden && !post.isHidden && doc.contains(post.nodes.root) && Header.isNodeVisible(post.nodes.root);
if (play) {
post.file.fullImage.play();
} else {
post.file.fullImage.pause();
}
}
}
},
setFitness: function() { 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, '-'));
} }
@ -8074,7 +8061,7 @@
toggle: function(post) { toggle: function(post) {
var headRect, left, root, thumb, top, x, y, _ref; var headRect, left, root, thumb, top, x, y, _ref;
thumb = post.file.thumb; thumb = post.file.thumb;
if (!(post.file.isExpanded || post.file.isExpanding)) { if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) {
ImageExpand.expand(post); ImageExpand.expand(post);
return; return;
} }
@ -8122,10 +8109,9 @@
} }
$.rmClass(post.nodes.root, 'expanded-image'); $.rmClass(post.nodes.root, 'expanded-image');
$.rmClass(post.file.thumb, 'expanding'); $.rmClass(post.file.thumb, 'expanding');
delete post.file.isExpanding;
return post.file.isExpanded = false; return post.file.isExpanded = false;
}, },
expand: function(post, src) { expand: function(post, src, disableAutoplay) {
var el, isVideo, thumb, _ref; var el, isVideo, thumb, _ref;
_ref = post.file, thumb = _ref.thumb, isVideo = _ref.isVideo; _ref = post.file, thumb = _ref.thumb, isVideo = _ref.isVideo;
if (post.isHidden || post.file.isExpanded || $.hasClass(thumb, 'expanding')) { if (post.isHidden || post.file.isExpanded || $.hasClass(thumb, 'expanding')) {
@ -8148,42 +8134,49 @@
$.after(thumb, el); $.after(thumb, el);
} }
return $.asap((function() { return $.asap((function() {
return el.videoHeight || el.naturalHeight; if (isVideo) {
return el.videoHeight;
} else {
return el.naturalHeight;
}
}), function() { }), function() {
return ImageExpand.completeExpand(post); return ImageExpand.completeExpand(post, disableAutoplay);
}); });
}, },
completeExpand: function(post) { completeExpand: function(post, disableAutoplay) {
var bottom, complete, thumb; var bottom, thumb;
thumb = post.file.thumb; thumb = post.file.thumb;
if (!$.hasClass(thumb, 'expanding')) { if (!$.hasClass(thumb, 'expanding')) {
return; return;
} }
delete post.file.isExpanding;
post.file.isExpanded = true;
complete = function() {
$.addClass(post.nodes.root, 'expanded-image');
$.rmClass(post.file.thumb, 'expanding');
if (post.file.isVideo) {
return ImageExpand.setupVideo(post);
}
};
if (!post.nodes.root.parentNode) { if (!post.nodes.root.parentNode) {
complete(); ImageExpand.completeExpand2(post);
return; return;
} }
if (post.file.isVideo && !d.hidden && Header.isNodeVisible(post.nodes.root)) {
post.file.fullImage.play();
}
bottom = post.nodes.root.getBoundingClientRect().bottom; bottom = post.nodes.root.getBoundingClientRect().bottom;
return $.queueTask(function() { return $.queueTask(function() {
complete(); ImageExpand.completeExpand2(post, disableAutoplay);
if (!(bottom <= 0)) { if (!(bottom <= 0)) {
return; return;
} }
return window.scrollBy(0, post.nodes.root.getBoundingClientRect().bottom - bottom); return window.scrollBy(0, post.nodes.root.getBoundingClientRect().bottom - bottom);
}); });
}, },
completeExpand2: function(post, disableAutoplay) {
var thumb;
thumb = post.file.thumb;
$.addClass(post.nodes.root, 'expanded-image');
$.rmClass(post.file.thumb, 'expanding');
post.file.isExpanded = true;
if (post.file.isVideo) {
ImageExpand.setupVideoControls(post);
post.file.fullImage.muted = !Conf['Allow Sound'];
post.file.fullImage.controls = Conf['Show Controls'];
if (Conf['Autoplay'] && !disableAutoplay) {
return ImageExpand.startVideo(post);
}
}
},
videoCB: { videoCB: {
click: function(e) { click: function(e) {
if (this.paused && !this.controls) { if (this.paused && !this.controls) {
@ -8238,34 +8231,29 @@
} }
return $.add(file.text, file.videoControls); return $.add(file.text, file.videoControls);
}, },
setupVideo: function(post) { startVideo: function(post) {
var file, video; var controls, file, video;
ImageExpand.setupVideoControls(post);
file = post.file; file = post.file;
video = file.fullImage; video = file.fullImage;
video.muted = !Conf['Allow Sound']; controls = video.controls;
video.controls = Conf['Show Controls']; video.controls = false;
if (Conf['Autoplay']) { video.play();
video.controls = false; if (controls) {
video.play(); return $.asap((function() {
if (Conf['Show Controls']) { return (video.readyState >= 3 && video.currentTime <= Math.max(0.1, video.duration - 0.5)) || !file.isExpanded;
return $.asap((function() { }), function() {
return (video.readyState >= 3 && video.currentTime <= Math.max(0.1, video.duration - 0.5)) || !file.isExpanded; if (file.isExpanded) {
}), function() { return video.controls = true;
if (file.isExpanded) { }
return video.controls = true; }, 500);
}
}, 500);
}
} }
}, },
error: function() { error: function() {
var URL, post, src, timeoutID; var URL, post, src, timeoutID;
post = Get.postFromNode(this); post = Get.postFromNode(this);
post.file.isReady = false;
$.rm(this); $.rm(this);
delete post.file.fullImage; delete post.file.fullImage;
if (!(post.file.isExpanding || post.file.isExpanded)) { if (!($.hasClass(post.file.thumb, 'expanding') || $.hasClass(post.nodes.root, 'expanded-image'))) {
return; return;
} }
ImageExpand.contract(post); ImageExpand.contract(post);
@ -8284,7 +8272,7 @@
} }
} }
timeoutID = setTimeout(ImageExpand.expand, 10000, post); timeoutID = setTimeout(ImageExpand.expand, 10000, post);
return $.ajax(post.file.URL, { return $.ajax(this.src, {
onloadend: function() { onloadend: function() {
if (this.status !== 404) { if (this.status !== 404) {
return; return;
@ -8299,7 +8287,7 @@
menu: { menu: {
init: function() { init: function() {
var conf, createSubEntry, el, name, subEntries, _ref; var conf, createSubEntry, el, name, subEntries, _ref;
if (!Conf['Image Expansion']) { if (g.VIEW === 'catalog' || !Conf['Image Expansion']) {
return; return;
} }
el = $.el('span', { el = $.el('span', {
@ -12985,7 +12973,7 @@
return; return;
} }
$.event('CloseMenu'); $.event('CloseMenu');
html = "<nav><div class=sections-list></div><p class='imp-exp-result warning'></p><div class=credits><a class=export>Export</a>&nbsp|&nbsp<a class=import>Import</a>&nbsp|&nbsp<a class=reset>Reset Settings</a>&nbsp|&nbsp<input type=file hidden><a href='https://github.com/ccd0/4chan-x' target=_blank>4chan X</a> |<a href='https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md' target=_blank>" + g.VERSION + "</a> |<a href='https://github.com/ccd0/4chan-x/blob/master/README.md#reporting-bugs-and-suggestions' target=_blank>Issues</a> |<a href=javascript:; class='close fa fa-times' title=Close></a></div></nav><div class=section-container><section></section></div>"; html = "<nav><div class=sections-list></div><p class='imp-exp-result warning'></p><div class=credits><a class=export>Export</a>&nbsp|&nbsp<a class=import>Import</a>&nbsp|&nbsp<a class=reset>Reset Settings</a>&nbsp|&nbsp<input type=file hidden><a href='https://github.com/ccd0/4chan-x' target=_blank>4chan X</a>&nbsp|&nbsp<a href='https://github.com/ccd0/4chan-x/blob/master/CHANGELOG.md' target=_blank>" + g.VERSION + "</a>&nbsp|&nbsp<a href='https://github.com/ccd0/4chan-x/issues' target=_blank>Issues</a>&nbsp|&nbsp<a href=javascript:; class='close fa fa-times' title=Close></a></div></nav><div class=section-container><section></section></div>";
Settings.overlay = overlay = $.el('div', { Settings.overlay = overlay = $.el('div', {
id: 'overlay' id: 'overlay'
}); });

View File

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'> <gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<app appid='lacclbnghgdicfifcamcmcnilckjamag'> <app appid='lacclbnghgdicfifcamcmcnilckjamag'>
<updatecheck codebase='https://ccd0.github.io/4chan-x/builds/crx.crx' version='1.7.2' /> <updatecheck codebase='https://ccd0.github.io/4chan-x/builds/crx.crx' version='1.7.3' />
</app> </app>
</gupdate> </gupdate>

View File

@ -1 +1 @@
postMessage({version:'1.7.2'},'*') postMessage({version:'1.7.3'},'*')

View File

@ -1,6 +1,6 @@
{ {
"name": "4chan-X", "name": "4chan-X",
"version": "1.7.2", "version": "1.7.3",
"description": "Cross-browser userscript for maximum lurking on 4chan.", "description": "Cross-browser userscript for maximum lurking on 4chan.",
"meta": { "meta": {
"name": "4chan X", "name": "4chan X",

View File

@ -409,6 +409,10 @@ Config =
true true
'Expand all images along with spoilers.' 'Expand all images along with spoilers.'
] ]
'Expand videos': [
false
'Expand all images also expands videos (no autoplay).'
]
'Expand from here': [ 'Expand from here': [
false false
'Expand all images only from current position to thread end.' 'Expand all images only from current position to thread end.'

View File

@ -6,9 +6,9 @@
<a class=import>Import</a>&nbsp|&nbsp <a class=import>Import</a>&nbsp|&nbsp
<a class=reset>Reset Settings</a>&nbsp|&nbsp <a class=reset>Reset Settings</a>&nbsp|&nbsp
<input type=file hidden> <input type=file hidden>
<a href='<%= meta.page %>' target=_blank><%= meta.name %></a> | <a href='<%= meta.page %>' target=_blank><%= meta.name %></a>&nbsp|&nbsp
<a href='<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md' target=_blank>#{g.VERSION}</a> | <a href='<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md' target=_blank>#{g.VERSION}</a>&nbsp|&nbsp
<a href='<%= meta.repo %>blob/<%= meta.mainBranch %>/README.md#reporting-bugs-and-suggestions' target=_blank>Issues</a> | <a href='<%= meta.repo %>issues' target=_blank>Issues</a>&nbsp|&nbsp
<a href=javascript:; class='close fa fa-times' title=Close></a> <a href=javascript:; class='close fa fa-times' title=Close></a>
</div> </div>
</nav> </nav>

View File

@ -1,37 +1,36 @@
ImageExpand = ImageExpand =
init: -> init: ->
return if !Conf['Image Expansion'] return if g.VIEW is 'catalog' or !Conf['Image Expansion']
@EAI = $.el 'a', @EAI = $.el 'a',
className: 'expand-all-shortcut fa fa-expand' className: 'expand-all-shortcut fa fa-expand'
textContent: 'EAI' textContent: 'EAI'
title: 'Expand All Images' title: 'Expand All Images'
href: 'javascript:;' href: 'javascript:;'
$.on @EAI, 'click', @cb.toggleAll $.on @EAI, 'click', ImageExpand.cb.toggleAll
Header.addShortcut @EAI, 3 Header.addShortcut @EAI, 3
$.on d, 'scroll visibilitychange', @cb.playVideos
Post.callbacks.push Post.callbacks.push
name: 'Image Expansion' name: 'Image Expansion'
cb: @node cb: @node
node: -> node: ->
return unless @file and (@file.isImage or @file.isVideo) return unless @file?.isImage or @file?.isVideo
{thumb} = @file {thumb} = @file
$.on thumb.parentNode, 'click', ImageExpand.cb.toggle $.on thumb.parentNode, 'click', ImageExpand.cb.toggle
if @isClone if @isClone and $.hasClass thumb, 'expanding'
if @file.isImage and @file.isExpanding # If we clone a post where the image is still loading,
# If we clone a post where the image is still loading, # make it loading in the clone too.
# make it loading in the clone too. ImageExpand.contract @
ImageExpand.contract @
ImageExpand.expand @
return
if @file.isExpanded and @file.isVideo
ImageExpand.setupVideoControls @
return
if ImageExpand.on and !@isHidden and (Conf['Expand spoilers'] or !@file.isSpoiler)
ImageExpand.expand @ ImageExpand.expand @
else if @isClone and @file.isExpanded and @file.isVideo
clone = @
ImageExpand.setupVideoControls clone
unless clone.origin.file.fullImage.paused
$.queueTask -> ImageExpand.startVideo clone
else if ImageExpand.on and !@isHidden and
(Conf['Expand spoilers'] or !@file.isSpoiler) and
(Conf['Expand videos'] or !@file.isVideo)
ImageExpand.expand @, null, true
cb: cb:
toggle: (e) -> toggle: (e) ->
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0 return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
@ -42,42 +41,36 @@ ImageExpand =
toggleAll: -> toggleAll: ->
$.event 'CloseMenu' $.event 'CloseMenu'
toggle = (post) ->
{file} = post
return unless file and (file.isImage or file.isVideo) and doc.contains post.nodes.root
if ImageExpand.on and
(!Conf['Expand spoilers'] and file.isSpoiler or
!Conf['Expand videos'] and file.isVideo or
Conf['Expand from here'] and Header.getTopOf(file.thumb) < 0)
return
$.queueTask func, post
if ImageExpand.on = $.hasClass ImageExpand.EAI, 'expand-all-shortcut' if ImageExpand.on = $.hasClass ImageExpand.EAI, 'expand-all-shortcut'
ImageExpand.EAI.className = 'contract-all-shortcut fa fa-compress' ImageExpand.EAI.className = 'contract-all-shortcut fa fa-compress'
ImageExpand.EAI.title = 'Contract All Images' ImageExpand.EAI.title = 'Contract All Images'
func = ImageExpand.expand func = (post) -> ImageExpand.expand post, null, true
else else
ImageExpand.EAI.className = 'expand-all-shortcut fa fa-expand' ImageExpand.EAI.className = 'expand-all-shortcut fa fa-expand'
ImageExpand.EAI.title = 'Expand All Images' ImageExpand.EAI.title = 'Expand All Images'
func = ImageExpand.contract func = ImageExpand.contract
g.posts.forEach (post) -> g.posts.forEach (post) ->
for post in [post].concat post.clones toggle post
{file} = post toggle post for post in post.clones
return unless file and (file.isImage or file.isVideo) and doc.contains post.nodes.root
if ImageExpand.on and (
post.isHidden or
!Conf['Expand spoilers'] and post.file.isSpoiler or
!doc.contains(post.nodes.root) or
Conf['Expand from here'] and Header.getTopOf(post.file.thumb) < 0)
return
$.queueTask func, post
return return
playVideos: (e) ->
for fullID, post of g.posts
continue unless post.file and post.file.isVideo and post.file.isExpanded
for post in [post].concat post.clones
play = !d.hidden and !post.isHidden and doc.contains(post.nodes.root) and Header.isNodeVisible post.nodes.root
if play then post.file.fullImage.play() else post.file.fullImage.pause()
return
setFitness: -> 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) -> toggle: (post) ->
{thumb} = post.file {thumb} = post.file
unless post.file.isExpanded or post.file.isExpanding unless post.file.isExpanded or $.hasClass thumb, 'expanding'
ImageExpand.expand post ImageExpand.expand post
return return
@ -117,10 +110,9 @@ ImageExpand =
delete post.file.videoControls delete post.file.videoControls
$.rmClass post.nodes.root, 'expanded-image' $.rmClass post.nodes.root, 'expanded-image'
$.rmClass post.file.thumb, 'expanding' $.rmClass post.file.thumb, 'expanding'
delete post.file.isExpanding
post.file.isExpanded = false post.file.isExpanded = false
expand: (post, src) -> expand: (post, src, disableAutoplay) ->
# Do not expand images of hidden/filtered replies, or already expanded pictures. # Do not expand images of hidden/filtered replies, or already expanded pictures.
{thumb, isVideo} = post.file {thumb, isVideo} = post.file
return if post.isHidden or post.file.isExpanded or $.hasClass thumb, 'expanding' return if post.isHidden or post.file.isExpanded or $.hasClass thumb, 'expanding'
@ -135,33 +127,34 @@ ImageExpand =
$.on el, 'error', ImageExpand.error $.on el, 'error', ImageExpand.error
el.src = src or post.file.URL el.src = src or post.file.URL
$.after thumb, el unless el is thumb.nextSibling $.after thumb, el unless el is thumb.nextSibling
$.asap (-> el.videoHeight or el.naturalHeight), -> $.asap (-> if isVideo then el.videoHeight else el.naturalHeight), ->
ImageExpand.completeExpand post ImageExpand.completeExpand post, disableAutoplay
completeExpand: (post) -> completeExpand: (post, disableAutoplay) ->
{thumb} = post.file {thumb} = post.file
return unless $.hasClass thumb, 'expanding' # contracted before the image loaded return unless $.hasClass thumb, 'expanding' # contracted before the image loaded
delete post.file.isExpanding
post.file.isExpanded = true
complete = ->
$.addClass post.nodes.root, 'expanded-image'
$.rmClass post.file.thumb, 'expanding'
ImageExpand.setupVideo post if post.file.isVideo
unless post.nodes.root.parentNode unless post.nodes.root.parentNode
# Image might start/finish loading before the post is inserted. # Image might start/finish loading before the post is inserted.
# Don't scroll when it's expanded in a QP for example. # Don't scroll when it's expanded in a QP for example.
complete() ImageExpand.completeExpand2 post
return return
post.file.fullImage.play() if post.file.isVideo and !d.hidden and Header.isNodeVisible post.nodes.root
{bottom} = post.nodes.root.getBoundingClientRect() {bottom} = post.nodes.root.getBoundingClientRect()
$.queueTask -> $.queueTask ->
complete() ImageExpand.completeExpand2 post, disableAutoplay
return unless bottom <= 0 return unless bottom <= 0
window.scrollBy 0, post.nodes.root.getBoundingClientRect().bottom - bottom window.scrollBy 0, post.nodes.root.getBoundingClientRect().bottom - bottom
completeExpand2: (post, disableAutoplay) ->
{thumb} = post.file
$.addClass post.nodes.root, 'expanded-image'
$.rmClass post.file.thumb, 'expanding'
post.file.isExpanded = true
if post.file.isVideo
ImageExpand.setupVideoControls post
post.file.fullImage.muted = !Conf['Allow Sound']
post.file.fullImage.controls = Conf['Show Controls']
ImageExpand.startVideo post if Conf['Autoplay'] and not disableAutoplay
videoCB: videoCB:
click: (e) -> click: (e) ->
if @paused and not @controls if @paused and not @controls
@ -170,9 +163,9 @@ ImageExpand =
# dragging to the left contracts the video # dragging to the left contracts the video
mousedown: (e) -> @dataset.mousedown = 'true' if e.button is 0 mousedown: (e) -> @dataset.mousedown = 'true' if e.button is 0
mouseup: (e) -> @dataset.mousedown = 'false' if e.button is 0 mouseup: (e) -> @dataset.mousedown = 'false' if e.button is 0
mouseover: (e) -> @dataset.mousedown = 'false' mouseover: (e) -> @dataset.mousedown = 'false'
mouseout: (e) -> mouseout: (e) ->
if @dataset.mousedown is 'true' and e.clientX <= @getBoundingClientRect().left if @dataset.mousedown is 'true' and e.clientX <= @getBoundingClientRect().left
ImageExpand.contract (Get.postFromNode @) ImageExpand.contract (Get.postFromNode @)
@ -200,30 +193,26 @@ ImageExpand =
$.add file.videoControls, [$.tn('\u00A0'), contract] $.add file.videoControls, [$.tn('\u00A0'), contract]
$.add file.text, file.videoControls $.add file.text, file.videoControls
setupVideo: (post) -> startVideo: (post) ->
ImageExpand.setupVideoControls post
{file} = post {file} = post
video = file.fullImage video = file.fullImage
video.muted = !Conf['Allow Sound'] {controls} = video
video.controls = Conf['Show Controls'] video.controls = false
if Conf['Autoplay'] video.play()
video.controls = false # Hacky workaround for Firefox forever-loading bug for very short videos
video.play() if controls
# Hacky workaround for Firefox forever-loading bug for very short videos $.asap (-> (video.readyState >= 3 and video.currentTime <= Math.max 0.1, (video.duration - 0.5)) or !file.isExpanded), ->
if Conf['Show Controls'] video.controls = true if file.isExpanded
$.asap (-> (video.readyState >= 3 and video.currentTime <= Math.max 0.1, (video.duration - 0.5)) or !file.isExpanded), -> , 500
video.controls = true if file.isExpanded
, 500
error: -> error: ->
post = Get.postFromNode @ post = Get.postFromNode @
post.file.isReady = false
$.rm @ $.rm @
delete post.file.fullImage delete post.file.fullImage
# Images can error: # Images can error:
# - before the image started loading. # - before the image started loading.
# - after the image started loading. # - after the image started loading.
unless post.file.isExpanding or post.file.isExpanded unless $.hasClass(post.file.thumb, 'expanding') or $.hasClass post.nodes.root, 'expanded-image'
# Don't try to re-expend if it was already contracted. # Don't try to re-expend if it was already contracted.
return return
ImageExpand.contract post ImageExpand.contract post
@ -241,7 +230,7 @@ ImageExpand =
timeoutID = setTimeout ImageExpand.expand, 10000, post timeoutID = setTimeout ImageExpand.expand, 10000, post
<% if (type === 'crx') { %> <% if (type === 'crx') { %>
$.ajax post.file.URL, $.ajax @src,
onloadend: -> onloadend: ->
return if @status isnt 404 return if @status isnt 404
clearTimeout timeoutID clearTimeout timeoutID
@ -264,7 +253,7 @@ ImageExpand =
menu: menu:
init: -> init: ->
return if !Conf['Image Expansion'] return if g.VIEW is 'catalog' or !Conf['Image Expansion']
el = $.el 'span', el = $.el 'span',
textContent: 'Image Expansion' textContent: 'Image Expansion'