Merge branch 'master' into webext
This commit is contained in:
commit
3e019cc116
39
CHANGELOG.md
39
CHANGELOG.md
@ -2,8 +2,47 @@
|
|||||||
|
|
||||||
-Sometimes the changelog has notes (not comprehensive) acknowledging people's work. This does not mean the changes are their fault, only that their code was used. All changes to the script are chosen by and the fault of the maintainer (ccd0).
|
-Sometimes the changelog has notes (not comprehensive) acknowledging people's work. This does not mean the changes are their fault, only that their code was used. All changes to the script are chosen by and the fault of the maintainer (ccd0).
|
||||||
|
|
||||||
|
### v1.13.12
|
||||||
|
|
||||||
|
**v1.13.12.1** *(2017-09-29)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.12.1/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.12.1/builds/4chan-X-noupdate.crx)]
|
||||||
|
- Merge v1.13.11.5: Fix lag after settings changes.
|
||||||
|
|
||||||
|
**v1.13.12.0** *(2017-09-28)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.12.0/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.12.0/builds/4chan-X-noupdate.crx)]
|
||||||
|
- Based on v1.13.11.4.
|
||||||
|
- Preliminary support for Greasemonkey 4.
|
||||||
|
- Minor custom cooldown bugfix.
|
||||||
|
- (BeltranBot) Fix 'open thread in new tab' keybind for VM/TM
|
||||||
|
|
||||||
|
### v1.13.11
|
||||||
|
|
||||||
|
**v1.13.11.5** *(2017-09-29)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.5/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.5/builds/4chan-X-noupdate.crx)]
|
||||||
|
- Fix lag after settings changes.
|
||||||
|
|
||||||
|
**v1.13.11.4** *(2017-08-24)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.4/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.4/builds/4chan-X-noupdate.crx)]
|
||||||
|
- Merge v1.13.10.7: Fix quote preview bug when reply is in index data but no thread object exists. #1478
|
||||||
|
|
||||||
|
**v1.13.11.3** *(2017-08-13)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.3/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.3/builds/4chan-X-noupdate.crx)]
|
||||||
|
- Add language setting for time formatting.
|
||||||
|
|
||||||
|
**v1.13.11.2** *(2017-08-12)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.2/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.2/builds/4chan-X-noupdate.crx)]
|
||||||
|
- Last Long Reply order will now ignore hidden and filtered replies.
|
||||||
|
|
||||||
|
**v1.13.11.1** *(2017-08-10)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.1/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.1/builds/4chan-X-noupdate.crx)]
|
||||||
|
- Merge v1.13.10.6: Disable 'Redirect to HTTPS' on platforms where we use localStorage for saving settings.
|
||||||
|
|
||||||
|
**v1.13.11.0** *(2017-08-08)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.0/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.11.0/builds/4chan-X-noupdate.crx)]
|
||||||
|
- Based on v1.13.10.5.
|
||||||
|
- Support [spoiler] and [code] tags in 'Copy Text' menu item.
|
||||||
|
- Trim quoted text to text fully inside post. #1108
|
||||||
|
|
||||||
### v1.13.10
|
### v1.13.10
|
||||||
|
|
||||||
|
**v1.13.10.7** *(2017-08-24)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.10.7/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.10.7/builds/4chan-X-noupdate.crx)]
|
||||||
|
- Fix quote preview bug when reply is in index data but no thread object exists. #1478
|
||||||
|
|
||||||
|
**v1.13.10.6** *(2017-08-10)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.10.6/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.10.6/builds/4chan-X-noupdate.crx)]
|
||||||
|
- Disable 'Redirect to HTTPS' on platforms where we use localStorage for saving settings.
|
||||||
|
|
||||||
**v1.13.10.5** *(2017-08-04)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.10.5/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.10.5/builds/4chan-X-noupdate.crx)]
|
**v1.13.10.5** *(2017-08-04)* - [[Userscript](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.10.5/builds/4chan-X-noupdate.user.js)] [[Chrome extension](https://raw.githubusercontent.com/ccd0/4chan-x/1.13.10.5/builds/4chan-X-noupdate.crx)]
|
||||||
- Better parsing of archive links for Quote Inlining / Hover.
|
- Better parsing of archive links for Quote Inlining / Hover.
|
||||||
- Add Board Tips.
|
- Add Board Tips.
|
||||||
|
|||||||
13
README.md
13
README.md
@ -10,21 +10,12 @@ https://github.com/Nebukazar/OneeChan.
|
|||||||
## Please note
|
## Please note
|
||||||
**Uninstalling**: 4chan X disables the native extension, so if you uninstall 4chan X, you'll need to re-enable it. To do this, click the `[Settings]` link in the top right corner, uncheck "`Disable the native extension`" in the panel that appears, and click the "`Save Settings`" button. If you don't see a "`Save Settings`" button, it may be being hidden by your ad blocker.
|
**Uninstalling**: 4chan X disables the native extension, so if you uninstall 4chan X, you'll need to re-enable it. To do this, click the `[Settings]` link in the top right corner, uncheck "`Disable the native extension`" in the panel that appears, and click the "`Save Settings`" button. If you don't see a "`Save Settings`" button, it may be being hidden by your ad blocker.
|
||||||
|
|
||||||
**Private browsing**: 4chan X does not yet support private browsing / incognito mode. Although it may work in this mode, browsing data recorded by 4chan X, such as your last read post in a thread and which posts are yours, will still need to be cleared manually by resetting your settings. To control what browsing data 4chan X records, use the `Remember Last Read Post` and `Remember Your Posts` options in the settings panel.
|
**Private browsing**: By default, 4chan X remembers your last read post in a thread and which posts were made by you, even if you are in private browsing / incognito mode. If you want to turn this off, uncheck the `Remember Last Read Post` and `Remember Your Posts` options in the settings panel. You can clear all 4chan browsing history saved by 4chan X by resetting your settings.
|
||||||
|
|
||||||
**HTTPS**: 4chan X currently shares your settings and post history between the HTTP and HTTPS versions of 4chan. If you are concerned about protecting your privacy against a man-in-the-middle attack, you should disable 4chan X on the HTTP version of 4chan and/or install [HTTPS Everywhere](https://www.eff.org/https-everywhere).
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
### Firefox
|
### Firefox
|
||||||
You will first need to install a userscript manager such as [Greasemonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/). Unfortunately, Firefox's transition to WebExtensions has forced a complete rewrite of Greasemonkey, leaving the current version of Greasemonkey unmaintained and unreliable. Until the WebExtensions version of Greasemonkey is complete, your options are:
|
Install [Greasemonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/), then **[click here to install 4chan X](https://www.4chan-x.net/builds/4chan-X.user.js)**.
|
||||||
|
|
||||||
- Use [Firefox ESR](https://www.mozilla.org/en-US/firefox/organizations/), which should continue to work with the current version of [Greasemonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/) until a new one is ready.
|
|
||||||
- If you're using a freedom-respecting build/fork of Firefox, you can try [this build of Greasemonkey](https://www.4chan-x.net/greasemonkey-2017.07.27.beta.xpi) based on [this pull request](https://github.com/greasemonkey/greasemonkey/pull/2507) which may fix the issues some people are having.
|
|
||||||
- Install [Violentmonkey](https://addons.mozilla.org/en-US/firefox/addon/violentmonkey/) or [Tampermonkey](https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/). These are already WebExtensions, but due to the current limitations of WebExtensions, they offer less functionality than Greasemonkey.
|
|
||||||
- Use [Greasemonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/) with the current version of Firefox, but don't be surprised when things break.
|
|
||||||
|
|
||||||
After Greasemonkey or an alternative userscript manager is installed, **[click here to install 4chan X](https://www.4chan-x.net/builds/4chan-X.user.js)**.
|
|
||||||
|
|
||||||
Ports of Greasemonkey are available for [SeaMonkey](https://sourceforge.net/projects/gmport/) and [Pale Moon](https://github.com/janekptacijarabaci/greasemonkey/releases/latest).
|
Ports of Greasemonkey are available for [SeaMonkey](https://sourceforge.net/projects/gmport/) and [Pale Moon](https://github.com/janekptacijarabaci/greasemonkey/releases/latest).
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name 4chan X beta
|
// @name 4chan X beta
|
||||||
// @version 1.13.10.5
|
// @version 1.13.12.1
|
||||||
// @minGMVer 1.14
|
// @minGMVer 1.14
|
||||||
// @minFFVer 26
|
// @minFFVer 26
|
||||||
// @namespace 4chan-X
|
// @namespace 4chan-X
|
||||||
@ -48,6 +48,11 @@
|
|||||||
// @grant GM_addValueChangeListener
|
// @grant GM_addValueChangeListener
|
||||||
// @grant GM_openInTab
|
// @grant GM_openInTab
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM_xmlhttpRequest
|
||||||
|
// @grant GM.getValue
|
||||||
|
// @grant GM.setValue
|
||||||
|
// @grant GM.deleteValue
|
||||||
|
// @grant GM.listValues
|
||||||
|
// @grant GM.xmlHttpRequest
|
||||||
// @run-at document-start
|
// @run-at document-start
|
||||||
// @updateURL https://www.4chan-x.net/builds/4chan-X-beta.meta.js
|
// @updateURL https://www.4chan-x.net/builds/4chan-X-beta.meta.js
|
||||||
// @downloadURL https://www.4chan-x.net/builds/4chan-X-beta.user.js
|
// @downloadURL https://www.4chan-x.net/builds/4chan-X-beta.user.js
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name 4chan X
|
// @name 4chan X
|
||||||
// @version 1.13.10.5
|
// @version 1.13.12.1
|
||||||
// @minGMVer 1.14
|
// @minGMVer 1.14
|
||||||
// @minFFVer 26
|
// @minFFVer 26
|
||||||
// @namespace 4chan-X
|
// @namespace 4chan-X
|
||||||
@ -48,6 +48,11 @@
|
|||||||
// @grant GM_addValueChangeListener
|
// @grant GM_addValueChangeListener
|
||||||
// @grant GM_openInTab
|
// @grant GM_openInTab
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM_xmlhttpRequest
|
||||||
|
// @grant GM.getValue
|
||||||
|
// @grant GM.setValue
|
||||||
|
// @grant GM.deleteValue
|
||||||
|
// @grant GM.listValues
|
||||||
|
// @grant GM.xmlHttpRequest
|
||||||
// @run-at document-start
|
// @run-at document-start
|
||||||
// @updateURL https://www.4chan-x.net/builds/4chan-X.meta.js
|
// @updateURL https://www.4chan-x.net/builds/4chan-X.meta.js
|
||||||
// @downloadURL https://www.4chan-x.net/builds/4chan-X.user.js
|
// @downloadURL https://www.4chan-x.net/builds/4chan-X.user.js
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
|
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
|
||||||
<app appid='lacclbnghgdicfifcamcmcnilckjamag'>
|
<app appid='lacclbnghgdicfifcamcmcnilckjamag'>
|
||||||
<updatecheck codebase='https://www.4chan-x.net/builds/4chan-X-beta.crx' version='1.13.10.5' />
|
<updatecheck codebase='https://www.4chan-x.net/builds/4chan-X-beta.crx' version='1.13.12.1' />
|
||||||
</app>
|
</app>
|
||||||
</gupdate>
|
</gupdate>
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?xml version='1.0' encoding='UTF-8'?>
|
||||||
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
|
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
|
||||||
<app appid='lacclbnghgdicfifcamcmcnilckjamag'>
|
<app appid='lacclbnghgdicfifcamcmcnilckjamag'>
|
||||||
<updatecheck codebase='https://www.4chan-x.net/builds/4chan-X.crx' version='1.13.10.5' />
|
<updatecheck codebase='https://www.4chan-x.net/builds/4chan-X.crx' version='1.13.12.1' />
|
||||||
</app>
|
</app>
|
||||||
</gupdate>
|
</gupdate>
|
||||||
|
|
||||||
|
|||||||
@ -23,8 +23,7 @@
|
|||||||
<a href="https://github.com/Nebukazar/OneeChan">https://github.com/Nebukazar/OneeChan</a>.</p>
|
<a href="https://github.com/Nebukazar/OneeChan">https://github.com/Nebukazar/OneeChan</a>.</p>
|
||||||
<h2 id="please-note">Please note</h2>
|
<h2 id="please-note">Please note</h2>
|
||||||
<p><strong>Uninstalling</strong>: 4chan X disables the native extension, so if you uninstall 4chan X, you'll need to re-enable it. To do this, click the <code>[Settings]</code> link in the top right corner, uncheck "<code>Disable the native extension</code>" in the panel that appears, and click the "<code>Save Settings</code>" button. If you don't see a "<code>Save Settings</code>" button, it may be being hidden by your ad blocker.</p>
|
<p><strong>Uninstalling</strong>: 4chan X disables the native extension, so if you uninstall 4chan X, you'll need to re-enable it. To do this, click the <code>[Settings]</code> link in the top right corner, uncheck "<code>Disable the native extension</code>" in the panel that appears, and click the "<code>Save Settings</code>" button. If you don't see a "<code>Save Settings</code>" button, it may be being hidden by your ad blocker.</p>
|
||||||
<p><strong>Private browsing</strong>: 4chan X does not yet support private browsing / incognito mode. Although it may work in this mode, browsing data recorded by 4chan X, such as your last read post in a thread and which posts are yours, will still need to be cleared manually by resetting your settings. To control what browsing data 4chan X records, use the <code>Remember Last Read Post</code> and <code>Remember Your Posts</code> options in the settings panel.</p>
|
<p><strong>Private browsing</strong>: By default, 4chan X remembers your last read post in a thread and which posts were made by you, even if you are in private browsing / incognito mode. If you want to turn this off, uncheck the <code>Remember Last Read Post</code> and <code>Remember Your Posts</code> options in the settings panel. You can clear all 4chan browsing history saved by 4chan X by resetting your settings.</p>
|
||||||
<p><strong>HTTPS</strong>: 4chan X currently shares your settings and post history between the HTTP and HTTPS versions of 4chan. If you are concerned about protecting your privacy against a man-in-the-middle attack, you should disable 4chan X on the HTTP version of 4chan and/or install <a href="https://www.eff.org/https-everywhere">HTTPS Everywhere</a>.</p>
|
|
||||||
<h2 id="install">Install</h2>
|
<h2 id="install">Install</h2>
|
||||||
<input hidden type="checkbox" id="firefox-hide"><div><h3 id="firefox"><label for="firefox-hide">Firefox</label></h3>
|
<input hidden type="checkbox" id="firefox-hide"><div><h3 id="firefox"><label for="firefox-hide">Firefox</label></h3>
|
||||||
<p>You will first need to install a userscript manager such as <a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/">Greasemonkey</a>. Unfortunately, Firefox's transition to WebExtensions has forced a complete rewrite of Greasemonkey, leaving the current version of Greasemonkey unmaintained and unreliable. Until the WebExtensions version of Greasemonkey is complete, your options are:</p>
|
<p>You will first need to install a userscript manager such as <a href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/">Greasemonkey</a>. Unfortunately, Firefox's transition to WebExtensions has forced a complete rewrite of Greasemonkey, leaving the current version of Greasemonkey unmaintained and unreliable. Until the WebExtensions version of Greasemonkey is complete, your options are:</p>
|
||||||
|
|||||||
@ -57,7 +57,12 @@
|
|||||||
"GM_listValues",
|
"GM_listValues",
|
||||||
"GM_addValueChangeListener",
|
"GM_addValueChangeListener",
|
||||||
"GM_openInTab",
|
"GM_openInTab",
|
||||||
"GM_xmlhttpRequest"
|
"GM_xmlhttpRequest",
|
||||||
|
"GM.getValue",
|
||||||
|
"GM.setValue",
|
||||||
|
"GM.deleteValue",
|
||||||
|
"GM.listValues",
|
||||||
|
"GM.xmlHttpRequest"
|
||||||
],
|
],
|
||||||
"min": {
|
"min": {
|
||||||
"chrome": "33",
|
"chrome": "33",
|
||||||
|
|||||||
@ -114,16 +114,6 @@
|
|||||||
"boards": ["h", "hc", "hm", "r", "s", "soc"],
|
"boards": ["h", "hc", "hm", "r", "s", "soc"],
|
||||||
"files": ["h", "hc", "hm", "r", "s", "soc"],
|
"files": ["h", "hc", "hm", "r", "s", "soc"],
|
||||||
"reports": true
|
"reports": true
|
||||||
}, {
|
|
||||||
"uid": 32,
|
|
||||||
"name": "4tan",
|
|
||||||
"domain": "boards.4tan.org",
|
|
||||||
"http": true,
|
|
||||||
"https": true,
|
|
||||||
"software": "foolfuuka",
|
|
||||||
"boards": ["3", "a", "aco", "adv", "an", "asp", "b", "bant", "biz", "c", "can", "cgl", "ck", "cm", "co", "cock", "d", "diy", "e", "f", "fa", "fap", "fit", "fitlit", "g", "gd", "gif", "h", "hc", "his", "hm", "hr", "i", "ic", "int", "jp", "k", "lgbt", "lit", "m", "mlp", "mlpol", "mo", "mtv", "mu", "n", "news", "o", "out", "outsoc", "p", "po", "pol", "qa", "qst", "r", "r9k", "s", "s4s", "sci", "soc", "sp", "spa", "t", "tg", "toy", "trash", "trv", "tv", "u", "v", "vg", "vint", "vip", "vp", "vr", "w", "wg", "wsg", "wsr", "x", "y"],
|
|
||||||
"files": ["bant", "can", "cock", "fap", "fitlit", "mlpol", "mo", "mtv", "outsoc", "spa", "vint"],
|
|
||||||
"reports": true
|
|
||||||
}, {
|
}, {
|
||||||
"uid": 33,
|
"uid": 33,
|
||||||
"name": "YEET Archive",
|
"name": "YEET Archive",
|
||||||
|
|||||||
@ -688,6 +688,9 @@ Index =
|
|||||||
else
|
else
|
||||||
Index.parsedThreads[threadID].isHidden
|
Index.parsedThreads[threadID].isHidden
|
||||||
|
|
||||||
|
isHiddenReply: (threadID, replyData) ->
|
||||||
|
PostHiding.isHidden(g.BOARD.ID, threadID, replyData.no) or Filter.isHidden(Build.parseJSON replyData, g.BOARD.ID)
|
||||||
|
|
||||||
buildThreads: (threadIDs, isCatalog) ->
|
buildThreads: (threadIDs, isCatalog) ->
|
||||||
threads = []
|
threads = []
|
||||||
newThreads = []
|
newThreads = []
|
||||||
@ -788,8 +791,7 @@ Index =
|
|||||||
|
|
||||||
replies = []
|
replies = []
|
||||||
for data in lastReplies
|
for data in lastReplies
|
||||||
continue if PostHiding.isHidden(g.BOARD.ID, thread.ID, data.no)
|
continue if Index.isHiddenReply thread.ID, data
|
||||||
continue if Filter.isHidden(Build.parseJSON data, g.BOARD.ID)
|
|
||||||
reply = Build.catalogReply thread, data
|
reply = Build.catalogReply thread, data
|
||||||
RelativeDates.update $('time', reply)
|
RelativeDates.update $('time', reply)
|
||||||
$.on $('.catalog-reply-preview', reply), 'mouseover', QuotePreview.mouseover
|
$.on $('.catalog-reply-preview', reply), 'mouseover', QuotePreview.mouseover
|
||||||
@ -813,12 +815,16 @@ Index =
|
|||||||
when 'lastlong'
|
when 'lastlong'
|
||||||
lastlong = (thread) ->
|
lastlong = (thread) ->
|
||||||
for r, i in (thread.last_replies or []) by -1
|
for r, i in (thread.last_replies or []) by -1
|
||||||
|
continue if Index.isHiddenReply thread.no, r
|
||||||
len = if r.com then Build.parseComment(r.com).replace(/[^a-z]/ig, '').length else 0
|
len = if r.com then Build.parseComment(r.com).replace(/[^a-z]/ig, '').length else 0
|
||||||
if len >= Index.lastLongThresholds[+!!r.ext]
|
if len >= Index.lastLongThresholds[+!!r.ext]
|
||||||
return r
|
return r
|
||||||
if thread.omitted_posts then thread.last_replies[0] else thread
|
if thread.omitted_posts then thread.last_replies[0] else thread
|
||||||
|
lastlongD = {}
|
||||||
|
for thread in liveThreadData
|
||||||
|
lastlongD[thread.no] = lastlong(thread).no
|
||||||
[liveThreadData...].sort((a, b) ->
|
[liveThreadData...].sort((a, b) ->
|
||||||
lastlong(b).no - lastlong(a).no
|
lastlongD[b.no] - lastlongD[a.no]
|
||||||
).map (post) -> post.no
|
).map (post) -> post.no
|
||||||
when 'bump' then liveThreadIDs
|
when 'bump' then liveThreadIDs
|
||||||
when 'birth' then [liveThreadIDs... ].sort (a, b) -> b - a
|
when 'birth' then [liveThreadIDs... ].sort (a, b) -> b - a
|
||||||
|
|||||||
@ -162,8 +162,12 @@ Settings =
|
|||||||
addCheckboxes fs, obj
|
addCheckboxes fs, obj
|
||||||
$.add section, fs
|
$.add section, fs
|
||||||
addCheckboxes $('div[data-name="JSON Index"] > .suboption-list', section), Config.Index
|
addCheckboxes $('div[data-name="JSON Index"] > .suboption-list', section), Config.Index
|
||||||
|
|
||||||
|
# Unsupported options
|
||||||
if $.engine isnt 'gecko'
|
if $.engine isnt 'gecko'
|
||||||
$('div[data-name="Remember QR Size"]', section).hidden = true # XXX not supported
|
$('div[data-name="Remember QR Size"]', section).hidden = true
|
||||||
|
if $.perProtocolSettings
|
||||||
|
$('div[data-name="Redirect to HTTPS"]', section).hidden = true
|
||||||
|
|
||||||
$.get items, (items) ->
|
$.get items, (items) ->
|
||||||
for key, val of items
|
for key, val of items
|
||||||
@ -489,7 +493,7 @@ Settings =
|
|||||||
$.id('lastarchivecheck').textContent = 'never'
|
$.id('lastarchivecheck').textContent = 'never'
|
||||||
|
|
||||||
items = {}
|
items = {}
|
||||||
for name in ['archiveLists', 'archiveAutoUpdate', 'captchaLanguage', 'boardnav', 'time', 'backlink', 'fileInfo', 'QR.personas', 'favicon', 'usercss', 'customCooldown', 'jsWhitelist']
|
for name in ['archiveLists', 'archiveAutoUpdate', 'captchaLanguage', 'boardnav', 'time', 'timeLocale', 'backlink', 'fileInfo', 'QR.personas', 'favicon', 'usercss', 'customCooldown', 'jsWhitelist']
|
||||||
items[name] = Conf[name]
|
items[name] = Conf[name]
|
||||||
input = inputs[name]
|
input = inputs[name]
|
||||||
event = if name in ['archiveLists', 'archiveAutoUpdate', 'QR.personas', 'favicon', 'usercss'] then 'change' else 'input'
|
event = if name in ['archiveLists', 'archiveAutoUpdate', 'QR.personas', 'favicon', 'usercss'] then 'change' else 'input'
|
||||||
@ -642,6 +646,9 @@ Settings =
|
|||||||
time: ->
|
time: ->
|
||||||
@nextElementSibling.textContent = Time.format @value, new Date()
|
@nextElementSibling.textContent = Time.format @value, new Date()
|
||||||
|
|
||||||
|
timeLocale: ->
|
||||||
|
Settings.time.call $('[name=time]', Settings.dialog)
|
||||||
|
|
||||||
backlink: ->
|
backlink: ->
|
||||||
@nextElementSibling.textContent = @value.replace /%(?:id|%)/g, (x) -> ({'%id': '123456789', '%%': '%'})[x]
|
@nextElementSibling.textContent = @value.replace /%(?:id|%)/g, (x) -> ({'%id': '123456789', '%%': '%'})[x]
|
||||||
|
|
||||||
|
|||||||
@ -64,6 +64,7 @@
|
|||||||
<div>Minute: <code>%M</code></div>
|
<div>Minute: <code>%M</code></div>
|
||||||
<div>Second: <code>%S</code></div>
|
<div>Second: <code>%S</code></div>
|
||||||
<div>Literal <code>%</code>: <code>%%</code></div>
|
<div>Literal <code>%</code>: <code>%%</code></div>
|
||||||
|
<div><a href="https://www.w3.org/International/articles/language-tags/" target="_blank">Language tag</a>: <input name="timeLocale" class="field" spellcheck="false"></div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ CopyTextLink =
|
|||||||
el: a
|
el: a
|
||||||
order: 12
|
order: 12
|
||||||
open: (post) ->
|
open: (post) ->
|
||||||
CopyTextLink.text = post.info.comment
|
CopyTextLink.text = (post.origin or post).commentOrig()
|
||||||
true
|
true
|
||||||
|
|
||||||
copy: ->
|
copy: ->
|
||||||
|
|||||||
@ -5,7 +5,7 @@ BoardTips =
|
|||||||
<%= html(
|
<%= html(
|
||||||
'New to /qa/?<br>' +
|
'New to /qa/?<br>' +
|
||||||
'/qa/ is NOT an effective way to contact the mods.<br>' +
|
'/qa/ is NOT an effective way to contact the mods.<br>' +
|
||||||
'Use <a href="https://www.rizon.net/chat" target="_blank">IRC</a> or <a href="https://www.4chan.org/feedback" target="_blank">feedback</a> instead. ' +
|
'Message a mod on <a href="https://www.rizon.net/chat" target="_blank">IRC</a> or use <a href="https://www.4chan.org/feedback" target="_blank">feedback</a> instead. ' +
|
||||||
'More details <a href="https://www.4chan-x.net/qa_instructions.png" target="_blank">here</a>.'
|
'More details <a href="https://www.4chan-x.net/qa_instructions.png" target="_blank">here</a>.'
|
||||||
) %>
|
) %>
|
||||||
]
|
]
|
||||||
|
|||||||
@ -312,7 +312,7 @@ Keybinds =
|
|||||||
return if g.VIEW isnt 'index'
|
return if g.VIEW isnt 'index'
|
||||||
url = "/#{thread.board}/thread/#{thread}"
|
url = "/#{thread.board}/thread/#{thread}"
|
||||||
if tab
|
if tab
|
||||||
$.open url
|
$.open location.origin + url
|
||||||
else
|
else
|
||||||
location.href = url
|
location.href = url
|
||||||
|
|
||||||
|
|||||||
@ -41,13 +41,26 @@ Time =
|
|||||||
'December'
|
'December'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
localeFormat: (date, options, defaultValue) ->
|
||||||
|
if Conf['timeLocale']
|
||||||
|
try
|
||||||
|
return Intl.DateTimeFormat(Conf['timeLocale'], options).format(date)
|
||||||
|
defaultValue
|
||||||
|
|
||||||
|
localeFormatPart: (date, options, part, defaultValue) ->
|
||||||
|
if Conf['timeLocale']
|
||||||
|
try
|
||||||
|
parts = Intl.DateTimeFormat(Conf['timeLocale'], options).formatToParts(date)
|
||||||
|
return parts.map((x) -> if x.type is part then x.value else '').join('')
|
||||||
|
defaultValue
|
||||||
|
|
||||||
zeroPad: (n) -> if n < 10 then "0#{n}" else n
|
zeroPad: (n) -> if n < 10 then "0#{n}" else n
|
||||||
|
|
||||||
formatters:
|
formatters:
|
||||||
a: -> Time.day[@getDay()][...3]
|
a: -> Time.localeFormat @, {weekday: 'short'}, Time.day[@getDay()][...3]
|
||||||
A: -> Time.day[@getDay()]
|
A: -> Time.localeFormat @, {weekday: 'long'}, Time.day[@getDay()]
|
||||||
b: -> Time.month[@getMonth()][...3]
|
b: -> Time.localeFormat @, {month: 'short'}, Time.month[@getMonth()][...3]
|
||||||
B: -> Time.month[@getMonth()]
|
B: -> Time.localeFormat @, {month: 'long'}, Time.month[@getMonth()]
|
||||||
d: -> Time.zeroPad @getDate()
|
d: -> Time.zeroPad @getDate()
|
||||||
e: -> @getDate()
|
e: -> @getDate()
|
||||||
H: -> Time.zeroPad @getHours()
|
H: -> Time.zeroPad @getHours()
|
||||||
@ -56,8 +69,8 @@ Time =
|
|||||||
l: -> @getHours() % 12 or 12
|
l: -> @getHours() % 12 or 12
|
||||||
m: -> Time.zeroPad @getMonth() + 1
|
m: -> Time.zeroPad @getMonth() + 1
|
||||||
M: -> Time.zeroPad @getMinutes()
|
M: -> Time.zeroPad @getMinutes()
|
||||||
p: -> if @getHours() < 12 then 'AM' else 'PM'
|
p: -> Time.localeFormatPart @, {hour: 'numeric', hour12: true}, 'dayperiod', (if @getHours() < 12 then 'AM' else 'PM')
|
||||||
P: -> if @getHours() < 12 then 'am' else 'pm'
|
P: -> Time.formatters.p.call(@).toLowerCase()
|
||||||
S: -> Time.zeroPad @getSeconds()
|
S: -> Time.zeroPad @getSeconds()
|
||||||
y: -> @getFullYear().toString()[2..]
|
y: -> @getFullYear().toString()[2..]
|
||||||
Y: -> @getFullYear()
|
Y: -> @getFullYear()
|
||||||
|
|||||||
@ -190,7 +190,6 @@ Unread =
|
|||||||
Unread.lastReadPost = ID
|
Unread.lastReadPost = ID
|
||||||
Unread.readCount++
|
Unread.readCount++
|
||||||
return if Unread.thread.isDead and !Unread.thread.isArchived
|
return if Unread.thread.isDead and !Unread.thread.isArchived
|
||||||
Unread.db.forceSync()
|
|
||||||
Unread.db.set
|
Unread.db.set
|
||||||
boardID: Unread.thread.board.ID
|
boardID: Unread.thread.board.ID
|
||||||
threadID: Unread.thread.ID
|
threadID: Unread.thread.ID
|
||||||
|
|||||||
@ -297,9 +297,20 @@ QR =
|
|||||||
return unless QR.postingIsEnabled
|
return unless QR.postingIsEnabled
|
||||||
sel = d.getSelection()
|
sel = d.getSelection()
|
||||||
post = Get.postFromNode @
|
post = Get.postFromNode @
|
||||||
|
{root} = post.nodes
|
||||||
|
postRange = new Range()
|
||||||
|
postRange.selectNode root
|
||||||
text = if post.board.ID is g.BOARD.ID then ">>#{post}\n" else ">>>/#{post.board}/#{post}\n"
|
text = if post.board.ID is g.BOARD.ID then ">>#{post}\n" else ">>>/#{post.board}/#{post}\n"
|
||||||
if sel.toString().trim() and post is Get.postFromNode sel.anchorNode
|
for i in [0...sel.rangeCount]
|
||||||
range = sel.getRangeAt 0
|
range = sel.getRangeAt i
|
||||||
|
# Trim range to be fully inside post
|
||||||
|
if range.compareBoundaryPoints(Range.START_TO_START, postRange) < 0
|
||||||
|
range.setStartBefore root
|
||||||
|
if range.compareBoundaryPoints(Range.END_TO_END, postRange) > 0
|
||||||
|
range.setEndAfter root
|
||||||
|
|
||||||
|
continue unless range.toString().trim()
|
||||||
|
|
||||||
frag = range.cloneContents()
|
frag = range.cloneContents()
|
||||||
ancestor = range.commonAncestorContainer
|
ancestor = range.commonAncestorContainer
|
||||||
# Quoting the insides of a spoiler/code tag.
|
# Quoting the insides of a spoiler/code tag.
|
||||||
@ -313,10 +324,7 @@ QR =
|
|||||||
$.replace node, $.tn '\n'
|
$.replace node, $.tn '\n'
|
||||||
for node in $$ 'br', frag
|
for node in $$ 'br', frag
|
||||||
$.replace node, $.tn '\n>' unless node is frag.lastChild
|
$.replace node, $.tn '\n>' unless node is frag.lastChild
|
||||||
for node in $$ 's, .removed-spoiler', frag
|
Post::insertTags frag
|
||||||
$.replace node, [$.tn('[spoiler]'), node.childNodes..., $.tn '[/spoiler]']
|
|
||||||
for node in $$ '.prettyprint', frag
|
|
||||||
$.replace node, [$.tn('[code]'), node.childNodes..., $.tn '[/code]']
|
|
||||||
for node in $$ '.linkify[data-original]', frag
|
for node in $$ '.linkify[data-original]', frag
|
||||||
$.replace node, $.tn node.dataset.original
|
$.replace node, $.tn node.dataset.original
|
||||||
for node in $$ '.embedder', frag
|
for node in $$ '.embedder', frag
|
||||||
|
|||||||
@ -159,9 +159,9 @@ QR.cooldown =
|
|||||||
''
|
''
|
||||||
seconds = Math.max seconds, QR.cooldown.delays[type + suffix] - elapsed
|
seconds = Math.max seconds, QR.cooldown.delays[type + suffix] - elapsed
|
||||||
|
|
||||||
# If additional cooldown is enabled, add the configured seconds to the count.
|
# If additional cooldown is enabled, add the configured seconds to the count.
|
||||||
if QR.cooldown.customCooldown
|
if QR.cooldown.customCooldown
|
||||||
seconds = Math.max seconds, parseInt(Conf['customCooldown'], 10) - elapsed
|
seconds = Math.max seconds, parseInt(Conf['customCooldown'], 10) - elapsed
|
||||||
|
|
||||||
nCooldowns += Object.keys(cooldowns).length
|
nCooldowns += Object.keys(cooldowns).length
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,6 @@ class DataBoard
|
|||||||
@save()
|
@save()
|
||||||
|
|
||||||
deleteIfEmpty: ({boardID, threadID}) ->
|
deleteIfEmpty: ({boardID, threadID}) ->
|
||||||
$.forceSync @key
|
|
||||||
if threadID
|
if threadID
|
||||||
unless Object.keys(@data.boards[boardID][threadID]).length
|
unless Object.keys(@data.boards[boardID][threadID]).length
|
||||||
delete @data.boards[boardID][threadID]
|
delete @data.boards[boardID][threadID]
|
||||||
|
|||||||
@ -5,9 +5,8 @@ class Fetcher
|
|||||||
return
|
return
|
||||||
|
|
||||||
# 4chan X catalog data
|
# 4chan X catalog data
|
||||||
if (post = Index.replyData?["#{@boardID}.#{@postID}"])
|
if (post = Index.replyData?["#{@boardID}.#{@postID}"]) and (thread = g.threads["#{@boardID}.#{@threadID}"])
|
||||||
board = g.boards[@boardID]
|
board = g.boards[@boardID]
|
||||||
thread = g.threads["#{@boardID}.#{@threadID}"]
|
|
||||||
post = new Post Build.postFromObject(post, @boardID), thread, board
|
post = new Post Build.postFromObject(post, @boardID), thread, board
|
||||||
post.isFetchedQuote = true
|
post.isFetchedQuote = true
|
||||||
Main.callbackNodes 'Post', [post]
|
Main.callbackNodes 'Post', [post]
|
||||||
|
|||||||
@ -129,6 +129,12 @@ class Post
|
|||||||
@cleanCommentDisplay bq
|
@cleanCommentDisplay bq
|
||||||
@nodesToText(bq).trim().replace(/\s+$/gm, '')
|
@nodesToText(bq).trim().replace(/\s+$/gm, '')
|
||||||
|
|
||||||
|
commentOrig: ->
|
||||||
|
# Get the comment's text for reposting purposes.
|
||||||
|
bq = @nodes.commentClean.cloneNode true
|
||||||
|
@insertTags bq
|
||||||
|
@nodesToText bq
|
||||||
|
|
||||||
nodesToText: (bq) ->
|
nodesToText: (bq) ->
|
||||||
text = ""
|
text = ""
|
||||||
nodes = $.X './/br|.//text()', bq
|
nodes = $.X './/br|.//text()', bq
|
||||||
@ -155,6 +161,13 @@ class Post
|
|||||||
$.rm b if (b = $ 'b', bq) and /^Rolled /.test(b.textContent)
|
$.rm b if (b = $ 'b', bq) and /^Rolled /.test(b.textContent)
|
||||||
$.rm $('.fortune', bq)
|
$.rm $('.fortune', bq)
|
||||||
|
|
||||||
|
insertTags: (bq) ->
|
||||||
|
for node in $$ 's, .removed-spoiler', bq
|
||||||
|
$.replace node, [$.tn('[spoiler]'), node.childNodes..., $.tn '[/spoiler]']
|
||||||
|
for node in $$ '.prettyprint', bq
|
||||||
|
$.replace node, [$.tn('[code]'), node.childNodes..., $.tn '[/code]']
|
||||||
|
return
|
||||||
|
|
||||||
parseQuotes: ->
|
parseQuotes: ->
|
||||||
@quotes = []
|
@quotes = []
|
||||||
# XXX https://github.com/4chan/4chan-JS/issues/77
|
# XXX https://github.com/4chan/4chan-JS/issues/77
|
||||||
|
|||||||
@ -857,6 +857,7 @@ Config =
|
|||||||
captchaLanguage: ''
|
captchaLanguage: ''
|
||||||
|
|
||||||
time: '%m/%d/%y(%a)%H:%M:%S'
|
time: '%m/%d/%y(%a)%H:%M:%S'
|
||||||
|
timeLocale: ''
|
||||||
|
|
||||||
backlink: '>>%id'
|
backlink: '>>%id'
|
||||||
|
|
||||||
|
|||||||
@ -72,7 +72,7 @@ Main =
|
|||||||
items[key] = undefined for key of Conf
|
items[key] = undefined for key of Conf
|
||||||
items['previousversion'] = undefined
|
items['previousversion'] = undefined
|
||||||
($.getSync or $.get) items, (items) ->
|
($.getSync or $.get) items, (items) ->
|
||||||
if (items['Redirect to HTTPS'] ? Conf['Redirect to HTTPS']) and location.protocol isnt 'https:'
|
if !$.perProtocolSettings and (items['Redirect to HTTPS'] ? Conf['Redirect to HTTPS']) and location.protocol isnt 'https:'
|
||||||
location.replace('https:' + location.host + location.pathname + location.search + location.hash)
|
location.replace('https:' + location.host + location.pathname + location.search + location.hash)
|
||||||
return
|
return
|
||||||
$.asap docSet, ->
|
$.asap docSet, ->
|
||||||
|
|||||||
@ -16,11 +16,14 @@
|
|||||||
"globals": {
|
"globals": {
|
||||||
"MediaError": false,
|
"MediaError": false,
|
||||||
"Set": false,
|
"Set": false,
|
||||||
|
"Promise": false,
|
||||||
|
"BroadcastChannel": false,
|
||||||
"GM_info": false,
|
"GM_info": false,
|
||||||
"cloneInto": false,
|
"cloneInto": false,
|
||||||
"unsafeWindow": false,
|
"unsafeWindow": false,
|
||||||
"chrome": false<%=
|
"chrome": false,
|
||||||
meta.grants.map(x => `,\n "${x}": false`).join('')
|
"GM": false<%=
|
||||||
|
meta.grants.filter(x => !/\./.test(x)).map(x => `,\n "${x}": false`).join('')
|
||||||
%><%=
|
%><%=
|
||||||
read('/tmp/declaration.js').match(/^var (.*);/)[1].split(', ').map(x => `,\n "${x}": true`).join('')
|
read('/tmp/declaration.js').match(/^var (.*);/)[1].split(', ').map(x => `,\n "${x}": true`).join('')
|
||||||
%><%=
|
%><%=
|
||||||
|
|||||||
@ -369,6 +369,13 @@ $.item = (key, val) ->
|
|||||||
item[key] = val
|
item[key] = val
|
||||||
item
|
item
|
||||||
|
|
||||||
|
$.oneItemSugar = (fn) ->
|
||||||
|
(key, val, cb) ->
|
||||||
|
if typeof key is 'string'
|
||||||
|
fn $.item(key, val), cb
|
||||||
|
else
|
||||||
|
fn key, val
|
||||||
|
|
||||||
$.syncing = {}
|
$.syncing = {}
|
||||||
|
|
||||||
<% if (type === 'crx') { %>
|
<% if (type === 'crx') { %>
|
||||||
@ -402,14 +409,8 @@ $.crxWorking = ->
|
|||||||
$.crxWarningShown = true
|
$.crxWarningShown = true
|
||||||
false
|
false
|
||||||
|
|
||||||
$.get = (key, val, cb) ->
|
$.get = $.oneItemSugar (data, cb) ->
|
||||||
return unless $.crxWorking()
|
return unless $.crxWorking()
|
||||||
if typeof cb is 'function'
|
|
||||||
data = $.item key, val
|
|
||||||
else
|
|
||||||
data = key
|
|
||||||
cb = val
|
|
||||||
|
|
||||||
results = {}
|
results = {}
|
||||||
get = (area) ->
|
get = (area) ->
|
||||||
chrome.storage[area].get Object.keys(data), (result) ->
|
chrome.storage[area].get Object.keys(data), (result) ->
|
||||||
@ -468,13 +469,8 @@ do ->
|
|||||||
setSync = $.debounce $.SECOND, ->
|
setSync = $.debounce $.SECOND, ->
|
||||||
setArea 'sync'
|
setArea 'sync'
|
||||||
|
|
||||||
$.set = (key, val, cb) ->
|
$.set = $.oneItemSugar (data, cb) ->
|
||||||
return unless $.crxWorking()
|
return unless $.crxWorking()
|
||||||
if typeof key is 'string'
|
|
||||||
data = $.item key, val
|
|
||||||
else
|
|
||||||
data = key
|
|
||||||
cb = val
|
|
||||||
$.extend items.local, data
|
$.extend items.local, data
|
||||||
setArea 'local', cb
|
setArea 'local', cb
|
||||||
|
|
||||||
@ -496,131 +492,167 @@ do ->
|
|||||||
# http://wiki.greasespot.net/Main_Page
|
# http://wiki.greasespot.net/Main_Page
|
||||||
# https://tampermonkey.net/documentation.php
|
# https://tampermonkey.net/documentation.php
|
||||||
|
|
||||||
# workaround for Firefox 53 issue
|
if GM?.deleteValue?
|
||||||
$.currentValue = {}
|
|
||||||
$.GM_getValue = (key) ->
|
|
||||||
try
|
|
||||||
$.currentValue[key] = GM_getValue key
|
|
||||||
catch err
|
|
||||||
$.currentValue[key]
|
|
||||||
$.GM_setValue = (key, val) ->
|
|
||||||
$.currentValue[key] = val
|
|
||||||
GM_setValue key, val
|
|
||||||
$.GM_deleteValue = (key) ->
|
|
||||||
delete $.currentValue[key]
|
|
||||||
GM_deleteValue key
|
|
||||||
|
|
||||||
if GM_deleteValue?
|
$.syncChannel = new BroadcastChannel(g.NAMESPACE + 'sync')
|
||||||
$.getValue = $.GM_getValue
|
|
||||||
$.listValues = -> GM_listValues() # error when called if missing
|
|
||||||
else if $.hasStorage
|
|
||||||
$.getValue = (key) -> localStorage[key]
|
|
||||||
$.listValues = ->
|
|
||||||
key for key of localStorage when key[...g.NAMESPACE.length] is g.NAMESPACE
|
|
||||||
else
|
|
||||||
$.getValue = ->
|
|
||||||
$.listValues = -> []
|
|
||||||
|
|
||||||
if GM_addValueChangeListener?
|
$.on $.syncChannel, 'message', (e) ->
|
||||||
$.setValue = $.GM_setValue
|
for key, val of e.data when (cb = $.syncing[key])
|
||||||
$.deleteValue = $.GM_deleteValue
|
cb val, key
|
||||||
else if GM_deleteValue?
|
|
||||||
$.oldValue = {}
|
|
||||||
$.setValue = (key, val) ->
|
|
||||||
$.GM_setValue key, val
|
|
||||||
if key of $.syncing
|
|
||||||
$.oldValue[key] = val
|
|
||||||
localStorage[key] = val if $.hasStorage # for `storage` events
|
|
||||||
$.deleteValue = (key) ->
|
|
||||||
$.GM_deleteValue key
|
|
||||||
if key of $.syncing
|
|
||||||
delete $.oldValue[key]
|
|
||||||
localStorage.removeItem key if $.hasStorage # for `storage` events
|
|
||||||
$.cantSync = true if !$.hasStorage
|
|
||||||
else if $.hasStorage
|
|
||||||
$.oldValue = {}
|
|
||||||
$.setValue = (key, val) ->
|
|
||||||
$.oldValue[key] = val if key of $.syncing
|
|
||||||
localStorage[key] = val
|
|
||||||
$.deleteValue = (key) ->
|
|
||||||
delete $.oldValue[key] if key of $.syncing
|
|
||||||
localStorage.removeItem key
|
|
||||||
else
|
|
||||||
$.setValue = ->
|
|
||||||
$.deleteValue = ->
|
|
||||||
$.cantSync = $.cantSet = true
|
|
||||||
|
|
||||||
if GM_addValueChangeListener?
|
|
||||||
$.sync = (key, cb) ->
|
$.sync = (key, cb) ->
|
||||||
$.syncing[key] = GM_addValueChangeListener g.NAMESPACE + key, (key2, oldValue, newValue, remote) ->
|
|
||||||
if remote
|
|
||||||
newValue = JSON.parse newValue unless newValue is undefined
|
|
||||||
cb newValue, key
|
|
||||||
$.forceSync = ->
|
|
||||||
else if GM_deleteValue? or $.hasStorage
|
|
||||||
$.sync = (key, cb) ->
|
|
||||||
key = g.NAMESPACE + key
|
|
||||||
$.syncing[key] = cb
|
$.syncing[key] = cb
|
||||||
$.oldValue[key] = $.getValue key
|
|
||||||
|
|
||||||
do ->
|
|
||||||
onChange = ({key, newValue}) ->
|
|
||||||
return if not (cb = $.syncing[key])
|
|
||||||
if newValue?
|
|
||||||
return if newValue is $.oldValue[key]
|
|
||||||
$.oldValue[key] = newValue
|
|
||||||
cb JSON.parse(newValue), key[g.NAMESPACE.length..]
|
|
||||||
else
|
|
||||||
return unless $.oldValue[key]?
|
|
||||||
delete $.oldValue[key]
|
|
||||||
cb undefined, key[g.NAMESPACE.length..]
|
|
||||||
$.on window, 'storage', onChange
|
|
||||||
|
|
||||||
$.forceSync = (key) ->
|
|
||||||
# Storage events don't work across origins
|
|
||||||
# e.g. http://boards.4chan.org and https://boards.4chan.org
|
|
||||||
# so force a check for changes to avoid lost data.
|
|
||||||
key = g.NAMESPACE + key
|
|
||||||
onChange {key, newValue: $.getValue key}
|
|
||||||
else
|
|
||||||
$.sync = ->
|
|
||||||
$.forceSync = ->
|
$.forceSync = ->
|
||||||
|
|
||||||
$.delete = (keys) ->
|
$.delete = (keys, cb) ->
|
||||||
unless keys instanceof Array
|
unless keys instanceof Array
|
||||||
keys = [keys]
|
keys = [keys]
|
||||||
for key in keys
|
Promise.all(GM.deleteValue(g.NAMESPACE + key) for key in keys).then ->
|
||||||
$.deleteValue g.NAMESPACE + key
|
items = {}
|
||||||
return
|
items[key] = undefined for key in keys
|
||||||
|
$.syncChannel.postMessage items
|
||||||
|
cb?()
|
||||||
|
|
||||||
$.get = (key, val, cb) ->
|
$.get = $.oneItemSugar (items, cb) ->
|
||||||
if typeof cb is 'function'
|
keys = Object.keys items
|
||||||
items = $.item key, val
|
Promise.all(GM.getValue(g.NAMESPACE + key) for key in keys).then (values) ->
|
||||||
|
for val, i in values when val
|
||||||
|
items[keys[i]] = JSON.parse val
|
||||||
|
cb items
|
||||||
|
|
||||||
|
$.set = $.oneItemSugar (items, cb) ->
|
||||||
|
Promise.all(GM.setValue(g.NAMESPACE + key, JSON.stringify(val)) for key, val of items).then ->
|
||||||
|
$.syncChannel.postMessage items
|
||||||
|
cb?()
|
||||||
|
|
||||||
|
$.clear = (cb) ->
|
||||||
|
GM.listValues.then (keys) ->
|
||||||
|
$.delete keys.map((key) -> key.replace g.NAMESPACE, ''), cb
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
# workaround for Firefox 53 issue
|
||||||
|
$.currentValue = {}
|
||||||
|
$.GM_getValue = (key) ->
|
||||||
|
try
|
||||||
|
$.currentValue[key] = GM_getValue key
|
||||||
|
catch err
|
||||||
|
$.currentValue[key]
|
||||||
|
$.GM_setValue = (key, val) ->
|
||||||
|
$.currentValue[key] = val
|
||||||
|
GM_setValue key, val
|
||||||
|
$.GM_deleteValue = (key) ->
|
||||||
|
delete $.currentValue[key]
|
||||||
|
GM_deleteValue key
|
||||||
|
|
||||||
|
unless GM_deleteValue?
|
||||||
|
$.perProtocolSettings = true
|
||||||
|
|
||||||
|
if GM_deleteValue?
|
||||||
|
$.getValue = $.GM_getValue
|
||||||
|
$.listValues = -> GM_listValues() # error when called if missing
|
||||||
|
else if $.hasStorage
|
||||||
|
$.getValue = (key) -> localStorage[key]
|
||||||
|
$.listValues = ->
|
||||||
|
key for key of localStorage when key[...g.NAMESPACE.length] is g.NAMESPACE
|
||||||
else
|
else
|
||||||
items = key
|
$.getValue = ->
|
||||||
cb = val
|
$.listValues = -> []
|
||||||
$.queueTask $.getSync, items, cb
|
|
||||||
|
|
||||||
$.getSync = (items, cb) ->
|
if GM_addValueChangeListener?
|
||||||
for key of items when (val2 = $.getValue g.NAMESPACE + key)
|
$.setValue = $.GM_setValue
|
||||||
items[key] = JSON.parse val2
|
$.deleteValue = $.GM_deleteValue
|
||||||
cb items
|
else if GM_deleteValue?
|
||||||
|
$.oldValue = {}
|
||||||
$.set = (keys, val, cb) ->
|
$.setValue = (key, val) ->
|
||||||
if typeof keys is 'string'
|
$.GM_setValue key, val
|
||||||
$.setValue(g.NAMESPACE + keys, JSON.stringify val)
|
if key of $.syncing
|
||||||
|
$.oldValue[key] = val
|
||||||
|
localStorage[key] = val if $.hasStorage # for `storage` events
|
||||||
|
$.deleteValue = (key) ->
|
||||||
|
$.GM_deleteValue key
|
||||||
|
if key of $.syncing
|
||||||
|
delete $.oldValue[key]
|
||||||
|
localStorage.removeItem key if $.hasStorage # for `storage` events
|
||||||
|
$.cantSync = true if !$.hasStorage
|
||||||
|
else if $.hasStorage
|
||||||
|
$.oldValue = {}
|
||||||
|
$.setValue = (key, val) ->
|
||||||
|
$.oldValue[key] = val if key of $.syncing
|
||||||
|
localStorage[key] = val
|
||||||
|
$.deleteValue = (key) ->
|
||||||
|
delete $.oldValue[key] if key of $.syncing
|
||||||
|
localStorage.removeItem key
|
||||||
else
|
else
|
||||||
for key, value of keys
|
$.setValue = ->
|
||||||
$.setValue(g.NAMESPACE + key, JSON.stringify value)
|
$.deleteValue = ->
|
||||||
cb = val
|
$.cantSync = $.cantSet = true
|
||||||
cb?()
|
|
||||||
|
if GM_addValueChangeListener?
|
||||||
|
$.sync = (key, cb) ->
|
||||||
|
$.syncing[key] = GM_addValueChangeListener g.NAMESPACE + key, (key2, oldValue, newValue, remote) ->
|
||||||
|
if remote
|
||||||
|
newValue = JSON.parse newValue unless newValue is undefined
|
||||||
|
cb newValue, key
|
||||||
|
$.forceSync = ->
|
||||||
|
else if GM_deleteValue? or $.hasStorage
|
||||||
|
$.sync = (key, cb) ->
|
||||||
|
key = g.NAMESPACE + key
|
||||||
|
$.syncing[key] = cb
|
||||||
|
$.oldValue[key] = $.getValue key
|
||||||
|
|
||||||
|
do ->
|
||||||
|
onChange = ({key, newValue}) ->
|
||||||
|
return if not (cb = $.syncing[key])
|
||||||
|
if newValue?
|
||||||
|
return if newValue is $.oldValue[key]
|
||||||
|
$.oldValue[key] = newValue
|
||||||
|
cb JSON.parse(newValue), key[g.NAMESPACE.length..]
|
||||||
|
else
|
||||||
|
return unless $.oldValue[key]?
|
||||||
|
delete $.oldValue[key]
|
||||||
|
cb undefined, key[g.NAMESPACE.length..]
|
||||||
|
$.on window, 'storage', onChange
|
||||||
|
|
||||||
|
$.forceSync = (key) ->
|
||||||
|
# Storage events don't work across origins
|
||||||
|
# e.g. http://boards.4chan.org and https://boards.4chan.org
|
||||||
|
# so force a check for changes to avoid lost data.
|
||||||
|
key = g.NAMESPACE + key
|
||||||
|
onChange {key, newValue: $.getValue key}
|
||||||
|
else
|
||||||
|
$.sync = ->
|
||||||
|
$.forceSync = ->
|
||||||
|
|
||||||
|
$.delete = (keys) ->
|
||||||
|
unless keys instanceof Array
|
||||||
|
keys = [keys]
|
||||||
|
for key in keys
|
||||||
|
$.deleteValue g.NAMESPACE + key
|
||||||
|
return
|
||||||
|
|
||||||
|
$.get = $.oneItemSugar (items, cb) ->
|
||||||
|
$.queueTask $.getSync, items, cb
|
||||||
|
|
||||||
|
$.getSync = (items, cb) ->
|
||||||
|
for key of items when (val2 = $.getValue g.NAMESPACE + key)
|
||||||
|
items[key] = JSON.parse val2
|
||||||
|
cb items
|
||||||
|
|
||||||
|
$.set = $.oneItemSugar (items, cb) ->
|
||||||
|
$.queueTask ->
|
||||||
|
for key, value of items
|
||||||
|
$.setValue(g.NAMESPACE + key, JSON.stringify value)
|
||||||
|
cb?()
|
||||||
|
|
||||||
|
$.clear = (cb) ->
|
||||||
|
# XXX https://github.com/greasemonkey/greasemonkey/issues/2033
|
||||||
|
# Also support case where GM_listValues is not defined.
|
||||||
|
$.delete Object.keys(Conf)
|
||||||
|
$.delete ['previousversion', 'QR Size', 'captchas', 'QR.persona', 'hiddenPSA']
|
||||||
|
try
|
||||||
|
$.delete $.listValues().map (key) -> key.replace g.NAMESPACE, ''
|
||||||
|
cb?()
|
||||||
|
|
||||||
$.clear = (cb) ->
|
|
||||||
# XXX https://github.com/greasemonkey/greasemonkey/issues/2033
|
|
||||||
# Also support case where GM_listValues is not defined.
|
|
||||||
$.delete Object.keys(Conf)
|
|
||||||
$.delete ['previousversion', 'QR Size', 'captchas', 'QR.persona', 'hiddenPSA']
|
|
||||||
try
|
|
||||||
$.delete $.listValues().map (key) -> key.replace g.NAMESPACE, ''
|
|
||||||
cb?()
|
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|||||||
@ -63,7 +63,7 @@ CrossOrigin =
|
|||||||
options.overrideMimeType = 'text/plain; charset=x-user-defined'
|
options.overrideMimeType = 'text/plain; charset=x-user-defined'
|
||||||
else
|
else
|
||||||
options.responseType = 'arraybuffer'
|
options.responseType = 'arraybuffer'
|
||||||
GM_xmlhttpRequest options
|
(GM?.xmlHttpRequest or GM_xmlhttpRequest) options
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
file: (url, cb) ->
|
file: (url, cb) ->
|
||||||
@ -99,7 +99,7 @@ CrossOrigin =
|
|||||||
return
|
return
|
||||||
callbacks[url] = [cb]
|
callbacks[url] = [cb]
|
||||||
<% if (type === 'userscript') { %>
|
<% if (type === 'userscript') { %>
|
||||||
GM_xmlhttpRequest
|
(GM?.xmlHttpRequest or GM_xmlhttpRequest)
|
||||||
method: "GET"
|
method: "GET"
|
||||||
url: url+''
|
url: url+''
|
||||||
onload: (xhr) ->
|
onload: (xhr) ->
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"version": "1.13.10.5",
|
"version": "1.13.12.1",
|
||||||
"date": "2017-08-04T04:26:25.809Z"
|
"date": "2017-09-29T00:42:32.947Z"
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user