Merge branch 'v3'

Conflicts:
	CHANGELOG.md
	LICENSE
	builds/crx/script.js
	src/General/Config.coffee
	src/Images/FappeTyme.coffee
	src/Linkification/Linkify.coffee
This commit is contained in:
Zixaphir 2013-12-16 14:01:47 -07:00
commit e156cc8e9c
16 changed files with 419 additions and 13162 deletions

View File

@ -1,11 +1,14 @@
### v2.6.4 **MayhemYDG**:
*2013-12-06* - More Index Improvements:
- New setting: `Anchor Hidden Threads`, enabled by default. Hidden threads will be moved at the end of the index to fill the first pages.
- New setting: `Refreshed Navigation`, disabled by default. When enabled, navigating through pages will refresh the index.
- The last index refresh timer will now indicate the last time the index changed from 4chan's side, instead of the last time you refreshed the index.
### v2.6.3 **noface**:
*2013-11-27* - Strawpoll.me embedding support (as usual, only works on HTTP 4chan due to lack of HTTPS)
### v2.6.2 **Zixaphir**:
*2013-11-27* - FappeTyme and WerkTyme now persist across sessions.
### v2.6.1 ### v2.6.1
*2013-11-27* *2013-11-27*
@ -22,7 +25,7 @@
- You cannot post an image reply immediately after a non-image reply anymore. - You cannot post an image reply immediately after a non-image reply anymore.
- **New option**: `Auto-hide header on scroll`. - **New option**: `Auto-hide header on scroll`.
- Added support for `4cdn.org`. - Added support for `4cdn.org`.
- More index navigation improvements: - Index navigation improvements:
- Searching in the index is now possible and will show matched OPs by: - Searching in the index is now possible and will show matched OPs by:
<ul> <ul>
<li> comment <li> comment
@ -40,8 +43,9 @@
</ul> </ul>
- The elapsed time since the last index refresh is now indicated at the top of the index. - The elapsed time since the last index refresh is now indicated at the top of the index.
- New setting: `Show replies`, enabled by default. Disable it to only show OPs in the index. - New setting: `Show replies`, enabled by default. Disable it to only show OPs in the index.
- The index refreshing notification will now only appear on initial page load with slow connections. - New setting: `Anchor Hidden Threads`, enabled by default. Hidden threads will be moved at the end of the index to fill the first pages.
- Index navigation improvements: - New setting: `Refreshed Navigation`, disabled by default. When enabled, navigating through pages will refresh the index.
- The last index refresh timer will now indicate the last time the index changed from 4chan's side, instead of the last time you refreshed the index.
- You can now refresh the index page you are on with the refresh shortcut in the header bar or the same keybind for refreshing threads. - You can now refresh the index page you are on with the refresh shortcut in the header bar or the same keybind for refreshing threads.
- You can now switch between paged and all-threads index modes via the "Index Navigation" header sub-menu:<br> - You can now switch between paged and all-threads index modes via the "Index Navigation" header sub-menu:<br>
![index navigation](img/changelog/3.12.0/0.png) ![index navigation](img/changelog/3.12.0/0.png)
@ -54,6 +58,7 @@
<li> File count <li> File count
</ul> </ul>
- Navigating across index pages is now instantaneous. - Navigating across index pages is now instantaneous.
- The index refreshing notification will now only appear on initial page load with slow connections.
- Added a keybind to open the catalog search field on index pages. - Added a keybind to open the catalog search field on index pages.
- Minor cooldown fix: - Minor cooldown fix:
- You cannot post an image reply immediately after a non-image reply anymore. - You cannot post an image reply immediately after a non-image reply anymore.

View File

