Merge branch 'next'
This commit is contained in:
commit
61954985bf
@ -246,18 +246,64 @@ Filter =
|
|||||||
re
|
re
|
||||||
$.set type, save, cb
|
$.set type, save, cb
|
||||||
|
|
||||||
|
removeFilters: (type, res, cb) ->
|
||||||
|
$.get type, Conf[type], (item) ->
|
||||||
|
save = item[type]
|
||||||
|
res = res.map(Filter.escape).join('|')
|
||||||
|
save = save.replace RegExp("(?:$\n|^)(?:#{res})$", 'mg'), ''
|
||||||
|
$.set type, save, cb
|
||||||
|
|
||||||
|
showFilters: (type) ->
|
||||||
|
# Open the settings and display & focus the relevant filter textarea.
|
||||||
|
Settings.open 'Filter'
|
||||||
|
section = $ '.section-container'
|
||||||
|
select = $ 'select[name=filter]', section
|
||||||
|
select.value = type
|
||||||
|
Settings.selectFilter.call select
|
||||||
|
$.onExists section, 'textarea', (ta) ->
|
||||||
|
tl = ta.textLength
|
||||||
|
ta.setSelectionRange tl, tl
|
||||||
|
ta.focus()
|
||||||
|
|
||||||
quickFilterMD5: ->
|
quickFilterMD5: ->
|
||||||
post = Get.postFromNode @
|
post = Get.postFromNode @
|
||||||
return unless post.file
|
files = post.files.filter((f) -> f.MD5)
|
||||||
Filter.addFilter 'MD5', "/#{post.file.MD5}/"
|
return unless files.length
|
||||||
|
filter = files.map((f) -> "/#{f.MD5}/").join('\n')
|
||||||
|
Filter.addFilter 'MD5', filter
|
||||||
origin = post.origin or post
|
origin = post.origin or post
|
||||||
if origin.isReply
|
if origin.isReply
|
||||||
PostHiding.hide origin
|
PostHiding.hide origin
|
||||||
else if g.VIEW is 'index'
|
else if g.VIEW is 'index'
|
||||||
ThreadHiding.hide origin.thread
|
ThreadHiding.hide origin.thread
|
||||||
# If post is still visible, give an indication that the MD5 was filtered.
|
{notice} = Filter.quickFilterMD5
|
||||||
if post.nodes.post.getBoundingClientRect().height
|
if notice
|
||||||
new Notice 'info', 'MD5 filtered.', 2
|
notice.filters.push filter
|
||||||
|
notice.posts.push origin
|
||||||
|
$('span', notice.el).textContent = "#{notice.filters.length} MD5s filtered."
|
||||||
|
else
|
||||||
|
msg = $.el 'div',
|
||||||
|
<%= html('<span>MD5 filtered.</span> [<a href="javascript:;">show</a>] [<a href="javascript:;">undo</a>]') %>
|
||||||
|
notice = Filter.quickFilterMD5.notice = new Notice 'info', msg, undefined, ->
|
||||||
|
delete Filter.quickFilterMD5.notice
|
||||||
|
notice.filters = [filter]
|
||||||
|
notice.posts = [origin]
|
||||||
|
links = $$ 'a', msg
|
||||||
|
$.on links[0], 'click', Filter.quickFilterCB.show.bind(notice)
|
||||||
|
$.on links[1], 'click', Filter.quickFilterCB.undo.bind(notice)
|
||||||
|
|
||||||
|
quickFilterCB:
|
||||||
|
show: ->
|
||||||
|
Filter.showFilters 'MD5'
|
||||||
|
@close()
|
||||||
|
undo: ->
|
||||||
|
Filter.removeFilters 'MD5', @filters
|
||||||
|
for post in @posts
|
||||||
|
if post.isReply
|
||||||
|
PostHiding.show post
|
||||||
|
else if g.VIEW is 'index'
|
||||||
|
ThreadHiding.show post.thread
|
||||||
|
@close()
|
||||||
|
|
||||||
escape: (value) ->
|
escape: (value) ->
|
||||||
value.replace ///
|
value.replace ///
|
||||||
@ -346,13 +392,4 @@ Filter =
|
|||||||
).join('\n')
|
).join('\n')
|
||||||
|
|
||||||
Filter.addFilter type, res, ->
|
Filter.addFilter type, res, ->
|
||||||
# Open the settings and display & focus the relevant filter textarea.
|
Filter.showFilters type
|
||||||
Settings.open 'Filter'
|
|
||||||
section = $ '.section-container'
|
|
||||||
select = $ 'select[name=filter]', section
|
|
||||||
select.value = type
|
|
||||||
Settings.selectFilter.call select
|
|
||||||
$.onExists section, 'textarea', (ta) ->
|
|
||||||
tl = ta.textLength
|
|
||||||
ta.setSelectionRange tl, tl
|
|
||||||
ta.focus()
|
|
||||||
|
|||||||
@ -145,7 +145,7 @@ ThreadHiding =
|
|||||||
a
|
a
|
||||||
|
|
||||||
makeStub: (thread, root) ->
|
makeStub: (thread, root) ->
|
||||||
numReplies = $$(g.SITE.selectors.postContainer + g.SITE.selectors.relative.replyPost, root).length
|
numReplies = $$(g.SITE.selectors.replyOriginal, root).length
|
||||||
numReplies += +summary.textContent.match /\d+/ if summary = $ g.SITE.selectors.summary, root
|
numReplies += +summary.textContent.match /\d+/ if summary = $ g.SITE.selectors.summary, root
|
||||||
|
|
||||||
a = ThreadHiding.makeButton thread, 'show'
|
a = ThreadHiding.makeButton thread, 'show'
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
Get =
|
Get =
|
||||||
|
url: (type, IDs, args...) ->
|
||||||
|
g.sites[IDs.siteID]?.urls[type] IDs, args...
|
||||||
threadExcerpt: (thread) ->
|
threadExcerpt: (thread) ->
|
||||||
{OP} = thread
|
{OP} = thread
|
||||||
excerpt = ("/#{decodeURIComponent thread.board.ID}/ - ") + (
|
excerpt = ("/#{decodeURIComponent thread.board.ID}/ - ") + (
|
||||||
|
|||||||
@ -234,7 +234,10 @@ Header =
|
|||||||
href: "/#{g.BOARD.ID}/"
|
href: "/#{g.BOARD.ID}/"
|
||||||
textContent: text or g.BOARD.ID
|
textContent: text or g.BOARD.ID
|
||||||
className: 'current'
|
className: 'current'
|
||||||
if /-catalog/.test(t)
|
if /-index/.test(t)
|
||||||
|
a.dataset.only = 'index'
|
||||||
|
else if /-catalog/.test(t)
|
||||||
|
a.dataset.only = 'catalog'
|
||||||
a.href += 'catalog.html'
|
a.href += 'catalog.html'
|
||||||
else if /-(archive|expired)/.test(t)
|
else if /-(archive|expired)/.test(t)
|
||||||
a = a.firstChild # Its text node.
|
a = a.firstChild # Its text node.
|
||||||
@ -263,9 +266,10 @@ Header =
|
|||||||
text or boardID
|
text or boardID
|
||||||
|
|
||||||
if m = t.match /-(index|catalog)/
|
if m = t.match /-(index|catalog)/
|
||||||
unless boardID is 'f' and m[1] is 'catalog'
|
urlIC = CatalogLinks[m[1]] {siteID: '4chan.org', boardID}
|
||||||
|
if urlIC
|
||||||
a.dataset.only = m[1]
|
a.dataset.only = m[1]
|
||||||
a.href = CatalogLinks[m[1]] boardID
|
a.href = urlIC
|
||||||
$.addClass a, 'catalog' if m[1] is 'catalog'
|
$.addClass a, 'catalog' if m[1] is 'catalog'
|
||||||
else
|
else
|
||||||
return a.firstChild # Its text node.
|
return a.firstChild # Its text node.
|
||||||
|
|||||||
@ -2,14 +2,17 @@ Index =
|
|||||||
showHiddenThreads: false
|
showHiddenThreads: false
|
||||||
changed: {}
|
changed: {}
|
||||||
|
|
||||||
|
enabledOn: ({siteID, boardID}) ->
|
||||||
|
Conf['JSON Index'] and g.sites[siteID].software is 'yotsuba' and boardID isnt 'f'
|
||||||
|
|
||||||
init: ->
|
init: ->
|
||||||
return unless g.VIEW is 'index' and g.BOARD.ID isnt 'f'
|
return unless g.VIEW is 'index'
|
||||||
|
|
||||||
# For IndexRefresh events
|
# For IndexRefresh events
|
||||||
$.one d, '4chanXInitFinished', @cb.initFinished
|
$.one d, '4chanXInitFinished', @cb.initFinished
|
||||||
$.on d, 'PostsInserted', @cb.postsInserted
|
$.on d, 'PostsInserted', @cb.postsInserted
|
||||||
|
|
||||||
return unless Conf['JSON Index']
|
return unless @enabledOn g.BOARD
|
||||||
|
|
||||||
@enabled = true
|
@enabled = true
|
||||||
|
|
||||||
@ -197,7 +200,7 @@ Index =
|
|||||||
|
|
||||||
menu:
|
menu:
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW isnt 'index' or !Conf['JSON Index'] or !Conf['Menu'] or !Conf['Thread Hiding Link'] or g.BOARD.ID is 'f'
|
return unless g.VIEW is 'index' and Conf['Menu'] and Conf['Thread Hiding Link'] and Index.enabledOn(g.BOARD)
|
||||||
|
|
||||||
Menu.menu.addEntry
|
Menu.menu.addEntry
|
||||||
el: $.el 'a',
|
el: $.el 'a',
|
||||||
|
|||||||
@ -561,10 +561,13 @@ Settings =
|
|||||||
$.id('lastarchivecheck').textContent = 'never'
|
$.id('lastarchivecheck').textContent = 'never'
|
||||||
|
|
||||||
items = {}
|
items = {}
|
||||||
for name in ['archiveLists', 'archiveAutoUpdate', 'fourchanImageHost', 'captchaLanguage', 'captchaServiceDomain', 'boardnav', 'time', 'timeLocale', 'backlink', 'pastedname', 'fileInfo', 'QR.personas', 'favicon', 'usercss', 'customCooldown', 'jsWhitelist']
|
for name, input of inputs when name not in ['captchaServiceKey', 'Interval', 'Custom CSS']
|
||||||
items[name] = Conf[name]
|
items[name] = Conf[name]
|
||||||
input = inputs[name]
|
event = if (
|
||||||
event = if name in ['archiveLists', 'archiveAutoUpdate', 'QR.personas', 'favicon', 'usercss'] then 'change' else 'input'
|
input.nodeName is 'SELECT' or
|
||||||
|
input.type in ['checkbox', 'radio'] or
|
||||||
|
(input.nodeName is 'TEXTAREA' and name not of Settings)
|
||||||
|
) then 'change' else 'input'
|
||||||
$.on input, event, $.cb[if input.type is 'checkbox' then 'checked' else 'value']
|
$.on input, event, $.cb[if input.type is 'checkbox' then 'checked' else 'value']
|
||||||
$.on input, event, Settings[name] if name of Settings
|
$.on input, event, Settings[name] if name of Settings
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,16 @@
|
|||||||
<button id="update-archives">Update now</button> Last updated: <time id="lastarchivecheck"></time> <label><input type="checkbox" name="archiveAutoUpdate"> Auto-update</label>
|
<button id="update-archives">Update now</button> Last updated: <time id="lastarchivecheck"></time> <label><input type="checkbox" name="archiveAutoUpdate"> Auto-update</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>External Catalog</legend>
|
||||||
|
<div class="warning" data-feature="External Catalog"><code>External Catalog</code> is disabled. This will be used only as a fallback.</div>
|
||||||
|
<div>
|
||||||
|
URLs of external catalog sites, where <code>%board</code> is to be replaced by the board name.<br>
|
||||||
|
Each URL should be followed by <code>;boards:</code> and optionally <code>;exclude:</code> and a list of supported/excluded boards in the format explained in the Filter guide.
|
||||||
|
</div>
|
||||||
|
<textarea hidden name="externalCatalogURLs" class="field" spellcheck="false"></textarea>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Override 4chan Image Host</legend>
|
<legend>Override 4chan Image Host</legend>
|
||||||
<div>Change 4chan image links to this domain. Leave blank for no change.</div>
|
<div>Change 4chan image links to this domain. Leave blank for no change.</div>
|
||||||
@ -173,3 +183,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<textarea hidden name="jsWhitelist" class="field" spellcheck="false"></textarea>
|
<textarea hidden name="jsWhitelist" class="field" spellcheck="false"></textarea>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Known Banners</legend>
|
||||||
|
<div>List of known banners, used for click-to-change feature.</div>
|
||||||
|
<textarea hidden name="knownBanners" class="field" spellcheck="false"></textarea>
|
||||||
|
</fieldset>
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
Banner =
|
Banner =
|
||||||
banners: `<%= JSON.stringify(readJSON('banners.json')) %>`
|
|
||||||
|
|
||||||
init: ->
|
init: ->
|
||||||
if Conf['Custom Board Titles']
|
if Conf['Custom Board Titles']
|
||||||
@db = new DataBoard 'customTitles', null, true
|
@db = new DataBoard 'customTitles', null, true
|
||||||
@ -44,7 +42,7 @@ Banner =
|
|||||||
cb:
|
cb:
|
||||||
toggle: ->
|
toggle: ->
|
||||||
unless Banner.choices?.length
|
unless Banner.choices?.length
|
||||||
Banner.choices = Banner.banners.slice()
|
Banner.choices = Conf['knownBanners'].split(',').slice()
|
||||||
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}"
|
||||||
|
|||||||
@ -13,10 +13,11 @@ CatalogLinks =
|
|||||||
link.href = CatalogLinks.index()
|
link.href = CatalogLinks.index()
|
||||||
when "/#{g.BOARD}/catalog"
|
when "/#{g.BOARD}/catalog"
|
||||||
link.href = CatalogLinks.catalog()
|
link.href = CatalogLinks.catalog()
|
||||||
if g.VIEW is 'catalog' and Conf['JSON Index'] and Conf['Use <%= meta.name %> Catalog']
|
if g.VIEW is 'catalog' and (catalogURL = CatalogLinks.catalog()) isnt g.SITE.urls.catalog?(g.BOARD)
|
||||||
catalogLink = link.parentNode.cloneNode true
|
catalogLink = link.parentNode.cloneNode true
|
||||||
catalogLink.firstElementChild.textContent = '<%= meta.name %> Catalog'
|
link2 = catalogLink.firstElementChild
|
||||||
catalogLink.firstElementChild.href = CatalogLinks.catalog()
|
link2.href = catalogURL
|
||||||
|
link2.textContent = if link2.hostname is location.hostname then '<%= meta.name %> Catalog' else 'External Catalog'
|
||||||
$.after link.parentNode, [$.tn(' '), catalogLink]
|
$.after link.parentNode, [$.tn(' '), catalogLink]
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -57,33 +58,63 @@ CatalogLinks =
|
|||||||
setLinks: (list) ->
|
setLinks: (list) ->
|
||||||
return unless (CatalogLinks.enabled ? Conf['Catalog Links']) and list
|
return unless (CatalogLinks.enabled ? Conf['Catalog Links']) and list
|
||||||
|
|
||||||
|
# do not transform links unless they differ from the expected value at most by this tail
|
||||||
|
tail = /(?:index)?(?:\.\w+)?$/
|
||||||
|
|
||||||
for a in $$('a:not([data-only])', list)
|
for a in $$('a:not([data-only])', list)
|
||||||
continue if (
|
{siteID, boardID} = a.dataset
|
||||||
a.hostname not in ['boards.4chan.org', 'boards.4channel.org', 'catalog.neet.tv'] or
|
unless siteID and boardID
|
||||||
!(board = a.pathname.split('/')[1]) or
|
{siteID, boardID, VIEW} = Site.parseURL a
|
||||||
board in ['f', 'status', '4chan'] or
|
continue unless (
|
||||||
a.pathname.split('/')[2] is 'archive' or
|
siteID and boardID and
|
||||||
$.hasClass a, 'external'
|
VIEW in ['index', 'catalog'] and
|
||||||
)
|
(a.dataset.indexOptions or a.href.replace(tail, '') is Get.url(VIEW, {siteID, boardID}).replace(tail, ''))
|
||||||
|
)
|
||||||
|
$.extend a.dataset, {siteID, boardID}
|
||||||
|
|
||||||
# Href is easier than pathname because then we don't have
|
board = {siteID, boardID}
|
||||||
# conditions where External Catalog has been disabled between switches.
|
url = if Conf['Header catalog links'] then CatalogLinks.catalog(board) else Get.url('index', board)
|
||||||
a.href = if Conf['Header catalog links'] then CatalogLinks.catalog(board) else "//#{BoardConfig.domain(board)}/#{board}/"
|
if url
|
||||||
|
a.href = url
|
||||||
if a.dataset.indexOptions and a.hostname in ['boards.4chan.org', 'boards.4channel.org'] and a.pathname.split('/')[2] is ''
|
if a.dataset.indexOptions and url.split('#')[0] is Get.url('index', board)
|
||||||
a.href += (if a.hash then '/' else '#') + a.dataset.indexOptions
|
a.href += (if a.hash then '/' else '#') + a.dataset.indexOptions
|
||||||
return
|
return
|
||||||
|
|
||||||
catalog: (board=g.BOARD.ID) ->
|
externalParse: ->
|
||||||
if Conf['External Catalog'] and board in ['3', 'a', 'adv', 'an', 'asp', 'biz', 'c', 'cgl', 'ck', 'cm', 'co', 'diy', 'f', 'fa', 'fit', 'g', 'gd', 'his', 'i', 'int', 'jp', 'k', 'lgbt', 'lit', 'm', 'mlp', 'mu', 'n', 'news', 'o', 'out', 'p', 'po', 'pol', 's4s', 'sci', 'sp', 'tg', 'toy', 'trv', 'tv', 'v', 'vg', 'vip', 'vp', 'vr', 'w', 'wg', 'wsg', 'wsr', 'x']
|
CatalogLinks.externalList = {}
|
||||||
"//catalog.neet.tv/#{board}/"
|
for line in Conf['externalCatalogURLs'].split '\n'
|
||||||
else if Conf['JSON Index'] and Conf['Use <%= meta.name %> Catalog']
|
continue if line[0] is '#'
|
||||||
if location.hostname in ['boards.4chan.org', 'boards.4channel.org'] and g.BOARD.ID is board and g.VIEW is 'index' then '#catalog' else "//#{BoardConfig.domain(board)}/#{board}/#catalog"
|
url = line.split(';')[0]
|
||||||
else
|
boards = Filter.parseBoards(line.match(/;boards:([^;]+)/)?[1] or '*')
|
||||||
"//#{BoardConfig.domain(board)}/#{board}/catalog"
|
excludes = Filter.parseBoards(line.match(/;exclude:([^;]+)/)?[1]) or {}
|
||||||
|
for board of boards
|
||||||
|
unless excludes[board] or excludes[board.split('/')[0] + '/*']
|
||||||
|
CatalogLinks.externalList[board] = url
|
||||||
|
return
|
||||||
|
|
||||||
index: (board=g.BOARD.ID) ->
|
external: ({siteID, boardID}) ->
|
||||||
if Conf['JSON Index'] and board isnt 'f'
|
CatalogLinks.externalParse() unless CatalogLinks.externalList
|
||||||
if location.hostname in ['boards.4chan.org', 'boards.4channel.org'] and g.BOARD.ID is board and g.VIEW is 'index' then '#index' else "//#{BoardConfig.domain(board)}/#{board}/#index"
|
external = (CatalogLinks.externalList["#{siteID}/#{boardID}"] or CatalogLinks.externalList["#{siteID}/*"])
|
||||||
|
if external then external.replace(/%board/g, boardID) else undefined
|
||||||
|
|
||||||
|
jsonIndex: (board, hash) ->
|
||||||
|
if g.SITE.ID is board.siteID and g.BOARD.ID is board.boardID and g.VIEW is 'index'
|
||||||
|
hash
|
||||||
else
|
else
|
||||||
"//#{BoardConfig.domain(board)}/#{board}/"
|
Get.url('index', board) + hash
|
||||||
|
|
||||||
|
catalog: (board=g.BOARD) ->
|
||||||
|
if Conf['External Catalog'] and (external = CatalogLinks.external board)
|
||||||
|
external
|
||||||
|
else if Index.enabledOn(board) and Conf['Use <%= meta.name %> Catalog']
|
||||||
|
CatalogLinks.jsonIndex board, '#catalog'
|
||||||
|
else if (nativeCatalog = Get.url 'catalog', board)
|
||||||
|
nativeCatalog
|
||||||
|
else
|
||||||
|
CatalogLinks.external board
|
||||||
|
|
||||||
|
index: (board=g.BOARD) ->
|
||||||
|
if Index.enabledOn(board)
|
||||||
|
CatalogLinks.jsonIndex board, '#index'
|
||||||
|
else
|
||||||
|
Get.url 'index', board
|
||||||
|
|||||||
@ -58,6 +58,7 @@ ExpandThread =
|
|||||||
return if @ isnt status.req # aborted
|
return if @ isnt status.req # aborted
|
||||||
delete status.req
|
delete status.req
|
||||||
ExpandThread.parse @, thread, a
|
ExpandThread.parse @, thread, a
|
||||||
|
status.numReplies = $$(g.SITE.selectors.replyOriginal, thread.nodes.root).length
|
||||||
|
|
||||||
contract: (thread, a, threadRoot) ->
|
contract: (thread, a, threadRoot) ->
|
||||||
status = ExpandThread.statuses[thread]
|
status = ExpandThread.statuses[thread]
|
||||||
@ -69,15 +70,7 @@ ExpandThread =
|
|||||||
return
|
return
|
||||||
|
|
||||||
replies = $$ '.thread > .replyContainer', threadRoot
|
replies = $$ '.thread > .replyContainer', threadRoot
|
||||||
if !Conf['JSON Index'] or Conf['Show Replies']
|
replies = replies[...(-status.numReplies)] if status.numReplies
|
||||||
num = if thread.isSticky
|
|
||||||
1
|
|
||||||
else switch g.BOARD.ID
|
|
||||||
# XXX boards config
|
|
||||||
when 'b', 'vg', 'bant' then 3
|
|
||||||
when 't' then 1
|
|
||||||
else 5
|
|
||||||
replies = replies[...-num]
|
|
||||||
postsCount = 0
|
postsCount = 0
|
||||||
filesCount = 0
|
filesCount = 0
|
||||||
for reply in replies
|
for reply in replies
|
||||||
|
|||||||
@ -21,14 +21,9 @@ Keybinds =
|
|||||||
{target} = e
|
{target} = e
|
||||||
if target.nodeName in ['INPUT', 'TEXTAREA']
|
if target.nodeName in ['INPUT', 'TEXTAREA']
|
||||||
return unless /(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test(key) and not /^Alt\+(\d|Up|Down|Left|Right)$/.test(key)
|
return unless /(Esc|Alt|Ctrl|Meta|Shift\+\w{2,})/.test(key) and not /^Alt\+(\d|Up|Down|Left|Right)$/.test(key)
|
||||||
unless (
|
if g.VIEW in ['index', 'thread']
|
||||||
g.VIEW not in ['index', 'thread'] or
|
|
||||||
g.VIEW is 'index' and Conf['JSON Index'] and Conf['Index Mode'] is 'catalog' or
|
|
||||||
g.VIEW is 'index' and g.BOARD.ID is 'f'
|
|
||||||
)
|
|
||||||
threadRoot = Nav.getThread()
|
threadRoot = Nav.getThread()
|
||||||
if op = $ '.op', threadRoot
|
thread = Get.threadFromRoot threadRoot
|
||||||
thread = Get.postFromNode(op).thread
|
|
||||||
switch key
|
switch key
|
||||||
# QR & Options
|
# QR & Options
|
||||||
when Conf['Toggle board list']
|
when Conf['Toggle board list']
|
||||||
@ -93,10 +88,10 @@ Keybinds =
|
|||||||
when Conf['Update']
|
when Conf['Update']
|
||||||
switch g.VIEW
|
switch g.VIEW
|
||||||
when 'thread'
|
when 'thread'
|
||||||
return unless Conf['Thread Updater']
|
return unless ThreadUpdater.enabled
|
||||||
ThreadUpdater.update()
|
ThreadUpdater.update()
|
||||||
when 'index'
|
when 'index'
|
||||||
return unless Conf['JSON Index'] and g.BOARD.ID isnt 'f'
|
return unless Index.enabled
|
||||||
Index.update()
|
Index.update()
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
@ -118,10 +113,11 @@ Keybinds =
|
|||||||
# Images
|
# Images
|
||||||
when Conf['Expand image']
|
when Conf['Expand image']
|
||||||
return unless ImageExpand.enabled and threadRoot
|
return unless ImageExpand.enabled and threadRoot
|
||||||
Keybinds.img threadRoot
|
post = Get.postFromNode Keybinds.post threadRoot
|
||||||
|
ImageExpand.toggle post if post.file
|
||||||
when Conf['Expand images']
|
when Conf['Expand images']
|
||||||
return unless ImageExpand.enabled and threadRoot
|
return unless ImageExpand.enabled
|
||||||
Keybinds.img threadRoot, true
|
ImageExpand.cb.toggleAll()
|
||||||
when Conf['Open Gallery']
|
when Conf['Open Gallery']
|
||||||
return unless Gallery.enabled
|
return unless Gallery.enabled
|
||||||
Gallery.cb.toggle()
|
Gallery.cb.toggle()
|
||||||
@ -133,47 +129,51 @@ Keybinds =
|
|||||||
FappeTyme.toggle 'werk'
|
FappeTyme.toggle 'werk'
|
||||||
# Board Navigation
|
# Board Navigation
|
||||||
when Conf['Front page']
|
when Conf['Front page']
|
||||||
if Conf['JSON Index'] and g.VIEW is 'index' and g.BOARD.ID isnt 'f'
|
if Index.enabled
|
||||||
Index.userPageNav 1
|
Index.userPageNav 1
|
||||||
else
|
else
|
||||||
location.href = "/#{g.BOARD}/"
|
location.href = "/#{g.BOARD}/"
|
||||||
when Conf['Open front page']
|
when Conf['Open front page']
|
||||||
$.open "#{location.origin}/#{g.BOARD}/"
|
$.open "#{location.origin}/#{g.BOARD}/"
|
||||||
when Conf['Next page']
|
when Conf['Next page']
|
||||||
return unless g.VIEW is 'index' and g.BOARD.ID isnt 'f'
|
return unless g.VIEW is 'index' and !g.SITE.isOnePage?(g.BOARD)
|
||||||
if Conf['JSON Index']
|
if Index.enabled
|
||||||
return unless Conf['Index Mode'] in ['paged', 'infinite']
|
return unless Conf['Index Mode'] in ['paged', 'infinite']
|
||||||
$('.next button', Index.pagelist).click()
|
$('.next button', Index.pagelist).click()
|
||||||
else
|
else
|
||||||
if form = $ '.next form'
|
$(g.SITE.selectors.nav.next)?.click()
|
||||||
location.href = form.action
|
|
||||||
when Conf['Previous page']
|
when Conf['Previous page']
|
||||||
return unless g.VIEW is 'index' and g.BOARD.ID isnt 'f'
|
return unless g.VIEW is 'index' and !g.SITE.isOnePage?(g.BOARD)
|
||||||
if Conf['JSON Index']
|
if Index.enabled
|
||||||
return unless Conf['Index Mode'] in ['paged', 'infinite']
|
return unless Conf['Index Mode'] in ['paged', 'infinite']
|
||||||
$('.prev button', Index.pagelist).click()
|
$('.prev button', Index.pagelist).click()
|
||||||
else
|
else
|
||||||
if form = $ '.prev form'
|
$(g.SITE.selectors.nav.prev)?.click()
|
||||||
location.href = form.action
|
|
||||||
when Conf['Search form']
|
when Conf['Search form']
|
||||||
return unless g.VIEW is 'index' and g.BOARD.ID isnt 'f'
|
return unless g.VIEW is 'index'
|
||||||
searchInput = if Conf['JSON Index'] then Index.searchInput else $.id('search-box')
|
searchInput = if Index.enabled
|
||||||
|
Index.searchInput
|
||||||
|
else if g.SITE.selectors.searchBox
|
||||||
|
$ g.SITE.selectors.searchBox
|
||||||
|
else
|
||||||
|
undefined
|
||||||
|
return unless searchInput
|
||||||
Header.scrollToIfNeeded searchInput
|
Header.scrollToIfNeeded searchInput
|
||||||
searchInput.focus()
|
searchInput.focus()
|
||||||
when Conf['Paged mode']
|
when Conf['Paged mode']
|
||||||
return unless Conf['JSON Index'] and g.BOARD.ID isnt 'f'
|
return unless Index.enabledOn(g.BOARD)
|
||||||
location.href = if g.VIEW is 'index' then '#paged' else "/#{g.BOARD}/#paged"
|
location.href = if g.VIEW is 'index' then '#paged' else "/#{g.BOARD}/#paged"
|
||||||
when Conf['Infinite scrolling mode']
|
when Conf['Infinite scrolling mode']
|
||||||
return unless Conf['JSON Index'] and g.BOARD.ID isnt 'f'
|
return unless Index.enabledOn(g.BOARD)
|
||||||
location.href = if g.VIEW is 'index' then '#infinite' else "/#{g.BOARD}/#infinite"
|
location.href = if g.VIEW is 'index' then '#infinite' else "/#{g.BOARD}/#infinite"
|
||||||
when Conf['All pages mode']
|
when Conf['All pages mode']
|
||||||
return unless Conf['JSON Index'] and g.BOARD.ID isnt 'f'
|
return unless Index.enabledOn(g.BOARD)
|
||||||
location.href = if g.VIEW is 'index' then '#all-pages' else "/#{g.BOARD}/#all-pages"
|
location.href = if g.VIEW is 'index' then '#all-pages' else "/#{g.BOARD}/#all-pages"
|
||||||
when Conf['Open catalog']
|
when Conf['Open catalog']
|
||||||
return if g.BOARD.ID is 'f'
|
return unless (catalog = CatalogLinks.catalog())
|
||||||
location.href = CatalogLinks.catalog()
|
location.href = catalog
|
||||||
when Conf['Cycle sort type']
|
when Conf['Cycle sort type']
|
||||||
return unless Conf['JSON Index'] and g.VIEW is 'index' and g.BOARD.ID isnt 'f'
|
return unless Index.enabled
|
||||||
Index.cycleSortType()
|
Index.cycleSortType()
|
||||||
# Thread Navigation
|
# Thread Navigation
|
||||||
when Conf['Next thread']
|
when Conf['Next thread']
|
||||||
@ -269,7 +269,11 @@ Keybinds =
|
|||||||
key
|
key
|
||||||
|
|
||||||
post: (thread) ->
|
post: (thread) ->
|
||||||
$('.post.highlight', thread) or $('.op', thread)
|
s = g.SITE.selectors
|
||||||
|
(
|
||||||
|
$("#{s.postContainer}#{s.highlightable.reply}.#{g.SITE.classes.highlight}", thread) or
|
||||||
|
$("#{if g.SITE.isOPContainerThread then s.thread else s.postContainer}#{s.highlightable.op}", thread)
|
||||||
|
)
|
||||||
|
|
||||||
qr: (thread) ->
|
qr: (thread) ->
|
||||||
QR.open()
|
QR.open()
|
||||||
@ -309,49 +313,44 @@ Keybinds =
|
|||||||
""
|
""
|
||||||
else "sage"
|
else "sage"
|
||||||
|
|
||||||
img: (thread, all) ->
|
|
||||||
if all
|
|
||||||
ImageExpand.cb.toggleAll()
|
|
||||||
else
|
|
||||||
post = Get.postFromNode Keybinds.post thread
|
|
||||||
ImageExpand.toggle post if post.file
|
|
||||||
|
|
||||||
open: (thread, tab) ->
|
open: (thread, tab) ->
|
||||||
return if g.VIEW isnt 'index'
|
return if g.VIEW isnt 'index'
|
||||||
url = "/#{thread.board}/thread/#{thread}"
|
url = Get.url 'thread', thread
|
||||||
if tab
|
if tab
|
||||||
$.open location.origin + url
|
$.open url
|
||||||
else
|
else
|
||||||
location.href = url
|
location.href = url
|
||||||
|
|
||||||
hl: (delta, thread) ->
|
hl: (delta, thread) ->
|
||||||
postEl = $ '.reply.highlight', thread
|
replySelector = "#{g.SITE.selectors.postContainer}#{g.SITE.selectors.highlightable.reply}"
|
||||||
|
{highlight} = g.SITE.classes
|
||||||
|
|
||||||
|
postEl = $ "#{replySelector}.#{highlight}", thread
|
||||||
|
|
||||||
unless delta
|
unless delta
|
||||||
$.rmClass postEl, 'highlight' if postEl
|
$.rmClass postEl, highlight if postEl
|
||||||
return
|
return
|
||||||
|
|
||||||
if postEl
|
if postEl
|
||||||
{height} = postEl.getBoundingClientRect()
|
{height} = postEl.getBoundingClientRect()
|
||||||
if Header.getTopOf(postEl) >= -height and Header.getBottomOf(postEl) >= -height # We're at least partially visible
|
if Header.getTopOf(postEl) >= -height and Header.getBottomOf(postEl) >= -height # We're at least partially visible
|
||||||
root = postEl.parentNode
|
{root} = Get.postFromNode(postEl).nodes
|
||||||
axis = if delta is +1
|
axis = if delta is +1
|
||||||
'following'
|
'following'
|
||||||
else
|
else
|
||||||
'preceding'
|
'preceding'
|
||||||
return if not (next = $.x "#{axis}-sibling::div[contains(@class,'replyContainer') and not(@hidden) and not(child::div[@class='stub'])][1]/child::div[contains(@class,'reply')]", root)
|
return unless (next = $.x "#{axis}-sibling::#{g.SITE.xpath.replyContainer}[not(@hidden) and not(child::div[@class='stub'])][1]", root)
|
||||||
|
next = $ replySelector, next unless next.matches(replySelector)
|
||||||
Header.scrollToIfNeeded next, delta is +1
|
Header.scrollToIfNeeded next, delta is +1
|
||||||
@focus next
|
$.addClass next, highlight
|
||||||
$.rmClass postEl, 'highlight'
|
$.rmClass postEl, highlight
|
||||||
return
|
return
|
||||||
$.rmClass postEl, 'highlight'
|
$.rmClass postEl, highlight
|
||||||
|
|
||||||
replies = $$ '.reply', thread
|
replies = $$ replySelector, thread
|
||||||
replies.reverse() if delta is -1
|
replies.reverse() if delta is -1
|
||||||
for reply in replies
|
for reply in replies
|
||||||
if delta is +1 and Header.getTopOf(reply) > 0 or delta is -1 and Header.getBottomOf(reply) > 0
|
if delta is +1 and Header.getTopOf(reply) > 0 or delta is -1 and Header.getBottomOf(reply) > 0
|
||||||
@focus reply
|
$.addClass reply, highlight
|
||||||
return
|
return
|
||||||
|
return
|
||||||
focus: (post) ->
|
|
||||||
$.addClass post, 'highlight'
|
|
||||||
|
|||||||
@ -39,22 +39,24 @@ Nav =
|
|||||||
Nav.scroll +1
|
Nav.scroll +1
|
||||||
|
|
||||||
getThread: ->
|
getThread: ->
|
||||||
return $ '.board' if $.hasClass doc, 'catalog-mode'
|
return g.threads["#{g.BOARD}.#{g.THREADID}"].nodes.root if g.VIEW is 'thread'
|
||||||
for threadRoot in $$ '.thread'
|
return if $.hasClass doc, 'catalog-mode'
|
||||||
|
for threadRoot in $$ g.SITE.selectors.thread
|
||||||
thread = Get.threadFromRoot threadRoot
|
thread = Get.threadFromRoot threadRoot
|
||||||
continue if thread.isHidden and !thread.stub
|
continue if thread.isHidden and !thread.stub
|
||||||
if Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height # not scrolled past
|
if Header.getTopOf(threadRoot) >= -threadRoot.getBoundingClientRect().height # not scrolled past
|
||||||
return threadRoot
|
return threadRoot
|
||||||
return $ '.board'
|
return
|
||||||
|
|
||||||
scroll: (delta) ->
|
scroll: (delta) ->
|
||||||
d.activeElement?.blur()
|
d.activeElement?.blur()
|
||||||
thread = Nav.getThread()
|
thread = Nav.getThread()
|
||||||
|
return unless thread
|
||||||
axis = if delta is +1
|
axis = if delta is +1
|
||||||
'following'
|
'following'
|
||||||
else
|
else
|
||||||
'preceding'
|
'preceding'
|
||||||
if next = $.x "#{axis}-sibling::div[contains(@class,'thread') and not(@hidden)][1]", thread
|
if next = $.x "#{axis}-sibling::#{g.SITE.xpath.thread}[not(@hidden)][1]", thread
|
||||||
# Unless we're not at the beginning of the current thread,
|
# Unless we're not at the beginning of the current thread,
|
||||||
# and thus wanting to move to beginning,
|
# and thus wanting to move to beginning,
|
||||||
# or we're above the first thread and don't want to skip it.
|
# or we're above the first thread and don't want to skip it.
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
ThreadUpdater =
|
ThreadUpdater =
|
||||||
init: ->
|
init: ->
|
||||||
return if g.VIEW isnt 'thread' or !Conf['Thread Updater']
|
return if g.VIEW isnt 'thread' or !Conf['Thread Updater']
|
||||||
|
@enabled = true
|
||||||
|
|
||||||
# Chromium won't play audio created in an inactive tab until the tab has been focused, so set it up now.
|
# Chromium won't play audio created in an inactive tab until the tab has been focused, so set it up now.
|
||||||
# XXX Sometimes the loading stalls in Firefox, esp. when opening in private browsing window followed by normal window.
|
# XXX Sometimes the loading stalls in Firefox, esp. when opening in private browsing window followed by normal window.
|
||||||
|
|||||||
@ -628,7 +628,7 @@ QR =
|
|||||||
$.rm nodes.flag
|
$.rm nodes.flag
|
||||||
delete nodes.flag
|
delete nodes.flag
|
||||||
|
|
||||||
if g.BOARD.ID is 'pol'
|
if g.BOARD.config.troll_flags
|
||||||
flag = QR.flags()
|
flag = QR.flags()
|
||||||
flag.dataset.name = 'flag'
|
flag.dataset.name = 'flag'
|
||||||
flag.dataset.default = '0'
|
flag.dataset.default = '0'
|
||||||
|
|||||||
@ -92,7 +92,8 @@ QuoteYou =
|
|||||||
|
|
||||||
cb:
|
cb:
|
||||||
seek: (type) ->
|
seek: (type) ->
|
||||||
$.rmClass highlight, 'highlight' if highlight = $ '.highlight'
|
{highlight} = g.SITE.classes
|
||||||
|
$.rmClass highlighted, highlight if (highlighted = $ ".#{highlight}")
|
||||||
|
|
||||||
unless QuoteYou.lastRead and doc.contains(QuoteYou.lastRead) and $.hasClass(QuoteYou.lastRead, 'quotesYou')
|
unless QuoteYou.lastRead and doc.contains(QuoteYou.lastRead) and $.hasClass(QuoteYou.lastRead, 'quotesYou')
|
||||||
if not (post = QuoteYou.lastRead = $ '.quotesYou')
|
if not (post = QuoteYou.lastRead = $ '.quotesYou')
|
||||||
@ -111,12 +112,16 @@ QuoteYou =
|
|||||||
QuoteYou.cb.scroll posts[if type is 'following' then 0 else posts.length - 1]
|
QuoteYou.cb.scroll posts[if type is 'following' then 0 else posts.length - 1]
|
||||||
|
|
||||||
scroll: (root) ->
|
scroll: (root) ->
|
||||||
post = $ '.post', root
|
post = Get.postFromRoot root
|
||||||
if !post.getBoundingClientRect().height
|
if !post.nodes.post.getBoundingClientRect().height
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
QuoteYou.lastRead = root
|
QuoteYou.lastRead = root
|
||||||
location.href = "##{post.id}"
|
location.href = Get.url('post', post)
|
||||||
Header.scrollTo post
|
Header.scrollTo post.nodes.post
|
||||||
$.addClass post, 'highlight'
|
if post.isReply
|
||||||
|
sel = "#{g.SITE.selectors.postContainer}#{g.SITE.selectors.highlightable.reply}"
|
||||||
|
node = post.nodes.root
|
||||||
|
node = $ sel, node unless node.matches(sel)
|
||||||
|
$.addClass node, g.SITE.classes.highlight
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class Callbacks
|
|||||||
@keys.push name unless @[name]
|
@keys.push name unless @[name]
|
||||||
@[name] = cb
|
@[name] = cb
|
||||||
|
|
||||||
execute: (node, keys=@keys, force) ->
|
execute: (node, keys=@keys, force=false) ->
|
||||||
return if node.callbacksExecuted and !force
|
return if node.callbacksExecuted and !force
|
||||||
node.callbacksExecuted = true
|
node.callbacksExecuted = true
|
||||||
for name in keys
|
for name in keys
|
||||||
|
|||||||
@ -1,7 +1,12 @@
|
|||||||
Post.Clone = class extends Post
|
Post.Clone = class extends Post
|
||||||
isClone: true
|
isClone: true
|
||||||
|
|
||||||
constructor: (@origin, @context, contractThumb) ->
|
constructor: ->
|
||||||
|
that = Object.create(Post.Clone.prototype)
|
||||||
|
that.construct arguments...
|
||||||
|
return that
|
||||||
|
|
||||||
|
construct: (@origin, @context, contractThumb) ->
|
||||||
for key in ['ID', 'postID', 'threadID', 'boardID', 'siteID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']
|
for key in ['ID', 'postID', 'threadID', 'boardID', 'siteID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']
|
||||||
# Copy or point to the origin's key value.
|
# Copy or point to the origin's key value.
|
||||||
@[key] = @origin[key]
|
@[key] = @origin[key]
|
||||||
|
|||||||
@ -825,6 +825,10 @@ Config =
|
|||||||
lastarchivecheck: 0
|
lastarchivecheck: 0
|
||||||
archiveAutoUpdate: true
|
archiveAutoUpdate: true
|
||||||
|
|
||||||
|
externalCatalogURLs: """
|
||||||
|
//catalog.neet.tv/%board/;boards:4chan.org:3,a,adv,an,asp,biz,c,cgl,ck,cm,co,diy,f,fa,fit,g,gd,his,i,int,jp,k,lgbt,lit,m,mlp,mu,n,news,o,out,p,po,pol,s4s,sci,sp,tg,toy,trv,tv,v,vg,vip,vp,vr,w,wg,wsg,wsr,x
|
||||||
|
"""
|
||||||
|
|
||||||
boardnav: """
|
boardnav: """
|
||||||
[ toggle-all ]
|
[ toggle-all ]
|
||||||
a-replace
|
a-replace
|
||||||
@ -1177,3 +1181,5 @@ Config =
|
|||||||
fourchanImageHost: 'i.4cdn.org'
|
fourchanImageHost: 'i.4cdn.org'
|
||||||
|
|
||||||
hiddenPSAList: [{}]
|
hiddenPSAList: [{}]
|
||||||
|
|
||||||
|
knownBanners: '<%= readJSON('banners.json').join(',') %>'
|
||||||
|
|||||||
@ -9,9 +9,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 4chan style fixes */
|
/* 4chan style fixes */
|
||||||
:root.photon.sw-yotsuba #arc-list tr:nth-of-type(odd) span.quote {
|
:root.photon #arc-list tr:nth-of-type(odd) span.quote {
|
||||||
color: #C0E17A;
|
color: #C0E17A;
|
||||||
}
|
}
|
||||||
|
:root.photon.highlight-you .quotesYou$site$highlightable$reply {
|
||||||
|
border-left: 3px solid rgba(221, 0, 0, .8) !important;
|
||||||
|
}
|
||||||
|
:root.photon.highlight-own .yourPost$site$highlightable$reply {
|
||||||
|
border-left: 3px dashed rgba(221, 0, 0, .8) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
:root.photon #header-bar.dialog {
|
:root.photon #header-bar.dialog {
|
||||||
|
|||||||
@ -9,9 +9,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 4chan style fixes */
|
/* 4chan style fixes */
|
||||||
:root.spooky.sw-yotsuba #arc-list span.quote {
|
:root.spooky #arc-list span.quote {
|
||||||
color: #634C2C;
|
color: #634C2C;
|
||||||
}
|
}
|
||||||
|
:root.spooky.highlight-you .quotesYou$site$highlightable$reply {
|
||||||
|
border-left: 3px solid rgba(145, 182, 214, .8) !important;
|
||||||
|
}
|
||||||
|
:root.spooky.highlight-own .yourPost$site$highlightable$reply {
|
||||||
|
border-left: 3px dashed rgba(145, 182, 214, .8) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
:root.spooky #header-bar.dialog {
|
:root.spooky #header-bar.dialog {
|
||||||
@ -67,16 +73,16 @@
|
|||||||
:root.spooky .qphl {
|
:root.spooky .qphl {
|
||||||
outline: 2px solid rgba(145, 182, 214, .8);
|
outline: 2px solid rgba(145, 182, 214, .8);
|
||||||
}
|
}
|
||||||
:root.spooky.highlight-you .quotesYou$site$relative$opHighlight,
|
:root.spooky.highlight-you .quotesYou$site$highlightable$op,
|
||||||
:root.spooky.highlight-you .quotesYou$site$relative$replyPost {
|
:root.spooky.highlight-you .quotesYou$site$highlightable$reply {
|
||||||
border-left: 3px solid rgba(145, 182, 214, .8);
|
border-left: 3px solid rgba(145, 182, 214, .8);
|
||||||
}
|
}
|
||||||
:root.spooky.highlight-own .yourPost$site$relative$opHighlight,
|
:root.spooky.highlight-own .yourPost$site$highlightable$op,
|
||||||
:root.spooky.highlight-own .yourPost$site$relative$replyPost {
|
:root.spooky.highlight-own .yourPost$site$highlightable$reply {
|
||||||
border-left: 3px dashed rgba(145, 182, 214, .8);
|
border-left: 3px dashed rgba(145, 182, 214, .8);
|
||||||
}
|
}
|
||||||
:root.spooky .filter-highlight$site$relative$opHighlight,
|
:root.spooky .filter-highlight$site$highlightable$op,
|
||||||
:root.spooky .filter-highlight$site$relative$replyPost {
|
:root.spooky .filter-highlight$site$highlightable$reply {
|
||||||
box-shadow: inset 5px 0 rgba(145, 182, 214, .5);
|
box-shadow: inset 5px 0 rgba(145, 182, 214, .5);
|
||||||
}
|
}
|
||||||
:root.spooky.highlight-own .yourPost > $site$sideArrows,
|
:root.spooky.highlight-own .yourPost > $site$sideArrows,
|
||||||
|
|||||||
@ -658,7 +658,9 @@ div[data-checked="false"] > .suboption-list {
|
|||||||
.section-advanced textarea {
|
.section-advanced textarea {
|
||||||
height: 150px;
|
height: 150px;
|
||||||
}
|
}
|
||||||
.section-advanced textarea[name="archiveLists"] {
|
.section-advanced textarea[name="archiveLists"],
|
||||||
|
.section-advanced textarea[name="externalCatalogURLs"],
|
||||||
|
.section-advanced textarea[name="knownBanners"] {
|
||||||
height: 75px;
|
height: 75px;
|
||||||
}
|
}
|
||||||
.section-advanced .archive-cell {
|
.section-advanced .archive-cell {
|
||||||
@ -1383,8 +1385,8 @@ input[name="Default Volume"] {
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
/* Fappe and Werk Tyme */
|
/* Fappe and Werk Tyme */
|
||||||
:root.fappeTyme $site$relative$replyOriginal.noFile,
|
:root.fappeTyme $site$replyOriginal.noFile,
|
||||||
:root.fappeTyme $site$relative$replyOriginal.noFile + br {
|
:root.fappeTyme $site$replyOriginal.noFile + br {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
:root.werkTyme $site$thumbLink,
|
:root.werkTyme $site$thumbLink,
|
||||||
@ -1438,16 +1440,16 @@ input[name="Default Volume"] {
|
|||||||
.qphl {
|
.qphl {
|
||||||
outline: 2px solid rgba(216, 94, 49, .8);
|
outline: 2px solid rgba(216, 94, 49, .8);
|
||||||
}
|
}
|
||||||
:root.highlight-you .quotesYou$site$relative$opHighlight,
|
:root.highlight-you .quotesYou$site$highlightable$op,
|
||||||
:root.highlight-you .quotesYou$site$relative$replyPost {
|
:root.highlight-you .quotesYou$site$highlightable$reply {
|
||||||
border-left: 3px solid rgba(221, 0, 0, .8);
|
border-left: 3px solid rgba(221, 0, 0, .8);
|
||||||
}
|
}
|
||||||
:root.highlight-own .yourPost$site$relative$opHighlight,
|
:root.highlight-own .yourPost$site$highlightable$op,
|
||||||
:root.highlight-own .yourPost$site$relative$replyPost {
|
:root.highlight-own .yourPost$site$highlightable$reply {
|
||||||
border-left: 3px dashed rgba(221, 0, 0, .8);
|
border-left: 3px dashed rgba(221, 0, 0, .8);
|
||||||
}
|
}
|
||||||
.filter-highlight$site$relative$opHighlight,
|
.filter-highlight$site$highlightable$op,
|
||||||
.filter-highlight$site$relative$replyPost {
|
.filter-highlight$site$highlightable$reply {
|
||||||
box-shadow: inset 5px 0 rgba(221, 0, 0, .5);
|
box-shadow: inset 5px 0 rgba(221, 0, 0, .5);
|
||||||
}
|
}
|
||||||
:root.highlight-own .yourPost > $site$sideArrows,
|
:root.highlight-own .yourPost > $site$sideArrows,
|
||||||
@ -1455,9 +1457,9 @@ input[name="Default Volume"] {
|
|||||||
.filter-highlight > $site$sideArrows {
|
.filter-highlight > $site$sideArrows {
|
||||||
color: rgba(221, 0, 0, .8);
|
color: rgba(221, 0, 0, .8);
|
||||||
}
|
}
|
||||||
:root.highlight-own .yourPost$site$relative$opHighlight::after,
|
:root.highlight-own .yourPost$site$highlightable$op::after,
|
||||||
:root.highlight-you .quotesYou$site$relative$opHighlight::after,
|
:root.highlight-you .quotesYou$site$highlightable$op::after,
|
||||||
.filter-highlight$site$relative$opHighlight::after {
|
.filter-highlight$site$highlightable$op::after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
clear: both;
|
clear: both;
|
||||||
@ -1466,7 +1468,7 @@ input[name="Default Volume"] {
|
|||||||
:root.werkTyme .catalog-thread.filter-highlight:not(:hover),
|
:root.werkTyme .catalog-thread.filter-highlight:not(:hover),
|
||||||
:root.werkTyme:not(.catalog-hover-expand) .catalog-thread.filter-highlight,
|
:root.werkTyme:not(.catalog-hover-expand) .catalog-thread.filter-highlight,
|
||||||
:root.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post,
|
:root.werkTyme.catalog-hover-expand .catalog-thread.filter-highlight > .catalog-container:hover > .catalog-post,
|
||||||
:root.catalog $site$catalog$thread.filter-highlight$site$relative$catalogHighlight {
|
:root.catalog $site$catalog$thread.filter-highlight$site$highlightable$catalog {
|
||||||
box-shadow: 0 0 3px 3px rgba(255, 0, 0, .5);
|
box-shadow: 0 0 3px 3px rgba(255, 0, 0, .5);
|
||||||
}
|
}
|
||||||
:root:not(.werkTyme) .catalog-thread.watched .catalog-thumb,
|
:root:not(.werkTyme) .catalog-thread.watched .catalog-thumb,
|
||||||
|
|||||||
@ -5,9 +5,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 4chan style fixes */
|
/* 4chan style fixes */
|
||||||
:root.tomorrow.sw-yotsuba #arc-list span.quote {
|
:root.tomorrow #arc-list span.quote {
|
||||||
color: #B5BD68;
|
color: #B5BD68;
|
||||||
}
|
}
|
||||||
|
:root.tomorrow.highlight-you .quotesYou$site$highlightable$reply {
|
||||||
|
border-left: 3px solid rgba(145, 182, 214, .8) !important;
|
||||||
|
}
|
||||||
|
:root.tomorrow.highlight-own .yourPost$site$highlightable$reply {
|
||||||
|
border-left: 3px dashed rgba(145, 182, 214, .8) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
:root.tomorrow #header-bar.dialog {
|
:root.tomorrow #header-bar.dialog {
|
||||||
@ -63,16 +69,16 @@
|
|||||||
:root.tomorrow .qphl {
|
:root.tomorrow .qphl {
|
||||||
outline: 2px solid rgba(145, 182, 214, .8);
|
outline: 2px solid rgba(145, 182, 214, .8);
|
||||||
}
|
}
|
||||||
:root.tomorrow.highlight-you .quotesYou$site$relative$opHighlight,
|
:root.tomorrow.highlight-you .quotesYou$site$highlightable$op,
|
||||||
:root.tomorrow.highlight-you .quotesYou$site$relative$replyPost {
|
:root.tomorrow.highlight-you .quotesYou$site$highlightable$reply {
|
||||||
border-left: 3px solid rgba(145, 182, 214, .8);
|
border-left: 3px solid rgba(145, 182, 214, .8);
|
||||||
}
|
}
|
||||||
:root.tomorrow.highlight-own .yourPost$site$relative$opHighlight,
|
:root.tomorrow.highlight-own .yourPost$site$highlightable$op,
|
||||||
:root.tomorrow.highlight-own .yourPost$site$relative$replyPost {
|
:root.tomorrow.highlight-own .yourPost$site$highlightable$reply {
|
||||||
border-left: 3px dashed rgba(145, 182, 214, .8);
|
border-left: 3px dashed rgba(145, 182, 214, .8);
|
||||||
}
|
}
|
||||||
:root.tomorrow .filter-highlight$site$relative$opHighlight,
|
:root.tomorrow .filter-highlight$site$highlightable$op,
|
||||||
:root.tomorrow .filter-highlight$site$relative$replyPost {
|
:root.tomorrow .filter-highlight$site$highlightable$reply {
|
||||||
box-shadow: inset 5px 0 rgba(145, 182, 214, .5);
|
box-shadow: inset 5px 0 rgba(145, 182, 214, .5);
|
||||||
}
|
}
|
||||||
:root.tomorrow.highlight-own .yourPost > $site$sideArrows,
|
:root.tomorrow.highlight-own .yourPost > $site$sideArrows,
|
||||||
|
|||||||
@ -8,6 +8,14 @@
|
|||||||
border-color: #98E;
|
border-color: #98E;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 4chan style fixes */
|
||||||
|
:root.yotsuba-b.highlight-you .quotesYou$site$highlightable$reply {
|
||||||
|
border-left: 3px solid rgba(221, 0, 0, .8) !important;
|
||||||
|
}
|
||||||
|
:root.yotsuba-b.highlight-own .yourPost$site$highlightable$reply {
|
||||||
|
border-left: 3px dashed rgba(221, 0, 0, .8) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
:root.yotsuba-b #header-bar.dialog {
|
:root.yotsuba-b #header-bar.dialog {
|
||||||
background-color: rgba(214,218,240,0.98);
|
background-color: rgba(214,218,240,0.98);
|
||||||
|
|||||||
@ -8,6 +8,14 @@
|
|||||||
border-color: #EA8;
|
border-color: #EA8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 4chan style fixes */
|
||||||
|
:root.yotsuba.highlight-you .quotesYou$site$highlightable$reply {
|
||||||
|
border-left: 3px solid rgba(221, 0, 0, .8) !important;
|
||||||
|
}
|
||||||
|
:root.yotsuba.highlight-own .yourPost$site$highlightable$reply {
|
||||||
|
border-left: 3px dashed rgba(221, 0, 0, .8) !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
:root.yotsuba #header-bar.dialog {
|
:root.yotsuba #header-bar.dialog {
|
||||||
background-color: rgba(240,224,214,0.98);
|
background-color: rgba(240,224,214,0.98);
|
||||||
|
|||||||
@ -123,11 +123,30 @@ Main =
|
|||||||
<%= html(meta.name + ' has been updated to <a href="' + meta.changelog + '" target="_blank">version ${g.VERSION}</a>.') %>
|
<%= html(meta.name + ' has been updated to <a href="' + meta.changelog + '" target="_blank">version ${g.VERSION}</a>.') %>
|
||||||
new Notice 'info', el, 15
|
new Notice 'info', el, 15
|
||||||
|
|
||||||
initFeatures: ->
|
parseURL: (site=g.SITE, url=location) ->
|
||||||
{hostname, search} = location
|
r = {}
|
||||||
pathname = location.pathname.split /\/+/
|
|
||||||
g.BOARD = new Board pathname[1] unless hostname in ['www.4chan.org', 'www.4channel.org']
|
|
||||||
|
|
||||||
|
return r if !site
|
||||||
|
r.siteID = site.ID
|
||||||
|
|
||||||
|
return r if site.isBoardlessPage?(url)
|
||||||
|
pathname = url.pathname.split /\/+/
|
||||||
|
r.boardID = pathname[1]
|
||||||
|
|
||||||
|
if site.isFileURL(url)
|
||||||
|
r.VIEW = 'file'
|
||||||
|
else if site.isAuxiliaryPage?(url)
|
||||||
|
# pass
|
||||||
|
else if pathname[2] in ['thread', 'res']
|
||||||
|
r.VIEW = 'thread'
|
||||||
|
r.threadID = r.THREADID = +pathname[3].replace(/\.\w+$/, '')
|
||||||
|
else if /^(?:catalog|archive)(?:\.\w+)?$/.test(pathname[2])
|
||||||
|
r.VIEW = pathname[2].replace(/\.\w+$/, '')
|
||||||
|
else if /^(?:index|\d*)(?:\.\w+)?$/.test(pathname[2])
|
||||||
|
r.VIEW = 'index'
|
||||||
|
r
|
||||||
|
|
||||||
|
initFeatures: ->
|
||||||
$.global ->
|
$.global ->
|
||||||
document.documentElement.classList.add 'js-enabled'
|
document.documentElement.classList.add 'js-enabled'
|
||||||
window.FCX = {}
|
window.FCX = {}
|
||||||
@ -136,29 +155,17 @@ Main =
|
|||||||
# XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638
|
# XXX https://bugs.chromium.org/p/chromium/issues/detail?id=920638
|
||||||
$.ajaxPageInit?()
|
$.ajaxPageInit?()
|
||||||
|
|
||||||
switch hostname
|
$.extend g, Main.parseURL()
|
||||||
when 'www.4chan.org', 'www.4channel.org'
|
g.BOARD = new Board g.boardID if g.boardID
|
||||||
$.onExists doc, 'body', -> $.addStyle CSS.www
|
|
||||||
Captcha.replace.init()
|
|
||||||
return
|
|
||||||
when 'sys.4chan.org', 'sys.4channel.org'
|
|
||||||
if pathname[2] is 'imgboard.php'
|
|
||||||
if /\bmode=report\b/.test search
|
|
||||||
Report.init()
|
|
||||||
else if (match = search.match /\bres=(\d+)/)
|
|
||||||
$.ready ->
|
|
||||||
if Conf['404 Redirect'] and $.id('errmsg')?.textContent is 'Error: Specified thread does not exist.'
|
|
||||||
Redirect.navigate 'thread', {
|
|
||||||
boardID: g.BOARD.ID
|
|
||||||
postID: +match[1]
|
|
||||||
}
|
|
||||||
else if pathname[2] is 'post'
|
|
||||||
PostSuccessful.init()
|
|
||||||
return
|
|
||||||
|
|
||||||
if g.SITE.isFileURL()
|
if !g.VIEW
|
||||||
|
g.SITE.initAuxiliary?()
|
||||||
|
return
|
||||||
|
|
||||||
|
if g.VIEW is 'file'
|
||||||
$.asap (-> d.readyState isnt 'loading'), ->
|
$.asap (-> d.readyState isnt 'loading'), ->
|
||||||
if g.SITE.software is 'yotsuba' and Conf['404 Redirect'] and g.SITE.is404?()
|
if g.SITE.software is 'yotsuba' and Conf['404 Redirect'] and g.SITE.is404?()
|
||||||
|
pathname = location.pathname.split /\/+/
|
||||||
Redirect.navigate 'file', {
|
Redirect.navigate 'file', {
|
||||||
boardID: g.BOARD.ID
|
boardID: g.BOARD.ID
|
||||||
filename: pathname[pathname.length - 1]
|
filename: pathname[pathname.length - 1]
|
||||||
@ -173,18 +180,6 @@ Main =
|
|||||||
ImageCommon.addControls video
|
ImageCommon.addControls video
|
||||||
return
|
return
|
||||||
|
|
||||||
return if g.SITE.isAuxiliaryPage?()
|
|
||||||
|
|
||||||
if pathname[2] in ['thread', 'res']
|
|
||||||
g.VIEW = 'thread'
|
|
||||||
g.THREADID = +pathname[3].replace(/\.\w+$/, '')
|
|
||||||
else if /^(?:catalog|archive)(?:\.\w+)?$/.test(pathname[2])
|
|
||||||
g.VIEW = pathname[2].replace(/\.\w+$/, '')
|
|
||||||
else if /^(?:index|\d*)(?:\.\w+)?$/.test(pathname[2])
|
|
||||||
g.VIEW = 'index'
|
|
||||||
else
|
|
||||||
return
|
|
||||||
|
|
||||||
g.threads = new SimpleDict()
|
g.threads = new SimpleDict()
|
||||||
g.posts = new SimpleDict()
|
g.posts = new SimpleDict()
|
||||||
|
|
||||||
|
|||||||
@ -4,12 +4,10 @@ SW.tinyboard =
|
|||||||
threadModTimeIgnoresSage: true
|
threadModTimeIgnoresSage: true
|
||||||
|
|
||||||
disabledFeatures: [
|
disabledFeatures: [
|
||||||
'Index Generator'
|
|
||||||
'Resurrect Quotes'
|
'Resurrect Quotes'
|
||||||
'Quick Reply Personas'
|
'Quick Reply Personas'
|
||||||
'Quick Reply'
|
'Quick Reply'
|
||||||
'Cooldown'
|
'Cooldown'
|
||||||
'Index Generator (Menu)'
|
|
||||||
'Report Link'
|
'Report Link'
|
||||||
'Delete Link'
|
'Delete Link'
|
||||||
'Edit Link'
|
'Edit Link'
|
||||||
@ -47,6 +45,9 @@ SW.tinyboard =
|
|||||||
|
|
||||||
urls:
|
urls:
|
||||||
thread: ({siteID, boardID, threadID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/res/#{threadID}.html"
|
thread: ({siteID, boardID, threadID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/res/#{threadID}.html"
|
||||||
|
post: ({postID}) -> "##{postID}"
|
||||||
|
index: ({siteID, boardID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/"
|
||||||
|
catalog: ({siteID, boardID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/catalog.html"
|
||||||
threadJSON: ({siteID, boardID, threadID}) ->
|
threadJSON: ({siteID, boardID, threadID}) ->
|
||||||
root = Conf['siteProperties'][siteID]?.root
|
root = Conf['siteProperties'][siteID]?.root
|
||||||
if root then "#{root}#{boardID}/res/#{threadID}.json" else ''
|
if root then "#{root}#{boardID}/res/#{threadID}.json" else ''
|
||||||
@ -68,6 +69,7 @@ SW.tinyboard =
|
|||||||
summary: '.omitted'
|
summary: '.omitted'
|
||||||
postContainer: 'div[id^="reply_"]:not(.hidden)' # postContainer is thread for OP
|
postContainer: 'div[id^="reply_"]:not(.hidden)' # postContainer is thread for OP
|
||||||
opBottom: '.op'
|
opBottom: '.op'
|
||||||
|
replyOriginal: 'div[id^="reply_"]:not(.hidden)'
|
||||||
infoRoot: '.intro'
|
infoRoot: '.intro'
|
||||||
info:
|
info:
|
||||||
subject: '.subject'
|
subject: '.subject'
|
||||||
@ -90,11 +92,10 @@ SW.tinyboard =
|
|||||||
thumb: 'a > .post-image'
|
thumb: 'a > .post-image'
|
||||||
thumbLink: '.file > a'
|
thumbLink: '.file > a'
|
||||||
multifile: '.files > .file'
|
multifile: '.files > .file'
|
||||||
relative:
|
highlightable:
|
||||||
opHighlight: ' > .op'
|
op: ' > .op'
|
||||||
replyPost: '.reply'
|
reply: '.reply'
|
||||||
replyOriginal: 'div[id^="reply_"]:not(.hidden)'
|
catalog: ' > .thread'
|
||||||
catalogHighlight: ' > .thread'
|
|
||||||
comment: '.body'
|
comment: '.body'
|
||||||
spoiler: '.spoiler'
|
spoiler: '.spoiler'
|
||||||
quotelink: 'a[onclick^="highlightReply("]'
|
quotelink: 'a[onclick^="highlightReply("]'
|
||||||
@ -106,10 +107,17 @@ SW.tinyboard =
|
|||||||
boardListBottom: '.boardlist.bottom'
|
boardListBottom: '.boardlist.bottom'
|
||||||
styleSheet: '#stylesheet'
|
styleSheet: '#stylesheet'
|
||||||
psa: '.blotter'
|
psa: '.blotter'
|
||||||
|
nav:
|
||||||
|
prev: '.pages > form > [value=Previous]'
|
||||||
|
next: '.pages > form > [value=Next]'
|
||||||
|
|
||||||
|
classes:
|
||||||
|
highlight: 'highlighted'
|
||||||
|
|
||||||
xpath:
|
xpath:
|
||||||
thread: 'div[starts-with(@id,"thread_")]'
|
thread: 'div[starts-with(@id,"thread_")]'
|
||||||
postContainer: 'div[starts-with(@id,"reply_") or starts-with(@id,"thread_")]'
|
postContainer: 'div[starts-with(@id,"reply_") or starts-with(@id,"thread_")]'
|
||||||
|
replyContainer: 'div[starts-with(@id,"reply_")]'
|
||||||
|
|
||||||
regexp:
|
regexp:
|
||||||
quotelink:
|
quotelink:
|
||||||
@ -154,8 +162,8 @@ SW.tinyboard =
|
|||||||
bgColoredEl: ->
|
bgColoredEl: ->
|
||||||
$.el 'div', className: 'post reply'
|
$.el 'div', className: 'post reply'
|
||||||
|
|
||||||
isFileURL: ->
|
isFileURL: (url) ->
|
||||||
/\/src\/[^\/]+/.test(location.pathname)
|
/\/src\/[^\/]+/.test(url.pathname)
|
||||||
|
|
||||||
parseNodes: (post, nodes) ->
|
parseNodes: (post, nodes) ->
|
||||||
# Add vichan's span.poster_id around the ID if not already present.
|
# Add vichan's span.poster_id around the ID if not already present.
|
||||||
|
|||||||
@ -4,6 +4,9 @@ SW.yotsuba =
|
|||||||
|
|
||||||
urls:
|
urls:
|
||||||
thread: ({boardID, threadID}) -> "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/thread/#{threadID}"
|
thread: ({boardID, threadID}) -> "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/thread/#{threadID}"
|
||||||
|
post: ({postID}) -> "#p#{postID}"
|
||||||
|
index: ({boardID}) -> "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/"
|
||||||
|
catalog: ({boardID}) -> if boardID is 'f' then undefined else "#{location.protocol}//#{BoardConfig.domain(boardID)}/#{boardID}/catalog"
|
||||||
threadJSON: ({boardID, threadID}) -> "#{location.protocol}//a.4cdn.org/#{boardID}/thread/#{threadID}.json"
|
threadJSON: ({boardID, threadID}) -> "#{location.protocol}//a.4cdn.org/#{boardID}/thread/#{threadID}.json"
|
||||||
threadsListJSON: ({boardID}) -> "#{location.protocol}//a.4cdn.org/#{boardID}/threads.json"
|
threadsListJSON: ({boardID}) -> "#{location.protocol}//a.4cdn.org/#{boardID}/threads.json"
|
||||||
archiveListJSON: ({boardID}) -> if BoardConfig.isArchived(boardID) then "#{location.protocol}//a.4cdn.org/#{boardID}/archive.json" else ''
|
archiveListJSON: ({boardID}) -> if BoardConfig.isArchived(boardID) then "#{location.protocol}//a.4cdn.org/#{boardID}/archive.json" else ''
|
||||||
@ -14,8 +17,9 @@ SW.yotsuba =
|
|||||||
thumb: ({boardID}, filename) ->
|
thumb: ({boardID}, filename) ->
|
||||||
"#{location.protocol}//#{ImageHost.thumbHost()}/#{boardID}/#{filename}"
|
"#{location.protocol}//#{ImageHost.thumbHost()}/#{boardID}/#{filename}"
|
||||||
|
|
||||||
isPrunedByAge: ({boardID}) -> boardID is 'f'
|
isPrunedByAge: ({boardID}) -> boardID is 'f'
|
||||||
areMD5sDeferred: ({boardID}) -> boardID is 'f'
|
areMD5sDeferred: ({boardID}) -> boardID is 'f'
|
||||||
|
isOnePage: ({boardID}) -> boardID is 'f'
|
||||||
noAudio: ({boardID}) -> BoardConfig.noAudio(boardID)
|
noAudio: ({boardID}) -> BoardConfig.noAudio(boardID)
|
||||||
|
|
||||||
selectors:
|
selectors:
|
||||||
@ -24,6 +28,7 @@ SW.yotsuba =
|
|||||||
threadDivider: '.board > hr'
|
threadDivider: '.board > hr'
|
||||||
summary: '.summary'
|
summary: '.summary'
|
||||||
postContainer: '.postContainer'
|
postContainer: '.postContainer'
|
||||||
|
replyOriginal: '.replyContainer:not([data-clone])'
|
||||||
sideArrows: 'div.sideArrows'
|
sideArrows: 'div.sideArrows'
|
||||||
post: '.post'
|
post: '.post'
|
||||||
infoRoot: '.postInfo'
|
infoRoot: '.postInfo'
|
||||||
@ -50,11 +55,10 @@ SW.yotsuba =
|
|||||||
link: '.fileText > a'
|
link: '.fileText > a'
|
||||||
thumb: 'a.fileThumb > [data-md5]'
|
thumb: 'a.fileThumb > [data-md5]'
|
||||||
thumbLink: 'a.fileThumb'
|
thumbLink: 'a.fileThumb'
|
||||||
relative:
|
highlightable:
|
||||||
opHighlight: '.opContainer'
|
op: '.opContainer'
|
||||||
replyPost: ' > .reply'
|
reply: ' > .reply'
|
||||||
replyOriginal: '.replyContainer:not([data-clone])'
|
catalog: ''
|
||||||
catalogHighlight: ''
|
|
||||||
comment: '.postMessage'
|
comment: '.postMessage'
|
||||||
spoiler: 's'
|
spoiler: 's'
|
||||||
quotelink: ':not(pre) > .quotelink' # XXX https://github.com/4chan/4chan-JS/issues/77: 4chan currently creates quote links inside [code] tags; ignore them
|
quotelink: ':not(pre) > .quotelink' # XXX https://github.com/4chan/4chan-JS/issues/77: 4chan currently creates quote links inside [code] tags; ignore them
|
||||||
@ -67,10 +71,18 @@ SW.yotsuba =
|
|||||||
styleSheet: 'link[title=switch]'
|
styleSheet: 'link[title=switch]'
|
||||||
psa: '#globalMessage'
|
psa: '#globalMessage'
|
||||||
psaTop: '#globalToggle'
|
psaTop: '#globalToggle'
|
||||||
|
searchBox: '#search-box'
|
||||||
|
nav:
|
||||||
|
prev: '.prev > form > [type=submit]'
|
||||||
|
next: '.next > form > [type=submit]'
|
||||||
|
|
||||||
|
classes:
|
||||||
|
highlight: 'highlight'
|
||||||
|
|
||||||
xpath:
|
xpath:
|
||||||
thread: 'div[contains(concat(" ",@class," ")," thread ")]'
|
thread: 'div[contains(concat(" ",@class," ")," thread ")]'
|
||||||
postContainer: 'div[contains(@class,"postContainer")]'
|
postContainer: 'div[contains(@class,"postContainer")]'
|
||||||
|
replyContainer: 'div[contains(@class,"replyContainer")]'
|
||||||
|
|
||||||
regexp:
|
regexp:
|
||||||
quotelink:
|
quotelink:
|
||||||
@ -105,11 +117,36 @@ SW.yotsuba =
|
|||||||
isIncomplete: ->
|
isIncomplete: ->
|
||||||
return g.VIEW in ['index', 'thread'] and not $('.board + *')
|
return g.VIEW in ['index', 'thread'] and not $('.board + *')
|
||||||
|
|
||||||
isAuxiliaryPage: ->
|
isBoardlessPage: (url) ->
|
||||||
location.hostname not in ['boards.4chan.org', 'boards.4channel.org']
|
url.hostname in ['www.4chan.org', 'www.4channel.org']
|
||||||
|
|
||||||
isFileURL: ->
|
isAuxiliaryPage: (url) ->
|
||||||
ImageHost.test(location.hostname)
|
url.hostname not in ['boards.4chan.org', 'boards.4channel.org']
|
||||||
|
|
||||||
|
isFileURL: (url) ->
|
||||||
|
ImageHost.test(url.hostname)
|
||||||
|
|
||||||
|
initAuxiliary: ->
|
||||||
|
switch location.hostname
|
||||||
|
when 'www.4chan.org', 'www.4channel.org'
|
||||||
|
$.onExists doc, 'body', -> $.addStyle CSS.www
|
||||||
|
Captcha.replace.init()
|
||||||
|
return
|
||||||
|
when 'sys.4chan.org', 'sys.4channel.org'
|
||||||
|
pathname = location.pathname.split /\/+/
|
||||||
|
if pathname[2] is 'imgboard.php'
|
||||||
|
if /\bmode=report\b/.test location.search
|
||||||
|
Report.init()
|
||||||
|
else if (match = location.search.match /\bres=(\d+)/)
|
||||||
|
$.ready ->
|
||||||
|
if Conf['404 Redirect'] and $.id('errmsg')?.textContent is 'Error: Specified thread does not exist.'
|
||||||
|
Redirect.navigate 'thread', {
|
||||||
|
boardID: g.BOARD.ID
|
||||||
|
postID: +match[1]
|
||||||
|
}
|
||||||
|
else if pathname[2] is 'post'
|
||||||
|
PostSuccessful.init()
|
||||||
|
return
|
||||||
|
|
||||||
scriptData: ->
|
scriptData: ->
|
||||||
for script in $$ 'script:not([src])', d.head
|
for script in $$ 'script:not([src])', d.head
|
||||||
|
|||||||
@ -6,14 +6,10 @@ Site =
|
|||||||
|
|
||||||
init: (cb) ->
|
init: (cb) ->
|
||||||
$.extend Conf['siteProperties'], Site.defaultProperties
|
$.extend Conf['siteProperties'], Site.defaultProperties
|
||||||
{hostname} = location
|
hostname = Site.resolve()
|
||||||
while hostname and hostname not of Conf['siteProperties']
|
if hostname and Conf['siteProperties'][hostname].software of SW
|
||||||
hostname = hostname.replace(/^[^.]*\.?/, '')
|
@set hostname
|
||||||
if hostname
|
cb()
|
||||||
hostname = canonical if (canonical = Conf['siteProperties'][hostname].canonical)
|
|
||||||
if Conf['siteProperties'][hostname].software of SW
|
|
||||||
@set hostname
|
|
||||||
cb()
|
|
||||||
$.onExists doc, 'body', =>
|
$.onExists doc, 'body', =>
|
||||||
for software of SW when (changes = SW[software].detect?())
|
for software of SW when (changes = SW[software].detect?())
|
||||||
changes.software = software
|
changes.software = software
|
||||||
@ -31,6 +27,18 @@ Site =
|
|||||||
return
|
return
|
||||||
return
|
return
|
||||||
|
|
||||||
|
resolve: (url=location) ->
|
||||||
|
{hostname} = url
|
||||||
|
while hostname and hostname not of Conf['siteProperties']
|
||||||
|
hostname = hostname.replace(/^[^.]*\.?/, '')
|
||||||
|
if hostname
|
||||||
|
hostname = canonical if (canonical = Conf['siteProperties'][hostname].canonical)
|
||||||
|
hostname
|
||||||
|
|
||||||
|
parseURL: (url) ->
|
||||||
|
siteID = Site.resolve url
|
||||||
|
Main.parseURL g.sites[siteID], url
|
||||||
|
|
||||||
set: (hostname) ->
|
set: (hostname) ->
|
||||||
for ID, properties of Conf['siteProperties']
|
for ID, properties of Conf['siteProperties']
|
||||||
continue if properties.canonical
|
continue if properties.canonical
|
||||||
|
|||||||
@ -15,5 +15,5 @@ for ext in ['jpg', 'png', 'gif']:
|
|||||||
print(banner, status)
|
print(banner, status)
|
||||||
if status == 200:
|
if status == 200:
|
||||||
banners.append(banner)
|
banners.append(banner)
|
||||||
with open('src/Miscellaneous/Banner/banners.json', 'w') as f:
|
with open('src/config/banners.json', 'w') as f:
|
||||||
f.write(json.dumps(banners))
|
f.write(json.dumps(banners))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user