4chan-x/src/classes/Post.coffee

272 lines
8.6 KiB
CoffeeScript

class Post
@callbacks = new Callbacks 'Post'
toString: -> @ID
constructor: (root, @thread, @board) ->
<% if (tests_enabled) { %>
root2 = root.cloneNode true
for el in $$ '.mobile', root2
$.rm el
for el in $$ 'a[href]', root2
href = el.href
href = href.replace /(^\w+:\/\/boards.4chan.org\/[^\/]+\/thread\/\d+)\/.*/, '$1'
el.setAttribute 'href', href
for el in $$ 'a[rel=canonical]', root2
el.removeAttribute 'rel'
for el in $$ 'img[src]', root2
el.src = el.src.replace /(spoiler-\w+)\d(\.png)$/, '$11$2'
Fourchan.code.call nodes: comment: $ '.postMessage', root2
for el in $$ 'pre[style=""]', root2
el.removeAttribute 'style'
textNodes = $.X './/text()', root2
i = 0
while node = textNodes.snapshotItem i++
node.data = node.data.replace /\s+/g, ' '
$.rm node if node.data is ''
@normalizedOriginal = root2
<% } %>
@ID = +root.id[2..]
@fullID = "#{@board}.#{@ID}"
@context = @
root.dataset.fullID = @fullID
post = $ '.post', root
info = $ '.postInfo', post
@nodes =
root: root
post: post
info: info
nameBlock: $ '.nameBlock', info
quote: $ '.postNum > a:nth-of-type(2)', info
comment: $ '.postMessage', post
links: []
quotelinks: []
# XXX Edge invalidates HTMLCollections when an ancestor node is inserted into another node.
# https://connect.microsoft.com/IE/feedback/details/1198967/ie11-appendchild-provoke-an-error-on-an-htmlcollection
if $.engine is 'edge'
Object.defineProperty @nodes, 'backlinks',
configurable: true
enumerable: true
get: -> info.getElementsByClassName 'backlink'
else
@nodes.backlinks = info.getElementsByClassName 'backlink'
unless @isReply = $.hasClass post, 'reply'
@thread.OP = @
@thread.isArchived = !!$ '.archivedIcon', info
@thread.isSticky = !!$ '.stickyIcon', info
@thread.isClosed = @thread.isArchived or !!$ '.closedIcon', info
@thread.kill() if @thread.isArchived
@info = {}
@info.nameBlock = if Conf['Anonymize']
'Anonymous'
else
@nodes.nameBlock.textContent.trim()
if subject = $ '.subject', info
@nodes.subject = subject
@info.subject = subject.textContent or undefined
if name = $ '.name', info
@nodes.name = name
@info.name = name.textContent
if email = $ '.useremail', info
@nodes.email = email
@info.email = decodeURIComponent email.href[7..]
if tripcode = $ '.postertrip', info
@nodes.tripcode = tripcode
@info.tripcode = tripcode.textContent
if uniqueID = $ '.posteruid', info
@nodes.uniqueID = uniqueID
@info.uniqueID = uniqueID.firstElementChild.textContent
if capcode = $ '.capcode.hand', info
@nodes.capcode = capcode
@info.capcode = capcode.textContent.replace '## ', ''
if flag = $ '.flag, .countryFlag', info
@nodes.flag = flag
@info.flag = flag.title
if date = $ '.dateTime', info
@nodes.date = date
@info.date = new Date date.dataset.utc * 1000
@parseComment()
@parseQuotes()
@parseFile()
@isDead = false
@isHidden = false
@clones = []
if g.posts[@fullID]
@isRebuilt = true
@clones = g.posts[@fullID].clones
clone.origin = @ for clone in @clones
@board.posts.push @ID, @
@thread.posts.push @ID, @
g.posts.push @fullID, @
parseComment: ->
# Merge text nodes and remove empty ones.
@nodes.comment.normalize()
# Get the comment's text.
# <br> -> \n
# Remove:
# 'Comment too long'...
# EXIF data. (/p/)
# Rolls. (/tg/)
# Fortunes. (/s4s/)
bq = @nodes.comment.cloneNode true
for node in $$ '.abbr + br, .exif, b, .fortune', bq
$.rm node
if abbr = $ '.abbr', bq
$.rm abbr
@info.comment = @nodesToText bq
if abbr
@info.comment = @info.comment.replace /\n\n$/, ''
# Hide spoilers.
# Remove:
# Preceding and following new lines.
# Trailing spaces.
commentDisplay = @info.comment
unless Conf['Remove Spoilers'] or Conf['Reveal Spoilers']
spoilers = $$ 's', bq
if spoilers.length
for node in spoilers
$.replace node, $.tn '[spoiler]'
commentDisplay = @nodesToText bq
@info.commentDisplay = commentDisplay.trim().replace /\s+$/gm, ''
nodesToText: (bq) ->
text = ""
nodes = $.X './/br|.//text()', bq
i = 0
while node = nodes.snapshotItem i++
text += node.data or '\n'
text
parseQuotes: ->
@quotes = []
# XXX https://github.com/4chan/4chan-JS/issues/77
# 4chan currently creates quote links inside [code] tags; ignore them
for quotelink in $$ ':not(pre) > .quotelink', @nodes.comment
@parseQuote quotelink
return
parseQuote: (quotelink) ->
# Only add quotes that link to posts on an imageboard.
# Don't add:
# - board links. (>>>/b/)
# - catalog links. (>>>/b/catalog or >>>/b/search)
# - rules links. (>>>/a/rules)
# - text-board quotelinks. (>>>/img/1234)
match = quotelink.href.match ///
^https?://boards\.4chan\.org/+
([^/]+) # boardID
/+(?:res|thread)/+\d+(?:/[^#]*)?#p
(\d+) # postID
$
///
return unless match or (@isClone and quotelink.dataset.postID) # normal or resurrected quote
@nodes.quotelinks.push quotelink
return if @isClone
# ES6 Set when?
fullID = "#{match[1]}.#{match[2]}"
@quotes.push fullID unless fullID in @quotes
parseFile: ->
return unless fileEl = $ '.file', @nodes.post
return unless link = $ '.fileText > a, .fileText-original > a', fileEl
return unless info = link.nextSibling?.textContent.match /\(([\d.]+ [KMG]?B).*\)/
fileText = fileEl.firstElementChild
@file =
text: fileText
link: link
url: link.href
name: fileText.title or link.title or link.textContent
size: info[1]
isImage: /(jpg|png|gif)$/i.test link.href
isVideo: /webm$/i.test link.href
dimensions: info[0].match(/\d+x\d+/)?[0]
tag: info[0].match(/,[^,]*, ([a-z]+)\)/i)?[1]
size = +@file.size.match(/[\d.]+/)[0]
unit = ['B', 'KB', 'MB', 'GB'].indexOf @file.size.match(/\w+$/)[0]
size *= 1024 while unit-- > 0
@file.sizeInBytes = size
if (thumb = $ '.fileThumb > [data-md5]', fileEl)
$.extend @file,
thumb: thumb
thumbURL: if m = link.href.match(/\d+(?=\.\w+$)/) then "#{location.protocol}//i.4cdn.org/#{@board}/#{m[0]}s.jpg"
MD5: thumb.dataset.md5
isSpoiler: $.hasClass thumb.parentNode, 'imgspoiler'
kill: (file) ->
if file
return if @file.isDead
@file.isDead = true
$.addClass @nodes.root, 'deleted-file'
else
return if @isDead
@isDead = true
$.addClass @nodes.root, 'deleted-post'
unless strong = $ 'strong.warning', @nodes.info
strong = $.el 'strong',
className: 'warning'
textContent: if @isReply then '[Deleted]' else '[Dead]'
$.after $('input', @nodes.info), strong
strong.textContent = if file then '[File deleted]' else '[Deleted]'
return if @isClone
for clone in @clones
clone.kill file
return if file
# Get quotelinks/backlinks to this post
# and paint them (Dead).
for quotelink in Get.allQuotelinksLinkingTo @ when not $.hasClass quotelink, 'deadlink'
quotelink.textContent = quotelink.textContent + '\u00A0(Dead)'
$.addClass quotelink, 'deadlink'
return
# XXX Workaround for 4chan's racing condition
# giving us false-positive dead posts.
resurrect: ->
@isDead = false
$.rmClass @nodes.root, 'deleted-post'
strong = $ 'strong.warning', @nodes.info
# no false-positive files
if @file and @file.isDead
strong.textContent = '[File deleted]'
else
$.rm strong
return if @isClone
for clone in @clones
clone.resurrect()
for quotelink in Get.allQuotelinksLinkingTo @ when $.hasClass quotelink, 'deadlink'
quotelink.textContent = quotelink.textContent.replace '\u00A0(Dead)', ''
$.rmClass quotelink, 'deadlink'
return
collect: ->
g.posts.rm @fullID
@thread.posts.rm @
@board.posts.rm @
addClone: (context, contractThumb) ->
new Clone @, context, contractThumb
rmClone: (index) ->
@clones.splice index, 1
for clone in @clones[index..]
clone.nodes.root.dataset.clone = index++
return