@ -1,5 +1,5 @@
/* /*
* appchan x - Version 2.6.4 - 2013-12-06 * appchan x - Version 2.6.4 - 2013-12-16
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE * https://github.com/zixaphir/appchan-x/blob/master/LICENSE

View File

@ -1,21 +0,0 @@
// ==UserScript==
// @name 4chan X
// @version 1.2.43
// @minGMVer 1.12
// @minFFVer 22
// @namespace 4chan-X
// @description Cross-browser userscript for maximum lurking on 4chan.
// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
// @match *://boards.4chan.org/*
// @match *://sys.4chan.org/*
// @match *://a.4cdn.org/*
// @match *://i.4cdn.org/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_openInTab
// @run-at document-start
// @updateURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan-X.meta.js
// @downloadURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan-X.user.js
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAgMAAAAqbBEUAAAACVBMVEUAAGcAAABmzDNZt9VtAAAAAXRSTlMAQObYZgAAAHFJREFUKFOt0LENACEIBdBv4Qju4wgWanEj3D6OcIVMKaitYHEU/jwTCQj8W75kiVCSBvdQ5/AvfVHBin11BgdRq3ysBgfwBDRrj3MCIA+oAQaku/Q1cNctrAmyDl577tOThYt/Y1RBM4DgOHzM0HFTAyLukH/cmRnqAAAAAElFTkSuQmCC
// ==/UserScript==

File diff suppressed because one or more lines are too long

View File

@ -22,7 +22,7 @@
// ==/UserScript== // ==/UserScript==
/* /*
* appchan x - Version 2.6.4 - 2013-12-06 * appchan x - Version 2.6.4 - 2013-12-16
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE * https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@ -347,11 +347,17 @@
MD5: '' MD5: ''
}, },
sauces: "https://www.google.com/searchbyimage?image_url=%TURL\nhttp://iqdb.org/?url=%TURL\n#//tineye.com/search?url=%TURL\n#http://saucenao.com/search.php?url=%TURL\n#http://3d.iqdb.org/?url=%TURL\n#http://regex.info/exif.cgi?imgurl=%URL\n# uploaders:\n#http://imgur.com/upload?url=%URL;text:Upload to imgur\n#http://ompldr.org/upload?url1=%URL;text:Upload to ompldr\n# \"View Same\" in archives:\n#//archive.foolz.us/_/search/image/%MD5/;text:View same on foolz\n#//archive.foolz.us/%board/search/image/%MD5/;text:View same on foolz /%board/\n#//archive.installgentoo.net/%board/image/%MD5;text:View same on installgentoo /%board/", sauces: "https://www.google.com/searchbyimage?image_url=%TURL\nhttp://iqdb.org/?url=%TURL\n#//tineye.com/search?url=%TURL\n#http://saucenao.com/search.php?url=%TURL\n#http://3d.iqdb.org/?url=%TURL\n#http://regex.info/exif.cgi?imgurl=%URL\n# uploaders:\n#http://imgur.com/upload?url=%URL;text:Upload to imgur\n#http://ompldr.org/upload?url1=%URL;text:Upload to ompldr\n# \"View Same\" in archives:\n#//archive.foolz.us/_/search/image/%MD5/;text:View same on foolz\n#//archive.foolz.us/%board/search/image/%MD5/;text:View same on foolz /%board/\n#//archive.installgentoo.net/%board/image/%MD5;text:View same on installgentoo /%board/",
FappeT: {
fappe: false,
werk: true
},
'Custom CSS': false, 'Custom CSS': false,
Index: { Index: {
'Index Mode': 'paged', 'Index Mode': 'paged',
'Index Sort': 'bump', 'Index Sort': 'bump',
'Show Replies': true 'Show Replies': true,
'Anchor Hidden Threads': true,
'Refreshed Navigation': false
}, },
Header: { Header: {
'Fixed Header': true, 'Fixed Header': true,
@ -4329,7 +4335,7 @@
Index = { Index = {
init: function() { init: function() {
var input, label, modeEntry, repliesEntry, sortEntry, _i, _j, _len, _len1, _ref, _ref1; var anchorEntry, input, label, modeEntry, name, refNavEntry, repliesEntry, sortEntry, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
if (g.VIEW !== 'index' || g.BOARD.ID === 'f') { if (g.VIEW !== 'index' || g.BOARD.ID === 'f') {
return; return;
@ -4407,17 +4413,40 @@
innerHTML: '<input type=checkbox name="Show Replies"> Show replies' innerHTML: '<input type=checkbox name="Show Replies"> Show replies'
}) })
}; };
input = repliesEntry.el.firstChild; anchorEntry = {
input.checked = Conf['Show Replies']; el: $.el('label', {
$.on(input, 'change', $.cb.checked); innerHTML: '<input type=checkbox name="Anchor Hidden Threads"> Anchor hidden threads',
$.on(input, 'change', this.cb.replies); title: 'Move hidden threads at the end of the index.'
})
};
refNavEntry = {
el: $.el('label', {
innerHTML: '<input type=checkbox name="Refreshed Navigation"> Refreshed navigation',
title: 'Refresh index when navigating through pages.'
})
};
_ref2 = [repliesEntry, anchorEntry, refNavEntry];
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
label = _ref2[_k];
input = label.el.firstChild;
name = input.name;
input.checked = Conf[name];
$.on(input, 'change', $.cb.checked);
switch (name) {
case 'Show Replies':
$.on(input, 'change', this.cb.replies);
break;
case 'Anchor Hidden Threads':
$.on(input, 'change', this.cb.sort);
}
}
$.event('AddMenuEntry', { $.event('AddMenuEntry', {
type: 'header', type: 'header',
el: $.el('span', { el: $.el('span', {
textContent: 'Index Navigation' textContent: 'Index Navigation'
}), }),
order: 90, order: 90,
subEntries: [modeEntry, sortEntry, repliesEntry] subEntries: [modeEntry, sortEntry, repliesEntry, anchorEntry, refNavEntry]
}); });
$.addClass(doc, 'index-loading'); $.addClass(doc, 'index-loading');
this.update(); this.update();
@ -4442,14 +4471,14 @@
return $.asap((function() { return $.asap((function() {
return $('.board', doc) || d.readyState !== 'loading'; return $('.board', doc) || d.readyState !== 'loading';
}), function() { }), function() {
var board, navLink, _k, _len2, _ref2; var board, navLink, _l, _len3, _ref3;
board = $('.board'); board = $('.board');
$.replace(board, Index.root); $.replace(board, Index.root);
d.implementation.createDocument(null, null, null).appendChild(board); d.implementation.createDocument(null, null, null).appendChild(board);
_ref2 = $$('.navLinks'); _ref3 = $$('.navLinks');
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
navLink = _ref2[_k]; navLink = _ref3[_l];
$.rm(navLink); $.rm(navLink);
} }
$.after($.x('child::form/preceding-sibling::hr[1]'), Index.navLinks); $.after($.x('child::form/preceding-sibling::hr[1]'), Index.navLinks);
@ -4503,7 +4532,7 @@
return; return;
} }
e.preventDefault(); e.preventDefault();
return Index.pageNav(+a.pathname.split('/')[2]); return Index.userPageNav(+a.pathname.split('/')[2]);
} }
}, },
scrollToIndex: function() { scrollToIndex: function() {
@ -4512,6 +4541,13 @@
getCurrentPage: function() { getCurrentPage: function() {
return +window.location.pathname.split('/')[2]; return +window.location.pathname.split('/')[2];
}, },
userPageNav: function(pageNum) {
if (Conf['Refreshed Navigation'] && Conf['Index Mode'] === 'paged') {
return Index.update(pageNum);
} else {
return Index.pageNav(pageNum);
}
},
pageNav: function(pageNum) { pageNav: function(pageNum) {
if (Index.currentPage === pageNum) { if (Index.currentPage === pageNum) {
return; return;
@ -4586,8 +4622,8 @@
$.before(a, strong); $.before(a, strong);
return $.add(strong, a); return $.add(strong, a);
}, },
update: function() { update: function(pageNum) {
var now, _ref, _ref1; var now, onload, _ref, _ref1;
if (!navigator.onLine) { if (!navigator.onLine) {
return; return;
@ -4611,15 +4647,21 @@
}), 5 * $.SECOND - (Date.now() - now)); }), 5 * $.SECOND - (Date.now() - now));
}); });
} }
if (typeof pageNum !== 'number') {
pageNum = null;
}
onload = function(e) {
return Index.load(e, pageNum);
};
Index.req = $.ajax("//a.4cdn.org/" + g.BOARD + "/catalog.json", { Index.req = $.ajax("//a.4cdn.org/" + g.BOARD + "/catalog.json", {
onabort: Index.load, onabort: onload,
onloadend: Index.load onloadend: onload
}, { }, {
whenModified: true whenModified: true
}); });
return $.addClass(Index.button, 'fa-spin'); return $.addClass(Index.button, 'fa-spin');
}, },
load: function(e) { load: function(e, pageNum) {
var err, notice, req, timeEl; var err, notice, req, timeEl;
$.rmClass(Index.button, 'fa-spin'); $.rmClass(Index.button, 'fa-spin');
@ -4633,7 +4675,9 @@
} }
try { try {
if (req.status === 200) { if (req.status === 200) {
Index.parse(JSON.parse(req.response)); Index.parse(JSON.parse(req.response), pageNum);
} else if (req.status === 304 && (pageNum != null)) {
Index.pageNav(pageNum);
} }
} catch (_error) { } catch (_error) {
err = _error; err = _error;
@ -4653,16 +4697,20 @@
setTimeout(notice.close, $.SECOND); setTimeout(notice.close, $.SECOND);
} }
timeEl = $('#index-last-refresh', Index.navLinks); timeEl = $('#index-last-refresh', Index.navLinks);
timeEl.dataset.utc = e.timeStamp / 1000; timeEl.dataset.utc = Date.parse(req.getResponseHeader('Last-Modified'));
RelativeDates.update(timeEl); RelativeDates.update(timeEl);
return Index.scrollToIndex(); return Index.scrollToIndex();
}, },
parse: function(pages) { parse: function(pages, pageNum) {
Index.parseThreadList(pages); Index.parseThreadList(pages);
Index.buildThreads(); Index.buildThreads();
Index.sort(); Index.sort();
Index.buildIndex();
Index.buildPagelist(); Index.buildPagelist();
if (pageNum != null) {
Index.pageNav(pageNum);
return;
}
Index.buildIndex();
return Index.setPage(); return Index.setPage();
}, },
parseThreadList: function(pages) { parseThreadList: function(pages) {
@ -4767,7 +4815,7 @@
return Main.callbackNodes(Post, posts); return Main.callbackNodes(Post, posts);
}, },
sort: function() { sort: function() {
var i, offset, sortedThreadIDs, threadID, threadRoot, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; var i, sortedThreadIDs, threadID, _i, _len;
switch (Conf['Index Sort']) { switch (Conf['Index Sort']) {
case 'bump': case 'bump':
@ -4814,25 +4862,31 @@
if (Index.isSearching) { if (Index.isSearching) {
Index.sortedNodes = Index.querySearch(Index.searchInput.value) || Index.sortedNodes; Index.sortedNodes = Index.querySearch(Index.searchInput.value) || Index.sortedNodes;
} }
Index.sortOnTop(function(thread) {
return thread.isSticky;
});
if (Conf['Filter']) {
Index.sortOnTop(function(thread) {
return thread.isOnTop;
});
}
if (Conf['Anchor Hidden Threads']) {
return Index.sortOnTop(function(thread) {
return !thread.isHidden;
});
}
},
sortOnTop: function(match) {
var i, offset, threadRoot, _i, _len, _ref, _ref1;
offset = 0; offset = 0;
_ref = Index.sortedNodes; _ref = Index.sortedNodes;
for (i = _j = 0, _len1 = _ref.length; _j < _len1; i = _j += 2) { for (i = _i = 0, _len = _ref.length; _i < _len; i = _i += 2) {
threadRoot = _ref[i]; threadRoot = _ref[i];
if (Get.threadFromRoot(threadRoot).isSticky) { if (match(Get.threadFromRoot(threadRoot))) {
(_ref1 = Index.sortedNodes).splice.apply(_ref1, [offset++ * 2, 0].concat(__slice.call(Index.sortedNodes.splice(i, 2)))); (_ref1 = Index.sortedNodes).splice.apply(_ref1, [offset++ * 2, 0].concat(__slice.call(Index.sortedNodes.splice(i, 2))));
} }
} }
if (!Conf['Filter']) {
return;
}
offset = 0;
_ref2 = Index.sortedNodes;
for (i = _k = 0, _len2 = _ref2.length; _k < _len2; i = _k += 2) {
threadRoot = _ref2[i];
if (Get.threadFromRoot(threadRoot).isOnTop) {
(_ref3 = Index.sortedNodes).splice.apply(_ref3, [offset++ * 2, 0].concat(__slice.call(Index.sortedNodes.splice(i, 2))));
}
}
}, },
buildIndex: function() { buildIndex: function() {
var nodes, nodesPerPage, pageNum; var nodes, nodesPerPage, pageNum;
@ -5036,7 +5090,7 @@
} }
flag = !flagCode ? '' : boardID === 'pol' ? " <img src='" + staticPath + "country/troll/" + (flagCode.toLowerCase()) + ".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>" : " <span title='" + flagName + "' class='flag flag-" + (flagCode.toLowerCase()) + "'></span>"; flag = !flagCode ? '' : boardID === 'pol' ? " <img src='" + staticPath + "country/troll/" + (flagCode.toLowerCase()) + ".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>" : " <span title='" + flagName + "' class='flag flag-" + (flagCode.toLowerCase()) + "'></span>";
if (file != null ? file.isDeleted : void 0) { if (file != null ? file.isDeleted : void 0) {
fileHTML = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted" + gifIcon + "' alt='File deleted.' class=fileDeleted>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res" + gifIcon + "' alt='File deleted.' class=fileDeletedRes>") + "</span></div>"; fileHTML = isOP ? ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted" + gifIcon + "' alt='File deleted.' class=fileDeleted>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res" + gifIcon + "' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
} else if (file) { } else if (file) {
ext = file.name.slice(-3); ext = file.name.slice(-3);
if (!file.twidth && !file.theight && ext === 'gif') { if (!file.twidth && !file.theight && ext === 'gif') {
@ -9729,31 +9783,35 @@
FappeTyme = { FappeTyme = {
init: function() { init: function() {
var el; var el, lc, type, _i, _len, _ref;
if (!(Conf['Fappe Tyme'] || Conf['Werk Tyme']) || g.VIEW === 'catalog' || g.BOARD === 'f') { if (!(Conf['Fappe Tyme'] || Conf['Werk Tyme']) || g.VIEW === 'catalog' || g.BOARD === 'f') {
return; return;
} }
if (Conf['Fappe Tyme']) { _ref = ["Fappe", "Werk"];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
type = _ref[_i];
if (!Conf["" + type + " Tyme"]) {
continue;
}
lc = type.toLowerCase();
el = $.el('a', { el = $.el('a', {
href: 'javascript:;', href: 'javascript:;',
id: 'fappeTyme', id: "" + lc + "Tyme",
title: 'Fappe Tyme', title: "" + type + " Tyme",
className: 'a-icon' className: 'a-icon'
}); });
$.on(el, 'click', FappeTyme.cb.fappe); if (type === 'Werk') {
Header.addShortcut(el, true); el.textContent = '\uf0b1';
} el.className = 'fa';
if (Conf['Werk Tyme']) { }
el = $.el('a', { $.on(el, 'click', FappeTyme.cb.toggle.bind({
href: 'javascript:;', name: "" + lc
id: 'werkTyme', }));
title: 'Werk Tyme',
className: 'fa',
textContent: '\uf0b1'
});
$.on(el, 'click', FappeTyme.cb.werk);
Header.addShortcut(el, true); Header.addShortcut(el, true);
if (Conf[lc]) {
FappeTyme.cb.set(type);
}
} }
return Post.callbacks.push({ return Post.callbacks.push({
name: 'Fappe Tyme', name: 'Fappe Tyme',
@ -9767,11 +9825,17 @@
return $.addClass(this.nodes.root, "noFile"); return $.addClass(this.nodes.root, "noFile");
}, },
cb: { cb: {
fappe: function() { set: function(type) {
return $.toggleClass(doc, 'fappeTyme'); FappeTyme[type].checked = Conf[type];
return $["" + (Conf[type] ? 'add' : 'rm') + "Class"](doc, "" + type + "Tyme");
}, },
werk: function() { toggle: function() {
return $.toggleClass(doc, 'werkTyme'); Conf[this.name] = !Conf[this.name];
FappeTyme.cb.set(this.name);
return $.cb.checked.call({
name: this.name,
checked: Conf[this.name]
});
} }
} }
}; };
@ -12304,8 +12368,8 @@
http: true, http: true,
https: true, https: true,
software: "foolfuuka", software: "foolfuuka",
boards: ["hr", "tg", "tv", "x"], boards: ["hr", "pol", "s4s", "tg", "tv", "x"],
files: ["hr", "tg", "tv", "x"] files: ["hr", "pol", "s4s", "tg", "tv", "x"]
}, },
"Nyafuu": { "Nyafuu": {
domain: "archive.nyafuu.org", domain: "archive.nyafuu.org",
@ -12315,14 +12379,6 @@
boards: ["c", "w", "wg"], boards: ["c", "w", "wg"],
files: ["c", "w", "wg"] files: ["c", "w", "wg"]
}, },
"fap archive": {
domain: "fuuka.worldathleticproject.org",
http: true,
https: true,
software: "foolfuuka",
boards: ["adv", "b", "cm", "d", "e", "h", "hc", "lgbt", "pol", "r", "s", "s4s", "soc", "trv", "u", "y"],
files: ["b", "cm", "d", "e", "h", "hc", "pol", "r", "s", "s4s", "soc", "u", "y"]
},
"Install Gentoo": { "Install Gentoo": {
domain: "archive.installgentoo.net", domain: "archive.installgentoo.net",
http: false, http: false,
@ -12344,7 +12400,7 @@
http: true, http: true,
software: "fuuka", software: "fuuka",
boards: ["an", "fit", "k", "mlp", "r9k", "toy"], boards: ["an", "fit", "k", "mlp", "r9k", "toy"],
files: ["an", "k", "toy"] files: ["an", "fit", "k", "r9k", "toy"]
}, },
"warosu": { "warosu": {
domain: "fuuka.warosu.org", domain: "fuuka.warosu.org",
@ -12354,6 +12410,14 @@
boards: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"], boards: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"],
files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"] files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"]
}, },
"Bui's Archive": {
domain: "archive.bui.pm",
http: true,
https: true,
software: "foolfuuka",
boards: ["b"],
files: ["b"]
},
"Foolz Beta": { "Foolz Beta": {
domain: "beta.foolz.us", domain: "beta.foolz.us",
http: true, http: true,
@ -14448,7 +14512,7 @@
if (!this.file || this.isClone) { if (!this.file || this.isClone) {
return; return;
} }
return this.file.text.innerHTML = FileInfo.funk(FileInfo, this); return this.file.text.innerHTML = "<span class=file-info>" + (FileInfo.funk(FileInfo, this)) + "</span>";
}, },
createFunc: function(format) { createFunc: function(format) {
var code; var code;
@ -14952,14 +15016,18 @@
Gallery.cb.toggle(); Gallery.cb.toggle();
break; break;
case Conf['fappeTyme']: case Conf['fappeTyme']:
FappeTyme.cb.fappe(); FappeTyme.cb.toggle.call({
name: 'fappe'
});
break; break;
case Conf['werkTyme']: case Conf['werkTyme']:
FappeTyme.cb.werk(); FappeTyme.cb.toggle.call({
name: 'werk'
});
break; break;
case Conf['Front page']: case Conf['Front page']:
if (g.VIEW === 'index') { if (g.VIEW === 'index') {
Index.pageNav(0); Index.userPageNav(0);
} else { } else {
window.location = "/" + g.BOARD + "/"; window.location = "/" + g.BOARD + "/";
} }
@ -15020,7 +15088,7 @@
Keybinds.hl(0, threadRoot); Keybinds.hl(0, threadRoot);
break; break;
case Conf['Hide']: case Conf['Hide']:
if (g.VIEW === 'index') { if (ThreadHiding.db) {
ThreadHiding.toggle(thread); ThreadHiding.toggle(thread);
} }
break; break;
@ -15133,7 +15201,7 @@
} }
}, },
hl: function(delta, thread) { hl: function(delta, thread) {
var axe, height, next, postEl, replies, reply, root, _i, _len; var axis, height, next, postEl, replies, reply, root, _i, _len;
postEl = $('.reply.highlight', thread); postEl = $('.reply.highlight', thread);
if (!delta) { if (!delta) {
@ -15146,8 +15214,8 @@
height = postEl.getBoundingClientRect().height; height = postEl.getBoundingClientRect().height;
if (Header.getTopOf(postEl) >= -height && Header.getBottomOf(postEl) >= -height) { if (Header.getTopOf(postEl) >= -height && Header.getBottomOf(postEl) >= -height) {
root = postEl.parentNode; root = postEl.parentNode;
axe = delta === +1 ? 'following' : 'preceding'; axis = delta === +1 ? 'following' : 'preceding';
if (!(next = $.x("" + axe + "-sibling::div[contains(@class,'replyContainer')][1]/child::div[contains(@class,'reply')]", root))) { if (!(next = $.x("" + axis + "-sibling::div[contains(@class,'replyContainer') and not(@hidden) and not(child::div[@class='stub'])][1]/child::div[contains(@class,'reply')]", root))) {
return; return;
} }
Header.scrollToIfNeeded(next, delta === +1); Header.scrollToIfNeeded(next, delta === +1);
@ -15236,11 +15304,11 @@
return $('.board'); return $('.board');
}, },
scroll: function(delta) { scroll: function(delta) {
var axe, next, thread, top; var axis, next, thread, top;
thread = Nav.getThread(); thread = Nav.getThread();
axe = delta === +1 ? 'following' : 'preceding'; axis = delta === +1 ? 'following' : 'preceding';
if (next = $.x("" + axe + "-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread)) { if (next = $.x("" + axis + "-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread)) {
top = Header.getTopOf(thread); top = Header.getTopOf(thread);
if (delta === +1 && top < 5 || delta === -1 && top > -5) { if (delta === +1 && top < 5 || delta === -1 && top > -5) {
thread = next; thread = next;

View File

@ -1,6 +1,6 @@
// Generated by CoffeeScript // Generated by CoffeeScript
/* /*
* appchan x - Version 2.6.4 - 2013-12-06 * appchan x - Version 2.6.4 - 2013-12-16
* *
* Licensed under the MIT license. * Licensed under the MIT license.
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE * https://github.com/zixaphir/appchan-x/blob/master/LICENSE
@ -326,11 +326,17 @@
MD5: '' MD5: ''
}, },
sauces: "https://www.google.com/searchbyimage?image_url=%TURL\nhttp://iqdb.org/?url=%TURL\n#//tineye.com/search?url=%TURL\n#http://saucenao.com/search.php?url=%TURL\n#http://3d.iqdb.org/?url=%TURL\n#http://regex.info/exif.cgi?imgurl=%URL\n# uploaders:\n#http://imgur.com/upload?url=%URL;text:Upload to imgur\n#http://ompldr.org/upload?url1=%URL;text:Upload to ompldr\n# \"View Same\" in archives:\n#//archive.foolz.us/_/search/image/%MD5/;text:View same on foolz\n#//archive.foolz.us/%board/search/image/%MD5/;text:View same on foolz /%board/\n#//archive.installgentoo.net/%board/image/%MD5;text:View same on installgentoo /%board/", sauces: "https://www.google.com/searchbyimage?image_url=%TURL\nhttp://iqdb.org/?url=%TURL\n#//tineye.com/search?url=%TURL\n#http://saucenao.com/search.php?url=%TURL\n#http://3d.iqdb.org/?url=%TURL\n#http://regex.info/exif.cgi?imgurl=%URL\n# uploaders:\n#http://imgur.com/upload?url=%URL;text:Upload to imgur\n#http://ompldr.org/upload?url1=%URL;text:Upload to ompldr\n# \"View Same\" in archives:\n#//archive.foolz.us/_/search/image/%MD5/;text:View same on foolz\n#//archive.foolz.us/%board/search/image/%MD5/;text:View same on foolz /%board/\n#//archive.installgentoo.net/%board/image/%MD5;text:View same on installgentoo /%board/",
FappeT: {
fappe: false,
werk: true
},
'Custom CSS': false, 'Custom CSS': false,
Index: { Index: {
'Index Mode': 'paged', 'Index Mode': 'paged',
'Index Sort': 'bump', 'Index Sort': 'bump',
'Show Replies': true 'Show Replies': true,
'Anchor Hidden Threads': true,
'Refreshed Navigation': false
}, },
Header: { Header: {
'Fixed Header': true, 'Fixed Header': true,
@ -4342,7 +4348,7 @@
Index = { Index = {
init: function() { init: function() {
var input, label, modeEntry, repliesEntry, sortEntry, _i, _j, _len, _len1, _ref, _ref1; var anchorEntry, input, label, modeEntry, name, refNavEntry, repliesEntry, sortEntry, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2;
if (g.VIEW !== 'index' || g.BOARD.ID === 'f') { if (g.VIEW !== 'index' || g.BOARD.ID === 'f') {
return; return;
@ -4420,17 +4426,40 @@
innerHTML: '<input type=checkbox name="Show Replies"> Show replies' innerHTML: '<input type=checkbox name="Show Replies"> Show replies'
}) })
}; };
input = repliesEntry.el.firstChild; anchorEntry = {
input.checked = Conf['Show Replies']; el: $.el('label', {
$.on(input, 'change', $.cb.checked); innerHTML: '<input type=checkbox name="Anchor Hidden Threads"> Anchor hidden threads',
$.on(input, 'change', this.cb.replies); title: 'Move hidden threads at the end of the index.'
})
};
refNavEntry = {
el: $.el('label', {
innerHTML: '<input type=checkbox name="Refreshed Navigation"> Refreshed navigation',
title: 'Refresh index when navigating through pages.'
})
};
_ref2 = [repliesEntry, anchorEntry, refNavEntry];
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
label = _ref2[_k];
input = label.el.firstChild;
name = input.name;
input.checked = Conf[name];
$.on(input, 'change', $.cb.checked);
switch (name) {
case 'Show Replies':
$.on(input, 'change', this.cb.replies);
break;
case 'Anchor Hidden Threads':
$.on(input, 'change', this.cb.sort);
}
}
$.event('AddMenuEntry', { $.event('AddMenuEntry', {
type: 'header', type: 'header',
el: $.el('span', { el: $.el('span', {
textContent: 'Index Navigation' textContent: 'Index Navigation'
}), }),
order: 90, order: 90,
subEntries: [modeEntry, sortEntry, repliesEntry] subEntries: [modeEntry, sortEntry, repliesEntry, anchorEntry, refNavEntry]
}); });
$.addClass(doc, 'index-loading'); $.addClass(doc, 'index-loading');
this.update(); this.update();
@ -4455,14 +4484,14 @@
return $.asap((function() { return $.asap((function() {
return $('.board', doc) || d.readyState !== 'loading'; return $('.board', doc) || d.readyState !== 'loading';
}), function() { }), function() {
var board, navLink, _k, _len2, _ref2; var board, navLink, _l, _len3, _ref3;
board = $('.board'); board = $('.board');
$.replace(board, Index.root); $.replace(board, Index.root);
d.implementation.createDocument(null, null, null).appendChild(board); d.implementation.createDocument(null, null, null).appendChild(board);
_ref2 = $$('.navLinks'); _ref3 = $$('.navLinks');
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) {
navLink = _ref2[_k]; navLink = _ref3[_l];
$.rm(navLink); $.rm(navLink);
} }
$.after($.x('child::form/preceding-sibling::hr[1]'), Index.navLinks); $.after($.x('child::form/preceding-sibling::hr[1]'), Index.navLinks);
@ -4516,7 +4545,7 @@
return; return;
} }
e.preventDefault(); e.preventDefault();
return Index.pageNav(+a.pathname.split('/')[2]); return Index.userPageNav(+a.pathname.split('/')[2]);
} }
}, },
scrollToIndex: function() { scrollToIndex: function() {
@ -4525,6 +4554,13 @@
getCurrentPage: function() { getCurrentPage: function() {
return +window.location.pathname.split('/')[2]; return +window.location.pathname.split('/')[2];
}, },
userPageNav: function(pageNum) {
if (Conf['Refreshed Navigation'] && Conf['Index Mode'] === 'paged') {
return Index.update(pageNum);
} else {
return Index.pageNav(pageNum);
}
},
pageNav: function(pageNum) { pageNav: function(pageNum) {
if (Index.currentPage === pageNum) { if (Index.currentPage === pageNum) {
return; return;
@ -4599,8 +4635,8 @@
$.before(a, strong); $.before(a, strong);
return $.add(strong, a); return $.add(strong, a);
}, },
update: function() { update: function(pageNum) {
var now, _ref, _ref1; var now, onload, _ref, _ref1;
if (!navigator.onLine) { if (!navigator.onLine) {
return; return;
@ -4624,15 +4660,21 @@
}), 5 * $.SECOND - (Date.now() - now)); }), 5 * $.SECOND - (Date.now() - now));
}); });
} }
if (typeof pageNum !== 'number') {
pageNum = null;
}
onload = function(e) {
return Index.load(e, pageNum);
};
Index.req = $.ajax("//a.4cdn.org/" + g.BOARD + "/catalog.json", { Index.req = $.ajax("//a.4cdn.org/" + g.BOARD + "/catalog.json", {
onabort: Index.load, onabort: onload,
onloadend: Index.load onloadend: onload
}, { }, {
whenModified: true whenModified: true
}); });
return $.addClass(Index.button, 'fa-spin'); return $.addClass(Index.button, 'fa-spin');
}, },
load: function(e) { load: function(e, pageNum) {
var err, notice, req, timeEl; var err, notice, req, timeEl;
$.rmClass(Index.button, 'fa-spin'); $.rmClass(Index.button, 'fa-spin');
@ -4646,7 +4688,9 @@
} }
try { try {
if (req.status === 200) { if (req.status === 200) {
Index.parse(JSON.parse(req.response)); Index.parse(JSON.parse(req.response), pageNum);
} else if (req.status === 304 && (pageNum != null)) {
Index.pageNav(pageNum);
} }
} catch (_error) { } catch (_error) {
err = _error; err = _error;
@ -4666,16 +4710,20 @@
setTimeout(notice.close, $.SECOND); setTimeout(notice.close, $.SECOND);
} }
timeEl = $('#index-last-refresh', Index.navLinks); timeEl = $('#index-last-refresh', Index.navLinks);
timeEl.dataset.utc = e.timeStamp; timeEl.dataset.utc = Date.parse(req.getResponseHeader('Last-Modified'));
RelativeDates.update(timeEl); RelativeDates.update(timeEl);
return Index.scrollToIndex(); return Index.scrollToIndex();
}, },
parse: function(pages) { parse: function(pages, pageNum) {
Index.parseThreadList(pages); Index.parseThreadList(pages);
Index.buildThreads(); Index.buildThreads();
Index.sort(); Index.sort();
Index.buildIndex();
Index.buildPagelist(); Index.buildPagelist();
if (pageNum != null) {
Index.pageNav(pageNum);
return;
}
Index.buildIndex();
return Index.setPage(); return Index.setPage();
}, },
parseThreadList: function(pages) { parseThreadList: function(pages) {
@ -4780,7 +4828,7 @@
return Main.callbackNodes(Post, posts); return Main.callbackNodes(Post, posts);
}, },
sort: function() { sort: function() {
var i, offset, sortedThreadIDs, threadID, threadRoot, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; var i, sortedThreadIDs, threadID, _i, _len;
switch (Conf['Index Sort']) { switch (Conf['Index Sort']) {
case 'bump': case 'bump':
@ -4827,25 +4875,31 @@
if (Index.isSearching) { if (Index.isSearching) {
Index.sortedNodes = Index.querySearch(Index.searchInput.value) || Index.sortedNodes; Index.sortedNodes = Index.querySearch(Index.searchInput.value) || Index.sortedNodes;
} }
Index.sortOnTop(function(thread) {
return thread.isSticky;
});
if (Conf['Filter']) {
Index.sortOnTop(function(thread) {
return thread.isOnTop;
});
}
if (Conf['Anchor Hidden Threads']) {
return Index.sortOnTop(function(thread) {
return !thread.isHidden;
});
}
},
sortOnTop: function(match) {
var i, offset, threadRoot, _i, _len, _ref, _ref1;
offset = 0; offset = 0;
_ref = Index.sortedNodes; _ref = Index.sortedNodes;
for (i = _j = 0, _len1 = _ref.length; _j < _len1; i = _j += 2) { for (i = _i = 0, _len = _ref.length; _i < _len; i = _i += 2) {
threadRoot = _ref[i]; threadRoot = _ref[i];
if (Get.threadFromRoot(threadRoot).isSticky) { if (match(Get.threadFromRoot(threadRoot))) {
(_ref1 = Index.sortedNodes).splice.apply(_ref1, [offset++ * 2, 0].concat(__slice.call(Index.sortedNodes.splice(i, 2)))); (_ref1 = Index.sortedNodes).splice.apply(_ref1, [offset++ * 2, 0].concat(__slice.call(Index.sortedNodes.splice(i, 2))));
} }
} }
if (!Conf['Filter']) {
return;
}
offset = 0;
_ref2 = Index.sortedNodes;
for (i = _k = 0, _len2 = _ref2.length; _k < _len2; i = _k += 2) {
threadRoot = _ref2[i];
if (Get.threadFromRoot(threadRoot).isOnTop) {
(_ref3 = Index.sortedNodes).splice.apply(_ref3, [offset++ * 2, 0].concat(__slice.call(Index.sortedNodes.splice(i, 2))));
}
}
}, },
buildIndex: function() { buildIndex: function() {
var nodes, nodesPerPage, pageNum; var nodes, nodesPerPage, pageNum;
@ -5049,7 +5103,7 @@
} }
flag = !flagCode ? '' : boardID === 'pol' ? " <img src='" + staticPath + "country/troll/" + (flagCode.toLowerCase()) + ".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>" : " <span title='" + flagName + "' class='flag flag-" + (flagCode.toLowerCase()) + "'></span>"; flag = !flagCode ? '' : boardID === 'pol' ? " <img src='" + staticPath + "country/troll/" + (flagCode.toLowerCase()) + ".gif' alt=" + flagCode + " title='" + flagName + "' class=countryFlag>" : " <span title='" + flagName + "' class='flag flag-" + (flagCode.toLowerCase()) + "'></span>";
if (file != null ? file.isDeleted : void 0) { if (file != null ? file.isDeleted : void 0) {
fileHTML = isOP ? ("<div class=file id=f" + postID + "><div class=fileInfo></div><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted" + gifIcon + "' alt='File deleted.' class=fileDeleted>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res" + gifIcon + "' alt='File deleted.' class=fileDeletedRes>") + "</span></div>"; fileHTML = isOP ? ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted" + gifIcon + "' alt='File deleted.' class=fileDeleted>") + "</span></div>" : ("<div class=file id=f" + postID + "><span class=fileThumb>") + ("<img src='" + staticPath + "filedeleted-res" + gifIcon + "' alt='File deleted.' class=fileDeletedRes>") + "</span></div>";
} else if (file) { } else if (file) {
ext = file.name.slice(-3); ext = file.name.slice(-3);
if (!file.twidth && !file.theight && ext === 'gif') { if (!file.twidth && !file.theight && ext === 'gif') {
@ -9715,31 +9769,35 @@
FappeTyme = { FappeTyme = {
init: function() { init: function() {
var el; var el, lc, type, _i, _len, _ref;
if (!(Conf['Fappe Tyme'] || Conf['Werk Tyme']) || g.VIEW === 'catalog' || g.BOARD === 'f') { if (!(Conf['Fappe Tyme'] || Conf['Werk Tyme']) || g.VIEW === 'catalog' || g.BOARD === 'f') {
return; return;
} }
if (Conf['Fappe Tyme']) { _ref = ["Fappe", "Werk"];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
type = _ref[_i];
if (!Conf["" + type + " Tyme"]) {
continue;
}
lc = type.toLowerCase();
el = $.el('a', { el = $.el('a', {
href: 'javascript:;', href: 'javascript:;',
id: 'fappeTyme', id: "" + lc + "Tyme",
title: 'Fappe Tyme', title: "" + type + " Tyme",
className: 'a-icon' className: 'a-icon'
}); });
$.on(el, 'click', FappeTyme.cb.fappe); if (type === 'Werk') {
Header.addShortcut(el, true); el.textContent = '\uf0b1';
} el.className = 'fa';
if (Conf['Werk Tyme']) { }
el = $.el('a', { $.on(el, 'click', FappeTyme.cb.toggle.bind({
href: 'javascript:;', name: "" + lc
id: 'werkTyme', }));
title: 'Werk Tyme',
className: 'fa',
textContent: '\uf0b1'
});
$.on(el, 'click', FappeTyme.cb.werk);
Header.addShortcut(el, true); Header.addShortcut(el, true);
if (Conf[lc]) {
FappeTyme.cb.set(type);
}
} }
return Post.callbacks.push({ return Post.callbacks.push({
name: 'Fappe Tyme', name: 'Fappe Tyme',
@ -9753,11 +9811,17 @@
return $.addClass(this.nodes.root, "noFile"); return $.addClass(this.nodes.root, "noFile");
}, },
cb: { cb: {
fappe: function() { set: function(type) {
return $.toggleClass(doc, 'fappeTyme'); FappeTyme[type].checked = Conf[type];
return $["" + (Conf[type] ? 'add' : 'rm') + "Class"](doc, "" + type + "Tyme");
}, },
werk: function() { toggle: function() {
return $.toggleClass(doc, 'werkTyme'); Conf[this.name] = !Conf[this.name];
FappeTyme.cb.set(this.name);
return $.cb.checked.call({
name: this.name,
checked: Conf[this.name]
});
} }
} }
}; };
@ -12296,8 +12360,8 @@
http: true, http: true,
https: true, https: true,
software: "foolfuuka", software: "foolfuuka",
boards: ["hr", "tg", "tv", "x"], boards: ["hr", "pol", "s4s", "tg", "tv", "x"],
files: ["hr", "tg", "tv", "x"] files: ["hr", "pol", "s4s", "tg", "tv", "x"]
}, },
"Nyafuu": { "Nyafuu": {
domain: "archive.nyafuu.org", domain: "archive.nyafuu.org",
@ -12307,14 +12371,6 @@
boards: ["c", "w", "wg"], boards: ["c", "w", "wg"],
files: ["c", "w", "wg"] files: ["c", "w", "wg"]
}, },
"fap archive": {
domain: "fuuka.worldathleticproject.org",
http: true,
https: true,
software: "foolfuuka",
boards: ["adv", "b", "cm", "d", "e", "h", "hc", "lgbt", "pol", "r", "s", "s4s", "soc", "trv", "u", "y"],
files: ["b", "cm", "d", "e", "h", "hc", "pol", "r", "s", "s4s", "soc", "u", "y"]
},
"Install Gentoo": { "Install Gentoo": {
domain: "archive.installgentoo.net", domain: "archive.installgentoo.net",
http: false, http: false,
@ -12336,7 +12392,7 @@
http: true, http: true,
software: "fuuka", software: "fuuka",
boards: ["an", "fit", "k", "mlp", "r9k", "toy"], boards: ["an", "fit", "k", "mlp", "r9k", "toy"],
files: ["an", "k", "toy"] files: ["an", "fit", "k", "r9k", "toy"]
}, },
"warosu": { "warosu": {
domain: "fuuka.warosu.org", domain: "fuuka.warosu.org",
@ -12346,6 +12402,14 @@
boards: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"], boards: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"],
files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"] files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"]
}, },
"Bui's Archive": {
domain: "archive.bui.pm",
http: true,
https: true,
software: "foolfuuka",
boards: ["b"],
files: ["b"]
},
"Foolz Beta": { "Foolz Beta": {
domain: "beta.foolz.us", domain: "beta.foolz.us",
http: true, http: true,
@ -14440,7 +14504,7 @@
if (!this.file || this.isClone) { if (!this.file || this.isClone) {
return; return;
} }
return this.file.text.innerHTML = FileInfo.funk(FileInfo, this); return this.file.text.innerHTML = "<span class=file-info>" + (FileInfo.funk(FileInfo, this)) + "</span>";
}, },
createFunc: function(format) { createFunc: function(format) {
var code; var code;
@ -14944,14 +15008,18 @@
Gallery.cb.toggle(); Gallery.cb.toggle();
break; break;
case Conf['fappeTyme']: case Conf['fappeTyme']:
FappeTyme.cb.fappe(); FappeTyme.cb.toggle.call({
name: 'fappe'
});
break; break;
case Conf['werkTyme']: case Conf['werkTyme']:
FappeTyme.cb.werk(); FappeTyme.cb.toggle.call({
name: 'werk'
});
break; break;
case Conf['Front page']: case Conf['Front page']:
if (g.VIEW === 'index') { if (g.VIEW === 'index') {
Index.pageNav(0); Index.userPageNav(0);
} else { } else {
window.location = "/" + g.BOARD + "/"; window.location = "/" + g.BOARD + "/";
} }
@ -15012,7 +15080,7 @@
Keybinds.hl(0, threadRoot); Keybinds.hl(0, threadRoot);
break; break;
case Conf['Hide']: case Conf['Hide']:
if (g.VIEW === 'index') { if (ThreadHiding.db) {
ThreadHiding.toggle(thread); ThreadHiding.toggle(thread);
} }
break; break;
@ -15125,7 +15193,7 @@
} }
}, },
hl: function(delta, thread) { hl: function(delta, thread) {
var axe, height, next, postEl, replies, reply, root, _i, _len; var axis, height, next, postEl, replies, reply, root, _i, _len;
postEl = $('.reply.highlight', thread); postEl = $('.reply.highlight', thread);
if (!delta) { if (!delta) {
@ -15138,8 +15206,8 @@
height = postEl.getBoundingClientRect().height; height = postEl.getBoundingClientRect().height;
if (Header.getTopOf(postEl) >= -height && Header.getBottomOf(postEl) >= -height) { if (Header.getTopOf(postEl) >= -height && Header.getBottomOf(postEl) >= -height) {
root = postEl.parentNode; root = postEl.parentNode;
axe = delta === +1 ? 'following' : 'preceding'; axis = delta === +1 ? 'following' : 'preceding';
if (!(next = $.x("" + axe + "-sibling::div[contains(@class,'replyContainer')][1]/child::div[contains(@class,'reply')]", root))) { if (!(next = $.x("" + axis + "-sibling::div[contains(@class,'replyContainer') and not(@hidden) and not(child::div[@class='stub'])][1]/child::div[contains(@class,'reply')]", root))) {
return; return;
} }
Header.scrollToIfNeeded(next, delta === +1); Header.scrollToIfNeeded(next, delta === +1);
@ -15228,11 +15296,11 @@
return $('.board'); return $('.board');
}, },
scroll: function(delta) { scroll: function(delta) {
var axe, next, thread, top; var axis, next, thread, top;
thread = Nav.getThread(); thread = Nav.getThread();
axe = delta === +1 ? 'following' : 'preceding'; axis = delta === +1 ? 'following' : 'preceding';
if (next = $.x("" + axe + "-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread)) { if (next = $.x("" + axis + "-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread)) {
top = Header.getTopOf(thread); top = Header.getTopOf(thread);
if (delta === +1 && top < 5 || delta === -1 && top > -5) { if (delta === +1 && top < 5 || delta === -1 && top > -5) {
thread = next; thread = next;

View File

@ -540,8 +540,8 @@ a.hide-announcement {
} }
/* File */ /* File */
.fileText:hover .fntrunc, .file-info:hover .fntrunc,
.fileText:not(:hover) .fnfull, .file-info:not(:hover) .fnfull,
.expanded-image > .post > .file > .fileThumb > img[data-md5], .expanded-image > .post > .file > .fileThumb > img[data-md5],
:not(.expanded-image) > .post > .file > .fileThumb > .full-image { :not(.expanded-image) > .post > .file > .fileThumb > .full-image {
display: none; display: none;

View File

@ -51,8 +51,8 @@ Redirect =
http: true http: true
https: true https: true
software: "foolfuuka" software: "foolfuuka"
boards: ["hr", "tg", "tv", "x"] boards: ["hr", "pol", "s4s", "tg", "tv", "x"]
files: ["hr", "tg", "tv", "x"] files: ["hr", "pol", "s4s", "tg", "tv", "x"]
"Nyafuu": "Nyafuu":
domain: "archive.nyafuu.org" domain: "archive.nyafuu.org"
@ -62,14 +62,6 @@ Redirect =
boards: ["c", "w", "wg"] boards: ["c", "w", "wg"]
files: ["c", "w", "wg"] files: ["c", "w", "wg"]
"fap archive":
domain: "fuuka.worldathleticproject.org"
http: true
https: true
software: "foolfuuka"
boards: ["adv", "b", "cm", "d", "e", "h", "hc", "lgbt", "pol", "r", "s", "s4s", "soc", "trv", "u", "y"]
files: ["b", "cm", "d", "e", "h", "hc", "pol", "r", "s", "s4s", "soc", "u", "y"]
"Install Gentoo": "Install Gentoo":
domain: "archive.installgentoo.net" domain: "archive.installgentoo.net"
http: false http: false
@ -91,7 +83,7 @@ Redirect =
http: true http: true
software: "fuuka" software: "fuuka"
boards: ["an", "fit", "k", "mlp", "r9k", "toy"] boards: ["an", "fit", "k", "mlp", "r9k", "toy"]
files: ["an", "k", "toy"] files: ["an", "fit", "k", "r9k", "toy"]
"warosu": "warosu":
domain: "fuuka.warosu.org" domain: "fuuka.warosu.org"
@ -101,6 +93,14 @@ Redirect =
boards: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"] boards: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"]
files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"] files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"]
"Bui's Archive":
domain: "archive.bui.pm"
http: true
https: true
software: "foolfuuka"
boards: ["b"]
files: ["b"]
"Foolz Beta": "Foolz Beta":
domain: "beta.foolz.us" domain: "beta.foolz.us"
http: true http: true

