Merge branch 'v3'
Conflicts: .gitignore CHANGELOG.md Gruntfile.coffee LICENSE builds/crx/manifest.json builds/crx/script.js latest.js package.json src/General/Config.coffee src/General/Globals.coffee src/General/Header.coffee src/General/Main.coffee src/General/UI.coffee src/General/css/style.css src/General/meta/manifest.json src/Posting/QuickReply.coffee
This commit is contained in:
commit
16b35dffce
9
.gitignore
vendored
9
.gitignore
vendored
@ -2,9 +2,14 @@ node_modules/
|
|||||||
*~
|
*~
|
||||||
*.db
|
*.db
|
||||||
tmp-crx/
|
tmp-crx/
|
||||||
tmp-userjs/
|
|
||||||
tmp-userscript/
|
tmp-userscript/
|
||||||
|
<<<<<<< HEAD
|
||||||
builds/4chan-X.zip
|
builds/4chan-X.zip
|
||||||
Gruntfile.js
|
Gruntfile.js
|
||||||
builds/4chan-*
|
builds/4chan-*
|
||||||
Gruntfile.js
|
Gruntfile.js
|
||||||
|
=======
|
||||||
|
builds/4chan-X-Chrome.zip
|
||||||
|
builds/4chan-X-Opera.nex
|
||||||
|
Gruntfile.js
|
||||||
|
>>>>>>> v3
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
**MayhemYDG**:
|
**MayhemYDG**:
|
||||||
- Remove /s4s/ from warosu archive
|
- Remove /s4s/ from warosu archive
|
||||||
- Fix CAPTCHA duplication on the report page
|
- Fix CAPTCHA duplication on the report page
|
||||||
- Small bug fixes
|
- Fix impossibility to create new threads when in dead threads.
|
||||||
|
- Drop Opera <15 support.
|
||||||
|
- Fix flag filtering on /sp/ and /int/.
|
||||||
|
- Minor fixes.
|
||||||
|
|
||||||
**seaweedchan**:
|
**seaweedchan**:
|
||||||
- Add `.active` class to `.menu-button` when clicked (and remove on menu close)
|
- Add `.active` class to `.menu-button` when clicked (and remove on menu close)
|
||||||
@ -9,6 +12,10 @@
|
|||||||
- Revert Mayhem's updater changes which caused silly issues
|
- Revert Mayhem's updater changes which caused silly issues
|
||||||
- Rename `Indicate Spoilers` to `Reveal Spoilers`
|
- Rename `Indicate Spoilers` to `Reveal Spoilers`
|
||||||
- If `Reveal Spoilers` is enabled but `Remove Spoilers` is not, act as if the spoiler is hovered
|
- If `Reveal Spoilers` is enabled but `Remove Spoilers` is not, act as if the spoiler is hovered
|
||||||
|
- Add a new option to hide "4chan X has been updated to ____" notifications for those having issues with them.
|
||||||
|
- Update archives
|
||||||
|
- Add `.active` class to `.menu-button` when clicked (and remove on menu close)
|
||||||
|
- Move /v/ and /vg/ back to Foolz archive
|
||||||
|
|
||||||
**Tracerneo**:
|
**Tracerneo**:
|
||||||
- Add ID styling for IDs with black text
|
- Add ID styling for IDs with black text
|
||||||
|
|||||||
@ -183,7 +183,6 @@ module.exports = (grunt) ->
|
|||||||
grunt.registerTask 'release', [
|
grunt.registerTask 'release', [
|
||||||
'default'
|
'default'
|
||||||
'compress:crx'
|
'compress:crx'
|
||||||
'copy:opera'
|
|
||||||
'shell:commit'
|
'shell:commit'
|
||||||
'shell:push'
|
'shell:push'
|
||||||
]
|
]
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* appchan x - Version 2.1.3 - 2013-07-07
|
* appchan x - Version 2.1.3 - 2013-07-21
|
||||||
*
|
*
|
||||||
* Licensed under the MIT license.
|
* Licensed under the MIT license.
|
||||||
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
* https://github.com/zixaphir/appchan-x/blob/master/LICENSE
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name 4chan X
|
// @name 4chan X
|
||||||
// @version 1.2.17
|
// @version 1.2.19
|
||||||
// @namespace 4chan-X
|
// @namespace 4chan-X
|
||||||
// @description Cross-browser userscript for maximum lurking on 4chan.
|
// @description Cross-browser userscript for maximum lurking on 4chan.
|
||||||
// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
// @license MIT; https://github.com/seaweedchan/4chan-x/blob/master/LICENSE
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,7 @@
|
|||||||
}],
|
}],
|
||||||
"homepage_url": "http://zixaphir.github.com/appchan-x/",
|
"homepage_url": "http://zixaphir.github.com/appchan-x/",
|
||||||
"minimum_chrome_version": "24",
|
"minimum_chrome_version": "24",
|
||||||
|
"minimum_opera_version": "15",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage"
|
"storage"
|
||||||
]
|
]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
3
html/Monitoring/ThreadStats.html
Normal file
3
html/Monitoring/ThreadStats.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div class="move" title="Post count / File count / Page count">
|
||||||
|
<span id="post-count">...</span> / <span id="file-count">...</span> / <span id="page-count">...</span>
|
||||||
|
</div>
|
||||||
38
html/Posting/QR.html
Normal file
38
html/Posting/QR.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<div>
|
||||||
|
<input type="checkbox" id="autohide" title="Auto-hide">
|
||||||
|
<select data-name="thread" title="Create a new thread / Reply">
|
||||||
|
<option value="new">New thread</option>
|
||||||
|
</select>
|
||||||
|
<span class="move"></span>
|
||||||
|
<a href="javascript:;" class="close" title="Close">×</a>
|
||||||
|
</div>
|
||||||
|
<form>
|
||||||
|
<div class="persona">
|
||||||
|
<input type="button" id="dump-button" title="Dump list" value="+">
|
||||||
|
<input data-name="name" list="list-name" placeholder="Name" class="field" size="1">
|
||||||
|
<input data-name="email" list="list-email" placeholder="E-mail" class="field" size="1">
|
||||||
|
<input data-name="sub" list="list-sub" placeholder="Subject" class="field" size="1">
|
||||||
|
</div>
|
||||||
|
<div id="dump-list-container">
|
||||||
|
<div id="dump-list"></div>
|
||||||
|
<a id="add-post" href="javascript:;" title="Add a post">+</a>
|
||||||
|
</div>
|
||||||
|
<div class="textarea">
|
||||||
|
<textarea data-name="com" placeholder="Comment" class="field"></textarea>
|
||||||
|
<span id="char-count"></span>
|
||||||
|
</div>
|
||||||
|
<div id="file-n-submit">
|
||||||
|
<input type="submit">
|
||||||
|
<input type="button" id="qr-file-button" value="Choose files">
|
||||||
|
<span id="qr-filename-container">
|
||||||
|
<span id="qr-no-file">No selected file</span>
|
||||||
|
<span id="qr-filename"></span>
|
||||||
|
</span>
|
||||||
|
<a id="qr-filerm" href="javascript:;" title="Remove file">×</a>
|
||||||
|
<input type="checkbox" id="qr-file-spoiler" title="Spoiler image">
|
||||||
|
</div>
|
||||||
|
<input type="file" multiple hidden>
|
||||||
|
</form>
|
||||||
|
<datalist id="list-name"></datalist>
|
||||||
|
<datalist id="list-email"></datalist>
|
||||||
|
<datalist id="list-sub"></datalist>
|
||||||
12
package.json
12
package.json
@ -20,15 +20,15 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"grunt": "~0.4.1",
|
"grunt": "~0.4.1",
|
||||||
"grunt-bump": "~0.0.2",
|
"grunt-bump": "~0.0.11",
|
||||||
"grunt-concurrent": "~0.2.0",
|
"grunt-concurrent": "~0.3.0",
|
||||||
"grunt-contrib-clean": "~0.4.1",
|
"grunt-contrib-clean": "~0.5.0",
|
||||||
"grunt-contrib-coffee": "~0.7.0",
|
"grunt-contrib-coffee": "~0.7.0",
|
||||||
"grunt-contrib-compress": "~0.5.1",
|
"grunt-contrib-compress": "~0.5.2",
|
||||||
"grunt-contrib-concat": "~0.3.0",
|
"grunt-contrib-concat": "~0.3.0",
|
||||||
"grunt-contrib-copy": "~0.4.1",
|
"grunt-contrib-copy": "~0.4.1",
|
||||||
"grunt-contrib-watch": "~0.4.4",
|
"grunt-contrib-watch": "~0.5.0",
|
||||||
"grunt-shell": "~0.2.2"
|
"grunt-shell": "~0.3.1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@ -21,7 +21,6 @@ Redirect =
|
|||||||
Redirect.post[boardID] = archive
|
Redirect.post[boardID] = archive
|
||||||
unless boardID of Redirect.file or !archive.files.contains boardID
|
unless boardID of Redirect.file or !archive.files.contains boardID
|
||||||
Redirect.file[boardID] = archive
|
Redirect.file[boardID] = archive
|
||||||
return
|
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
'Foolz':
|
'Foolz':
|
||||||
@ -29,7 +28,7 @@ Redirect =
|
|||||||
'http': false
|
'http': false
|
||||||
'https': true
|
'https': true
|
||||||
'software': 'foolfuuka'
|
'software': 'foolfuuka'
|
||||||
'boards': ['a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'v', 'vg', 'vp', 'vr', 'wsg']
|
'boards': ['a', 'co', 'gd', 'jp', 'm', 'q', 'sp', 'tg', 'tv', 'vg', 'vp', 'vr', 'wsg']
|
||||||
'files': ['a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg']
|
'files': ['a', 'gd', 'jp', 'm', 'q', 'tg', 'vg', 'vp', 'vr', 'wsg']
|
||||||
|
|
||||||
'NSFW Foolz':
|
'NSFW Foolz':
|
||||||
@ -63,25 +62,17 @@ Redirect =
|
|||||||
'boards': ['c', 'w', 'wg']
|
'boards': ['c', 'w', 'wg']
|
||||||
'files': ['c', 'w', 'wg']
|
'files': ['c', 'w', 'wg']
|
||||||
|
|
||||||
'Love is Over':
|
|
||||||
'domain': 'loveisover.me'
|
|
||||||
'http': true
|
|
||||||
'https': true
|
|
||||||
'software': 'foolfuuka'
|
|
||||||
'boards': ['d', 'h', 'v']
|
|
||||||
'files': ['d', 'h', 'v']
|
|
||||||
|
|
||||||
'Foolz a Shit':
|
'Foolz a Shit':
|
||||||
'domain': 'archive.foolzashit.com'
|
'domain': 'archive.foolzashit.com'
|
||||||
'http': true
|
'http': true
|
||||||
'https': true
|
'https': true
|
||||||
'software': 'foolfuuka'
|
'software': 'foolfuuka'
|
||||||
'boards': ['adv', 'asp', 'cm', 'e', 'i', 'lgbt', 'n', 'o', 'p', 'pol', 's', 's4s', 't', 'trv', 'y']
|
'boards': ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv']
|
||||||
'files': ['adv', 'asp', 'cm', 'e', 'i', 'lgbt', 'n', 'o', 'p', 's', 's4s', 't', 'trv', 'y']
|
'files': ['adv', 'asp', 'cm', 'i', 'lgbt', 'n', 'o', 'p', 's4s', 't', 'trv']
|
||||||
|
|
||||||
'Install Gentoo':
|
'Install Gentoo':
|
||||||
'domain': 'archive.installgentoo.net'
|
'domain': 'archive.installgentoo.net'
|
||||||
'http': true
|
'http': false
|
||||||
'https': true
|
'https': true
|
||||||
'software': 'fuuka'
|
'software': 'fuuka'
|
||||||
'boards': ['diy', 'g', 'sci']
|
'boards': ['diy', 'g', 'sci']
|
||||||
@ -109,6 +100,14 @@ Redirect =
|
|||||||
'software': 'fuuka'
|
'software': 'fuuka'
|
||||||
'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr']
|
'boards': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'tg', 'vr']
|
||||||
'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'vr']
|
'files': ['3', 'cgl', 'ck', 'fa', 'ic', 'jp', 'lit', 'q', 'vr']
|
||||||
|
|
||||||
|
'worldathleticproject':
|
||||||
|
'domain': 'fuuka.worldathleticproject.org'
|
||||||
|
'http': true
|
||||||
|
'https': true
|
||||||
|
'software': 'foolfuuka'
|
||||||
|
'boards': ['e', 'h', 'p', 's', 'u']
|
||||||
|
'files': ['e', 'h', 'p', 's', 'u']
|
||||||
|
|
||||||
to: (dest, data) ->
|
to: (dest, data) ->
|
||||||
archive = (if dest is 'search' then Redirect.thread else Redirect[dest])[data.boardID]
|
archive = (if dest is 'search' then Redirect.thread else Redirect[dest])[data.boardID]
|
||||||
|
|||||||
@ -209,7 +209,7 @@ Filter =
|
|||||||
el = $.el 'a',
|
el = $.el 'a',
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
textContent: text
|
textContent: text
|
||||||
el.setAttribute 'data-type', type
|
el.dataset.type = type
|
||||||
$.on el, 'click', Filter.menu.makeFilter
|
$.on el, 'click', Filter.menu.makeFilter
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -113,7 +113,7 @@ ThreadHiding =
|
|||||||
className: "#{type}-thread-button"
|
className: "#{type}-thread-button"
|
||||||
innerHTML: "<span class=brackets-wrap> #{if type is 'hide' then '-' else '+'} </span>"
|
innerHTML: "<span class=brackets-wrap> #{if type is 'hide' then '-' else '+'} </span>"
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
a.setAttribute 'data-fullid', thread.fullID
|
a.dataset.fullID = thread.fullID
|
||||||
$.on a, 'click', ThreadHiding.toggle
|
$.on a, 'click', ThreadHiding.toggle
|
||||||
a
|
a
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ ThreadHiding =
|
|||||||
|
|
||||||
toggle: (thread) ->
|
toggle: (thread) ->
|
||||||
unless thread instanceof Thread
|
unless thread instanceof Thread
|
||||||
thread = g.threads[@dataset.fullid]
|
thread = g.threads[@dataset.fullID]
|
||||||
if thread.isHidden
|
if thread.isHidden
|
||||||
ThreadHiding.show thread
|
ThreadHiding.show thread
|
||||||
else
|
else
|
||||||
|
|||||||
@ -108,12 +108,12 @@ Build =
|
|||||||
capcodeStart = ''
|
capcodeStart = ''
|
||||||
capcode = ''
|
capcode = ''
|
||||||
|
|
||||||
flag =
|
flag = unless flagCode
|
||||||
if flagCode
|
''
|
||||||
" <img src='#{staticPath}country/#{if boardID is 'pol' then 'troll/' else ''}" +
|
else if boardID is 'pol'
|
||||||
flagCode.toLowerCase() + ".gif' alt=#{flagCode} title='#{flagName}' class=countryFlag>"
|
" <img src='#{staticPath}country/troll/#{flagCode.toLowerCase()}.gif' alt=#{flagCode} title='#{flagName}' class=countryFlag>"
|
||||||
else
|
else
|
||||||
''
|
" <span title='#{flagName}' class='flag flag-#{flagCode.toLowerCase()}'></span>"
|
||||||
|
|
||||||
if file?.isDeleted
|
if file?.isDeleted
|
||||||
fileHTML = if isOP
|
fileHTML = if isOP
|
||||||
|
|||||||
@ -19,7 +19,7 @@ Get =
|
|||||||
if index then post.clones[index] else post
|
if index then post.clones[index] else post
|
||||||
postFromNode: (root) ->
|
postFromNode: (root) ->
|
||||||
Get.postFromRoot $.x 'ancestor::div[contains(@class,"postContainer")][1]', root
|
Get.postFromRoot $.x 'ancestor::div[contains(@class,"postContainer")][1]', root
|
||||||
contextFromLink: (quotelink) ->
|
contextFromNode: (quotelink) ->
|
||||||
Get.postFromRoot $.x 'ancestor::div[parent::div[@class="thread"]][1]', quotelink
|
Get.postFromRoot $.x 'ancestor::div[parent::div[@class="thread"]][1]', quotelink
|
||||||
postDataFromLink: (link) ->
|
postDataFromLink: (link) ->
|
||||||
if link.hostname is 'boards.4chan.org'
|
if link.hostname is 'boards.4chan.org'
|
||||||
@ -28,9 +28,8 @@ Get =
|
|||||||
threadID = path[3]
|
threadID = path[3]
|
||||||
postID = link.hash[2..]
|
postID = link.hash[2..]
|
||||||
else # resurrected quote
|
else # resurrected quote
|
||||||
boardID = link.dataset.boardid
|
{boardID, threadID, postID} = link.dataset
|
||||||
threadID = link.dataset.threadid or 0
|
threadID or= 0
|
||||||
postID = link.dataset.postid
|
|
||||||
return {
|
return {
|
||||||
boardID: boardID
|
boardID: boardID
|
||||||
threadID: +threadID
|
threadID: +threadID
|
||||||
@ -185,7 +184,7 @@ Get =
|
|||||||
# quotes
|
# quotes
|
||||||
.replace /((>){2}(>\/[a-z\d]+\/)?\d+)/g, '<span class=deadlink>$1</span>'
|
.replace /((>){2}(>\/[a-z\d]+\/)?\d+)/g, '<span class=deadlink>$1</span>'
|
||||||
|
|
||||||
threadID = data.thread_num
|
threadID = +data.thread_num
|
||||||
o =
|
o =
|
||||||
# id
|
# id
|
||||||
postID: "#{postID}"
|
postID: "#{postID}"
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
editTheme = {} # Currently editted theme.
|
|
||||||
editMascot = {} # Which mascot we're editting.
|
editTheme = {}
|
||||||
userNavigation = {} # ...
|
editMascot = {}
|
||||||
|
userNavigation = {}
|
||||||
Conf = {}
|
Conf = {}
|
||||||
c = console
|
c = console
|
||||||
d = document
|
d = document
|
||||||
@ -3074,4 +3075,4 @@ textarea,
|
|||||||
border: 1px solid #111 !important;
|
border: 1px solid #111 !important;
|
||||||
background-color: #933;
|
background-color: #933;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -55,7 +55,7 @@ Header =
|
|||||||
return unless Main.isThisPageLegit()
|
return unless Main.isThisPageLegit()
|
||||||
# Wait for #boardNavMobile instead of #boardNavDesktop,
|
# Wait for #boardNavMobile instead of #boardNavDesktop,
|
||||||
# it might be incomplete otherwise.
|
# it might be incomplete otherwise.
|
||||||
$.asap (-> $.id('boardNavMobile') or d.readyState in ['interactive', 'complete']), @setBoardList
|
$.asap (-> $.id('boardNavMobile') or d.readyState isnt 'loading'), Header.setBoardList
|
||||||
$.prepend d.body, @bar
|
$.prepend d.body, @bar
|
||||||
$.add d.body, Header.hover
|
$.add d.body, Header.hover
|
||||||
@setBarPosition Conf['Bottom Header']
|
@setBarPosition Conf['Bottom Header']
|
||||||
@ -106,7 +106,7 @@ Header =
|
|||||||
list = $ '#custom-board-list', Header.bar
|
list = $ '#custom-board-list', Header.bar
|
||||||
$.rmAll list
|
$.rmAll list
|
||||||
return unless text
|
return unless text
|
||||||
as = $$('#full-board-list a', Header.bar)
|
as = $$ '#full-board-list a[title]', Header.bar
|
||||||
nodes = text.match(/[\w@]+((-(all|title|replace|full|index|catalog|url:"[^"]+[^"]"|text:"[^"]+")|\,"[^"]+[^"]"))*|[^\w@]+/g).map (t) ->
|
nodes = text.match(/[\w@]+((-(all|title|replace|full|index|catalog|url:"[^"]+[^"]"|text:"[^"]+")|\,"[^"]+[^"]"))*|[^\w@]+/g).map (t) ->
|
||||||
if /^[^\w@]/.test t
|
if /^[^\w@]/.test t
|
||||||
return $.tn t
|
return $.tn t
|
||||||
@ -141,7 +141,7 @@ Header =
|
|||||||
a.textContent
|
a.textContent
|
||||||
|
|
||||||
if m = t.match /-(index|catalog)/
|
if m = t.match /-(index|catalog)/
|
||||||
a.setAttribute 'data-only', m[1]
|
a.dataset.only = m[1]
|
||||||
a.href = "//boards.4chan.org/#{board}/"
|
a.href = "//boards.4chan.org/#{board}/"
|
||||||
if m[1] is 'catalog'
|
if m[1] is 'catalog'
|
||||||
a.href += 'catalog'
|
a.href += 'catalog'
|
||||||
|
|||||||
@ -191,20 +191,18 @@ Main =
|
|||||||
threads = []
|
threads = []
|
||||||
posts = []
|
posts = []
|
||||||
|
|
||||||
for boardChild in board.children
|
for threadRoot in $$ '.board > .thread', board
|
||||||
continue unless $.hasClass boardChild, 'thread'
|
thread = new Thread +threadRoot.id[1..], g.BOARD
|
||||||
thread = new Thread boardChild.id[1..], g.BOARD
|
|
||||||
threads.push thread
|
threads.push thread
|
||||||
for threadChild in boardChild.children
|
for postRoot in $$ '.thread > .postContainer', threadRoot
|
||||||
continue unless $.hasClass threadChild, 'postContainer'
|
|
||||||
try
|
try
|
||||||
posts.push new Post threadChild, thread, g.BOARD
|
posts.push new Post postRoot, thread, g.BOARD
|
||||||
catch err
|
catch err
|
||||||
# Skip posts that we failed to parse.
|
# Skip posts that we failed to parse.
|
||||||
unless errors
|
unless errors
|
||||||
errors = []
|
errors = []
|
||||||
errors.push
|
errors.push
|
||||||
message: "Parsing of Post No.#{threadChild.id.match(/\d+/)} failed. Post will be skipped."
|
message: "Parsing of Post No.#{postRoot.id.match(/\d+/)} failed. Post will be skipped."
|
||||||
error: err
|
error: err
|
||||||
Main.handleErrors errors if errors
|
Main.handleErrors errors if errors
|
||||||
|
|
||||||
@ -372,7 +370,7 @@ Main =
|
|||||||
unless 'thisPageIsLegit' of Main
|
unless 'thisPageIsLegit' of Main
|
||||||
Main.thisPageIsLegit = location.hostname is 'boards.4chan.org' and
|
Main.thisPageIsLegit = location.hostname is 'boards.4chan.org' and
|
||||||
!$('link[href*="favicon-status.ico"]', d.head) and
|
!$('link[href*="favicon-status.ico"]', d.head) and
|
||||||
d.title not in ['4chan - Temporarily Offline', '4chan - Error']
|
d.title not in ['4chan - Temporarily Offline', '4chan - Error', '504 Gateway Time-out']
|
||||||
Main.thisPageIsLegit
|
Main.thisPageIsLegit
|
||||||
|
|
||||||
Main.init()
|
Main.init()
|
||||||
|
|||||||
@ -24,7 +24,8 @@ Settings =
|
|||||||
changelog = '<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md'
|
changelog = '<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md'
|
||||||
el = $.el 'span',
|
el = $.el 'span',
|
||||||
innerHTML: "<%= meta.name %> has been updated to <a href='#{changelog}' target=_blank>version #{g.VERSION}</a>."
|
innerHTML: "<%= meta.name %> has been updated to <a href='#{changelog}' target=_blank>version #{g.VERSION}</a>."
|
||||||
new Notification 'info', el, 30
|
if Conf['Show Updated Notifications']
|
||||||
|
new Notification 'info', el, 30
|
||||||
else
|
else
|
||||||
$.on d, '4chanXInitFinished', Settings.open
|
$.on d, '4chanXInitFinished', Settings.open
|
||||||
$.set
|
$.set
|
||||||
@ -427,7 +428,6 @@ Settings =
|
|||||||
|
|
||||||
usercss: ->
|
usercss: ->
|
||||||
CustomCSS.update()
|
CustomCSS.update()
|
||||||
|
|
||||||
keybinds: (section) ->
|
keybinds: (section) ->
|
||||||
section.innerHTML = """
|
section.innerHTML = """
|
||||||
<%= grunt.file.read('src/General/html/Settings/Keybinds.html').replace(/>\s+</g, '><').trim() %>
|
<%= grunt.file.read('src/General/html/Settings/Keybinds.html').replace(/>\s+</g, '><').trim() %>
|
||||||
|
|||||||
@ -303,13 +303,13 @@ UI = do ->
|
|||||||
|
|
||||||
hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb, close}) ->
|
hoverstart = ({root, el, latestEvent, endEvents, asapTest, cb, close}) ->
|
||||||
o = {
|
o = {
|
||||||
root: root
|
root
|
||||||
el: el
|
el
|
||||||
style: el.style
|
style: el.style
|
||||||
cb: cb
|
cb
|
||||||
close: close
|
close: close
|
||||||
endEvents: endEvents
|
endEvents
|
||||||
latestEvent: latestEvent
|
latestEvent
|
||||||
clientHeight: doc.clientHeight
|
clientHeight: doc.clientHeight
|
||||||
clientWidth: doc.clientWidth
|
clientWidth: doc.clientWidth
|
||||||
}
|
}
|
||||||
@ -325,6 +325,11 @@ UI = do ->
|
|||||||
if $.x 'ancestor::div[contains(@class,"inline")][1]', root
|
if $.x 'ancestor::div[contains(@class,"inline")][1]', root
|
||||||
$.on d, 'keydown', o.hoverend
|
$.on d, 'keydown', o.hoverend
|
||||||
$.on root, 'mousemove', o.hover
|
$.on root, 'mousemove', o.hover
|
||||||
|
<% if (type === 'userscript') { %>
|
||||||
|
# Workaround for https://github.com/MayhemYDG/4chan-x/issues/377
|
||||||
|
o.workaround = (e) -> o.hoverend() unless root.contains e.target
|
||||||
|
$.on doc, 'mousemove', o.workaround
|
||||||
|
<% } %>
|
||||||
|
|
||||||
hover = (e) ->
|
hover = (e) ->
|
||||||
@latestEvent = e
|
@latestEvent = e
|
||||||
@ -355,6 +360,10 @@ UI = do ->
|
|||||||
$.off @root, @endEvents, @hoverend
|
$.off @root, @endEvents, @hoverend
|
||||||
$.off d, 'keydown', @hoverend
|
$.off d, 'keydown', @hoverend
|
||||||
$.off @root, 'mousemove', @hover
|
$.off @root, 'mousemove', @hover
|
||||||
|
<% if (type === 'userscript') { %>
|
||||||
|
# Workaround for https://github.com/MayhemYDG/4chan-x/issues/377
|
||||||
|
$.off doc, 'mousemove', @workaround
|
||||||
|
<% } %>
|
||||||
@cb.call @ if @cb
|
@cb.call @ if @cb
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1057
src/General/css/style.css
Normal file
1057
src/General/css/style.css
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
<div class=warning #{if Conf['Filter'] then 'hidden' else ''}><code>Filter</code> is disabled.</div>
|
<div class=warning #{if Conf['Filter'] then 'hidden' else ''}><code>Filter</code> is disabled.</div>
|
||||||
<p>
|
<p>
|
||||||
Use <a href=https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions>regular expressions</a>, one per line.<br>
|
Use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions">regular expressions</a>, one per line.<br>
|
||||||
Lines starting with a <code>#</code> will be ignored.<br>
|
Lines starting with a <code>#</code> will be ignored.<br>
|
||||||
For example, <code>/weeaboo/i</code> will filter posts containing the string `<code>weeaboo</code>`, case-insensitive.<br>
|
For example, <code>/weeaboo/i</code> will filter posts containing the string `<code>weeaboo</code>`, case-insensitive.<br>
|
||||||
MD5 filtering uses exact string matching, not regular expressions.
|
MD5 filtering uses exact string matching, not regular expressions.
|
||||||
@ -26,4 +26,4 @@
|
|||||||
Highlighted OPs will have their threads put on top of board pages by default.<br>
|
Highlighted OPs will have their threads put on top of board pages by default.<br>
|
||||||
For example: <code>top:yes;</code> or <code>top:no;</code>.
|
For example: <code>top:yes;</code> or <code>top:no;</code>.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -49,7 +49,7 @@ $.id = (id) ->
|
|||||||
d.getElementById id
|
d.getElementById id
|
||||||
|
|
||||||
$.ready = (fc) ->
|
$.ready = (fc) ->
|
||||||
if d.readyState in ['interactive', 'complete']
|
unless d.readyState is 'loading'
|
||||||
$.queueTask fc
|
$.queueTask fc
|
||||||
return
|
return
|
||||||
cb = ->
|
cb = ->
|
||||||
@ -221,9 +221,7 @@ $.event = (event, detail, root=d) ->
|
|||||||
|
|
||||||
$.open = (URL) ->
|
$.open = (URL) ->
|
||||||
<% if (type === 'userscript') { %>
|
<% if (type === 'userscript') { %>
|
||||||
# XXX fix GM opening file://// for protocol-less URLs.
|
$.open = (URL) -> GM_openInTab URL
|
||||||
# https://github.com/greasemonkey/greasemonkey/issues/1719
|
|
||||||
GM_openInTab ($.el 'a', href: URL).href
|
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
window.open URL, '_blank'
|
window.open URL, '_blank'
|
||||||
<% } %>
|
<% } %>
|
||||||
@ -298,29 +296,21 @@ $.minmax = (value, min, max) ->
|
|||||||
value
|
value
|
||||||
)
|
)
|
||||||
|
|
||||||
$.syncing = {}
|
$.item = (key, val) ->
|
||||||
|
item = {}
|
||||||
|
item[key] = val
|
||||||
|
item
|
||||||
|
|
||||||
$.sync = do ->
|
$.syncing = {}
|
||||||
<% if (type === 'crx') { %>
|
<% if (type === 'crx') { %>
|
||||||
|
$.sync = do ->
|
||||||
chrome.storage.onChanged.addListener (changes) ->
|
chrome.storage.onChanged.addListener (changes) ->
|
||||||
for key of changes
|
for key of changes
|
||||||
if cb = $.syncing[key]
|
if cb = $.syncing[key]
|
||||||
cb changes[key].newValue
|
cb changes[key].newValue
|
||||||
return
|
return
|
||||||
(key, cb) -> $.syncing[key] = cb
|
(key, cb) -> $.syncing[key] = cb
|
||||||
<% } else { %>
|
|
||||||
window.addEventListener 'storage', (e) ->
|
|
||||||
if cb = $.syncing[e.key]
|
|
||||||
cb JSON.parse e.newValue
|
|
||||||
, false
|
|
||||||
(key, cb) -> $.syncing[g.NAMESPACE + key] = cb
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
$.item = (key, val) ->
|
|
||||||
item = {}
|
|
||||||
item[key] = val
|
|
||||||
item
|
|
||||||
<% if (type === 'crx') { %>
|
|
||||||
$.localKeys = [
|
$.localKeys = [
|
||||||
# filters
|
# filters
|
||||||
'name',
|
'name',
|
||||||
@ -372,6 +362,7 @@ $.get = (key, val, cb) ->
|
|||||||
if syncItems
|
if syncItems
|
||||||
count++
|
count++
|
||||||
chrome.storage.sync.get syncItems, done
|
chrome.storage.sync.get syncItems, done
|
||||||
|
|
||||||
$.set = do ->
|
$.set = do ->
|
||||||
items = {}
|
items = {}
|
||||||
localItems = {}
|
localItems = {}
|
||||||
@ -397,8 +388,13 @@ $.set = do ->
|
|||||||
set()
|
set()
|
||||||
|
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
|
|
||||||
# http://wiki.greasespot.net/Main_Page
|
# http://wiki.greasespot.net/Main_Page
|
||||||
|
$.sync = do ->
|
||||||
|
$.on window, 'storage', (e) ->
|
||||||
|
if cb = $.syncing[e.key]
|
||||||
|
cb JSON.parse e.newValue
|
||||||
|
(key, cb) -> $.syncing[g.NAMESPACE + key] = cb
|
||||||
|
|
||||||
$.delete = (keys) ->
|
$.delete = (keys) ->
|
||||||
unless keys instanceof Array
|
unless keys instanceof Array
|
||||||
keys = [keys]
|
keys = [keys]
|
||||||
|
|||||||
@ -59,5 +59,4 @@ class Clone extends Post
|
|||||||
|
|
||||||
@isDead = true if origin.isDead
|
@isDead = true if origin.isDead
|
||||||
@isClone = true
|
@isClone = true
|
||||||
index = origin.clones.push(@) - 1
|
root.dataset.clone = origin.clones.push(@) - 1
|
||||||
root.setAttribute 'data-clone', index
|
|
||||||
|
|||||||
@ -16,29 +16,34 @@ class Post
|
|||||||
quotelinks: []
|
quotelinks: []
|
||||||
backlinks: info.getElementsByClassName 'backlink'
|
backlinks: info.getElementsByClassName 'backlink'
|
||||||
|
|
||||||
|
unless @isReply = $.hasClass post, 'reply'
|
||||||
|
@thread.OP = @
|
||||||
|
@thread.isSticky = !!$ '.stickyIcon', info
|
||||||
|
@thread.isClosed = !!$ '.closedIcon', info
|
||||||
|
|
||||||
@info = {}
|
@info = {}
|
||||||
if subject = $ '.subject', info
|
if subject = $ '.subject', info
|
||||||
@nodes.subject = subject
|
@nodes.subject = subject
|
||||||
@info.subject = subject.textContent
|
@info.subject = subject.textContent
|
||||||
if name = $ '.name', info
|
if name = $ '.name', info
|
||||||
@nodes.name = name
|
@nodes.name = name
|
||||||
@info.name = name.textContent
|
@info.name = name.textContent
|
||||||
if email = $ '.useremail', info
|
if email = $ '.useremail', info
|
||||||
@nodes.email = email
|
@nodes.email = email
|
||||||
@info.email = decodeURIComponent email.href[7..]
|
@info.email = decodeURIComponent email.href[7..]
|
||||||
if tripcode = $ '.postertrip', info
|
if tripcode = $ '.postertrip', info
|
||||||
@nodes.tripcode = tripcode
|
@nodes.tripcode = tripcode
|
||||||
@info.tripcode = tripcode.textContent
|
@info.tripcode = tripcode.textContent
|
||||||
if uniqueID = $ '.posteruid', info
|
if uniqueID = $ '.posteruid', info
|
||||||
@nodes.uniqueID = uniqueID
|
@nodes.uniqueID = uniqueID
|
||||||
@info.uniqueID = uniqueID.firstElementChild.textContent
|
@info.uniqueID = uniqueID.firstElementChild.textContent
|
||||||
if capcode = $ '.capcode.hand', info
|
if capcode = $ '.capcode.hand', info
|
||||||
@nodes.capcode = capcode
|
@nodes.capcode = capcode
|
||||||
@info.capcode = capcode.textContent.replace '## ', ''
|
@info.capcode = capcode.textContent.replace '## ', ''
|
||||||
if flag = $ '.countryFlag', info
|
if flag = $ '.flag, .countryFlag', info
|
||||||
@nodes.flag = flag
|
@nodes.flag = flag
|
||||||
@info.flag = flag.title
|
@info.flag = flag.title
|
||||||
if date = $ '.dateTime', info
|
if date = $ '.dateTime', info
|
||||||
@nodes.date = date
|
@nodes.date = date
|
||||||
@info.date = new Date date.dataset.utc * 1000
|
@info.date = new Date date.dataset.utc * 1000
|
||||||
@info.yours = QR.db.get
|
@info.yours = QR.db.get
|
||||||
@ -48,43 +53,7 @@ class Post
|
|||||||
|
|
||||||
@parseComment()
|
@parseComment()
|
||||||
@parseQuotes()
|
@parseQuotes()
|
||||||
|
@parseFile(that)
|
||||||
if (file = $ '.file', post) and thumb = $ 'img[data-md5]', file
|
|
||||||
# Supports JPG/PNG/GIF/PDF.
|
|
||||||
# Flash files are not supported.
|
|
||||||
alt = thumb.alt
|
|
||||||
anchor = thumb.parentNode
|
|
||||||
fileInfo = file.firstElementChild
|
|
||||||
@file =
|
|
||||||
info: fileInfo
|
|
||||||
text: fileInfo.firstElementChild
|
|
||||||
thumb: thumb
|
|
||||||
URL: anchor.href
|
|
||||||
size: alt.match(/[\d.]+\s\w+/)[0]
|
|
||||||
MD5: thumb.dataset.md5
|
|
||||||
isSpoiler: $.hasClass anchor, 'imgspoiler'
|
|
||||||
size = +@file.size.match(/[\d.]+/)[0]
|
|
||||||
unit = ['B', 'KB', 'MB', 'GB'].indexOf @file.size.match(/\w+$/)[0]
|
|
||||||
size *= 1024 while unit-- > 0
|
|
||||||
@file.sizeInBytes = size
|
|
||||||
@file.thumbURL =
|
|
||||||
if that.isArchived
|
|
||||||
thumb.src
|
|
||||||
else
|
|
||||||
"#{location.protocol}//thumbs.4chan.org/#{board}/thumb/#{@file.URL.match(/(\d+)\./)[1]}s.jpg"
|
|
||||||
# replace %22 with quotes, see:
|
|
||||||
# crbug.com/81193
|
|
||||||
# webk.it/62107
|
|
||||||
# https://www.w3.org/Bugs/Public/show_bug.cgi?id=16909
|
|
||||||
# http://www.whatwg.org/specs/web-apps/current-work/#multipart-form-data
|
|
||||||
@file.name = $('span[title]', fileInfo).title.replace /%22/g, '"'
|
|
||||||
if @file.isImage = /(jpg|png|gif)$/i.test @file.name
|
|
||||||
@file.dimensions = @file.text.textContent.match(/\d+x\d+/)[0]
|
|
||||||
|
|
||||||
unless @isReply = $.hasClass post, 'reply'
|
|
||||||
@thread.OP = @
|
|
||||||
@thread.isSticky = !!$ '.stickyIcon', @nodes.info
|
|
||||||
@thread.isClosed = !!$ '.closedIcon', @nodes.info
|
|
||||||
|
|
||||||
@clones = []
|
@clones = []
|
||||||
g.posts[@fullID] = thread.posts[@] = board.posts[@] = @
|
g.posts[@fullID] = thread.posts[@] = board.posts[@] = @
|
||||||
@ -108,18 +77,18 @@ class Post
|
|||||||
nodes = d.evaluate './/br|.//text()', bq, null, 7, null
|
nodes = d.evaluate './/br|.//text()', bq, null, 7, null
|
||||||
i = 0
|
i = 0
|
||||||
while i < nodes.snapshotLength
|
while i < nodes.snapshotLength
|
||||||
text.push if data = nodes.snapshotItem(i++).data then data else '\n'
|
text.push nodes.snapshotItem(i++).data or '\n'
|
||||||
@info.comment = text.join('').trim().replace /\s+$/gm, ''
|
@info.comment = text.join('').trim().replace /\s+$/gm, ''
|
||||||
|
|
||||||
parseQuotes: ->
|
parseQuotes: ->
|
||||||
quotes = {}
|
quotes = {}
|
||||||
for quotelink in $$ '.quotelink', @nodes.comment
|
for quotelink in $$ '.quotelink', @nodes.comment
|
||||||
# Don't add board links. (>>>/b/)
|
# Don't add board links. (>>>/b/)
|
||||||
hash = quotelink.hash
|
{hash} = quotelink
|
||||||
continue unless hash
|
continue unless hash
|
||||||
|
|
||||||
# Don't add catalog links. (>>>/b/catalog or >>>/b/search)
|
# Don't add catalog links. (>>>/b/catalog or >>>/b/search)
|
||||||
pathname = quotelink.pathname
|
{pathname} = quotelink
|
||||||
continue if /catalog$/.test pathname
|
continue if /catalog$/.test pathname
|
||||||
|
|
||||||
# Don't add rules links. (>>>/a/rules)
|
# Don't add rules links. (>>>/a/rules)
|
||||||
@ -128,14 +97,49 @@ class Post
|
|||||||
|
|
||||||
@nodes.quotelinks.push quotelink
|
@nodes.quotelinks.push quotelink
|
||||||
|
|
||||||
# Don't count capcode replies as quotes. (Admin/Mod/Dev Replies: ...)
|
# Don't count capcode replies as quotes in OPs. (Admin/Mod/Dev Replies: ...)
|
||||||
continue if quotelink.parentNode.parentNode.className is 'capcodeReplies'
|
continue if !@isReply and $.hasClass quotelink.parentNode.parentNode, 'capcodeReplies'
|
||||||
|
|
||||||
# Basically, only add quotes that link to posts on an imageboard.
|
# Basically, only add quotes that link to posts on an imageboard.
|
||||||
quotes["#{pathname.split('/')[1]}.#{hash[2..]}"] = true
|
quotes["#{pathname.split('/')[1]}.#{hash[2..]}"] = true
|
||||||
return if @isClone
|
return if @isClone
|
||||||
@quotes = Object.keys quotes
|
@quotes = Object.keys quotes
|
||||||
|
|
||||||
|
parseFile: (that) ->
|
||||||
|
return unless (fileEl = $ '.file', @nodes.post) and thumb = $ 'img[data-md5]', fileEl
|
||||||
|
# Supports JPG/PNG/GIF/PDF.
|
||||||
|
# Flash files are not supported.
|
||||||
|
alt = thumb.alt
|
||||||
|
anchor = thumb.parentNode
|
||||||
|
fileInfo = fileEl.firstElementChild
|
||||||
|
@file =
|
||||||
|
info: fileInfo
|
||||||
|
text: fileInfo.firstElementChild
|
||||||
|
thumb: thumb
|
||||||
|
URL: anchor.href
|
||||||
|
size: alt.match(/[\d.]+\s\w+/)[0]
|
||||||
|
MD5: thumb.dataset.md5
|
||||||
|
isSpoiler: $.hasClass anchor, 'imgspoiler'
|
||||||
|
size = +@file.size.match(/[\d.]+/)[0]
|
||||||
|
unit = ['B', 'KB', 'MB', 'GB'].indexOf @file.size.match(/\w+$/)[0]
|
||||||
|
size *= 1024 while unit-- > 0
|
||||||
|
@file.sizeInBytes = size
|
||||||
|
@file.thumbURL = if that.isArchived
|
||||||
|
thumb.src
|
||||||
|
else
|
||||||
|
"#{location.protocol}//thumbs.4chan.org/#{@board}/thumb/#{@file.URL.match(/(\d+)\./)[1]}s.jpg"
|
||||||
|
@file.name = $('span[title]', fileInfo).title
|
||||||
|
<% if (type === 'crx') { %>
|
||||||
|
# replace %22 with quotes, see:
|
||||||
|
# crbug.com/81193
|
||||||
|
# webk.it/62107
|
||||||
|
# https://www.w3.org/Bugs/Public/show_bug.cgi?id=16909
|
||||||
|
# http://www.whatwg.org/specs/web-apps/current-work/#multipart-form-data
|
||||||
|
@file.name = @file.name.replace /%22/g, '"'
|
||||||
|
<% } %>
|
||||||
|
if @file.isImage = /(jpg|png|gif)$/i.test @file.name
|
||||||
|
@file.dimensions = @file.text.textContent.match(/\d+x\d+/)[0]
|
||||||
|
|
||||||
kill: (file, now) ->
|
kill: (file, now) ->
|
||||||
now or= new Date()
|
now or= new Date()
|
||||||
if file
|
if file
|
||||||
@ -190,10 +194,12 @@ class Post
|
|||||||
quotelink.textContent = quotelink.textContent.replace '\u00A0(Dead)', ''
|
quotelink.textContent = quotelink.textContent.replace '\u00A0(Dead)', ''
|
||||||
$.rmClass quotelink, 'deadlink'
|
$.rmClass quotelink, 'deadlink'
|
||||||
return
|
return
|
||||||
|
|
||||||
addClone: (context) ->
|
addClone: (context) ->
|
||||||
new Clone @, context
|
new Clone @, context
|
||||||
|
|
||||||
rmClone: (index) ->
|
rmClone: (index) ->
|
||||||
@clones.splice index, 1
|
@clones.splice index, 1
|
||||||
for clone in @clones[index..]
|
for clone in @clones[index..]
|
||||||
clone.nodes.root.setAttribute 'data-clone', index++
|
clone.nodes.root.dataset.clone = index++
|
||||||
return
|
return
|
||||||
|
|||||||
@ -2,8 +2,7 @@ class Thread
|
|||||||
callbacks: []
|
callbacks: []
|
||||||
toString: -> @ID
|
toString: -> @ID
|
||||||
|
|
||||||
constructor: (ID, @board) ->
|
constructor: (@ID, @board) ->
|
||||||
@ID = +ID
|
|
||||||
@fullID = "#{@board}.#{@ID}"
|
@fullID = "#{@board}.#{@ID}"
|
||||||
@posts = {}
|
@posts = {}
|
||||||
|
|
||||||
@ -11,4 +10,4 @@ class Thread
|
|||||||
|
|
||||||
kill: ->
|
kill: ->
|
||||||
@isDead = true
|
@isDead = true
|
||||||
@timeOfDeath = Date.now()
|
@timeOfDeath = Date.now()
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
}],
|
}],
|
||||||
"homepage_url": "<%= meta.page %>",
|
"homepage_url": "<%= meta.page %>",
|
||||||
"minimum_chrome_version": "24",
|
"minimum_chrome_version": "24",
|
||||||
|
"minimum_opera_version": "15",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage"
|
"storage"
|
||||||
]
|
]
|
||||||
|
|||||||
@ -13,7 +13,7 @@ ImageHover =
|
|||||||
el = $.el 'img',
|
el = $.el 'img',
|
||||||
id: 'ihover'
|
id: 'ihover'
|
||||||
src: post.file.URL
|
src: post.file.URL
|
||||||
el.setAttribute 'data-fullid', post.fullID
|
el.dataset.fullID = post.fullID
|
||||||
$.add Header.hover, el
|
$.add Header.hover, el
|
||||||
UI.hover
|
UI.hover
|
||||||
root: @
|
root: @
|
||||||
@ -24,7 +24,7 @@ ImageHover =
|
|||||||
$.on el, 'error', ImageHover.error
|
$.on el, 'error', ImageHover.error
|
||||||
error: ->
|
error: ->
|
||||||
return unless doc.contains @
|
return unless doc.contains @
|
||||||
post = g.posts[@dataset.fullid]
|
post = g.posts[@dataset.fullID]
|
||||||
|
|
||||||
src = @src.split '/'
|
src = @src.split '/'
|
||||||
if src[2] is 'images.4chan.org'
|
if src[2] is 'images.4chan.org'
|
||||||
@ -48,4 +48,4 @@ ImageHover =
|
|||||||
post.kill()
|
post.kill()
|
||||||
else if postObj.filedeleted
|
else if postObj.filedeleted
|
||||||
clearTimeout timeoutID
|
clearTimeout timeoutID
|
||||||
post.kill true
|
post.kill true
|
||||||
|
|||||||
@ -4,12 +4,10 @@ Sauce =
|
|||||||
|
|
||||||
links = []
|
links = []
|
||||||
for link in Conf['sauces'].split '\n'
|
for link in Conf['sauces'].split '\n'
|
||||||
continue if link[0] is '#'
|
|
||||||
try
|
try
|
||||||
links.push @createSauceLink link.trim()
|
links.push @createSauceLink link.trim() if link[0] isnt '#'
|
||||||
catch err
|
catch err
|
||||||
# Don't add random text plz.
|
# Don't add random text plz.
|
||||||
continue
|
|
||||||
return unless links.length
|
return unless links.length
|
||||||
@links = links
|
@links = links
|
||||||
@link = $.el 'a', target: '_blank'
|
@link = $.el 'a', target: '_blank'
|
||||||
@ -44,9 +44,8 @@ DeleteLink =
|
|||||||
return if DeleteLink.cooldown.counting is post
|
return if DeleteLink.cooldown.counting is post
|
||||||
|
|
||||||
$.off @, 'click', DeleteLink.delete
|
$.off @, 'click', DeleteLink.delete
|
||||||
@textContent = "Deleting #{@textContent}..."
|
|
||||||
|
|
||||||
fileOnly = $.hasClass @, 'delete-file'
|
fileOnly = $.hasClass @, 'delete-file'
|
||||||
|
@textContent = "Deleting #{if fileOnly then 'file' else 'post'}..."
|
||||||
|
|
||||||
form =
|
form =
|
||||||
mode: 'usrdel'
|
mode: 'usrdel'
|
||||||
|
|||||||
@ -8,29 +8,21 @@ Menu =
|
|||||||
cb: @node
|
cb: @node
|
||||||
|
|
||||||
node: ->
|
node: ->
|
||||||
button = Menu.makeButton @
|
|
||||||
if @isClone
|
if @isClone
|
||||||
$.replace $('.menu-button', @nodes.info), button
|
button = $ '.menu-button', @nodes.info
|
||||||
return
|
else
|
||||||
$.add @nodes.info, [$.tn('\u00A0'), button]
|
button = Menu.makeButton @
|
||||||
|
$.add @nodes.info, [$.tn('\u00A0'), button]
|
||||||
|
$.on button, 'click', Menu.toggle
|
||||||
|
|
||||||
makeButton: do ->
|
makeButton: do ->
|
||||||
a = null
|
a = null
|
||||||
(post) ->
|
->
|
||||||
a or= $.el 'a',
|
a or= $.el 'a',
|
||||||
className: 'menu-button brackets-wrap'
|
className: 'menu-button brackets-wrap'
|
||||||
innerHTML: '<span class=drop-marker></span>'
|
innerHTML: '<span class=drop-marker></span>'
|
||||||
href: 'javascript:;'
|
href: 'javascript:;'
|
||||||
clone = a.cloneNode true
|
a.cloneNode true
|
||||||
clone.setAttribute 'data-postid', post.fullID
|
|
||||||
clone.setAttribute 'data-clone', true if post.isClone
|
|
||||||
$.on clone, 'click', Menu.toggle
|
|
||||||
clone
|
|
||||||
|
|
||||||
toggle: (e) ->
|
toggle: (e) ->
|
||||||
post =
|
Menu.menu.toggle e, @, Get.postFromNode @
|
||||||
if @dataset.clone
|
|
||||||
Get.postFromNode @
|
|
||||||
else
|
|
||||||
g.posts[@dataset.postid]
|
|
||||||
Menu.menu.toggle e, @, post
|
|
||||||
|
|||||||
@ -93,10 +93,10 @@ Keybinds =
|
|||||||
window.location = "/#{g.BOARD}/catalog"
|
window.location = "/#{g.BOARD}/catalog"
|
||||||
# Thread Navigation
|
# Thread Navigation
|
||||||
when Conf['Next thread']
|
when Conf['Next thread']
|
||||||
return if g.VIEW is 'thread'
|
return if g.VIEW isnt 'index'
|
||||||
Nav.scroll +1
|
Nav.scroll +1
|
||||||
when Conf['Previous thread']
|
when Conf['Previous thread']
|
||||||
return if g.VIEW is 'thread'
|
return if g.VIEW isnt 'index'
|
||||||
Nav.scroll -1
|
Nav.scroll -1
|
||||||
when Conf['Expand thread']
|
when Conf['Expand thread']
|
||||||
ExpandThread.toggle thread
|
ExpandThread.toggle thread
|
||||||
|
|||||||
@ -58,8 +58,7 @@ Nav =
|
|||||||
# unless we're not at the beginning of the current thread
|
# unless we're not at the beginning of the current thread
|
||||||
# (and thus wanting to move to beginning)
|
# (and thus wanting to move to beginning)
|
||||||
# or we're above the first thread and don't want to skip it
|
# or we're above the first thread and don't want to skip it
|
||||||
unless (delta is -1 and Math.ceil(top) < 0) or (delta is +1 and top > 1)
|
if (delta is -1 and top > -5) or (delta is +1 and top < 5)
|
||||||
i += delta
|
top = threads[i + delta]?.getBoundingClientRect().top - topMargin
|
||||||
|
|
||||||
top = threads[i]?.getBoundingClientRect().top - topMargin
|
|
||||||
window.scrollBy 0, top
|
window.scrollBy 0, top
|
||||||
|
|||||||
@ -158,8 +158,7 @@ ThreadUpdater =
|
|||||||
By sending the `If-Modified-Since` header we get a proper status code, and no response.
|
By sending the `If-Modified-Since` header we get a proper status code, and no response.
|
||||||
This saves bandwidth for both the user and the servers and avoid unnecessary computation.
|
This saves bandwidth for both the user and the servers and avoid unnecessary computation.
|
||||||
###
|
###
|
||||||
# XXX 304 -> 0 in Opera
|
[text, klass] = if req.status is 304
|
||||||
[text, klass] = if [0, 304].contains req.status
|
|
||||||
[null, null]
|
[null, null]
|
||||||
else
|
else
|
||||||
["#{req.statusText} (#{req.status})", 'warning']
|
["#{req.statusText} (#{req.status})", 'warning']
|
||||||
|
|||||||
@ -36,13 +36,11 @@ Unread =
|
|||||||
# Let the header's onload callback handle it.
|
# Let the header's onload callback handle it.
|
||||||
return if (hash = location.hash.match /\d+/) and hash[0] of Unread.thread.posts
|
return if (hash = location.hash.match /\d+/) and hash[0] of Unread.thread.posts
|
||||||
if Unread.posts.length
|
if Unread.posts.length
|
||||||
# Scroll to before the first unread post.
|
# Scroll to a non-hidden, non-OP post that's before the first unread post.
|
||||||
prevID = 0
|
post = Unread.posts[0]
|
||||||
while root = $.x 'preceding-sibling::div[contains(@class,"postContainer")][1]', Unread.posts[0].nodes.root
|
while root = $.x 'preceding-sibling::div[contains(@class,"replyContainer")][1]', post.nodes.root
|
||||||
post = Get.postFromRoot root
|
break unless (post = Get.postFromRoot root).isHidden
|
||||||
break if prevID is post.ID
|
return unless root
|
||||||
prevID = post.ID
|
|
||||||
break unless post.isHidden
|
|
||||||
onload = -> root.scrollIntoView false if checkPosition root
|
onload = -> root.scrollIntoView false if checkPosition root
|
||||||
else
|
else
|
||||||
# Scroll to the last read post.
|
# Scroll to the last read post.
|
||||||
@ -188,9 +186,7 @@ Unread =
|
|||||||
else
|
else
|
||||||
Favicon.default
|
Favicon.default
|
||||||
|
|
||||||
<% if (type !== 'crx') { %>
|
<% if (type === 'userscript') { %>
|
||||||
# `favicon.href = href` doesn't work on Firefox.
|
# `favicon.href = href` doesn't work on Firefox.
|
||||||
# `favicon.href = href` isn't enough on Opera.
|
|
||||||
# Opera won't always update the favicon if the href didn't change.
|
|
||||||
$.add d.head, Favicon.el
|
$.add d.head, Favicon.el
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|||||||
@ -97,8 +97,8 @@ QR =
|
|||||||
$.rmClass QR.captcha.nodes.input, 'error'
|
$.rmClass QR.captcha.nodes.input, 'error'
|
||||||
if Conf['QR Shortcut']
|
if Conf['QR Shortcut']
|
||||||
$.toggleClass $('.qr-shortcut'), 'disabled'
|
$.toggleClass $('.qr-shortcut'), 'disabled'
|
||||||
for i in QR.posts
|
for post in QR.posts.splice 0, QR.posts.length, new QR.post true
|
||||||
QR.posts[0].rm()
|
post.delete()
|
||||||
QR.cooldown.auto = false
|
QR.cooldown.auto = false
|
||||||
QR.status()
|
QR.status()
|
||||||
|
|
||||||
@ -152,7 +152,8 @@ QR =
|
|||||||
|
|
||||||
status: ->
|
status: ->
|
||||||
return unless QR.nodes
|
return unless QR.nodes
|
||||||
if g.DEAD
|
{thread} = QR.posts[0]
|
||||||
|
if thread isnt 'new' and g.threads["#{g.BOARD}.#{thread}"].isDead
|
||||||
value = 404
|
value = 404
|
||||||
disabled = true
|
disabled = true
|
||||||
QR.cooldown.auto = false
|
QR.cooldown.auto = false
|
||||||
@ -373,14 +374,10 @@ QR =
|
|||||||
e?.preventDefault()
|
e?.preventDefault()
|
||||||
return unless QR.postingIsEnabled
|
return unless QR.postingIsEnabled
|
||||||
|
|
||||||
sel = d.getSelection()
|
sel = d.getSelection()
|
||||||
selectionRoot = $.x 'ancestor::div[contains(@class,"postContainer")][1]', sel.anchorNode
|
post = Get.postFromNode @
|
||||||
post = Get.postFromNode @
|
text = ">>#{post}\n"
|
||||||
{OP} = Get.contextFromLink(@).thread
|
if (s = sel.toString().trim()) and post is Get.postFromNode sel.anchorNode
|
||||||
|
|
||||||
text = ">>#{post}\n"
|
|
||||||
if (s = sel.toString().trim()) and post.nodes.root is selectionRoot
|
|
||||||
# XXX Opera doesn't retain `\n`s?
|
|
||||||
s = s.replace /\n/g, '\n>'
|
s = s.replace /\n/g, '\n>'
|
||||||
text += ">#{s}\n"
|
text += ">#{s}\n"
|
||||||
|
|
||||||
@ -391,7 +388,7 @@ QR =
|
|||||||
$.addClass QR.nodes.el, 'dump'
|
$.addClass QR.nodes.el, 'dump'
|
||||||
QR.cooldown.auto = true
|
QR.cooldown.auto = true
|
||||||
{com, thread} = QR.nodes
|
{com, thread} = QR.nodes
|
||||||
thread.value = OP.ID unless com.value
|
thread.value = Get.contextFromNode(@).thread unless com.value
|
||||||
thread.nextElementSibling.firstElementChild.textContent = thread.options[thread.selectedIndex].textContent
|
thread.nextElementSibling.firstElementChild.textContent = thread.options[thread.selectedIndex].textContent
|
||||||
|
|
||||||
caretPos = com.selectionStart
|
caretPos = com.selectionStart
|
||||||
@ -449,7 +446,7 @@ QR =
|
|||||||
QR.nodes.fileInput.click()
|
QR.nodes.fileInput.click()
|
||||||
|
|
||||||
fileInput: (files) ->
|
fileInput: (files) ->
|
||||||
if @ instanceof Element #or files instanceof Event # file input
|
if files instanceof Event # file input
|
||||||
files = [@files...]
|
files = [@files...]
|
||||||
QR.nodes.fileInput.value = null # Don't hold the files from being modified on windows
|
QR.nodes.fileInput.value = null # Don't hold the files from being modified on windows
|
||||||
{length} = files
|
{length} = files
|
||||||
@ -506,7 +503,7 @@ QR =
|
|||||||
for elm in $$ '*', el
|
for elm in $$ '*', el
|
||||||
$.on elm, 'blur', QR.focusout
|
$.on elm, 'blur', QR.focusout
|
||||||
$.on elm, 'focus', QR.focusin
|
$.on elm, 'focus', QR.focusin
|
||||||
<% } %>
|
<% } %>
|
||||||
$.on el, 'click', @select.bind @
|
$.on el, 'click', @select.bind @
|
||||||
$.on @nodes.rm, 'click', (e) => e.stopPropagation(); @rm()
|
$.on @nodes.rm, 'click', (e) => e.stopPropagation(); @rm()
|
||||||
$.on @nodes.label, 'click', (e) => e.stopPropagation()
|
$.on @nodes.label, 'click', (e) => e.stopPropagation()
|
||||||
@ -555,7 +552,7 @@ QR =
|
|||||||
@unlock()
|
@unlock()
|
||||||
|
|
||||||
rm: ->
|
rm: ->
|
||||||
$.rm @nodes.el
|
@delete()
|
||||||
index = QR.posts.indexOf @
|
index = QR.posts.indexOf @
|
||||||
if QR.posts.length is 1
|
if QR.posts.length is 1
|
||||||
new QR.post true
|
new QR.post true
|
||||||
@ -563,7 +560,9 @@ QR =
|
|||||||
else if @ is QR.selected
|
else if @ is QR.selected
|
||||||
(QR.posts[index-1] or QR.posts[index+1]).select()
|
(QR.posts[index-1] or QR.posts[index+1]).select()
|
||||||
QR.posts.splice index, 1
|
QR.posts.splice index, 1
|
||||||
return unless window.URL
|
QR.status()
|
||||||
|
delete: ->
|
||||||
|
$.rm @nodes.el
|
||||||
URL.revokeObjectURL @URL
|
URL.revokeObjectURL @URL
|
||||||
|
|
||||||
lock: (lock=true) ->
|
lock: (lock=true) ->
|
||||||
@ -608,15 +607,18 @@ QR =
|
|||||||
if input.type is 'checkbox'
|
if input.type is 'checkbox'
|
||||||
@spoiler = input.checked
|
@spoiler = input.checked
|
||||||
return
|
return
|
||||||
{value} = input
|
{name} = input.dataset
|
||||||
@[input.dataset.name] = value
|
@[name] = input.value
|
||||||
return if input.nodeName isnt 'TEXTAREA'
|
switch name
|
||||||
@nodes.span.textContent = value
|
when 'thread'
|
||||||
QR.characterCount()
|
QR.status()
|
||||||
# Disable auto-posting if you're typing in the first post
|
when 'com'
|
||||||
# during the last 5 seconds of the cooldown.
|
@nodes.span.textContent = @com
|
||||||
if QR.cooldown.auto and @ is QR.posts[0] and 0 < QR.cooldown.seconds <= 5
|
QR.characterCount()
|
||||||
QR.cooldown.auto = false
|
# Disable auto-posting if you're typing in the first post
|
||||||
|
# during the last 5 seconds of the cooldown.
|
||||||
|
if QR.cooldown.auto and @ is QR.posts[0] and 0 < QR.cooldown.seconds <= 5
|
||||||
|
QR.cooldown.auto = false
|
||||||
|
|
||||||
forceSave: ->
|
forceSave: ->
|
||||||
return unless @ is QR.selected
|
return unless @ is QR.selected
|
||||||
@ -630,26 +632,15 @@ QR =
|
|||||||
@filename = "#{file.name} (#{$.bytesToString file.size})"
|
@filename = "#{file.name} (#{$.bytesToString file.size})"
|
||||||
@nodes.el.title = @filename
|
@nodes.el.title = @filename
|
||||||
@nodes.label.hidden = false if QR.spoiler
|
@nodes.label.hidden = false if QR.spoiler
|
||||||
URL.revokeObjectURL @URL if window.URL
|
URL.revokeObjectURL @URL
|
||||||
@showFileData()
|
@showFileData()
|
||||||
unless /^image/.test file.type
|
unless /^image/.test file.type
|
||||||
@nodes.el.style.backgroundImage = null
|
@nodes.el.style.backgroundImage = null
|
||||||
return
|
return
|
||||||
@setThumbnail()
|
@setThumbnail()
|
||||||
|
|
||||||
setThumbnail: (fileURL) ->
|
setThumbnail: ->
|
||||||
# XXX Opera does not support blob URL
|
|
||||||
# Create a redimensioned thumbnail.
|
# Create a redimensioned thumbnail.
|
||||||
unless window.URL
|
|
||||||
unless fileURL
|
|
||||||
reader = new FileReader()
|
|
||||||
reader.onload = (e) =>
|
|
||||||
@setThumbnail e.target.result
|
|
||||||
reader.readAsDataURL @file
|
|
||||||
return
|
|
||||||
else
|
|
||||||
fileURL = URL.createObjectURL @file
|
|
||||||
|
|
||||||
img = $.el 'img'
|
img = $.el 'img'
|
||||||
|
|
||||||
img.onload = =>
|
img.onload = =>
|
||||||
@ -661,7 +652,7 @@ QR =
|
|||||||
s *= 3 if @file.type is 'image/gif' # let them animate
|
s *= 3 if @file.type is 'image/gif' # let them animate
|
||||||
{height, width} = img
|
{height, width} = img
|
||||||
if height < s or width < s
|
if height < s or width < s
|
||||||
@URL = fileURL if window.URL
|
@URL = fileURL
|
||||||
@nodes.el.style.backgroundImage = "url(#{@URL})"
|
@nodes.el.style.backgroundImage = "url(#{@URL})"
|
||||||
return
|
return
|
||||||
if height <= width
|
if height <= width
|
||||||
@ -674,10 +665,6 @@ QR =
|
|||||||
cv.height = img.height = height
|
cv.height = img.height = height
|
||||||
cv.width = img.width = width
|
cv.width = img.width = width
|
||||||
cv.getContext('2d').drawImage img, 0, 0, width, height
|
cv.getContext('2d').drawImage img, 0, 0, width, height
|
||||||
unless window.URL
|
|
||||||
@nodes.el.style.backgroundImage = "url(#{cv.toDataURL()})"
|
|
||||||
delete @URL
|
|
||||||
return
|
|
||||||
URL.revokeObjectURL fileURL
|
URL.revokeObjectURL fileURL
|
||||||
applyBlob = (blob) =>
|
applyBlob = (blob) =>
|
||||||
@URL = URL.createObjectURL blob
|
@URL = URL.createObjectURL blob
|
||||||
@ -695,6 +682,7 @@ QR =
|
|||||||
|
|
||||||
applyBlob new Blob [ui8a], type: 'image/png'
|
applyBlob new Blob [ui8a], type: 'image/png'
|
||||||
|
|
||||||
|
fileURL = URL.createObjectURL @file
|
||||||
img.src = fileURL
|
img.src = fileURL
|
||||||
|
|
||||||
rmFile: ->
|
rmFile: ->
|
||||||
@ -704,7 +692,6 @@ QR =
|
|||||||
@nodes.el.style.backgroundImage = null
|
@nodes.el.style.backgroundImage = null
|
||||||
@nodes.label.hidden = true if QR.spoiler
|
@nodes.label.hidden = true if QR.spoiler
|
||||||
@showFileData()
|
@showFileData()
|
||||||
return unless window.URL
|
|
||||||
URL.revokeObjectURL @URL
|
URL.revokeObjectURL @URL
|
||||||
|
|
||||||
showFileData: ->
|
showFileData: ->
|
||||||
@ -729,33 +716,26 @@ QR =
|
|||||||
@nodes.span.textContent = @com
|
@nodes.span.textContent = @com
|
||||||
reader.readAsText file
|
reader.readAsText file
|
||||||
|
|
||||||
dragStart: ->
|
dragStart: -> $.addClass @, 'drag'
|
||||||
$.addClass @, 'drag'
|
dragEnd: -> $.rmClass @, 'drag'
|
||||||
|
dragEnter: -> $.addClass @, 'over'
|
||||||
dragEnd: ->
|
dragLeave: -> $.rmClass @, 'over'
|
||||||
$.rmClass @, 'drag'
|
|
||||||
|
|
||||||
dragEnter: ->
|
|
||||||
$.addClass @, 'over'
|
|
||||||
|
|
||||||
dragLeave: ->
|
|
||||||
$.rmClass @, 'over'
|
|
||||||
|
|
||||||
dragOver: (e) ->
|
dragOver: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.dataTransfer.dropEffect = 'move'
|
e.dataTransfer.dropEffect = 'move'
|
||||||
|
|
||||||
drop: ->
|
drop: ->
|
||||||
el = $ '.drag', @parentNode
|
$.rmClass @, 'over'
|
||||||
$.rmClass el, 'drag' # Opera doesn't fire dragEnd if we drop it on something else
|
|
||||||
$.rmClass @, 'over'
|
|
||||||
return unless @draggable
|
return unless @draggable
|
||||||
|
el = $ '.drag', @parentNode
|
||||||
index = (el) -> [el.parentNode.children...].indexOf el
|
index = (el) -> [el.parentNode.children...].indexOf el
|
||||||
oldIndex = index el
|
oldIndex = index el
|
||||||
newIndex = index @
|
newIndex = index @
|
||||||
(if oldIndex < newIndex then $.after else $.before) @, el
|
(if oldIndex < newIndex then $.after else $.before) @, el
|
||||||
post = QR.posts.splice(oldIndex, 1)[0]
|
post = QR.posts.splice(oldIndex, 1)[0]
|
||||||
QR.posts.splice newIndex, 0, post
|
QR.posts.splice newIndex, 0, post
|
||||||
|
QR.status()
|
||||||
|
|
||||||
captcha:
|
captcha:
|
||||||
init: ->
|
init: ->
|
||||||
@ -784,20 +764,17 @@ QR =
|
|||||||
img: imgContainer.firstChild
|
img: imgContainer.firstChild
|
||||||
input: input
|
input: input
|
||||||
|
|
||||||
if window.MutationObserver
|
new MutationObserver(@load.bind @).observe @nodes.challenge,
|
||||||
observer = new MutationObserver @load.bind @
|
childList: true
|
||||||
observer.observe @nodes.challenge,
|
|
||||||
childList: true
|
|
||||||
else
|
|
||||||
$.on @nodes.challenge, 'DOMNodeInserted', @load.bind @
|
|
||||||
|
|
||||||
$.on imgContainer, 'click', @reload.bind @
|
$.on imgContainer, 'click', @reload.bind @
|
||||||
$.on input, 'keydown', @keydown.bind @
|
$.on input, 'keydown', @keydown.bind @
|
||||||
$.on input, 'focus', -> $.addClass QR.nodes.el, 'focus'
|
$.on input, 'focus', -> $.addClass QR.nodes.el, 'focus'
|
||||||
$.on input, 'blur', -> $.rmClass QR.nodes.el, 'focus'
|
$.on input, 'blur', -> $.rmClass QR.nodes.el, 'focus'
|
||||||
|
|
||||||
$.get 'captchas', [], (item) =>
|
$.get 'captchas', [], ({captchas}) =>
|
||||||
@sync item['captchas']
|
@sync captchas
|
||||||
|
|
||||||
$.sync 'captchas', @sync
|
$.sync 'captchas', @sync
|
||||||
# start with an uncached captcha
|
# start with an uncached captcha
|
||||||
@reload()
|
@reload()
|
||||||
@ -806,12 +783,13 @@ QR =
|
|||||||
# XXX Firefox lacks focusin/focusout support.
|
# XXX Firefox lacks focusin/focusout support.
|
||||||
$.on input, 'blur', QR.focusout
|
$.on input, 'blur', QR.focusout
|
||||||
$.on input, 'focus', QR.focusin
|
$.on input, 'focus', QR.focusin
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
$.addClass QR.nodes.el, 'has-captcha'
|
$.addClass QR.nodes.el, 'has-captcha'
|
||||||
$.after QR.nodes.dumpList.parentElement, [imgContainer, input]
|
$.after QR.nodes.dumpList.parentElement, [imgContainer, input]
|
||||||
|
|
||||||
sync: (@captchas) ->
|
sync: (captchas) ->
|
||||||
|
QR.captcha.captchas = captchas
|
||||||
QR.captcha.count()
|
QR.captcha.count()
|
||||||
|
|
||||||
getOne: ->
|
getOne: ->
|
||||||
@ -931,10 +909,6 @@ QR =
|
|||||||
# Add empty mimeType to avoid errors with URLs selected in Window's file dialog.
|
# Add empty mimeType to avoid errors with URLs selected in Window's file dialog.
|
||||||
QR.mimeTypes.push ''
|
QR.mimeTypes.push ''
|
||||||
nodes.fileInput.max = $('input[name=MAX_FILE_SIZE]').value
|
nodes.fileInput.max = $('input[name=MAX_FILE_SIZE]').value
|
||||||
<% if (type !== 'userjs') { %>
|
|
||||||
# Opera's accept attribute is fucked up
|
|
||||||
nodes.fileInput.accept = "text/*, #{mimeTypes}"
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
QR.spoiler = !!$ 'input[name=spoiler]'
|
QR.spoiler = !!$ 'input[name=spoiler]'
|
||||||
if QR.spoiler
|
if QR.spoiler
|
||||||
@ -969,7 +943,7 @@ QR =
|
|||||||
for elm in $$ '*', QR.nodes.el
|
for elm in $$ '*', QR.nodes.el
|
||||||
$.on elm, 'blur', QR.focusout
|
$.on elm, 'blur', QR.focusout
|
||||||
$.on elm, 'focus', QR.focusin
|
$.on elm, 'focus', QR.focusin
|
||||||
<% } %>
|
<% } %>
|
||||||
$.on dialog, 'focusin', QR.focusin
|
$.on dialog, 'focusin', QR.focusin
|
||||||
$.on dialog, 'focusout', QR.focusout
|
$.on dialog, 'focusout', QR.focusout
|
||||||
$.on nodes.autohide, 'change', QR.toggleHide
|
$.on nodes.autohide, 'change', QR.toggleHide
|
||||||
@ -1133,11 +1107,6 @@ QR =
|
|||||||
QR.status()
|
QR.status()
|
||||||
|
|
||||||
response: ->
|
response: ->
|
||||||
<% if (type === 'userjs') { %>
|
|
||||||
# The upload.onload callback is not called
|
|
||||||
# or at least not in time with Opera.
|
|
||||||
QR.req.upload.onload()
|
|
||||||
<% } %>
|
|
||||||
{req} = QR
|
{req} = QR
|
||||||
delete QR.req
|
delete QR.req
|
||||||
|
|
||||||
@ -1229,15 +1198,15 @@ QR =
|
|||||||
|
|
||||||
QR.cooldown.set {req, post, isReply}
|
QR.cooldown.set {req, post, isReply}
|
||||||
|
|
||||||
if threadID is postID # new thread
|
URL = if threadID is postID # new thread
|
||||||
URL = "/#{g.BOARD}/res/#{threadID}"
|
"/#{g.BOARD}/res/#{threadID}"
|
||||||
else if g.VIEW is 'index' and !QR.cooldown.auto and Conf['Open Post in New Tab'] # replying from the index
|
else if g.VIEW is 'index' and !QR.cooldown.auto and Conf['Open Post in New Tab'] # replying from the index
|
||||||
URL = "/#{g.BOARD}/res/#{threadID}#p#{postID}"
|
"/#{g.BOARD}/res/#{threadID}#p#{postID}"
|
||||||
if URL
|
if URL
|
||||||
if Conf['Open Post in New Tab']
|
if Conf['Open Post in New Tab']
|
||||||
$.open "/#{g.BOARD}/res/#{threadID}"
|
$.open URL
|
||||||
else
|
else
|
||||||
window.location = "/#{g.BOARD}/res/#{threadID}"
|
window.location = URL
|
||||||
|
|
||||||
QR.status()
|
QR.status()
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ QuoteInline =
|
|||||||
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
|
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
{boardID, threadID, postID} = Get.postDataFromLink @
|
{boardID, threadID, postID} = Get.postDataFromLink @
|
||||||
context = Get.contextFromLink @
|
context = Get.contextFromNode @
|
||||||
if $.hasClass @, 'inlined'
|
if $.hasClass @, 'inlined'
|
||||||
QuoteInline.rm @, boardID, threadID, postID, context
|
QuoteInline.rm @, boardID, threadID, postID, context
|
||||||
else
|
else
|
||||||
@ -107,4 +107,4 @@ QuoteInline =
|
|||||||
{boardID, threadID, postID} = Get.postDataFromLink inlined
|
{boardID, threadID, postID} = Get.postDataFromLink inlined
|
||||||
QuoteInline.rm inlined, boardID, threadID, postID, context
|
QuoteInline.rm inlined, boardID, threadID, postID, context
|
||||||
$.rmClass inlined, 'inlined'
|
$.rmClass inlined, 'inlined'
|
||||||
return
|
return
|
||||||
|
|||||||
@ -22,8 +22,9 @@ QuotePreview =
|
|||||||
qp = $.el 'div',
|
qp = $.el 'div',
|
||||||
id: 'qp'
|
id: 'qp'
|
||||||
className: 'dialog'
|
className: 'dialog'
|
||||||
|
|
||||||
$.add Header.hover, qp
|
$.add Header.hover, qp
|
||||||
Get.postClone boardID, threadID, postID, qp, Get.contextFromLink @
|
Get.postClone boardID, threadID, postID, qp, Get.contextFromNode @
|
||||||
|
|
||||||
UI.hover
|
UI.hover
|
||||||
root: @
|
root: @
|
||||||
|
|||||||
@ -21,6 +21,9 @@ Quotify =
|
|||||||
if deadlink.parentNode.className is 'prettyprint'
|
if deadlink.parentNode.className is 'prettyprint'
|
||||||
# Don't quotify deadlinks inside code tags,
|
# Don't quotify deadlinks inside code tags,
|
||||||
# un-`span` them.
|
# un-`span` them.
|
||||||
|
# This won't be necessary once 4chan
|
||||||
|
# stops quotifying inside code tags:
|
||||||
|
# https://github.com/4chan/4chan-JS/issues/77
|
||||||
$.replace deadlink, [deadlink.childNodes...]
|
$.replace deadlink, [deadlink.childNodes...]
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -48,9 +51,8 @@ Quotify =
|
|||||||
target: '_blank'
|
target: '_blank'
|
||||||
textContent: "#{quote}\u00A0(Dead)"
|
textContent: "#{quote}\u00A0(Dead)"
|
||||||
|
|
||||||
a.setAttribute 'data-boardid', boardID
|
$.extend a.dataset, {boardID, threadID: post.thread.ID, postID}
|
||||||
a.setAttribute 'data-threadid', post.thread.ID
|
|
||||||
a.setAttribute 'data-postid', postID
|
|
||||||
else if redirect = Redirect.to 'thread', {boardID, threadID: 0, postID}
|
else if redirect = Redirect.to 'thread', {boardID, threadID: 0, postID}
|
||||||
# Replace the .deadlink span if we can redirect.
|
# Replace the .deadlink span if we can redirect.
|
||||||
a = $.el 'a',
|
a = $.el 'a',
|
||||||
@ -60,9 +62,8 @@ Quotify =
|
|||||||
textContent: "#{quote}\u00A0(Dead)"
|
textContent: "#{quote}\u00A0(Dead)"
|
||||||
if Redirect.to 'post', {boardID, postID}
|
if Redirect.to 'post', {boardID, postID}
|
||||||
# Make it function as a normal quote if we can fetch the post.
|
# Make it function as a normal quote if we can fetch the post.
|
||||||
$.addClass a, 'quotelink'
|
$.addClass a, 'quotelink'
|
||||||
a.setAttribute 'data-boardid', boardID
|
$.extend a.dataset, {boardID, postID}
|
||||||
a.setAttribute 'data-postid', postID
|
|
||||||
|
|
||||||
unless @quotes.contains quoteID
|
unless @quotes.contains quoteID
|
||||||
@quotes.push quoteID
|
@quotes.push quoteID
|
||||||
@ -73,4 +74,4 @@ Quotify =
|
|||||||
|
|
||||||
$.replace deadlink, a
|
$.replace deadlink, a
|
||||||
if $.hasClass a, 'quotelink'
|
if $.hasClass a, 'quotelink'
|
||||||
@nodes.quotelinks.push a
|
@nodes.quotelinks.push a
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user