Merge branch 'master' into webext

This commit is contained in:
ccd0 2017-09-29 02:45:54 -07:00
commit 3e019cc116
35 changed files with 1615 additions and 1053 deletions

View File

@ -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).
### 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.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)]
- Better parsing of archive links for Quote Inlining / Hover.
- Add Board Tips.

View File

@ -10,21 +10,12 @@ https://github.com/Nebukazar/OneeChan.
## 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.
**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.
**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).
**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.
## Install
### 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:
- 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)**.
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)**.
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.

View File

@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X beta
// @version 1.13.10.5
// @version 1.13.12.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@ -48,6 +48,11 @@
// @grant GM_addValueChangeListener
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.deleteValue
// @grant GM.listValues
// @grant GM.xmlHttpRequest
// @run-at document-start
// @updateURL https://www.4chan-x.net/builds/4chan-X-beta.meta.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.

View File

@ -1,6 +1,6 @@
// ==UserScript==
// @name 4chan X
// @version 1.13.10.5
// @version 1.13.12.1
// @minGMVer 1.14
// @minFFVer 26
// @namespace 4chan-X
@ -48,6 +48,11 @@
// @grant GM_addValueChangeListener
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.deleteValue
// @grant GM.listValues
// @grant GM.xmlHttpRequest
// @run-at document-start
// @updateURL https://www.4chan-x.net/builds/4chan-X.meta.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.

View File

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<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>
</gupdate>

View File

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
<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>
</gupdate>

View File

@ -23,8 +23,7 @@
<a href="https://github.com/Nebukazar/OneeChan">https://github.com/Nebukazar/OneeChan</a>.</p>
<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 &quot;<code>Disable the native extension</code>&quot; in the panel that appears, and click the &quot;<code>Save Settings</code>&quot; button. If you don't see a &quot;<code>Save Settings</code>&quot; 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>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>
<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>
<h2 id="install">Install</h2>
<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>

View File

@ -57,7 +57,12 @@
"GM_listValues",
"GM_addValueChangeListener",
"GM_openInTab",
"GM_xmlhttpRequest"
"GM_xmlhttpRequest",
"GM.getValue",
"GM.setValue",
"GM.deleteValue",
"GM.listValues",
"GM.xmlHttpRequest"
],
"min": {
"chrome": "33",

View File

@ -114,16 +114,6 @@
"boards": ["h", "hc", "hm", "r", "s", "soc"],
"files": ["h", "hc", "hm", "r", "s", "soc"],
"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,
"name": "YEET Archive",

View File

@ -688,6 +688,9 @@ Index =
else
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) ->
threads = []
newThreads = []
@ -788,8 +791,7 @@ Index =
replies = []
for data in lastReplies
continue if PostHiding.isHidden(g.BOARD.ID, thread.ID, data.no)
continue if Filter.isHidden(Build.parseJSON data, g.BOARD.ID)
continue if Index.isHiddenReply thread.ID, data
reply = Build.catalogReply thread, data
RelativeDates.update $('time', reply)
$.on $('.catalog-reply-preview', reply), 'mouseover', QuotePreview.mouseover
@ -813,12 +815,16 @@ Index =
when 'lastlong'
lastlong = (thread) ->
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
if len >= Index.lastLongThresholds[+!!r.ext]
return r
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) ->
lastlong(b).no - lastlong(a).no
lastlongD[b.no] - lastlongD[a.no]
).map (post) -> post.no
when 'bump' then liveThreadIDs
when 'birth' then [liveThreadIDs... ].sort (a, b) -> b - a

View File

@ -162,8 +162,12 @@ Settings =
addCheckboxes fs, obj
$.add section, fs
addCheckboxes $('div[data-name="JSON Index"] > .suboption-list', section), Config.Index
# Unsupported options
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) ->
for key, val of items
@ -489,7 +493,7 @@ Settings =
$.id('lastarchivecheck').textContent = 'never'
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]
input = inputs[name]
event = if name in ['archiveLists', 'archiveAutoUpdate', 'QR.personas', 'favicon', 'usercss'] then 'change' else 'input'
@ -642,6 +646,9 @@ Settings =
time: ->
@nextElementSibling.textContent = Time.format @value, new Date()
timeLocale: ->
Settings.time.call $('[name=time]', Settings.dialog)
backlink: ->
@nextElementSibling.textContent = @value.replace /%(?:id|%)/g, (x) -> ({'%id': '123456789', '%%': '%'})[x]

View File

@ -64,6 +64,7 @@
<div>Minute: <code>%M</code></div>
<div>Second: <code>%S</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>

View File