View File

@ -124,7 +124,7 @@ Build =
if file?.isDeleted if file?.isDeleted
fileHTML = if isOP fileHTML = if isOP
"<div class=file id=f#{postID}><div class=fileInfo></div><span class=fileThumb>" + "<div class=file id=f#{postID}><span class=fileThumb>" +
"<img src='#{staticPath}filedeleted#{gifIcon}' alt='File deleted.' class=fileDeleted>" + "<img src='#{staticPath}filedeleted#{gifIcon}' alt='File deleted.' class=fileDeleted>" +
"</span></div>" "</span></div>"
else else

View File

@ -836,12 +836,18 @@ http://iqdb.org/?url=%TURL
#//archive.installgentoo.net/%board/image/%MD5;text:View same on installgentoo /%board/ #//archive.installgentoo.net/%board/image/%MD5;text:View same on installgentoo /%board/
""" """
FappeT:
fappe: false
werk: true
'Custom CSS': false 'Custom CSS': false
Index: Index:
'Index Mode': 'paged' 'Index Mode': 'paged'
'Index Sort': 'bump' 'Index Sort': 'bump'
'Show Replies': true 'Show Replies': true
'Anchor Hidden Threads': true
'Refreshed Navigation': false
Header: Header:
'Fixed Header': true 'Fixed Header': true

