Merge from Appchan X: Whitespace and other changes not affecting compiled script.

This commit is contained in:
ccd0 2015-01-30 01:22:40 -08:00
parent 1b7cc5270c
commit 8df52a0b24
42 changed files with 258 additions and 110 deletions

View File

@ -58,6 +58,7 @@ Redirect =
protocol = Redirect.protocol archive protocol = Redirect.protocol archive
URL = new String "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}" URL = new String "#{protocol}#{archive.domain}/_/api/chan/post/?board=#{boardID}&num=#{postID}"
return '' unless Redirect.securityCheck URL return '' unless Redirect.securityCheck URL
URL.archive = archive URL.archive = archive
URL URL

View File

@ -6,6 +6,7 @@ Anonymize =
Post.callbacks.push Post.callbacks.push
name: 'Anonymize' name: 'Anonymize'
cb: @node cb: @node
node: -> node: ->
return if @info.capcode or @isClone return if @info.capcode or @isClone
{name, tripcode, email} = @nodes {name, tripcode, email} = @nodes
@ -17,6 +18,7 @@ Anonymize =
if @info.email if @info.email
$.replace email, name $.replace email, name
delete @nodes.email delete @nodes.email
archive: -> archive: ->
$.ready -> $.ready ->
name.textContent = 'Anonymous' for name in $$ '.name' name.textContent = 'Anonymous' for name in $$ '.name'

View File

@ -84,11 +84,13 @@ Filter =
(value) -> regexp is value (value) -> regexp is value
else else
(value) -> regexp.test value (value) -> regexp.test value
settings = settings =
hide: !hl hide: !hl
stub: stub stub: stub
class: hl class: hl
top: top top: top
(value, isReply) -> (value, isReply) ->
if isReply and op is 'only' or !isReply and op is 'no' if isReply and op is 'only' or !isReply and op is 'no'
return false return false
@ -103,10 +105,7 @@ Filter =
# Continue if there's nothing to filter (no tripcode for example). # Continue if there's nothing to filter (no tripcode for example).
continue if value is false continue if value is false
for filter in Filter.filters[key] for filter in Filter.filters[key] when result = filter value, @isReply
unless result = filter value, @isReply
continue
# Hide # Hide
if result.hide if result.hide
if @isReply if @isReply

View File

@ -12,12 +12,14 @@ PostHiding =
node: -> node: ->
return if !@isReply or @isClone or @isFetchedQuote return if !@isReply or @isClone or @isFetchedQuote
if data = PostHiding.db.get {boardID: @board.ID, threadID: @thread.ID, postID: @ID} if data = PostHiding.db.get {boardID: @board.ID, threadID: @thread.ID, postID: @ID}
if data.thisPost if data.thisPost
PostHiding.hide @, data.makeStub, data.hideRecursively PostHiding.hide @, data.makeStub, data.hideRecursively
else else
Recursive.apply PostHiding.hide, @, data.makeStub, true Recursive.apply PostHiding.hide, @, data.makeStub, true
Recursive.add PostHiding.hide, @, data.makeStub, true Recursive.add PostHiding.hide, @, data.makeStub, true
return unless Conf['Reply Hiding Buttons'] return unless Conf['Reply Hiding Buttons']
sideArrows = $('.sideArrows', @nodes.root) sideArrows = $('.sideArrows', @nodes.root)
$.replace sideArrows.firstChild, PostHiding.makeButton @, 'hide' $.replace sideArrows.firstChild, PostHiding.makeButton @, 'hide'

View File

@ -1,18 +1,16 @@
Recursive = Recursive =
recursives: {} recursives: {}
init: -> init: ->
return if g.VIEW not in ['index', 'thread'] return unless g.VIEW in ['index', 'thread']
Post.callbacks.push Post.callbacks.push
name: 'Recursive' name: 'Recursive'
cb: @node cb: @node
node: -> node: ->
return if @isClone or @isFetchedQuote return if @isClone or @isFetchedQuote
for quote in @quotes for quote in @quotes when obj = Recursive.recursives[quote]
if obj = Recursive.recursives[quote] for recursive, i in obj.recursives
for recursive, i in obj.recursives recursive @, obj.args[i]...
recursive @, obj.args[i]...
return return
add: (recursive, post, args...) -> add: (recursive, post, args...) ->

View File