@ -12,7 +12,7 @@ CopyTextLink =
el: a
order: 12
open: (post) ->
CopyTextLink.text = post.info.comment
CopyTextLink.text = (post.origin or post).commentOrig()
true
copy: ->

View File

@ -5,7 +5,7 @@ BoardTips =
<%= html(
'New to /qa/?<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>.'
) %>
]

View File

@ -312,7 +312,7 @@ Keybinds =
return if g.VIEW isnt 'index'
url = "/#{thread.board}/thread/#{thread}"
if tab
$.open url
$.open location.origin + url
else
location.href = url

View File

@ -41,13 +41,26 @@ Time =
'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
formatters:
a: -> Time.day[@getDay()][...3]
A: -> Time.day[@getDay()]
b: -> Time.month[@getMonth()][...3]
B: -> Time.month[@getMonth()]
a: -> Time.localeFormat @, {weekday: 'short'}, Time.day[@getDay()][...3]
A: -> Time.localeFormat @, {weekday: 'long'}, Time.day[@getDay()]
b: -> Time.localeFormat @, {month: 'short'}, Time.month[@getMonth()][...3]
B: -> Time.localeFormat @, {month: 'long'}, Time.month[@getMonth()]
d: -> Time.zeroPad @getDate()
e: -> @getDate()
H: -> Time.zeroPad @getHours()
@ -56,8 +69,8 @@ Time =
l: -> @getHours() % 12 or 12
m: -> Time.zeroPad @getMonth() + 1
M: -> Time.zeroPad @getMinutes()
p: -> if @getHours() < 12 then 'AM' else 'PM'
P: -> if @getHours() < 12 then 'am' else 'pm'
p: -> Time.localeFormatPart @, {hour: 'numeric', hour12: true}, 'dayperiod', (if @getHours() < 12 then 'AM' else 'PM')
P: -> Time.formatters.p.call(@).toLowerCase()
S: -> Time.zeroPad @getSeconds()
y: -> @getFullYear().toString()[2..]
Y: -> @getFullYear()

View File

@ -190,7 +190,6 @@ Unread =
Unread.lastReadPost = ID
Unread.readCount++
return if Unread.thread.isDead and !Unread.thread.isArchived
Unread.db.forceSync()
Unread.db.set
boardID: Unread.thread.board.ID
threadID: Unread.thread.ID

View File

@ -297,9 +297,20 @@ QR =
return unless QR.postingIsEnabled
sel = d.getSelection()
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"
if sel.toString().trim() and post is Get.postFromNode sel.anchorNode
range = sel.getRangeAt 0
for i in [0...sel.rangeCount]
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()
ancestor = range.commonAncestorContainer
# Quoting the insides of a spoiler/code tag.
@ -313,10 +324,7 @@ QR =
$.replace node, $.tn '\n'
for node in $$ 'br', frag
$.replace node, $.tn '\n>' unless node is frag.lastChild
for node in $$ 's, .removed-spoiler', frag
$.replace node, [$.tn('[spoiler]'), node.childNodes..., $.tn '[/spoiler]']
for node in $$ '.prettyprint', frag
$.replace node, [$.tn('[code]'), node.childNodes..., $.tn '[/code]']
Post::insertTags frag
for node in $$ '.linkify[data-original]', frag
$.replace node, $.tn node.dataset.original
for node in $$ '.embedder', frag

View File

@ -159,9 +159,9 @@ QR.cooldown =
''
seconds = Math.max seconds, QR.cooldown.delays[type + suffix] - elapsed
# If additional cooldown is enabled, add the configured seconds to the count.
if QR.cooldown.customCooldown
seconds = Math.max seconds, parseInt(Conf['customCooldown'], 10) - elapsed
# If additional cooldown is enabled, add the configured seconds to the count.
if QR.cooldown.customCooldown
seconds = Math.max seconds, parseInt(Conf['customCooldown'], 10) - elapsed
nCooldowns += Object.keys(cooldowns).length

View File

@ -30,7 +30,6 @@ class DataBoard
@save()
deleteIfEmpty: ({boardID, threadID}) ->
$.forceSync @key
if threadID
unless Object.keys(@data.boards[boardID][threadID]).length
delete @data.boards[boardID][threadID]

View File

@ -5,9 +5,8 @@ class Fetcher
return
# 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]
thread = g.threads["#{@boardID}.#{@threadID}"]
post = new Post Build.postFromObject(post, @boardID), thread, board
post.isFetchedQuote = true
Main.callbackNodes 'Post', [post]

View File

@ -129,6 +129,12 @@ class Post
@cleanCommentDisplay bq
@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) ->
text = ""
nodes = $.X './/br|.//text()', bq
@ -155,6 +161,13 @@ class Post
$.rm b if (b = $ 'b', bq) and /^Rolled /.test(b.textContent)
$.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: ->
@quotes = []
# XXX https://github.com/4chan/4chan-JS/issues/77