View File

@ -38,18 +38,33 @@ Index =
$.on input, 'change', @cb.sort $.on input, 'change', @cb.sort
repliesEntry = repliesEntry =
el: $.el 'label', innerHTML: '<input type=checkbox name="Show Replies"> Show replies' el: $.el 'label',
input = repliesEntry.el.firstChild innerHTML: '<input type=checkbox name="Show Replies"> Show replies'
input.checked = Conf['Show Replies'] anchorEntry =
$.on input, 'change', $.cb.checked el: $.el 'label',
$.on input, 'change', @cb.replies innerHTML: '<input type=checkbox name="Anchor Hidden Threads"> Anchor hidden threads'
title: 'Move hidden threads at the end of the index.'
refNavEntry =
el: $.el 'label',
innerHTML: '<input type=checkbox name="Refreshed Navigation"> Refreshed navigation'
title: 'Refresh index when navigating through pages.'
for label in [repliesEntry, anchorEntry, refNavEntry]
input = label.el.firstChild
{name} = input
input.checked = Conf[name]
$.on input, 'change', $.cb.checked
switch name
when 'Show Replies'
$.on input, 'change', @cb.replies
when 'Anchor Hidden Threads'
$.on input, 'change', @cb.sort
$.event 'AddMenuEntry', $.event 'AddMenuEntry',
type: 'header' type: 'header'
el: $.el 'span', el: $.el 'span',
textContent: 'Index Navigation' textContent: 'Index Navigation'
order: 90 order: 90
subEntries: [modeEntry, sortEntry, repliesEntry] subEntries: [modeEntry, sortEntry, repliesEntry, anchorEntry, refNavEntry]
$.addClass doc, 'index-loading' $.addClass doc, 'index-loading'
@update() @update()
@ -111,13 +126,18 @@ Index =
return return
return if a.textContent is 'Catalog' return if a.textContent is 'Catalog'
e.preventDefault() e.preventDefault()
Index.pageNav +a.pathname.split('/')[2] Index.userPageNav +a.pathname.split('/')[2]
scrollToIndex: -> scrollToIndex: ->
Header.scrollToIfNeeded Index.root Header.scrollToIfNeeded Index.root
getCurrentPage: -> getCurrentPage: ->
+window.location.pathname.split('/')[2] +window.location.pathname.split('/')[2]
userPageNav: (pageNum) ->
if Conf['Refreshed Navigation'] and Conf['Index Mode'] is 'paged'
Index.update pageNum
else
Index.pageNav pageNum
pageNav: (pageNum) -> pageNav: (pageNum) ->
return if Index.currentPage is pageNum return if Index.currentPage is pageNum
history.pushState null, '', if pageNum is 0 then './' else pageNum history.pushState null, '', if pageNum is 0 then './' else pageNum
@ -174,7 +194,7 @@ Index =
$.before a, strong $.before a, strong
$.add strong, a $.add strong, a
update: -> update: (pageNum) ->
return unless navigator.onLine return unless navigator.onLine
Index.req?.abort() Index.req?.abort()
Index.notice?.close() Index.notice?.close()
@ -189,13 +209,15 @@ Index =
return unless Index.req and !Index.notice return unless Index.req and !Index.notice
Index.notice = new Notice 'info', 'Refreshing index...' Index.notice = new Notice 'info', 'Refreshing index...'
), 5 * $.SECOND - (Date.now() - now) ), 5 * $.SECOND - (Date.now() - now)
pageNum = null if typeof pageNum isnt 'number' # event
onload = (e) -> Index.load e, pageNum
Index.req = $.ajax "//a.4cdn.org/#{g.BOARD}/catalog.json", Index.req = $.ajax "//a.4cdn.org/#{g.BOARD}/catalog.json",
onabort: Index.load onabort: onload
onloadend: Index.load onloadend: onload
, ,
whenModified: true whenModified: true
$.addClass Index.button, 'fa-spin' $.addClass Index.button, 'fa-spin'
load: (e) -> load: (e, pageNum) ->
$.rmClass Index.button, 'fa-spin' $.rmClass Index.button, 'fa-spin'
{req, notice} = Index {req, notice} = Index
delete Index.req delete Index.req
@ -207,7 +229,10 @@ Index =
return return
try try
Index.parse JSON.parse req.response if req.status is 200 if req.status is 200
Index.parse JSON.parse(req.response), pageNum
else if req.status is 304 and pageNum?
Index.pageNav pageNum
catch err catch err
c.error 'Index failure:', err.stack c.error 'Index failure:', err.stack
# network error or non-JSON content for example. # network error or non-JSON content for example.
@ -225,15 +250,18 @@ Index =
setTimeout notice.close, $.SECOND setTimeout notice.close, $.SECOND
timeEl = $ '#index-last-refresh', Index.navLinks timeEl = $ '#index-last-refresh', Index.navLinks
timeEl.dataset.utc = e.timeStamp <% if (type === 'userscript') { %>/ 1000<% } %> timeEl.dataset.utc = Date.parse req.getResponseHeader 'Last-Modified'
RelativeDates.update timeEl RelativeDates.update timeEl
Index.scrollToIndex() Index.scrollToIndex()
parse: (pages) -> parse: (pages, pageNum) ->
Index.parseThreadList pages Index.parseThreadList pages
Index.buildThreads() Index.buildThreads()
Index.sort() Index.sort()
Index.buildIndex()
Index.buildPagelist() Index.buildPagelist()
if pageNum?
Index.pageNav pageNum
return
Index.buildIndex()
Index.setPage() Index.setPage()
parseThreadList: (pages) -> parseThreadList: (pages) ->
Index.pagesNum = pages.length Index.pagesNum = pages.length
@ -319,15 +347,15 @@ Index =
Index.sortedNodes.push Index.nodes[i], Index.nodes[i + 1] Index.sortedNodes.push Index.nodes[i], Index.nodes[i + 1]
if Index.isSearching if Index.isSearching
Index.sortedNodes = Index.querySearch(Index.searchInput.value) or Index.sortedNodes Index.sortedNodes = Index.querySearch(Index.searchInput.value) or Index.sortedNodes
# Put the sticky threads on top of the index. # Sticky threads
Index.sortOnTop (thread) -> thread.isSticky
# Highlighted threads
Index.sortOnTop((thread) -> thread.isOnTop) if Conf['Filter']
# Non-hidden threads
Index.sortOnTop((thread) -> !thread.isHidden) if Conf['Anchor Hidden Threads']
sortOnTop: (match) ->
offset = 0 offset = 0
for threadRoot, i in Index.sortedNodes by 2 when Get.threadFromRoot(threadRoot).isSticky for threadRoot, i in Index.sortedNodes by 2 when match Get.threadFromRoot threadRoot
Index.sortedNodes.splice offset++ * 2, 0, Index.sortedNodes.splice(i, 2)...
return unless Conf['Filter']
# Put the highlighted thread & <hr> on top of the index
# while keeping the original order they appear in.
offset = 0
for threadRoot, i in Index.sortedNodes by 2 when Get.threadFromRoot(threadRoot).isOnTop
Index.sortedNodes.splice offset++ * 2, 0, Index.sortedNodes.splice(i, 2)... Index.sortedNodes.splice offset++ * 2, 0, Index.sortedNodes.splice(i, 2)...
return return
buildIndex: -> buildIndex: ->

