Merge branch 'v3'
Conflicts: CHANGELOG.md LICENSE builds/appchan-x.user.js builds/crx/script.js src/General/Main.coffee src/General/Settings.coffee src/General/html/Settings/Settings.html src/Miscellaneous/AnnouncementHiding.coffee
This commit is contained in:
commit
1f49ef2212
@ -1,5 +1,12 @@
|
||||
<<<<<<< HEAD
|
||||
### v2.8.7
|
||||
*2014-01-19*
|
||||
=======
|
||||
**MayhemYDG**:
|
||||
- Added a `Reset Settings` button in the settings.
|
||||
- More stability update.
|
||||
- Stability update.
|
||||
>>>>>>> v3
|
||||
|
||||
**Zixaphir**:
|
||||
- Fix posting.
|
||||
|
||||
48
CONTRIBUTING.md
Normal file
48
CONTRIBUTING.md
Normal file
@ -0,0 +1,48 @@
|
||||
## Reporting bugs and suggestions
|
||||
|
||||
Reporting bugs:
|
||||
|
||||
1. Make sure both your **browser** and **4chan X** are up to date.<br>
|
||||
Only **Chrome**, **Firefox** and **Opera** are supported.<br>
|
||||
**SRWare Iron**, **Firefox ESR**, **Pale Moon**, **Waterfox**, and other derivatives are not supported, use them at your own risk.
|
||||
2. Look at the list of [known problems and solutions](https://github.com/MayhemYDG/4chan-x/wiki/FAQ#known-problems).
|
||||
3. Disable your other extensions & scripts to identify conflicts.
|
||||
4. If your issue persists, open a [new issue](https://github.com/MayhemYDG/4chan-x/issues) with the following information:
|
||||
1. Precise steps to reproduce the problem, with the expected and actual results.
|
||||
2. [Console errors](https://github.com/MayhemYDG/4chan-x/wiki/FAQ#console-errors), if any.
|
||||
3. 4chan X version, browser variant, browser version, and Greasemonkey version if you are using it.
|
||||
4. Your exported settings. If your settings contains sensible information (e.g. personas), edit the text file manually.
|
||||
|
||||
Respect these guidelines:
|
||||
- Describe the issue clearly, put some effort into it. A one-liner isn't a good enough description.
|
||||
- If you want to get your suggestion implemented sooner, make it convincing.
|
||||
- If you want to criticize, make it convincing and constructive.
|
||||
- Be mature. Act like an idiot and you will be blocked without warning.
|
||||
|
||||
## Development & Contribution
|
||||
|
||||
### Get started
|
||||
|
||||
- Install [node.js](http://nodejs.org/).
|
||||
- Install [Grunt's CLI](http://gruntjs.com/) with `npm install -g grunt-cli`.
|
||||
- Clone 4chan X.
|
||||
- `cd` into it.
|
||||
- Install/Update 4chan X dependencies with `npm install`.
|
||||
|
||||
### Build
|
||||
|
||||
- Build with `grunt`.
|
||||
- Continuously build with `grunt watch`.
|
||||
|
||||
### Release
|
||||
|
||||
- Update the version with `grunt patch`, `grunt minor` or `grunt major`.
|
||||
- Release with `grunt release`.
|
||||
|
||||
Note: this is only used to release new 4chan X versions, and is **not** needed or wanted in pull requests.
|
||||
|
||||
### Contribute
|
||||
|
||||
- Edit the sources.
|
||||
- If the edits affect regular users, edit the changelog.
|
||||
- Open a pull request.
|
||||
2
LICENSE
2
LICENSE
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* appchan x - Version 2.8.7 - 2014-01-21
|
||||
* appchan x - Version 2.8.7 - 2014-01-26
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name 4chan X
|
||||
// @version 1.3.2
|
||||
// @minGMVer 1.13
|
||||
// @minGMVer 1.14
|
||||
// @minFFVer 26
|
||||
// @namespace 4chan-X
|
||||
// @description Cross-browser userscript for maximum lurking on 4chan.
|
||||
@ -13,6 +13,7 @@
|
||||
// @grant GM_getValue
|
||||
// @grant GM_setValue
|
||||
// @grant GM_deleteValue
|
||||
// @grant GM_listValues
|
||||
// @grant GM_openInTab
|
||||
// @run-at document-start
|
||||
// @updateURL https://github.com/seaweedchan/4chan-x/raw/stable/builds/4chan-X.meta.js
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name appchan x
|
||||
// @version 2.8.7
|
||||
// @minGMVer 1.13
|
||||
// @minGMVer 1.14
|
||||
// @minFFVer 26
|
||||
// @namespace zixaphir
|
||||
// @description The most comprehensive 4chan userscript.
|
||||
@ -13,6 +13,7 @@
|
||||
// @grant GM_getValue
|
||||
// @grant GM_setValue
|
||||
// @grant GM_deleteValue
|
||||
// @grant GM_listValues
|
||||
// @grant GM_openInTab
|
||||
// @run-at document-start
|
||||
// @updateURL https://github.com/zixaphir/appchan-x/raw/stable/builds/appchan-x.meta.js
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// ==UserScript==
|
||||
// @name appchan x
|
||||
// @version 2.8.7
|
||||
// @minGMVer 1.13
|
||||
// @minGMVer 1.14
|
||||
// @minFFVer 26
|
||||
// @namespace zixaphir
|
||||
// @description The most comprehensive 4chan userscript.
|
||||
@ -14,6 +14,7 @@
|
||||
// @grant GM_getValue
|
||||
// @grant GM_setValue
|
||||
// @grant GM_deleteValue
|
||||
// @grant GM_listValues
|
||||
// @grant GM_openInTab
|
||||
// @run-at document-start
|
||||
// @updateURL https://github.com/zixaphir/appchan-x/raw/stable/builds/appchan-x.meta.js
|
||||
@ -22,7 +23,7 @@
|
||||
// ==/UserScript==
|
||||
|
||||
/*
|
||||
* appchan x - Version 2.8.7 - 2014-01-21
|
||||
* appchan x - Version 2.8.7 - 2014-01-26
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||
@ -111,8 +112,8 @@
|
||||
|
||||
(function() {
|
||||
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, Clone, Color, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, InfiniScroll, JSColor, Keybinds, Linkify, Main, MascotTools, Mascots, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, SimpleDict, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
__slice = [].slice,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
@ -2624,6 +2625,9 @@
|
||||
return lastModified[url] = r.getResponseHeader('Last-Modified');
|
||||
});
|
||||
}
|
||||
if (/\.json$/.test(url)) {
|
||||
r.responseType = 'json';
|
||||
}
|
||||
$.extend(r, options);
|
||||
$.extend(r.upload, upCallbacks);
|
||||
r.send(form);
|
||||
@ -2712,12 +2716,16 @@
|
||||
return d.evaluate(path, root, null, 7, null);
|
||||
};
|
||||
|
||||
$.addClass = function(el, className) {
|
||||
return el.classList.add(className);
|
||||
$.addClass = function() {
|
||||
var className, el, _ref;
|
||||
el = arguments[0], className = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
return (_ref = el.classList).add.apply(_ref, className);
|
||||
};
|
||||
|
||||
$.rmClass = function(el, className) {
|
||||
return el.classList.remove(className);
|
||||
$.rmClass = function() {
|
||||
var className, el, _ref;
|
||||
el = arguments[0], className = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
return (_ref = el.classList).remove.apply(_ref, className);
|
||||
};
|
||||
|
||||
$.toggleClass = function(el, className) {
|
||||
@ -2742,12 +2750,7 @@
|
||||
})();
|
||||
|
||||
$.rmAll = function(root) {
|
||||
var node, _i, _len, _ref;
|
||||
_ref = __slice.call(root.childNodes);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
node = _ref[_i];
|
||||
root.removeChild(node);
|
||||
}
|
||||
return root.textContent = '';
|
||||
};
|
||||
|
||||
$.tn = function(s) {
|
||||
@ -2980,6 +2983,13 @@
|
||||
};
|
||||
})();
|
||||
|
||||
$.clear = function(cb) {
|
||||
$["delete"](GM_listValues().map(function(key) {
|
||||
return key.replace(g.NAMESPACE, '');
|
||||
}));
|
||||
return typeof cb === "function" ? cb() : void 0;
|
||||
};
|
||||
|
||||
$.remove = function(arr, value) {
|
||||
var i;
|
||||
i = arr.indexOf(value);
|
||||
@ -3622,7 +3632,7 @@
|
||||
}
|
||||
board = _this.data.boards[boardID];
|
||||
threads = {};
|
||||
_ref = JSON.parse(e.target.response);
|
||||
_ref = e.target.response;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
page = _ref[_i];
|
||||
_ref1 = page.threads;
|
||||
@ -4190,18 +4200,16 @@
|
||||
toggleHideBarOnScroll: function(e) {
|
||||
var hide;
|
||||
hide = this.checked;
|
||||
$.set('Header auto-hide on scroll', hide);
|
||||
$.cb.checked.call(this);
|
||||
return Header.setHideBarOnScroll(hide);
|
||||
},
|
||||
hideBarOnScroll: function() {
|
||||
var offsetY;
|
||||
offsetY = window.pageYOffset;
|
||||
if (offsetY > (Header.previousOffset || 0)) {
|
||||
$.addClass(Header.bar, 'autohide');
|
||||
$.addClass(Header.bar, 'scroll');
|
||||
$.addClass(Header.bar, 'autohide', 'scroll');
|
||||
} else {
|
||||
$.rmClass(Header.bar, 'autohide');
|
||||
$.rmClass(Header.bar, 'scroll');
|
||||
$.rmClass(Header.bar, 'autohide', 'scroll');
|
||||
}
|
||||
return Header.previousOffset = offsetY;
|
||||
},
|
||||
@ -4249,14 +4257,38 @@
|
||||
return Header.scrollTo(post);
|
||||
},
|
||||
scrollTo: function(root, down, needed) {
|
||||
var x;
|
||||
var height, x;
|
||||
if (down) {
|
||||
x = Header.getBottomOf(root);
|
||||
if (Conf['Header auto-hide on scroll'] && Conf['Bottom header']) {
|
||||
height = Header.bar.getBoundingClientRect().height;
|
||||
if (x <= 0) {
|
||||
if (!Header.isHidden()) {
|
||||
x += height;
|
||||
}
|
||||
} else {
|
||||
if (Header.isHidden()) {
|
||||
x -= height;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(needed && x >= 0)) {
|
||||
return window.scrollBy(0, -x);
|
||||
}
|
||||
} else {
|
||||
x = Header.getTopOf(root);
|
||||
if (Conf['Header auto-hide on scroll'] && !Conf['Bottom header']) {
|
||||
height = Header.bar.getBoundingClientRect().height;
|
||||
if (x >= 0) {
|
||||
if (!Header.isHidden()) {
|
||||
x += height;
|
||||
}
|
||||
} else {
|
||||
if (Header.isHidden()) {
|
||||
x -= height;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(needed && x >= 0)) {
|
||||
return window.scrollBy(0, x);
|
||||
}
|
||||
@ -4284,6 +4316,15 @@
|
||||
}
|
||||
return bottom;
|
||||
},
|
||||
isHidden: function() {
|
||||
var top;
|
||||
top = Header.bar.getBoundingClientRect().top;
|
||||
if (Conf['Bottom header']) {
|
||||
return top === doc.clientHeight;
|
||||
} else {
|
||||
return top < 0;
|
||||
}
|
||||
},
|
||||
addShortcut: function(el, icon) {
|
||||
$.addClass(el, 'shortcut');
|
||||
return $.add(Header[icon ? 'icons' : 'stats'], el);
|
||||
@ -4731,7 +4772,7 @@
|
||||
Index.board = "" + g.BOARD;
|
||||
try {
|
||||
if (req.status === 200) {
|
||||
Index.parse(JSON.parse(req.response), pageNum);
|
||||
Index.parse(req.response, pageNum);
|
||||
} else if (req.status === 304 && (pageNum != null)) {
|
||||
Index.pageNav(pageNum);
|
||||
}
|
||||
@ -5009,6 +5050,9 @@
|
||||
pageNum = Index.getCurrentPage();
|
||||
}
|
||||
} else {
|
||||
if (!Index.searchInput.dataset.searching) {
|
||||
return;
|
||||
}
|
||||
pageNum = Index.pageBeforeSearch;
|
||||
delete Index.pageBeforeSearch;
|
||||
Index.searchInput.removeAttribute('data-searching');
|
||||
@ -5378,6 +5422,7 @@
|
||||
return $.cache(url, function() {
|
||||
return Get.archivedPost(this, boardID, postID, root, context);
|
||||
}, {
|
||||
responseType: 'json',
|
||||
withCredentials: url.archive.withCredentials
|
||||
});
|
||||
}
|
||||
@ -5418,7 +5463,7 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
posts = JSON.parse(req.response).posts;
|
||||
posts = req.response.posts;
|
||||
Build.spoilerRange[boardID] = posts[0].custom_spoiler;
|
||||
for (_i = 0, _len = posts.length; _i < _len; _i++) {
|
||||
post = posts[_i];
|
||||
@ -5454,7 +5499,7 @@
|
||||
Get.insert(post, root, context);
|
||||
return;
|
||||
}
|
||||
data = JSON.parse(req.response);
|
||||
data = req.response;
|
||||
if (data.error) {
|
||||
$.addClass(root, 'warning');
|
||||
root.textContent = data.error;
|
||||
@ -5559,6 +5604,7 @@
|
||||
this.type = type;
|
||||
this.rmEntry = __bind(this.rmEntry, this);
|
||||
this.addEntry = __bind(this.addEntry, this);
|
||||
this.onFocus = __bind(this.onFocus, this);
|
||||
this.keybinds = __bind(this.keybinds, this);
|
||||
this.close = __bind(this.close, this);
|
||||
$.on(d, 'AddMenuEntry', this.addEntry);
|
||||
@ -5723,6 +5769,11 @@
|
||||
return e.stopPropagation();
|
||||
};
|
||||
|
||||
Menu.prototype.onFocus = function(e) {
|
||||
e.stopPropagation();
|
||||
return this.focus(e.target);
|
||||
};
|
||||
|
||||
Menu.prototype.focus = function(entry) {
|
||||
var bottom, cHeight, cWidth, eRect, focused, left, right, sRect, style, submenu, top, _i, _len, _ref, _ref1, _ref2;
|
||||
while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) {
|
||||
@ -5774,10 +5825,7 @@
|
||||
var el, subEntries, subEntry, _i, _len;
|
||||
el = entry.el, subEntries = entry.subEntries;
|
||||
$.addClass(el, 'entry');
|
||||
$.on(el, 'focus mouseover', (function(e) {
|
||||
e.stopPropagation();
|
||||
return this.focus(el);
|
||||
}).bind(this));
|
||||
$.on(el, 'focus mouseover', this.onFocus);
|
||||
el.style.order = entry.order || 100;
|
||||
if (!subEntries) {
|
||||
return;
|
||||
@ -6684,7 +6732,7 @@
|
||||
return;
|
||||
}
|
||||
threads = {};
|
||||
_ref = JSON.parse(this.response);
|
||||
_ref = this.response;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
page = _ref[_i];
|
||||
_ref1 = page.threads;
|
||||
@ -7888,7 +7936,7 @@
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
case 304:
|
||||
text = "" + (service.text(JSON.parse(response.responseText)));
|
||||
text = "" + (service.text(response.response));
|
||||
if (Conf['Embedding']) {
|
||||
embed.dataset.title = text;
|
||||
}
|
||||
@ -7984,7 +8032,7 @@
|
||||
if (status !== 200 && status !== 304) {
|
||||
return div.innerHTML = "ERROR " + status;
|
||||
}
|
||||
files = JSON.parse(this.response).files;
|
||||
files = this.response.files;
|
||||
_ref = ['video/mp4', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'image/svg', 'audio/mpeg'];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
type = _ref[_i];
|
||||
@ -8889,7 +8937,7 @@
|
||||
QR.cooldown.set({
|
||||
delay: 2
|
||||
});
|
||||
} else if (err.textContent && (m = err.textContent.match(/wait\s(\d+)\ssecond/i))) {
|
||||
} else if (err.textContent && (m = err.textContent.match(/wait\s+(\d+)\s+second/i))) {
|
||||
QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : true;
|
||||
QR.cooldown.set({
|
||||
delay: m[1]
|
||||
@ -10028,12 +10076,13 @@
|
||||
}
|
||||
return $.ajax("//api.4chan.org/" + post.board + "/res/" + post.thread + ".json", {
|
||||
onload: function() {
|
||||
var i, postObj;
|
||||
var i, postObj, posts;
|
||||
if (this.status !== 200) {
|
||||
return;
|
||||
}
|
||||
i = 0;
|
||||
while (postObj = JSON.parse(this.response).posts[i++]) {
|
||||
posts = this.response.posts;
|
||||
while (postObj = posts[i++]) {
|
||||
if (postObj.no === post.ID) {
|
||||
break;
|
||||
}
|
||||
@ -10310,7 +10359,7 @@
|
||||
if (this.status !== 200) {
|
||||
return;
|
||||
}
|
||||
_ref = JSON.parse(this.response).posts;
|
||||
_ref = this.response.posts;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
postObj = _ref[_i];
|
||||
if (postObj.no === post.ID) {
|
||||
@ -10438,7 +10487,7 @@
|
||||
if (this.status !== 200) {
|
||||
return;
|
||||
}
|
||||
_ref = JSON.parse(this.response).posts;
|
||||
_ref = this.response.posts;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
postObj = _ref[_i];
|
||||
if (postObj.no === post.ID) {
|
||||
@ -11061,21 +11110,21 @@
|
||||
});
|
||||
},
|
||||
onThreadsLoad: function() {
|
||||
var page, pages, thread, _i, _j, _len, _len1, _ref;
|
||||
var page, thread, _i, _j, _len, _len1, _ref, _ref1;
|
||||
if (!(Conf["Page Count in Stats"] && 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];
|
||||
_ref = this.response;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
page = _ref[_i];
|
||||
_ref1 = page.threads;
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
thread = _ref1[_j];
|
||||
if (!(thread.no === ThreadStats.thread.ID)) {
|
||||
continue;
|
||||
}
|
||||
ThreadStats.pageCountEl.textContent = page.page;
|
||||
(page.page === pages.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
|
||||
(page.page === this.response.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -11279,7 +11328,7 @@
|
||||
switch (req.status) {
|
||||
case 200:
|
||||
g.DEAD = false;
|
||||
ThreadUpdater.parse(JSON.parse(req.response).posts);
|
||||
ThreadUpdater.parse(req.response.posts);
|
||||
ThreadUpdater.setInterval();
|
||||
break;
|
||||
case 404:
|
||||
@ -12334,8 +12383,8 @@
|
||||
}
|
||||
}, {
|
||||
name: "4plebs",
|
||||
boards: ["hr", "o", "pol", "s4s", "tg", "tv", "x"],
|
||||
files: ["hr", "o", "pol", "s4s", "tg", "tv", "x"],
|
||||
boards: ["adv", "hr", "o", "pol", "s4s", "tg", "tv", "x"],
|
||||
files: ["adv", "hr", "o", "pol", "s4s", "tg", "tv", "x"],
|
||||
data: {
|
||||
domain: "archive.4plebs.org",
|
||||
http: true,
|
||||
@ -12352,6 +12401,16 @@
|
||||
https: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "Love is Over",
|
||||
boards: ["d", "i"],
|
||||
files: ["d", "i"],
|
||||
data: {
|
||||
domain: "loveisover.me",
|
||||
http: true,
|
||||
https: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "Install Gentoo",
|
||||
boards: ["diy", "g", "sci"],
|
||||
@ -12367,7 +12426,7 @@
|
||||
boards: ["cgl", "g", "mu", "w"],
|
||||
files: ["cgl", "g", "mu", "w"],
|
||||
data: {
|
||||
domain: "rbt.asia",
|
||||
domain: "archive.rebeccablacktech.com",
|
||||
http: true,
|
||||
https: true,
|
||||
software: "fuuka"
|
||||
@ -12387,10 +12446,37 @@
|
||||
files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"],
|
||||
data: {
|
||||
domain: "fuuka.warosu.org",
|
||||
http: true,
|
||||
https: true,
|
||||
software: "fuuka"
|
||||
}
|
||||
}, {
|
||||
name: "fgts",
|
||||
boards: ["soc"],
|
||||
files: ["soc"],
|
||||
data: {
|
||||
domain: "fgts.eu",
|
||||
http: true,
|
||||
https: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "maware",
|
||||
boards: ["t"],
|
||||
files: ["t"],
|
||||
data: {
|
||||
domain: "archive.mawa.re",
|
||||
http: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "installgentoo.com",
|
||||
boards: ["g", "t"],
|
||||
files: ["g", "t"],
|
||||
data: {
|
||||
domain: "chan.installgentoo.com",
|
||||
http: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "Foolz Beta",
|
||||
boards: ["a", "co", "d", "gd", "h", "jp", "m", "mlp", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
|
||||
@ -12402,16 +12488,6 @@
|
||||
withCredentials: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "Love is Over",
|
||||
boards: ["d", "i"],
|
||||
files: ["d", "i"],
|
||||
data: {
|
||||
domain: "loveisover.me",
|
||||
http: true,
|
||||
https: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}
|
||||
],
|
||||
to: function(dest, data) {
|
||||
@ -14503,18 +14579,21 @@
|
||||
return a.textContent = ExpandThread.text('+', postsCount, filesCount);
|
||||
},
|
||||
parse: function(req, thread, a) {
|
||||
var data, filesCount, post, postData, posts, postsCount, postsRoot, root, _i, _len, _ref;
|
||||
var filesCount, post, postData, posts, postsCount, postsRoot, root, _i, _len, _ref, _ref1;
|
||||
if ((_ref = req.status) !== 200 && _ref !== 304) {
|
||||
a.textContent = "Error " + req.statusText + " (" + req.status + ")";
|
||||
return;
|
||||
}
|
||||
data = JSON.parse(req.response).posts;
|
||||
Build.spoilerRange[thread.board] = data.shift().custom_spoiler;
|
||||
Build.spoilerRange[thread.board] = req.response.posts[0].custom_spoiler;
|
||||
posts = [];
|
||||
postsRoot = [];
|
||||
filesCount = 0;
|
||||
for (_i = 0, _len = data.length; _i < _len; _i++) {
|
||||
postData = data[_i];
|
||||
_ref1 = req.response.posts;
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
postData = _ref1[_i];
|
||||
if (postData.no === thread.ID) {
|
||||
continue;
|
||||
}
|
||||
if (post = thread.posts[postData.no]) {
|
||||
if ('file' in post) {
|
||||
filesCount++;
|
||||
@ -15643,8 +15722,6 @@
|
||||
}
|
||||
},
|
||||
clean: function() {
|
||||
var posts, threads;
|
||||
posts = g.posts, threads = g.threads;
|
||||
g.threads.forEach(function(thread) {
|
||||
return thread.collect();
|
||||
});
|
||||
@ -15746,7 +15823,6 @@
|
||||
},
|
||||
updateBoard: function(boardID) {
|
||||
var fullBoardList, onload, req;
|
||||
req = null;
|
||||
fullBoardList = $('#full-board-list', Header.boardList);
|
||||
$.rmClass($('.current', fullBoardList), 'current');
|
||||
$.addClass($("a[href*='/" + boardID + "/']", fullBoardList), 'current');
|
||||
@ -15762,7 +15838,7 @@
|
||||
return;
|
||||
}
|
||||
try {
|
||||
_ref = JSON.parse(req.response).boards;
|
||||
_ref = req.response.boards;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
aboard = _ref[_i];
|
||||
if (!(aboard.board === boardID)) {
|
||||
@ -15905,7 +15981,7 @@
|
||||
}
|
||||
Navigate.title();
|
||||
try {
|
||||
return Navigate.parse(JSON.parse(req.response).posts);
|
||||
return Navigate.parse(req.response.posts);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
console.error('Navigate failure:');
|
||||
@ -15983,59 +16059,27 @@
|
||||
|
||||
Settings = {
|
||||
init: function() {
|
||||
var addSection, check, el, key, settings, value, _ref;
|
||||
var check, el, settings,
|
||||
_this = this;
|
||||
el = $.el('a', {
|
||||
className: 'settings-link',
|
||||
href: 'javascript:;',
|
||||
textContent: 'Settings'
|
||||
});
|
||||
$.on(el, 'click', Settings.open);
|
||||
$.on(el, 'click', this.open);
|
||||
$.event('AddMenuEntry', {
|
||||
type: 'header',
|
||||
el: el,
|
||||
order: 1
|
||||
});
|
||||
$.get('previousversion', null, function(item) {
|
||||
var changelog, curr, prev, previous;
|
||||
if (previous = item['previousversion']) {
|
||||
if (previous === g.VERSION) {
|
||||
return;
|
||||
}
|
||||
prev = previous.match(/\d+/g).map(Number);
|
||||
curr = g.VERSION.match(/\d+/g).map(Number);
|
||||
if (!(prev[0] <= curr[0] && prev[1] <= curr[1] && prev[2] <= curr[2])) {
|
||||
return;
|
||||
}
|
||||
changelog = 'https://github.com/zixaphir/appchan-x/blob/master/CHANGELOG.md';
|
||||
el = $.el('span', {
|
||||
innerHTML: "appchan x has been updated to <a href='" + changelog + "' target=_blank>version " + g.VERSION + "</a>."
|
||||
});
|
||||
if (Conf['Show Updated Notifications']) {
|
||||
new Notice('info', el, 30);
|
||||
}
|
||||
} else {
|
||||
$.on(d, '4chanXInitFinished', Settings.open);
|
||||
}
|
||||
return $.set('previousversion', g.VERSION);
|
||||
});
|
||||
addSection = Settings.addSection;
|
||||
_ref = {
|
||||
'style': 'Style',
|
||||
'themes': 'Themes',
|
||||
'mascots': 'Mascots',
|
||||
'main': 'Script',
|
||||
'filter': 'Filter',
|
||||
'sauce': 'Sauce',
|
||||
'advanced': 'Advanced',
|
||||
'keybinds': 'Keybinds'
|
||||
};
|
||||
for (key in _ref) {
|
||||
value = _ref[key];
|
||||
addSection(value, Settings[key]);
|
||||
}
|
||||
$.on(d, 'AddSettingsSection', Settings.addSection);
|
||||
this.addSection('Main', this.main);
|
||||
this.addSection('Filter', this.filter);
|
||||
this.addSection('Sauce', this.sauce);
|
||||
this.addSection('Advanced', this.advanced);
|
||||
this.addSection('Keybinds', this.keybinds);
|
||||
$.on(d, 'AddSettingsSection', this.addSection);
|
||||
$.on(d, 'OpenSettings', function(e) {
|
||||
return Settings.open(e.detail);
|
||||
return _this.open(e.detail);
|
||||
});
|
||||
settings = JSON.parse(localStorage.getItem('4chan-settings')) || {};
|
||||
if (!settings.disableAll) {
|
||||
@ -16071,13 +16115,14 @@
|
||||
Settings.dialog = dialog = $.el('div', {
|
||||
id: 'appchanx-settings',
|
||||
"class": 'dialog',
|
||||
innerHTML: "<nav><div class=sections-list></div><span class='imp-exp-result warning'></span><div class=credits><a class=export>Export</a> |<a class=import>Import</a> |<input type=file style='display: none;'><a href='http://zixaphir.github.com/appchan-x/' target=_blank>appchan x</a> |<a href='https://github.com/zixaphir/appchan-x/blob/master/CHANGELOG.md' target=_blank>" + g.VERSION + "</a> |<a href='https://github.com/zixaphir/appchan-x/blob/master/README.md#reporting-bugs-and-suggestions' target=_blank>Issues</a> |<a href=javascript:; class='close fa' title=Close>\uf00d</a></div></nav><hr><div class=section-container><section></section></div>"
|
||||
innerHTML: "<nav><div class=sections-list></div><span class='imp-exp-result warning'></span><div class=credits><a class=export>Export</a> | <a class=import>Import</a> | <a class=reset>Reset Settings</a> | <input type=file hidden><a href='http://zixaphir.github.com/appchan-x/' target=_blank>appchan x</a> |<a href='https://github.com/zixaphir/appchan-x/blob/master/CHANGELOG.md' target=_blank>" + g.VERSION + "</a> |<a href='https://github.com/zixaphir/appchan-x/blob/master/README.md#reporting-bugs-and-suggestions' target=_blank>Issues</a> |<a href=javascript:; class='close fa' title=Close>\uf00d</a></div></nav><hr><div class=section-container><section></section></div>"
|
||||
});
|
||||
Settings.overlay = overlay = $.el('div', {
|
||||
id: 'overlay'
|
||||
});
|
||||
$.on($('.export', dialog), 'click', Settings["export"]);
|
||||
$.on($('.import', dialog), 'click', Settings["import"]);
|
||||
$.on($('.reset', dialog), 'click', Settings.reset);
|
||||
$.on($('input', dialog), 'change', Settings.onImport);
|
||||
links = [];
|
||||
_ref = Settings.sections;
|
||||
@ -16137,7 +16182,7 @@
|
||||
return $.event('OpenSettings', null, section);
|
||||
},
|
||||
main: function(section) {
|
||||
var arr, button, description, div, fs, hiddenNum, input, inputs, items, key, obj, _ref;
|
||||
var arr, button, description, div, fs, input, inputs, items, key, obj, _ref;
|
||||
items = {};
|
||||
inputs = {};
|
||||
_ref = Config.main;
|
||||
@ -16173,45 +16218,34 @@
|
||||
innerHTML: "<button></button><span class=description>: Clear manually-hidden threads and posts on all boards. Reload the page to apply."
|
||||
});
|
||||
button = $('button', div);
|
||||
hiddenNum = 0;
|
||||
$.get('hiddenThreads', {
|
||||
boards: {}
|
||||
}, function(item) {
|
||||
var ID, board, thread, _ref1;
|
||||
_ref1 = item.hiddenThreads.boards;
|
||||
$.get({
|
||||
hiddenThreads: {},
|
||||
hiddenPosts: {}
|
||||
}, function(_arg) {
|
||||
var ID, board, hiddenNum, hiddenPosts, hiddenThreads, thread, _ref1, _ref2;
|
||||
hiddenThreads = _arg.hiddenThreads, hiddenPosts = _arg.hiddenPosts;
|
||||
hiddenNum = 0;
|
||||
_ref1 = hiddenThreads.boards;
|
||||
for (ID in _ref1) {
|
||||
board = _ref1[ID];
|
||||
for (ID in board) {
|
||||
thread = board[ID];
|
||||
hiddenNum++;
|
||||
}
|
||||
hiddenNum += Object.keys(board).length;
|
||||
}
|
||||
return button.textContent = "Hidden: " + hiddenNum;
|
||||
});
|
||||
$.get('hiddenPosts', {
|
||||
boards: {}
|
||||
}, function(item) {
|
||||
var ID, board, post, thread, _ref1;
|
||||
_ref1 = item.hiddenPosts.boards;
|
||||
for (ID in _ref1) {
|
||||
board = _ref1[ID];
|
||||
_ref2 = hiddenPosts.boards;
|
||||
for (ID in _ref2) {
|
||||
board = _ref2[ID];
|
||||
for (ID in board) {
|
||||
thread = board[ID];
|
||||
for (ID in thread) {
|
||||
post = thread[ID];
|
||||
hiddenNum++;
|
||||
}
|
||||
hiddenNum += Object.keys(thread).length;
|
||||
}
|
||||
}
|
||||
return button.textContent = "Hidden: " + hiddenNum;
|
||||
});
|
||||
$.on(button, 'click', function() {
|
||||
this.textContent = 'Hidden: 0';
|
||||
return $.get('hiddenThreads', {
|
||||
boards: {}
|
||||
}, function(item) {
|
||||
var boardID;
|
||||
for (boardID in item.hiddenThreads.boards) {
|
||||
return $.get('hiddenThreads', {}, function(_arg) {
|
||||
var boardID, hiddenThreads;
|
||||
hiddenThreads = _arg.hiddenThreads;
|
||||
for (boardID in hiddenThreads.boards) {
|
||||
localStorage.removeItem("4chan-hide-t-" + boardID);
|
||||
}
|
||||
return $["delete"](['hiddenThreads', 'hiddenPosts']);
|
||||
@ -16219,41 +16253,29 @@
|
||||
});
|
||||
return $.after($('input[name="Stubs"]', section).parentNode.parentNode, div);
|
||||
},
|
||||
"export": function(now, data) {
|
||||
var a, db, span, _i, _len, _ref;
|
||||
if (typeof now !== 'number') {
|
||||
now = Date.now();
|
||||
data = {
|
||||
"export": function() {
|
||||
return $.get(Conf, function(Conf) {
|
||||
delete Conf['archives'];
|
||||
return Settings.downloadExport({
|
||||
version: g.VERSION,
|
||||
date: now
|
||||
};
|
||||
_ref = DataBoard.keys;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
db = _ref[_i];
|
||||
Conf[db] = {
|
||||
boards: {}
|
||||
};
|
||||
}
|
||||
$.get(Conf, function(Conf) {
|
||||
delete Conf['archives'];
|
||||
data.Conf = Conf;
|
||||
return Settings["export"](now, data);
|
||||
date: Date.now(),
|
||||
Conf: Conf
|
||||
});
|
||||
return;
|
||||
}
|
||||
a = $.el('a', {
|
||||
className: 'warning',
|
||||
textContent: 'Save me!',
|
||||
download: "appchan x v" + g.VERSION + "-" + now + ".json",
|
||||
href: "data:application/json;base64," + (btoa(unescape(encodeURIComponent(JSON.stringify(data, null, 2))))),
|
||||
target: '_blank'
|
||||
});
|
||||
span = $('.imp-exp-result', Settings.dialog);
|
||||
$.rmAll(span);
|
||||
return $.add(span, a);
|
||||
},
|
||||
downloadExport: function(data) {
|
||||
var a, p;
|
||||
a = $.el('a', {
|
||||
download: "appchan x v" + g.VERSION + "-" + data.date + ".json",
|
||||
href: "data:application/json;base64," + (btoa(unescape(encodeURIComponent(JSON.stringify(data, null, 2)))))
|
||||
});
|
||||
p = $('.imp-exp-result', Settings.dialog);
|
||||
$.rmAll(p);
|
||||
$.add(p, a);
|
||||
return a.click();
|
||||
},
|
||||
"import": function() {
|
||||
return this.nextElementSibling.click();
|
||||
return $('input', this.parentNode).click();
|
||||
},
|
||||
onImport: function() {
|
||||
var file, output, reader;
|
||||
@ -16267,10 +16289,9 @@
|
||||
}
|
||||
reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
var data, err;
|
||||
var err;
|
||||
try {
|
||||
data = JSON.parse(e.target.result);
|
||||
Settings.loadSettings(data);
|
||||
Settings.loadSettings(JSON.parse(e.target.result));
|
||||
if (confirm('Import successful. Reload now?')) {
|
||||
return window.location.reload();
|
||||
}
|
||||
@ -16291,16 +16312,14 @@
|
||||
}
|
||||
return $.set(data.Conf);
|
||||
},
|
||||
convertSettings: function(data, map) {
|
||||
var newKey, prevKey;
|
||||
for (prevKey in map) {
|
||||
newKey = map[prevKey];
|
||||
if (newKey) {
|
||||
data.Conf[newKey] = data.Conf[prevKey];
|
||||
}
|
||||
delete data.Conf[prevKey];
|
||||
reset: function() {
|
||||
if (confirm('Your current settings will be entirely wiped, are you sure?')) {
|
||||
return $.clear(function() {
|
||||
if (confirm('Reset successful. Reload now?')) {
|
||||
return window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
return data;
|
||||
},
|
||||
filter: function(section) {
|
||||
var select;
|
||||
@ -17233,7 +17252,7 @@
|
||||
return $.ready(Main.initReady);
|
||||
},
|
||||
initReady: function() {
|
||||
var GMver, err, href, i, passLink, styleSelector, v, _i, _len, _ref, _ref1;
|
||||
var GMver, err, href, i, passLink, styleSelector, test, v, _i, _len, _ref, _ref1;
|
||||
if ((_ref = d.title) === '4chan - Temporarily Offline' || _ref === '4chan - 404 Not Found') {
|
||||
if (Conf['404 Redirect'] && g.VIEW === 'thread') {
|
||||
href = Redirect.to('thread', {
|
||||
@ -17260,14 +17279,19 @@
|
||||
} else {
|
||||
$.event('4chanXInitFinished');
|
||||
}
|
||||
test = $.el('span');
|
||||
test.classList.add('a', 'b');
|
||||
if (test.className !== 'a b') {
|
||||
new Notice('warning', "Your version of Firefox is outdated (v26 minimum) and appchan x may not operate correctly.", 30);
|
||||
}
|
||||
GMver = GM_info.version.split('.');
|
||||
_ref1 = "1.13".split('.');
|
||||
_ref1 = "1.14".split('.');
|
||||
for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) {
|
||||
v = _ref1[i];
|
||||
if (v === GMver[i]) {
|
||||
continue;
|
||||
}
|
||||
(v < GMver[i]) || new Notice('warning', "Your version of Greasemonkey is outdated (v" + GM_info.version + " instead of v1.13 minimum) and appchan x may not operate correctly.", 30);
|
||||
(v < GMver[i]) || new Notice('warning', "Your version of Greasemonkey is outdated (v" + GM_info.version + " instead of v1.14 minimum) and appchan x may not operate correctly.", 30);
|
||||
break;
|
||||
}
|
||||
try {
|
||||
@ -17308,10 +17332,27 @@
|
||||
Main.handleErrors(errors);
|
||||
}
|
||||
Main.callbackNodes(Thread, threads);
|
||||
return Main.callbackNodesDB(Post, posts, function() {
|
||||
Main.callbackNodesDB(Post, posts, function() {
|
||||
return $.event('4chanXInitFinished');
|
||||
});
|
||||
}
|
||||
return $.get('previousversion', null, function(_arg) {
|
||||
var changelog, el, previousversion;
|
||||
previousversion = _arg.previousversion;
|
||||
if (previousversion === g.VERSION) {
|
||||
return;
|
||||
}
|
||||
if (previousversion) {
|
||||
changelog = 'https://github.com/zixaphir/appchan-x/blob/master/CHANGELOG.md';
|
||||
el = $.el('span', {
|
||||
innerHTML: "appchan x has been updated to <a href='" + changelog + "' target=_blank>version " + g.VERSION + "</a>."
|
||||
});
|
||||
new Notice('info', el, 15);
|
||||
} else {
|
||||
Settings.open();
|
||||
}
|
||||
return $.set('previousversion', g.VERSION);
|
||||
});
|
||||
},
|
||||
callbackNodes: function(klass, nodes) {
|
||||
var cb, i, node;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript
|
||||
/*
|
||||
* appchan x - Version 2.8.7 - 2014-01-21
|
||||
* appchan x - Version 2.8.7 - 2014-01-26
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||
@ -89,8 +89,8 @@
|
||||
|
||||
(function() {
|
||||
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, Callbacks, CatalogLinks, Clone, Color, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, GlobalMessage, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, InfiniScroll, JSColor, Keybinds, Linkify, Main, MascotTools, Mascots, Menu, Nav, Navigate, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, RandomAccessList, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Rice, Sauce, Settings, SimpleDict, Style, ThemeTools, Themes, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, editMascot, editTheme, g, userNavigation,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
__slice = [].slice,
|
||||
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
@ -2602,6 +2602,9 @@
|
||||
return lastModified[url] = r.getResponseHeader('Last-Modified');
|
||||
});
|
||||
}
|
||||
if (/\.json$/.test(url)) {
|
||||
r.responseType = 'json';
|
||||
}
|
||||
$.extend(r, options);
|
||||
$.extend(r.upload, upCallbacks);
|
||||
r.send(form);
|
||||
@ -2690,12 +2693,16 @@
|
||||
return d.evaluate(path, root, null, 7, null);
|
||||
};
|
||||
|
||||
$.addClass = function(el, className) {
|
||||
return el.classList.add(className);
|
||||
$.addClass = function() {
|
||||
var className, el, _ref;
|
||||
el = arguments[0], className = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
return (_ref = el.classList).add.apply(_ref, className);
|
||||
};
|
||||
|
||||
$.rmClass = function(el, className) {
|
||||
return el.classList.remove(className);
|
||||
$.rmClass = function() {
|
||||
var className, el, _ref;
|
||||
el = arguments[0], className = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
return (_ref = el.classList).remove.apply(_ref, className);
|
||||
};
|
||||
|
||||
$.toggleClass = function(el, className) {
|
||||
@ -2720,12 +2727,7 @@
|
||||
})();
|
||||
|
||||
$.rmAll = function(root) {
|
||||
var node, _i, _len, _ref;
|
||||
_ref = __slice.call(root.childNodes);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
node = _ref[_i];
|
||||
root.removeChild(node);
|
||||
}
|
||||
return root.textContent = '';
|
||||
};
|
||||
|
||||
$.tn = function(s) {
|
||||
@ -2952,24 +2954,48 @@
|
||||
};
|
||||
|
||||
$.set = (function() {
|
||||
var items, localItems, set;
|
||||
items = {};
|
||||
localItems = {};
|
||||
set = $.debounce($.SECOND, function() {
|
||||
var items, setAll, setArea, timeout;
|
||||
items = {
|
||||
sync: {},
|
||||
local: {}
|
||||
};
|
||||
timeout = {};
|
||||
setArea = function(area) {
|
||||
var data;
|
||||
data = items[area];
|
||||
if (!Object.keys(data).length || timeout[area]) {
|
||||
return;
|
||||
}
|
||||
items[area] = {};
|
||||
return chrome.storage[area].set(data, function() {
|
||||
var key, val;
|
||||
if (chrome.runtime.lastError) {
|
||||
c.error(chrome.runtime.lastError.message);
|
||||
for (key in data) {
|
||||
val = data[key];
|
||||
if (!(key in items[area])) {
|
||||
items[area][key] = val;
|
||||
}
|
||||
}
|
||||
timeout[area] = setTimeout(setArea, $.MINUTE, area);
|
||||
return;
|
||||
}
|
||||
return delete timeout[area];
|
||||
});
|
||||
};
|
||||
setAll = $.debounce($.SECOND, function() {
|
||||
var err, key, _i, _len, _ref;
|
||||
_ref = $.localKeys;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
key = _ref[_i];
|
||||
if (key in items) {
|
||||
(localItems || (localItems = {}))[key] = items[key];
|
||||
delete items[key];
|
||||
if (key in items.sync) {
|
||||
items.local[key] = items.sync[key];
|
||||
delete items.sync[key];
|
||||
}
|
||||
}
|
||||
try {
|
||||
chrome.storage.local.set(localItems);
|
||||
chrome.storage.sync.set(items);
|
||||
items = {};
|
||||
return localItems = {};
|
||||
setArea('local');
|
||||
return setArea('sync');
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
return c.error(err.stack);
|
||||
@ -2977,14 +3003,30 @@
|
||||
});
|
||||
return function(key, val) {
|
||||
if (typeof key === 'string') {
|
||||
items[key] = val;
|
||||
items.sync[key] = val;
|
||||
} else {
|
||||
$.extend(items, key);
|
||||
$.extend(items.sync, key);
|
||||
}
|
||||
return set();
|
||||
return setAll();
|
||||
};
|
||||
})();
|
||||
|
||||
$.clear = function(cb) {
|
||||
var count, done;
|
||||
count = 2;
|
||||
done = function() {
|
||||
if (chrome.runtime.lastError) {
|
||||
c.error(chrome.runtime.lastError.message);
|
||||
return;
|
||||
}
|
||||
if (!--count) {
|
||||
return typeof cb === "function" ? cb() : void 0;
|
||||
}
|
||||
};
|
||||
chrome.storage.local.clear(done);
|
||||
return chrome.storage.sync.clear(done);
|
||||
};
|
||||
|
||||
$.remove = function(arr, value) {
|
||||
var i;
|
||||
i = arr.indexOf(value);
|
||||
@ -3628,7 +3670,7 @@
|
||||
}
|
||||
board = _this.data.boards[boardID];
|
||||
threads = {};
|
||||
_ref = JSON.parse(e.target.response);
|
||||
_ref = e.target.response;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
page = _ref[_i];
|
||||
_ref1 = page.threads;
|
||||
@ -4200,18 +4242,16 @@
|
||||
toggleHideBarOnScroll: function(e) {
|
||||
var hide;
|
||||
hide = this.checked;
|
||||
$.set('Header auto-hide on scroll', hide);
|
||||
$.cb.checked.call(this);
|
||||
return Header.setHideBarOnScroll(hide);
|
||||
},
|
||||
hideBarOnScroll: function() {
|
||||
var offsetY;
|
||||
offsetY = window.pageYOffset;
|
||||
if (offsetY > (Header.previousOffset || 0)) {
|
||||
$.addClass(Header.bar, 'autohide');
|
||||
$.addClass(Header.bar, 'scroll');
|
||||
$.addClass(Header.bar, 'autohide', 'scroll');
|
||||
} else {
|
||||
$.rmClass(Header.bar, 'autohide');
|
||||
$.rmClass(Header.bar, 'scroll');
|
||||
$.rmClass(Header.bar, 'autohide', 'scroll');
|
||||
}
|
||||
return Header.previousOffset = offsetY;
|
||||
},
|
||||
@ -4259,14 +4299,38 @@
|
||||
return Header.scrollTo(post);
|
||||
},
|
||||
scrollTo: function(root, down, needed) {
|
||||
var x;
|
||||
var height, x;
|
||||
if (down) {
|
||||
x = Header.getBottomOf(root);
|
||||
if (Conf['Header auto-hide on scroll'] && Conf['Bottom header']) {
|
||||
height = Header.bar.getBoundingClientRect().height;
|
||||
if (x <= 0) {
|
||||
if (!Header.isHidden()) {
|
||||
x += height;
|
||||
}
|
||||
} else {
|
||||
if (Header.isHidden()) {
|
||||
x -= height;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(needed && x >= 0)) {
|
||||
return window.scrollBy(0, -x);
|
||||
}
|
||||
} else {
|
||||
x = Header.getTopOf(root);
|
||||
if (Conf['Header auto-hide on scroll'] && !Conf['Bottom header']) {
|
||||
height = Header.bar.getBoundingClientRect().height;
|
||||
if (x >= 0) {
|
||||
if (!Header.isHidden()) {
|
||||
x += height;
|
||||
}
|
||||
} else {
|
||||
if (Header.isHidden()) {
|
||||
x -= height;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(needed && x >= 0)) {
|
||||
return window.scrollBy(0, x);
|
||||
}
|
||||
@ -4294,6 +4358,15 @@
|
||||
}
|
||||
return bottom;
|
||||
},
|
||||
isHidden: function() {
|
||||
var top;
|
||||
top = Header.bar.getBoundingClientRect().top;
|
||||
if (Conf['Bottom header']) {
|
||||
return top === doc.clientHeight;
|
||||
} else {
|
||||
return top < 0;
|
||||
}
|
||||
},
|
||||
addShortcut: function(el, icon) {
|
||||
$.addClass(el, 'shortcut');
|
||||
return $.add(Header[icon ? 'icons' : 'stats'], el);
|
||||
@ -4741,7 +4814,7 @@
|
||||
Index.board = "" + g.BOARD;
|
||||
try {
|
||||
if (req.status === 200) {
|
||||
Index.parse(JSON.parse(req.response), pageNum);
|
||||
Index.parse(req.response, pageNum);
|
||||
} else if (req.status === 304 && (pageNum != null)) {
|
||||
Index.pageNav(pageNum);
|
||||
}
|
||||
@ -5019,6 +5092,9 @@
|
||||
pageNum = Index.getCurrentPage();
|
||||
}
|
||||
} else {
|
||||
if (!Index.searchInput.dataset.searching) {
|
||||
return;
|
||||
}
|
||||
pageNum = Index.pageBeforeSearch;
|
||||
delete Index.pageBeforeSearch;
|
||||
delete Index.searchInput.dataset.searching;
|
||||
@ -5388,6 +5464,7 @@
|
||||
return $.cache(url, function() {
|
||||
return Get.archivedPost(this, boardID, postID, root, context);
|
||||
}, {
|
||||
responseType: 'json',
|
||||
withCredentials: url.archive.withCredentials
|
||||
});
|
||||
}
|
||||
@ -5428,7 +5505,7 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
posts = JSON.parse(req.response).posts;
|
||||
posts = req.response.posts;
|
||||
Build.spoilerRange[boardID] = posts[0].custom_spoiler;
|
||||
for (_i = 0, _len = posts.length; _i < _len; _i++) {
|
||||
post = posts[_i];
|
||||
@ -5464,7 +5541,7 @@
|
||||
Get.insert(post, root, context);
|
||||
return;
|
||||
}
|
||||
data = JSON.parse(req.response);
|
||||
data = req.response;
|
||||
if (data.error) {
|
||||
$.addClass(root, 'warning');
|
||||
root.textContent = data.error;
|
||||
@ -5569,6 +5646,7 @@
|
||||
this.type = type;
|
||||
this.rmEntry = __bind(this.rmEntry, this);
|
||||
this.addEntry = __bind(this.addEntry, this);
|
||||
this.onFocus = __bind(this.onFocus, this);
|
||||
this.keybinds = __bind(this.keybinds, this);
|
||||
this.close = __bind(this.close, this);
|
||||
$.on(d, 'AddMenuEntry', this.addEntry);
|
||||
@ -5733,6 +5811,11 @@
|
||||
return e.stopPropagation();
|
||||
};
|
||||
|
||||
Menu.prototype.onFocus = function(e) {
|
||||
e.stopPropagation();
|
||||
return this.focus(e.target);
|
||||
};
|
||||
|
||||
Menu.prototype.focus = function(entry) {
|
||||
var bottom, cHeight, cWidth, eRect, focused, left, right, sRect, style, submenu, top, _i, _len, _ref, _ref1, _ref2;
|
||||
while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) {
|
||||
@ -5784,10 +5867,7 @@
|
||||
var el, subEntries, subEntry, _i, _len;
|
||||
el = entry.el, subEntries = entry.subEntries;
|
||||
$.addClass(el, 'entry');
|
||||
$.on(el, 'focus mouseover', (function(e) {
|
||||
e.stopPropagation();
|
||||
return this.focus(el);
|
||||
}).bind(this));
|
||||
$.on(el, 'focus mouseover', this.onFocus);
|
||||
el.style.order = entry.order || 100;
|
||||
if (!subEntries) {
|
||||
return;
|
||||
@ -6687,7 +6767,7 @@
|
||||
return;
|
||||
}
|
||||
threads = {};
|
||||
_ref = JSON.parse(this.response);
|
||||
_ref = this.response;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
page = _ref[_i];
|
||||
_ref1 = page.threads;
|
||||
@ -7891,7 +7971,7 @@
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
case 304:
|
||||
text = "" + (service.text(JSON.parse(response.responseText)));
|
||||
text = "" + (service.text(response.response));
|
||||
if (Conf['Embedding']) {
|
||||
embed.dataset.title = text;
|
||||
}
|
||||
@ -7987,7 +8067,7 @@
|
||||
if (status !== 200 && status !== 304) {
|
||||
return div.innerHTML = "ERROR " + status;
|
||||
}
|
||||
files = JSON.parse(this.response).files;
|
||||
files = this.response.files;
|
||||
_ref = ['video/mp4', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'image/svg', 'audio/mpeg'];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
type = _ref[_i];
|
||||
@ -8880,7 +8960,7 @@
|
||||
QR.cooldown.set({
|
||||
delay: 2
|
||||
});
|
||||
} else if (err.textContent && (m = err.textContent.match(/wait\s(\d+)\ssecond/i))) {
|
||||
} else if (err.textContent && (m = err.textContent.match(/wait\s+(\d+)\s+second/i))) {
|
||||
QR.cooldown.auto = QR.captcha.isEnabled ? !!QR.captcha.captchas.length : true;
|
||||
QR.cooldown.set({
|
||||
delay: m[1]
|
||||
@ -10011,12 +10091,13 @@
|
||||
}
|
||||
return $.ajax("//api.4chan.org/" + post.board + "/res/" + post.thread + ".json", {
|
||||
onload: function() {
|
||||
var i, postObj;
|
||||
var i, postObj, posts;
|
||||
if (this.status !== 200) {
|
||||
return;
|
||||
}
|
||||
i = 0;
|
||||
while (postObj = JSON.parse(this.response).posts[i++]) {
|
||||
posts = this.response.posts;
|
||||
while (postObj = posts[i++]) {
|
||||
if (postObj.no === post.ID) {
|
||||
break;
|
||||
}
|
||||
@ -10287,27 +10368,16 @@
|
||||
}
|
||||
}
|
||||
timeoutID = setTimeout(ImageExpand.expand, 10000, post);
|
||||
return $.ajax("//a.4cdn.org/" + post.board + "/res/" + post.thread + ".json", {
|
||||
onload: function() {
|
||||
var postObj, _i, _len, _ref;
|
||||
if (this.status !== 200) {
|
||||
return $.ajax(this.src, {
|
||||
onloadend: function() {
|
||||
if (this.status !== 404) {
|
||||
return;
|
||||
}
|
||||
_ref = JSON.parse(this.response).posts;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
postObj = _ref[_i];
|
||||
if (postObj.no === post.ID) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (postObj.no !== post.ID) {
|
||||
clearTimeout(timeoutID);
|
||||
return post.kill();
|
||||
} else if (postObj.filedeleted) {
|
||||
clearTimeout(timeoutID);
|
||||
return post.kill(true);
|
||||
}
|
||||
clearTimeout(timeoutID);
|
||||
return post.kill(true);
|
||||
}
|
||||
}, {
|
||||
type: 'head'
|
||||
});
|
||||
},
|
||||
menu: {
|
||||
@ -10415,27 +10485,16 @@
|
||||
timeoutID = setTimeout((function() {
|
||||
return _this.src = post.file.URL + '?' + Date.now();
|
||||
}), 3000);
|
||||
return $.ajax("//a.4cdn.org/" + post.board + "/res/" + post.thread + ".json", {
|
||||
onload: function() {
|
||||
var postObj, _i, _len, _ref;
|
||||
if (this.status !== 200) {
|
||||
return $.ajax(this.src, {
|
||||
onloadend: function() {
|
||||
if (this.status !== 404) {
|
||||
return;
|
||||
}
|
||||
_ref = JSON.parse(this.response).posts;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
postObj = _ref[_i];
|
||||
if (postObj.no === post.ID) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (postObj.no !== post.ID) {
|
||||
clearTimeout(timeoutID);
|
||||
return post.kill();
|
||||
} else if (postObj.filedeleted) {
|
||||
clearTimeout(timeoutID);
|
||||
return post.kill(true);
|
||||
}
|
||||
clearTimeout(timeoutID);
|
||||
return post.kill(true);
|
||||
}
|
||||
}, {
|
||||
type: 'head'
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -11044,21 +11103,21 @@
|
||||
});
|
||||
},
|
||||
onThreadsLoad: function() {
|
||||
var page, pages, thread, _i, _j, _len, _len1, _ref;
|
||||
var page, thread, _i, _j, _len, _len1, _ref, _ref1;
|
||||
if (!(Conf["Page Count in Stats"] && 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];
|
||||
_ref = this.response;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
page = _ref[_i];
|
||||
_ref1 = page.threads;
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
thread = _ref1[_j];
|
||||
if (!(thread.no === ThreadStats.thread.ID)) {
|
||||
continue;
|
||||
}
|
||||
ThreadStats.pageCountEl.textContent = page.page;
|
||||
(page.page === pages.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
|
||||
(page.page === this.response.length - 1 ? $.addClass : $.rmClass)(ThreadStats.pageCountEl, 'warning');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -11262,7 +11321,7 @@
|
||||
switch (req.status) {
|
||||
case 200:
|
||||
g.DEAD = false;
|
||||
ThreadUpdater.parse(JSON.parse(req.response).posts);
|
||||
ThreadUpdater.parse(req.response.posts);
|
||||
ThreadUpdater.setInterval();
|
||||
break;
|
||||
case 404:
|
||||
@ -12323,8 +12382,8 @@
|
||||
}
|
||||
}, {
|
||||
name: "4plebs",
|
||||
boards: ["hr", "o", "pol", "s4s", "tg", "tv", "x"],
|
||||
files: ["hr", "o", "pol", "s4s", "tg", "tv", "x"],
|
||||
boards: ["adv", "hr", "o", "pol", "s4s", "tg", "tv", "x"],
|
||||
files: ["adv", "hr", "o", "pol", "s4s", "tg", "tv", "x"],
|
||||
data: {
|
||||
domain: "archive.4plebs.org",
|
||||
http: true,
|
||||
@ -12341,6 +12400,16 @@
|
||||
https: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "Love is Over",
|
||||
boards: ["d", "i"],
|
||||
files: ["d", "i"],
|
||||
data: {
|
||||
domain: "loveisover.me",
|
||||
http: true,
|
||||
https: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "Install Gentoo",
|
||||
boards: ["diy", "g", "sci"],
|
||||
@ -12356,7 +12425,7 @@
|
||||
boards: ["cgl", "g", "mu", "w"],
|
||||
files: ["cgl", "g", "mu", "w"],
|
||||
data: {
|
||||
domain: "rbt.asia",
|
||||
domain: "archive.rebeccablacktech.com",
|
||||
http: true,
|
||||
https: true,
|
||||
software: "fuuka"
|
||||
@ -12376,10 +12445,37 @@
|
||||
files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"],
|
||||
data: {
|
||||
domain: "fuuka.warosu.org",
|
||||
http: true,
|
||||
https: true,
|
||||
software: "fuuka"
|
||||
}
|
||||
}, {
|
||||
name: "fgts",
|
||||
boards: ["soc"],
|
||||
files: ["soc"],
|
||||
data: {
|
||||
domain: "fgts.eu",
|
||||
http: true,
|
||||
https: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "maware",
|
||||
boards: ["t"],
|
||||
files: ["t"],
|
||||
data: {
|
||||
domain: "archive.mawa.re",
|
||||
http: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "installgentoo.com",
|
||||
boards: ["g", "t"],
|
||||
files: ["g", "t"],
|
||||
data: {
|
||||
domain: "chan.installgentoo.com",
|
||||
http: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "Foolz Beta",
|
||||
boards: ["a", "co", "d", "gd", "h", "jp", "m", "mlp", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
|
||||
@ -12391,16 +12487,6 @@
|
||||
withCredentials: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}, {
|
||||
name: "Love is Over",
|
||||
boards: ["d", "i"],
|
||||
files: ["d", "i"],
|
||||
data: {
|
||||
domain: "loveisover.me",
|
||||
http: true,
|
||||
https: true,
|
||||
software: "foolfuuka"
|
||||
}
|
||||
}
|
||||
],
|
||||
to: function(dest, data) {
|
||||
@ -14498,18 +14584,21 @@
|
||||
return a.textContent = ExpandThread.text('+', postsCount, filesCount);
|
||||
},
|
||||
parse: function(req, thread, a) {
|
||||
var data, filesCount, post, postData, posts, postsCount, postsRoot, root, _i, _len, _ref;
|
||||
var filesCount, post, postData, posts, postsCount, postsRoot, root, _i, _len, _ref, _ref1;
|
||||
if ((_ref = req.status) !== 200 && _ref !== 304) {
|
||||
a.textContent = "Error " + req.statusText + " (" + req.status + ")";
|
||||
return;
|
||||
}
|
||||
data = JSON.parse(req.response).posts;
|
||||
Build.spoilerRange[thread.board] = data.shift().custom_spoiler;
|
||||
Build.spoilerRange[thread.board] = req.response.posts[0].custom_spoiler;
|
||||
posts = [];
|
||||
postsRoot = [];
|
||||
filesCount = 0;
|
||||
for (_i = 0, _len = data.length; _i < _len; _i++) {
|
||||
postData = data[_i];
|
||||
_ref1 = req.response.posts;
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
postData = _ref1[_i];
|
||||
if (postData.no === thread.ID) {
|
||||
continue;
|
||||
}
|
||||
if (post = thread.posts[postData.no]) {
|
||||
if ('file' in post) {
|
||||
filesCount++;
|
||||
@ -15638,8 +15727,6 @@
|
||||
}
|
||||
},
|
||||
clean: function() {
|
||||
var posts, threads;
|
||||
posts = g.posts, threads = g.threads;
|
||||
g.threads.forEach(function(thread) {
|
||||
return thread.collect();
|
||||
});
|
||||
@ -15741,7 +15828,6 @@
|
||||
},
|
||||
updateBoard: function(boardID) {
|
||||
var fullBoardList, onload, req;
|
||||
req = null;
|
||||
fullBoardList = $('#full-board-list', Header.boardList);
|
||||
$.rmClass($('.current', fullBoardList), 'current');
|
||||
$.addClass($("a[href*='/" + boardID + "/']", fullBoardList), 'current');
|
||||
@ -15757,7 +15843,7 @@
|
||||
return;
|
||||
}
|
||||
try {
|
||||
_ref = JSON.parse(req.response).boards;
|
||||
_ref = req.response.boards;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
aboard = _ref[_i];
|
||||
if (!(aboard.board === boardID)) {
|
||||
@ -15900,7 +15986,7 @@
|
||||
}
|
||||
Navigate.title();
|
||||
try {
|
||||
return Navigate.parse(JSON.parse(req.response).posts);
|
||||
return Navigate.parse(req.response.posts);
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
console.error('Navigate failure:');
|
||||
@ -15978,59 +16064,27 @@
|
||||
|
||||
Settings = {
|
||||
init: function() {
|
||||
var addSection, check, el, key, settings, value, _ref;
|
||||
var check, el, settings,
|
||||
_this = this;
|
||||
el = $.el('a', {
|
||||
className: 'settings-link',
|
||||
href: 'javascript:;',
|
||||
textContent: 'Settings'
|
||||
});
|
||||
$.on(el, 'click', Settings.open);
|
||||
$.on(el, 'click', this.open);
|
||||
$.event('AddMenuEntry', {
|
||||
type: 'header',
|
||||
el: el,
|
||||
order: 1
|
||||
});
|
||||
$.get('previousversion', null, function(item) {
|
||||
var changelog, curr, prev, previous;
|
||||
if (previous = item['previousversion']) {
|
||||
if (previous === g.VERSION) {
|
||||
return;
|
||||
}
|
||||
prev = previous.match(/\d+/g).map(Number);
|
||||
curr = g.VERSION.match(/\d+/g).map(Number);
|
||||
if (!(prev[0] <= curr[0] && prev[1] <= curr[1] && prev[2] <= curr[2])) {
|
||||
return;
|
||||
}
|
||||
changelog = 'https://github.com/zixaphir/appchan-x/blob/master/CHANGELOG.md';
|
||||
el = $.el('span', {
|
||||
innerHTML: "appchan x has been updated to <a href='" + changelog + "' target=_blank>version " + g.VERSION + "</a>."
|
||||
});
|
||||
if (Conf['Show Updated Notifications']) {
|
||||
new Notice('info', el, 30);
|
||||
}
|
||||
} else {
|
||||
$.on(d, '4chanXInitFinished', Settings.open);
|
||||
}
|
||||
return $.set('previousversion', g.VERSION);
|
||||
});
|
||||
addSection = Settings.addSection;
|
||||
_ref = {
|
||||
'style': 'Style',
|
||||
'themes': 'Themes',
|
||||
'mascots': 'Mascots',
|
||||
'main': 'Script',
|
||||
'filter': 'Filter',
|
||||
'sauce': 'Sauce',
|
||||
'advanced': 'Advanced',
|
||||
'keybinds': 'Keybinds'
|
||||
};
|
||||
for (key in _ref) {
|
||||
value = _ref[key];
|
||||
addSection(value, Settings[key]);
|
||||
}
|
||||
$.on(d, 'AddSettingsSection', Settings.addSection);
|
||||
this.addSection('Main', this.main);
|
||||
this.addSection('Filter', this.filter);
|
||||
this.addSection('Sauce', this.sauce);
|
||||
this.addSection('Advanced', this.advanced);
|
||||
this.addSection('Keybinds', this.keybinds);
|
||||
$.on(d, 'AddSettingsSection', this.addSection);
|
||||
$.on(d, 'OpenSettings', function(e) {
|
||||
return Settings.open(e.detail);
|
||||
return _this.open(e.detail);
|
||||
});
|
||||
settings = JSON.parse(localStorage.getItem('4chan-settings')) || {};
|
||||
if (!settings.disableAll) {
|
||||
@ -16066,13 +16120,14 @@
|
||||
Settings.dialog = dialog = $.el('div', {
|
||||
id: 'appchanx-settings',
|
||||
"class": 'dialog',
|
||||
innerHTML: "<nav><div class=sections-list></div><span class='imp-exp-result warning'></span><div class=credits><a class=export>Export</a> |<a class=import>Import</a> |<input type=file style='display: none;'><a href='http://zixaphir.github.com/appchan-x/' target=_blank>appchan x</a> |<a href='https://github.com/zixaphir/appchan-x/blob/master/CHANGELOG.md' target=_blank>" + g.VERSION + "</a> |<a href='https://github.com/zixaphir/appchan-x/blob/master/README.md#reporting-bugs-and-suggestions' target=_blank>Issues</a> |<a href=javascript:; class='close fa' title=Close>\uf00d</a></div></nav><hr><div class=section-container><section></section></div>"
|
||||
innerHTML: "<nav><div class=sections-list></div><span class='imp-exp-result warning'></span><div class=credits><a class=export>Export</a> | <a class=import>Import</a> | <a class=reset>Reset Settings</a> | <input type=file hidden><a href='http://zixaphir.github.com/appchan-x/' target=_blank>appchan x</a> |<a href='https://github.com/zixaphir/appchan-x/blob/master/CHANGELOG.md' target=_blank>" + g.VERSION + "</a> |<a href='https://github.com/zixaphir/appchan-x/blob/master/README.md#reporting-bugs-and-suggestions' target=_blank>Issues</a> |<a href=javascript:; class='close fa' title=Close>\uf00d</a></div></nav><hr><div class=section-container><section></section></div>"
|
||||
});
|
||||
Settings.overlay = overlay = $.el('div', {
|
||||
id: 'overlay'
|
||||
});
|
||||
$.on($('.export', dialog), 'click', Settings["export"]);
|
||||
$.on($('.import', dialog), 'click', Settings["import"]);
|
||||
$.on($('.reset', dialog), 'click', Settings.reset);
|
||||
$.on($('input', dialog), 'change', Settings.onImport);
|
||||
links = [];
|
||||
_ref = Settings.sections;
|
||||
@ -16132,7 +16187,7 @@
|
||||
return $.event('OpenSettings', null, section);
|
||||
},
|
||||
main: function(section) {
|
||||
var arr, button, description, div, fs, hiddenNum, input, inputs, items, key, obj, _ref;
|
||||
var arr, button, description, div, fs, input, inputs, items, key, obj, _ref;
|
||||
items = {};
|
||||
inputs = {};
|
||||
_ref = Config.main;
|
||||
@ -16168,45 +16223,34 @@
|
||||
innerHTML: "<button></button><span class=description>: Clear manually-hidden threads and posts on all boards. Reload the page to apply."
|
||||
});
|
||||
button = $('button', div);
|
||||
hiddenNum = 0;
|
||||
$.get('hiddenThreads', {
|
||||
boards: {}
|
||||
}, function(item) {
|
||||
var ID, board, thread, _ref1;
|
||||
_ref1 = item.hiddenThreads.boards;
|
||||
$.get({
|
||||
hiddenThreads: {},
|
||||
hiddenPosts: {}
|
||||
}, function(_arg) {
|
||||
var ID, board, hiddenNum, hiddenPosts, hiddenThreads, thread, _ref1, _ref2;
|
||||
hiddenThreads = _arg.hiddenThreads, hiddenPosts = _arg.hiddenPosts;
|
||||
hiddenNum = 0;
|
||||
_ref1 = hiddenThreads.boards;
|
||||
for (ID in _ref1) {
|
||||
board = _ref1[ID];
|
||||
for (ID in board) {
|
||||
thread = board[ID];
|
||||
hiddenNum++;
|
||||
}
|
||||
hiddenNum += Object.keys(board).length;
|
||||
}
|
||||
return button.textContent = "Hidden: " + hiddenNum;
|
||||
});
|
||||
$.get('hiddenPosts', {
|
||||
boards: {}
|
||||
}, function(item) {
|
||||
var ID, board, post, thread, _ref1;
|
||||
_ref1 = item.hiddenPosts.boards;
|
||||
for (ID in _ref1) {
|
||||
board = _ref1[ID];
|
||||
_ref2 = hiddenPosts.boards;
|
||||
for (ID in _ref2) {
|
||||
board = _ref2[ID];
|
||||
for (ID in board) {
|
||||
thread = board[ID];
|
||||
for (ID in thread) {
|
||||
post = thread[ID];
|
||||
hiddenNum++;
|
||||
}
|
||||
hiddenNum += Object.keys(thread).length;
|
||||
}
|
||||
}
|
||||
return button.textContent = "Hidden: " + hiddenNum;
|
||||
});
|
||||
$.on(button, 'click', function() {
|
||||
this.textContent = 'Hidden: 0';
|
||||
return $.get('hiddenThreads', {
|
||||
boards: {}
|
||||
}, function(item) {
|
||||
var boardID;
|
||||
for (boardID in item.hiddenThreads.boards) {
|
||||
return $.get('hiddenThreads', {}, function(_arg) {
|
||||
var boardID, hiddenThreads;
|
||||
hiddenThreads = _arg.hiddenThreads;
|
||||
for (boardID in hiddenThreads.boards) {
|
||||
localStorage.removeItem("4chan-hide-t-" + boardID);
|
||||
}
|
||||
return $["delete"](['hiddenThreads', 'hiddenPosts']);
|
||||
@ -16214,39 +16258,26 @@
|
||||
});
|
||||
return $.after($('input[name="Stubs"]', section).parentNode.parentNode, div);
|
||||
},
|
||||
"export": function(now, data) {
|
||||
var a, db, _i, _len, _ref;
|
||||
if (typeof now !== 'number') {
|
||||
now = Date.now();
|
||||
data = {
|
||||
"export": function() {
|
||||
return $.get(Conf, function(Conf) {
|
||||
delete Conf['archives'];
|
||||
return Settings.downloadExport({
|
||||
version: g.VERSION,
|
||||
date: now
|
||||
};
|
||||
_ref = DataBoard.keys;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
db = _ref[_i];
|
||||
Conf[db] = {
|
||||
boards: {}
|
||||
};
|
||||
}
|
||||
$.get(Conf, function(Conf) {
|
||||
delete Conf['archives'];
|
||||
data.Conf = Conf;
|
||||
return Settings["export"](now, data);
|
||||
date: Date.now(),
|
||||
Conf: Conf
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
},
|
||||
downloadExport: function(data) {
|
||||
var a;
|
||||
a = $.el('a', {
|
||||
className: 'warning',
|
||||
textContent: 'Save me!',
|
||||
download: "appchan x v" + g.VERSION + "-" + now + ".json",
|
||||
href: "data:application/json;base64," + (btoa(unescape(encodeURIComponent(JSON.stringify(data, null, 2))))),
|
||||
target: '_blank'
|
||||
download: "appchan x v" + g.VERSION + "-" + data.date + ".json",
|
||||
href: "data:application/json;base64," + (btoa(unescape(encodeURIComponent(JSON.stringify(data, null, 2)))))
|
||||
});
|
||||
return a.click();
|
||||
},
|
||||
"import": function() {
|
||||
return this.nextElementSibling.click();
|
||||
return $('input', this.parentNode).click();
|
||||
},
|
||||
onImport: function() {
|
||||
var file, output, reader;
|
||||
@ -16260,10 +16291,9 @@
|
||||
}
|
||||
reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
var data, err;
|
||||
var err;
|
||||
try {
|
||||
data = JSON.parse(e.target.result);
|
||||
Settings.loadSettings(data);
|
||||
Settings.loadSettings(JSON.parse(e.target.result));
|
||||
if (confirm('Import successful. Reload now?')) {
|
||||
return window.location.reload();
|
||||
}
|
||||
@ -16284,16 +16314,14 @@
|
||||
}
|
||||
return $.set(data.Conf);
|
||||
},
|
||||
convertSettings: function(data, map) {
|
||||
var newKey, prevKey;
|
||||
for (prevKey in map) {
|
||||
newKey = map[prevKey];
|
||||
if (newKey) {
|
||||
data.Conf[newKey] = data.Conf[prevKey];
|
||||
}
|
||||
delete data.Conf[prevKey];
|
||||
reset: function() {
|
||||
if (confirm('Your current settings will be entirely wiped, are you sure?')) {
|
||||
return $.clear(function() {
|
||||
if (confirm('Reset successful. Reload now?')) {
|
||||
return window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
return data;
|
||||
},
|
||||
filter: function(section) {
|
||||
var select;
|
||||
@ -17285,10 +17313,27 @@
|
||||
Main.handleErrors(errors);
|
||||
}
|
||||
Main.callbackNodes(Thread, threads);
|
||||
return Main.callbackNodesDB(Post, posts, function() {
|
||||
Main.callbackNodesDB(Post, posts, function() {
|
||||
return $.event('4chanXInitFinished');
|
||||
});
|
||||
}
|
||||
return $.get('previousversion', null, function(_arg) {
|
||||
var changelog, el, previousversion;
|
||||
previousversion = _arg.previousversion;
|
||||
if (previousversion === g.VERSION) {
|
||||
return;
|
||||
}
|
||||
if (previousversion) {
|
||||
changelog = 'https://github.com/zixaphir/appchan-x/blob/master/CHANGELOG.md';
|
||||
el = $.el('span', {
|
||||
innerHTML: "appchan x has been updated to <a href='" + changelog + "' target=_blank>version " + g.VERSION + "</a>."
|
||||
});
|
||||
new Notice('info', el, 15);
|
||||
} else {
|
||||
Settings.open();
|
||||
}
|
||||
return $.set('previousversion', g.VERSION);
|
||||
});
|
||||
},
|
||||
callbackNodes: function(klass, nodes) {
|
||||
var cb, i, node;
|
||||
|
||||
18
package.json
18
package.json
@ -22,22 +22,22 @@
|
||||
"min": {
|
||||
"chrome": "31",
|
||||
"firefox": "26",
|
||||
"greasemonkey": "1.13"
|
||||
"greasemonkey": "1.14"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"font-awesome": "https://github.com/FortAwesome/Font-Awesome/archive/v4.0.3.tar.gz",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-bump": "~0.0.11",
|
||||
"grunt-concurrent": "~0.4.0",
|
||||
"font-awesome": "~4.0.3",
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-bump": "~0.0.13",
|
||||
"grunt-concurrent": "~0.4.3",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-coffee": "~0.8.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-coffee": "~0.8.2",
|
||||
"grunt-contrib-compress": "~0.6.0",
|
||||
"grunt-contrib-concat": "~0.3.0",
|
||||
"grunt-contrib-copy": "~0.5.0",
|
||||
"grunt-contrib-watch": "~0.5.3",
|
||||
"grunt-shell": "~0.6.0",
|
||||
"load-grunt-tasks": "~0.2.0"
|
||||
"grunt-shell": "~0.6.3",
|
||||
"load-grunt-tasks": "~0.2.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
Redirect =
|
||||
|
||||
init: ->
|
||||
o =
|
||||
thread: {}
|
||||
@ -52,8 +51,8 @@ Redirect =
|
||||
software: "foolfuuka"
|
||||
,
|
||||
name: "4plebs"
|
||||
boards: ["hr", "o", "pol", "s4s", "tg", "tv", "x"]
|
||||
files: ["hr", "o", "pol", "s4s", "tg", "tv", "x"]
|
||||
boards: ["adv", "hr", "o", "pol", "s4s", "tg", "tv", "x"]
|
||||
files: ["adv", "hr", "o", "pol", "s4s", "tg", "tv", "x"]
|
||||
data:
|
||||
domain: "archive.4plebs.org"
|
||||
http: true
|
||||
@ -68,6 +67,15 @@ Redirect =
|
||||
http: true
|
||||
https: true
|
||||
software: "foolfuuka"
|
||||
,
|
||||
name: "Love is Over"
|
||||
boards: ["d", "i"],
|
||||
files: ["d", "i"]
|
||||
data:
|
||||
domain: "loveisover.me"
|
||||
http: true
|
||||
https: true
|
||||
software: "foolfuuka"
|
||||
,
|
||||
name: "Install Gentoo"
|
||||
boards: ["diy", "g", "sci"]
|
||||
@ -82,7 +90,7 @@ Redirect =
|
||||
boards: ["cgl", "g", "mu", "w"]
|
||||
files: ["cgl", "g", "mu", "w"]
|
||||
data:
|
||||
domain: "rbt.asia"
|
||||
domain: "archive.rebeccablacktech.com"
|
||||
http: true
|
||||
https: true
|
||||
software: "fuuka"
|
||||
@ -100,9 +108,33 @@ Redirect =
|
||||
files: ["3", "cgl", "ck", "fa", "ic", "jp", "lit", "tg", "vr"]
|
||||
data:
|
||||
domain: "fuuka.warosu.org"
|
||||
http: true
|
||||
https: true
|
||||
software: "fuuka"
|
||||
,
|
||||
name: "fgts"
|
||||
boards: ["soc"]
|
||||
files: ["soc"]
|
||||
data:
|
||||
domain: "fgts.eu"
|
||||
http: true
|
||||
https: true
|
||||
software: "foolfuuka"
|
||||
,
|
||||
name: "maware"
|
||||
boards: ["t"]
|
||||
files: ["t"]
|
||||
data:
|
||||
domain: "archive.mawa.re"
|
||||
http: true
|
||||
software: "foolfuuka"
|
||||
,
|
||||
name: "installgentoo.com"
|
||||
boards: ["g", "t"]
|
||||
files: ["g", "t"]
|
||||
data:
|
||||
domain: "chan.installgentoo.com"
|
||||
http: true
|
||||
software: "foolfuuka"
|
||||
,
|
||||
name: "Foolz Beta"
|
||||
boards: ["a", "co", "d", "gd", "h", "jp", "m", "mlp", "sp", "tg", "tv", "u", "v", "vg", "vp", "vr", "wsg"],
|
||||
@ -113,15 +145,6 @@ Redirect =
|
||||
https: true
|
||||
withCredentials: true
|
||||
software: "foolfuuka"
|
||||
,
|
||||
name: "Love is Over"
|
||||
boards: ["d", "i"],
|
||||
files: ["d", "i"]
|
||||
data:
|
||||
domain: "loveisover.me"
|
||||
http: true
|
||||
https: true
|
||||
software: "foolfuuka"
|
||||
]
|
||||
|
||||
to: (dest, data) ->
|
||||
|
||||
@ -58,7 +58,7 @@ ThreadHiding =
|
||||
$.cache "//a.4cdn.org/#{g.BOARD}/threads.json", ->
|
||||
return unless @status is 200
|
||||
threads = {}
|
||||
for page in JSON.parse @response
|
||||
for page in @response
|
||||
for thread in page.threads
|
||||
if thread.no of hiddenThreadsOnCatalog
|
||||
threads[thread.no] = hiddenThreadsOnCatalog[thread.no]
|
||||
|
||||
@ -80,6 +80,7 @@ Get =
|
||||
$.cache url,
|
||||
-> Get.archivedPost @, boardID, postID, root, context
|
||||
,
|
||||
responseType: 'json'
|
||||
withCredentials: url.archive.withCredentials
|
||||
insert: (post, root, context) ->
|
||||
# Stop here if the container has been removed while loading.
|
||||
@ -118,7 +119,7 @@ Get =
|
||||
"Error #{req.statusText} (#{req.status})."
|
||||
return
|
||||
|
||||
posts = JSON.parse(req.response).posts
|
||||
{posts} = req.response
|
||||
Build.spoilerRange[boardID] = posts[0].custom_spoiler
|
||||
for post in posts
|
||||
break if post.no is postID # we found it!
|
||||
@ -149,7 +150,7 @@ Get =
|
||||
Get.insert post, root, context
|
||||
return
|
||||
|
||||
data = JSON.parse req.response
|
||||
data = req.response
|
||||
if data.error
|
||||
$.addClass root, 'warning'
|
||||
root.textContent = data.error
|
||||
@ -227,4 +228,4 @@ Get =
|
||||
'[/moot]': '</div>'
|
||||
'[banned]': '<strong style="color: red;">'
|
||||
'[/banned]': '</strong>'
|
||||
}[text] or text.replace ':lit', ''
|
||||
}[text] or text.replace ':lit', ''
|
||||
|
||||
@ -254,17 +254,15 @@ Header =
|
||||
|
||||
toggleHideBarOnScroll: (e) ->
|
||||
hide = @checked
|
||||
$.set 'Header auto-hide on scroll', hide
|
||||
$.cb.checked.call @
|
||||
Header.setHideBarOnScroll hide
|
||||
|
||||
hideBarOnScroll: ->
|
||||
offsetY = window.pageYOffset
|
||||
if offsetY > (Header.previousOffset or 0)
|
||||
$.addClass Header.bar, 'autohide'
|
||||
$.addClass Header.bar, 'scroll'
|
||||
$.addClass Header.bar, 'autohide', 'scroll'
|
||||
else
|
||||
$.rmClass Header.bar, 'autohide'
|
||||
$.rmClass Header.bar, 'scroll'
|
||||
$.rmClass Header.bar, 'autohide', 'scroll'
|
||||
Header.previousOffset = offsetY
|
||||
|
||||
setBarPosition: (bottom) ->
|
||||
@ -320,9 +318,21 @@ Header =
|
||||
scrollTo: (root, down, needed) ->
|
||||
if down
|
||||
x = Header.getBottomOf root
|
||||
if Conf['Header auto-hide on scroll'] and Conf['Bottom header']
|
||||
{height} = Header.bar.getBoundingClientRect()
|
||||
if x <= 0
|
||||
x += height if !Header.isHidden()
|
||||
else
|
||||
x -= height if Header.isHidden()
|
||||
window.scrollBy 0, -x unless needed and x >= 0
|
||||
else
|
||||
x = Header.getTopOf root
|
||||
if Conf['Header auto-hide on scroll'] and !Conf['Bottom header']
|
||||
{height} = Header.bar.getBoundingClientRect()
|
||||
if x >= 0
|
||||
x += height if !Header.isHidden()
|
||||
else
|
||||
x -= height if Header.isHidden()
|
||||
window.scrollBy 0, x unless needed and x >= 0
|
||||
|
||||
scrollToIfNeeded: (root, down) ->
|
||||
@ -342,6 +352,12 @@ Header =
|
||||
headRect = Header.toggle.getBoundingClientRect()
|
||||
bottom -= clientHeight - headRect.bottom + headRect.height
|
||||
bottom
|
||||
isHidden: ->
|
||||
{top} = Header.bar.getBoundingClientRect()
|
||||
if Conf['Bottom header']
|
||||
top is doc.clientHeight
|
||||
else
|
||||
top < 0
|
||||
|
||||
addShortcut: (el, icon) ->
|
||||
$.addClass el, 'shortcut'
|
||||
|
||||
@ -274,7 +274,7 @@ Index =
|
||||
|
||||
try
|
||||
if req.status is 200
|
||||
Index.parse JSON.parse(req.response), pageNum
|
||||
Index.parse req.response, pageNum
|
||||
else if req.status is 304 and pageNum?
|
||||
Index.pageNav pageNum
|
||||
catch err
|
||||
@ -470,6 +470,7 @@ Index =
|
||||
else
|
||||
pageNum = Index.getCurrentPage()
|
||||
else
|
||||
return unless Index.searchInput.dataset.searching
|
||||
pageNum = Index.pageBeforeSearch
|
||||
delete Index.pageBeforeSearch
|
||||
<% if (type === 'userscript') { %>
|
||||
|
||||
@ -125,12 +125,18 @@ Main =
|
||||
'left=0,top=0,width=500,height=255,toolbar=0,resizable=0'
|
||||
$.before styleSelector.previousSibling, [$.tn '['; passLink, $.tn ']\u00A0\u00A0']
|
||||
|
||||
# Parse HTML or skip it and start building from JSON.
|
||||
unless Conf['JSON Navigation'] and g.VIEW is 'index'
|
||||
Main.initThread()
|
||||
else
|
||||
$.event '4chanXInitFinished'
|
||||
|
||||
<% if (type === 'userscript') { %>
|
||||
test = $.el 'span'
|
||||
test.classList.add 'a', 'b'
|
||||
if test.className isnt 'a b'
|
||||
new Notice 'warning', "Your version of Firefox is outdated (v<%= meta.min.firefox %> minimum) and <%= meta.name %> may not operate correctly.", 30
|
||||
|
||||
GMver = GM_info.version.split '.'
|
||||
for v, i in "<%= meta.min.greasemonkey %>".split '.'
|
||||
continue if v is GMver[i]
|
||||
@ -167,6 +173,17 @@ Main =
|
||||
Main.callbackNodesDB Post, posts, ->
|
||||
$.event '4chanXInitFinished'
|
||||
|
||||
$.get 'previousversion', null, ({previousversion}) ->
|
||||
return if previousversion is g.VERSION
|
||||
if previousversion
|
||||
changelog = '<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md'
|
||||
el = $.el 'span',
|
||||
innerHTML: "<%= meta.name %> has been updated to <a href='#{changelog}' target=_blank>version #{g.VERSION}</a>."
|
||||
new Notice 'info', el, 15
|
||||
else
|
||||
Settings.open()
|
||||
$.set 'previousversion', g.VERSION
|
||||
|
||||
callbackNodes: (klass, nodes) ->
|
||||
i = 0
|
||||
cb = klass.callbacks
|
||||
|
||||
@ -33,15 +33,11 @@ Navigate =
|
||||
return
|
||||
|
||||
clean: ->
|
||||
{posts, threads} = g
|
||||
|
||||
# Garbage collection
|
||||
g.threads.forEach (thread) -> thread.collect()
|
||||
|
||||
QuoteBacklink.containers = {}
|
||||
|
||||
# Delete nodes
|
||||
$.rmAll $ '.board'
|
||||
$.rmAll $('.board')
|
||||
|
||||
features: [
|
||||
['Thread Excerpt', ThreadExcerpt]
|
||||
@ -110,8 +106,6 @@ Navigate =
|
||||
}[g.VIEW]()
|
||||
|
||||
updateBoard: (boardID) ->
|
||||
req = null
|
||||
|
||||
fullBoardList = $ '#full-board-list', Header.boardList
|
||||
$.rmClass $('.current', fullBoardList), 'current'
|
||||
$.addClass $("a[href*='/#{boardID}/']", fullBoardList), 'current'
|
||||
@ -127,7 +121,7 @@ Navigate =
|
||||
return unless req.status is 200
|
||||
|
||||
try
|
||||
for aboard in JSON.parse(req.response).boards when aboard.board is boardID
|
||||
for aboard in req.response.boards when aboard.board is boardID
|
||||
board = aboard
|
||||
break
|
||||
|
||||
@ -243,7 +237,7 @@ Navigate =
|
||||
Navigate.title()
|
||||
|
||||
try
|
||||
Navigate.parse JSON.parse(req.response).posts
|
||||
Navigate.parse req.response.posts
|
||||
catch err
|
||||
console.error 'Navigate failure:'
|
||||
console.log err
|
||||
|
||||
@ -5,45 +5,21 @@ Settings =
|
||||
className: 'settings-link'
|
||||
href: 'javascript:;'
|
||||
textContent: 'Settings'
|
||||
$.on el, 'click', Settings.open
|
||||
$.on el, 'click', @open
|
||||
|
||||
$.event 'AddMenuEntry',
|
||||
type: 'header'
|
||||
el: el
|
||||
order: 1
|
||||
|
||||
$.get 'previousversion', null, (item) ->
|
||||
if previous = item['previousversion']
|
||||
return if previous is g.VERSION
|
||||
# Avoid conflicts between sync'd newer versions
|
||||
# and out of date extension on this device.
|
||||
prev = previous.match(/\d+/g).map Number
|
||||
curr = g.VERSION.match(/\d+/g).map Number
|
||||
return unless prev[0] <= curr[0] and prev[1] <= curr[1] and prev[2] <= curr[2]
|
||||
@addSection 'Main', @main
|
||||
@addSection 'Filter', @filter
|
||||
@addSection 'Sauce', @sauce
|
||||
@addSection 'Advanced', @advanced
|
||||
@addSection 'Keybinds', @keybinds
|
||||
|
||||
changelog = '<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md'
|
||||
el = $.el 'span',
|
||||
innerHTML: "<%= meta.name %> has been updated to <a href='#{changelog}' target=_blank>version #{g.VERSION}</a>."
|
||||
if Conf['Show Updated Notifications']
|
||||
new Notice 'info', el, 30
|
||||
else
|
||||
$.on d, '4chanXInitFinished', Settings.open
|
||||
$.set 'previousversion', g.VERSION
|
||||
|
||||
{addSection} = Settings
|
||||
addSection value, Settings[key] for key, value of {
|
||||
'style': 'Style'
|
||||
'themes': 'Themes'
|
||||
'mascots': 'Mascots'
|
||||
'main': 'Script'
|
||||
'filter': 'Filter'
|
||||
'sauce': 'Sauce'
|
||||
'advanced': 'Advanced'
|
||||
'keybinds': 'Keybinds'
|
||||
}
|
||||
|
||||
$.on d, 'AddSettingsSection', Settings.addSection
|
||||
$.on d, 'OpenSettings', (e) -> Settings.open e.detail
|
||||
$.on d, 'AddSettingsSection', @addSection
|
||||
$.on d, 'OpenSettings', (e) => @open e.detail
|
||||
|
||||
settings = JSON.parse(localStorage.getItem '4chan-settings') or {}
|
||||
unless settings.disableAll
|
||||
@ -79,8 +55,10 @@ Settings =
|
||||
|
||||
$.on $('.export', dialog), 'click', Settings.export
|
||||
$.on $('.import', dialog), 'click', Settings.import
|
||||
$.on $('.reset', dialog), 'click', Settings.reset
|
||||
$.on $('input', dialog), 'change', Settings.onImport
|
||||
|
||||
|
||||
links = []
|
||||
for section in Settings.sections
|
||||
link = $.el 'a',
|
||||
@ -153,58 +131,41 @@ Settings =
|
||||
div = $.el 'div',
|
||||
innerHTML: "<button></button><span class=description>: Clear manually-hidden threads and posts on all boards. Reload the page to apply."
|
||||
button = $ 'button', div
|
||||
hiddenNum = 0
|
||||
$.get 'hiddenThreads', boards: {}, (item) ->
|
||||
for ID, board of item.hiddenThreads.boards
|
||||
$.get {hiddenThreads: {}, hiddenPosts: {}}, ({hiddenThreads, hiddenPosts}) ->
|
||||
hiddenNum = 0
|
||||
for ID, board of hiddenThreads.boards
|
||||
hiddenNum += Object.keys(board).length
|
||||
for ID, board of hiddenPosts.boards
|
||||
for ID, thread of board
|
||||
hiddenNum++
|
||||
button.textContent = "Hidden: #{hiddenNum}"
|
||||
$.get 'hiddenPosts', boards: {}, (item) ->
|
||||
for ID, board of item.hiddenPosts.boards
|
||||
for ID, thread of board
|
||||
for ID, post of thread
|
||||
hiddenNum++
|
||||
hiddenNum += Object.keys(thread).length
|
||||
button.textContent = "Hidden: #{hiddenNum}"
|
||||
$.on button, 'click', ->
|
||||
@textContent = 'Hidden: 0'
|
||||
$.get 'hiddenThreads', boards: {}, (item) ->
|
||||
for boardID of item.hiddenThreads.boards
|
||||
$.get 'hiddenThreads', {}, ({hiddenThreads}) ->
|
||||
for boardID of hiddenThreads.boards
|
||||
localStorage.removeItem "4chan-hide-t-#{boardID}"
|
||||
$.delete ['hiddenThreads', 'hiddenPosts']
|
||||
$.after $('input[name="Stubs"]', section).parentNode.parentNode, div
|
||||
|
||||
export: (now, data) ->
|
||||
unless typeof now is 'number'
|
||||
now = Date.now()
|
||||
data =
|
||||
version: g.VERSION
|
||||
date: now
|
||||
for db in DataBoard.keys
|
||||
Conf[db] = boards: {}
|
||||
# Make sure to export the most recent data.
|
||||
$.get Conf, (Conf) ->
|
||||
# XXX don't export archives.
|
||||
delete Conf['archives']
|
||||
data.Conf = Conf
|
||||
Settings.export now, data
|
||||
return
|
||||
a = $.el 'a',
|
||||
className: 'warning'
|
||||
textContent: 'Save me!'
|
||||
download: "<%= meta.name %> v#{g.VERSION}-#{now}.json"
|
||||
href: "data:application/json;base64,#{btoa unescape encodeURIComponent JSON.stringify data, null, 2}"
|
||||
target: '_blank'
|
||||
<% if (type !== 'userscript') { %>
|
||||
a.click()
|
||||
<% } else { %>
|
||||
# XXX Firefox won't let us download automatically.
|
||||
span = $ '.imp-exp-result', Settings.dialog
|
||||
$.rmAll span
|
||||
$.add span, a
|
||||
<% } %>
|
||||
export: ->
|
||||
# Make sure to export the most recent data.
|
||||
$.get Conf, (Conf) ->
|
||||
# XXX don't export archives.
|
||||
delete Conf['archives']
|
||||
Settings.downloadExport {version: g.VERSION, date: Date.now(), Conf}
|
||||
|
||||
downloadExport: (data) ->
|
||||
a = $.el 'a',
|
||||
download: "<%= meta.name %> v#{g.VERSION}-#{data.date}.json"
|
||||
href: "data:application/json;base64,#{btoa unescape encodeURIComponent JSON.stringify data, null, 2}"
|
||||
<% if (type === 'userscript') { %>
|
||||
p = $ '.imp-exp-result', Settings.dialog
|
||||
$.rmAll p
|
||||
$.add p, a
|
||||
<% } %>
|
||||
a.click()
|
||||
import: ->
|
||||
@nextElementSibling.click()
|
||||
$('input', @parentNode).click()
|
||||
|
||||
onImport: ->
|
||||
return unless file = @files[0]
|
||||
@ -215,8 +176,7 @@ Settings =
|
||||
reader = new FileReader()
|
||||
reader.onload = (e) ->
|
||||
try
|
||||
data = JSON.parse e.target.result
|
||||
Settings.loadSettings data
|
||||
Settings.loadSettings JSON.parse e.target.result
|
||||
if confirm 'Import successful. Reload now?'
|
||||
window.location.reload()
|
||||
catch err
|
||||
@ -230,11 +190,9 @@ Settings =
|
||||
delete data.Conf['WatchedThreads']
|
||||
$.set data.Conf
|
||||
|
||||
convertSettings: (data, map) ->
|
||||
for prevKey, newKey of map
|
||||
data.Conf[newKey] = data.Conf[prevKey] if newKey
|
||||
delete data.Conf[prevKey]
|
||||
data
|
||||
reset: ->
|
||||
if confirm 'Your current settings will be entirely wiped, are you sure?'
|
||||
$.clear -> window.location.reload() if confirm 'Reset successful. Reload now?'
|
||||
|
||||
filter: (section) ->
|
||||
section.innerHTML = <%= importHTML('Settings/Filter-select') %>
|
||||
|
||||
@ -154,6 +154,9 @@ UI = do ->
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
onFocus: (e) =>
|
||||
e.stopPropagation()
|
||||
@focus e.target
|
||||
focus: (entry) ->
|
||||
while focused = $.x 'parent::*/child::*[contains(@class,"focused")]', entry
|
||||
$.rmClass focused, 'focused'
|
||||
@ -196,10 +199,7 @@ UI = do ->
|
||||
parseEntry: (entry) ->
|
||||
{el, subEntries} = entry
|
||||
$.addClass el, 'entry'
|
||||
$.on el, 'focus mouseover', ((e) ->
|
||||
e.stopPropagation()
|
||||
@focus el
|
||||
).bind @
|
||||
$.on el, 'focus mouseover', @onFocus
|
||||
el.style.order = entry.order or 100
|
||||
return unless subEntries
|
||||
$.addClass el, 'has-submenu'
|
||||
|
||||
@ -653,6 +653,15 @@ span.hide-announcement {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dashed;
|
||||
}
|
||||
@supports (text-decoration-style: dashed) or (-moz-text-decoration-style: dashed) {
|
||||
.quotelink.forwardlink,
|
||||
.backlink.forwardlink {
|
||||
text-decoration: underline;
|
||||
-moz-text-decoration-style: dashed;
|
||||
text-decoration-style: dashed;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
.filtered {
|
||||
text-decoration: underline line-through;
|
||||
}
|
||||
@ -1189,7 +1198,7 @@ a:only-of-type > .remove {
|
||||
left: 0px;
|
||||
width: 200px;
|
||||
}
|
||||
.export, .import {
|
||||
.export, .import, .reset {
|
||||
cursor: pointer;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
@ -2,9 +2,10 @@
|
||||
<div class=sections-list></div>
|
||||
<span class='imp-exp-result warning'></span>
|
||||
<div class=credits>
|
||||
<a class=export>Export</a> |
|
||||
<a class=import>Import</a> |
|
||||
<input type=file style='display: none;'>
|
||||
<a class=export>Export</a> | 
|
||||
<a class=import>Import</a> | 
|
||||
<a class=reset>Reset Settings</a> | 
|
||||
<input type=file hidden>
|
||||
<a href='<%= meta.page %>' target=_blank><%= meta.name %></a> |
|
||||
<a href='<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md' target=_blank>#{g.VERSION}</a> |
|
||||
<a href='<%= meta.repo %>blob/<%= meta.mainBranch %>/README.md#reporting-bugs-and-suggestions' target=_blank>Issues</a> |
|
||||
|
||||
@ -54,6 +54,8 @@ $.ajax = do ->
|
||||
if whenModified
|
||||
r.setRequestHeader 'If-Modified-Since', lastModified[url] if url of lastModified
|
||||
$.on r, 'load', -> lastModified[url] = r.getResponseHeader 'Last-Modified'
|
||||
if /\.json$/.test url
|
||||
r.responseType = 'json'
|
||||
$.extend r, options
|
||||
$.extend r.upload, upCallbacks
|
||||
r.send form
|
||||
@ -113,11 +115,11 @@ $.X = (path, root) ->
|
||||
# XPathResult.ORDERED_NODE_SNAPSHOT_TYPE === 7
|
||||
d.evaluate path, root, null, 7, null
|
||||
|
||||
$.addClass = (el, className) ->
|
||||
el.classList.add className
|
||||
$.addClass = (el, className...) ->
|
||||
el.classList.add className...
|
||||
|
||||
$.rmClass = (el, className) ->
|
||||
el.classList.remove className
|
||||
$.rmClass = (el, className...) ->
|
||||
el.classList.remove className...
|
||||
|
||||
$.toggleClass = (el, className) ->
|
||||
el.classList.toggle className
|
||||
@ -131,12 +133,8 @@ $.rm = do ->
|
||||
else
|
||||
(el) -> el.parentNode?.removeChild el
|
||||
|
||||
$.rmAll = (root) ->
|
||||
# jsperf.com/emptify-element
|
||||
for node in [root.childNodes...]
|
||||
# HTMLSelectElement.remove !== Element.remove
|
||||
root.removeChild node
|
||||
return
|
||||
# jsperf.com/emptify-element/9
|
||||
$.rmAll = (root) -> root.textContent = ''
|
||||
|
||||
$.tn = (s) ->
|
||||
d.createTextNode s
|
||||
@ -332,29 +330,50 @@ $.get = (key, val, cb) ->
|
||||
chrome.storage.sync.get syncItems, done
|
||||
|
||||
$.set = do ->
|
||||
items = {}
|
||||
localItems = {}
|
||||
items =
|
||||
sync: {}
|
||||
local: {}
|
||||
timeout = {}
|
||||
|
||||
set = $.debounce $.SECOND, ->
|
||||
setArea = (area) ->
|
||||
data = items[area]
|
||||
return if !Object.keys(data).length or timeout[area]
|
||||
items[area] = {}
|
||||
chrome.storage[area].set data, ->
|
||||
if chrome.runtime.lastError
|
||||
c.error chrome.runtime.lastError.message
|
||||
for key, val of data when key not of items[area]
|
||||
items[area][key] = val
|
||||
timeout[area] = setTimeout setArea, $.MINUTE, area
|
||||
return
|
||||
delete timeout[area]
|
||||
|
||||
setAll = $.debounce $.SECOND, ->
|
||||
for key in $.localKeys
|
||||
if key of items
|
||||
(localItems or= {})[key] = items[key]
|
||||
delete items[key]
|
||||
if key of items.sync
|
||||
items.local[key] = items.sync[key]
|
||||
delete items.sync[key]
|
||||
try
|
||||
chrome.storage.local.set localItems
|
||||
chrome.storage.sync.set items
|
||||
items = {}
|
||||
localItems = {}
|
||||
setArea 'local'
|
||||
setArea 'sync'
|
||||
catch err
|
||||
c.error err.stack
|
||||
|
||||
(key, val) ->
|
||||
if typeof key is 'string'
|
||||
items[key] = val
|
||||
items.sync[key] = val
|
||||
else
|
||||
$.extend items, key
|
||||
set()
|
||||
|
||||
$.extend items.sync, key
|
||||
setAll()
|
||||
$.clear = (cb) ->
|
||||
count = 2
|
||||
done = ->
|
||||
if chrome.runtime.lastError
|
||||
c.error chrome.runtime.lastError.message
|
||||
return
|
||||
cb?() unless --count
|
||||
chrome.storage.local.clear done
|
||||
chrome.storage.sync.clear done
|
||||
<% } else { %>
|
||||
|
||||
# http://wiki.greasespot.net/Main_Page
|
||||
@ -402,6 +421,9 @@ $.set = do ->
|
||||
for key, val of keys
|
||||
set key, val
|
||||
return
|
||||
$.clear = (cb) ->
|
||||
$.delete GM_listValues().map (key) -> key.replace g.NAMESPACE, ''
|
||||
cb?()
|
||||
<% } %>
|
||||
|
||||
$.remove = (arr, value) ->
|
||||
|
||||
@ -78,7 +78,7 @@ class DataBoard
|
||||
return
|
||||
board = @data.boards[boardID]
|
||||
threads = {}
|
||||
for page in JSON.parse e.target.response
|
||||
for page in e.target.response
|
||||
for thread in page.threads
|
||||
if thread.no of board
|
||||
threads[thread.no] = board[thread.no]
|
||||
@ -93,4 +93,4 @@ class DataBoard
|
||||
disconnect: ->
|
||||
$.desync @key
|
||||
delete @sync
|
||||
delete @data
|
||||
delete @data
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
// @grant GM_getValue
|
||||
// @grant GM_setValue
|
||||
// @grant GM_deleteValue
|
||||
// @grant GM_listValues
|
||||
// @grant GM_openInTab
|
||||
// @run-at document-start
|
||||
// @updateURL <%= meta.repo %>raw/stable/builds/<%= meta.files.metajs %>
|
||||
|
||||
@ -203,7 +203,8 @@ Gallery =
|
||||
$.ajax "//api.4chan.org/#{post.board}/res/#{post.thread}.json", onload: ->
|
||||
return if @status isnt 200
|
||||
i = 0
|
||||
while postObj = JSON.parse(@response).posts[i++]
|
||||
{posts} = @response
|
||||
while postObj = posts[i++]
|
||||
break if postObj.no is post.ID
|
||||
unless postObj.no
|
||||
return post.kill()
|
||||
|
||||
@ -156,10 +156,19 @@ ImageExpand =
|
||||
return
|
||||
|
||||
timeoutID = setTimeout ImageExpand.expand, 10000, post
|
||||
<% if (type === 'crx') { %>
|
||||
$.ajax @src,
|
||||
onloadend: ->
|
||||
return if @status isnt 404
|
||||
clearTimeout timeoutID
|
||||
post.kill true
|
||||
,
|
||||
type: 'head'
|
||||
<% } else { %>
|
||||
# XXX CORS for i.4cdn.org WHEN?
|
||||
$.ajax "//a.4cdn.org/#{post.board}/res/#{post.thread}.json", onload: ->
|
||||
return if @status isnt 200
|
||||
for postObj in JSON.parse(@response).posts
|
||||
for postObj in @response.posts
|
||||
break if postObj.no is post.ID
|
||||
if postObj.no isnt post.ID
|
||||
clearTimeout timeoutID
|
||||
@ -167,6 +176,7 @@ ImageExpand =
|
||||
else if postObj.filedeleted
|
||||
clearTimeout timeoutID
|
||||
post.kill true
|
||||
<% } %>
|
||||
|
||||
menu:
|
||||
init: ->
|
||||
|
||||
@ -38,10 +38,19 @@ ImageHover =
|
||||
return
|
||||
|
||||
timeoutID = setTimeout (=> @src = post.file.URL + '?' + Date.now()), 3000
|
||||
<% if (type === 'crx') { %>
|
||||
$.ajax @src,
|
||||
onloadend: ->
|
||||
return if @status isnt 404
|
||||
clearTimeout timeoutID
|
||||
post.kill true
|
||||
,
|
||||
type: 'head'
|
||||
<% } else { %>
|
||||
# XXX CORS for i.4cdn.org WHEN?
|
||||
$.ajax "//a.4cdn.org/#{post.board}/res/#{post.thread}.json", onload: ->
|
||||
return if @status isnt 200
|
||||
for postObj in JSON.parse(@response).posts
|
||||
for postObj in @response.posts
|
||||
break if postObj.no is post.ID
|
||||
if postObj.no isnt post.ID
|
||||
clearTimeout timeoutID
|
||||
@ -49,3 +58,4 @@ ImageHover =
|
||||
else if postObj.filedeleted
|
||||
clearTimeout timeoutID
|
||||
post.kill true
|
||||
<% } %>
|
||||
|
||||
@ -241,7 +241,7 @@ Linkify =
|
||||
service = Linkify.types[key].title
|
||||
switch response.status
|
||||
when 200, 304
|
||||
text = "#{service.text JSON.parse response.responseText}"
|
||||
text = "#{service.text response.response}"
|
||||
if Conf['Embedding']
|
||||
embed.dataset.title = text
|
||||
when 404
|
||||
@ -304,7 +304,7 @@ Linkify =
|
||||
$.cache "https://mediacru.sh/#{a.dataset.uid}.json", ->
|
||||
{status} = @
|
||||
return div.innerHTML = "ERROR #{status}" unless status in [200, 304]
|
||||
{files} = JSON.parse @response
|
||||
{files} = @response
|
||||
for type in ['video/mp4', 'video/ogv', 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg', 'image/svg', 'audio/mpeg']
|
||||
for file in files
|
||||
if file.type is type
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
PSAHiding =
|
||||
init: ->
|
||||
return unless Conf['Announcement Hiding']
|
||||
|
||||
$.addClass doc, 'hide-announcement'
|
||||
|
||||
$.on d, '4chanXInitFinished', @setup
|
||||
|
||||
setup: ->
|
||||
$.off d, '4chanXInitFinished', PSAHiding.setup
|
||||
|
||||
|
||||
@ -77,13 +77,13 @@ ExpandThread =
|
||||
a.textContent = "Error #{req.statusText} (#{req.status})"
|
||||
return
|
||||
|
||||
data = JSON.parse(req.response).posts
|
||||
Build.spoilerRange[thread.board] = data.shift().custom_spoiler
|
||||
Build.spoilerRange[thread.board] = req.response.posts[0].custom_spoiler
|
||||
|
||||
posts = []
|
||||
postsRoot = []
|
||||
filesCount = 0
|
||||
for postData in data
|
||||
for postData in req.response.posts
|
||||
continue if postData.no is thread.ID
|
||||
if post = thread.posts[postData.no]
|
||||
filesCount++ if 'file' of post
|
||||
postsRoot.push post.nodes.root
|
||||
|
||||
@ -77,9 +77,8 @@ ThreadStats =
|
||||
|
||||
onThreadsLoad: ->
|
||||
return unless Conf["Page Count in Stats"] and @status is 200
|
||||
pages = JSON.parse @response
|
||||
for page in pages
|
||||
for page in @response
|
||||
for thread in page.threads when 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'
|
||||
(if page.page is @response.length - 1 then $.addClass else $.rmClass) ThreadStats.pageCountEl, 'warning'
|
||||
return
|
||||
|
||||
@ -159,7 +159,7 @@ ThreadUpdater =
|
||||
switch req.status
|
||||
when 200
|
||||
g.DEAD = false
|
||||
ThreadUpdater.parse JSON.parse(req.response).posts
|
||||
ThreadUpdater.parse req.response.posts
|
||||
ThreadUpdater.setInterval()
|
||||
when 404
|
||||
g.DEAD = true
|
||||
|
||||
@ -695,7 +695,7 @@ QR =
|
||||
# Too many frequent mistyped captchas will auto-ban you!
|
||||
# On connection error, the post most likely didn't go through.
|
||||
QR.cooldown.set delay: 2
|
||||
else if err.textContent and m = err.textContent.match /wait\s(\d+)\ssecond/i
|
||||
else if err.textContent and m = err.textContent.match /wait\s+(\d+)\s+second/i
|
||||
QR.cooldown.auto = if QR.captcha.isEnabled
|
||||
!!QR.captcha.captchas.length
|
||||
else
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user