Merge branch 'master' of https://github.com/seaweedchan/4chan-x into v3
Conflicts: CHANGELOG.md LICENSE builds/4chan-X.meta.js builds/4chan-X.user.js builds/crx/script.js json/archives.json
This commit is contained in:
commit
7fb48b93d4
66
CHANGELOG.md
66
CHANGELOG.md
@ -1,18 +1,9 @@
|
||||
<<<<<<< HEAD
|
||||
**Zixaphir**:
|
||||
- Better MediaCru.sh embedding
|
||||
- Infinite Scrolling
|
||||
|
||||
### v1.2.41
|
||||
*2013-10-03*
|
||||
=======
|
||||
## 3.14.0 - *2013-11-21*
|
||||
|
||||
**MayhemYDG**:
|
||||
- Tiny posting cooldown adjustment:
|
||||
- You can post an image reply immediately after a non-image reply.
|
||||
- **New option**: `Auto-hide header on scroll`.
|
||||
- Added support for `4cdn.org`.
|
||||
|
||||
## 3.13.0 - *2013-11-16*
|
||||
|
||||
- More index navigation improvements:
|
||||
- Searching in the index is now possible and will show matched OPs by:
|
||||
<ul>
|
||||
@ -31,14 +22,7 @@
|
||||
</ul>
|
||||
- The elapsed time since the last index refresh is now indicated at the top of the index.
|
||||
- New setting: `Show replies`, enabled by default. Disable it to only show OPs in the index.
|
||||
|
||||
### 3.12.1 - *2013-11-04*
|
||||
|
||||
- The index refreshing notification will now only appear on initial page load with slow connections.
|
||||
- Minor fixes.
|
||||
|
||||
## 3.12.0 - *2013-11-03*
|
||||
|
||||
- Index navigation improvements:
|
||||
- You can now refresh the index page you are on with the refresh shortcut in the header bar or the same keybind for refreshing threads.
|
||||
- You can now switch between paged and all-threads index modes via the "Index Navigation" header sub-menu:<br>
|
||||
@ -53,48 +37,43 @@
|
||||
</ul>
|
||||
- Navigating across index pages is now instantaneous.
|
||||
- Added a keybind to open the catalog search field on index pages.
|
||||
- Various minor fixes
|
||||
|
||||
### 3.11.5 - *2013-10-03*
|
||||
### v1.2.43
|
||||
*2013-11-10*
|
||||
|
||||
- Minor Chrome 30 fix.
|
||||
>>>>>>> ce0c0c1623702e7931908183160fa04a31b26897
|
||||
**noface**:
|
||||
- Strawpoll.me embedding support (as usual, only works on HTTP 4chan due to lack of HTTPS)
|
||||
|
||||
### v1.2.42
|
||||
*2013-10-22*
|
||||
|
||||
**Zixaphir**:
|
||||
- Better MediaCru.sh embedding
|
||||
- Infinite Scrolling
|
||||
|
||||
### v1.2.41
|
||||
*2013-10-03*
|
||||
|
||||
**MayhemYDG**:
|
||||
- Minor Chrome 30 fix
|
||||
|
||||
<<<<<<< HEAD
|
||||
### v1.2.40
|
||||
*2013-09-22*
|
||||
=======
|
||||
- Tiny posting cooldown adjustment:
|
||||
- You can post an image reply immediately after a non-image reply.
|
||||
>>>>>>> ce0c0c1623702e7931908183160fa04a31b26897
|
||||
|
||||
**MayhemYDG**:
|
||||
- /pol/ flag selector
|
||||
|
||||
<<<<<<< HEAD
|
||||
**seaweedchan**:
|
||||
- Delete cooldown update
|
||||
- Small bug fixes
|
||||
- Don't show warnings AND desktop notifications at the same time, and prefer QR warnings unless the document is hidden
|
||||
=======
|
||||
- Update posting cooldown timers to match 4chan settings:
|
||||
- Cooldown may vary between inter-thread and intra-thread replies.
|
||||
- Cooldown may vary when posting a file or not.
|
||||
- Cooldown does not take sageing into account anymore.
|
||||
- Timers vary across boards.
|
||||
>>>>>>> ce0c0c1623702e7931908183160fa04a31b26897
|
||||
|
||||
### v1.2.39
|
||||
*2013-09-19*
|
||||
|
||||
<<<<<<< HEAD
|
||||
**seaweedchan**:
|
||||
- Fix thread updater bug introduced in last version
|
||||
=======
|
||||
- Updated post and deletion cooldown timers to match 4chan changes: they are now twice as long.
|
||||
>>>>>>> ce0c0c1623702e7931908183160fa04a31b26897
|
||||
|
||||
### v1.2.38
|
||||
*2013-09-19*
|
||||
@ -436,17 +415,8 @@
|
||||
### v1.2.3
|
||||
*2013-05-14*
|
||||
|
||||
<<<<<<< HEAD
|
||||
**MayhemYDG**:
|
||||
- Add new archive selection
|
||||
=======
|
||||
- **New feature**: `Archive selection`
|
||||
- Select which archive you want for specific boards and redirection type.
|
||||
- Access it in the `Archives` tab of the Settings window.
|
||||
- The list of archived boards will now update automatically, independently from 4chan X updates.
|
||||
- If you're an archiver and want [data](https://github.com/MayhemYDG/4chan-x/blob/v3/json/archives.json) about your archive to be updated, added or removed: send a PR or open an issue.
|
||||
- Fix quote previews getting 'stuck' in Opera.
|
||||
>>>>>>> ce0c0c1623702e7931908183160fa04a31b26897
|
||||
|
||||
**seaweedchan**:
|
||||
- Change watcher favicon to a heart. Change class name from `.favicon` to `.watch-thread-link`. Add `.watched` if thread is watched.
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 4chan X - Version 1.2.41 - 2013-11-22
|
||||
* 4chan X - Version 1.2.43 - 2013-11-23
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// ==UserScript==
|
||||
// @name 4chan X
|
||||
// @version 1.2.41
|
||||
// @version 1.2.43
|
||||
// @minGMVer 1.12
|
||||
// @minFFVer 22
|
||||
// @namespace 4chan-X
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Generated by CoffeeScript
|
||||
// ==UserScript==
|
||||
// @name 4chan X
|
||||
// @version 1.2.41
|
||||
// @version 1.2.43
|
||||
// @minGMVer 1.12
|
||||
// @minFFVer 22
|
||||
// @namespace 4chan-X
|
||||
@ -22,7 +22,7 @@
|
||||
// ==/UserScript==
|
||||
|
||||
/*
|
||||
* 4chan X - Version 1.2.41 - 2013-11-22
|
||||
* 4chan X - Version 1.2.43 - 2013-11-23
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||
@ -340,7 +340,7 @@
|
||||
doc = d.documentElement;
|
||||
|
||||
g = {
|
||||
VERSION: '1.2.41',
|
||||
VERSION: '1.2.43',
|
||||
NAMESPACE: '4chan X.',
|
||||
boards: {},
|
||||
threads: {},
|
||||
@ -5718,6 +5718,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
StrawPoll: {
|
||||
regExp: /strawpoll\.me\/(?:embed_\d+\/)?(\d+)/,
|
||||
style: 'border: 0; width: 600px; height: 406px;',
|
||||
el: function(a) {
|
||||
return $.el('iframe', {
|
||||
src: "http://strawpoll.me/embed_1/" + a.dataset.uid
|
||||
});
|
||||
}
|
||||
},
|
||||
TwitchTV: {
|
||||
regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/,
|
||||
style: "border: none; width: 640px; height: 360px;",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "4chan X",
|
||||
"version": "1.2.41",
|
||||
"version": "1.2.43",
|
||||
"manifest_version": 2,
|
||||
"description": "Cross-browser userscript for maximum lurking on 4chan.",
|
||||
"icons": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Generated by CoffeeScript
|
||||
/*
|
||||
* 4chan X - Version 1.2.41 - 2013-11-22
|
||||
* 4chan X - Version 1.2.43 - 2013-11-23
|
||||
*
|
||||
* Licensed under the MIT license.
|
||||
* https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||
@ -82,7 +82,7 @@
|
||||
'use strict';
|
||||
|
||||
(function() {
|
||||
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, InfiniScroll, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g,
|
||||
var $, $$, Anonymize, ArchiveLink, AutoGIF, Banner, Board, Build, CatalogLinks, Clone, Conf, Config, CustomCSS, DataBoard, DeleteLink, Dice, DownloadLink, Emoji, ExpandComment, ExpandThread, FappeTyme, Favicon, FileInfo, Filter, Fourchan, Gallery, Get, Header, IDColor, ImageExpand, ImageHover, ImageLoader, Index, InfiniScroll, Keybinds, Linkify, Main, Menu, Nav, Notice, PSAHiding, Polyfill, Post, PostHiding, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, QuoteStrikeThrough, QuoteThreading, QuoteYou, Quotify, Recursive, Redirect, RelativeDates, RemoveSpoilers, Report, ReportLink, RevealSpoilers, Sauce, Settings, Thread, ThreadExcerpt, ThreadHiding, ThreadStats, ThreadUpdater, ThreadWatcher, Time, UI, Unread, c, d, doc, g,
|
||||
__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,
|
||||
@ -233,6 +233,11 @@
|
||||
'sageEmoji': '4chan SS',
|
||||
'emojiPos': 'before',
|
||||
'Custom CSS': false,
|
||||
Index: {
|
||||
'Index Mode': 'paged',
|
||||
'Index Sort': 'bump',
|
||||
'Show Replies': true
|
||||
},
|
||||
Header: {
|
||||
'Fixed Header': true,
|
||||
'Header auto-hide': false,
|
||||
@ -313,7 +318,7 @@
|
||||
doc = d.documentElement;
|
||||
|
||||
g = {
|
||||
VERSION: '1.2.41',
|
||||
VERSION: '1.2.43',
|
||||
NAMESPACE: '4chan X.',
|
||||
boards: {},
|
||||
threads: {},
|
||||
@ -2054,6 +2059,610 @@
|
||||
}
|
||||
};
|
||||
|
||||
Index = {
|
||||
init: function() {
|
||||
var input, label, modeEntry, repliesEntry, sortEntry, _i, _j, _len, _len1, _ref, _ref1;
|
||||
|
||||
if (g.VIEW !== 'index' || g.BOARD.ID === 'f') {
|
||||
return;
|
||||
}
|
||||
this.button = $.el('a', {
|
||||
className: 'index-refresh-shortcut fa fa-refresh',
|
||||
title: 'Refresh Index',
|
||||
href: 'javascript:;'
|
||||
});
|
||||
$.on(this.button, 'click', this.update);
|
||||
Header.addShortcut(this.button, 1);
|
||||
modeEntry = {
|
||||
el: $.el('span', {
|
||||
textContent: 'Index mode'
|
||||
}),
|
||||
subEntries: [
|
||||
{
|
||||
el: $.el('label', {
|
||||
innerHTML: '<input type=radio name="Index Mode" value="paged"> Paged'
|
||||
})
|
||||
}, {
|
||||
el: $.el('label', {
|
||||
innerHTML: '<input type=radio name="Index Mode" value="all pages"> All threads'
|
||||
})
|
||||
}
|
||||
]
|
||||
};
|
||||
_ref = modeEntry.subEntries;
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
label = _ref[_i];
|
||||
input = label.el.firstChild;
|
||||
input.checked = Conf['Index Mode'] === input.value;
|
||||
$.on(input, 'change', $.cb.value);
|
||||
$.on(input, 'change', this.cb.mode);
|
||||
}
|
||||
sortEntry = {
|
||||
el: $.el('span', {
|
||||
textContent: 'Sort by'
|
||||
}),
|
||||
subEntries: [
|
||||
{
|
||||
el: $.el('label', {
|
||||
innerHTML: '<input type=radio name="Index Sort" value="bump"> Bump order'
|
||||
})
|
||||
}, {
|
||||
el: $.el('label', {
|
||||
innerHTML: '<input type=radio name="Index Sort" value="lastreply"> Last reply'
|
||||
})
|
||||
}, {
|
||||
el: $.el('label', {
|
||||
innerHTML: '<input type=radio name="Index Sort" value="birth"> Creation date'
|
||||
})
|
||||
}, {
|
||||
el: $.el('label', {
|
||||
innerHTML: '<input type=radio name="Index Sort" value="replycount"> Reply count'
|
||||
})
|
||||
}, {
|
||||
el: $.el('label', {
|
||||
innerHTML: '<input type=radio name="Index Sort" value="filecount"> File count'
|
||||
})
|
||||
}
|
||||
]
|
||||
};
|
||||
_ref1 = sortEntry.subEntries;
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
label = _ref1[_j];
|
||||
input = label.el.firstChild;
|
||||
input.checked = Conf['Index Sort'] === input.value;
|
||||
$.on(input, 'change', $.cb.value);
|
||||
$.on(input, 'change', this.cb.sort);
|
||||
}
|
||||
repliesEntry = {
|
||||
el: $.el('label', {
|
||||
innerHTML: '<input type=checkbox name="Show Replies"> Show replies'
|
||||
})
|
||||
};
|
||||
input = repliesEntry.el.firstChild;
|
||||
input.checked = Conf['Show Replies'];
|
||||
$.on(input, 'change', $.cb.checked);
|
||||
$.on(input, 'change', this.cb.replies);
|
||||
$.event('AddMenuEntry', {
|
||||
type: 'header',
|
||||
el: $.el('span', {
|
||||
textContent: 'Index Navigation'
|
||||
}),
|
||||
order: 90,
|
||||
subEntries: [modeEntry, sortEntry, repliesEntry]
|
||||
});
|
||||
$.addClass(doc, 'index-loading');
|
||||
this.update();
|
||||
this.root = $.el('div', {
|
||||
className: 'board'
|
||||
});
|
||||
this.pagelist = $.el('div', {
|
||||
className: 'pagelist',
|
||||
hidden: true,
|
||||
innerHTML: "<div class=\"prev\"><a><button disabled>Previous</button></a></div><div class=\"pages\"></div><div class=\"next\"><a><button disabled>Next</button></a></div><div class=\"pages cataloglink\"><a href=\"./catalog\">Catalog</a></div>"
|
||||
});
|
||||
this.navLinks = $.el('div', {
|
||||
className: 'navLinks',
|
||||
innerHTML: "[<a href=\"./catalog\">Catalog</a>] [<time id=\"index-last-refresh\" title=\"Last index refresh\">...</time>] <input type=\"search\" id=\"index-search\" class=\"field\" placeholder=\"Search\"><a id=\"index-search-clear\" class=\"fa fa-times-circle\" href=\"javascript:;\"></a>"
|
||||
});
|
||||
this.searchInput = $('#index-search', this.navLinks);
|
||||
this.currentPage = this.getCurrentPage();
|
||||
$.on(window, 'popstate', this.cb.popstate);
|
||||
$.on(this.pagelist, 'click', this.cb.pageNav);
|
||||
$.on(this.searchInput, 'input', this.onSearchInput);
|
||||
$.on($('#index-search-clear', this.navLinks), 'click', this.clearSearch);
|
||||
return $.asap((function() {
|
||||
return $('.board', doc) || d.readyState !== 'loading';
|
||||
}), function() {
|
||||
var board, navLink, _k, _len2, _ref2;
|
||||
|
||||
board = $('.board');
|
||||
$.replace(board, Index.root);
|
||||
d.implementation.createDocument(null, null, null).appendChild(board);
|
||||
_ref2 = $$('.navLinks');
|
||||
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
|
||||
navLink = _ref2[_k];
|
||||
$.rm(navLink);
|
||||
}
|
||||
$.after($.x('child::form/preceding-sibling::hr[1]'), Index.navLinks);
|
||||
$.rmClass(doc, 'index-loading');
|
||||
return $.asap((function() {
|
||||
return $('.pagelist') || d.readyState !== 'loading';
|
||||
}), function() {
|
||||
return $.replace($('.pagelist'), Index.pagelist);
|
||||
});
|
||||
});
|
||||
},
|
||||
cb: {
|
||||
mode: function() {
|
||||
Index.togglePagelist();
|
||||
return Index.buildIndex();
|
||||
},
|
||||
sort: function() {
|
||||
Index.sort();
|
||||
return Index.buildIndex();
|
||||
},
|
||||
replies: function() {
|
||||
Index.buildThreads();
|
||||
Index.sort();
|
||||
return Index.buildIndex();
|
||||
},
|
||||
popstate: function(e) {
|
||||
var pageNum;
|
||||
|
||||
pageNum = Index.getCurrentPage();
|
||||
if (Index.currentPage !== pageNum) {
|
||||
return Index.pageLoad(pageNum);
|
||||
}
|
||||
},
|
||||
pageNav: function(e) {
|
||||
var a;
|
||||
|
||||
if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
switch (e.target.nodeName) {
|
||||
case 'BUTTON':
|
||||
a = e.target.parentNode;
|
||||
break;
|
||||
case 'A':
|
||||
a = e.target;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (a.textContent === 'Catalog') {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
return Index.pageNav(+a.pathname.split('/')[2]);
|
||||
}
|
||||
},
|
||||
scrollToIndex: function() {
|
||||
return Header.scrollToIfNeeded(Index.root);
|
||||
},
|
||||
getCurrentPage: function() {
|
||||
return +window.location.pathname.split('/')[2];
|
||||
},
|
||||
pageNav: function(pageNum) {
|
||||
if (Index.currentPage === pageNum) {
|
||||
return;
|
||||
}
|
||||
history.pushState(null, '', pageNum === 0 ? './' : pageNum);
|
||||
return Index.pageLoad(pageNum);
|
||||
},
|
||||
pageLoad: function(pageNum) {
|
||||
Index.currentPage = pageNum;
|
||||
if (Conf['Index Mode'] !== 'paged') {
|
||||
return;
|
||||
}
|
||||
Index.buildIndex();
|
||||
Index.setPage();
|
||||
return Index.scrollToIndex();
|
||||
},
|
||||
getPagesNum: function() {
|
||||
if (Index.isSearching) {
|
||||
return Math.ceil((Index.sortedNodes.length / 2) / Index.threadsNumPerPage);
|
||||
} else {
|
||||
return Index.pagesNum;
|
||||
}
|
||||
},
|
||||
getMaxPageNum: function() {
|
||||
return Math.max(0, Index.getPagesNum() - 1);
|
||||
},
|
||||
togglePagelist: function() {
|
||||
return Index.pagelist.hidden = Conf['Index Mode'] !== 'paged';
|
||||
},
|
||||
buildPagelist: function() {
|
||||
var a, i, maxPageNum, nodes, pagesRoot, _i;
|
||||
|
||||
pagesRoot = $('.pages', Index.pagelist);
|
||||
maxPageNum = Index.getMaxPageNum();
|
||||
if (pagesRoot.childElementCount !== maxPageNum + 1) {
|
||||
nodes = [];
|
||||
for (i = _i = 0; _i <= maxPageNum; i = _i += 1) {
|
||||
a = $.el('a', {
|
||||
textContent: i,
|
||||
href: i ? i : './'
|
||||
});
|
||||
nodes.push($.tn('['), a, $.tn('] '));
|
||||
}
|
||||
$.rmAll(pagesRoot);
|
||||
$.add(pagesRoot, nodes);
|
||||
}
|
||||
return Index.togglePagelist();
|
||||
},
|
||||
setPage: function() {
|
||||
var a, href, maxPageNum, next, pageNum, pagesRoot, prev, strong;
|
||||
|
||||
pageNum = Index.getCurrentPage();
|
||||
maxPageNum = Index.getMaxPageNum();
|
||||
pagesRoot = $('.pages', Index.pagelist);
|
||||
prev = pagesRoot.previousSibling.firstChild;
|
||||
next = pagesRoot.nextSibling.firstChild;
|
||||
href = Math.max(pageNum - 1, 0);
|
||||
prev.href = href === 0 ? './' : href;
|
||||
prev.firstChild.disabled = href === pageNum;
|
||||
href = Math.min(pageNum + 1, maxPageNum);
|
||||
next.href = href === 0 ? './' : href;
|
||||
next.firstChild.disabled = href === pageNum;
|
||||
if (strong = $('strong', pagesRoot)) {
|
||||
if (+strong.textContent === pageNum) {
|
||||
return;
|
||||
}
|
||||
$.replace(strong, strong.firstChild);
|
||||
} else {
|
||||
strong = $.el('strong');
|
||||
}
|
||||
a = pagesRoot.children[pageNum];
|
||||
$.before(a, strong);
|
||||
return $.add(strong, a);
|
||||
},
|
||||
update: function() {
|
||||
var now, _ref, _ref1;
|
||||
|
||||
if (!navigator.onLine) {
|
||||
return;
|
||||
}
|
||||
if ((_ref = Index.req) != null) {
|
||||
_ref.abort();
|
||||
}
|
||||
if ((_ref1 = Index.notice) != null) {
|
||||
_ref1.close();
|
||||
}
|
||||
if (d.readyState !== 'loading') {
|
||||
Index.notice = new Notice('info', 'Refreshing index...');
|
||||
} else {
|
||||
now = Date.now();
|
||||
$.ready(function() {
|
||||
return setTimeout((function() {
|
||||
if (!(Index.req && !Index.notice)) {
|
||||
return;
|
||||
}
|
||||
return Index.notice = new Notice('info', 'Refreshing index...');
|
||||
}), 5 * $.SECOND - (Date.now() - now));
|
||||
});
|
||||
}
|
||||
Index.req = $.ajax("//a.4cdn.org/" + g.BOARD + "/catalog.json", {
|
||||
onabort: Index.load,
|
||||
onloadend: Index.load
|
||||
}, {
|
||||
whenModified: true
|
||||
});
|
||||
return $.addClass(Index.button, 'fa-spin');
|
||||
},
|
||||
load: function(e) {
|
||||
var err, notice, req, timeEl;
|
||||
|
||||
$.rmClass(Index.button, 'fa-spin');
|
||||
req = Index.req, notice = Index.notice;
|
||||
delete Index.req;
|
||||
delete Index.notice;
|
||||
if (e.type === 'abort') {
|
||||
req.onloadend = null;
|
||||
notice.close();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (req.status === 200) {
|
||||
Index.parse(JSON.parse(req.response));
|
||||
}
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
c.error('Index failure:', err.stack);
|
||||
if (notice) {
|
||||
notice.setType('error');
|
||||
notice.el.lastElementChild.textContent = 'Index refresh failed.';
|
||||
setTimeout(notice.close, 2 * $.SECOND);
|
||||
} else {
|
||||
new Notice('error', 'Index refresh failed.', 2);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (notice) {
|
||||
notice.setType('success');
|
||||
notice.el.lastElementChild.textContent = 'Index refreshed!';
|
||||
setTimeout(notice.close, $.SECOND);
|
||||
}
|
||||
timeEl = $('#index-last-refresh', Index.navLinks);
|
||||
timeEl.dataset.utc = e.timeStamp;
|
||||
RelativeDates.update(timeEl);
|
||||
return Index.scrollToIndex();
|
||||
},
|
||||
parse: function(pages) {
|
||||
Index.parseThreadList(pages);
|
||||
Index.buildThreads();
|
||||
Index.sort();
|
||||
Index.buildIndex();
|
||||
Index.buildPagelist();
|
||||
return Index.setPage();
|
||||
},
|
||||
parseThreadList: function(pages) {
|
||||
var thread, threadID, _ref, _ref1;
|
||||
|
||||
Index.pagesNum = pages.length;
|
||||
Index.threadsNumPerPage = pages[0].threads.length;
|
||||
Index.liveThreadData = pages.reduce((function(arr, next) {
|
||||
return arr.concat(next.threads);
|
||||
}), []);
|
||||
Index.liveThreadIDs = Index.liveThreadData.map(function(data) {
|
||||
return data.no;
|
||||
});
|
||||
_ref = g.BOARD.threads;
|
||||
for (threadID in _ref) {
|
||||
thread = _ref[threadID];
|
||||
if (_ref1 = thread.ID, __indexOf.call(Index.liveThreadIDs, _ref1) < 0) {
|
||||
thread.collect();
|
||||
}
|
||||
}
|
||||
},
|
||||
buildThreads: function() {
|
||||
var err, errors, i, posts, thread, threadData, threadRoot, threads, _i, _len, _ref;
|
||||
|
||||
Index.nodes = [];
|
||||
threads = [];
|
||||
posts = [];
|
||||
_ref = Index.liveThreadData;
|
||||
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
||||
threadData = _ref[i];
|
||||
threadRoot = Build.thread(g.BOARD, threadData);
|
||||
Index.nodes.push(threadRoot, $.el('hr'));
|
||||
if (thread = g.BOARD.threads[threadData.no]) {
|
||||
thread.setPage(Math.floor(i / Index.threadsNumPerPage));
|
||||
thread.setStatus('Sticky', !!threadData.sticky);
|
||||
thread.setStatus('Closed', !!threadData.closed);
|
||||
} else {
|
||||
thread = new Thread(threadData.no, g.BOARD);
|
||||
threads.push(thread);
|
||||
}
|
||||
if (thread.ID in thread.posts) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
posts.push(new Post($('.opContainer', threadRoot), thread, g.BOARD));
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (!errors) {
|
||||
errors = [];
|
||||
}
|
||||
errors.push({
|
||||
message: "Parsing of Post No." + thread + " failed. Post will be skipped.",
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
if (errors) {
|
||||
Main.handleErrors(errors);
|
||||
}
|
||||
$.nodes(Index.nodes);
|
||||
Main.callbackNodes(Thread, threads);
|
||||
Main.callbackNodes(Post, posts);
|
||||
return $.event('IndexRefresh');
|
||||
},
|
||||
buildReplies: function(threadRoots) {
|
||||
var data, err, errors, i, lastReplies, node, nodes, post, posts, thread, threadRoot, _i, _j, _len, _len1;
|
||||
|
||||
posts = [];
|
||||
for (_i = 0, _len = threadRoots.length; _i < _len; _i += 2) {
|
||||
threadRoot = threadRoots[_i];
|
||||
thread = Get.threadFromRoot(threadRoot);
|
||||
i = Index.liveThreadIDs.indexOf(thread.ID);
|
||||
if (!(lastReplies = Index.liveThreadData[i].last_replies)) {
|
||||
continue;
|
||||
}
|
||||
nodes = [];
|
||||
for (_j = 0, _len1 = lastReplies.length; _j < _len1; _j++) {
|
||||
data = lastReplies[_j];
|
||||
if (post = thread.posts[data.no]) {
|
||||
nodes.push(post.nodes.root);
|
||||
continue;
|
||||
}
|
||||
nodes.push(node = Build.postFromObject(data, thread.board.ID));
|
||||
try {
|
||||
posts.push(new Post(node, thread, thread.board));
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
if (!errors) {
|
||||
errors = [];
|
||||
}
|
||||
errors.push({
|
||||
message: "Parsing of Post No." + data.no + " failed. Post will be skipped.",
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
$.add(threadRoot, nodes);
|
||||
}
|
||||
if (errors) {
|
||||
Main.handleErrors(errors);
|
||||
}
|
||||
return Main.callbackNodes(Post, posts);
|
||||
},
|
||||
sort: function() {
|
||||
var i, offset, sortedThreadIDs, threadID, threadRoot, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3;
|
||||
|
||||
switch (Conf['Index Sort']) {
|
||||
case 'bump':
|
||||
sortedThreadIDs = Index.liveThreadIDs;
|
||||
break;
|
||||
case 'lastreply':
|
||||
sortedThreadIDs = __slice.call(Index.liveThreadData).sort(function(a, b) {
|
||||
if ('last_replies' in a) {
|
||||
a = a.last_replies[a.last_replies.length - 1];
|
||||
}
|
||||
if ('last_replies' in b) {
|
||||
b = b.last_replies[b.last_replies.length - 1];
|
||||
}
|
||||
return b.no - a.no;
|
||||
}).map(function(data) {
|
||||
return data.no;
|
||||
});
|
||||
break;
|
||||
case 'birth':
|
||||
sortedThreadIDs = __slice.call(Index.liveThreadIDs).sort(function(a, b) {
|
||||
return b - a;
|
||||
});
|
||||
break;
|
||||
case 'replycount':
|
||||
sortedThreadIDs = __slice.call(Index.liveThreadData).sort(function(a, b) {
|
||||
return b.replies - a.replies;
|
||||
}).map(function(data) {
|
||||
return data.no;
|
||||
});
|
||||
break;
|
||||
case 'filecount':
|
||||
sortedThreadIDs = __slice.call(Index.liveThreadData).sort(function(a, b) {
|
||||
return b.images - a.images;
|
||||
}).map(function(data) {
|
||||
return data.no;
|
||||
});
|
||||
}
|
||||
Index.sortedNodes = [];
|
||||
for (_i = 0, _len = sortedThreadIDs.length; _i < _len; _i++) {
|
||||
threadID = sortedThreadIDs[_i];
|
||||
i = Index.liveThreadIDs.indexOf(threadID) * 2;
|
||||
Index.sortedNodes.push(Index.nodes[i], Index.nodes[i + 1]);
|
||||
}
|
||||
if (Index.isSearching) {
|
||||
Index.sortedNodes = Index.querySearch(Index.searchInput.value) || Index.sortedNodes;
|
||||
}
|
||||
offset = 0;
|
||||
_ref = Index.sortedNodes;
|
||||
for (i = _j = 0, _len1 = _ref.length; _j < _len1; i = _j += 2) {
|
||||
threadRoot = _ref[i];
|
||||
if (Get.threadFromRoot(threadRoot).isSticky) {
|
||||
(_ref1 = Index.sortedNodes).splice.apply(_ref1, [offset++ * 2, 0].concat(__slice.call(Index.sortedNodes.splice(i, 2))));
|
||||
}
|
||||
}
|
||||
if (!Conf['Filter']) {
|
||||
return;
|
||||
}
|
||||
offset = 0;
|
||||
_ref2 = Index.sortedNodes;
|
||||
for (i = _k = 0, _len2 = _ref2.length; _k < _len2; i = _k += 2) {
|
||||
threadRoot = _ref2[i];
|
||||
if (Get.threadFromRoot(threadRoot).isOnTop) {
|
||||
(_ref3 = Index.sortedNodes).splice.apply(_ref3, [offset++ * 2, 0].concat(__slice.call(Index.sortedNodes.splice(i, 2))));
|
||||
}
|
||||
}
|
||||
},
|
||||
buildIndex: function() {
|
||||
var nodes, nodesPerPage, pageNum;
|
||||
|
||||
if (Conf['Index Mode'] === 'paged') {
|
||||
pageNum = Index.getCurrentPage();
|
||||
nodesPerPage = Index.threadsNumPerPage * 2;
|
||||
nodes = Index.sortedNodes.slice(nodesPerPage * pageNum, nodesPerPage * (pageNum + 1));
|
||||
} else {
|
||||
nodes = Index.sortedNodes;
|
||||
}
|
||||
$.rmAll(Index.root);
|
||||
if (Conf['Show Replies']) {
|
||||
Index.buildReplies(nodes);
|
||||
}
|
||||
$.event('IndexBuild', nodes);
|
||||
return $.add(Index.root, nodes);
|
||||
},
|
||||
isSearching: false,
|
||||
clearSearch: function() {
|
||||
Index.searchInput.value = null;
|
||||
Index.onSearchInput();
|
||||
return Index.searchInput.focus();
|
||||
},
|
||||
onSearchInput: function() {
|
||||
var pageNum;
|
||||
|
||||
if (Index.isSearching = !!Index.searchInput.value.trim()) {
|
||||
if (!Index.searchInput.dataset.searching) {
|
||||
Index.searchInput.dataset.searching = 1;
|
||||
Index.pageBeforeSearch = Index.getCurrentPage();
|
||||
pageNum = 0;
|
||||
} else {
|
||||
pageNum = Index.getCurrentPage();
|
||||
}
|
||||
} else {
|
||||
pageNum = Index.pageBeforeSearch;
|
||||
delete Index.pageBeforeSearch;
|
||||
delete Index.searchInput.dataset.searching;
|
||||
}
|
||||
Index.sort();
|
||||
if (Conf['Index Mode'] === 'paged') {
|
||||
pageNum = Math.min(pageNum, Index.getMaxPageNum());
|
||||
}
|
||||
Index.buildPagelist();
|
||||
if (Index.currentPage === pageNum) {
|
||||
Index.buildIndex();
|
||||
return Index.setPage();
|
||||
} else {
|
||||
return Index.pageNav(pageNum);
|
||||
}
|
||||
},
|
||||
querySearch: function(query) {
|
||||
var keywords;
|
||||
|
||||
if (!(keywords = query.toLowerCase().match(/\S+/g))) {
|
||||
return;
|
||||
}
|
||||
return Index.search(keywords);
|
||||
},
|
||||
search: function(keywords) {
|
||||
var found, i, threadRoot, _i, _len, _ref;
|
||||
|
||||
found = [];
|
||||
_ref = Index.sortedNodes;
|
||||
for (i = _i = 0, _len = _ref.length; _i < _len; i = _i += 2) {
|
||||
threadRoot = _ref[i];
|
||||
if (Index.searchMatch(Get.threadFromRoot(threadRoot), keywords)) {
|
||||
found.push(Index.sortedNodes[i], Index.sortedNodes[i + 1]);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
},
|
||||
searchMatch: function(thread, keywords) {
|
||||
var file, info, key, keyword, text, _i, _j, _len, _len1, _ref, _ref1;
|
||||
|
||||
_ref = thread.OP, info = _ref.info, file = _ref.file;
|
||||
text = [];
|
||||
_ref1 = ['comment', 'subject', 'name', 'tripcode', 'email'];
|
||||
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
|
||||
key = _ref1[_i];
|
||||
if (key in info) {
|
||||
text.push(info[key]);
|
||||
}
|
||||
}
|
||||
if (file) {
|
||||
text.push(file.name);
|
||||
}
|
||||
text = text.join(' ').toLowerCase();
|
||||
for (_j = 0, _len1 = keywords.length; _j < _len1; _j++) {
|
||||
keyword = keywords[_j];
|
||||
if (-1 === text.indexOf(keyword)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Build = {
|
||||
spoilerRange: {},
|
||||
shortFilename: function(filename, isReply) {
|
||||
@ -2198,7 +2807,7 @@
|
||||
container = $.el('div', {
|
||||
id: "pc" + postID,
|
||||
className: "postContainer " + (isOP ? 'op' : 'reply') + "Container",
|
||||
innerHTML: ""
|
||||
innerHTML: "" + (isOP ? '' : "<div class=sideArrows id=sa" + postID + ">>></div>") + "<div id=p" + postID + " class='post " + (isOP ? 'op' : 'reply') + (capcode === 'admin_highlight' ? ' highlightPost' : '') + "'><div class='postInfoM mobile' id=pim" + postID + "><span class='nameBlock" + capcodeClass + "'><span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + capcode + userID + flag + sticky + closed) + "<br>" + subject + "</span><span class='dateTime postNum' data-utc=" + dateUTC + ">" + date + "<a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + ">No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "'>" + postID + "</a></span></div>" + (isOP ? fileHTML : '') + "<div class='postInfo desktop' id=pi" + postID + "><input type=checkbox name=" + postID + " value=delete>" + subject + "<span class='nameBlock" + capcodeClass + "'>" + emailStart + "<span class=name>" + (name || '') + "</span>" + (tripcode + capcodeStart + emailEnd + capcode + userID + flag) + "</span>" + " " + "<span class=dateTime data-utc=" + dateUTC + ">" + date + "</span>" + " " + "<span class='postNum desktop'><a href=" + ("/" + boardID + "/res/" + threadID + "#p" + postID) + " title='Highlight this post'>No.</a><a href='" + (g.VIEW === 'thread' && g.THREADID === +threadID ? "javascript:quote(" + postID + ")" : "/" + boardID + "/res/" + threadID + "#q" + postID) + "' title='Quote this post'>" + postID + "</a>" + (pageIcon + sticky + closed + replyLink) + "</span></div>" + (isOP ? '' : fileHTML) + "<blockquote class=postMessage id=m" + postID + ">" + (comment || '') + "</blockquote>" + " " + "</div>"
|
||||
});
|
||||
_ref = $$('.quotelink', container);
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
@ -5114,6 +5723,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
StrawPoll: {
|
||||
regExp: /strawpoll\.me\/(?:embed_\d+\/)?(\d+)/,
|
||||
style: 'border: 0; width: 600px; height: 406px;',
|
||||
el: function(a) {
|
||||
return $.el('iframe', {
|
||||
src: "http://strawpoll.me/embed_1/" + a.dataset.uid
|
||||
});
|
||||
}
|
||||
},
|
||||
TwitchTV: {
|
||||
regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/,
|
||||
style: "border: none; width: 640px; height: 360px;",
|
||||
@ -11994,8 +12612,7 @@
|
||||
'Index Navigation': Nav,
|
||||
'Keybinds': Keybinds,
|
||||
'Show Dice Roll': Dice,
|
||||
'Banner': Banner,
|
||||
'Infinite Scrolling': InfiniScroll
|
||||
'Banner': Banner
|
||||
});
|
||||
$.on(d, 'AddCallback', Main.addCallback);
|
||||
return $.ready(Main.initReady);
|
||||
@ -12209,7 +12826,7 @@
|
||||
return Klass.callbacks.push(obj.callback);
|
||||
},
|
||||
handleErrors: function(errors) {
|
||||
var div, err, error, logs, _i, _len;
|
||||
var div, error, logs, _i, _len;
|
||||
|
||||
if (!(errors instanceof Array)) {
|
||||
error = errors;
|
||||
@ -12233,12 +12850,7 @@
|
||||
});
|
||||
for (_i = 0, _len = errors.length; _i < _len; _i++) {
|
||||
error = errors[_i];
|
||||
try {
|
||||
$.add(logs, Main.parseError(error));
|
||||
} catch (_error) {
|
||||
err = _error;
|
||||
c.error(error.message, error.stack);
|
||||
}
|
||||
$.add(logs, Main.parseError(error));
|
||||
}
|
||||
return new Notice('error', [div, logs], 30);
|
||||
},
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 56 KiB |
@ -1 +1 @@
|
||||
postMessage({version:'1.2.41'},'*')
|
||||
postMessage({version:'1.2.43'},'*')
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "4chan-X",
|
||||
"version": "1.2.41",
|
||||
"version": "1.2.43",
|
||||
"description": "Cross-browser userscript for maximum lurking on 4chan.",
|
||||
"meta": {
|
||||
"name": "4chan X",
|
||||
|
||||
@ -346,6 +346,13 @@ Linkify =
|
||||
api: (uid) -> "//soundcloud.com/oembed?show_artwork=false&&maxwidth=500px&show_comments=false&format=json&url=https://www.soundcloud.com/#{uid}"
|
||||
text: (_) -> _.title
|
||||
|
||||
StrawPoll:
|
||||
regExp: /strawpoll\.me\/(?:embed_\d+\/)?(\d+)/
|
||||
style: 'border: 0; width: 600px; height: 406px;'
|
||||
el: (a) ->
|
||||
$.el 'iframe',
|
||||
src: "http://strawpoll.me/embed_1/#{a.dataset.uid}"
|
||||
|
||||
TwitchTV:
|
||||
regExp: /.*(?:twitch.tv\/)([^#\&\?]*).*/
|
||||
style: "border: none; width: 640px; height: 360px;"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user