View File

@ -150,11 +150,11 @@ a {
.fixed.top-header #header-bar { .fixed.top-header #header-bar {
border-bottom-width: 1px; border-bottom-width: 1px;
} }
.fixed.bottom #header-bar { .fixed.bottom-header #header-bar {
box-shadow: 0 -1px 2px rgba(0, 0, 0, .15); box-shadow: 0 -1px 2px rgba(0, 0, 0, .15);
border-top-width: 1px; border-top-width: 1px;
} }
.fixed.bottom #header-bar .menu-button i { .fixed.bottom-header #header-bar .menu-button i {
border-top: none; border-top: none;
border-bottom: 6px solid; border-bottom: 6px solid;
} }
@ -170,7 +170,7 @@ a {
-webkit-transform: translateY(-100%); -webkit-transform: translateY(-100%);
transform: translateY(-100%); transform: translateY(-100%);
} }
.fixed.bottom #header-bar.autohide:not(:hover) { .fixed.bottom-header #header-bar.autohide:not(:hover) {
-webkit-transform: translateY(100%); -webkit-transform: translateY(100%);
transform: translateY(100%); transform: translateY(100%);
} }
@ -666,10 +666,15 @@ a.hide-announcement {
max-width: 75%; max-width: 75%;
padding-bottom: 16px; padding-bottom: 16px;
} }
/* Fappe Tyme */
.fappeTyme .thread > .noFile, .fappeTyme .thread > .noFile,
.fappeTyme .threadContainer > .noFile { .fappeTyme .threadContainer > .noFile {
display: none; display: none;
} }
/* Werk Tyme */
.werkTyme .post .file {
display: none;
}
/* Index/Reply Navigation */ /* Index/Reply Navigation */
#navlinks { #navlinks {