View File

@ -857,6 +857,7 @@ Config =
captchaLanguage: ''
time: '%m/%d/%y(%a)%H:%M:%S'
timeLocale: ''
backlink: '>>%id'

View File

@ -72,7 +72,7 @@ Main =
items[key] = undefined for key of Conf
items['previousversion'] = undefined
($.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)
return
$.asap docSet, ->

View File

@ -16,11 +16,14 @@
"globals": {
"MediaError": false,
"Set": false,
"Promise": false,
"BroadcastChannel": false,
"GM_info": false,
"cloneInto": false,
"unsafeWindow": false,
"chrome": false<%=
meta.grants.map(x => `,\n "${x}": false`).join('')
"chrome": false,
"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('')
%><%=

View File

@ -369,6 +369,13 @@ $.item = (key, val) ->
item[key] = val
item
$.oneItemSugar = (fn) ->
(key, val, cb) ->
if typeof key is 'string'
fn $.item(key, val), cb
else
fn key, val
$.syncing = {}
<% if (type === 'crx') { %>
@ -402,14 +409,8 @@ $.crxWorking = ->
$.crxWarningShown = true
false
$.get = (key, val, cb) ->
$.get = $.oneItemSugar (data, cb) ->
return unless $.crxWorking()
if typeof cb is 'function'
data = $.item key, val
else
data = key
cb = val
results = {}
get = (area) ->
chrome.storage[area].get Object.keys(data), (result) ->
@ -468,13 +469,8 @@ do ->
setSync = $.debounce $.SECOND, ->
setArea 'sync'
$.set = (key, val, cb) ->
$.set = $.oneItemSugar (data, cb) ->
return unless $.crxWorking()
if typeof key is 'string'
data = $.item key, val
else
data = key
cb = val
$.extend items.local, data
setArea 'local', cb
@ -496,131 +492,167 @@ do ->
# http://wiki.greasespot.net/Main_Page
# https://tampermonkey.net/documentation.php
# 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
if GM?.deleteValue?
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
$.getValue = ->
$.listValues = -> []
$.syncChannel = new BroadcastChannel(g.NAMESPACE + 'sync')
if GM_addValueChangeListener?
$.setValue = $.GM_setValue
$.deleteValue = $.GM_deleteValue
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
$.on $.syncChannel, 'message', (e) ->
for key, val of e.data when (cb = $.syncing[key])
cb val, key
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
$.delete = (keys, cb) ->
unless keys instanceof Array
keys = [keys]
Promise.all(GM.deleteValue(g.NAMESPACE + key) for key in keys).then ->
items = {}
items[key] = undefined for key in keys
$.syncChannel.postMessage items
cb?()
$.get = (key, val, cb) ->
if typeof cb is 'function'
items = $.item key, val
$.get = $.oneItemSugar (items, cb) ->
keys = Object.keys items
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
items = key
cb = val
$.queueTask $.getSync, items, cb
$.getValue = ->
$.listValues = -> []
$.getSync = (items, cb) ->
for key of items when (val2 = $.getValue g.NAMESPACE + key)
items[key] = JSON.parse val2
cb items
$.set = (keys, val, cb) ->
if typeof keys is 'string'
$.setValue(g.NAMESPACE + keys, JSON.stringify val)
if GM_addValueChangeListener?
$.setValue = $.GM_setValue
$.deleteValue = $.GM_deleteValue
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
for key, value of keys
$.setValue(g.NAMESPACE + key, JSON.stringify value)
cb = val
cb?()
$.setValue = ->
$.deleteValue = ->
$.cantSync = $.cantSet = true
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?()
<% } %>

View File

@ -63,7 +63,7 @@ CrossOrigin =
options.overrideMimeType = 'text/plain; charset=x-user-defined'
else
options.responseType = 'arraybuffer'
GM_xmlhttpRequest options
(GM?.xmlHttpRequest or GM_xmlhttpRequest) options
<% } %>
file: (url, cb) ->
@ -99,7 +99,7 @@ CrossOrigin =
return
callbacks[url] = [cb]
<% if (type === 'userscript') { %>
GM_xmlhttpRequest
(GM?.xmlHttpRequest or GM_xmlhttpRequest)
method: "GET"
url: url+''
onload: (xhr) ->

View File

@ -1,4 +1,4 @@
{
"version": "1.13.10.5",
"date": "2017-08-04T04:26:25.809Z"
"version": "1.13.12.1",
"date": "2017-09-29T00:42:32.947Z"
}