Merge branch 'v3'

Conflicts:
	CHANGELOG.md
	LICENSE
	builds/4chan-X.js
	builds/4chan-X.meta.js
	builds/4chan-X.user.js
	builds/crx/manifest.json
	builds/crx/script.js
	latest.js
	package.json
	src/General/Main.coffee
	src/General/css/style.css
This commit is contained in:
Zixaphir 2013-05-27 14:22:31 -07:00
commit ca4be499a3
14 changed files with 311 additions and 128 deletions

View File

@ -1,7 +1,15 @@
**MayhemYDG**:
- Tiny fixes
- Add page count to thread stats
- Better performance for Fit Height by using vh
**seaweedchan**:
- Added OpenSettings event on 4chan X settings/sections open for userscripts like OneeChan and 4chan Style Script
- Changed defaults that use the arrow keys to shift+arrow key to not conflict with scrolling
- Made Mayhem's page count in thread stats optional
- Small bug fixes
- Fix YouTube videos in Firefox taking z-index priority
- Fix Persistent QR not working for /f/
- New image expansion option: `Advance on contract`. Advances to next post unless Fappe Tyme is enabled (temporary)
- Change `.qr-link` to `.qr-link-container` and `.qr-link>a` to `.qr-link`
- Update /q/'s posting cooldown
@ -16,6 +24,10 @@
**Wohlfe**:
- Add /pol/ archiving for FoolzaShit
**zixaphir**:
- New option: `Image Prefetching`. Adds a toggle to the header menu for per-thread prefetching.
- Make Advance on contract work with Fappe Tyme
### v2.0.4
*2013-05-15*
**MayhemYDG**:

View File