View File

@ -2,28 +2,21 @@ FappeTyme =
init: -> init: ->
return if !(Conf['Fappe Tyme'] or Conf['Werk Tyme']) or g.VIEW is 'catalog' or g.BOARD is 'f' return if !(Conf['Fappe Tyme'] or Conf['Werk Tyme']) or g.VIEW is 'catalog' or g.BOARD is 'f'
if Conf['Fappe Tyme'] for type in ["Fappe", "Werk"] when Conf["#{type} Tyme"]
lc = type.toLowerCase()
el = $.el 'a', el = $.el 'a',
href: 'javascript:;' href: 'javascript:;'
id: 'fappeTyme' id: "#{lc}Tyme"
title: 'Fappe Tyme' title: "#{type} Tyme"
className: 'a-icon' className: 'a-icon'
if type is 'Werk'
el.textContent = '\uf0b1'
el.className = 'fa'
$.on el, 'click', FappeTyme.cb.fappe $.on el, 'click', FappeTyme.cb.toggle.bind {name: "#{lc}"}
Header.addShortcut el, true
if Conf['Werk Tyme']
el = $.el 'a',
href: 'javascript:;'
id: 'werkTyme'
title: 'Werk Tyme'
className: 'fa'
textContent: '\uf0b1'
$.on el, 'click', FappeTyme.cb.werk
Header.addShortcut el, true Header.addShortcut el, true
FappeTyme.cb.set type if Conf[lc]
Post.callbacks.push Post.callbacks.push
name: 'Fappe Tyme' name: 'Fappe Tyme'
@ -34,7 +27,11 @@ FappeTyme =
$.addClass @nodes.root, "noFile" $.addClass @nodes.root, "noFile"
cb: cb:
fappe: -> set: (type) ->
$.toggleClass doc, 'fappeTyme' FappeTyme[type].checked = Conf[type]
werk: -> $["#{if Conf[type] then 'add' else 'rm'}Class"] doc, "#{type}Tyme"
$.toggleClass doc, 'werkTyme'
toggle: ->
Conf[@name] = !Conf[@name]
FappeTyme.cb.set @name
$.cb.checked.call {name: @name, checked: Conf[@name]}

