Merge branch 'catalog'
This commit is contained in:
commit
c47c02e95d
@ -113,6 +113,8 @@ Filter =
|
||||
|
||||
# Highlight
|
||||
$.addClass @nodes.root, result.class
|
||||
unless @highlights and result.class in @highlights
|
||||
(@highlights or= []).push result.class
|
||||
if !@isReply and result.top
|
||||
@thread.isOnTop = true
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
ThreadHiding =
|
||||
init: ->
|
||||
return if g.VIEW isnt 'index' or !Conf['Thread Hiding Buttons'] and !Conf['Thread Hiding Link']
|
||||
return if g.VIEW isnt 'index' or !Conf['Thread Hiding Buttons'] and !Conf['Thread Hiding Link'] and !Conf['JSON Navigation']
|
||||
|
||||
@db = new DataBoard 'hiddenThreads'
|
||||
@syncCatalog()
|
||||
@ -81,7 +81,7 @@ ThreadHiding =
|
||||
el: div
|
||||
order: 20
|
||||
open: ({thread, isReply}) ->
|
||||
if isReply or thread.isHidden
|
||||
if isReply or thread.isHidden or Conf['JSON Navigation'] and Conf['Index Mode'] is 'catalog'
|
||||
return false
|
||||
ThreadHiding.menu.thread = thread
|
||||
true
|
||||
@ -188,6 +188,7 @@ ThreadHiding =
|
||||
return if thread.isHidden
|
||||
threadRoot = thread.OP.nodes.root.parentNode
|
||||
thread.isHidden = true
|
||||
Index.updateHideLabel() if Conf['JSON Navigation']
|
||||
|
||||
return threadRoot.hidden = true unless makeStub
|
||||
|
||||
@ -199,3 +200,4 @@ ThreadHiding =
|
||||
delete thread.stub
|
||||
threadRoot = thread.OP.nodes.root.parentNode
|
||||
threadRoot.hidden = thread.isHidden = false
|
||||
Index.updateHideLabel() if Conf['JSON Navigation']
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
Build =
|
||||
staticPath: '//s.4cdn.org/image/'
|
||||
gifIcon: if window.devicePixelRatio >= 2 then '@2x.gif' else '.gif'
|
||||
initPixelRatio: window.devicePixelRatio
|
||||
spoilerRange: {}
|
||||
unescape: (text) ->
|
||||
@ -289,11 +291,91 @@ Build =
|
||||
nodes = [if OP then OP.nodes.root else Build.postFromObject data, board.ID]
|
||||
if data.omitted_posts or !Conf['Show Replies'] and data.replies
|
||||
[posts, files] = if Conf['Show Replies']
|
||||
[data.omitted_posts, data.omitted_images]
|
||||
# XXX data.omitted_images is not accurate.
|
||||
[data.omitted_posts, data.images - data.last_replies.filter((data) -> !!data.ext).length]
|
||||
else
|
||||
# XXX data.images is not accurate.
|
||||
[data.replies, data.omitted_images + data.last_replies.filter((data) -> !!data.ext).length]
|
||||
[data.replies, data.images]
|
||||
nodes.push Build.summary board.ID, data.no, posts, files
|
||||
nodes
|
||||
|
||||
fullThread: (board, data) -> Build.postFromObject data, board.ID
|
||||
|
||||
catalogThread: (thread) ->
|
||||
{staticPath, gifIcon} = Build
|
||||
data = Index.liveThreadData[Index.liveThreadIDs.indexOf thread.ID]
|
||||
|
||||
if data.spoiler and !Conf['Reveal Spoiler Thumbnails']
|
||||
src = "#{staticPath}spoiler"
|
||||
if spoilerRange = Build.spoilerRange[thread.board]
|
||||
# Randomize the spoiler image.
|
||||
src += "-#{thread.board}" + Math.floor 1 + spoilerRange * Math.random()
|
||||
src += '.png'
|
||||
imgClass = 'spoiler-file'
|
||||
else if data.filedeleted
|
||||
src = "#{staticPath}filedeleted-res#{gifIcon}"
|
||||
imgClass = 'deleted-file'
|
||||
else if thread.OP.file
|
||||
src = thread.OP.file.thumbURL
|
||||
max = Math.max data.tn_w, data.tn_h
|
||||
imgWidth = data.tn_w * 150 / max
|
||||
imgHeight = data.tn_h * 150 / max
|
||||
else
|
||||
src = "#{staticPath}nofile.png"
|
||||
imgClass = 'no-file'
|
||||
|
||||
thumb = if imgClass
|
||||
<%= html('<img src="${src}" class="catalog-thumb ${imgClass}">') %>
|
||||
else
|
||||
<%= html('<img src="${src}" class="catalog-thumb" width="${imgWidth}" height="${imgHeight}">') %>
|
||||
|
||||
postCount = data.replies + 1
|
||||
fileCount = data.images + !!data.ext
|
||||
pageCount = Index.liveThreadIDs.indexOf(thread.ID) // Index.threadsNumPerPage + 1
|
||||
|
||||
subject = if thread.OP.info.subject
|
||||
<%= html('<div class="subject">${thread.OP.info.subject}</div>') %>
|
||||
else
|
||||
<%= html('') %>
|
||||
|
||||
root = $.el 'div',
|
||||
className: 'catalog-thread'
|
||||
$.extend root, <%= html(
|
||||
'<a href="/${thread.board}/thread/${thread.ID}">' +
|
||||
'&{thumb}' +
|
||||
'</a>' +
|
||||
'<div class="catalog-stats" title="Post count / File count / Page count">' +
|
||||
'<span class="post-count">${postCount}</span> / <span class="file-count">${fileCount}</span> / <span class="page-count">${pageCount}</span>' +
|
||||
'<span class="catalog-icons"></span>' +
|
||||
'</div>' +
|
||||
'&{subject}' +
|
||||
'<div class="comment">&{thread.OP.info.commentHTML}</div>'
|
||||
) %>
|
||||
|
||||
root.dataset.fullID = thread.fullID
|
||||
$.addClass root, 'pinned' if thread.isPinned
|
||||
$.addClass root, thread.OP.highlights... if thread.OP.highlights
|
||||
|
||||
for quotelink in $$ '.quotelink, .deadlink', root.lastElementChild
|
||||
$.replace quotelink, [quotelink.childNodes...]
|
||||
for pp in $$ '.prettyprint', root.lastElementChild
|
||||
$.replace pp, $.tn pp.textContent
|
||||
for br in $$ 'br', root.lastElementChild when !br.previousSibling or br.previousSibling.nodeName is 'BR'
|
||||
$.rm br
|
||||
|
||||
if thread.isSticky
|
||||
$.add $('.catalog-icons', root), $.el 'img',
|
||||
src: "#{staticPath}sticky#{gifIcon}"
|
||||
className: 'stickyIcon'
|
||||
title: 'Sticky'
|
||||
if thread.isClosed
|
||||
$.add $('.catalog-icons', root), $.el 'img',
|
||||
src: "#{staticPath}closed#{gifIcon}"
|
||||
className: 'closedIcon'
|
||||
title: 'Closed'
|
||||
|
||||
if data.bumplimit
|
||||
$.addClass $('.post-count', root), 'warning'
|
||||
if data.imagelimit
|
||||
$.addClass $('.file-count', root), 'warning'
|
||||
|
||||
root
|
||||
|
||||
@ -5,6 +5,10 @@ Config =
|
||||
true
|
||||
'Replace the board index with a dynamically generated one supporting searching, sorting, and infinite scrolling.'
|
||||
]
|
||||
'Use 4chan X Catalog': [
|
||||
false
|
||||
'Link to 4chan X\'s catalog instead of the native 4chan one.'
|
||||
]
|
||||
'Catalog Links': [
|
||||
true
|
||||
'Add toggle link in header menu to turn Navigation links into links to each board\'s catalog.'
|
||||
@ -553,6 +557,7 @@ http://iqdb.org/?url=%TURL
|
||||
|
||||
Index:
|
||||
'Index Mode': 'paged'
|
||||
'Previous Index Mode': 'paged'
|
||||
'Index Sort': 'bump'
|
||||
'Show Replies': true
|
||||
'Anchor Hidden Threads': true
|
||||
|
||||
@ -197,7 +197,7 @@ Header =
|
||||
if Conf['External Catalog']
|
||||
a.href = CatalogLinks.external board
|
||||
else
|
||||
a.href += 'catalog'
|
||||
a.href += if Conf['JSON Navigation'] and Conf['Use 4chan X Catalog'] then '#catalog' else 'catalog'
|
||||
$.addClass a, 'catalog'
|
||||
|
||||
$.addClass a, 'navSmall' if board is '@'
|
||||
|
||||
@ -1,9 +1,27 @@
|
||||
Index =
|
||||
showHiddenThreads: false
|
||||
init: ->
|
||||
return if g.BOARD.ID is 'f' or g.VIEW isnt 'index' or !Conf['JSON Navigation']
|
||||
return if g.BOARD.ID is 'f' or !Conf['JSON Navigation']
|
||||
if g.VIEW is 'thread' and Conf['Use 4chan X Catalog']
|
||||
$.ready ->
|
||||
for link in $$ '.navLinks.desktop a' when link.pathname is "/#{g.BOARD}/catalog"
|
||||
link.href = "/#{g.BOARD}/#catalog"
|
||||
return if g.VIEW isnt 'index'
|
||||
|
||||
@board = "#{g.BOARD}"
|
||||
|
||||
@db = new DataBoard 'pinnedThreads'
|
||||
Thread.callbacks.push
|
||||
name: 'Thread Pinning'
|
||||
cb: @threadNode
|
||||
CatalogThread.callbacks.push
|
||||
name: 'Catalog Features'
|
||||
cb: @catalogNode
|
||||
|
||||
if Conf['Use 4chan X Catalog'] and Conf['Index Mode'] is 'catalog'
|
||||
Index.setMode Conf['Previous Index Mode']
|
||||
@cb.popstate()
|
||||
|
||||
@button = $.el 'a',
|
||||
className: 'index-refresh-shortcut fa fa-refresh'
|
||||
title: 'Refresh'
|
||||
@ -18,28 +36,17 @@ Index =
|
||||
{ el: $.el 'label', <%= html('<input type="radio" name="Index Mode" value="paged"> Paged') %> }
|
||||
{ el: $.el 'label', <%= html('<input type="radio" name="Index Mode" value="infinite"> Infinite scrolling') %> }
|
||||
{ el: $.el 'label', <%= html('<input type="radio" name="Index Mode" value="all pages"> All threads') %> }
|
||||
{ el: $.el 'label', <%= html('<input type="radio" name="Index Mode" value="catalog"> Catalog') %> }
|
||||
]
|
||||
open: ->
|
||||
for label in @subEntries
|
||||
input = label.el.firstChild
|
||||
input.checked = Conf['Index Mode'] is input.value
|
||||
true
|
||||
for label in modeEntry.subEntries
|
||||
input = label.el.firstChild
|
||||
input.checked = Conf['Index Mode'] is input.value
|
||||
$.on input, 'change', $.cb.value
|
||||
$.on input, 'change', @cb.mode
|
||||
|
||||
sortEntry =
|
||||
el: $.el 'span', textContent: 'Sort by'
|
||||
subEntries: [
|
||||
{ el: $.el 'label', <%= html('<input type="radio" name="Index Sort" value="bump"> Bump order') %> }
|
||||
{ el: $.el 'label', <%= html('<input type="radio" name="Index Sort" value="lastreply"> Last reply') %> }
|
||||
{ el: $.el 'label', <%= html('<input type="radio" name="Index Sort" value="birth"> Creation date') %> }
|
||||
{ el: $.el 'label', <%= html('<input type="radio" name="Index Sort" value="replycount"> Reply count') %> }
|
||||
{ el: $.el 'label', <%= html('<input type="radio" name="Index Sort" value="filecount"> File count') %> }
|
||||
]
|
||||
for label in sortEntry.subEntries
|
||||
input = label.el.firstChild
|
||||
input.checked = Conf['Index Sort'] is input.value
|
||||
$.on input, 'change', $.cb.value
|
||||
$.on input, 'change', @cb.sort
|
||||
|
||||
repliesEntry = el: UI.checkbox 'Show Replies', ' Show replies'
|
||||
anchorEntry = el: UI.checkbox 'Anchor Hidden Threads', ' Anchor hidden threads'
|
||||
refNavEntry = el: UI.checkbox 'Refreshed Navigation', ' Refreshed navigation'
|
||||
@ -59,20 +66,20 @@ Index =
|
||||
el: $.el 'span',
|
||||
textContent: 'Index Navigation'
|
||||
order: 98
|
||||
subEntries: [repliesEntry, anchorEntry, refNavEntry, modeEntry, sortEntry]
|
||||
subEntries: [repliesEntry, anchorEntry, refNavEntry, modeEntry]
|
||||
|
||||
$.addClass doc, 'index-loading'
|
||||
@root = $.el 'div', className: 'board'
|
||||
@pagelist = $.el 'div',
|
||||
className: 'pagelist'
|
||||
hidden: true
|
||||
$.addClass doc, 'index-loading', "#{Conf['Index Mode'].replace /\ /g, '-'}-mode"
|
||||
@root = $.el 'div', className: 'board'
|
||||
@pagelist = $.el 'div', className: 'pagelist'
|
||||
$.extend @pagelist, <%= importHTML('Features/Index-pagelist') %>
|
||||
@navLinks = $.el 'div',
|
||||
className: 'navLinks'
|
||||
$('.cataloglink a', @pagelist).href = if Conf['Use 4chan X Catalog'] then '#catalog' else "/#{g.BOARD}/catalog"
|
||||
@navLinks = $.el 'div', className: 'navLinks'
|
||||
$.extend @navLinks, <%= importHTML('Features/Index-navlinks') %>
|
||||
$('.returnlink a', @navLinks).href = "//boards.4chan.org/#{g.BOARD}/"
|
||||
$('.cataloglink a', @navLinks).href = "//boards.4chan.org/#{g.BOARD}/catalog"
|
||||
$('.returnlink a', @navLinks).href = if Conf['Use 4chan X Catalog'] then '#index' else "/#{g.BOARD}/"
|
||||
$('.cataloglink a', @navLinks).href = if Conf['Use 4chan X Catalog'] then '#catalog' else "/#{g.BOARD}/catalog"
|
||||
@searchInput = $ '#index-search', @navLinks
|
||||
@hideLabel = $ '#hidden-label', @navLinks
|
||||
@selectSort = $ '#index-sort', @navLinks
|
||||
@currentPage = @getCurrentPage()
|
||||
$.on window, 'popstate', @cb.popstate
|
||||
|
||||
@ -80,6 +87,10 @@ Index =
|
||||
$.on @pagelist, 'click', @cb.pageNav
|
||||
$.on @searchInput, 'input', @onSearchInput
|
||||
$.on $('#index-search-clear', @navLinks), 'click', @clearSearch
|
||||
$.on $('#hidden-toggle a', @navLinks), 'click', @cb.toggleHiddenThreads
|
||||
@selectSort.value = Conf[@selectSort.name]
|
||||
$.on @selectSort, 'change', $.cb.value
|
||||
$.on @selectSort, 'change', @cb.sort
|
||||
|
||||
@update()
|
||||
$.asap (-> $('.board', doc) or d.readyState isnt 'loading'), ->
|
||||
@ -129,10 +140,93 @@ Index =
|
||||
new Notice 'info', "Last page reached.", 2
|
||||
setTimeout reset, 3 * $.SECOND
|
||||
|
||||
menu:
|
||||
init: ->
|
||||
return if g.VIEW isnt 'index' or !Conf['JSON Navigation'] or !Conf['Menu'] or g.BOARD.ID is 'f'
|
||||
|
||||
Menu.menu.addEntry
|
||||
el: $.el 'a', href: 'javascript:;'
|
||||
order: 5
|
||||
open: ({thread}) ->
|
||||
return false if Conf['Index Mode'] isnt 'catalog'
|
||||
@el.textContent = if thread.isHidden
|
||||
'Unhide thread'
|
||||
else
|
||||
'Hide thread'
|
||||
$.off @el, 'click', @cb if @cb
|
||||
@cb = ->
|
||||
$.event 'CloseMenu'
|
||||
Index.toggleHide thread
|
||||
$.on @el, 'click', @cb
|
||||
true
|
||||
|
||||
Menu.menu.addEntry
|
||||
el: $.el 'a', href: 'javascript:;'
|
||||
order: 6
|
||||
open: ({thread}) ->
|
||||
return false if Conf['Index Mode'] isnt 'catalog'
|
||||
@el.textContent = if thread.isPinned
|
||||
'Unpin thread'
|
||||
else
|
||||
'Pin thread'
|
||||
$.off @el, 'click', @cb if @cb
|
||||
@cb = ->
|
||||
$.event 'CloseMenu'
|
||||
Index.togglePin thread
|
||||
$.on @el, 'click', @cb
|
||||
true
|
||||
|
||||
threadNode: ->
|
||||
return unless Index.db.get {boardID: @board.ID, threadID: @ID}
|
||||
@pin()
|
||||
catalogNode: ->
|
||||
$.on @nodes.thumb.parentNode, 'click', Index.onClick
|
||||
onClick: (e) ->
|
||||
return if e.button isnt 0
|
||||
thread = g.threads[@parentNode.dataset.fullID]
|
||||
if e.shiftKey
|
||||
Index.toggleHide thread
|
||||
else if e.altKey
|
||||
Index.togglePin thread
|
||||
else
|
||||
return
|
||||
e.preventDefault()
|
||||
toggleHide: (thread) ->
|
||||
$.rm thread.catalogView.nodes.root
|
||||
if Index.showHiddenThreads
|
||||
ThreadHiding.show thread
|
||||
return unless ThreadHiding.db.get {boardID: thread.board.ID, threadID: thread.ID}
|
||||
# Don't save when un-hiding filtered threads.
|
||||
else
|
||||
ThreadHiding.hide thread
|
||||
ThreadHiding.saveHiddenState thread
|
||||
togglePin: (thread) ->
|
||||
data =
|
||||
boardID: thread.board.ID
|
||||
threadID: thread.ID
|
||||
if thread.isPinned
|
||||
thread.unpin()
|
||||
Index.db.delete data
|
||||
else
|
||||
thread.pin()
|
||||
data.val = true
|
||||
Index.db.set data
|
||||
Index.sort()
|
||||
Index.buildIndex()
|
||||
|
||||
cb:
|
||||
mode: ->
|
||||
Index.togglePagelist()
|
||||
toggleHiddenThreads: ->
|
||||
$('#hidden-toggle a', Index.navLinks).textContent = if Index.showHiddenThreads = !Index.showHiddenThreads
|
||||
'Hide'
|
||||
else
|
||||
'Show'
|
||||
Index.sort()
|
||||
Index.buildIndex()
|
||||
mode: ->
|
||||
Index.setMode @value
|
||||
Index.pushState Conf['Index Mode'], Index.currentPage
|
||||
Index.buildIndex()
|
||||
Index.setPage()
|
||||
sort: ->
|
||||
Index.sort()
|
||||
Index.buildIndex()
|
||||
@ -140,9 +234,31 @@ Index =
|
||||
Index.buildThreads()
|
||||
Index.sort()
|
||||
Index.buildIndex()
|
||||
hashchange: (e) ->
|
||||
switch command = location.hash[1..]
|
||||
when 'paged', 'infinite', 'all-pages', 'catalog'
|
||||
mode = command.replace /-/g, ' '
|
||||
when 'index'
|
||||
mode = Conf['Previous Index Mode']
|
||||
if mode
|
||||
Index.setMode mode
|
||||
history.replaceState {mode}, '', if Index.currentPage is 1 then './' else Index.currentPage
|
||||
if e
|
||||
# hash change, not call from init
|
||||
Index.buildIndex()
|
||||
Index.setPage()
|
||||
return
|
||||
history.replaceState {mode: Conf['Index Mode']}, ''
|
||||
popstate: (e) ->
|
||||
unless e?.state
|
||||
# page load or hash change
|
||||
return Index.cb.hashchange.call @, e
|
||||
{mode} = e.state
|
||||
pageNum = Index.getCurrentPage()
|
||||
Index.pageLoad pageNum if Index.currentPage isnt pageNum
|
||||
unless Conf['Index Mode'] is mode and Index.currentPage is pageNum
|
||||
Index.setMode mode
|
||||
Index.buildIndex()
|
||||
Index.setPage()
|
||||
pageNav: (e) ->
|
||||
return if e.shiftKey or e.altKey or e.ctrlKey or e.metaKey or e.button isnt 0
|
||||
switch e.target.nodeName
|
||||
@ -158,23 +274,36 @@ Index =
|
||||
Index.userPageNav +a.pathname.split('/')[2] or 1
|
||||
|
||||
scrollToIndex: ->
|
||||
Header.scrollToIfNeeded Index.root
|
||||
Header.scrollToIfNeeded Index.navLinks
|
||||
|
||||
getCurrentPage: ->
|
||||
+window.location.pathname.split('/')[2] or 1
|
||||
if Conf['Index Mode'] in ['all pages', 'catalog']
|
||||
1
|
||||
else
|
||||
+window.location.pathname.split('/')[2] or 1
|
||||
userPageNav: (pageNum) ->
|
||||
history.pushState null, '', if pageNum is 1 then './' else pageNum
|
||||
if Conf['Refreshed Navigation'] and Conf['Index Mode'] isnt 'all pages'
|
||||
Index.pushState Conf['Index Mode'], pageNum
|
||||
if Conf['Refreshed Navigation']
|
||||
Index.update pageNum
|
||||
else
|
||||
return if Index.currentPage is pageNum
|
||||
Index.pageLoad pageNum
|
||||
pushState: (mode, pageNum) ->
|
||||
history.pushState {mode}, '', if pageNum is 1 then './' else pageNum
|
||||
pageLoad: (pageNum) ->
|
||||
Index.currentPage = pageNum
|
||||
return if Conf['Index Mode'] is 'all pages'
|
||||
Index.buildIndex()
|
||||
Index.setPage()
|
||||
Index.scrollToIndex()
|
||||
setMode: (mode) ->
|
||||
$.rmClass doc, "#{Conf['Index Mode'].replace /\ /g, '-'}-mode"
|
||||
$.addClass doc, "#{mode.replace /\ /g, '-'}-mode"
|
||||
Conf['Index Mode'] = mode
|
||||
$.set 'Index Mode', mode
|
||||
Index.currentPage = Index.getCurrentPage()
|
||||
if mode not in ['catalog', Conf['Previous Index Mode']]
|
||||
Conf['Previous Index Mode'] = mode
|
||||
$.set 'Previous Index Mode', mode
|
||||
|
||||
getPagesNum: ->
|
||||
if Index.isSearching
|
||||
@ -183,8 +312,6 @@ Index =
|
||||
Index.pagesNum
|
||||
getMaxPageNum: ->
|
||||
Math.max 1, Index.getPagesNum()
|
||||
togglePagelist: ->
|
||||
Index.pagelist.hidden = Conf['Index Mode'] isnt 'paged'
|
||||
buildPagelist: ->
|
||||
pagesRoot = $ '.pages', Index.pagelist
|
||||
maxPageNum = Index.getMaxPageNum()
|
||||
@ -197,7 +324,6 @@ Index =
|
||||
nodes.push $.tn('['), a, $.tn '] '
|
||||
$.rmAll pagesRoot
|
||||
$.add pagesRoot, nodes
|
||||
Index.togglePagelist()
|
||||
setPage: (pageNum) ->
|
||||
pageNum or= Index.getCurrentPage()
|
||||
maxPageNum = Index.getMaxPageNum()
|
||||
@ -221,7 +347,21 @@ Index =
|
||||
$.before a, strong
|
||||
$.add strong, a
|
||||
|
||||
update: (pageNum, forceReparse) ->
|
||||
updateHideLabel: ->
|
||||
hiddenCount = 0
|
||||
for threadID, thread of g.BOARD.threads when thread.isHidden
|
||||
hiddenCount++ if thread.ID in Index.liveThreadIDs
|
||||
unless hiddenCount
|
||||
Index.hideLabel.hidden = true
|
||||
Index.cb.toggleHiddenThreads() if Index.showHiddenThreads
|
||||
return
|
||||
Index.hideLabel.hidden = false
|
||||
$('#hidden-count', Index.navLinks).textContent = if hiddenCount is 1
|
||||
'1 hidden thread'
|
||||
else
|
||||
"#{hiddenCount} hidden threads"
|
||||
|
||||
update: (pageNum) ->
|
||||
return unless navigator.onLine
|
||||
delete Index.pageNum
|
||||
Index.req?.abort()
|
||||
@ -241,7 +381,7 @@ Index =
|
||||
onabort: onload
|
||||
onloadend: onload
|
||||
,
|
||||
whenModified: !forceReparse
|
||||
whenModified: true
|
||||
$.addClass Index.button, 'fa-spin'
|
||||
|
||||
load: (e, pageNum) ->
|
||||
@ -317,6 +457,8 @@ Index =
|
||||
try
|
||||
threadRoot = Build.thread g.BOARD, threadData
|
||||
if thread = g.BOARD.threads[threadData.no]
|
||||
thread.setCount 'post', threadData.replies + 1, threadData.bumplimit
|
||||
thread.setCount 'file', threadData.images + !!threadData.ext, threadData.imagelimit
|
||||
thread.setStatus 'Sticky', !!threadData.sticky
|
||||
thread.setStatus 'Closed', !!threadData.closed
|
||||
else
|
||||
@ -338,6 +480,7 @@ Index =
|
||||
$.nodes Index.nodes
|
||||
Main.callbackNodes Thread, threads
|
||||
Main.callbackNodes Post, posts
|
||||
Index.updateHideLabel()
|
||||
$.event 'IndexRefresh'
|
||||
|
||||
buildReplies: (threadRoots) ->
|
||||
@ -365,6 +508,16 @@ Index =
|
||||
Main.handleErrors errors if errors
|
||||
Main.callbackNodes Post, posts
|
||||
|
||||
buildCatalogViews: ->
|
||||
threads = Index.sortedNodes
|
||||
.map((threadRoot) -> Get.threadFromRoot threadRoot)
|
||||
.filter (thread) -> !thread.isHidden isnt Index.showHiddenThreads
|
||||
catalogThreads = []
|
||||
for thread in threads when !thread.catalogView
|
||||
catalogThreads.push new CatalogThread Build.catalogThread(thread), thread
|
||||
Main.callbackNodes CatalogThread, catalogThreads
|
||||
threads.map (thread) -> thread.catalogView.nodes.root
|
||||
|
||||
sort: ->
|
||||
{liveThreadIDs, liveThreadData} = Index
|
||||
sortedThreadIDs = {
|
||||
@ -388,7 +541,7 @@ Index =
|
||||
# Sticky threads
|
||||
Index.sortOnTop (thread) -> thread.isSticky
|
||||
# Highlighted threads
|
||||
Index.sortOnTop((thread) -> thread.isOnTop) if Conf['Filter']
|
||||
Index.sortOnTop (thread) -> thread.isOnTop or thread.isPinned
|
||||
# Non-hidden threads
|
||||
Index.sortOnTop((thread) -> !thread.isHidden) if Conf['Anchor Hidden Threads']
|
||||
|
||||
@ -400,14 +553,20 @@ Index =
|
||||
Index.sortedNodes = topNodes.concat(bottomNodes)
|
||||
|
||||
buildIndex: ->
|
||||
if Conf['Index Mode'] isnt 'all pages'
|
||||
nodes = Index.buildSinglePage Index.getCurrentPage()
|
||||
else
|
||||
nodes = Index.sortedNodes
|
||||
switch Conf['Index Mode']
|
||||
when 'all pages'
|
||||
nodes = Index.sortedNodes
|
||||
when 'catalog'
|
||||
nodes = Index.buildCatalogViews()
|
||||
else
|
||||
nodes = Index.buildSinglePage Index.getCurrentPage()
|
||||
$.rmAll Index.root
|
||||
$.rmAll Header.hover
|
||||
Index.buildReplies nodes if Conf['Show Replies']
|
||||
Index.buildStructure nodes
|
||||
if Conf['Index Mode'] is 'catalog'
|
||||
$.add Index.root, nodes
|
||||
else
|
||||
Index.buildReplies nodes if Conf['Show Replies']
|
||||
Index.buildStructure nodes
|
||||
|
||||
buildSinglePage: (pageNum) ->
|
||||
nodesPerPage = Index.threadsNumPerPage
|
||||
@ -439,12 +598,8 @@ Index =
|
||||
return unless Index.searchInput.dataset.searching
|
||||
pageNum = Index.pageBeforeSearch
|
||||
delete Index.pageBeforeSearch
|
||||
<% if (type === 'userscript') { %>
|
||||
# XXX https://github.com/greasemonkey/greasemonkey/issues/1571
|
||||
Index.searchInput.removeAttribute 'data-searching'
|
||||
<% } else { %>
|
||||
delete Index.searchInput.dataset.searching
|
||||
<% } %>
|
||||
Index.sort()
|
||||
# Go to the last available page if we were past the limit.
|
||||
pageNum = Math.min pageNum, Index.getMaxPageNum() if Conf['Index Mode'] isnt 'all pages'
|
||||
@ -453,7 +608,7 @@ Index =
|
||||
Index.buildIndex()
|
||||
Index.setPage()
|
||||
else
|
||||
history.pushState null, '', if pageNum is 1 then './' else pageNum
|
||||
Index.pushState Conf['Index Mode'], pageNum
|
||||
Index.pageLoad pageNum
|
||||
|
||||
querySearch: (query) ->
|
||||
|
||||
@ -293,6 +293,7 @@ Main =
|
||||
['Strike-through Quotes', QuoteStrikeThrough]
|
||||
['Quick Reply', QR]
|
||||
['Menu', Menu]
|
||||
['Index Generator (Menu)', Index.menu]
|
||||
['Report Link', ReportLink]
|
||||
['Thread Hiding (Menu)', ThreadHiding.menu]
|
||||
['Reply Hiding (Menu)', PostHiding.menu]
|
||||
|
||||
@ -471,7 +471,11 @@ hr + div.center:not(.ad-cnt):not(.topad):not(.middlead):not(.bottomad) {
|
||||
/* Index */
|
||||
:root.index-loading .navLinks,
|
||||
:root.index-loading .board,
|
||||
:root.index-loading .pagelist {
|
||||
:root.index-loading .pagelist,
|
||||
:root.infinite-mode .pagelist,
|
||||
:root.all-pages-mode .pagelist,
|
||||
:root.catalog-mode .pagelist,
|
||||
:root:not(.catalog-mode) #hidden-label {
|
||||
display: none;
|
||||
}
|
||||
#index-search {
|
||||
@ -485,7 +489,10 @@ hr + div.center:not(.ad-cnt):not(.topad):not(.middlead):not(.bottomad) {
|
||||
}
|
||||
#index-search-clear {
|
||||
color: gray;
|
||||
margin-left: -1em;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: -1em;
|
||||
width: 0;
|
||||
}
|
||||
<% if (type === 'crx') { %>
|
||||
/* ``::-webkit-*'' selectors break selector lists on Firefox. */
|
||||
@ -494,10 +501,101 @@ hr + div.center:not(.ad-cnt):not(.topad):not(.middlead):not(.bottomad) {
|
||||
#index-search:not([data-searching]) + #index-search-clear {
|
||||
display: none;
|
||||
}
|
||||
#index-sort {
|
||||
float: right;
|
||||
}
|
||||
.summary {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Catalog */
|
||||
:root.catalog-mode .board {
|
||||
text-align: center;
|
||||
}
|
||||
.catalog-thread {
|
||||
display: -webkit-inline-flex;
|
||||
display: inline-flex;
|
||||
text-align: left;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
width: 165px;
|
||||
margin: 0 2px 5px;
|
||||
max-height: 320px;
|
||||
word-wrap: break-word;
|
||||
vertical-align: top;
|
||||
}
|
||||
.catalog-thread > a {
|
||||
flex-shrink: 0;
|
||||
-webkit-flex-shrink: 0;
|
||||
position: relative;
|
||||
}
|
||||
.catalog-thumb {
|
||||
max-width: 150px;
|
||||
max-height: 150px;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, .25);
|
||||
}
|
||||
.catalog-thumb:not(.deleted-file):not(.no-file) {
|
||||
min-width: 30px;
|
||||
min-height: 30px;
|
||||
}
|
||||
.catalog-thumb.spoiler-file {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
.catalog-thumb.deleted-file {
|
||||
width: 127px;
|
||||
height: 13px;
|
||||
padding: 20px 11px;
|
||||
}
|
||||
.catalog-thumb.no-file {
|
||||
width: 77px;
|
||||
height: 13px;
|
||||
padding: 20px 36px;
|
||||
}
|
||||
.catalog-icons > img,
|
||||
.catalog-stats > .menu-button {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin: 0;
|
||||
vertical-align: text-top;
|
||||
padding-left: 2px;
|
||||
}
|
||||
.catalog-stats > .menu-button {
|
||||
text-align: center;
|
||||
font-weight: normal;
|
||||
}
|
||||
.catalog-stats > .menu-button > i::before {
|
||||
line-height: 11px;
|
||||
}
|
||||
.catalog-stats {
|
||||
-webkit-flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
cursor: help;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.catalog-thread > .subject {
|
||||
-webkit-flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
-webkit-align-self: stretch;
|
||||
align-self: stretch;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
}
|
||||
.catalog-thread > .comment {
|
||||
-webkit-flex-shrink: 1;
|
||||
flex-shrink: 1;
|
||||
-webkit-align-self: stretch;
|
||||
align-self: stretch;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Announcement Hiding */
|
||||
:root.hide-announcement #globalMessage {
|
||||
display: none;
|
||||
@ -752,9 +850,21 @@ span.hide-announcement {
|
||||
display: none;
|
||||
}
|
||||
/* Werk Tyme */
|
||||
:root.werkTyme .postContainer:not(.noFile) .fileThumb {
|
||||
:root.werkTyme .postContainer:not(.noFile) .fileThumb,
|
||||
:root.werkTyme .catalog-thumb:not(.deleted-file):not(.no-file),
|
||||
:root:not(.werkTyme) .werkTyme-filename {
|
||||
display: none;
|
||||
}
|
||||
.werkTyme-filename {
|
||||
font-weight: bold;
|
||||
}
|
||||
:root.werkTyme .catalog-thread > a {
|
||||
text-align: center;
|
||||
}
|
||||
.pinned .werkTyme-filename,
|
||||
.filter-highlight .werkTyme-filename {
|
||||
border: 2px solid rgba(255, 0, 0, .5);
|
||||
}
|
||||
|
||||
/* Index/Reply Navigation */
|
||||
#navlinks {
|
||||
@ -762,6 +872,9 @@ span.hide-announcement {
|
||||
top: 25px;
|
||||
right: 10px;
|
||||
}
|
||||
:root.catalog-mode #navlinks {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Filter */
|
||||
.opContainer.filter-highlight {
|
||||
@ -770,6 +883,10 @@ span.hide-announcement {
|
||||
.filter-highlight > .reply {
|
||||
box-shadow: -5px 0 rgba(255, 0, 0, .5);
|
||||
}
|
||||
.pinned .catalog-thumb,
|
||||
.filter-highlight .catalog-thumb {
|
||||
border: 2px solid rgba(255, 0, 0, .5);
|
||||
}
|
||||
|
||||
/* Spoiler text */
|
||||
:root.reveal-spoilers s {
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
<span class="brackets-wrap returnlink"><a href="javascript:;">Return</a></span>
|
||||
<span class="brackets-wrap cataloglink"><a href="javascript:;">Catalog</a></span>
|
||||
<span class="brackets-wrap returnlink"><a href="./">Return</a></span>
|
||||
<span class="brackets-wrap cataloglink"><a href="./catalog">Catalog</a></span>
|
||||
<span class="brackets-wrap bottomlink"><a href="#bottom">Bottom</a></span>
|
||||
<span class="brackets-wrap" id="index-last-refresh"><time title="Last index refresh">...</time></span>
|
||||
<input type="search" id="index-search" class="field" placeholder="Search">
|
||||
<a id="index-search-clear" href="javascript:;" title="Clear search">×</a>
|
||||
<span id="hidden-label" hidden> — <span id="hidden-count"></span> <span id="hidden-toggle">[<a href="javascript:;">Show</a>]</span></span>
|
||||
<select id="index-sort" name="Index Sort">
|
||||
<option disabled>Index Sort</option>
|
||||
<option value="bump">Bump order</option>
|
||||
<option value="lastreply">Last reply</option>
|
||||
<option value="birth">Creation date</option>
|
||||
<option value="replycount">Reply count</option>
|
||||
<option value="filecount">File count</option>
|
||||
</select>
|
||||
|
||||
16
src/General/lib/catalogthread.class
Normal file
16
src/General/lib/catalogthread.class
Normal file
@ -0,0 +1,16 @@
|
||||
class CatalogThread
|
||||
@callbacks = new Callbacks 'CatalogThread'
|
||||
toString: -> @ID
|
||||
|
||||
constructor: (root, @thread) ->
|
||||
@ID = @thread.ID
|
||||
@board = @thread.board
|
||||
@nodes =
|
||||
root: root
|
||||
thumb: $ '.catalog-thumb', root
|
||||
icons: $ '.catalog-icons', root
|
||||
postCount: $ '.post-count', root
|
||||
fileCount: $ '.file-count', root
|
||||
pageCount: $ '.page-count', root
|
||||
comment: $ '.comment', root
|
||||
@thread.catalogView = @
|
||||
@ -1,9 +1,10 @@
|
||||
<%= grunt.file.read('src/General/lib/callbacks.class') %>
|
||||
<%= grunt.file.read('src/General/lib/board.class') %>
|
||||
<%= grunt.file.read('src/General/lib/thread.class') %>
|
||||
<%= grunt.file.read('src/General/lib/catalogthread.class') %>
|
||||
<%= grunt.file.read('src/General/lib/post.class') %>
|
||||
<%= grunt.file.read('src/General/lib/clone.class') %>
|
||||
<%= grunt.file.read('src/General/lib/databoard.class') %>
|
||||
<%= grunt.file.read('src/General/lib/notice.class') %>
|
||||
<%= grunt.file.read('src/General/lib/randomaccesslist.class') %>
|
||||
<%= grunt.file.read('src/General/lib/simpledict.class') %>
|
||||
<%= grunt.file.read('src/General/lib/simpledict.class') %>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class DataBoard
|
||||
@keys = ['hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads']
|
||||
@keys = ['pinnedThreads', 'hiddenThreads', 'hiddenPosts', 'lastReadPosts', 'yourPosts', 'watchedThreads']
|
||||
|
||||
constructor: (@key, sync, dontClean) ->
|
||||
@data = Conf[key]
|
||||
|
||||
@ -78,6 +78,9 @@ class Post
|
||||
@parseQuotes()
|
||||
@parseFile that
|
||||
|
||||
@isDead = false
|
||||
@isHidden = false
|
||||
|
||||
@clones = []
|
||||
g.posts.push @fullID, thread.posts.push @, board.posts.push @, @
|
||||
@kill() if that.isArchived
|
||||
@ -85,6 +88,7 @@ class Post
|
||||
parseComment: ->
|
||||
# Merge text nodes and remove empty ones.
|
||||
@nodes.comment.normalize()
|
||||
|
||||
# Get the comment's text.
|
||||
# <br> -> \n
|
||||
# Remove:
|
||||
@ -97,7 +101,11 @@ class Post
|
||||
for node in $$ '.abbr, .exif, b', bq
|
||||
$.rm node
|
||||
@info.comment = @nodesToText bq
|
||||
# Hide spoilers.
|
||||
|
||||
# Save cleaned comment HTML.
|
||||
@info.commentHTML = <%= html('&{bq}') %>
|
||||
|
||||
# Get the comment's text with spoilers hidden.
|
||||
spoilers = $$ 's', bq
|
||||
@info.commentSpoilered = if spoilers.length
|
||||
for node in spoilers
|
||||
@ -177,17 +185,14 @@ class Post
|
||||
$.rmClass node, 'desktop'
|
||||
return
|
||||
|
||||
kill: (file, now) ->
|
||||
now or= new Date()
|
||||
kill: (file) ->
|
||||
if file
|
||||
return if @file.isDead
|
||||
@file.isDead = true
|
||||
@file.timeOfDeath = now
|
||||
$.addClass @nodes.root, 'deleted-file'
|
||||
else
|
||||
return if @isDead
|
||||
@isDead = true
|
||||
@timeOfDeath = now
|
||||
$.addClass @nodes.root, 'deleted-post'
|
||||
|
||||
unless strong = $ 'strong.warning', @nodes.info
|
||||
@ -199,7 +204,7 @@ class Post
|
||||
|
||||
return if @isClone
|
||||
for clone in @clones
|
||||
clone.kill file, now
|
||||
clone.kill file
|
||||
|
||||
return if file
|
||||
# Get quotelinks/backlinks to this post
|
||||
@ -212,7 +217,6 @@ class Post
|
||||
# giving us false-positive dead posts.
|
||||
resurrect: ->
|
||||
delete @isDead
|
||||
delete @timeOfDeath
|
||||
$.rmClass @nodes.root, 'deleted-post'
|
||||
strong = $ 'strong.warning', @nodes.info
|
||||
# no false-positive files
|
||||
|
||||
@ -5,12 +5,19 @@ class Thread
|
||||
constructor: (@ID, @board) ->
|
||||
@fullID = "#{@board}.#{@ID}"
|
||||
@posts = new SimpleDict
|
||||
@isDead = false
|
||||
@isHidden = false
|
||||
@isOnTop = false
|
||||
@isPinned = false
|
||||
@isSticky = false
|
||||
@isClosed = false
|
||||
@isArchived = false
|
||||
@postLimit = false
|
||||
@fileLimit = false
|
||||
|
||||
@OP = null
|
||||
@catalogView = null
|
||||
|
||||
g.threads.push @fullID, board.threads.push @, @
|
||||
|
||||
setPage: (pageNum) ->
|
||||
@ -20,6 +27,12 @@ class Thread
|
||||
$.after $('a[title="Reply to this post"]', info), [$.tn(' '), icon]
|
||||
icon.title = "This thread is on page #{pageNum} in the original index."
|
||||
icon.textContent = "[#{pageNum}]"
|
||||
@catalogView.nodes.pageCount.textContent = pageNum if @catalogView
|
||||
setCount: (type, count, reachedLimit) ->
|
||||
return unless @catalogView
|
||||
el = @catalogView.nodes["#{type}Count"]
|
||||
el.textContent = count
|
||||
(if reachedLimit then $.addClass else $.rmClass) el, 'warning'
|
||||
|
||||
setStatus: (type, status) ->
|
||||
name = "is#{type}"
|
||||
@ -37,21 +50,31 @@ class Thread
|
||||
unless status
|
||||
$.rm icon.previousSibling
|
||||
$.rm icon
|
||||
$.rm $ ".#{typeLC}Icon", @catalogView.nodes.icons if @catalogView
|
||||
return
|
||||
icon = $.el 'img',
|
||||
src: "//s.4cdn.org/image/#{typeLC}#{if window.devicePixelRatio >= 2 then '@2x' else ''}.gif"
|
||||
src: "#{Build.staticPath}#{typeLC}#{Build.gifIcon}"
|
||||
alt: type
|
||||
title: type
|
||||
className: "#{typeLC}Icon retina"
|
||||
root = if type isnt 'Sticky' and @isSticky
|
||||
$ '.stickyIcon', @OP.nodes.info
|
||||
else
|
||||
$('.page-num', @OP.nodes.info) or $('[title="Reply to this post"]', @OP.nodes.info)
|
||||
$('.page-num', @OP.nodes.info) or $('[title="Reply to this post"]', @OP.nodes.info)
|
||||
$.after root, [$.tn(' '), icon]
|
||||
|
||||
return unless @catalogView
|
||||
(if type is 'Sticky' and @isClosed then $.prepend else $.add) @catalogView.nodes.icons, icon.cloneNode()
|
||||
|
||||
pin: ->
|
||||
@isPinned = true
|
||||
$.addClass @catalogView.nodes.root, 'pinned' if @catalogView
|
||||
unpin: ->
|
||||
@isPinned = false
|
||||
$.rmClass @catalogView.nodes.root, 'pinned' if @catalogView
|
||||
|
||||
kill: ->
|
||||
@isDead = true
|
||||
@timeOfDeath = Date.now()
|
||||
|
||||
collect: ->
|
||||
@posts.forEach (post) -> post.collect()
|
||||
|
||||
@ -20,10 +20,22 @@ FappeTyme =
|
||||
name: 'Fappe Tyme'
|
||||
cb: @node
|
||||
|
||||
CatalogThread.callbacks.push
|
||||
name: 'Werk Tyme'
|
||||
cb: @catalogNode
|
||||
|
||||
node: ->
|
||||
return if @file
|
||||
$.addClass @nodes.root, "noFile"
|
||||
|
||||
catalogNode: ->
|
||||
{file} = @thread.OP
|
||||
return if !file
|
||||
filename = $.el 'div',
|
||||
textContent: file.name
|
||||
className: 'werkTyme-filename'
|
||||
$.add @nodes.thumb.parentNode, filename
|
||||
|
||||
cb:
|
||||
set: (type) ->
|
||||
FappeTyme[type].checked = Conf[type]
|
||||
|
||||
@ -11,18 +11,22 @@ Menu =
|
||||
Post.callbacks.push
|
||||
name: 'Menu'
|
||||
cb: @node
|
||||
CatalogThread.callbacks.push
|
||||
name: 'Menu'
|
||||
cb: @catalogNode
|
||||
|
||||
node: ->
|
||||
if @isClone
|
||||
$.on $('.menu-button', @nodes.info), 'click', Menu.toggle
|
||||
Menu.makeButton @, $('.menu-button', @nodes.info)
|
||||
return
|
||||
$.add @nodes.info, Menu.makeButton()
|
||||
$.add @nodes.info, Menu.makeButton @
|
||||
|
||||
makeButton: ->
|
||||
clone = Menu.button.cloneNode true
|
||||
$.on clone, 'click', Menu.toggle
|
||||
clone
|
||||
catalogNode: ->
|
||||
post = g.threads[@thread.fullID].OP
|
||||
$.after @nodes.icons, Menu.makeButton post
|
||||
|
||||
toggle: (e) ->
|
||||
post = Get.postFromNode @
|
||||
Menu.menu.toggle e, @, post
|
||||
makeButton: (post, button) ->
|
||||
button or= Menu.button.cloneNode true
|
||||
$.on button, 'click', (e) ->
|
||||
Menu.menu.toggle e, @, post
|
||||
button
|
||||
|
||||
@ -24,14 +24,17 @@ CatalogLinks =
|
||||
CatalogLinks.set @checked
|
||||
|
||||
set: (useCatalog) ->
|
||||
path = if useCatalog then 'catalog' else ''
|
||||
path = if useCatalog
|
||||
if Conf['JSON Navigation'] and Conf['Use 4chan X Catalog'] then '#catalog' else 'catalog'
|
||||
else
|
||||
''
|
||||
|
||||
generateURL = if useCatalog and Conf['External Catalog']
|
||||
CatalogLinks.external
|
||||
else
|
||||
(board) -> a.href = "/#{board}/#{path}"
|
||||
|
||||
for a in $$ """#board-list a:not(.catalog), #boardNavDesktopFoot a"""
|
||||
for a in $$ """#board-list a:not([data-only]), #boardNavDesktopFoot a"""
|
||||
continue if a.hostname not in ['boards.4chan.org', 'catalog.neet.tv', '4index.gropes.us'] or
|
||||
!(board = a.pathname.split('/')[1]) or
|
||||
board in ['f', 'status', '4chan'] or
|
||||
|
||||
@ -21,7 +21,7 @@ Fourchan =
|
||||
if (!jsMath) return;
|
||||
if (jsMath.loaded) {
|
||||
// process one post
|
||||
jsMath.ProcessBeforeShowing(document.getElementById(e.detail));
|
||||
jsMath.ProcessBeforeShowing(e.target);
|
||||
} else if (jsMath.Autoload && jsMath.Autoload.checked) {
|
||||
// load jsMath and process whole document
|
||||
jsMath.Autoload.Script.Push('ProcessBeforeShowing', [null]);
|
||||
@ -32,6 +32,9 @@ Fourchan =
|
||||
Post.callbacks.push
|
||||
name: 'Parse /sci/ math'
|
||||
cb: @math
|
||||
CatalogThread.callbacks.push
|
||||
name: 'Parse /sci/ math'
|
||||
cb: @math
|
||||
code: ->
|
||||
return if @isClone
|
||||
apply = (e) ->
|
||||
@ -44,8 +47,8 @@ Fourchan =
|
||||
return
|
||||
math: ->
|
||||
return if (@isClone and doc.contains @origin.nodes.root) or !$ '.math', @nodes.comment
|
||||
$.asap (=> doc.contains @nodes.post), =>
|
||||
$.event 'jsmath', @nodes.post.id, window
|
||||
$.asap (=> doc.contains @nodes.comment), =>
|
||||
$.event 'jsmath', null, @nodes.comment
|
||||
parseThread: (threadID, offset, limit) ->
|
||||
# Fix /sci/
|
||||
# Fix /g/
|
||||
|
||||
@ -21,21 +21,21 @@ Keybinds =
|
||||
{target} = e
|
||||
if target.nodeName in ['INPUT', 'TEXTAREA']
|
||||
return unless /(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test key
|
||||
unless g.VIEW is 'catalog'
|
||||
unless g.VIEW is 'catalog' or g.VIEW is 'index' and Conf['JSON Navigation'] and Conf['Index Mode'] is 'catalog'
|
||||
threadRoot = Nav.getThread()
|
||||
if op = $ '.op', threadRoot
|
||||
thread = Get.postFromNode(op).thread
|
||||
switch key
|
||||
# QR & Options
|
||||
when Conf['Toggle board list']
|
||||
if Conf['Custom Board Navigation']
|
||||
Header.toggleBoardList()
|
||||
return unless Conf['Custom Board Navigation']
|
||||
Header.toggleBoardList()
|
||||
when Conf['Toggle header']
|
||||
Header.toggleBarVisibility()
|
||||
when Conf['Open empty QR']
|
||||
Keybinds.qr()
|
||||
when Conf['Open QR']
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless threadRoot
|
||||
Keybinds.qr threadRoot
|
||||
when Conf['Open settings']
|
||||
Settings.open()
|
||||
@ -45,11 +45,13 @@ Keybinds =
|
||||
else if (notifications = $$ '.notification').length
|
||||
for notification in notifications
|
||||
$('.close', notification).click()
|
||||
else if QR.nodes
|
||||
else if QR.nodes and !QR.nodes.el.hidden
|
||||
if Conf['Persistent QR']
|
||||
QR.hide()
|
||||
else
|
||||
QR.close()
|
||||
else
|
||||
return
|
||||
when Conf['Spoiler tags']
|
||||
return if target.nodeName isnt 'TEXTAREA'
|
||||
Keybinds.tags 'spoiler', target
|
||||
@ -63,25 +65,31 @@ Keybinds =
|
||||
return if target.nodeName isnt 'TEXTAREA'
|
||||
Keybinds.tags 'math', target
|
||||
when Conf['Toggle sage']
|
||||
Keybinds.sage() if QR.nodes
|
||||
return unless QR.nodes and !QR.nodes.el.hidden
|
||||
Keybinds.sage()
|
||||
when Conf['Submit QR']
|
||||
QR.submit() if QR.nodes and !QR.status()
|
||||
return unless QR.nodes and !QR.nodes.el.hidden
|
||||
QR.submit() if !QR.status()
|
||||
# Index/Thread related
|
||||
when Conf['Update']
|
||||
switch g.VIEW
|
||||
when 'thread'
|
||||
ThreadUpdater.update() if Conf['Thread Updater']
|
||||
return unless Conf['Thread Updater']
|
||||
ThreadUpdater.update()
|
||||
when 'index'
|
||||
if Conf['JSON Navigation'] then Index.update()
|
||||
return unless Conf['JSON Navigation']
|
||||
Index.update()
|
||||
else
|
||||
return
|
||||
when Conf['Watch']
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless thread
|
||||
ThreadWatcher.toggle thread
|
||||
# Images
|
||||
when Conf['Expand image']
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless threadRoot
|
||||
Keybinds.img threadRoot
|
||||
when Conf['Expand images']
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless threadRoot
|
||||
Keybinds.img threadRoot, true
|
||||
when Conf['Open Gallery']
|
||||
return if g.VIEW is 'catalog'
|
||||
@ -95,6 +103,10 @@ Keybinds =
|
||||
# Board Navigation
|
||||
when Conf['Front page']
|
||||
if Conf['JSON Navigation'] and g.VIEW is 'index'
|
||||
if Conf['Use 4chan X Catalog'] and Conf['Index Mode'] is 'catalog'
|
||||
window.location = '#index'
|
||||
return
|
||||
return unless Conf['Index Mode'] in ['paged', 'infinite']
|
||||
Index.userPageNav 1
|
||||
else
|
||||
window.location = "/#{g.BOARD}/"
|
||||
@ -103,16 +115,16 @@ Keybinds =
|
||||
when Conf['Next page']
|
||||
return unless g.VIEW is 'index'
|
||||
if Conf['JSON Navigation']
|
||||
if Conf['Index Mode'] isnt 'all pages'
|
||||
$('.next button', Index.pagelist).click()
|
||||
return unless Conf['Index Mode'] in ['paged', 'infinite']
|
||||
$('.next button', Index.pagelist).click()
|
||||
else
|
||||
if form = $ '.next form'
|
||||
window.location = form.action
|
||||
when Conf['Previous page']
|
||||
return unless g.VIEW is 'index'
|
||||
if Conf['JSON Navigation']
|
||||
if Conf['Index Mode'] isnt 'all pages'
|
||||
$('.prev button', Index.pagelist).click()
|
||||
return unless Conf['Index Mode'] in ['paged', 'infinite']
|
||||
$('.prev button', Index.pagelist).click()
|
||||
else
|
||||
if form = $ '.prev form'
|
||||
window.location = form.action
|
||||
@ -125,45 +137,45 @@ Keybinds =
|
||||
if Conf['External Catalog']
|
||||
window.location = CatalogLinks.external(g.BOARD.ID)
|
||||
else
|
||||
window.location = "/#{g.BOARD}/catalog"
|
||||
window.location = "/#{g.BOARD}/" + if Conf['JSON Navigation'] and Conf['Use 4chan X Catalog'] then '#catalog' else 'catalog'
|
||||
# Thread Navigation
|
||||
when Conf['Next thread']
|
||||
return if g.VIEW isnt 'index'
|
||||
return if g.VIEW isnt 'index' or !threadRoot
|
||||
Nav.scroll +1
|
||||
when Conf['Previous thread']
|
||||
return if g.VIEW isnt 'index'
|
||||
return if g.VIEW isnt 'index' or !threadRoot
|
||||
Nav.scroll -1
|
||||
when Conf['Expand thread']
|
||||
return if g.VIEW isnt 'index'
|
||||
return if g.VIEW isnt 'index' or !threadRoot
|
||||
ExpandThread.toggle thread
|
||||
when Conf['Open thread']
|
||||
return if g.VIEW isnt 'index'
|
||||
return if g.VIEW isnt 'index' or !threadRoot
|
||||
Keybinds.open thread
|
||||
when Conf['Open thread tab']
|
||||
return if g.VIEW isnt 'index'
|
||||
return if g.VIEW isnt 'index' or !threadRoot
|
||||
Keybinds.open thread, true
|
||||
# Reply Navigation
|
||||
when Conf['Next reply']
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless threadRoot
|
||||
Keybinds.hl +1, threadRoot
|
||||
when Conf['Previous reply']
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless threadRoot
|
||||
Keybinds.hl -1, threadRoot
|
||||
when Conf['Deselect reply']
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless threadRoot
|
||||
Keybinds.hl 0, threadRoot
|
||||
when Conf['Hide']
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless thread
|
||||
ThreadHiding.toggle thread if ThreadHiding.db
|
||||
when Conf['Previous Post Quoting You']
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless threadRoot
|
||||
QuoteYou.cb.seek 'preceding'
|
||||
when Conf['Next Post Quoting You']
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless threadRoot
|
||||
QuoteYou.cb.seek 'following'
|
||||
<% if (tests_enabled) { %>
|
||||
when 't'
|
||||
return if g.VIEW is 'catalog'
|
||||
return unless threadRoot
|
||||
BuildTest.testAll()
|
||||
<% } %>
|
||||
else
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user