4chan-x/src/site/SW.tinyboard.coffee

200 lines
6.4 KiB
CoffeeScript

SW.tinyboard =
isOPContainerThread: true
mayLackJSON: true
threadModTimeIgnoresSage: true
disabledFeatures: [
'Resurrect Quotes'
'Quick Reply Personas'
'Quick Reply'
'Cooldown'
'Report Link'
'Delete Link'
'Edit Link'
'Quote Inlining'
'Quote Previewing'
'Quote Backlinks'
'File Info Formatting'
'Image Expansion'
'Image Expansion (Menu)'
'Comment Expansion'
'Thread Expansion'
'Favicon'
'Quote Threading'
'Thread Updater'
'Banner'
'Flash Features'
'Reply Pruning'
<% if (readJSON('/.tests_enabled')) { %>
'Build Test'
<% } %>
]
detect: ->
for script in $$ 'script:not([src])', d.head
if (m = script.textContent.match(/\bvar configRoot=(".*?")/))
properties = {}
try
root = JSON.parse m[1]
if root[0] is '/'
properties.root = location.origin + root
else if /^https?:/.test(root)
properties.root = root
return properties
false
urls:
thread: ({siteID, boardID, threadID}) -> "#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/res/#{threadID}.html"
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}) ->
root = Conf['siteProperties'][siteID]?.root
if root then "#{root}#{boardID}/res/#{threadID}.json" else ''
threadsListJSON: ({siteID, boardID}) ->
root = Conf['siteProperties'][siteID]?.root
if root then "#{root}#{boardID}/threads.json" else ''
catalogJSON: ({siteID, boardID}) ->
root = Conf['siteProperties'][siteID]?.root
if root then "#{root}#{boardID}/catalog.json" else ''
file: ({siteID, boardID}, filename) ->
"#{Conf['siteProperties'][siteID]?.root or "http://#{siteID}/"}#{boardID}/#{filename}"
thumb: (board, filename) ->
SW.tinyboard.urls.file board, filename
selectors:
board: 'form[name="postcontrols"]'
thread: 'input[name="board"] ~ div[id^="thread_"]'
threadDivider: 'div[id^="thread_"] > hr:last-of-type'
summary: '.omitted'
postContainer: 'div[id^="reply_"]:not(.hidden)' # postContainer is thread for OP
opBottom: '.op'
infoRoot: '.intro'
info:
subject: '.subject'
name: '.name'
email: '.email'
tripcode: '.trip'
uniqueID: '.poster_id'
capcode: '.capcode'
flag: '.flag'
date: 'time'
nameBlock: 'label'
quote: 'a[href*="#q"]'
reply: 'a[href*="/res/"]:not([href*="#"])'
icons:
isSticky: '.fa-thumb-tack'
isClosed: '.fa-lock'
file:
text: '.fileinfo'
link: '.fileinfo > a'
thumb: 'a > .post-image'
thumbLink: '.file > a'
multifile: '.files > .file'
relative:
opHighlight: ' > .op'
replyPost: '.reply'
replyOriginal: 'div[id^="reply_"]:not(.hidden)'
catalogHighlight: ' > .thread'
comment: '.body'
spoiler: '.spoiler'
quotelink: 'a[onclick^="highlightReply("]'
catalog:
board: '#Grid'
thread: '.mix'
thumb: '.thread-image'
boardList: '.boardlist'
boardListBottom: '.boardlist.bottom'
styleSheet: '#stylesheet'
psa: '.blotter'
nav:
prev: '.pages > form > [value=Previous]'
next: '.pages > form > [value=Next]'
xpath:
thread: 'div[starts-with(@id,"thread_")]'
postContainer: 'div[starts-with(@id,"reply_") or starts-with(@id,"thread_")]'
regexp:
quotelink:
///
/
([^/]+) # boardID
/res/
(\d+) # threadID
\.\w+#
(\d+) # postID
$
///
quotelinkHTML:
/<a [^>]*\bhref="[^"]*\/([^\/]+)\/res\/(\d+)\.\w+#(\d+)"/g
Build:
parseJSON: (data, board) ->
o = SW.yotsuba.Build.parseJSON(data, board)
if data.ext is 'deleted'
delete o.file
$.extend o,
files: []
fileDeleted: true
filesDeleted: [0]
if data.extra_files
for extra_file, i in data.extra_files
if extra_file.ext is 'deleted'
o.filesDeleted.push i
else
file = SW.yotsuba.Build.parseJSONFile(data, board)
o.files.push file
if o.files.length
o.file = o.files[0]
o
parseComment: (html) ->
html = html
.replace(/<br\b[^<]*>/gi, '\n')
.replace(/<[^>]*>/g, '')
$.unescape html
bgColoredEl: ->
$.el 'div', className: 'post reply'
isFileURL: (url) ->
/\/src\/[^\/]+/.test(url.pathname)
parseNodes: (post, nodes) ->
# Add vichan's span.poster_id around the ID if not already present.
return if nodes.uniqueID
nodes.info.normalize()
{nextSibling} = nodes.nameBlock
if nextSibling.nodeType is 3 and (m = nextSibling.textContent.match /(\s*ID:\s*)(\S+)/)
nextSibling = nextSibling.splitText m[1].length
nextSibling.splitText m[2].length
nodes.uniqueID = uniqueID = $.el 'span', {className: 'poster_id'}
$.replace nextSibling, uniqueID
$.add uniqueID, nextSibling
parseFile: (post, file) ->
{text, link, thumb} = file
return false if $.x("ancestor::#{@xpath.postContainer}[1]", text) isnt post.nodes.root # file belongs to a reply
return false if not (infoNode = if '(' in link.nextSibling?.textContent then link.nextSibling else link.nextElementSibling)
return false if not (info = infoNode.textContent.match /\((Spoiler Image, )?([\d.]+ [KMG]?B).*\)/)
nameNode = $ '.postfilename', text
$.extend file,
name: if nameNode then (nameNode.title or nameNode.textContent) else link.pathname.match(/[^/]*$/)[0]
size: info[2]
dimensions: info[0].match(/\d+x\d+/)?[0]
if thumb
$.extend file,
thumbURL: if /\/static\//.test(thumb.src) and /\.(?:gif|jpe?g|png)$/.test(link.href) then link.href else thumb.src
isSpoiler: !!info[1] or link.textContent is 'Spoiler Image'
true
isThumbExpanded: (file) ->
# Detect old Tinyboard image expansion that changes src attribute on thumbnail.
$.hasClass file.thumb.parentNode, 'expanded'
isLinkified: (link) ->
/\bnofollow\b/.test(link.rel)
catalogPin: (threadRoot) ->
threadRoot.dataset.sticky = 'true'