@ -334,6 +334,7 @@ Build =
root = $.el 'div', root = $.el 'div',
className: 'catalog-thread' className: 'catalog-thread'
$.extend root, <%= html( $.extend root, <%= html(
'<a href="/${thread.board}/thread/${thread.ID}">' + '<a href="/${thread.board}/thread/${thread.ID}">' +
'&{thumb}' + '&{thumb}' +
@ -352,12 +353,15 @@ Build =
for quote in $$ '.quotelink', root.lastElementChild for quote in $$ '.quotelink', root.lastElementChild
href = quote.getAttribute 'href' href = quote.getAttribute 'href'
quote.href = "/#{thread.board}/thread/#{thread.ID}" + href if href[0] is '#' quote.href = "/#{thread.board}/thread/#{thread.ID}" + href if href[0] is '#'
for exif in $$ '.abbr, .exif', root.lastElementChild for exif in $$ '.abbr, .exif', root.lastElementChild
$.rm exif $.rm exif
for pp in $$ '.prettyprint', root.lastElementChild for pp in $$ '.prettyprint', root.lastElementChild
cc = $.el 'span', className: 'catalog-code' cc = $.el 'span', className: 'catalog-code'
$.add cc, [pp.childNodes...] $.add cc, [pp.childNodes...]
$.replace pp, cc $.replace pp, cc
for br in $$ 'br', root.lastElementChild when br.previousSibling?.nodeName is 'BR' for br in $$ 'br', root.lastElementChild when br.previousSibling?.nodeName is 'BR'
$.rm br $.rm br
@ -366,6 +370,7 @@ Build =
src: "#{staticPath}sticky#{gifIcon}" src: "#{staticPath}sticky#{gifIcon}"
className: 'stickyIcon' className: 'stickyIcon'
title: 'Sticky' title: 'Sticky'
if thread.isClosed if thread.isClosed
$.add $('.catalog-icons', root), $.el 'img', $.add $('.catalog-icons', root), $.el 'img',
src: "#{staticPath}closed#{gifIcon}" src: "#{staticPath}closed#{gifIcon}"
@ -374,6 +379,7 @@ Build =
if data.bumplimit if data.bumplimit
$.addClass $('.post-count', root), 'warning' $.addClass $('.post-count', root), 'warning'
if data.imagelimit if data.imagelimit
$.addClass $('.file-count', root), 'warning' $.addClass $('.file-count', root), 'warning'

View File

@ -1,7 +1,7 @@
Config = Config =
main: main:
'Miscellaneous': 'Miscellaneous':
'JSON Navigation' : [ 'JSON Navigation': [
true true
'Replace the original board index with one supporting searching, sorting, infinite scrolling, and a catalog mode.' 'Replace the original board index with one supporting searching, sorting, infinite scrolling, and a catalog mode.'
] ]
@ -112,6 +112,7 @@ Config =
true true
'<%= meta.name %> is NOT designed to work with the native extension.' '<%= meta.name %> is NOT designed to work with the native extension.'
] ]
'Linkification': 'Linkification':
'Linkify': [ 'Linkify': [
true true
@ -587,69 +588,69 @@ Config =
filter: filter:
name: """ name: """
# Filter any namefags: # Filter any namefags:
#/^(?!Anonymous$)/ #/^(?!Anonymous$)/
""" """
uniqueID: """ uniqueID: """
# Filter a specific ID: # Filter a specific ID:
#/Txhvk1Tl/ #/Txhvk1Tl/
""" """
tripcode: """ tripcode: """
# Filter any tripfag # Filter any tripfag
#/^!/ #/^!/
""" """
capcode: """ capcode: """
# Set a custom class for mods: # Set a custom class for mods:
#/Mod$/;highlight:mod;op:yes #/Mod$/;highlight:mod;op:yes
# Set a custom class for moot: # Set a custom class for moot:
#/Admin$/;highlight:moot;op:yes #/Admin$/;highlight:moot;op:yes
""" """
subject: """ subject: """
# Filter Generals on /v/: # Filter Generals on /v/:
#/general/i;boards:v;op:only #/general/i;boards:v;op:only
""" """
comment: """ comment: """
# Filter Stallman copypasta on /g/: # Filter Stallman copypasta on /g/:
#/what you\'re refer+ing to as linux/i;boards:g #/what you\'re refer+ing to as linux/i;boards:g
""" """
flag: '' flag: ''
filename: '' filename: ''
dimensions: """ dimensions: """
# Highlight potential wallpapers: # Highlight potential wallpapers:
#/1920x1080/;op:yes;highlight;top:no;boards:w,wg #/1920x1080/;op:yes;highlight;top:no;boards:w,wg
""" """
filesize: '' filesize: ''
MD5: '' MD5: ''
sauces: """ sauces: """
https://www.google.com/searchbyimage?image_url=%TURL https://www.google.com/searchbyimage?image_url=%TURL
http://iqdb.org/?url=%TURL http://iqdb.org/?url=%TURL
#//tineye.com/search?url=%TURL #//tineye.com/search?url=%TURL
#//saucenao.com/search.php?url=%TURL #//saucenao.com/search.php?url=%TURL
#http://3d.iqdb.org/?url=%TURL #http://3d.iqdb.org/?url=%TURL
#http://regex.info/exif.cgi?imgurl=%URL #http://regex.info/exif.cgi?imgurl=%URL
# uploaders: # uploaders:
#//imgur.com/upload?url=%URL;text:Upload to imgur #//imgur.com/upload?url=%URL;text:Upload to imgur
# "View Same" in archives: # "View Same" in archives:
#https://archive.moe/_/search/image/%MD5/;text:View same on archive.moe #https://archive.moe/_/search/image/%MD5/;text:View same on archive.moe
#https://archive.moe/%board/search/image/%MD5/;text:View same on archive.moe/%board/;boards:<%= #https://archive.moe/%board/search/image/%MD5/;text:View same on archive.moe/%board/;boards:<%=
grunt.file.readJSON('src/Archive/archives.json').filter(function(x) {return x.uid === 0})[0].files.join(',') grunt.file.readJSON('src/Archive/archives.json').filter(function(x) {return x.uid === 0})[0].files.join(',')
%> %>
#https://rbt.asia/%board/image/%MD5;text:View same on RBT /%board/;boards:<%= #https://rbt.asia/%board/image/%MD5;text:View same on RBT /%board/;boards:<%=
grunt.file.readJSON('src/Archive/archives.json').filter(function(x) {return x.uid === 8})[0].files.join(',') grunt.file.readJSON('src/Archive/archives.json').filter(function(x) {return x.uid === 8})[0].files.join(',')
%> %>
# Search with full image only for image file types: # Search with full image only for image file types:
#https://www.google.com/searchbyimage?image_url=%URL;types:gif,jpg,png #https://www.google.com/searchbyimage?image_url=%URL;types:gif,jpg,png
#https://www.google.com/searchbyimage?image_url=%TURL;types:webm,pdf #https://www.google.com/searchbyimage?image_url=%TURL;types:webm,pdf
""" """
FappeT: FappeT:
werk: false werk: false
@ -678,29 +679,29 @@ http://iqdb.org/?url=%TURL
'Custom Board Navigation': true 'Custom Board Navigation': true
boardnav: """ boardnav: """
[ toggle-all ] [ toggle-all ]
a-replace a-replace
c-replace c-replace
g-replace g-replace
k-replace k-replace
v-replace v-replace
vg-replace vg-replace
vr-replace vr-replace
ck-replace ck-replace
co-replace co-replace
fit-replace fit-replace
jp-replace jp-replace
mu-replace mu-replace
sp-replace sp-replace
tv-replace tv-replace
vp-replace vp-replace
[external-text:"FAQ","<%= meta.faq %>"] [external-text:"FAQ","<%= meta.faq %>"]
""" """
QR: QR:
'QR.personas': """ 'QR.personas': """
#options:"sage";boards:jp;always #options:"sage";boards:jp;always
""" """
time: '%m/%d/%y(%a)%H:%M:%S' time: '%m/%d/%y(%a)%H:%M:%S'

View File

@ -51,7 +51,7 @@ Get =
# get all their backlinks. # get all their backlinks.
posts.forEach (qPost) -> posts.forEach (qPost) ->
if fullID in qPost.quotes if fullID in qPost.quotes
handleQuotes qPost, 'quotelinks' handleQuotes qPost, 'quotelinks'
# Second: # Second:
# If we have quote backlinks: # If we have quote backlinks:
@ -66,10 +66,12 @@ Get =
quotelinks.filter (quotelink) -> quotelinks.filter (quotelink) ->
{boardID, postID} = Get.postDataFromLink quotelink {boardID, postID} = Get.postDataFromLink quotelink
boardID is post.board.ID and postID is post.ID boardID is post.board.ID and postID is post.ID
scriptData: -> scriptData: ->
for script in $$ 'script:not([src])', d.head for script in $$ 'script:not([src])', d.head
return script.textContent if /\bcooldowns *=/.test script.textContent return script.textContent if /\bcooldowns *=/.test script.textContent
'' ''
postClone: (boardID, threadID, postID, root, context) -> postClone: (boardID, threadID, postID, root, context) ->
if post = g.posts["#{boardID}.#{postID}"] if post = g.posts["#{boardID}.#{postID}"]
Get.insert post, root, context Get.insert post, root, context
@ -81,6 +83,7 @@ Get =
Get.fetchedPost @, boardID, threadID, postID, root, context Get.fetchedPost @, boardID, threadID, postID, root, context
else else
Get.archivedPost boardID, postID, root, context Get.archivedPost boardID, postID, root, context
insert: (post, root, context) -> insert: (post, root, context) ->
# Stop here if the container has been removed while loading. # Stop here if the container has been removed while loading.
return unless root.parentNode return unless root.parentNode
@ -95,6 +98,7 @@ Get =
$.rmAll root $.rmAll root
$.add root, nodes.root $.add root, nodes.root
$.event 'PostsInserted' $.event 'PostsInserted'
fetchedPost: (req, boardID, threadID, postID, root, context) -> fetchedPost: (req, boardID, threadID, postID, root, context) ->
# In case of multiple callbacks for the same request, # In case of multiple callbacks for the same request,
# don't parse the same original post more than once. # don't parse the same original post more than once.
@ -127,6 +131,7 @@ Get =
$.cache api, -> $.cache api, ->
Get.fetchedPost @, boardID, threadID, postID, root, context Get.fetchedPost @, boardID, threadID, postID, root, context
return return
# The post can be deleted by the time we check a quote. # The post can be deleted by the time we check a quote.
unless Get.archivedPost boardID, postID, root, context unless Get.archivedPost boardID, postID, root, context
$.addClass root, 'warning' $.addClass root, 'warning'
@ -141,6 +146,7 @@ Get =
post.isFetchedQuote = true post.isFetchedQuote = true
Main.callbackNodes Post, [post] Main.callbackNodes Post, [post]
Get.insert post, root, context Get.insert post, root, context
archivedPost: (boardID, postID, root, context) -> archivedPost: (boardID, postID, root, context) ->
return false unless Conf['Resurrect Quotes'] return false unless Conf['Resurrect Quotes']
return false unless url = Redirect.to 'post', {boardID, postID} return false unless url = Redirect.to 'post', {boardID, postID}
@ -161,6 +167,7 @@ Get =
Get.parseArchivedPost response, boardID, postID, root, context Get.parseArchivedPost response, boardID, postID, root, context
return true return true
return false return false
parseArchivedPost: (data, boardID, postID, root, context) -> parseArchivedPost: (data, boardID, postID, root, context) ->
# In case of multiple callbacks for the same request, # In case of multiple callbacks for the same request,
# don't parse the same original post more than once. # don't parse the same original post more than once.

View File

@ -143,7 +143,10 @@ Header =
$.extend boardList, <%= html( $.extend boardList, <%= html(
'<span id="custom-board-list"></span>' + '<span id="custom-board-list"></span>' +
'<span id="full-board-list" hidden>' + '<span id="full-board-list" hidden>' +
'<span class="hide-board-list-container brackets-wrap"><a href="javascript:;" class="hide-board-list-button">&nbsp;-&nbsp;</a></span> ' + '<span class="hide-board-list-container brackets-wrap">' +
'<a href="javascript:;" class="hide-board-list-button">&nbsp;-&nbsp;</a>' +
'</span>' +
' ' +
'<span class="boardList"></span>' + '<span class="boardList"></span>' +
'</span>' '</span>'
) %> ) %>
@ -185,11 +188,13 @@ Header =
nodes = boardnav.match(/[\w@]+(-(all|title|replace|full|index|catalog|archive|expired|text:"[^"]+"(,"[^"]+")?))*|[^\w@]+/g).map (t) -> nodes = boardnav.match(/[\w@]+(-(all|title|replace|full|index|catalog|archive|expired|text:"[^"]+"(,"[^"]+")?))*|[^\w@]+/g).map (t) ->
if /^[^\w@]/.test t if /^[^\w@]/.test t
return $.tn t return $.tn t
text = url = null text = url = null
t = t.replace /-text:"([^"]+)"(?:,"([^"]+)")?/g, (m0, m1, m2) -> t = t.replace /-text:"([^"]+)"(?:,"([^"]+)")?/g, (m0, m1, m2) ->
text = m1 text = m1
url = m2 url = m2
'' ''
if /^toggle-all/.test t if /^toggle-all/.test t
a = $.el 'a', a = $.el 'a',
className: 'show-board-list-button' className: 'show-board-list-button'
@ -197,12 +202,14 @@ Header =
href: 'javascript:;' href: 'javascript:;'
$.on a, 'click', Header.toggleBoardList $.on a, 'click', Header.toggleBoardList
return a return a
if /^external/.test t if /^external/.test t
a = $.el 'a', a = $.el 'a',
href: url or 'javascript:;' href: url or 'javascript:;'
textContent: text or '+' textContent: text or '+'
className: 'external' className: 'external'
return a return a
boardID = if /^current/.test t boardID = if /^current/.test t
g.BOARD.ID g.BOARD.ID
else else
@ -411,7 +418,6 @@ Header =
hash = @location.hash[1..] hash = @location.hash[1..]
return unless /^p\d+$/.test(hash) and post = $.id hash return unless /^p\d+$/.test(hash) and post = $.id hash
return if (Get.postFromRoot post).isHidden return if (Get.postFromRoot post).isHidden
Header.scrollTo post Header.scrollTo post
scrollTo: (root, down, needed) -> scrollTo: (root, down, needed) ->
@ -451,10 +457,12 @@ Header =
headRect = Header.toggle.getBoundingClientRect() headRect = Header.toggle.getBoundingClientRect()
bottom -= clientHeight - headRect.bottom + headRect.height bottom -= clientHeight - headRect.bottom + headRect.height
bottom bottom
isNodeVisible: (node) -> isNodeVisible: (node) ->
return false if d.hidden or !doc.contains node return false if d.hidden or !doc.contains node
{height} = node.getBoundingClientRect() {height} = node.getBoundingClientRect()
Header.getTopOf(node) + height >= 0 and Header.getBottomOf(node) + height >= 0 Header.getTopOf(node) + height >= 0 and Header.getBottomOf(node) + height >= 0
isHidden: -> isHidden: ->
{top} = Header.bar.getBoundingClientRect() {top} = Header.bar.getBoundingClientRect()
if Conf['Bottom header'] if Conf['Bottom header']

View File

@ -161,6 +161,7 @@ Index =
catalogNode: -> catalogNode: ->
$.on @nodes.thumb.parentNode, 'click', Index.onClick $.on @nodes.thumb.parentNode, 'click', Index.onClick
onClick: (e) -> onClick: (e) ->
return if e.button isnt 0 return if e.button isnt 0
thread = g.threads[@parentNode.dataset.fullID] thread = g.threads[@parentNode.dataset.fullID]
@ -169,6 +170,7 @@ Index =
else else
return return
e.preventDefault() e.preventDefault()
toggleHide: (thread) -> toggleHide: (thread) ->
$.rm thread.catalogView.nodes.root $.rm thread.catalogView.nodes.root
if Index.showHiddenThreads if Index.showHiddenThreads
@ -178,6 +180,7 @@ Index =
else else
ThreadHiding.hide thread ThreadHiding.hide thread
ThreadHiding.saveHiddenState thread ThreadHiding.saveHiddenState thread
cycleSortType: -> cycleSortType: ->
types = [Index.selectSort.options...].filter (option) -> !option.disabled types = [Index.selectSort.options...].filter (option) -> !option.disabled
for type, i in types for type, i in types
@ -193,15 +196,18 @@ Index =
'Show' 'Show'
Index.sort() Index.sort()
Index.buildIndex() Index.buildIndex()
mode: -> mode: ->
mode = @value mode = @value
unless mode is 'catalog' unless mode is 'catalog'
Conf['Previous Index Mode'] = mode Conf['Previous Index Mode'] = mode
$.set 'Previous Index Mode', mode $.set 'Previous Index Mode', mode
Index.pageLoad Index.pushState {mode} Index.pageLoad Index.pushState {mode}
sort: -> sort: ->
Index.sort() Index.sort()
Index.buildIndex() Index.buildIndex()
size: (e) -> size: (e) ->
if Conf['Index Mode'] isnt 'catalog' if Conf['Index Mode'] isnt 'catalog'
$.rmClass Index.root, 'catalog-small' $.rmClass Index.root, 'catalog-small'
@ -213,10 +219,12 @@ Index =
$.addClass Index.root, 'catalog-large' $.addClass Index.root, 'catalog-large'
$.rmClass Index.root, 'catalog-small' $.rmClass Index.root, 'catalog-small'
Index.buildIndex() if e Index.buildIndex() if e
replies: -> replies: ->
Index.buildThreads() Index.buildThreads()
Index.sort() Index.sort()
Index.buildIndex() Index.buildIndex()
popstate: (e) -> popstate: (e) ->
if e?.state if e?.state
{search, mode} = e.state {search, mode} = e.state
@ -236,6 +244,7 @@ Index =
scroll: true scroll: true
if state.command if state.command
Index[if Conf['Refreshed Navigation'] then 'update' else 'pageLoad'] state Index[if Conf['Refreshed Navigation'] then 'update' else 'pageLoad'] state
pageNav: (e) -> pageNav: (e) ->
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
switch e.target.nodeName switch e.target.nodeName
@ -249,6 +258,7 @@ Index =
return if a.textContent is 'Catalog' return if a.textContent is 'Catalog'
e.preventDefault() e.preventDefault()
Index.userPageNav +a.pathname.split('/')[2] or 1 Index.userPageNav +a.pathname.split('/')[2] or 1
frontPage: (e) -> frontPage: (e) ->
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()
@ -262,12 +272,14 @@ Index =
1 1
else else
+window.location.pathname.split('/')[2] or 1 +window.location.pathname.split('/')[2] or 1
userPageNav: (page, noRefresh) -> userPageNav: (page, noRefresh) ->
state = Index.pushState {page, scroll: true} state = Index.pushState {page, scroll: true}
if Conf['Refreshed Navigation'] and !noRefresh if Conf['Refreshed Navigation'] and !noRefresh
Index.update state Index.update state
else else
Index.pageLoad state if state.page Index.pageLoad state if state.page
pushState: (state) -> pushState: (state) ->
{pathname, hash} = location {pathname, hash} = location
pageBeforeSearch = history.state?.oldpage pageBeforeSearch = history.state?.oldpage
@ -309,6 +321,7 @@ Index =
oldpage: pageBeforeSearch oldpage: pageBeforeSearch
, '', pathname + hash , '', pathname + hash
state state
pageLoad: ({sort, search, mode, scroll}) -> pageLoad: ({sort, search, mode, scroll}) ->
if sort or search? if sort or search?
Index.sort() Index.sort()
@ -318,6 +331,7 @@ Index =
Index.buildIndex() Index.buildIndex()
Index.setPage() Index.setPage()
Index.scrollToIndex() if scroll Index.scrollToIndex() if scroll
applyMode: -> applyMode: ->
for mode in ['paged', 'infinite', 'all pages', 'catalog'] for mode in ['paged', 'infinite', 'all pages', 'catalog']
$[if mode is Conf['Index Mode'] then 'addClass' else 'rmClass'] doc, "#{mode.replace /\ /g, '-'}-mode" $[if mode is Conf['Index Mode'] then 'addClass' else 'rmClass'] doc, "#{mode.replace /\ /g, '-'}-mode"
@ -331,8 +345,10 @@ Index =
Math.ceil Index.sortedNodes.length / Index.threadsNumPerPage Math.ceil Index.sortedNodes.length / Index.threadsNumPerPage
else else
Index.pagesNum Index.pagesNum
getMaxPageNum: -> getMaxPageNum: ->
Math.max 1, Index.getPagesNum() Math.max 1, Index.getPagesNum()
buildPagelist: -> buildPagelist: ->
pagesRoot = $ '.pages', Index.pagelist pagesRoot = $ '.pages', Index.pagelist
maxPageNum = Index.getMaxPageNum() maxPageNum = Index.getMaxPageNum()
@ -345,10 +361,12 @@ Index =
nodes.push $.tn('['), a, $.tn '] ' nodes.push $.tn('['), a, $.tn '] '
$.rmAll pagesRoot $.rmAll pagesRoot
$.add pagesRoot, nodes $.add pagesRoot, nodes
setPage: -> setPage: ->
pageNum = Index.getCurrentPage() pageNum = Index.getCurrentPage()
maxPageNum = Index.getMaxPageNum() maxPageNum = Index.getMaxPageNum()
pagesRoot = $ '.pages', Index.pagelist pagesRoot = $ '.pages', Index.pagelist
# Previous/Next buttons # Previous/Next buttons
prev = pagesRoot.previousSibling.firstChild prev = pagesRoot.previousSibling.firstChild
next = pagesRoot.nextSibling.firstChild next = pagesRoot.nextSibling.firstChild
@ -358,12 +376,14 @@ Index =
href = Math.min pageNum + 1, maxPageNum href = Math.min pageNum + 1, maxPageNum
next.href = if href is 1 then './' else href next.href = if href is 1 then './' else href
next.firstChild.disabled = href is pageNum next.firstChild.disabled = href is pageNum
# <strong> current page # <strong> current page
if strong = $ 'strong', pagesRoot if strong = $ 'strong', pagesRoot
return if +strong.textContent is pageNum return if +strong.textContent is pageNum
$.replace strong, strong.firstChild $.replace strong, strong.firstChild
else else
strong = $.el 'strong' strong = $.el 'strong'
a = pagesRoot.children[pageNum - 1] a = pagesRoot.children[pageNum - 1]
$.before a, strong $.before a, strong
$.add strong, a $.add strong, a

View File

@ -33,10 +33,13 @@ Main =
else # string or number else # string or number
Conf[parent] = obj Conf[parent] = obj
return return
flatten null, Config flatten null, Config
for db in DataBoard.keys for db in DataBoard.keys
Conf[db] = boards: {} Conf[db] = boards: {}
Conf['selectedArchives'] = {} Conf['selectedArchives'] = {}
$.get Conf, (items) -> $.get Conf, (items) ->
$.extend Conf, items $.extend Conf, items
$.asap (-> doc = d.documentElement), Main.initFeatures $.asap (-> doc = d.documentElement), Main.initFeatures
@ -71,7 +74,7 @@ Main =
pathname = location.pathname.split '/' pathname = location.pathname.split '/'
if pathname[2] isnt 'thread' or pathname.length > 4 if pathname[2] isnt 'thread' or pathname.length > 4
pathname[2] = 'thread' pathname[2] = 'thread'
history.replaceState null, '', pathname.slice(0,4).join('/') + location.hash history.replaceState null, '', pathname[0...4].join('/') + location.hash
# c.time 'All initializations' # c.time 'All initializations'
for [name, feature] in Main.features for [name, feature] in Main.features

View File

@ -73,11 +73,13 @@ Settings =
delete Settings.dialog delete Settings.dialog
sections: [] sections: []
addSection: (title, open) -> addSection: (title, open) ->
if typeof title isnt 'string' if typeof title isnt 'string'
{title, open} = title.detail {title, open} = title.detail
hyphenatedTitle = title.toLowerCase().replace /\s+/g, '-' hyphenatedTitle = title.toLowerCase().replace /\s+/g, '-'
Settings.sections.push {title, hyphenatedTitle, open} Settings.sections.push {title, hyphenatedTitle, open}
openSection: -> openSection: ->
if selected = $ '.tab-selected', Settings.dialog if selected = $ '.tab-selected', Settings.dialog
$.rmClass selected, 'tab-selected' $.rmClass selected, 'tab-selected'
@ -142,12 +144,14 @@ Settings =
localStorage.removeItem "4chan-hide-t-#{boardID}" localStorage.removeItem "4chan-hide-t-#{boardID}"
$.delete ['hiddenThreads', 'hiddenPosts'] $.delete ['hiddenThreads', 'hiddenPosts']
$.after $('input[name="Stubs"]', section).parentNode.parentNode, div $.after $('input[name="Stubs"]', section).parentNode.parentNode, div
export: -> export: ->
# Make sure to export the most recent data. # Make sure to export the most recent data.
$.get Conf, (Conf) -> $.get Conf, (Conf) ->
# XXX don't export archives. # XXX don't export archives.
delete Conf['archives'] delete Conf['archives']
Settings.downloadExport {version: g.VERSION, date: Date.now(), Conf} Settings.downloadExport {version: g.VERSION, date: Date.now(), Conf}
downloadExport: (data) -> downloadExport: (data) ->
a = $.el 'a', a = $.el 'a',
download: "<%= meta.name %> v#{g.VERSION}-#{data.date}.json" download: "<%= meta.name %> v#{g.VERSION}-#{data.date}.json"
@ -160,12 +164,14 @@ Settings =
a.click() a.click()
import: -> import: ->
$('input', @parentNode).click() $('input', @parentNode).click()
onImport: -> onImport: ->
return unless file = @files[0] return unless file = @files[0]
output = $('.imp-exp-result') output = $('.imp-exp-result')
unless confirm 'Your current settings will be entirely overwritten, are you sure?' unless confirm 'Your current settings will be entirely overwritten, are you sure?'
output.textContent = 'Import aborted.' output.textContent = 'Import aborted.'
return return
reader = new FileReader() reader = new FileReader()
reader.onload = (e) -> reader.onload = (e) ->
try try
@ -176,6 +182,7 @@ Settings =
output.textContent = 'Import failed due to an error.' output.textContent = 'Import failed due to an error.'
c.error err.stack c.error err.stack
reader.readAsText file reader.readAsText file
loadSettings: (data) -> loadSettings: (data) ->
version = data.version.split '.' version = data.version.split '.'
if version[0] is '2' if version[0] is '2'
@ -259,6 +266,7 @@ Settings =
data.Conf['watchedThreads'] = boards: ThreadWatcher.convert data.Conf['WatchedThreads'] data.Conf['watchedThreads'] = boards: ThreadWatcher.convert data.Conf['WatchedThreads']
delete data.Conf['WatchedThreads'] delete data.Conf['WatchedThreads']
$.clear -> $.set data.Conf $.clear -> $.set data.Conf
reset: -> reset: ->
if confirm 'Your current settings will be entirely wiped, are you sure?' if confirm 'Your current settings will be entirely wiped, are you sure?'
$.clear -> window.location.reload() if confirm 'Reset successful. Reload now?' $.clear -> window.location.reload() if confirm 'Reset successful. Reload now?'
@ -326,9 +334,11 @@ Settings =
interval = $ 'input[name="Interval"]', section interval = $ 'input[name="Interval"]', section
customCSS = $ 'input[name="Custom CSS"]', section customCSS = $ 'input[name="Custom CSS"]', section
interval.value = Conf['Interval'] interval.value = Conf['Interval']
customCSS.checked = Conf['Custom CSS'] customCSS.checked = Conf['Custom CSS']
inputs['usercss'].disabled = !Conf['Custom CSS'] inputs['usercss'].disabled = !Conf['Custom CSS']
$.on interval, 'change', ThreadUpdater.cb.interval $.on interval, 'change', ThreadUpdater.cb.interval
$.on customCSS, 'change', Settings.togglecss $.on customCSS, 'change', Settings.togglecss
$.on $('#apply-css', section), 'click', Settings.usercss $.on $('#apply-css', section), 'click', Settings.usercss
@ -421,10 +431,13 @@ Settings =
boardnav: -> boardnav: ->
Header.generateBoardList @value Header.generateBoardList @value
time: -> time: ->
@nextElementSibling.textContent = Time.format @value, new Date() @nextElementSibling.textContent = Time.format @value, new Date()
backlink: -> backlink: ->
@nextElementSibling.textContent = @value.replace /%(?:id|%)/g, (x) -> {'%id': '123456789', '%%': '%'}[x] @nextElementSibling.textContent = @value.replace /%(?:id|%)/g, (x) -> {'%id': '123456789', '%%': '%'}[x]
fileInfo: -> fileInfo: ->
data = data =
isReply: true isReply: true
@ -437,6 +450,7 @@ Settings =
isImage: true isImage: true
isSpoiler: true isSpoiler: true
FileInfo.format @value, data, @nextElementSibling FileInfo.format @value, data, @nextElementSibling
favicon: -> favicon: ->
Favicon.switch() Favicon.switch()
Unread.update() if g.VIEW is 'thread' and Conf['Unread Favicon'] Unread.update() if g.VIEW is 'thread' and Conf['Unread Favicon']
@ -445,12 +459,14 @@ Settings =
img[1].src = Favicon.unreadSFW img[1].src = Favicon.unreadSFW
img[2].src = Favicon.unreadNSFW img[2].src = Favicon.unreadNSFW
img[3].src = Favicon.unreadDead img[3].src = Favicon.unreadDead
togglecss: -> togglecss: ->
if $('textarea[name=usercss]', $.x 'ancestor::fieldset[1]', @).disabled = !@checked if $('textarea[name=usercss]', $.x 'ancestor::fieldset[1]', @).disabled = !@checked
CustomCSS.rmStyle() CustomCSS.rmStyle()
else else
CustomCSS.addStyle() CustomCSS.addStyle()
$.cb.checked.call @ $.cb.checked.call @
usercss: -> usercss: ->
CustomCSS.update() CustomCSS.update()
@ -475,6 +491,7 @@ Settings =
for key, val of items for key, val of items
inputs[key].value = val inputs[key].value = val
return return
keybind: (e) -> keybind: (e) ->
return if e.keyCode is 9 # tab return if e.keyCode is 9 # tab
e.preventDefault() e.preventDefault()

View File

@ -328,6 +328,7 @@ 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') { %> <% if (type === 'userscript') { %>
# Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=674955 # Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=674955
o.workaround = (e) -> o.hoverend(e) unless root.contains e.target o.workaround = (e) -> o.hoverend(e) unless root.contains e.target

View File

@ -443,6 +443,7 @@ do ->
delete $.oldValue[key] delete $.oldValue[key]
cb undefined, key cb undefined, key
$.on window, 'storage', ({key}) -> onChange key $.on window, 'storage', ({key}) -> onChange key
$.forceSync = (key) -> $.forceSync = (key) ->
# Storage events don't work across origins # Storage events don't work across origins
# e.g. http://boards.4chan.org and https://boards.4chan.org # e.g. http://boards.4chan.org and https://boards.4chan.org
@ -482,6 +483,7 @@ $.set = do ->
$.oldValue[key] = val $.oldValue[key] = val
# for `storage` events # for `storage` events
localStorage.setItem key, val localStorage.setItem key, val
(keys, val) -> (keys, val) ->
if typeof keys is 'string' if typeof keys is 'string'
set keys, val set keys, val

View File

@ -62,7 +62,7 @@ class RandomAccessList
shift: -> shift: ->
@rm @first.ID @rm @first.ID
order: -> order: ->
order = [item = @first] order = [item = @first]
order.push item while item = item.next order.push item while item = item.next

View File

@ -28,6 +28,7 @@ class Thread
icon.title = "This thread is on page #{pageNum} in the original index." icon.title = "This thread is on page #{pageNum} in the original index."
icon.textContent = "[#{pageNum}]" icon.textContent = "[#{pageNum}]"
@catalogView.nodes.pageCount.textContent = pageNum if @catalogView @catalogView.nodes.pageCount.textContent = pageNum if @catalogView
setCount: (type, count, reachedLimit) -> setCount: (type, count, reachedLimit) ->
return unless @catalogView return unless @catalogView
el = @catalogView.nodes["#{type}Count"] el = @catalogView.nodes["#{type}Count"]
@ -47,6 +48,7 @@ class Thread
typeLC = type.toLowerCase() typeLC = type.toLowerCase()
icon = $ ".#{typeLC}Icon", @OP.nodes.info icon = $ ".#{typeLC}Icon", @OP.nodes.info
return if !!icon is status return if !!icon is status
unless status unless status
$.rm icon.previousSibling $.rm icon.previousSibling
$.rm icon $.rm icon
@ -57,6 +59,7 @@ class Thread
alt: type alt: type
title: type title: type
className: "#{typeLC}Icon retina" className: "#{typeLC}Icon retina"
root = if type isnt 'Sticky' and @isSticky root = if type isnt 'Sticky' and @isSticky
$ '.stickyIcon', @OP.nodes.info $ '.stickyIcon', @OP.nodes.info
else else

View File

@ -78,6 +78,7 @@ Gallery =
$.on d, 'keydown', cb.keybinds $.on d, 'keydown', cb.keybinds
$.off d, 'keydown', Keybinds.keydown if Conf['Keybinds'] $.off d, 'keydown', Keybinds.keydown if Conf['Keybinds']
for file in $$ '.post .file' when !$ '.fileDeletedRes, .fileDeleted', file for file in $$ '.post .file' when !$ '.fileDeletedRes, .fileDeleted', file
post = Get.postFromNode file post = Get.postFromNode file
Gallery.generateThumb post Gallery.generateThumb post
@ -87,6 +88,7 @@ Gallery =
if Header.getTopOf(candidate) + candidate.getBoundingClientRect().height >= 0 if Header.getTopOf(candidate) + candidate.getBoundingClientRect().height >= 0
image = candidate image = candidate
$.addClass doc, 'gallery-open' $.addClass doc, 'gallery-open'
$.add d.body, dialog $.add d.body, dialog
nodes.thumbs.scrollTop = 0 nodes.thumbs.scrollTop = 0
@ -103,6 +105,7 @@ Gallery =
return if post.isClone or post.isHidden return if post.isClone or post.isHidden
return unless post.file and (post.file.isImage or post.file.isVideo or Conf['PDF in Gallery']) return unless post.file and (post.file.isImage or post.file.isVideo or Conf['PDF in Gallery'])
return if Gallery.fullIDs[post.fullID] return if Gallery.fullIDs[post.fullID]
Gallery.fullIDs[post.fullID] = true Gallery.fullIDs[post.fullID] = true
thumb = $.el 'a', thumb = $.el 'a',
@ -110,13 +113,14 @@ Gallery =
href: post.file.URL href: post.file.URL
target: '_blank' target: '_blank'
title: post.file.name title: post.file.name
thumb.dataset.id = Gallery.images.length
thumb.dataset.id = Gallery.images.length
thumb.dataset.post = post.fullID thumb.dataset.post = post.fullID
thumbImg = post.file.thumb.cloneNode false thumbImg = post.file.thumb.cloneNode false
thumbImg.style.cssText = '' thumbImg.style.cssText = ''
$.add thumb, thumbImg $.add thumb, thumbImg
$.on thumb, 'click', Gallery.cb.open $.on thumb, 'click', Gallery.cb.open
Gallery.images.push thumb Gallery.images.push thumb
@ -135,6 +139,7 @@ Gallery =
elType = 'img' elType = 'img'
elType = 'video' if /\.webm$/.test(thumb.href) elType = 'video' if /\.webm$/.test(thumb.href)
elType = 'iframe' if /\.pdf$/.test(thumb.href) elType = 'iframe' if /\.pdf$/.test(thumb.href)
$[if elType is 'iframe' then 'addClass' else 'rmClass'] doc, 'gal-pdf' $[if elType is 'iframe' then 'addClass' else 'rmClass'] doc, 'gal-pdf'
file = $.el elType, file = $.el elType,
title: name.download = name.textContent = thumb.title title: name.download = name.textContent = thumb.title
@ -142,7 +147,7 @@ Gallery =
Gallery.error file, thumb Gallery.error file, thumb
file.src = name.href = thumb.href file.src = name.href = thumb.href
$.extend file.dataset, thumb.dataset $.extend file.dataset, thumb.dataset
nodes.current.pause?() unless nodes.current.error nodes.current.pause?() unless nodes.current.error
$.replace nodes.current, file $.replace nodes.current, file
if elType is 'video' if elType is 'video'
@ -242,10 +247,12 @@ Gallery =
Gallery.cb.open.call( Gallery.cb.open.call(
Gallery.images[+Gallery.nodes.current.dataset.id + 1] or Gallery.images[0] Gallery.images[+Gallery.nodes.current.dataset.id + 1] or Gallery.images[0]
) )
click: (e) -> click: (e) ->
return if ImageCommon.onControls e return if ImageCommon.onControls e
e.preventDefault() e.preventDefault()
Gallery.cb.advance() Gallery.cb.advance()
advance: -> if Gallery.nodes.current.paused then Gallery.nodes.current.play() else Gallery.cb.next() advance: -> if Gallery.nodes.current.paused then Gallery.nodes.current.play() else Gallery.cb.next()
toggle: -> (if Gallery.nodes then Gallery.cb.close else Gallery.build)() toggle: -> (if Gallery.nodes then Gallery.cb.close else Gallery.build)()
blank: (e) -> Gallery.cb.close() if e.target is @ blank: (e) -> Gallery.cb.close() if e.target is @

View File

@ -7,6 +7,7 @@ ImageExpand =
textContent: 'EAI' textContent: 'EAI'
title: 'Expand All Images' title: 'Expand All Images'
href: 'javascript:;' href: 'javascript:;'
$.on @EAI, 'click', @cb.toggleAll $.on @EAI, 'click', @cb.toggleAll
Header.addShortcut @EAI, 3 Header.addShortcut @EAI, 3
$.on d, 'scroll visibilitychange', @cb.playVideos $.on d, 'scroll visibilitychange', @cb.playVideos
@ -18,14 +19,17 @@ ImageExpand =
node: -> node: ->
return unless @file and (@file.isImage or @file.isVideo) return unless @file and (@file.isImage or @file.isVideo)
$.on @file.thumb.parentNode, 'click', ImageExpand.cb.toggle $.on @file.thumb.parentNode, 'click', ImageExpand.cb.toggle
if @isClone if @isClone
if @file.isExpanding if @file.isExpanding
# If we clone a post where the image is still loading, # If we clone a post where the image is still loading,
# make it loading in the clone too. # make it loading in the clone too.
ImageExpand.contract @ ImageExpand.contract @
ImageExpand.expand @ ImageExpand.expand @
else if @file.isExpanded and @file.isVideo else if @file.isExpanded and @file.isVideo
ImageExpand.setupVideo @, !@origin.file.fullImage?.paused or @origin.file.wasPlaying, @file.fullImage.controls ImageExpand.setupVideo @, !@origin.file.fullImage?.paused or @origin.file.wasPlaying, @file.fullImage.controls
else if ImageExpand.on and !@isHidden and !@isFetchedQuote and else if ImageExpand.on and !@isHidden and !@isFetchedQuote and
(Conf['Expand spoilers'] or !@file.isSpoiler) and (Conf['Expand spoilers'] or !@file.isSpoiler) and
(Conf['Expand videos'] or !@file.isVideo) (Conf['Expand videos'] or !@file.isVideo)
@ -247,7 +251,7 @@ ImageExpand =
el = $.el 'span', el = $.el 'span',
textContent: 'Image Expansion' textContent: 'Image Expansion'
className: 'image-expansion-link' className: 'image-expansion-link'
{createSubEntry} = ImageExpand.menu {createSubEntry} = ImageExpand.menu
subEntries = [] subEntries = []

View File

@ -9,13 +9,16 @@ ImageHover =
CatalogThread.callbacks.push CatalogThread.callbacks.push
name: 'Catalog Image Hover' name: 'Catalog Image Hover'
cb: @catalogNode cb: @catalogNode
node: -> node: ->
return unless @file and (@file.isImage or @file.isVideo) return unless @file and (@file.isImage or @file.isVideo)
$.on @file.thumb, 'mouseover', ImageHover.mouseover @ $.on @file.thumb, 'mouseover', ImageHover.mouseover @
catalogNode: -> catalogNode: ->
{file} = @thread.OP {file} = @thread.OP
return unless file and (file.isImage or file.isVideo) return unless file and (file.isImage or file.isVideo)
$.on @nodes.thumb, 'mouseover', ImageHover.mouseover @thread.OP $.on @nodes.thumb, 'mouseover', ImageHover.mouseover @thread.OP
mouseover: (post) -> (e) -> mouseover: (post) -> (e) ->
return unless doc.contains @ return unless doc.contains @
{file} = post {file} = post
@ -30,6 +33,7 @@ ImageHover =
el.dataset.fullID = post.fullID el.dataset.fullID = post.fullID
$.on el, 'error', error $.on el, 'error', error
el.src = file.URL el.src = file.URL
if Conf['Restart when Opened'] if Conf['Restart when Opened']
ImageCommon.rewind el ImageCommon.rewind el
ImageCommon.rewind @ ImageCommon.rewind @
@ -46,7 +50,7 @@ ImageHover =
maxWidth = Math.max left, doc.clientWidth - right maxWidth = Math.max left, doc.clientWidth - right
maxHeight = doc.clientHeight - padding maxHeight = doc.clientHeight - padding
scale = Math.min 1, maxWidth / width, maxHeight / height scale = Math.min 1, maxWidth / width, maxHeight / height
el.style.maxWidth = "#{scale * width}px" el.style.maxWidth = "#{scale * width}px"
el.style.maxHeight = "#{scale * height}px" el.style.maxHeight = "#{scale * height}px"
UI.hover UI.hover
root: @ root: @
@ -62,6 +66,7 @@ ImageHover =
el.pause() if isVideo el.pause() if isVideo
$.rm el $.rm el
el.removeAttribute 'style' el.removeAttribute 'style'
error: (post) -> -> error: (post) -> ->
return if ImageCommon.decodeError @, post return if ImageCommon.decodeError @, post
ImageCommon.error @, post, 3 * $.SECOND, (URL) => ImageCommon.error @, post, 3 * $.SECOND, (URL) =>

View File

@ -75,6 +75,7 @@ ImageLoader =
if !chrome? if !chrome?
$.on thumb, 'loadeddata', -> @removeAttribute 'poster' $.on thumb, 'loadeddata', -> @removeAttribute 'poster'
return return
el = $.el if isImage then 'img' else 'video' el = $.el if isImage then 'img' else 'video'
if replace and isImage if replace and isImage
$.on el, 'load', -> $.on el, 'load', ->

View File

@ -40,7 +40,7 @@ Sauce =
type type
ext = post.file.URL.match(/\.([^\.]*)$/)?[1] or '' ext = post.file.URL.match(/\.([^\.]*)$/)?[1] or ''
return null unless !parts['boards'] or post.board.ID in parts['boards'].split ',' return null unless !parts['boards'] or post.board.ID in parts['boards'].split ','
return null unless !parts['types'] or ext in parts['types'].split ',' return null unless !parts['types'] or ext in parts['types'].split ','
a = Sauce.link.cloneNode true a = Sauce.link.cloneNode true
a.href = parts['url'] a.href = parts['url']
a.textContent = parts['text'] a.textContent = parts['text']
@ -49,8 +49,7 @@ Sauce =
node: -> node: ->
return if @isClone or !@file return if @isClone or !@file
nodes = [] nodes = []
for link in Sauce.links for link in Sauce.links when node = Sauce.createSauceLink link, @
if node = Sauce.createSauceLink link, @ # \u00A0 is nbsp
# \u00A0 is nbsp nodes.push $.tn('\u00A0'), node
nodes.push $.tn('\u00A0'), node
$.add @file.text, nodes $.add @file.text, nodes

View File

@ -5,12 +5,14 @@ Menu =
@button = $.el 'a', @button = $.el 'a',
className: 'menu-button' className: 'menu-button'
href: 'javascript:;' href: 'javascript:;'
$.extend @button, <%= html('<i class="fa fa-angle-down"></i>') %> $.extend @button, <%= html('<i class="fa fa-angle-down"></i>') %>
@menu = new UI.Menu 'post' @menu = new UI.Menu 'post'
Post.callbacks.push Post.callbacks.push
name: 'Menu' name: 'Menu'
cb: @node cb: @node
CatalogThread.callbacks.push CatalogThread.callbacks.push
name: 'Menu' name: 'Menu'
cb: @catalogNode cb: @catalogNode

View File

@ -1,6 +1,6 @@
PSAHiding = PSAHiding =
init: -> init: ->
return if !Conf['Announcement Hiding'] return unless Conf['Announcement Hiding']
$.addClass doc, 'hide-announcement' $.addClass doc, 'hide-announcement'
$.one d, '4chanXInitFinished', @setup $.one d, '4chanXInitFinished', @setup
@ -24,7 +24,9 @@ PSAHiding =
PSAHiding.btn = btn = $.el 'span', PSAHiding.btn = btn = $.el 'span',
title: 'Mark announcement as read and hide.' title: 'Mark announcement as read and hide.'
className: 'hide-announcement' className: 'hide-announcement'
$.extend btn, <%= html('[<a href="javascript:;">Dismiss</a>]') %> $.extend btn, <%= html('[<a href="javascript:;">Dismiss</a>]') %>
$.on btn, 'click', PSAHiding.toggle $.on btn, 'click', PSAHiding.toggle
$.get 'hiddenPSA', 0, ({hiddenPSA}) -> $.get 'hiddenPSA', 0, ({hiddenPSA}) ->
@ -33,6 +35,7 @@ PSAHiding =
$.rmClass doc, 'hide-announcement' $.rmClass doc, 'hide-announcement'
$.sync 'hiddenPSA', PSAHiding.sync $.sync 'hiddenPSA', PSAHiding.sync
toggle: (e) -> toggle: (e) ->
if $.hasClass @, 'hide-announcement' if $.hasClass @, 'hide-announcement'
UTC = +$.id('globalMessage').dataset.utc UTC = +$.id('globalMessage').dataset.utc
@ -41,6 +44,7 @@ PSAHiding =
$.event 'CloseMenu' $.event 'CloseMenu'
$.delete 'hiddenPSA' $.delete 'hiddenPSA'
PSAHiding.sync UTC PSAHiding.sync UTC
sync: (UTC) -> sync: (UTC) ->
{psa} = PSAHiding {psa} = PSAHiding
PSAHiding.hidden = PSAHiding.btn.hidden = UTC? and UTC >= +psa.dataset.utc PSAHiding.hidden = PSAHiding.btn.hidden = UTC? and UTC >= +psa.dataset.utc

View File

@ -55,7 +55,7 @@ Banner =
i = Math.floor(Banner.choices.length * Math.random()) i = Math.floor(Banner.choices.length * Math.random())
banner = Banner.choices.splice i, 1 banner = Banner.choices.splice i, 1
$('img', @parentNode).src = "//s.4cdn.org/image/title/#{banner}" $('img', @parentNode).src = "//s.4cdn.org/image/title/#{banner}"
click: (e) -> click: (e) ->
if e.ctrlKey or e.metaKey if e.ctrlKey or e.metaKey
@contentEditable = true @contentEditable = true

View File

@ -2,12 +2,15 @@ CustomCSS =
init: -> init: ->
return unless Conf['Custom CSS'] return unless Conf['Custom CSS']
@addStyle() @addStyle()
addStyle: -> addStyle: ->
@style = $.addStyle Conf['usercss'], 'custom-css', -> $.id 'fourchanx-css' @style = $.addStyle Conf['usercss'], 'custom-css', -> $.id 'fourchanx-css'
rmStyle: -> rmStyle: ->
if @style if @style
$.rm @style $.rm @style
delete @style delete @style
update: -> update: ->
unless @style unless @style
@addStyle() @addStyle()

View File

@ -12,10 +12,13 @@ ExpandComment =
node: -> node: ->
if a = $ '.abbr > a:not([onclick])', @nodes.comment if a = $ '.abbr > a:not([onclick])', @nodes.comment
$.on a, 'click', ExpandComment.cb $.on a, 'click', ExpandComment.cb
callbacks: [] callbacks: []
cb: (e) -> cb: (e) ->
e.preventDefault() e.preventDefault()
ExpandComment.expand Get.postFromNode @ ExpandComment.expand Get.postFromNode @
expand: (post) -> expand: (post) ->
if post.nodes.longComment and !post.nodes.longComment.parentNode if post.nodes.longComment and !post.nodes.longComment.parentNode
$.replace post.nodes.shortComment, post.nodes.longComment $.replace post.nodes.shortComment, post.nodes.longComment
@ -24,12 +27,14 @@ ExpandComment =
return unless a = $ '.abbr > a', post.nodes.comment return unless a = $ '.abbr > a', post.nodes.comment
a.textContent = "Post No.#{post} Loading..." a.textContent = "Post No.#{post} Loading..."
$.cache "//a.4cdn.org#{a.pathname.split('/').splice(0,4).join('/')}.json", -> ExpandComment.parse @, a, post $.cache "//a.4cdn.org#{a.pathname.split('/').splice(0,4).join('/')}.json", -> ExpandComment.parse @, a, post
contract: (post) -> contract: (post) ->
return unless post.nodes.shortComment return unless post.nodes.shortComment
a = $ '.abbr > a', post.nodes.shortComment a = $ '.abbr > a', post.nodes.shortComment
a.textContent = 'here' a.textContent = 'here'
$.replace post.nodes.longComment, post.nodes.shortComment $.replace post.nodes.longComment, post.nodes.shortComment
post.nodes.comment = post.nodes.shortComment post.nodes.comment = post.nodes.shortComment
parse: (req, a, post) -> parse: (req, a, post) ->
{status} = req {status} = req
unless status in [200, 304] unless status in [200, 304]

View File

@ -1,6 +1,6 @@
Fourchan = Fourchan =
init: -> init: ->
return if g.VIEW not in ['index', 'thread'] return unless g.VIEW in ['index', 'thread']
if g.BOARD.ID is 'g' if g.BOARD.ID is 'g'
$.globalEval ''' $.globalEval '''

View File

@ -12,6 +12,8 @@ IDColor =
span = $ '.hand', @nodes.uniqueID span = $ '.hand', @nodes.uniqueID
return unless span and span.nodeName is 'SPAN' return unless span and span.nodeName is 'SPAN'
rgb = IDColor.compute uid rgb = IDColor.compute uid
# Style the damn node.
{style} = span {style} = span
style.color = rgb[3] style.color = rgb[3]
style.backgroundColor = "rgb(#{rgb[0]},#{rgb[1]},#{rgb[2]})" style.backgroundColor = "rgb(#{rgb[0]},#{rgb[1]},#{rgb[2]})"
@ -21,16 +23,24 @@ IDColor =
compute: (uid) -> compute: (uid) ->
return IDColor.ids[uid] if IDColor.ids[uid] return IDColor.ids[uid] if IDColor.ids[uid]
# Convert chars to integers, bitshift and math to create a larger integer
# Create a nice string of binary
hash = IDColor.hash uid hash = IDColor.hash uid
# Convert binary string to numerical values with bitshift and '&' truncation.
rgb = [ rgb = [
(hash >> 24) & 0xFF (hash >> 24) & 0xFF
(hash >> 16) & 0xFF (hash >> 16) & 0xFF
(hash >> 8) & 0xFF (hash >> 8) & 0xFF
] ]
# Weight color luminance values, assign a font color that should be readable.
rgb[3] = if (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 125 rgb[3] = if (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 125
'#000' '#000'
else else
'#fff' '#fff'
# Cache.
@ids[uid] = rgb @ids[uid] = rgb
hash: (uid) -> hash: (uid) ->
@ -38,4 +48,4 @@ IDColor =
i = 0 i = 0
while i < 8 while i < 8
msg = (msg << 5) - msg + uid.charCodeAt i++ msg = (msg << 5) - msg + uid.charCodeAt i++
msg msg

View File

@ -1,6 +1,6 @@
IDHighlight = IDHighlight =
init: -> init: ->
return if g.VIEW not in ['index', 'thread'] return unless g.VIEW in ['index', 'thread']
Post.callbacks.push Post.callbacks.push
name: 'Highlight by User ID' name: 'Highlight by User ID'

View File

@ -56,16 +56,16 @@ Keybinds =
else else
return return
when Conf['Spoiler tags'] when Conf['Spoiler tags']
return if target.nodeName isnt 'TEXTAREA' return unless target.nodeName is 'TEXTAREA'
Keybinds.tags 'spoiler', target Keybinds.tags 'spoiler', target
when Conf['Code tags'] when Conf['Code tags']
return if target.nodeName isnt 'TEXTAREA' return unless target.nodeName is 'TEXTAREA'
Keybinds.tags 'code', target Keybinds.tags 'code', target
when Conf['Eqn tags'] when Conf['Eqn tags']
return if target.nodeName isnt 'TEXTAREA' return unless target.nodeName is 'TEXTAREA'
Keybinds.tags 'eqn', target Keybinds.tags 'eqn', target
when Conf['Math tags'] when Conf['Math tags']
return if target.nodeName isnt 'TEXTAREA' return unless target.nodeName is 'TEXTAREA'
Keybinds.tags 'math', target Keybinds.tags 'math', target
when Conf['Toggle sage'] when Conf['Toggle sage']
return unless QR.nodes and !QR.nodes.el.hidden return unless QR.nodes and !QR.nodes.el.hidden

View File

@ -62,7 +62,9 @@ Nav =
# Add extra space to the end of the page if necessary so that all threads can be selected by keybinds. # Add extra space to the end of the page if necessary so that all threads can be selected by keybinds.
extra = Header.getTopOf(thread) + doc.clientHeight - d.body.getBoundingClientRect().bottom extra = Header.getTopOf(thread) + doc.clientHeight - d.body.getBoundingClientRect().bottom
d.body.style.marginBottom = "#{extra}px" if extra > 0 d.body.style.marginBottom = "#{extra}px" if extra > 0
Header.scrollTo thread Header.scrollTo thread
if extra > 0 and !Nav.haveExtra if extra > 0 and !Nav.haveExtra
Nav.haveExtra = true Nav.haveExtra = true
$.on d, 'scroll', Nav.removeExtra $.on d, 'scroll', Nav.removeExtra

View File

@ -8,9 +8,11 @@ RemoveSpoilers =
Post.callbacks.push Post.callbacks.push
name: 'Reveal Spoilers' name: 'Reveal Spoilers'
cb: @node cb: @node
CatalogThread.callbacks.push CatalogThread.callbacks.push
name: 'Reveal Spoilers' name: 'Reveal Spoilers'
cb: @node cb: @node
if g.VIEW is 'archive' if g.VIEW is 'archive'
$.ready -> RemoveSpoilers.unspoiler $.id 'arc-list' $.ready -> RemoveSpoilers.unspoiler $.id 'arc-list'

View File

@ -5,6 +5,7 @@ Time =
Post.callbacks.push Post.callbacks.push
name: 'Time Formatting' name: 'Time Formatting'
cb: @node cb: @node
node: -> node: ->
return if @isClone return if @isClone
@nodes.date.textContent = Time.format Conf['time'], @info.date @nodes.date.textContent = Time.format Conf['time'], @info.date
@ -14,6 +15,7 @@ Time =
Time.formatters[c].call(date) Time.formatters[c].call(date)
else else
s s
day: [ day: [
'Sunday' 'Sunday'
'Monday' 'Monday'
@ -23,6 +25,7 @@ Time =
'Friday' 'Friday'
'Saturday' 'Saturday'
] ]
month: [ month: [
'January' 'January'
'February' 'February'
@ -37,7 +40,9 @@ Time =
'November' 'November'
'December' 'December'
] ]
zeroPad: (n) -> if n < 10 then "0#{n}" else n zeroPad: (n) -> if n < 10 then "0#{n}" else n
formatters: formatters:
a: -> Time.day[@getDay()][...3] a: -> Time.day[@getDay()][...3]
A: -> Time.day[@getDay()] A: -> Time.day[@getDay()]

View File

@ -18,6 +18,7 @@ ThreadStats =
$.extend sc, statsHTML $.extend sc, statsHTML
$.ready -> $.ready ->
Header.addShortcut sc Header.addShortcut sc
else else
@dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;', @dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;',
<%= html('<div class="move" title="${statsTitle}">&{statsHTML}</div>') %> <%= html('<div class="move" title="${statsTitle}">&{statsHTML}</div>') %>

View File

@ -8,7 +8,7 @@ ThreadUpdater =
$.extend sc, <%= html('<span id="update-status"></span><span id="update-timer" title="Update now"></span>') %> $.extend sc, <%= html('<span id="update-status"></span><span id="update-timer" title="Update now"></span>') %>
$.ready -> $.ready ->
Header.addShortcut sc Header.addShortcut sc
else else
@dialog = sc = UI.dialog 'updater', 'bottom: 0px; left: 0px;', @dialog = sc = UI.dialog 'updater', 'bottom: 0px; left: 0px;',
<%= html('<div class="move"></div><span id="update-status"></span><span id="update-timer" title="Update now"></span>') %> <%= html('<div class="move"></div><span id="update-status"></span><span id="update-timer" title="Update now"></span>') %>
$.addClass doc, 'float' $.addClass doc, 'float'
@ -176,7 +176,7 @@ ThreadUpdater =
setInterval: -> setInterval: ->
i = ThreadUpdater.interval + 1 i = ThreadUpdater.interval + 1
if Conf['Optional Increase'] if Conf['Optional Increase']
# Lower the max refresh rate limit on visible tabs. # Lower the max refresh rate limit on visible tabs.
cur = ThreadUpdater.outdateCount or 1 cur = ThreadUpdater.outdateCount or 1

View File

@ -11,10 +11,10 @@ ThreadWatcher =
@db = new DataBoard 'watchedThreads', @refresh, true @db = new DataBoard 'watchedThreads', @refresh, true
@dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', <%= importHTML('Monitoring/ThreadWatcher') %> @dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', <%= importHTML('Monitoring/ThreadWatcher') %>
@status = $ '#watcher-status', @dialog @status = $ '#watcher-status', @dialog
@list = @dialog.lastElementChild @list = @dialog.lastElementChild
@refreshButton = $ '.move > .refresh', @dialog @refreshButton = $ '.move > .refresh', @dialog
@unreaddb = Unread.db or new DataBoard 'lastReadPosts' @unreaddb = Unread.db or new DataBoard 'lastReadPosts'
$.on d, 'QRPostSuccessful', @cb.post $.on d, 'QRPostSuccessful', @cb.post
@ -23,6 +23,7 @@ ThreadWatcher =
$.on $('.move > .close', @dialog), 'click', @toggleWatcher $.on $('.move > .close', @dialog), 'click', @toggleWatcher
$.on d, '4chanXInitFinished', @ready $.on d, '4chanXInitFinished', @ready
switch g.VIEW switch g.VIEW
when 'index' when 'index'
$.on d, 'IndexRefresh', @cb.onIndexRefresh $.on d, 'IndexRefresh', @cb.onIndexRefresh
@ -147,6 +148,7 @@ ThreadWatcher =
fetchCount: fetchCount:
fetched: 0 fetched: 0
fetching: 0 fetching: 0
fetchAuto: -> fetchAuto: ->
clearTimeout ThreadWatcher.timeout clearTimeout ThreadWatcher.timeout
return unless Conf['Auto Update Thread Watcher'] return unless Conf['Auto Update Thread Watcher']
@ -158,6 +160,7 @@ ThreadWatcher =
ThreadWatcher.fetchAllStatus() ThreadWatcher.fetchAllStatus()
db.save() db.save()
ThreadWatcher.timeout = setTimeout ThreadWatcher.fetchAuto, interval ThreadWatcher.timeout = setTimeout ThreadWatcher.fetchAuto, interval
fetchAllStatus: -> fetchAllStatus: ->
ThreadWatcher.db.forceSync() ThreadWatcher.db.forceSync()
ThreadWatcher.unreaddb.forceSync() ThreadWatcher.unreaddb.forceSync()
@ -166,6 +169,7 @@ ThreadWatcher =
for thread in threads for thread in threads
ThreadWatcher.fetchStatus thread ThreadWatcher.fetchStatus thread
return return
fetchStatus: (thread) -> fetchStatus: (thread) ->
{boardID, threadID, data} = thread {boardID, threadID, data} = thread
return if data.isDead and !Conf['Show Unread Count'] return if data.isDead and !Conf['Show Unread Count']
@ -177,6 +181,7 @@ ThreadWatcher =
$.ajax "//a.4cdn.org/#{boardID}/thread/#{threadID}.json", $.ajax "//a.4cdn.org/#{boardID}/thread/#{threadID}.json",
onloadend: -> onloadend: ->
ThreadWatcher.parseStatus.call @, thread ThreadWatcher.parseStatus.call @, thread
parseStatus: ({boardID, threadID, data}) -> parseStatus: ({boardID, threadID, data}) ->
{fetchCount} = ThreadWatcher {fetchCount} = ThreadWatcher
fetchCount.fetched++ fetchCount.fetched++
@ -233,6 +238,7 @@ ThreadWatcher =
delete data.unread delete data.unread
delete data.quotingYou delete data.quotingYou
ThreadWatcher.db.set {boardID, threadID, val: data} ThreadWatcher.db.set {boardID, threadID, val: data}
ThreadWatcher.refresh() ThreadWatcher.refresh()
getAll: -> getAll: ->
@ -269,8 +275,8 @@ ThreadWatcher =
div = $.el 'div' div = $.el 'div'
fullID = "#{boardID}.#{threadID}" fullID = "#{boardID}.#{threadID}"
div.dataset.fullID = fullID div.dataset.fullID = fullID
$.addClass div, 'current' if g.VIEW is 'thread' and fullID is "#{g.BOARD}.#{g.THREADID}" $.addClass div, 'current' if g.VIEW is 'thread' and fullID is "#{g.BOARD}.#{g.THREADID}"
$.addClass div, 'dead-thread' if data.isDead $.addClass div, 'dead-thread' if data.isDead
if Conf['Show Unread Count'] if Conf['Show Unread Count']
$.addClass div, 'replies-unread' if data.unread $.addClass div, 'replies-unread' if data.unread
$.addClass div, 'replies-quoting-you' if data.quotingYou $.addClass div, 'replies-quoting-you' if data.quotingYou
@ -336,6 +342,7 @@ ThreadWatcher =
ThreadWatcher.rm boardID, threadID ThreadWatcher.rm boardID, threadID
else else
ThreadWatcher.add thread ThreadWatcher.add thread
add: (thread) -> add: (thread) ->
data = {} data = {}
boardID = thread.board.ID boardID = thread.board.ID
@ -350,6 +357,7 @@ ThreadWatcher =
ThreadWatcher.refresh() ThreadWatcher.refresh()
if Conf['Show Unread Count'] if Conf['Show Unread Count']
ThreadWatcher.fetchStatus {boardID, threadID, data} ThreadWatcher.fetchStatus {boardID, threadID, data}
rm: (boardID, threadID) -> rm: (boardID, threadID) ->
ThreadWatcher.db.delete {boardID, threadID} ThreadWatcher.db.delete {boardID, threadID}
ThreadWatcher.refresh() ThreadWatcher.refresh()
@ -423,6 +431,7 @@ ThreadWatcher =
@refreshers.push refresh.bind entry if refresh @refreshers.push refresh.bind entry if refresh
@menu.addEntry entry @menu.addEntry entry
return return
createSubEntry: (name, desc) -> createSubEntry: (name, desc) ->
entry = entry =
type: 'thread watcher' type: 'thread watcher'
@ -430,6 +439,6 @@ ThreadWatcher =
entry.el.title = desc entry.el.title = desc
input = entry.el.firstElementChild input = entry.el.firstElementChild
$.on input, 'change', $.cb.checked $.on input, 'change', $.cb.checked
$.on input, 'change', ThreadWatcher.refresh if name in ['Current Board', 'Show Unread Count'] $.on input, 'change', ThreadWatcher.refresh if name in ['Current Board', 'Show Unread Count']
$.on input, 'change', ThreadWatcher.fetchAuto if name in ['Show Unread Count', 'Auto Update Thread Watcher'] $.on input, 'change', ThreadWatcher.fetchAuto if name in ['Show Unread Count', 'Auto Update Thread Watcher']
entry entry

View File

@ -20,6 +20,7 @@ Unread =
Thread.callbacks.push Thread.callbacks.push
name: 'Unread' name: 'Unread'
cb: @node cb: @node
Post.callbacks.push Post.callbacks.push
name: 'Unread' name: 'Unread'
cb: @addPost cb: @addPost

View File

@ -1,5 +1,5 @@
Captcha.noscript = Captcha.noscript =
lifetime: 2 * $.MINUTE lifetime: 2 * $.MINUTE
iframeURL: '//www.google.com/recaptcha/api/fallback?k=<%= meta.recaptchaKey %>' iframeURL: '//www.google.com/recaptcha/api/fallback?k=<%= meta.recaptchaKey %>'
init: -> init: ->

View File

@ -113,6 +113,7 @@ QR =
return return
if Conf['QR Shortcut'] if Conf['QR Shortcut']
$.rmClass $('.qr-shortcut'), 'disabled' $.rmClass $('.qr-shortcut'), 'disabled'
close: -> close: ->
if QR.req if QR.req
QR.abort() QR.abort()
@ -129,6 +130,7 @@ QR =
QR.cooldown.auto = false QR.cooldown.auto = false
QR.status() QR.status()
QR.captcha.destroy() QR.captcha.destroy()
focus: -> focus: ->
$.queueTask -> $.queueTask ->
return unless QR.nodes return unless QR.nodes
@ -142,18 +144,22 @@ QR =
$.on d, 'scroll', QR.scrollLock $.on d, 'scroll', QR.scrollLock
else else
$.off d, 'scroll', QR.scrollLock $.off d, 'scroll', QR.scrollLock
scrollLock: (e) -> scrollLock: (e) ->
if d.activeElement and QR.nodes.el.contains(d.activeElement) and d.activeElement.nodeName is 'IFRAME' if d.activeElement and QR.nodes.el.contains(d.activeElement) and d.activeElement.nodeName is 'IFRAME'
window.scroll window.scrollX, QR.scrollY window.scroll window.scrollX, QR.scrollY
else else
$.off d, 'scroll', QR.scrollLock $.off d, 'scroll', QR.scrollLock
hide: -> hide: ->
d.activeElement.blur() d.activeElement.blur()
$.addClass QR.nodes.el, 'autohide' $.addClass QR.nodes.el, 'autohide'
QR.nodes.autohide.checked = true QR.nodes.autohide.checked = true
unhide: -> unhide: ->
$.rmClass QR.nodes.el, 'autohide' $.rmClass QR.nodes.el, 'autohide'
QR.nodes.autohide.checked = false QR.nodes.autohide.checked = false
toggleHide: -> toggleHide: ->
if @checked if @checked
QR.hide() QR.hide()
@ -195,6 +201,7 @@ QR =
<% } %> <% } %>
notifications: [] notifications: []
cleanNotifications: -> cleanNotifications: ->
for notification in QR.notifications for notification in QR.notifications
notification.close() notification.close()
@ -449,7 +456,8 @@ QR =
dialog: -> dialog: ->
QR.nodes = nodes = QR.nodes = nodes =
el: dialog = UI.dialog 'qr', 'top: 50px; right: 0px;', <%= importHTML('Features/QuickReply') %> el: dialog = UI.dialog 'qr', 'top: 50px; right: 0px;',
<%= importHTML('Features/QuickReply') %>
setNode = (name, query) -> setNode = (name, query) ->
nodes[name] = $ query, dialog nodes[name] = $ query, dialog
@ -478,7 +486,7 @@ QR =
setNode 'spoilerPar', '#qr-spoiler-label' setNode 'spoilerPar', '#qr-spoiler-label'
setNode 'status', '[type=submit]' setNode 'status', '[type=submit]'
setNode 'fileInput', '[type=file]' setNode 'fileInput', '[type=file]'
rules = $('ul.rules').textContent.trim() rules = $('ul.rules').textContent.trim()
match_min = rules.match(/.+smaller than (\d+)x(\d+).+/) match_min = rules.match(/.+smaller than (\d+)x(\d+).+/)
match_max = rules.match(/.+greater than (\d+)x(\d+).+/) match_max = rules.match(/.+greater than (\d+)x(\d+).+/)
@ -516,7 +524,9 @@ QR =
nodes.spoiler.parentElement.hidden = true nodes.spoiler.parentElement.hidden = true
if g.BOARD.ID is 'f' and g.VIEW isnt 'thread' if g.BOARD.ID is 'f' and g.VIEW isnt 'thread'
nodes.flashTag = $.el 'select', name: 'filetag' nodes.flashTag = $.el 'select',
name: 'filetag'
$.extend nodes.flashTag, <%= html( $.extend nodes.flashTag, <%= html(
'<option value="0">Hentai</option>' + '<option value="0">Hentai</option>' +
'<option value="6">Porn</option>' + '<option value="6">Porn</option>' +
@ -526,6 +536,7 @@ QR =
'<option value="5">Loop</option>' + '<option value="5">Loop</option>' +
'<option value="4" selected>Other</option>' '<option value="4" selected>Other</option>'
) %> ) %>
nodes.flashTag.dataset.default = '4' nodes.flashTag.dataset.default = '4'
$.add nodes.form, nodes.flashTag $.add nodes.form, nodes.flashTag
@ -537,8 +548,8 @@ QR =
$.on nodes.urlButton, 'click', QR.handleUrl $.on nodes.urlButton, 'click', QR.handleUrl
$.on nodes.addPost, 'click', -> new QR.post true $.on nodes.addPost, 'click', -> new QR.post true
$.on nodes.form, 'submit', QR.submit $.on nodes.form, 'submit', QR.submit
$.on nodes.fileRM, 'click', -> QR.selected.rmFile() $.on nodes.fileRM, 'click', -> QR.selected.rmFile()
$.on nodes.fileExtras, 'click', (e) -> e.stopPropagation() $.on nodes.fileExtras, 'click', (e) -> e.stopPropagation()
$.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click() $.on nodes.spoiler, 'change', -> QR.selected.nodes.spoiler.click()
$.on nodes.fileInput, 'change', QR.handleFiles $.on nodes.fileInput, 'change', QR.handleFiles
@ -566,6 +577,7 @@ QR =
QR.status() QR.status()
QR.cooldown.init() QR.cooldown.init()
QR.captcha.init() QR.captcha.init()
$.add d.body, dialog $.add d.body, dialog
QR.captcha.setup() QR.captcha.setup()
@ -798,7 +810,7 @@ QR =
QR.close() QR.close()
else else
post.rm() post.rm()
QR.captcha.setup (d.activeElement is QR.nodes.status) QR.captcha.setup(d.activeElement is QR.nodes.status)
QR.cooldown.add req.uploadEndTime, threadID, postID QR.cooldown.add req.uploadEndTime, threadID, postID
@ -806,6 +818,7 @@ QR =
"#{window.location.origin}/#{g.BOARD}/thread/#{threadID}" "#{window.location.origin}/#{g.BOARD}/thread/#{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
"#{window.location.origin}/#{g.BOARD}/thread/#{threadID}#p#{postID}" "#{window.location.origin}/#{g.BOARD}/thread/#{threadID}#p#{postID}"
if URL if URL
if Conf['Open Post in New Tab'] or postsCount if Conf['Open Post in New Tab'] or postsCount
$.open URL $.open URL

View File

@ -71,6 +71,7 @@ QR.post = class
(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
QR.status() QR.status()
delete: -> delete: ->
$.rm @nodes.el $.rm @nodes.el
URL.revokeObjectURL @URL URL.revokeObjectURL @URL
@ -103,10 +104,13 @@ QR.post = class
load: -> load: ->
# Load this post's values. # Load this post's values.
for name in ['thread', 'name', 'email', 'sub', 'com', 'filename'] for name in ['thread', 'name', 'email', 'sub', 'com', 'filename']
continue unless node = QR.nodes[name] continue unless node = QR.nodes[name]
node.value = @[name] or node.dataset.default or null node.value = @[name] or node.dataset.default or null
(if @thread isnt 'new' then $.addClass else $.rmClass) QR.nodes.el, 'reply-to-thread' (if @thread isnt 'new' then $.addClass else $.rmClass) QR.nodes.el, 'reply-to-thread'
@showFileData() @showFileData()
QR.characterCount() QR.characterCount()

View File

@ -9,6 +9,7 @@ QuoteThreading =
@enabled = true @enabled = true
@controls = $.el 'span', @controls = $.el 'span',
<%= html('<label><input id="threadingControl" type="checkbox" checked> Threading</label>') %> <%= html('<label><input id="threadingControl" type="checkbox" checked> Threading</label>') %>
@threadNewLink = $.el 'span', @threadNewLink = $.el 'span',
className: 'brackets-wrap threadnewlink' className: 'brackets-wrap threadnewlink'
hidden: true hidden: true
@ -27,6 +28,7 @@ QuoteThreading =
Thread.callbacks.push Thread.callbacks.push
name: 'Quote Threading' name: 'Quote Threading'
cb: @setThread cb: @setThread
Post.callbacks.push Post.callbacks.push
name: 'Quote Threading' name: 'Quote Threading'
cb: @node cb: @node

View File

@ -55,7 +55,6 @@ Quotify =
className: 'quotelink deadlink' className: 'quotelink deadlink'
target: '_blank' target: '_blank'
textContent: "#{quote}\u00A0(Dead)" textContent: "#{quote}\u00A0(Dead)"
$.extend a.dataset, {boardID, threadID: post.thread.ID, postID} $.extend a.dataset, {boardID, threadID: post.thread.ID, postID}
else else