Build = h_staticPath: '//s.4cdn.org/image/' h_gifIcon: if window.devicePixelRatio >= 2 then '@2x.gif' else '.gif' spoilerRange: {} h_escape: (text) -> (text+'').replace /[&"'<>]/g, (c) -> {'&': '&', "'": ''', '"': '"', '<': '<', '>': '>'}[c] unescape: (text) -> return text unless text? text.replace /&(amp|#039|quot|lt|gt);/g, (c) -> {'&': '&', ''': "'", '"': '"', '<': '<', '>': '>'}[c] shortFilename: (filename, isReply) -> # FILENAME SHORTENING SCIENCE: # OPs have a +10 characters threshold. # The file extension is not taken into account. threshold = if isReply then 30 else 40 ext = filename.match(/\.?[^\.]*$/)[0] if filename.length - ext.length > threshold "#{filename[...threshold - 5]}(...)#{ext}" else filename thumbRotate: do -> n = 0 -> n = (n + 1) % 3 postFromObject: (data, boardID) -> o = # id postID: data.no threadID: data.resto or data.no boardID: boardID # info name: Build.unescape data.name capcode: data.capcode tripcode: data.trip uniqueID: data.id email: Build.unescape data.email subject: Build.unescape data.sub flagCode: data.country flagName: Build.unescape data.country_name date: data.now dateUTC: data.time h_comment: data.com or '' # thread status isSticky: !!data.sticky isClosed: !!data.closed # file if data.filedeleted o.file = isDeleted: true else if data.ext o.file = name: (Build.unescape data.filename) + data.ext timestamp: "#{data.tim}#{data.ext}" url: if boardID is 'f' "//i.4cdn.org/#{boardID}/#{encodeURIComponent data.filename}#{data.ext}" else "//i.4cdn.org/#{boardID}/#{data.tim}#{data.ext}" height: data.h width: data.w MD5: data.md5 size: data.fsize turl: "//#{Build.thumbRotate()}.t.4cdn.org/#{boardID}/#{data.tim}s.jpg" theight: data.tn_h twidth: data.tn_w isSpoiler: !!data.spoiler isDeleted: false Build.post o post: (o, isArchived) -> ### This function contains code from 4chan-JS (https://github.com/4chan/4chan-JS). @license: https://github.com/4chan/4chan-JS/blob/master/LICENSE ### E = Build.h_escape { postID, threadID, boardID name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC isSticky, isClosed h_comment file } = o name or= '' subject or= '' isOP = postID is threadID {h_staticPath, h_gifIcon} = Build if isOP h_sideArrows = '' else h_sideArrows = "
>>
" h_postClass = "post #{if isOP then 'op' else 'reply'}#{if capcode is 'admin_highlight' then ' highlightPost' else ''}" if tripcode h_tripcode = " #{E tripcode}" else h_tripcode = '' email_processed = encodeURIComponent(email).replace /%40/g, '@' if email h_emailStart = "" h_emailEnd = '' else h_emailStart = '' h_emailEnd = '' switch capcode when 'admin', 'admin_highlight' h_capcodeClass = ' capcodeAdmin' h_capcodeStart = ' ## Admin' h_capcodeIcon = " " when 'mod' h_capcodeClass = ' capcodeMod' h_capcodeStart = ' ## Mod' h_capcodeIcon = " " when 'developer' h_capcodeClass = ' capcodeDeveloper' h_capcodeStart = ' ## Developer' h_capcodeIcon = " " else h_capcodeClass = '' h_capcodeStart = '' h_capcodeIcon = '' if !capcode and uniqueID h_userID = " (ID: #{E uniqueID}) " else h_userID = '' unless flagCode h_flag = '' else if boardID is 'pol' h_flag = " #{E flagCode}" else h_flag = " " if file?.isDeleted if isOP h_file = "
" h_file += "File deleted." h_file += '
' else h_file = "
" h_file += "File deleted." h_file += '
' else if file fileSize = $.bytesToString file.size fileThumb = file.turl if file.isSpoiler unless isArchived fileThumb = "#{h_staticPath}spoiler" if spoilerRange = Build.spoilerRange[boardID] # Randomize the spoiler image. fileThumb += "-#{boardID}" + Math.floor 1 + spoilerRange * Math.random() fileThumb += '.png' file.twidth = file.theight = 100 shortFilename = 'Spoiler Image' else shortFilename = Build.shortFilename file.name if boardID is 'f' h_imgSrc = '' else h_imgSrc = "" h_imgSrc += "#{E fileSize}" h_imgSrc += '' if file.url[-4..] is '.pdf' h_fileDims = 'PDF' else h_fileDims = "#{+file.width}x#{+file.height}" h_fileTitle1 = '' h_fileTitle2 = '' if file.isSpoiler h_fileTitle1 = " title='#{E file.name}'" else if file.name isnt shortFilename h_fileTitle2 = " title='#{E file.name}'" h_fileInfo = "
" h_fileInfo += "File: #{E shortFilename} (#{E fileSize}, #{h_fileDims})" h_fileInfo += '
' h_file = "
#{h_fileInfo}#{h_imgSrc}
" else h_file = '' if g.VIEW is 'thread' and g.THREADID is +threadID h_quoteLink = "javascript:quote(#{+postID})" else h_quoteLink = "/#{E boardID}/thread/#{+threadID}\#q#{+postID}" if isSticky h_sticky = " Sticky" else h_sticky = '' if isClosed h_closed = " Closed" else h_closed = '' if isOP and g.VIEW is 'index' and Conf['JSON Navigation'] pageNum = Math.floor(Index.liveThreadIDs.indexOf(postID) / Index.threadsNumPerPage) + 1 h_pageIcon = " [#{+pageNum}]" else h_pageIcon = '' if isOP and g.VIEW is 'index' h_replyLink = "   [Reply]" else h_replyLink = '' container = $.el 'div', id: "pc#{postID}" className: "postContainer #{if isOP then 'op' else 'reply'}Container" innerHTML: <%= grunt.file.read('src/General/html/Build/post.html').replace(/>\s+/g, '>').replace(/\s+ # Fix pathnames for quote in $$ '.quotelink', container href = quote.getAttribute 'href' continue if href[0] is '/' # Cross-board quote, or board link if href[0] is '#' quote.href = "/#{boardID}/thread/#{threadID}#{href}" else quote.href = "/#{boardID}/thread/#{href}" container summary: (boardID, threadID, posts, files) -> text = [] text.push "#{posts} post#{if posts > 1 then 's' else ''}" text.push "and #{files} image repl#{if files > 1 then 'ies' else 'y'}" if files text.push 'omitted.' $.el 'a', className: 'summary' textContent: text.join ' ' href: "/#{boardID}/thread/#{threadID}" thread: (board, data, full) -> Build.spoilerRange[board] = data.custom_spoiler if (OP = board.posts[data.no]) and root = OP.nodes.root.parentNode $.rmAll root else root = $.el 'div', className: 'thread' id: "t#{data.no}" $.add root, Build[if full then 'fullThread' else 'excerptThread'] board, data, OP root excerptThread: (board, data, OP) -> 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] else # XXX data.images is not accurate. [data.replies, data.omitted_images + data.last_replies.filter((data) -> !!data.ext).length] nodes.push Build.summary board.ID, data.no, posts, files nodes fullThread: (board, data) -> Build.postFromObject data, board.ID