View File

@ -8,7 +8,7 @@ FileInfo =
cb: @node cb: @node
node: -> node: ->
return if !@file or @isClone return if !@file or @isClone
@file.text.innerHTML = FileInfo.funk FileInfo, @ @file.text.innerHTML = "<span class=file-info>#{FileInfo.funk FileInfo, @}</span>"
createFunc: (format) -> createFunc: (format) ->
code = format.replace /%(.)/g, (s, c) -> code = format.replace /%(.)/g, (s, c) ->
if c of FileInfo.formatters if c of FileInfo.formatters
@ -21,11 +21,10 @@ FileInfo =
return "#{size.toFixed()} Bytes" return "#{size.toFixed()} Bytes"
i = 1 + ['KB', 'MB'].indexOf unit i = 1 + ['KB', 'MB'].indexOf unit
size /= 1024 while i-- size /= 1024 while i--
size = size = if unit is 'MB'
if unit is 'MB' Math.round(size * 100) / 100
Math.round(size * 100) / 100 else
else size.toFixed()
size.toFixed()
"#{size} #{unit}" "#{size} #{unit}"
escape: (name) -> escape: (name) ->
name.replace /<|>/g, (c) -> name.replace /<|>/g, (c) ->

View File

@ -81,13 +81,13 @@ Keybinds =
when Conf['Open Gallery'] when Conf['Open Gallery']
Gallery.cb.toggle() Gallery.cb.toggle()
when Conf['fappeTyme'] when Conf['fappeTyme']
FappeTyme.cb.fappe() FappeTyme.cb.toggle.call {name: 'fappe'}
when Conf['werkTyme'] when Conf['werkTyme']
FappeTyme.cb.werk() FappeTyme.cb.toggle.call {name: 'werk'}
# Board Navigation # Board Navigation
when Conf['Front page'] when Conf['Front page']
if g.VIEW is 'index' if g.VIEW is 'index'
Index.pageNav 0 Index.userPageNav 0
else else
window.location = "/#{g.BOARD}/" window.location = "/#{g.BOARD}/"
when Conf['Open front page'] when Conf['Open front page']
@ -126,7 +126,7 @@ Keybinds =
when Conf['Deselect reply'] when Conf['Deselect reply']
Keybinds.hl 0, threadRoot Keybinds.hl 0, threadRoot
when Conf['Hide'] when Conf['Hide']
ThreadHiding.toggle thread if g.VIEW is 'index' ThreadHiding.toggle thread if ThreadHiding.db
when Conf['Previous Post Quoting You'] when Conf['Previous Post Quoting You']
QuoteYou.cb.seek 'preceding' QuoteYou.cb.seek 'preceding'
when Conf['Next Post Quoting You'] when Conf['Next Post Quoting You']
@ -223,11 +223,11 @@ Keybinds =
{height} = postEl.getBoundingClientRect() {height} = postEl.getBoundingClientRect()
if Header.getTopOf(postEl) >= -height and Header.getBottomOf(postEl) >= -height # We're at least partially visible if Header.getTopOf(postEl) >= -height and Header.getBottomOf(postEl) >= -height # We're at least partially visible
root = postEl.parentNode root = postEl.parentNode
axe = if delta is +1 axis = if delta is +1
'following' 'following'
else else
'preceding' 'preceding'
return unless next = $.x "#{axe}-sibling::div[contains(@class,'replyContainer')][1]/child::div[contains(@class,'reply')]", root return unless next = $.x "#{axis}-sibling::div[contains(@class,'replyContainer') and not(@hidden) and not(child::div[@class='stub'])][1]/child::div[contains(@class,'reply')]", root
Header.scrollToIfNeeded next, delta is +1 Header.scrollToIfNeeded next, delta is +1
@focus next @focus next
$.rmClass postEl, 'highlight' $.rmClass postEl, 'highlight'

View File

@ -43,11 +43,11 @@ Nav =
scroll: (delta) -> scroll: (delta) ->
thread = Nav.getThread() thread = Nav.getThread()
axe = if delta is +1 axis = if delta is +1
'following' 'following'
else else
'preceding' 'preceding'
if next = $.x "#{axe}-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread if next = $.x "#{axis}-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread
# Unless we're not at the beginning of the current thread, # Unless we're not at the beginning of the current thread,
# and thus wanting to move to beginning, # and thus wanting to move to beginning,
# or we're above the first thread and don't want to skip it. # or we're above the first thread and don't want to skip it.