diff --git a/Gruntfile.coffee b/Gruntfile.coffee
index 60220c8af..426576abe 100755
--- a/Gruntfile.coffee
+++ b/Gruntfile.coffee
@@ -1,7 +1,30 @@
module.exports = (grunt) ->
importHTML = (filename) ->
- "'''#{grunt.file.read("src/General/html/#{filename}.html").replace(/^\s+|\s+$
+ parts = template.split /([\$&@]){([^}]*)}/
+ parts2 = []
+ checkText = ''
+ for part, i in parts
+ switch i % 3
+ when 0
+ parts2.push JSON.stringify part unless part is ''
+ checkText += part
+ when 1
+ if /<[^>]*$/.test(checkText) and not (part is '$' and /\=['"][^"'<>]*$/.test checkText)
+ throw new Error "Illegal insertion into HTML template: #{template}"
+ expr = parts[i+1]
+ expr = "(#{expr})" for x in parts[i+1].split ')'
+ parts2.push switch part
+ when '$' then "E#{expr}"
+ when '&' then "#{expr}.innerHTML"
+ when '@' then "#{expr}.map((x) -> x.innerHTML).join('')"
+ unless /^(<\w+( [\w-]+(='[^"'<>]*'|="[^"'<>]*")?)*>|<\/\w+>|[^"'<>]*)*$/.test checkText
+ throw new Error "HTML template is ill-formed: #{template}"
+ output = if parts2.length is 0 then '""' else parts2.join ' + '
+ "(innerHTML: #{output})"
# Project configuration.
grunt.initConfig
@@ -12,6 +35,7 @@ module.exports = (grunt) ->
get: ->
pkg = grunt.config 'pkg'
pkg.importHTML = importHTML
+ pkg.html = html
pkg.tests_enabled or= false
pkg
enumerable: true
diff --git a/src/Filtering/ThreadHiding.coffee b/src/Filtering/ThreadHiding.coffee
index e0555b226..ea5555bbb 100755
--- a/src/Filtering/ThreadHiding.coffee
+++ b/src/Filtering/ThreadHiding.coffee
@@ -137,8 +137,8 @@ ThreadHiding =
makeButton: (thread, type) ->
a = $.el 'a',
className: "#{type}-thread-button"
- innerHTML: ""
href: 'javascript:;'
+ $.extend a, <%= html('') %>
a.dataset.fullID = thread.fullID
$.on a, 'click', ThreadHiding.toggle
a
diff --git a/src/General/Build.coffee b/src/General/Build.coffee
index 091208e75..59973e2bf 100755
--- a/src/General/Build.coffee
+++ b/src/General/Build.coffee
@@ -39,7 +39,7 @@ Build =
flagName: Build.unescape data.country_name
date: data.now
dateUTC: data.time
- h_comment: data.com or ''
+ comment: {innerHTML: data.com or ''}
# thread status
isSticky: !!data.sticky
isClosed: !!data.closed
@@ -75,86 +75,75 @@ Build =
postID, threadID, boardID
name, capcode, tripcode, uniqueID, email, subject, flagCode, flagName, date, dateUTC
isSticky, isClosed
+ comment
file
} = o
name or= ''
subject or= ''
- h_comment = o.h_comment
isOP = postID is threadID
- if Build.initPixelRatio >= 2
- h_retina = '@2x'
- else
- h_retina = ''
+ retina = if Build.initPixelRatio >= 2 then '@2x' else ''
### Name Block ###
switch capcode
when 'admin', 'admin_highlight'
- h_capcodeClass = ' capcodeAdmin'
- h_capcodeStart = ' ## Admin'
- h_capcodeIcon = "
"
+ capcodeClass = ' capcodeAdmin'
+ capcodeStart = <%= html(' ## Admin') %>
+ capcodeIcon = <%= html('
') %>
when 'mod'
- h_capcodeClass = ' capcodeMod'
- h_capcodeStart = ' ## Mod'
- h_capcodeIcon = "
"
+ capcodeClass = ' capcodeMod'
+ capcodeStart = <%= html(' ## Mod') %>
+ capcodeIcon = <%= html('
') %>
when 'developer'
- h_capcodeClass = ' capcodeDeveloper'
- h_capcodeStart = ' ## Developer'
- h_capcodeIcon = "
"
+ capcodeClass = ' capcodeDeveloper'
+ capcodeStart = <%= html(' ## Developer') %>
+ capcodeIcon = <%= html('
') %>
else
- h_capcodeClass = ''
- h_capcodeStart = ''
- h_capcodeIcon = ''
+ capcodeClass = ''
+ capcodeStart = <%= html('') %>
+ capcodeIcon = <%= html('') %>
- if capcode
- h_nameClass = ' capcode'
+ nameClass = if capcode then ' capcode' else ''
+
+ tripcodeField = if tripcode
+ <%= html(' ${tripcode}') %>
else
- h_nameClass = ''
+ <%= html('') %>
- if tripcode
- h_tripcode = " #{E tripcode}"
- else
- h_tripcode = ''
-
- h_emailCont = "#{E name}#{h_tripcode}#{h_capcodeStart}"
+ emailField = <%= html('${name}&{tripcodeField}&{capcodeStart}') %>
if email
emailProcessed = encodeURIComponent(email).replace /%40/g, '@'
- h_email = "#{h_emailCont}"
- else
- h_email = h_emailCont
+ emailField = <%= html('&{emailField}') %>
unless isOP and boardID is 'f'
- h_email += ' '
+ emailField = <%= html('&{emailField} ') %>
- if !capcode and uniqueID
- h_userID = " (ID: #{E uniqueID})"
+ userID = if !capcode and uniqueID
+ <%= html(' (ID: ${uniqueID})') %>
else
- h_userID = ''
+ <%= html('') %>
- unless flagCode
- h_flag = ''
+ flag = unless flagCode
+ <%= html('') %>
+ else if boardID is 'pol'
+ <%= html('
') %>
else
- flagCodeLC = flagCode.toLowerCase()
- if boardID is 'pol'
- h_flag = "
"
- else
- h_flag = ""
+ <%= html('') %>
- h_nameBlock = ""
- h_nameBlock += "#{h_email}#{h_capcodeIcon}#{h_userID}#{h_flag}"
- h_nameBlock += ' '
+ nameBlock = <%= html(
+ '' +
+ '&{emailField}&{capcodeIcon}&{userID}&{flag}' +
+ ' '
+ ) %>
### Post Info ###
- if isOP or boardID is 'f'
- h_subject = "#{E subject} "
+ subjectField = if isOP or boardID is 'f'
+ <%= html('${subject} ') %>
else
- h_subject = ''
+ <%= html('') %>
- if isOP and boardID is 'f'
- h_desktop2 = ''
- else
- h_desktop2 = ' desktop'
+ desktop2 = if isOP and boardID is 'f' then '' else ' desktop'
postLink = Build.postURL boardID, threadID, postID
quoteLink = if Build.sameThread boardID, threadID
@@ -162,56 +151,59 @@ Build =
else
"/#{boardID}/thread/#{threadID}\#q#{postID}"
- if isOP and g.VIEW is 'index' and Conf['JSON Navigation']
+ pageIcon = if isOP and g.VIEW is 'index' and Conf['JSON Navigation']
pageNum = Math.floor(Index.liveThreadIDs.indexOf(postID) / Index.threadsNumPerPage) + 1
- h_pageIcon = " [#{+pageNum}]"
+ <%= html(' [${pageNum}]') %>
else
- h_pageIcon = ''
+ <%= html('') %>
- if isSticky
- h_sticky = "
"
+ sticky = if isSticky
+ <%= html('
') %>
else
- h_sticky = ''
+ <%= html('') %>
- if isClosed
- h_closed = "
"
+ closed = if isClosed
+ <%= html('
') %>
else
- h_closed = ''
+ <%= html('') %>
- if isOP and g.VIEW is 'index'
- h_replyLink = " [Reply]"
+ replyLink = if isOP and g.VIEW is 'index'
+ <%= html(' [Reply]') %>
else
- h_replyLink = ''
+ <%= html('') %>
- h_postInfo = "
"
- h_postInfo += "
"
- h_postInfo += h_subject
- h_postInfo += h_nameBlock
- h_postInfo += "
#{E date} "
- h_postInfo += "
"
- h_postInfo += "No."
- h_postInfo += "#{+postID}"
- h_postInfo += "#{h_pageIcon}#{h_sticky}#{h_closed}#{h_replyLink}"
- h_postInfo += ''
- h_postInfo += '
'
+ postInfo = <%= html(
+ '' +
+ '
' +
+ '&{subjectField}' +
+ '&{nameBlock}' +
+ '
${date} ' +
+ '
' +
+ 'No.' +
+ '${postID}' +
+ '&{pageIcon}&{sticky}&{closed}&{replyLink}' +
+ '' +
+ '
'
+ ) %>
### File Info ###
- if file?.isDeleted
- h_fileCont = ''
- h_fileCont += "
"
- h_fileCont += ''
+ fileCont = if file?.isDeleted
+ <%= html(
+ '' +
+ '
' +
+ ''
+ ) %>
else if file and boardID is 'f'
- fileSize = $.bytesToString file.size
- h_fileCont = ""
- h_fileCont += "File: #{E file.name}"
- h_fileCont += "-(#{E fileSize}, #{+file.width}x#{+file.height}, #{E file.tag})"
- h_fileCont += ' '
+ <%= html(
+ '' +
+ 'File: ${file.name}' +
+ '-(${$.bytesToString file.size}, ${file.width}x${file.height}, ${file.tag})' +
+ ' '
+ ) %>
else if file
if file.isSpoiler
- h_fileTitle1 = "title='#{E file.name}'"
shortFilename = 'Spoiler Image'
- h_spoilerClass = ' imgspoiler'
if spoilerRange = Build.spoilerRange[boardID]
# Randomize the spoiler image.
fileThumb = "//s.4cdn.org/image/spoiler-#{boardID}#{Math.floor 1 + spoilerRange * Math.random()}.png"
@@ -219,58 +211,58 @@ Build =
fileThumb = '//s.4cdn.org/image/spoiler.png'
file.twidth = file.theight = 100
else
- h_fileTitle1 = ''
shortFilename = Build.shortFilename file.name, !isOP
- h_spoilerClass = ''
fileThumb = file.turl
- if file.isSpoiler or file.name is shortFilename
- h_fileTitle2 = ''
+ fileSize = $.bytesToString file.size
+ fileDims = if file.url[-4..] is '.pdf' then 'PDF' else "#{+file.width}x#{+file.height}"
+
+ fileLink = if file.isSpoiler or file.name is shortFilename
+ <%= html('${shortFilename}') %>
else
- h_fileTitle2 = "title='#{E file.name}'"
+ <%= html('${shortFilename}') %>
- fileSize = $.bytesToString file.size
-
- if file.url[-4..] is '.pdf'
- h_fileDims = 'PDF'
+ fileText = if file.isSpoiler
+ <%= html('File: &{fileLink} (${fileSize}, ${fileDims})
') %>
else
- h_fileDims = "#{+file.width}x#{+file.height}"
+ <%= html('File: &{fileLink} (${fileSize}, ${fileDims})
') %>
- h_fileCont = ""
- h_fileCont += "File:
#{E shortFilename} (#{E fileSize}, #{h_fileDims})"
- h_fileCont += '
'
- h_fileCont += ""
- h_fileCont += "
"
- h_fileCont += ''
+ <%= html(
+ '&{fileText}' +
+ '' +
+ '
' +
+ ''
+ ) %>
- if file
- h_file = "#{h_fileCont}
"
+ fileBlock = if file
+ <%= html('&{fileCont}
') %>
else
- h_file = ''
+ <%= html('') %>
### Whole Post ###
- if capcode is 'admin_highlight'
- h_highlightPost = ' highlightPost'
- else
- h_highlightPost = ''
+ highlightPost = if capcode is 'admin_highlight' then ' highlightPost' else ''
- h_message = "#{h_comment}
"
+ message = <%= html('&{comment}
') %>
- if isOP
- h_post = ""
- h_post += "#{h_file}#{h_postInfo}#{h_message}"
- h_post += '
'
+ wholePost = if isOP
+ <%= html(
+ '' +
+ '&{fileBlock}&{postInfo}&{message}' +
+ '
'
+ ) %>
else
- h_post = ">>
"
- h_post += ""
- h_post += "#{h_postInfo}#{h_file}#{h_message}"
- h_post += '
'
+ <%= html(
+ '>>
' +
+ '' +
+ '&{postInfo}&{fileBlock}&{message}' +
+ '
'
+ ) %>
container = $.el 'div',
className: "postContainer #{if isOP then 'op' else 'reply'}Container"
id: "pc#{postID}"
- innerHTML: h_post
+ $.extend container, wholePost
# Fix pathnames
for quote in $$ '.quotelink', container
diff --git a/src/General/Get.coffee b/src/General/Get.coffee
index 92c926282..87ee63c13 100755
--- a/src/General/Get.coffee
+++ b/src/General/Get.coffee
@@ -159,21 +159,24 @@ Get =
root.textContent = data.error
return
- # convert comment to html
- h_comment = E (data.comment or '')
# https://github.com/eksopl/fuuka/blob/master/Board/Yotsuba.pm#L413-452
# https://github.com/eksopl/asagi/blob/master/src/main/java/net/easymodo/asagi/Yotsuba.java#L109-138
- h_comment = h_comment.replace ///
- \n
- |
- \[/?[a-z]+(:lit)?\]
- ///g, Get.parseMarkup
-
- h_comment = h_comment
- # greentext
- .replace(/(^|>)(>[^<$]*)(<|$)/g, '$1$2$3')
- # quotes
- .replace /((>){2}(>\/[a-z\d]+\/)?\d+)/g, '$1'
+ comment = (data.comment or '').split /(\n|\[\/?(?:b|spoiler|code|moot|banned)\])/
+ comment = for text, i in comment
+ if i % 2 is 1
+ Get.archiveTags[text]
+ else
+ greentext = text[0] is '>'
+ text = text.replace /(\[\/?[a-z]+):lit(\])/, '$1$2'
+ text = for text2, j in text.split /(>>(?:>\/[a-z\d]+\/)?\d+)/g
+ if j % 2 is 1
+ <%= html('${text2}') %>
+ else
+ <%= html('${text2}') %>
+ text = <%= html('@{text}') %>
+ text = <%= html('&{text}') %> if greentext
+ text
+ comment = <%= html('@{comment}') %>
threadID = +data.thread_num
o =
@@ -195,7 +198,7 @@ Get =
flagName: data.poster_country_name
date: data.fourchan_date
dateUTC: data.timestamp
- h_comment: h_comment
+ comment: comment
# file
if data.media?.media_filename
o.file =
@@ -221,17 +224,15 @@ Get =
post.isFetchedQuote = true
Main.callbackNodes Post, [post]
Get.insert post, root, context
- parseMarkup: (text) ->
- {
- '\n': '
'
- '[b]': ''
- '[/b]': ''
- '[spoiler]': ''
- '[/spoiler]': ''
- '[code]': ''
- '[/code]': '
'
- '[moot]': ''
- '[/moot]': '
'
- '[banned]': ''
- '[/banned]': ''
- }[text] or text.replace ':lit', ''
+ archiveTags:
+ '\n': <%= html('
') %>
+ '[b]': <%= html('') %>
+ '[/b]': <%= html('') %>
+ '[spoiler]': <%= html('') %>
+ '[/spoiler]': <%= html('') %>
+ '[code]': <%= html('') %>
+ '[/code]': <%= html('') %>
+ '[moot]': <%= html('') %>
+ '[/moot]': <%= html('
') %>
+ '[banned]': <%= html('') %>
+ '[/banned]': <%= html('') %>
diff --git a/src/General/Globals.coffee b/src/General/Globals.coffee
index 2334e7813..538393b13 100755
--- a/src/General/Globals.coffee
+++ b/src/General/Globals.coffee
@@ -5,6 +5,9 @@ doc = d.documentElement
g =
VERSION: '<%= version %>'
NAMESPACE: '<%= meta.name %>.'
+ NAME: '<%= meta.name %>'
+ FAQ: '<%= meta.faq %>'
+ CHANGELOG: '<%= meta.repo %>blob/<%= meta.mainBranch %>/CHANGELOG.md'
boards: {}
E = (text) ->
(text+'').replace /[&"'<>]/g, (x) ->
diff --git a/src/General/Header.coffee b/src/General/Header.coffee
index ead795dbb..c4ee7f4b7 100755
--- a/src/General/Header.coffee
+++ b/src/General/Header.coffee
@@ -4,7 +4,7 @@ Header =
menuButton = $.el 'span',
className: 'menu-button'
- innerHTML: ''
+ $.extend menuButton, <%= html('') %>
barFixedToggler = UI.checkbox 'Fixed Header', ' Fixed Header'
headerToggler = UI.checkbox 'Header auto-hide', ' Auto-hide header'
@@ -132,7 +132,13 @@ Header =
fourchannav = $.id 'boardNavDesktop'
Header.boardList = boardList = $.el 'span',
id: 'board-list'
- innerHTML: " - #{fourchannav.innerHTML}"
+ $.extend boardList, <%= html(
+ '' +
+ '' +
+ ' - ' +
+ '&{fourchannav}' +
+ ''
+ ) %>
for a in $$ 'a', boardList
if a.pathname.split('/')[1] is g.BOARD.ID
a.className = 'current'
@@ -442,11 +448,11 @@ Header =
return
el = $.el 'span',
- innerHTML: '''
- <%= meta.name %> needs your permission to show desktop notifications.
- [FAQ]
- or
- '''
+ <%= html(
+ '${g.NAME} needs your permission to show desktop notifications. ' +
+ '[FAQ]
' +
+ ' or '
+ ) %>
[authorize, disable] = $$ 'button', el
$.on authorize, 'click', ->
Notification.requestPermission (status) ->
diff --git a/src/General/Index.coffee b/src/General/Index.coffee
index a0dcfa6a1..1ad465b22 100644
--- a/src/General/Index.coffee
+++ b/src/General/Index.coffee
@@ -15,9 +15,9 @@ Index =
modeEntry =
el: $.el 'span', textContent: 'Index mode'
subEntries: [
- { el: $.el 'label', innerHTML: ' Paged' }
- { el: $.el 'label', innerHTML: ' Infinite scrolling' }
- { el: $.el 'label', innerHTML: ' All threads' }
+ { el: $.el 'label', <%= html(' Paged') %> }
+ { el: $.el 'label', <%= html(' Infinite scrolling') %> }
+ { el: $.el 'label', <%= html(' All threads') %> }
]
for label in modeEntry.subEntries
input = label.el.firstChild
@@ -28,11 +28,11 @@ Index =
sortEntry =
el: $.el 'span', textContent: 'Sort by'
subEntries: [
- { el: $.el 'label', innerHTML: ' Bump order' }
- { el: $.el 'label', innerHTML: ' Last reply' }
- { el: $.el 'label', innerHTML: ' Creation date' }
- { el: $.el 'label', innerHTML: ' Reply count' }
- { el: $.el 'label', innerHTML: ' File count' }
+ { el: $.el 'label', <%= html(' Bump order') %> }
+ { el: $.el 'label', <%= html(' Last reply') %> }
+ { el: $.el 'label', <%= html(' Creation date') %> }
+ { el: $.el 'label', <%= html(' Reply count') %> }
+ { el: $.el 'label', <%= html(' File count') %> }
]
for label in sortEntry.subEntries
input = label.el.firstChild
@@ -66,10 +66,10 @@ Index =
@pagelist = $.el 'div',
className: 'pagelist'
hidden: true
- innerHTML: <%= importHTML('Features/Index-pagelist') %>
+ $.extend @pagelist, <%= importHTML('Features/Index-pagelist') %>
@navLinks = $.el 'div',
className: 'navLinks'
- innerHTML: <%= importHTML('Features/Index-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"
@searchInput = $ '#index-search', @navLinks
diff --git a/src/General/Main.coffee b/src/General/Main.coffee
index 3ada33895..396ab0033 100755
--- a/src/General/Main.coffee
+++ b/src/General/Main.coffee
@@ -189,7 +189,7 @@ Main =
return if previousversion is g.VERSION
if previousversion
el = $.el 'span',
- innerHTML: '<%= meta.name %> has been updated to version <%= version %>.'
+ <%= html('${g.NAME} has been updated to version ${g.VERSION}.') %>
new Notice 'info', el, 15
else
Settings.open()
@@ -230,7 +230,7 @@ Main =
return
div = $.el 'div',
- innerHTML: "#{+errors.length} errors occurred. [show]"
+ <%= html('${errors.length} errors occurred. [show]') %>
$.on div.lastElementChild, 'click', ->
[@textContent, logs.hidden] = if @textContent is 'show'
['hide', false]
diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee
index 059454fe4..e304685bc 100755
--- a/src/General/Settings.coffee
+++ b/src/General/Settings.coffee
@@ -34,7 +34,7 @@ Settings =
Settings.dialog = dialog = $.el 'div',
id: 'fourchanx-settings'
className: 'dialog'
- innerHTML: <%= importHTML('Settings/Settings') %>
+ $.extend dialog, <%= importHTML('Settings/Settings') %>
$.on $('.export', Settings.dialog), 'click', Settings.export
$.on $('.import', Settings.dialog), 'click', Settings.import
@@ -90,8 +90,7 @@ Settings =
inputs = {}
for key, obj of Config.main
fs = $.el 'fieldset',
- innerHTML: ''
- fs.firstElementChild.textContent = key
+ <%= html('') %>
for key, arr of obj
description = arr[1]
div = $.el 'div'
@@ -112,7 +111,7 @@ Settings =
return
div = $.el 'div',
- innerHTML: ': Clear manually-hidden threads and posts on all boards. Reload the page to apply.'
+ <%= html(': Clear manually-hidden threads and posts on all boards. Reload the page to apply.') %>
button = $ 'button', div
$.get {hiddenThreads: {}, hiddenPosts: {}}, ({hiddenThreads, hiddenPosts}) ->
hiddenNum = 0
@@ -247,7 +246,7 @@ Settings =
$.clear -> window.location.reload() if confirm 'Reset successful. Reload now?'
filter: (section) ->
- section.innerHTML = <%= importHTML('Settings/Filter-select') %>
+ $.extend section, <%= importHTML('Settings/Filter-select') %>
select = $ 'select', section
$.on select, 'change', Settings.selectFilter
Settings.selectFilter.call select
@@ -265,11 +264,11 @@ Settings =
$.on ta, 'change', $.cb.value
$.add div, ta
return
- div.innerHTML = <%= importHTML('Settings/Filter-guide') %>
+ $.extend div, <%= importHTML('Settings/Filter-guide') %>
$('.warning', div).hidden = Conf['Filter']
sauce: (section) ->
- section.innerHTML = <%= importHTML('Settings/Sauce') %>
+ $.extend section, <%= importHTML('Settings/Sauce') %>
$('.warning', section).hidden = Conf['Sauce']
ta = $ 'textarea', section
$.get 'sauces', Conf['sauces'], (item) ->
@@ -277,7 +276,7 @@ Settings =
$.on ta, 'change', $.cb.value
advanced: (section) ->
- section.innerHTML = <%= importHTML('Settings/Advanced') %>
+ $.extend section, <%= importHTML('Settings/Advanced') %>
warning.hidden = Conf[warning.dataset.feature] for warning in $$ '.warning', section
items = {}
@@ -387,7 +386,7 @@ Settings =
textContent: archive
value: archive
- td.innerHTML = ''
+ $.extend td, <%= html('') %>
select = td.firstElementChild
unless select.disabled = length is 1
# XXX GM can't into datasets
@@ -441,7 +440,7 @@ Settings =
CustomCSS.update()
keybinds: (section) ->
- section.innerHTML = <%= importHTML('Settings/Keybinds') %>
+ $.extend section, <%= importHTML('Settings/Keybinds') %>
$('.warning', section).hidden = Conf['Keybinds']
tbody = $ 'tbody', section
@@ -449,8 +448,7 @@ Settings =
inputs = {}
for key, arr of Config.hotkeys
tr = $.el 'tr',
- innerHTML: ' | | '
- tr.firstElementChild.textContent = arr[1]
+ <%= html('${arr[1]} | | ') %>
input = $ 'input', tr
input.name = key
input.spellcheck = false
diff --git a/src/General/lib/$.coffee b/src/General/lib/$.coffee
index 1574641c7..6a03efe78 100755
--- a/src/General/lib/$.coffee
+++ b/src/General/lib/$.coffee
@@ -46,9 +46,11 @@ $.ajax = do ->
blockedError = (url) ->
return if blockedURLs[url]
blockedURLs[url] = true
- h_message = '<%= meta.name %> was blocked from loading the following URL:
'
- h_message += '[More info]'
- message = $.el 'div', innerHTML: h_message
+ message = $.el 'div',
+ <%= html(
+ '${g.NAME} was blocked from loading the following URL:
' +
+ '[More info]'
+ ) %>
$('span', message).textContent = (if /^\/\//.test url then location.protocol else '') + url
new Notice 'error', message, 30, -> delete blockedURLs[url]
(url, options, extra={}) ->
diff --git a/src/General/lib/notice.class b/src/General/lib/notice.class
index 0d4ae079a..2e6d4a18e 100644
--- a/src/General/lib/notice.class
+++ b/src/General/lib/notice.class
@@ -1,7 +1,7 @@
class Notice
constructor: (type, content, @timeout, @onclose) ->
@el = $.el 'div',
- innerHTML: ''
+ <%= html('') %>
@el.style.opacity = 0
@setType type
$.on @el.firstElementChild, 'click', @close
diff --git a/src/Images/Gallery.coffee b/src/Images/Gallery.coffee
index 1810fa3e7..ae84dd317 100644
--- a/src/Images/Gallery.coffee
+++ b/src/Images/Gallery.coffee
@@ -36,7 +36,7 @@ Gallery =
nodes.el = dialog = $.el 'div',
id: 'a-gallery'
- innerHTML: <%= importHTML('Features/Gallery') %>
+ $.extend dialog, <%= importHTML('Features/Gallery') %>
nodes[key] = $ value, dialog for key, value of {
buttons: '.gal-buttons'
@@ -288,7 +288,7 @@ Gallery =
createSubEntries: ->
subEntries = ['Hide Thumbnails', 'Fit Width', 'Fit Height', 'Scroll to Post'].map Gallery.menu.createSubEntry
- delayLabel = $.el 'label', innerHTML: 'Slide Delay: '
+ delayLabel = $.el 'label', <%= html('Slide Delay: ') %>
delayInput = delayLabel.firstElementChild
delayInput.value = Gallery.delay
$.on delayInput, 'change', Gallery.cb.setDelay
diff --git a/src/Images/ImageExpand.coffee b/src/Images/ImageExpand.coffee
index 339d2112e..892c6e101 100755
--- a/src/Images/ImageExpand.coffee
+++ b/src/Images/ImageExpand.coffee
@@ -10,6 +10,8 @@ ImageExpand =
$.on @EAI, 'click', @cb.toggleAll
Header.addShortcut @EAI, 3
$.on d, 'scroll visibilitychange', @cb.playVideos
+ @videoControls = $.el 'span', className: 'video-controls'
+ $.extend @videoControls, <%= html('\u00A0contract') %>
Post.callbacks.push
name: 'Image Expansion'
@@ -219,10 +221,6 @@ ImageExpand =
if controls
ImageCommon.addControls fullImage
- videoControls: $.el 'span',
- className: 'video-controls'
- innerHTML: '\u00A0contract'
-
videoCB: do ->
# dragging to the left contracts the video
mousedown = false
diff --git a/src/Images/ImageLoader.coffee b/src/Images/ImageLoader.coffee
index 2153c68ff..7fc8337c4 100755
--- a/src/Images/ImageLoader.coffee
+++ b/src/Images/ImageLoader.coffee
@@ -14,7 +14,7 @@ ImageLoader =
return unless Conf['Image Prefetching'] and g.VIEW is 'thread'
prefetch = $.el 'label',
- innerHTML: ' Prefetch Images'
+ <%= html(' Prefetch Images') %>
@el = prefetch.firstElementChild
$.on @el, 'change', @toggle
diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee
index 24293cdc5..36db98d3b 100755
--- a/src/Linkification/Linkify.coffee
+++ b/src/Linkification/Linkify.coffee
@@ -175,8 +175,7 @@ Linkify =
try
$.cache service.api(uid), (-> Linkify.cb.title @, data), responseType: 'json'
catch err
- link.innerHTML = 'Title Link Blocked (are you using NoScript?)'
- $.prepend link, $.tn "[#{key}] "
+ $.extend link, <%= html('[${key}] Title Link Blocked (are you using NoScript?)') %>
return
cb:
@@ -239,7 +238,7 @@ Linkify =
key: 'gist'
regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/
el: (a) ->
- div = $.el 'iframe',
+ $.el 'iframe',
# Github doesn't allow embedding straight from the site, so we use an external site to bypass that.
src: "http://www.purplegene.com/script?url=https://gist.github.com/#{a.dataset.uid}.js"
title:
@@ -251,10 +250,7 @@ Linkify =
regExp: /(http|www).*\.(gif|png|jpg|jpeg|bmp)$/
style: 'border: 0; width: auto; height: auto;'
el: (a) ->
- el = $.el 'div'
- el.innerHTML = '
'
- el.firstChild.href = el.firstChild.firstChild.src = a.href
- el
+ $.el 'div', <%= html('
') %>
,
key: 'InstallGentoo'
regExp: /.*(?:paste.installgentoo.com\/view\/)([0-9a-z_]+)/
@@ -297,17 +293,13 @@ Linkify =
return div.textContent = "ERROR: Not a valid filetype" unless embed
switch embed.type
when 'video/mp4', 'video/webm', 'video/ogv'
- el.innerHTML = ''
- for ext, i in ['mp4', 'webm', 'ogv']
+ $.extend el, <%= html('') %>
+ for ext, i in ['mp4', 'webm']
el.firstChild.children[i].src = "https://mediacru.sh/#{a.dataset.uid}.#{ext}"
when 'image/svg+xml', 'image/png', 'image/gif', 'image/jpeg'
- el.innerHTML = '
'
- el.firstChild.href = a.href
- el.firstChild.firstChild.src = "https://mediacru.sh/#{file.file}"
+ $.extend el, <%= html('
') %>
when 'audio/mpeg', 'audio/ogg'
- el.innerHTML = ''
- for ext, i in ['ogg', 'mp3']
- el.firstChild.children[i].src = "https://mediacru.sh/#{a.dataset.uid}.#{ext}"
+ $.extend el, <%= html('') %>
else
el.textContent = "ERROR: No valid filetype."
el
@@ -350,14 +342,14 @@ Linkify =
idparam = {'b': 'archive_id', 'c': 'chapter_id'}
obj = $.el 'object',
data: 'http://www.twitch.tv/widgets/archive_embed_player.swf'
- obj.innerHTML = ''
+ $.extend obj, <%= html('') %>
obj.children[1].value = "channel=#{channel}&start_volume=25&auto_play=false{idparam[type]}=#{id}"
obj
else
channel = (/(\w+)/.exec a.dataset.uid)[0]
obj = $.el 'object',
data: "http://www.twitch.tv/widgets/live_embed_player.swf?channel=#{channel}"
- obj.innerHTML = ''
+ $.extend obj, <%= html('') %>
obj.children[1].value = "hostname=www.twitch.tv&channel=#{channel}&auto_play=true&start_volume=25"
obj
,
diff --git a/src/Menu/Menu.coffee b/src/Menu/Menu.coffee
index aa67c193f..20a2992b6 100755
--- a/src/Menu/Menu.coffee
+++ b/src/Menu/Menu.coffee
@@ -16,8 +16,8 @@ Menu =
makeButton: do ->
a = $.el 'a',
className: 'menu-button'
- innerHTML: ''
href: 'javascript:;'
+ $.extend a, <%= html('') %>
->
button = a.cloneNode true
$.on button, 'click', Menu.toggle
diff --git a/src/Miscellaneous/AnnouncementHiding.coffee b/src/Miscellaneous/AnnouncementHiding.coffee
index 24ab1eee2..07d9892ce 100755
--- a/src/Miscellaneous/AnnouncementHiding.coffee
+++ b/src/Miscellaneous/AnnouncementHiding.coffee
@@ -22,10 +22,9 @@ PSAHiding =
$.on entry.el, 'click', PSAHiding.toggle
PSAHiding.btn = btn = $.el 'span',
- innerHTML: '[Dismiss]'
title: 'Mark announcement as read and hide.'
className: 'hide-announcement'
- href: 'javascript:;'
+ $.extend btn, <%= html('[Dismiss]') %>
$.on btn, 'click', PSAHiding.toggle
$.get 'hiddenPSA', 0, ({hiddenPSA}) ->
diff --git a/src/Miscellaneous/FileInfo.coffee b/src/Miscellaneous/FileInfo.coffee
index 44360cc82..703e0202d 100755
--- a/src/Miscellaneous/FileInfo.coffee
+++ b/src/Miscellaneous/FileInfo.coffee
@@ -7,58 +7,34 @@ FileInfo =
cb: @node
node: ->
return if !@file or @isClone
- @file.text.innerHTML = ''
+ $.extend @file.text, <%= html('') %>
FileInfo.format Conf['fileInfo'], @, @file.text.firstElementChild
format: (formatString, post, outputNode) ->
- FileInfo.innerHTML = ''
+ output = []
formatString.replace /%(.)|[^%]+/g, (s, c) ->
- if c of FileInfo.formatters
+ output.push if c of FileInfo.formatters
FileInfo.formatters[c].call post
else
- FileInfo.innerHTML += E s
+ <%= html('${s}') %>
''
- outputNode.innerHTML = FileInfo.innerHTML
+ $.extend outputNode, <%= html('@{output}') %>
formatters:
- t: ->
- timestamp = @file.URL.match(/\d+\..+$/)[0]
- FileInfo.innerHTML += E timestamp
- T: ->
- FileInfo.innerHTML += ""
- FileInfo.formatters.t.call @
- FileInfo.innerHTML += ''
- l: ->
- FileInfo.innerHTML += ""
- FileInfo.formatters.n.call @
- FileInfo.innerHTML += ''
- L: ->
- FileInfo.innerHTML += ""
- FileInfo.formatters.N.call @
- FileInfo.innerHTML += ''
+ t: -> <%= html('${@file.URL.match(/\\d+\\..+$/)[0]}') %>
+ T: -> <%= html('&{FileInfo.formatters.t.call @}') %>
+ l: -> <%= html('&{FileInfo.formatters.n.call @}') %>
+ L: -> <%= html('&{FileInfo.formatters.N.call @}') %>
n: ->
fullname = @file.name
shortname = Build.shortFilename @file.name, @isReply
if fullname is shortname
- FileInfo.innerHTML += E fullname
+ <%= html('${fullname}') %>
else
- FileInfo.innerHTML += "#{E shortname}#{E fullname}"
- N: ->
- FileInfo.innerHTML += E @file.name
- p: ->
- if @file.isSpoiler
- FileInfo.innerHTML += 'Spoiler, '
- s: ->
- FileInfo.innerHTML += E @file.size
- B: ->
- sizeB = Math.round(@file.sizeInBytes)
- FileInfo.innerHTML += "#{+sizeB} Bytes"
- K: ->
- sizeKB = Math.round(@file.sizeInBytes/1024)
- FileInfo.innerHTML += "#{+sizeKB} KB"
- M: ->
- sizeMB = Math.round(@file.sizeInBytes/1048576*100)/100
- FileInfo.innerHTML += "#{+sizeMB} MB"
- r: ->
- dim = @file.dimensions or 'PDF'
- FileInfo.innerHTML += E dim
- '%': ->
- FileInfo.innerHTML += '%'
+ <%= html('${shortname}${fullname}') %>
+ N: -> <%= html('${@file.name}') %>
+ p: -> if @file.isSpoiler then <%= html('Spoiler, ') %> else <%= html('') %>
+ s: -> <%= html('${@file.size}') %>
+ B: -> <%= html('${Math.round @file.sizeInBytes} Bytes') %>
+ K: -> <%= html('${Math.round(@file.sizeInBytes/1024)} KB') %>
+ M: -> <%= html('${Math.round(@file.sizeInBytes/1048576*100)/100} MB') %>
+ r: -> <%= html('${@file.dimensions or "PDF"}') %>
+ '%': -> <%= html('%') %>
diff --git a/src/Monitoring/ThreadStats.coffee b/src/Monitoring/ThreadStats.coffee
index af24dcb5d..393b497e9 100755
--- a/src/Monitoring/ThreadStats.coffee
+++ b/src/Monitoring/ThreadStats.coffee
@@ -2,16 +2,19 @@ ThreadStats =
init: ->
return if g.VIEW isnt 'thread' or !Conf['Thread Stats']
+ countHTML = <%= html('0 / 0') %>
+ countHTML = <%= html('&{countHTML} / 0') %> if Conf['Page Count in Stats']
+
if Conf['Updater and Stats in Header']
@dialog = sc = $.el 'span',
- innerHTML: "0 / 0#{if Conf['Page Count in Stats'] then ' / 0' else ''}"
id: 'thread-stats'
title: 'Post Count / File Count' + (if Conf["Page Count in Stats"] then " / Page Count" else "")
+ $.extend sc, countHTML
$.ready ->
Header.addShortcut sc
else
@dialog = sc = UI.dialog 'thread-stats', 'bottom: 0px; right: 0px;',
- innerHTML: "0 / 0#{if Conf['Page Count in Stats'] then ' / 0' else ''}
"
+ <%= html('&{countHTML}
') %>
$.ready =>
$.add d.body, sc
diff --git a/src/Monitoring/ThreadUpdater.coffee b/src/Monitoring/ThreadUpdater.coffee
index e75ab2864..52bcee0db 100755
--- a/src/Monitoring/ThreadUpdater.coffee
+++ b/src/Monitoring/ThreadUpdater.coffee
@@ -4,13 +4,13 @@ ThreadUpdater =
if Conf['Updater and Stats in Header']
@dialog = sc = $.el 'span',
- innerHTML: ''
id: 'updater'
+ $.extend sc, <%= html('') %>
$.ready ->
Header.addShortcut sc
else
@dialog = sc = UI.dialog 'updater', 'bottom: 0px; left: 0px;',
- innerHTML: ''
+ <%= html('') %>
$.addClass doc, 'float'
$.ready =>
$.addClass doc, 'float'
@@ -26,8 +26,8 @@ ThreadUpdater =
$.on @status, 'click', @update
updateLink = $.el 'span',
- innerHTML: 'Update'
className: 'brackets-wrap updatelink'
+ $.extend updateLink, <%= html('Update') %>
$.ready ->
$.add $('.navLinksBot'), [$.tn(' '), updateLink]
$.on updateLink.firstElementChild, 'click', @update
@@ -46,7 +46,7 @@ ThreadUpdater =
subEntries.push el: el
@settings = $.el 'span',
- innerHTML: 'Interval'
+ <%= html('Interval') %>
$.on @settings, 'click', @intervalShortcut
diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee
index f048b4d53..00d2923d1 100755
--- a/src/Monitoring/ThreadWatcher.coffee
+++ b/src/Monitoring/ThreadWatcher.coffee
@@ -10,7 +10,7 @@ ThreadWatcher =
className: 'disabled fa fa-eye'
@db = new DataBoard 'watchedThreads', @refresh, true
- @dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', innerHTML: <%= importHTML('Monitoring/ThreadWatcher') %>
+ @dialog = UI.dialog 'thread-watcher', 'top: 50px; left: 0px;', <%= importHTML('Monitoring/ThreadWatcher') %>
@status = $ '#watcher-status', @dialog
@list = @dialog.lastElementChild
diff --git a/src/Posting/QR.captcha.coffee b/src/Posting/QR.captcha.coffee
index 05f194a44..1501b9317 100644
--- a/src/Posting/QR.captcha.coffee
+++ b/src/Posting/QR.captcha.coffee
@@ -8,7 +8,7 @@ QR.captcha =
imgContainer = $.el 'div',
className: 'captcha-img'
title: 'Reload reCAPTCHA'
- innerHTML: '
'
+ $.extend imgContainer, <%= html('
') %>
input = $.el 'input',
className: 'captcha-input field'
title: 'Verification'
diff --git a/src/Posting/QR.coffee b/src/Posting/QR.coffee
index c9d1dd925..2cb601c28 100644
--- a/src/Posting/QR.coffee
+++ b/src/Posting/QR.coffee
@@ -45,9 +45,9 @@ QR =
return unless QR.postingIsEnabled
link = $.el 'h1',
- innerHTML: "#{if g.VIEW is 'thread' then 'Reply to Thread' else 'Start a Thread'}"
className: "qr-link-container"
-
+ $.extend link, <%= html('${if g.VIEW is "thread" then "Reply to Thread" else "Start a Thread"}') %>
+
QR.link = link.firstElementChild
$.on link.firstChild, 'click', ->
$.event 'CloseMenu'
@@ -58,8 +58,8 @@ QR =
if Conf['Bottom QR Link'] and g.VIEW is 'thread'
linkBot = $.el 'div',
- innerHTML: 'Reply to Thread'
className: "brackets-wrap qr-link-container-bottom"
+ $.extend linkBot, <%= html('Reply to Thread') %>
$.on linkBot.firstElementChild, 'click', ->
$.event 'CloseMenu'
@@ -422,7 +422,7 @@ QR =
dialog: ->
QR.nodes = nodes =
- el: dialog = UI.dialog 'qr', 'top:0;right:0;', innerHTML: <%= importHTML('Features/QuickReply') %>
+ el: dialog = UI.dialog 'qr', 'top:0;right:0;', <%= importHTML('Features/QuickReply') %>
setNode = (name, query) ->
nodes[name] = $ query, dialog
@@ -484,17 +484,16 @@ QR =
nodes.spoiler.parentElement.hidden = true
if g.BOARD.ID is 'f' and g.VIEW isnt 'thread'
- nodes.flashTag = $.el 'select',
- name: 'filetag'
- innerHTML: '''
-
-
-
-
-
-
-
- '''
+ nodes.flashTag = $.el 'select', name: 'filetag'
+ $.extend nodes.flashTag, <%= html(
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ '' +
+ ''
+ ) %>
nodes.flashTag.dataset.default = '4'
$.add nodes.form, nodes.flashTag
@@ -682,10 +681,11 @@ QR =
QR.cooldown.auto = false
QR.status()
QR.error $.el 'span',
- innerHTML: '''
- 4chan X encountered an error while posting.
- [Banned?] [More info]
- '''
+ <%= html(
+ '4chan X encountered an error while posting. ' +
+ '[Banned?] ' +
+ '[More info]'
+ ) %>
extra =
form: $.formData formData
upCallbacks:
@@ -718,9 +718,9 @@ QR =
if ban = $ '.banType', resDoc # banned/warning
err = $.el 'span',
if ban.textContent.toLowerCase() is 'banned'
- innerHTML: "You are banned on #{$('.board', resDoc).innerHTML}! ;_;
Click here to see the reason."
+ <%= html('You are banned on &{$(".board", resDoc)}! ;_;
Click here to see the reason.') %>
else
- innerHTML: "You were issued a warning on #{$('.board', resDoc).innerHTML} as #{$('.nameBlock', resDoc).innerHTML}.
Reason: #{$('.reason', resDoc).innerHTML}"
+ <%= html('You were issued a warning on &{$(".board", resDoc)} as &{$(".nameBlock", resDoc)}.
Reason: &{$(".reason", resDoc)}') %>
else if err = resDoc.getElementById 'errmsg' # error!
$('a', err)?.target = '_blank' # duplicate image link
else if resDoc.title isnt 'Post successful!'
diff --git a/src/Posting/QR.post.coffee b/src/Posting/QR.post.coffee
index d3330299b..f0dab5857 100644
--- a/src/Posting/QR.post.coffee
+++ b/src/Posting/QR.post.coffee
@@ -4,7 +4,7 @@ QR.post = class
className: 'qr-preview'
draggable: true
href: 'javascript:;'
- innerHTML: ''
+ $.extend el, <%= html('') %>
@nodes =
el: el
diff --git a/src/Quotelinks/QuoteThreading.coffee b/src/Quotelinks/QuoteThreading.coffee
index a54d2cf5d..0fcc84223 100755
--- a/src/Quotelinks/QuoteThreading.coffee
+++ b/src/Quotelinks/QuoteThreading.coffee
@@ -8,7 +8,7 @@ QuoteThreading =
@enabled = true
@controls = $.el 'span',
- innerHTML: ''
+ <%= html('') %>
input = $ 'input', @controls
$.on input, 'change', @toggle