@ -1,5 +1,5 @@
/*
* appchan x - Version 2.0.4 - 2013-05-23
* appchan x - Version 2.0.4 - 2013-05-27
*
* Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE

View File

@ -20,7 +20,7 @@
// ==/UserScript==
/*
* appchan x - Version 2.0.4 - 2013-05-23
* appchan x - Version 2.0.4 - 2013-05-27
*
* Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@ -186,6 +186,7 @@
'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.'],
'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'],
'Thread Stats': [true, 'Display reply and image count.'],
'Page Count in Stats': [false, 'Display the page count in the thread stats as well.'],
'Updater and Stats in Header': [true, 'Places the thread updater and thread stats in the header instead of floating them.'],
'Thread Watcher': [true, 'Bookmark threads.'],
'Toggleable Thread Watcher': [false, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'],
@ -350,7 +351,7 @@
},
time: '%m/%d/%y(%a)%H:%M:%S',
backlink: '>>%id',
fileInfo: '%l (%p%s, %r)',
fileInfo: '%L (%p%s, %r)',
favicon: 'ferongr',
usercss: "/* Tripcode Italics: */\n/*\nspan.postertrip {\nfont-style: italic;\n}\n*/\n\n/* Add a rounded border to thumbnails (but not expanded images): */\n/*\n.fileThumb > img:first-child {\nborder: solid 2px rgba(0,0,100,0.5);\nborder-radius: 10px;\n}\n*/\n\n/* Make highlighted posts look inset on the page: */\n/*\ndiv.post:target,\ndiv.post.highlight {\nbox-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);\n}\n*/",
hotkeys: {
@ -373,11 +374,11 @@
'fappeTyme': ['f', 'Fappe Tyme.'],
'Front page': ['0', 'Jump to page 0.'],
'Open front page': ['Shift+0', 'Open page 0 in a new tab.'],
'Next page': ['Right', 'Jump to the next page.'],
'Previous page': ['Left', 'Jump to the previous page.'],
'Next page': ['Shift+Right', 'Jump to the next page.'],
'Previous page': ['Shift+Left', 'Jump to the previous page.'],
'Open catalog': ['Shift+c', 'Open the catalog of the current board'],
'Next thread': ['Down', 'See next thread.'],
'Previous thread': ['Up', 'See previous thread.'],
'Next thread': ['Shift+Down', 'See next thread.'],
'Previous thread': ['Shift+Up', 'See previous thread.'],
'Expand thread': ['Ctrl+e', 'Expand thread.'],
'Open thread': ['o', 'Open thread in current tab.'],
'Open thread tab': ['Shift+o', 'Open thread in new tab.'],
@ -4164,7 +4165,7 @@
}
flag = flagCode ? (" <img src='" + staticPath + "country/" + (boardID === 'pol' ? 'troll/' : '')) + flagCode.toLowerCase() + (".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>") : '';
if (file != null ? file.isDeleted : void 0) {
fileHtml = isOP ? ("<div class=file id=f" + data.no + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + data.no + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
fileHtml = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
} else if (file) {
ext = file.name.slice(-3);
if (!file.twidth && !file.theight && ext === 'gif') {
@ -6671,7 +6672,7 @@
regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/,
el: function() {
return $.el('iframe', {
src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '')
src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque"
});
},
title: {
@ -6688,7 +6689,7 @@
style: 'border: 0; width: 150px; height: 45px;',
el: function() {
return $.el('object', {
innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
});
}
},
@ -6696,7 +6697,7 @@
regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/,
el: function() {
return $.el('iframe', {
src: "//player.vimeo.com/video/" + this.name
src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque"
});
},
title: {
@ -6711,8 +6712,8 @@
LiveLeak: {
regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/,
el: function() {
return $.el('iframe', {
src: "http://www.liveleak.com/e/" + this.name + "?autostart=true"
return $.el('object', {
innerHTML: "<embed src='http://www.liveleak.com/e/" + this.name + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
});
}
},
@ -6885,7 +6886,11 @@
this.db = new DataBoard('yourPosts');
$.ready(this.initReady);
if (Conf['Persistent QR']) {
$.on(d, '4chanXInitFinished', this.persist);
if (g.BOARD.ID !== 'f') {
$.on(d, '4chanXInitFinished', this.persist);
} else {
$.ready(this.persist);
}
}
Post.prototype.callbacks.push({
name: 'Quick Reply',
@ -7150,6 +7155,9 @@
list = $("#list-" + type, QR.nodes.el);
for (_i = 0, _len = arr.length; _i < _len; _i++) {
val = arr[_i];
if (!val) {
continue;
}
$.add(list, $.el('option', {
textContent: val
}));
@ -8456,14 +8464,11 @@
}
},
setFitness: function() {
var checked;
checked = this.checked;
(checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-'));
(this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-'));
if (this.name !== 'Fit height') {
return;
}
if (checked) {
if (this.checked) {
$.on(window, 'resize', ImageExpand.resize);
if (!ImageExpand.style) {
ImageExpand.style = $.addStyle(null);
@ -8474,6 +8479,9 @@
}
}
},
resize: function() {
return ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:" + doc.clientHeight + "px}";
},
toggle: function(post) {
var headRect, node, rect, root, thumb, top;
@ -8669,9 +8677,6 @@
};
}
},
resize: function() {
return ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:" + doc.clientHeight + "px}";
},
menuToggle: function(e) {
return ImageExpand.opmenu.toggle(e, this, g);
}
@ -8792,7 +8797,7 @@
return $.event('AddMenuEntry', {
type: 'header',
el: prefetch,
order: 120
order: 104
});
},
node: function() {
@ -9278,20 +9283,23 @@
}
if (Conf['Updater and Stats in Header']) {
this.dialog = sc = $.el('span', {
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>",
id: 'thread-stats'
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>" + (Conf["Page Count in Stats"] ? " / <span id=page-count>0</span>" : ""),
id: 'thread-stats',
title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "")
});
$.ready(function() {
return Header.addShortcut(sc);
});
} else {
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move><span id=post-count>0</span> / <span id=file-count>0</span></div>");
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move title='Post Count / File Count" + (Conf["Page Count in Stats"] ? " / Page Count" : "") + "'><span id=post-count>0</span> / <span id=file-count>0</span>" + (Conf["Page Count in Stats"] ? " / <span id=page-count>0</span>" : "") + "</div>");
$.ready(function() {
return $.add(d.body, sc);
});
}
this.postCountEl = $('#post-count', sc);
this.fileCountEl = $('#file-count', sc);
this.pageCountEl = $('#page-count', sc);
this.lastModified = '0';
return Thread.prototype.callbacks.push({
name: 'Thread Stats',
cb: this.node
@ -9311,6 +9319,7 @@
}
}
ThreadStats.thread = this;
ThreadStats.fetchPage();
ThreadStats.update(postCount, fileCount);
return $.on(d, 'ThreadUpdate', ThreadStats.onUpdate);
},
@ -9331,6 +9340,43 @@
fileCountEl.textContent = fileCount;
(thread.postLimit && !thread.isSticky ? $.addClass : $.rmClass)(postCountEl, 'warning');
return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning');
},
fetchPage: function() {
if (ThreadStats.thread.isDead || !Conf["Page Count in Stats"]) {
return;
}
setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE);
return $.ajax("//api.4chan.org/" + ThreadStats.thread.board + "/threads.json", {
onload: ThreadStats.onThreadsLoad
}, {
headers: {
'If-Modified-Since': ThreadStats.lastModified
}
});
},
onThreadsLoad: function() {
var page, pages, thread, _i, _j, _len, _len1, _ref;
if (!Conf["Page Count in Stats"]) {
return;
}
ThreadStats.lastModified = this.getResponseHeader('Last-Modified');
if (this.status !== 200) {
return;
}
pages = JSON.parse(this.response);
for (_i = 0, _len = pages.length; _i < _len; _i++) {
page = pages[_i];
_ref = page.threads;
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
thread = _ref[_j];
if (thread.no === ThreadStats.thread.ID) {
ThreadStats.pageCountEl.textContent = page.page;
(page.page === pages.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
return;
}
}
}
}
};
@ -12766,11 +12812,17 @@
$.open("/" + g.BOARD + "/#delform");
break;
case Conf['Next page']:
if (g.VIEW === 'thread') {
return;
}
if (form = $('.next form')) {
window.location = form.action;
}
break;
case Conf['Previous page']:
if (g.VIEW === 'thread') {
return;
}
if (form = $('.prev form')) {
window.location = form.action;
}
@ -13485,7 +13537,8 @@
(sectionToOpen ? sectionToOpen : links[0]).click();
$.on($('.close', dialog), 'click', Settings.close);
$.on(overlay, 'click', Settings.close);
return $.add(d.body, [overlay, dialog]);
$.add(d.body, [overlay, dialog]);
return $.event('OpenSettings', null, dialog);
},
close: function() {
if (!Settings.dialog) {
@ -13521,7 +13574,8 @@
$.rmAll(section);
section.className = "section-" + this.hyphenatedTitle;
this.open(section, mode);
return section.scrollTop = 0;
section.scrollTop = 0;
return $.event('OpenSettings', null, section);
},
main: function(section) {
var arr, button, description, div, fs, hiddenNum, input, inputs, items, key, obj, _ref;

View File

@ -20,7 +20,7 @@
// ==/UserScript==
/*
* appchan x - Version 2.0.4 - 2013-05-23
* appchan x - Version 2.0.4 - 2013-05-27
*
* Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@ -186,6 +186,7 @@
'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.'],
'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'],
'Thread Stats': [true, 'Display reply and image count.'],
'Page Count in Stats': [false, 'Display the page count in the thread stats as well.'],
'Updater and Stats in Header': [true, 'Places the thread updater and thread stats in the header instead of floating them.'],
'Thread Watcher': [true, 'Bookmark threads.'],
'Toggleable Thread Watcher': [false, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'],
@ -351,7 +352,7 @@
},
time: '%m/%d/%y(%a)%H:%M:%S',
backlink: '>>%id',
fileInfo: '%l (%p%s, %r)',
fileInfo: '%L (%p%s, %r)',
favicon: 'ferongr',
usercss: "/* Tripcode Italics: */\n/*\nspan.postertrip {\nfont-style: italic;\n}\n*/\n\n/* Add a rounded border to thumbnails (but not expanded images): */\n/*\n.fileThumb > img:first-child {\nborder: solid 2px rgba(0,0,100,0.5);\nborder-radius: 10px;\n}\n*/\n\n/* Make highlighted posts look inset on the page: */\n/*\ndiv.post:target,\ndiv.post.highlight {\nbox-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);\n}\n*/",
hotkeys: {
@ -374,11 +375,11 @@
'fappeTyme': ['f', 'Fappe Tyme.'],
'Front page': ['0', 'Jump to page 0.'],
'Open front page': ['Shift+0', 'Open page 0 in a new tab.'],
'Next page': ['Right', 'Jump to the next page.'],
'Previous page': ['Left', 'Jump to the previous page.'],
'Next page': ['Shift+Right', 'Jump to the next page.'],
'Previous page': ['Shift+Left', 'Jump to the previous page.'],
'Open catalog': ['Shift+c', 'Open the catalog of the current board'],
'Next thread': ['Down', 'See next thread.'],
'Previous thread': ['Up', 'See previous thread.'],
'Next thread': ['Shift+Down', 'See next thread.'],
'Previous thread': ['Shift+Up', 'See previous thread.'],
'Expand thread': ['Ctrl+e', 'Expand thread.'],
'Open thread': ['o', 'Open thread in current tab.'],
'Open thread tab': ['Shift+o', 'Open thread in new tab.'],
@ -4160,7 +4161,7 @@
}
flag = flagCode ? (" <img src='" + staticPath + "country/" + (boardID === 'pol' ? 'troll/' : '')) + flagCode.toLowerCase() + (".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>") : '';
if (file != null ? file.isDeleted : void 0) {
fileHtml = isOP ? ("<div class=file id=f" + data.no + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + data.no + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
fileHtml = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
} else if (file) {
ext = file.name.slice(-3);
if (!file.twidth && !file.theight && ext === 'gif') {
@ -6655,7 +6656,7 @@
regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/,
el: function() {
return $.el('iframe', {
src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '')
src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque"
});
},
title: {
@ -6672,7 +6673,7 @@
style: 'border: 0; width: 150px; height: 45px;',
el: function() {
return $.el('object', {
innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
});
}
},
@ -6680,7 +6681,7 @@
regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/,
el: function() {
return $.el('iframe', {
src: "//player.vimeo.com/video/" + this.name
src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque"
});
},
title: {
@ -6695,8 +6696,8 @@
LiveLeak: {
regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/,
el: function() {
return $.el('iframe', {
src: "http://www.liveleak.com/e/" + this.name + "?autostart=true"
return $.el('object', {
innerHTML: "<embed src='http://www.liveleak.com/e/" + this.name + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
});
}
},
@ -6869,7 +6870,11 @@
this.db = new DataBoard('yourPosts');
$.ready(this.initReady);
if (Conf['Persistent QR']) {
$.on(d, '4chanXInitFinished', this.persist);
if (g.BOARD.ID !== 'f') {
$.on(d, '4chanXInitFinished', this.persist);
} else {
$.ready(this.persist);
}
}
Post.prototype.callbacks.push({
name: 'Quick Reply',
@ -7134,6 +7139,9 @@
list = $("#list-" + type, QR.nodes.el);
for (_i = 0, _len = arr.length; _i < _len; _i++) {
val = arr[_i];
if (!val) {
continue;
}
$.add(list, $.el('option', {
textContent: val
}));
@ -8465,22 +8473,7 @@
}
},
setFitness: function() {
var checked;
checked = this.checked;
(checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-'));
if (this.name !== 'Fit height') {
return;
}
if (checked) {
$.on(window, 'resize', ImageExpand.resize);
if (!ImageExpand.style) {
ImageExpand.style = $.addStyle(null);
}
return ImageExpand.resize();
} else {
return $.off(window, 'resize', ImageExpand.resize);
}
return (this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-'));
}
},
toggle: function(post) {
@ -8678,9 +8671,6 @@
};
}
},
resize: function() {
return ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:" + doc.clientHeight + "px}";
},
menuToggle: function(e) {
return ImageExpand.opmenu.toggle(e, this, g);
}
@ -8801,7 +8791,7 @@
return $.event('AddMenuEntry', {
type: 'header',
el: prefetch,
order: 120
order: 104
});
},
node: function() {
@ -9287,20 +9277,23 @@
}
if (Conf['Updater and Stats in Header']) {
this.dialog = sc = $.el('span', {
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>",
id: 'thread-stats'
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>" + (Conf["Page Count in Stats"] ? " / <span id=page-count>0</span>" : ""),
id: 'thread-stats',
title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "")
});
$.ready(function() {
return Header.addShortcut(sc);
});
} else {
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move><span id=post-count>0</span> / <span id=file-count>0</span></div>");
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move title='Post Count / File Count" + (Conf["Page Count in Stats"] ? " / Page Count" : "") + "'><span id=post-count>0</span> / <span id=file-count>0</span>" + (Conf["Page Count in Stats"] ? " / <span id=page-count>0</span>" : "") + "</div>");
$.ready(function() {
return $.add(d.body, sc);
});
}
this.postCountEl = $('#post-count', sc);
this.fileCountEl = $('#file-count', sc);
this.pageCountEl = $('#page-count', sc);
this.lastModified = '0';
return Thread.prototype.callbacks.push({
name: 'Thread Stats',
cb: this.node
@ -9320,6 +9313,7 @@
}
}
ThreadStats.thread = this;
ThreadStats.fetchPage();
ThreadStats.update(postCount, fileCount);
return $.on(d, 'ThreadUpdate', ThreadStats.onUpdate);
},
@ -9340,6 +9334,43 @@
fileCountEl.textContent = fileCount;
(thread.postLimit && !thread.isSticky ? $.addClass : $.rmClass)(postCountEl, 'warning');
return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning');
},
fetchPage: function() {
if (ThreadStats.thread.isDead || !Conf["Page Count in Stats"]) {
return;
}
setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE);
return $.ajax("//api.4chan.org/" + ThreadStats.thread.board + "/threads.json", {
onload: ThreadStats.onThreadsLoad
}, {
headers: {
'If-Modified-Since': ThreadStats.lastModified
}
});
},
onThreadsLoad: function() {
var page, pages, thread, _i, _j, _len, _len1, _ref;
if (!Conf["Page Count in Stats"]) {
return;
}
ThreadStats.lastModified = this.getResponseHeader('Last-Modified');
if (this.status !== 200) {
return;
}
pages = JSON.parse(this.response);
for (_i = 0, _len = pages.length; _i < _len; _i++) {
page = pages[_i];
_ref = page.threads;
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
thread = _ref[_j];
if (thread.no === ThreadStats.thread.ID) {
ThreadStats.pageCountEl.textContent = page.page;
(page.page === pages.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
return;
}
}
}
}
};
@ -12775,11 +12806,17 @@
$.open("/" + g.BOARD + "/#delform");
break;
case Conf['Next page']:
if (g.VIEW === 'thread') {
return;
}
if (form = $('.next form')) {
window.location = form.action;
}
break;
case Conf['Previous page']:
if (g.VIEW === 'thread') {
return;
}
if (form = $('.prev form')) {
window.location = form.action;
}
@ -13494,7 +13531,8 @@
(sectionToOpen ? sectionToOpen : links[0]).click();
$.on($('.close', dialog), 'click', Settings.close);
$.on(overlay, 'click', Settings.close);
return $.add(d.body, [overlay, dialog]);
$.add(d.body, [overlay, dialog]);
return $.event('OpenSettings', null, dialog);
},
close: function() {
if (!Settings.dialog) {
@ -13530,7 +13568,8 @@
$.rmAll(section);
section.className = "section-" + this.hyphenatedTitle;
this.open(section, mode);
return section.scrollTop = 0;
section.scrollTop = 0;
return $.event('OpenSettings', null, section);
},
main: function(section) {
var arr, button, description, div, fs, hiddenNum, input, inputs, items, key, obj, _ref;

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript
/*
* appchan x - Version 2.0.4 - 2013-05-23
* appchan x - Version 2.0.4 - 2013-05-27
*
* Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@ -167,6 +167,7 @@
'Scroll to Last Read Post': [true, 'Scroll back to the last read post when reopening a thread.'],
'Thread Excerpt': [true, 'Show an excerpt of the thread in the tab title.'],
'Thread Stats': [true, 'Display reply and image count.'],
'Page Count in Stats': [false, 'Display the page count in the thread stats as well.'],
'Updater and Stats in Header': [true, 'Places the thread updater and thread stats in the header instead of floating them.'],
'Thread Watcher': [true, 'Bookmark threads.'],
'Toggleable Thread Watcher': [false, 'Adds a shortcut for the thread watcher, hides the watcher by default, and makes it scroll with the page.'],
@ -331,7 +332,7 @@
},
time: '%m/%d/%y(%a)%H:%M:%S',
backlink: '>>%id',
fileInfo: '%l (%p%s, %r)',
fileInfo: '%L (%p%s, %r)',
favicon: 'ferongr',
usercss: "/* Tripcode Italics: */\n/*\nspan.postertrip {\nfont-style: italic;\n}\n*/\n\n/* Add a rounded border to thumbnails (but not expanded images): */\n/*\n.fileThumb > img:first-child {\nborder: solid 2px rgba(0,0,100,0.5);\nborder-radius: 10px;\n}\n*/\n\n/* Make highlighted posts look inset on the page: */\n/*\ndiv.post:target,\ndiv.post.highlight {\nbox-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);\n}\n*/",
hotkeys: {
@ -354,11 +355,11 @@
'fappeTyme': ['f', 'Fappe Tyme.'],
'Front page': ['0', 'Jump to page 0.'],
'Open front page': ['Shift+0', 'Open page 0 in a new tab.'],
'Next page': ['Right', 'Jump to the next page.'],
'Previous page': ['Left', 'Jump to the previous page.'],
'Next page': ['Shift+Right', 'Jump to the next page.'],
'Previous page': ['Shift+Left', 'Jump to the previous page.'],
'Open catalog': ['Shift+c', 'Open the catalog of the current board'],
'Next thread': ['Down', 'See next thread.'],
'Previous thread': ['Up', 'See previous thread.'],
'Next thread': ['Shift+Down', 'See next thread.'],
'Previous thread': ['Shift+Up', 'See previous thread.'],
'Expand thread': ['Ctrl+e', 'Expand thread.'],
'Open thread': ['o', 'Open thread in current tab.'],
'Open thread tab': ['Shift+o', 'Open thread in new tab.'],
@ -4161,7 +4162,7 @@
}
flag = flagCode ? (" <img src='" + staticPath + "country/" + (boardID === 'pol' ? 'troll/' : '')) + flagCode.toLowerCase() + (".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>") : '';
if (file != null ? file.isDeleted : void 0) {
fileHtml = isOP ? ("<div class=file id=f" + data.no + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + data.no + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
fileHtml = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
} else if (file) {
ext = file.name.slice(-3);
if (!file.twidth && !file.theight && ext === 'gif') {
@ -6656,7 +6657,7 @@
regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/,
el: function() {
return $.el('iframe', {
src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '')
src: "//www.youtube.com/embed/" + this.name + (this.option ? '#' + this.option : '') + "?wmode=opaque"
});
},
title: {
@ -6673,7 +6674,7 @@
style: 'border: 0; width: 150px; height: 45px;',
el: function() {
return $.el('object', {
innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=" + (this.name.replace(/^i\//, '')) + "&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
});
}
},
@ -6681,7 +6682,7 @@
regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/,
el: function() {
return $.el('iframe', {
src: "//player.vimeo.com/video/" + this.name
src: "//player.vimeo.com/video/" + this.name + "?wmode=opaque"
});
},
title: {
@ -6696,8 +6697,8 @@
LiveLeak: {
regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/,
el: function() {
return $.el('iframe', {
src: "http://www.liveleak.com/e/" + this.name + "?autostart=true"
return $.el('object', {
innerHTML: "<embed src='http://www.liveleak.com/e/" + this.name + "?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
});
}
},
@ -6870,7 +6871,11 @@
this.db = new DataBoard('yourPosts');
$.ready(this.initReady);
if (Conf['Persistent QR']) {
$.on(d, '4chanXInitFinished', this.persist);
if (g.BOARD.ID !== 'f') {
$.on(d, '4chanXInitFinished', this.persist);
} else {
$.ready(this.persist);
}
}
Post.prototype.callbacks.push({
name: 'Quick Reply',
@ -7136,6 +7141,9 @@
list = $("#list-" + type, QR.nodes.el);
for (_i = 0, _len = arr.length; _i < _len; _i++) {
val = arr[_i];
if (!val) {
continue;
}
$.add(list, $.el('option', {
textContent: val
}));
@ -8442,22 +8450,7 @@
}
},
setFitness: function() {
var checked;
checked = this.checked;
(checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-'));
if (this.name !== 'Fit height') {
return;
}
if (checked) {
$.on(window, 'resize', ImageExpand.resize);
if (!ImageExpand.style) {
ImageExpand.style = $.addStyle(null);
}
return ImageExpand.resize();
} else {
return $.off(window, 'resize', ImageExpand.resize);
}
return (this.checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-'));
}
},
toggle: function(post) {
@ -8655,9 +8648,6 @@
};
}
},
resize: function() {
return ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:" + doc.clientHeight + "px}";
},
menuToggle: function(e) {
return ImageExpand.opmenu.toggle(e, this, g);
}
@ -8778,7 +8768,7 @@
return $.event('AddMenuEntry', {
type: 'header',
el: prefetch,
order: 120
order: 104
});
},
node: function() {
@ -9264,20 +9254,23 @@
}
if (Conf['Updater and Stats in Header']) {
this.dialog = sc = $.el('span', {
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>",
id: 'thread-stats'
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>" + (Conf["Page Count in Stats"] ? " / <span id=page-count>0</span>" : ""),
id: 'thread-stats',
title: 'Post Count / File Count' + (Conf["Page Count in Stats"] ? " / Page Count" : "")
});
$.ready(function() {
return Header.addShortcut(sc);
});
} else {
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move><span id=post-count>0</span> / <span id=file-count>0</span></div>");
this.dialog = sc = UI.dialog('thread-stats', 'bottom: 0px; right: 0px;', "<div class=move title='Post Count / File Count" + (Conf["Page Count in Stats"] ? " / Page Count" : "") + "'><span id=post-count>0</span> / <span id=file-count>0</span>" + (Conf["Page Count in Stats"] ? " / <span id=page-count>0</span>" : "") + "</div>");
$.ready(function() {
return $.add(d.body, sc);
});
}
this.postCountEl = $('#post-count', sc);
this.fileCountEl = $('#file-count', sc);
this.pageCountEl = $('#page-count', sc);
this.lastModified = '0';
return Thread.prototype.callbacks.push({
name: 'Thread Stats',
cb: this.node
@ -9297,6 +9290,7 @@
}
}
ThreadStats.thread = this;
ThreadStats.fetchPage();
ThreadStats.update(postCount, fileCount);
return $.on(d, 'ThreadUpdate', ThreadStats.onUpdate);
},
@ -9317,6 +9311,43 @@
fileCountEl.textContent = fileCount;
(thread.postLimit && !thread.isSticky ? $.addClass : $.rmClass)(postCountEl, 'warning');
return (thread.fileLimit && !thread.isSticky ? $.addClass : $.rmClass)(fileCountEl, 'warning');
},
fetchPage: function() {
if (ThreadStats.thread.isDead || !Conf["Page Count in Stats"]) {
return;
}
setTimeout(ThreadStats.fetchPage, 2 * $.MINUTE);
return $.ajax("//api.4chan.org/" + ThreadStats.thread.board + "/threads.json", {
onload: ThreadStats.onThreadsLoad
}, {
headers: {
'If-Modified-Since': ThreadStats.lastModified
}
});
},
onThreadsLoad: function() {
var page, pages, thread, _i, _j, _len, _len1, _ref;
if (!Conf["Page Count in Stats"]) {
return;
}
ThreadStats.lastModified = this.getResponseHeader('Last-Modified');
if (this.status !== 200) {
return;
}
pages = JSON.parse(this.response);
for (_i = 0, _len = pages.length; _i < _len; _i++) {
page = pages[_i];
_ref = page.threads;
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
thread = _ref[_j];
if (thread.no === ThreadStats.thread.ID) {
ThreadStats.pageCountEl.textContent = page.page;
(page.page === pages.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
return;
}
}
}
}
};
@ -12757,11 +12788,17 @@
$.open("/" + g.BOARD + "/#delform");
break;
case Conf['Next page']:
if (g.VIEW === 'thread') {
return;
}
if (form = $('.next form')) {
window.location = form.action;
}
break;
case Conf['Previous page']:
if (g.VIEW === 'thread') {
return;
}
if (form = $('.prev form')) {
window.location = form.action;
}
@ -13476,7 +13513,8 @@
(sectionToOpen ? sectionToOpen : links[0]).click();
$.on($('.close', dialog), 'click', Settings.close);
$.on(overlay, 'click', Settings.close);
return $.add(d.body, [overlay, dialog]);
$.add(d.body, [overlay, dialog]);
return $.event('OpenSettings', null, dialog);
},
close: function() {
if (!Settings.dialog) {
@ -13512,7 +13550,8 @@
$.rmAll(section);
section.className = "section-" + this.hyphenatedTitle;
this.open(section, mode);
return section.scrollTop = 0;
section.scrollTop = 0;
return $.event('OpenSettings', null, section);
},
main: function(section) {
var arr, button, description, div, fs, hiddenNum, input, inputs, items, key, obj, _ref;

View File

@ -117,11 +117,11 @@ Build =
if file?.isDeleted
fileHtml = if isOP
"<div class=file id=f#{data.no}><div class=fileInfo></div><span class=fileThumb>" +
"<div class=file id=f#{postID}><div class=fileInfo></div><span class=fileThumb>" +
"<img src='#{staticPath}filedeleted.gif' alt='File deleted.' class=fileDeletedRes>" +
"</span></div>"
else
"<div class=file id=f#{data.no}><span class=fileThumb>" +
"<div class=file id=f#{postID}><span class=fileThumb>" +
"<img src='#{staticPath}filedeleted-res.gif' alt='File deleted.' class=fileDeletedRes>" +
"</span></div>"
else if file

View File

@ -221,6 +221,10 @@ Config =
true
'Display reply and image count.'
]
'Page Count in Stats': [
false
'Display the page count in the thread stats as well.'
]
'Updater and Stats in Header': [
true,
'Places the thread updater and thread stats in the header instead of floating them.'
@ -813,7 +817,7 @@ http://iqdb.org/?url=%TURL
backlink: '>>%id'
fileInfo: '%l (%p%s, %r)'
fileInfo: '%L (%p%s, %r)'
favicon: 'ferongr'
@ -924,11 +928,11 @@ box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);
'Open page 0 in a new tab.'
]
'Next page': [
'Right'
'Shift+Right'
'Jump to the next page.'
]
'Previous page': [
'Left'
'Shift+Left'
'Jump to the previous page.'
]
'Open catalog': [
@ -937,11 +941,11 @@ box-shadow: inset 2px 2px 2px rgba(0,0,0,0.2);
]
# Thread Navigation
'Next thread': [
'Down'
'Shift+Down'
'See next thread.'
]
'Previous thread': [
'Up'
'Shift+Up'
'See previous thread.'
]
'Expand thread': [

View File

@ -93,6 +93,8 @@ Settings =
$.add d.body, [overlay, dialog]
$.event 'OpenSettings', null, dialog
close: ->
return unless Settings.dialog
$.rm Settings.overlay
@ -117,6 +119,7 @@ Settings =
section.className = "section-#{@hyphenatedTitle}"
@open section, mode
section.scrollTop = 0
$.event 'OpenSettings', null, section
main: (section) ->
items = {}

View File

@ -55,16 +55,20 @@ ImageExpand =
$.queueTask func, post
return
setFitness: ->
{checked} = @
(if checked then $.addClass else $.rmClass) doc, @name.toLowerCase().replace /\s+/g, '-'
(if @checked then $.addClass else $.rmClass) doc, @name.toLowerCase().replace /\s+/g, '-'
<% if (type === 'userjs') { %>
# XXX Opera doesn't support CSS vh.
return unless @name is 'Fit height'
if checked
if @checked
$.on window, 'resize', ImageExpand.resize
unless ImageExpand.style
ImageExpand.style = $.addStyle null
ImageExpand.resize()
else
$.off window, 'resize', ImageExpand.resize
resize: ->
ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:#{doc.clientHeight}px}"
<% } %>
toggle: (post) ->
{thumb} = post.file
@ -204,8 +208,5 @@ ImageExpand =
$.on input, 'change', $.cb.checked
el: label
resize: ->
ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:#{doc.clientHeight}px}"
menuToggle: (e) ->
ImageExpand.opmenu.toggle e, @, g
ImageExpand.opmenu.toggle e, @, g

View File

@ -18,7 +18,7 @@ ImageLoader =
$.event 'AddMenuEntry',
type: 'header'
el: prefetch
order: 120
order: 104
node: ->
return if @isClone or @isHidden or @thread.isHidden or !@file?.isImage

View File

@ -151,7 +151,7 @@ Linkify =
regExp: /.*(?:youtu.be\/|youtube.*v=|youtube.*\/embed\/|youtube.*\/v\/|youtube.*videos\/)([^#\&\?]*)\??(t\=.*)?/
el: ->
$.el 'iframe',
src: "//www.youtube.com/embed/#{@name}#{if @option then '#' + @option else ''}"
src: "//www.youtube.com/embed/#{@name}#{if @option then '#' + @option else ''}?wmode=opaque"
title:
api: -> "https://gdata.youtube.com/feeds/api/videos/#{@name}?alt=json&fields=title/text(),yt:noembed,app:control/yt:state/@reasonCode"
text: -> JSON.parse(@responseText).entry.title.$t
@ -161,13 +161,13 @@ Linkify =
style: 'border: 0; width: 150px; height: 45px;'
el: ->
$.el 'object',
innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=#{@name.replace /^i\//, ''}&autoplay=0' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
innerHTML: "<embed src='http://vocaroo.com/player.swf?playMediaID=#{@name.replace /^i\//, ''}&autoplay=0' wmode='opaque' width='150' height='45' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
Vimeo:
regExp: /.*(?:vimeo.com\/)([^#\&\?]*).*/
el: ->
$.el 'iframe',
src: "//player.vimeo.com/video/#{@name}"
src: "//player.vimeo.com/video/#{@name}?wmode=opaque"
title:
api: -> "https://vimeo.com/api/oembed.json?url=http://vimeo.com/#{@name}"
text: -> JSON.parse(@responseText).title
@ -175,8 +175,8 @@ Linkify =
LiveLeak:
regExp: /.*(?:liveleak.com\/view.+i=)([0-9a-z_]+)/
el: ->
$.el 'iframe',
src: "http://www.liveleak.com/e/#{@name}?autostart=true"
$.el 'object',
innerHTML: "<embed src='http://www.liveleak.com/e/#{@name}?autostart=true' wmode='opaque' width='640' height='390' pluginspage='http://get.adobe.com/flashplayer/' type='application/x-shockwave-flash'></embed>"
audio:
regExp: /(.*\.(mp3|ogg|wav))$/

View File

@ -79,9 +79,11 @@ Keybinds =
when Conf['Open front page']
$.open "/#{g.BOARD}/#delform"
when Conf['Next page']
return if g.VIEW is 'thread'
if form = $ '.next form'
window.location = form.action
when Conf['Previous page']
return if g.VIEW is 'thread'
if form = $ '.prev form'
window.location = form.action
when Conf['Open catalog']

View File

@ -4,18 +4,21 @@ ThreadStats =
if Conf['Updater and Stats in Header']
@dialog = sc = $.el 'span',
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>"
innerHTML: "<span id=post-count>0</span> / <span id=file-count>0</span>#{if Conf["Page Count in Stats"] then " / <span id=page-count>0</span>" else ""}"
id: 'thread-stats'
title: 'Post Count / File Count' + (if Conf["Page Count in Stats"] then " / Page Count" else "")
$.ready ->
Header.addShortcut sc
else
@dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;',
"<div class=move><span id=post-count>0</span> / <span id=file-count>0</span></div>"
"<div class=move title='Post Count / File Count#{if Conf["Page Count in Stats"] then " / Page Count" else ""}'><span id=post-count>0</span> / <span id=file-count>0</span>#{if Conf["Page Count in Stats"] then " / <span id=page-count>0</span>" else ""}</div>"
$.ready =>
$.add d.body, sc
@postCountEl = $ '#post-count', sc
@fileCountEl = $ '#file-count', sc
@pageCountEl = $ '#page-count', sc
@lastModified = '0'
Thread::callbacks.push
name: 'Thread Stats'
@ -28,6 +31,7 @@ ThreadStats =
postCount++
fileCount++ if post.file
ThreadStats.thread = @
ThreadStats.fetchPage()
ThreadStats.update postCount, fileCount
$.on d, 'ThreadUpdate', ThreadStats.onUpdate
@ -41,4 +45,22 @@ ThreadStats =
postCountEl.textContent = postCount
fileCountEl.textContent = fileCount
(if thread.postLimit and !thread.isSticky then $.addClass else $.rmClass) postCountEl, 'warning'
(if thread.fileLimit and !thread.isSticky then $.addClass else $.rmClass) fileCountEl, 'warning'
(if thread.fileLimit and !thread.isSticky then $.addClass else $.rmClass) fileCountEl, 'warning'
fetchPage: ->
return if ThreadStats.thread.isDead or !Conf["Page Count in Stats"]
setTimeout ThreadStats.fetchPage, 2 * $.MINUTE
$.ajax "//api.4chan.org/#{ThreadStats.thread.board}/threads.json", onload: ThreadStats.onThreadsLoad,
headers: 'If-Modified-Since': ThreadStats.lastModified
onThreadsLoad: ->
return if !Conf["Page Count in Stats"]
ThreadStats.lastModified = @getResponseHeader 'Last-Modified'
return if @status isnt 200
pages = JSON.parse @response
for page in pages
for thread in page.threads
if thread.no is ThreadStats.thread.ID
ThreadStats.pageCountEl.textContent = page.page
(if page.page is pages.length - 1 then $.addClass else $.rmClass) ThreadStats.pageCountEl, 'warning'
return

View File

@ -3,7 +3,12 @@ QR =
@db = new DataBoard 'yourPosts'
$.ready @initReady
$.on d, '4chanXInitFinished', @persist if Conf['Persistent QR']
if Conf['Persistent QR']
unless g.BOARD.ID is 'f'
$.on d, '4chanXInitFinished', @persist
else
$.ready @persist
Post::callbacks.push
name: 'Quick Reply'
@ -209,6 +214,8 @@ QR =
loadPersonas: (type, arr) ->
list = $ "#list-#{type}", QR.nodes.el
for val in arr
# XXX Firefox displays empty <option>s in the completion list.
continue unless val
$.add list, $.el 'option',
textContent: val
return