diff --git a/LICENSE b/LICENSE index ba5abcb23..671794a8b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ /* -* appchan x - Version 2.9.43 - 2015-01-08 +* appchan x - Version 2.9.43 - 2015-01-09 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js index 4f6c9fabb..1bb01d7a2 100644 --- a/builds/appchan-x.user.js +++ b/builds/appchan-x.user.js @@ -27,7 +27,7 @@ // ==/UserScript== /* -* appchan x - Version 2.9.43 - 2015-01-08 +* appchan x - Version 2.9.43 - 2015-01-09 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -7612,13 +7612,13 @@ } }; thisPost = { - el: UI.checkbox('thisPost', ' This post', true) + el: UI.checkbox('thisPost', 'This post', true) }; replies = { - el: UI.checkbox('replies', ' Hide replies', Conf['Recursive Hiding']) + el: UI.checkbox('replies', 'Hide replies', Conf['Recursive Hiding']) }; makeStub = { - el: UI.checkbox('makeStub', ' Make stub', Conf['Stubs']) + el: UI.checkbox('makeStub', 'Make stub', Conf['Stubs']) }; Menu.menu.addEntry({ el: $.el('div', { @@ -7648,14 +7648,14 @@ } }; thisPost = { - el: UI.checkbox('thisPost', ' This post', false), + el: UI.checkbox('thisPost', 'This post', false), open: function(post) { this.el.firstChild.checked = post.isHidden; return true; } }; replies = { - el: UI.checkbox('replies', ' Show replies', false), + el: UI.checkbox('replies', 'Show replies', false), open: function(post) { var data; data = PostHiding.db.get({ @@ -17204,7 +17204,7 @@ Settings = { init: function() { - var addSection, arr, check, el, settings, _i, _len, _ref; + var add, check, el, settings; el = $.el('a', { className: 'settings-link', title: 'Appchan X Settings', @@ -17216,12 +17216,16 @@ el: el, order: 1 }); - addSection = this.addSection; - _ref = [['style', 'Style'], ['themes', 'Themes'], ['mascots', 'Mascots'], ['main', 'Script'], ['filter', 'Filter'], ['sauce', 'Sauce'], ['advanced', 'Advanced'], ['keybinds', 'Keybinds']]; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - arr = _ref[_i]; - addSection(arr[1], Settings[arr[0]]); - } + add = this.addSection; + add('Style', this.style); + add('Themes', this.themes); + add('Mascots', this.mascots); + add('Main', this.main); + add('Filter', this.filter); + add('Sauce', this.sauce); + add('Advanced', this.advanced); + add('Keybinds', this.keybinds); + $.on(d, 'AddSettingsSection', Settings.addSection); $.on(d, 'OpenSettings', function(e) { return Settings.open(e.detail); }); @@ -17295,9 +17299,13 @@ return $.event('OpenSettings', null, dialog); }, close: function() { + var _ref; if (!Settings.dialog) { return; } + if ((_ref = d.activeElement) != null) { + _ref.blur(); + } $.rm(Settings.overlay); $.rm(Settings.dialog); delete Settings.overlay; @@ -17305,7 +17313,10 @@ }, sections: [], addSection: function(title, open) { - var hyphenatedTitle; + var hyphenatedTitle, _ref; + if (typeof title !== 'string') { + _ref = title.detail, title = _ref.title, open = _ref.open; + } hyphenatedTitle = title.toLowerCase().replace(/\s+/g, '-'); return Settings.sections.push({ title: title, @@ -17327,27 +17338,45 @@ return $.event('OpenSettings', null, section); }, main: function(section) { - var arr, button, description, div, fs, input, inputs, items, key, obj, _ref; + var arr, button, container, containers, description, div, fs, input, inputs, items, key, level, obj, _ref; items = {}; inputs = {}; _ref = Config.main; for (key in _ref) { obj = _ref[key]; fs = $.el('fieldset', { - innerHTML: "" + key + "" + innerHTML: "" + E(key) + "" }); + containers = [fs]; for (key in obj) { arr = obj[key]; description = arr[1]; - div = $.el('div', { - innerHTML: "" + description + "" - }); + div = $.el('div'); + $.add(div, [ + UI.checkbox(key, key, false), $.el('span', { + "class": 'description', + textContent: ": " + description + }) + ]); input = $('input', div); $.on($('label', div), 'mouseover', Settings.mouseover); - $.on(input, 'change', $.cb.checked); + $.on(input, 'change', function() { + this.parentNode.parentNode.dataset.checked = this.checked; + return $.cb.checked.call(this); + }); items[key] = Conf[key]; inputs[key] = input; - $.add(fs, div); + level = arr[2] || 0; + if (containers.length <= level) { + container = $.el('div', { + className: 'suboption-list' + }); + $.add(containers[containers.length - 1].lastElementChild, container); + containers[level] = container; + } else if (containers.length > level + 1) { + containers.splice(level + 1, containers.length - (level + 1)); + } + $.add(containers[level], div); } Rice.nodes(fs); $.add(section, fs); @@ -17357,10 +17386,11 @@ for (key in items) { val = items[key]; inputs[key].checked = val; + inputs[key].parentNode.parentNode.dataset.checked = val; } }); div = $.el('div', { - innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." + innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." }); button = $('button', div); $.get('hiddenPosts', {}, function(_arg) { @@ -17412,6 +17442,7 @@ return; } if (!confirm('Your current settings will be entirely overwritten, are you sure?')) { + new Notice('info', "Import aborted.", 1); return; } reader = new FileReader(); @@ -17419,14 +17450,13 @@ var err; try { Settings.loadSettings(JSON.parse(e.target.result)); + if (confirm('Import successful. Reload now?')) { + return window.location.reload(); + } } catch (_error) { err = _error; alert('Import failed due to an error.'); - c.error(err.stack); - return; - } - if (confirm('Import successful. Reload now?')) { - return window.location.reload(); + return c.error(err.stack); } }; return reader.readAsText(file); @@ -17477,9 +17507,10 @@ $.add(div, ta); return; } - return $.extend(div, { + $.extend(div, { innerHTML: "
Filter is disabled.
\r

\rUse regular expressions, one per line.
\rLines starting with a # will be ignored.
\rFor example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
\rMD5 filtering uses exact string matching, not regular expressions.\r

\r\r" }); + return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; @@ -17488,33 +17519,25 @@ }); ta = $('textarea', section); $.get('sauces', Conf['sauces'], function(item) { - return ta.value = item['sauces'].replace(/\$\d/g, function(c) { - switch (c) { - case '$1': - return '%TURL'; - case '$2': - return '%URL'; - case '$3': - return '%MD5'; - case '$4': - return '%board'; - default: - return c; - } - }); + return ta.value = item['sauces']; }); return $.on(ta, 'change', $.cb.value); }, advanced: function(section) { - var archBoards, boardID, boardOptions, boardSelect, boards, event, files, i, input, inputs, item, items, name, o, row, rows, software, ta, table, withCredentials, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _m, _n, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; + var archBoards, boardID, boardOptions, boardSelect, boards, customCSS, event, files, i, input, inputs, interval, item, items, name, o, row, rows, software, ta, table, warning, withCredentials, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _len6, _m, _n, _o, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6; $.extend(section, { innerHTML: "
\rArchiver\r
404 Redirect is disabled.
\r
\r\r\r\r\r\r\r\r
Thread redirectionPost fetchingFile redirection
\rDisabled selections indicate that only one archive is available for that board and redirection type.\r
\r
\rCustom Board Navigation\r
\rNew lines will be converted into spaces.

\r
In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
\r
Board link: g
\r
Title link: g-title
\r
Board link (Replace with title when on that board): g-replace
\r
Full text link: g-full
\r
Custom text link: g-text:\"Install Gentoo\"
\r
External link: external-text:\"Google\",\"http://www.google.com\"
\r
Index mode: g-mode:\"type\" where type is paged, all threads or catalog
\r
Index sort: g-sort:\"type\" where type is bump order, last reply, creation date, reply count or file countCombinations are possible: g-text:\"VIP Catalog\"-mode:\"catalog\"-sort:\"creation date\"
\r
Full board list toggle: toggle-all
\r
\r
\r[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h-mode:\"catalog\"-sort:\"file count\"] [t-text:\"Piracy\"]
\rwill give you
\r[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
\rif you are on /g/.\r
\r
\r
\rTime Formatting is disabled.\r
:
\r
Supported format specifiers:
\r
Day: %a, %A, %d, %e
\r
Month: %m, %b, %B
\r
Year: %y, %Y
\r
Hour: %k, %H, %l, %I, %p, %P
\r
Minute: %M
\r
Second: %S
\r
\r
\rQuote Backlinks formatting is disabled.\r
:
\r
\r
\rFile Info Formatting is disabled.\r
:
\r
Link: %l (truncated), %L (untruncated), %T (Unix timestamp)
\r
Original file name: %n (truncated), %N (untruncated), %t (Unix timestamp)
\r
Spoiler indicator: %p
\r
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
\r
Resolution: %r (Displays 'PDF' for PDF files)
\r
\r
\rQuick Reply Personas\r\r

\rOne item per line.
\rItems will be added in the relevant input's auto-completion list.
\rPassword items will always be used, since there is no password input.
\rLines starting with a # will be ignored.\r

\r
    You can use these settings with each item, separate them with semicolons:\r
  • Possible items are: name, options (or equivalently email), subject and password.
  • \r
  • Wrap values of items with quotes, like this: options:\"sage\".
  • \r
  • Force values as defaults with the always keyword, for example: options:\"sage\";always.
  • \r
  • Select specific boards for an item, separated with commas, for example: options:\"sage\";boards:jp;always.
  • \r
\r
\r
\rUnread Favicon is disabled.\r\r\r
\r
\rThread Updater is disabled.\r
\rInterval: \r
\r
\r
\r Custom CSS\r
\r\r\r
\r
\r" }); + _ref = $$('.warning', section); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + warning = _ref[_i]; + warning.hidden = Conf[warning.dataset.feature]; + } items = {}; inputs = {}; - _ref = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; + _ref1 = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss']; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + name = _ref1[_j]; input = $("[name='" + name + "']", section); items[name] = Conf[name]; inputs[name] = input; @@ -17540,15 +17563,20 @@ } return Rice.nodes(section); }); - $.on($('input[name=Interval]', section), 'change', ThreadUpdater.cb.interval); - $.on($('input[name="Custom CSS"]', section), 'change', Settings.togglecss); - $.on($.id('apply-css'), 'click', Settings.usercss); + interval = $('input[name="Interval"]', section); + customCSS = $('input[name="Custom CSS"]', section); + interval.value = Conf['Interval']; + customCSS.checked = Conf['Custom CSS']; + inputs['usercss'].disabled = !Conf['Custom CSS']; + $.on(interval, 'change', ThreadUpdater.cb.interval); + $.on(customCSS, 'change', Settings.togglecss); + $.on($('#apply-css', section), 'click', Settings.usercss); archBoards = {}; - _ref1 = Redirect.archives; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - _ref2 = _ref1[_j], name = _ref2.name, boards = _ref2.boards, files = _ref2.files, software = _ref2.software, withCredentials = _ref2.withCredentials; - for (_k = 0, _len2 = boards.length; _k < _len2; _k++) { - boardID = boards[_k]; + _ref2 = Redirect.archives; + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + _ref3 = _ref2[_k], name = _ref3.name, boards = _ref3.boards, files = _ref3.files, software = _ref3.software, withCredentials = _ref3.withCredentials; + for (_l = 0, _len3 = boards.length; _l < _len3; _l++) { + boardID = boards[_l]; o = archBoards[boardID] || (archBoards[boardID] = { thread: [[], []], post: [[], []], @@ -17566,9 +17594,9 @@ } for (boardID in archBoards) { o = archBoards[boardID]; - _ref3 = ['thread', 'post', 'file']; - for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { - item = _ref3[_l]; + _ref4 = ['thread', 'post', 'file']; + for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) { + item = _ref4[_m]; if (o[item][0].length === 0 && o[item][1].length !== 0) { o[item][0].push('disabled'); } @@ -17577,9 +17605,9 @@ } rows = []; boardOptions = []; - _ref4 = Object.keys(archBoards).sort(); - for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) { - boardID = _ref4[_m]; + _ref5 = Object.keys(archBoards).sort(); + for (_n = 0, _len5 = _ref5.length; _n < _len5; _n++) { + boardID = _ref5[_n]; row = $.el('tr', { className: "board-" + boardID }); @@ -17590,13 +17618,14 @@ selected: boardID === g.BOARD.ID })); o = archBoards[boardID]; - _ref5 = ['thread', 'post', 'file']; - for (_n = 0, _len5 = _ref5.length; _n < _len5; _n++) { - item = _ref5[_n]; + _ref6 = ['thread', 'post', 'file']; + for (_o = 0, _len6 = _ref6.length; _o < _len6; _o++) { + item = _ref6[_o]; $.add(row, Settings.addArchiveCell(boardID, o, item)); } rows.push(row); } + rows[0].hidden = !g.BOARD.ID in archBoards; $.add($('tbody', section), rows); boardSelect = $('#archive-board-select', section); $.add(boardSelect, boardOptions); @@ -17638,7 +17667,9 @@ value: archive })); } - td.innerHTML = ''; + $.extend(td, { + innerHTML: "" + }); select = td.firstElementChild; if (!(select.disabled = length === 1)) { select.setAttribute('data-boardid', boardID); @@ -17665,7 +17696,12 @@ return this.nextElementSibling.textContent = Time.format(this.value, new Date()); }, backlink: function() { - return this.nextElementSibling.textContent = this.value.replace(/%id/g, '123456789'); + return this.nextElementSibling.textContent = this.value.replace(/%(?:id|%)/g, function(x) { + return { + '%id': '123456789', + '%%': '%' + }[x]; + }); }, fileInfo: function() { var data; @@ -17678,21 +17714,25 @@ sizeInBytes: 276 * 1024, dimensions: '1280x720', isImage: true, - isVideo: false, isSpoiler: true } }; - return this.nextElementSibling.innerHTML = FileInfo.format(this.value, data); + return FileInfo.format(this.value, data, this.nextElementSibling); }, favicon: function() { - Favicon.init(); + var img; + Favicon["switch"](); if (g.VIEW === 'thread' && Conf['Unread Favicon']) { Unread.update(); } - return $.id('favicon-preview').innerHTML = "\n\n\n"; + img = this.nextElementSibling.children; + img[0].src = Favicon["default"]; + img[1].src = Favicon.unreadSFW; + img[2].src = Favicon.unreadNSFW; + return img[3].src = Favicon.unreadDead; }, togglecss: function() { - if ($('textarea', this.parentNode.parentNode).disabled = !this.checked) { + if ($('textarea[name=usercss]', $.x('ancestor::fieldset[1]', this)).disabled = !this.checked) { CustomCSS.rmStyle(); } else { CustomCSS.addStyle(); @@ -17707,6 +17747,7 @@ $.extend(section, { innerHTML: "
Keybinds are disabled.
\r
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
\r
Press Backspace to disable a keybind.
\r\r\r
ActionsKeybinds
" }); + $('.warning', section).hidden = Conf['Keybinds']; tbody = $('tbody', section); items = {}; inputs = {}; @@ -17714,7 +17755,7 @@ for (key in _ref) { arr = _ref[key]; tr = $.el('tr', { - innerHTML: "" + arr[1] + "" + innerHTML: "" + E(arr[1]) + "" }); input = $('input', tr); input.name = key; @@ -17747,7 +17788,7 @@ return $.cb.value.call(this); }, style: function(section) { - var arr, description, div, fs, html, input, inputs, items, key, name, nodes, obj, type, value, _i, _len, _ref; + var arr, description, div, fs, html, input, inputs, items, key, name, nodes, obj, span, type, value, _i, _len, _ref; nodes = $.frag(); items = {}; inputs = {}; @@ -17765,7 +17806,9 @@ }); if (type) { if (type === 'text') { - div.innerHTML = "
" + key + "
" + description + "
"; + $.extend(div, { + innerHTML: "
" + E(key) + "
" + E(description) + "
" + }); input = $("input", div); } else { html = "
" + key + "
" + description + "
" + key + "
" + description + ""; + span = $.el('span', { + "class": 'description', + textContent: description + }); + span.style.display = 'none'; + $.add(div, [UI.checkbox(key, key), span]); input = $('input', div); } items[key] = Conf[key]; @@ -17839,8 +17887,10 @@ } div = $.el('div', { className: "theme " + (name === Conf[g.THEMESTRING] ? 'selectedtheme' : ''), - id: name, - innerHTML: "
\n " + name + "\n \n " + theme['Author'] + "\n \n (SAGE)\n \n " + theme['Author Tripcode'] + "\n \n No.27583594\n \n >>edit\n \n >>export\n \n >>delete\n
\n >>27582902\n
\n Post content is right here.\n

\n Selected\n

" + id: name + }); + $.extend(div, { + innerHTML: "
\r
\r
\r\r" + name + "\r\r\r" + theme['Author'] + "\r\r\r(SAGE)\r\r\r" + theme['Author Tripcode'] + "\r\r\r\rNo.27583594\r\r\r>>edit\r\r\r>>export\r\r\r>>delete\r\r
\r
\r\r>>27582902\r\r
\rPost content is right here.\r
\r

\rSelected\r

\r
" }); div.style.backgroundColor = theme['Background Color']; _ref = $$('a[data-color]', div); @@ -17857,8 +17907,10 @@ $.add(suboptions, div); } div = $.el('div', { - id: 'addthemes', - innerHTML: "New Theme\n/\nImport Theme\n/\nUndelete Theme" + id: 'addthemes' + }); + $.extend(div, { + innerHTML: "New Theme\r/\rImport Theme\r\r/\rUndelete Theme" }); $.on($("#newtheme", div), 'click', function() { ThemeTools.init("untitled"); @@ -17886,8 +17938,10 @@ } div = $.el('div', { id: name, - className: theme, - innerHTML: "
\n " + name + "\n \n " + theme['Author'] + "\n \n (SAGE)\n \n " + theme['Author Tripcode'] + "\n \n No.27583594\n
\n >>27582902\n
\n I forgive you for using VLC to open me. ;__;\n
" + className: theme + }); + $.extend(div, { + innerHTML: "
\r
\r\r" + name + "\r\r\r" + theme['Author'] + "\r\r\r(SAGE)\r\r\r" + theme['Author Tripcode'] + "\r\r\r\rNo.27583594\r\r
\r
\r\r>>27582902\r\r
\rI forgive you for using VLC to open me. ;__;\r
\r
" }); $.on(div, 'click', cb.restore); $.add(suboptions, div); @@ -17942,15 +17996,17 @@ }); mascotHide = $.el("div", { id: "mascot_hide", - className: "reply", - innerHTML: "Hide Categories
" + className: "reply" + }, { + innerHTML: "Hide Categories
" }); keys = Object.keys(Mascots); keys.sort(); if (mode === 'default') { mascotoptions = $.el('div', { - id: 'mascot-options', - innerHTML: "EditDeleteExport" + id: 'mascot-options' + }, { + innerHTML: "EditDeleteExport" }); $.on($('.edit', mascotoptions), 'click', cb.edit); $.on($('.delete', mascotoptions), 'click', cb["delete"]); @@ -17967,14 +18023,13 @@ menu = $('div', mascotHide); categories[name] = div = $.el("div", { id: name, - className: "mascots-container", - innerHTML: "

" + name + "

", + className: "mascots-container" + }, { + innerHTML: "

" + E(name) + "

" + }, { hidden: __indexOf.call(Conf["Hidden Categories"], name) >= 0 }); - option = $.el("label", { - name: name, - innerHTML: "= 0 ? 'checked' : '') + ">" + name - }); + option = UI.checkbox(name, name, __indexOf.call(Conf["Hidden Categories"], name) >= 0); $.on($('input', option), 'change', cb.category); $.add(suboptions, div); $.add(menu, option); @@ -17987,16 +18042,20 @@ mascot = Mascots[name]; mascotEl = $.el('div', { id: name, - className: __indexOf.call(Conf[g.MASCOTSTRING], name) >= 0 ? 'mascot enabled' : 'mascot', - innerHTML: "
" + (name.replace(/_/g, " ")) + "
" + className: __indexOf.call(Conf[g.MASCOTSTRING], name) >= 0 ? 'mascot enabled' : 'mascot' + }); + $.extend(div, { + innerHTML: "
" + (name.replace(/_/g, ' ')) + "
\r
" }); $.on(mascotEl, 'click', cb.select); $.on(mascotEl, 'mouseover', addoptions); $.add(categories[mascot.category] || categories[MascotTools.categories[0]], mascotEl); } batchmascots = $.el('div', { - id: "mascots_batch", - innerHTML: " Clear All\n/\nSelect All\n/\nAdd Mascot\n/\nImport Mascot\n/\nUndelete Mascots\n/\nGet More Mascots!" + id: "mascots_batch" + }); + $.extend(batchmascots, { + innerHTML: "Clear All\r/\rSelect All\r/\rAdd Mascot\r/\rImport Mascot\r/\rUndelete Mascots\r/\rGet More Mascots!" }); $.on($('#clear', batchmascots), 'click', function() { var enabledMascots, _k, _len2; @@ -18050,15 +18109,18 @@ mascot = Mascots[name]; mascotEl = $.el('div', { className: 'mascot', - id: name, - innerHTML: "
" + (name.replace(/_/g, " ")) + "
" + id: name + }); + $.extend(mascotEl, { + innerHTML: "
" + (name.replace(/_/g, ' ')) + "
\r
" }); $.on(mascotEl, 'click', cb.restore); $.add(container, mascotEl); } $.add(suboptions, container); batchmascots = $.el('div', { - id: "mascots_batch", + id: "mascots_batch" + }, { innerHTML: "Return" }); $.on($('#return', batchmascots), 'click', function() { diff --git a/builds/crx/script.js b/builds/crx/script.js index 54680a7c3..c00c1d1b6 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -1,6 +1,6 @@ // Generated by CoffeeScript /* -* appchan x - Version 2.9.43 - 2015-01-08 +* appchan x - Version 2.9.43 - 2015-01-09 * * Licensed under the MIT license. * https://github.com/zixaphir/appchan-x/blob/master/LICENSE @@ -7657,13 +7657,13 @@ } }; thisPost = { - el: UI.checkbox('thisPost', ' This post', true) + el: UI.checkbox('thisPost', 'This post', true) }; replies = { - el: UI.checkbox('replies', ' Hide replies', Conf['Recursive Hiding']) + el: UI.checkbox('replies', 'Hide replies', Conf['Recursive Hiding']) }; makeStub = { - el: UI.checkbox('makeStub', ' Make stub', Conf['Stubs']) + el: UI.checkbox('makeStub', 'Make stub', Conf['Stubs']) }; Menu.menu.addEntry({ el: $.el('div', { @@ -7693,14 +7693,14 @@ } }; thisPost = { - el: UI.checkbox('thisPost', ' This post', false), + el: UI.checkbox('thisPost', 'This post', false), open: function(post) { this.el.firstChild.checked = post.isHidden; return true; } }; replies = { - el: UI.checkbox('replies', ' Show replies', false), + el: UI.checkbox('replies', 'Show replies', false), open: function(post) { var data; data = PostHiding.db.get({ @@ -17227,7 +17227,7 @@ Settings = { init: function() { - var addSection, arr, check, el, settings, _i, _len, _ref; + var add, check, el, settings; el = $.el('a', { className: 'settings-link', title: 'Appchan X Settings', @@ -17239,12 +17239,16 @@ el: el, order: 1 }); - addSection = this.addSection; - _ref = [['style', 'Style'], ['themes', 'Themes'], ['mascots', 'Mascots'], ['main', 'Script'], ['filter', 'Filter'], ['sauce', 'Sauce'], ['advanced', 'Advanced'], ['keybinds', 'Keybinds']]; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - arr = _ref[_i]; - addSection(arr[1], Settings[arr[0]]); - } + add = this.addSection; + add('Style', this.style); + add('Themes', this.themes); + add('Mascots', this.mascots); + add('Main', this.main); + add('Filter', this.filter); + add('Sauce', this.sauce); + add('Advanced', this.advanced); + add('Keybinds', this.keybinds); + $.on(d, 'AddSettingsSection', Settings.addSection); $.on(d, 'OpenSettings', function(e) { return Settings.open(e.detail); }); @@ -17318,9 +17322,13 @@ return $.event('OpenSettings', null, dialog); }, close: function() { + var _ref; if (!Settings.dialog) { return; } + if ((_ref = d.activeElement) != null) { + _ref.blur(); + } $.rm(Settings.overlay); $.rm(Settings.dialog); delete Settings.overlay; @@ -17328,7 +17336,10 @@ }, sections: [], addSection: function(title, open) { - var hyphenatedTitle; + var hyphenatedTitle, _ref; + if (typeof title !== 'string') { + _ref = title.detail, title = _ref.title, open = _ref.open; + } hyphenatedTitle = title.toLowerCase().replace(/\s+/g, '-'); return Settings.sections.push({ title: title, @@ -17350,27 +17361,45 @@ return $.event('OpenSettings', null, section); }, main: function(section) { - var arr, button, description, div, fs, input, inputs, items, key, obj, _ref; + var arr, button, container, containers, description, div, fs, input, inputs, items, key, level, obj, _ref; items = {}; inputs = {}; _ref = Config.main; for (key in _ref) { obj = _ref[key]; fs = $.el('fieldset', { - innerHTML: "" + key + "" + innerHTML: "" + E(key) + "" }); + containers = [fs]; for (key in obj) { arr = obj[key]; description = arr[1]; - div = $.el('div', { - innerHTML: "" + description + "" - }); + div = $.el('div'); + $.add(div, [ + UI.checkbox(key, key, false), $.el('span', { + "class": 'description', + textContent: ": " + description + }) + ]); input = $('input', div); $.on($('label', div), 'mouseover', Settings.mouseover); - $.on(input, 'change', $.cb.checked); + $.on(input, 'change', function() { + this.parentNode.parentNode.dataset.checked = this.checked; + return $.cb.checked.call(this); + }); items[key] = Conf[key]; inputs[key] = input; - $.add(fs, div); + level = arr[2] || 0; + if (containers.length <= level) { + container = $.el('div', { + className: 'suboption-list' + }); + $.add(containers[containers.length - 1].lastElementChild, container); + containers[level] = container; + } else if (containers.length > level + 1) { + containers.splice(level + 1, containers.length - (level + 1)); + } + $.add(containers[level], div); } Rice.nodes(fs); $.add(section, fs); @@ -17380,10 +17409,11 @@ for (key in items) { val = items[key]; inputs[key].checked = val; + inputs[key].parentNode.parentNode.dataset.checked = val; } }); div = $.el('div', { - innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." + innerHTML: ": Clear manually-hidden threads and posts on all boards. Reload the page to apply." }); button = $('button', div); $.get('hiddenPosts', {}, function(_arg) { @@ -17433,6 +17463,7 @@ return; } if (!confirm('Your current settings will be entirely overwritten, are you sure?')) { + new Notice('info', "Import aborted.", 1); return; } reader = new FileReader(); @@ -17440,14 +17471,13 @@ var err; try { Settings.loadSettings(JSON.parse(e.target.result)); + if (confirm('Import successful. Reload now?')) { + return window.location.reload(); + } } catch (_error) { err = _error; alert('Import failed due to an error.'); - c.error(err.stack); - return; - } - if (confirm('Import successful. Reload now?')) { - return window.location.reload(); + return c.error(err.stack); } }; return reader.readAsText(file); @@ -17498,9 +17528,10 @@ $.add(div, ta); return; } - return $.extend(div, { + $.extend(div, { innerHTML: "
Filter is disabled.
\r

\rUse regular expressions, one per line.
\rLines starting with a # will be ignored.
\rFor example, /weeaboo/i will filter posts containing the string `weeaboo`, case-insensitive.
\rMD5 filtering uses exact string matching, not regular expressions.\r

\r
    You can use these settings with each regular expression, separate them with semicolons:\r
  • \rPer boards, separate them with commas. It is global if not specified.
    \rFor example: boards:a,jp;.\r
  • \r
  • \rFilter OPs only along with their threads (`only`), replies only (`no`), or both (`yes`, this is default).
    \rFor example: op:only;, op:no; or op:yes;.\r
  • \r
  • \rOverrule the `Show Stubs` setting if specified: create a stub (`yes`) or not (`no`).
    \rFor example: stub:yes; or stub:no;.\r
  • \r
  • \rHighlight instead of hiding. You can specify a class name to use with a userstyle.
    \rFor example: highlight; or highlight:wallpaper;.\r
  • \r
  • \rHighlighted OPs will have their threads put on top of the board index by default.
    \rFor example: top:yes; or top:no;.\r
  • \r
\r" }); + return $('.warning', div).hidden = Conf['Filter']; }, sauce: function(section) { var ta; @@ -17509,33 +17540,25 @@ }); ta = $('textarea', section); $.get('sauces', Conf['sauces'], function(item) { - return ta.value = item['sauces'].replace(/\$\d/g, function(c) { - switch (c) { - case '$1': - return '%TURL'; - case '$2': - return '%URL'; - case '$3': - return '%MD5'; - case '$4': - return '%board'; - default: - return c; - } - }); + return ta.value = item['sauces']; }); return $.on(ta, 'change', $.cb.value); }, advanced: function(section) { - var archBoards, boardID, boardOptions, boardSelect, boards, event, files, i, input, inputs, item, items, name, o, row, rows, software, ta, table, withCredentials, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _m, _n, _ref, _ref1, _ref2, _ref3, _ref4, _ref5; + var archBoards, boardID, boardOptions, boardSelect, boards, customCSS, event, files, i, input, inputs, interval, item, items, name, o, row, rows, software, ta, table, warning, withCredentials, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _len5, _len6, _m, _n, _o, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6; $.extend(section, { innerHTML: "
\rArchiver\r
404 Redirect is disabled.
\r
\r\r\r\r\r\r\r\r
Thread redirectionPost fetchingFile redirection
\rDisabled selections indicate that only one archive is available for that board and redirection type.\r
\r
\rCustom Board Navigation\r
\rNew lines will be converted into spaces.

\r
In the following examples for /g/, g can be changed to a different board ID (a, b, etc...), the current board (current), or the Twitter link (@).
\r
Board link: g
\r
Title link: g-title
\r
Board link (Replace with title when on that board): g-replace
\r
Full text link: g-full
\r
Custom text link: g-text:\"Install Gentoo\"
\r
External link: external-text:\"Google\",\"http://www.google.com\"
\r
Index mode: g-mode:\"type\" where type is paged, all threads or catalog
\r
Index sort: g-sort:\"type\" where type is bump order, last reply, creation date, reply count or file countCombinations are possible: g-text:\"VIP Catalog\"-mode:\"catalog\"-sort:\"creation date\"
\r
Full board list toggle: toggle-all
\r
\r
\r[ toggle-all ] [current-title] [g-title / a-title / jp-title] [x / wsg / h-mode:\"catalog\"-sort:\"file count\"] [t-text:\"Piracy\"]
\rwill give you
\r[ + ] [Technology] [Technology / Anime & Manga / Otaku Culture] [x / wsg / h] [Piracy]
\rif you are on /g/.\r
\r
\r
\rTime Formatting is disabled.\r
:
\r\r
Day: %a, %A, %d, %e
\r
Month: %m, %b, %B
\r
Year: %y, %Y
\r
Hour: %k, %H, %l, %I, %p, %P
\r
Minute: %M
\r
Second: %S
\r
\r
\rQuote Backlinks formatting is disabled.\r
:
\r
\r
\rFile Info Formatting is disabled.\r
:
\r
Link: %l (truncated), %L (untruncated), %T (Unix timestamp)
\r
Original file name: %n (truncated), %N (untruncated), %t (Unix timestamp)
\r
Spoiler indicator: %p
\r
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
\r
Resolution: %r (Displays 'PDF' for PDF files)
\r
\r
\rQuick Reply Personas\r\r

\rOne item per line.
\rItems will be added in the relevant input's auto-completion list.
\rPassword items will always be used, since there is no password input.
\rLines starting with a # will be ignored.\r

\r
    You can use these settings with each item, separate them with semicolons:\r
  • Possible items are: name, options (or equivalently email), subject and password.
  • \r
  • Wrap values of items with quotes, like this: options:\"sage\".
  • \r
  • Force values as defaults with the always keyword, for example: options:\"sage\";always.
  • \r
  • Select specific boards for an item, separated with commas, for example: options:\"sage\";boards:jp;always.
  • \r
\r
\r
\rUnread Favicon is disabled.\r\r\r
\r
\rThread Updater is disabled.\r
\rInterval: \r
\r
\r
\r Custom CSS\r
\r\r\r
\r
\r" }); + _ref = $$('.warning', section); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + warning = _ref[_i]; + warning.hidden = Conf[warning.dataset.feature]; + } items = {}; inputs = {}; - _ref = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; + _ref1 = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss']; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + name = _ref1[_j]; input = $("[name='" + name + "']", section); items[name] = Conf[name]; inputs[name] = input; @@ -17561,15 +17584,20 @@ } return Rice.nodes(section); }); - $.on($('input[name=Interval]', section), 'change', ThreadUpdater.cb.interval); - $.on($('input[name="Custom CSS"]', section), 'change', Settings.togglecss); - $.on($.id('apply-css'), 'click', Settings.usercss); + interval = $('input[name="Interval"]', section); + customCSS = $('input[name="Custom CSS"]', section); + interval.value = Conf['Interval']; + customCSS.checked = Conf['Custom CSS']; + inputs['usercss'].disabled = !Conf['Custom CSS']; + $.on(interval, 'change', ThreadUpdater.cb.interval); + $.on(customCSS, 'change', Settings.togglecss); + $.on($('#apply-css', section), 'click', Settings.usercss); archBoards = {}; - _ref1 = Redirect.archives; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - _ref2 = _ref1[_j], name = _ref2.name, boards = _ref2.boards, files = _ref2.files, software = _ref2.software, withCredentials = _ref2.withCredentials; - for (_k = 0, _len2 = boards.length; _k < _len2; _k++) { - boardID = boards[_k]; + _ref2 = Redirect.archives; + for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { + _ref3 = _ref2[_k], name = _ref3.name, boards = _ref3.boards, files = _ref3.files, software = _ref3.software, withCredentials = _ref3.withCredentials; + for (_l = 0, _len3 = boards.length; _l < _len3; _l++) { + boardID = boards[_l]; o = archBoards[boardID] || (archBoards[boardID] = { thread: [[], []], post: [[], []], @@ -17587,9 +17615,9 @@ } for (boardID in archBoards) { o = archBoards[boardID]; - _ref3 = ['thread', 'post', 'file']; - for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { - item = _ref3[_l]; + _ref4 = ['thread', 'post', 'file']; + for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) { + item = _ref4[_m]; if (o[item][0].length === 0 && o[item][1].length !== 0) { o[item][0].push('disabled'); } @@ -17598,9 +17626,9 @@ } rows = []; boardOptions = []; - _ref4 = Object.keys(archBoards).sort(); - for (_m = 0, _len4 = _ref4.length; _m < _len4; _m++) { - boardID = _ref4[_m]; + _ref5 = Object.keys(archBoards).sort(); + for (_n = 0, _len5 = _ref5.length; _n < _len5; _n++) { + boardID = _ref5[_n]; row = $.el('tr', { className: "board-" + boardID }); @@ -17611,13 +17639,14 @@ selected: boardID === g.BOARD.ID })); o = archBoards[boardID]; - _ref5 = ['thread', 'post', 'file']; - for (_n = 0, _len5 = _ref5.length; _n < _len5; _n++) { - item = _ref5[_n]; + _ref6 = ['thread', 'post', 'file']; + for (_o = 0, _len6 = _ref6.length; _o < _len6; _o++) { + item = _ref6[_o]; $.add(row, Settings.addArchiveCell(boardID, o, item)); } rows.push(row); } + rows[0].hidden = !g.BOARD.ID in archBoards; $.add($('tbody', section), rows); boardSelect = $('#archive-board-select', section); $.add(boardSelect, boardOptions); @@ -17659,7 +17688,9 @@ value: archive })); } - td.innerHTML = ''; + $.extend(td, { + innerHTML: "" + }); select = td.firstElementChild; if (!(select.disabled = length === 1)) { select.setAttribute('data-boardid', boardID); @@ -17686,7 +17717,12 @@ return this.nextElementSibling.textContent = Time.format(this.value, new Date()); }, backlink: function() { - return this.nextElementSibling.textContent = this.value.replace(/%id/g, '123456789'); + return this.nextElementSibling.textContent = this.value.replace(/%(?:id|%)/g, function(x) { + return { + '%id': '123456789', + '%%': '%' + }[x]; + }); }, fileInfo: function() { var data; @@ -17699,21 +17735,25 @@ sizeInBytes: 276 * 1024, dimensions: '1280x720', isImage: true, - isVideo: false, isSpoiler: true } }; - return this.nextElementSibling.innerHTML = FileInfo.format(this.value, data); + return FileInfo.format(this.value, data, this.nextElementSibling); }, favicon: function() { - Favicon.init(); + var img; + Favicon["switch"](); if (g.VIEW === 'thread' && Conf['Unread Favicon']) { Unread.update(); } - return $.id('favicon-preview').innerHTML = "\n\n\n"; + img = this.nextElementSibling.children; + img[0].src = Favicon["default"]; + img[1].src = Favicon.unreadSFW; + img[2].src = Favicon.unreadNSFW; + return img[3].src = Favicon.unreadDead; }, togglecss: function() { - if ($('textarea', this.parentNode.parentNode).disabled = !this.checked) { + if ($('textarea[name=usercss]', $.x('ancestor::fieldset[1]', this)).disabled = !this.checked) { CustomCSS.rmStyle(); } else { CustomCSS.addStyle(); @@ -17728,6 +17768,7 @@ $.extend(section, { innerHTML: "
Keybinds are disabled.
\r
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
\r
Press Backspace to disable a keybind.
\r\r\r
ActionsKeybinds
" }); + $('.warning', section).hidden = Conf['Keybinds']; tbody = $('tbody', section); items = {}; inputs = {}; @@ -17735,7 +17776,7 @@ for (key in _ref) { arr = _ref[key]; tr = $.el('tr', { - innerHTML: "" + arr[1] + "" + innerHTML: "" + E(arr[1]) + "" }); input = $('input', tr); input.name = key; @@ -17768,7 +17809,7 @@ return $.cb.value.call(this); }, style: function(section) { - var arr, description, div, fs, html, input, inputs, items, key, name, nodes, obj, type, value, _i, _len, _ref; + var arr, description, div, fs, html, input, inputs, items, key, name, nodes, obj, span, type, value, _i, _len, _ref; nodes = $.frag(); items = {}; inputs = {}; @@ -17786,7 +17827,9 @@ }); if (type) { if (type === 'text') { - div.innerHTML = "
" + key + "
" + description + "
"; + $.extend(div, { + innerHTML: "
" + E(key) + "
" + E(description) + "
" + }); input = $("input", div); } else { html = "
" + key + "
" + description + "
" + key + "
" + description + ""; + span = $.el('span', { + "class": 'description', + textContent: description + }); + span.style.display = 'none'; + $.add(div, [UI.checkbox(key, key), span]); input = $('input', div); } items[key] = Conf[key]; @@ -17860,8 +17908,10 @@ } div = $.el('div', { className: "theme " + (name === Conf[g.THEMESTRING] ? 'selectedtheme' : ''), - id: name, - innerHTML: "
\n " + name + "\n \n " + theme['Author'] + "\n \n (SAGE)\n \n " + theme['Author Tripcode'] + "\n \n No.27583594\n \n >>edit\n \n >>export\n \n >>delete\n
\n >>27582902\n
\n Post content is right here.\n

\n Selected\n

" + id: name + }); + $.extend(div, { + innerHTML: "
\r
\r
\r\r" + name + "\r\r\r" + theme['Author'] + "\r\r\r(SAGE)\r\r\r" + theme['Author Tripcode'] + "\r\r\r\rNo.27583594\r\r\r>>edit\r\r\r>>export\r\r\r>>delete\r\r
\r
\r\r>>27582902\r\r
\rPost content is right here.\r
\r

\rSelected\r

\r
" }); div.style.backgroundColor = theme['Background Color']; _ref = $$('a[data-color]', div); @@ -17878,8 +17928,10 @@ $.add(suboptions, div); } div = $.el('div', { - id: 'addthemes', - innerHTML: "New Theme\n/\nImport Theme\n/\nUndelete Theme" + id: 'addthemes' + }); + $.extend(div, { + innerHTML: "New Theme\r/\rImport Theme\r\r/\rUndelete Theme" }); $.on($("#newtheme", div), 'click', function() { ThemeTools.init("untitled"); @@ -17907,8 +17959,10 @@ } div = $.el('div', { id: name, - className: theme, - innerHTML: "
\n " + name + "\n \n " + theme['Author'] + "\n \n (SAGE)\n \n " + theme['Author Tripcode'] + "\n \n No.27583594\n
\n >>27582902\n
\n I forgive you for using VLC to open me. ;__;\n
" + className: theme + }); + $.extend(div, { + innerHTML: "
\r
\r\r" + name + "\r\r\r" + theme['Author'] + "\r\r\r(SAGE)\r\r\r" + theme['Author Tripcode'] + "\r\r\r\rNo.27583594\r\r
\r
\r\r>>27582902\r\r
\rI forgive you for using VLC to open me. ;__;\r
\r
" }); $.on(div, 'click', cb.restore); $.add(suboptions, div); @@ -17963,15 +18017,17 @@ }); mascotHide = $.el("div", { id: "mascot_hide", - className: "reply", - innerHTML: "Hide Categories
" + className: "reply" + }, { + innerHTML: "Hide Categories
" }); keys = Object.keys(Mascots); keys.sort(); if (mode === 'default') { mascotoptions = $.el('div', { - id: 'mascot-options', - innerHTML: "EditDeleteExport" + id: 'mascot-options' + }, { + innerHTML: "EditDeleteExport" }); $.on($('.edit', mascotoptions), 'click', cb.edit); $.on($('.delete', mascotoptions), 'click', cb["delete"]); @@ -17988,14 +18044,13 @@ menu = $('div', mascotHide); categories[name] = div = $.el("div", { id: name, - className: "mascots-container", - innerHTML: "

" + name + "

", + className: "mascots-container" + }, { + innerHTML: "

" + E(name) + "

" + }, { hidden: __indexOf.call(Conf["Hidden Categories"], name) >= 0 }); - option = $.el("label", { - name: name, - innerHTML: "= 0 ? 'checked' : '') + ">" + name - }); + option = UI.checkbox(name, name, __indexOf.call(Conf["Hidden Categories"], name) >= 0); $.on($('input', option), 'change', cb.category); $.add(suboptions, div); $.add(menu, option); @@ -18008,16 +18063,20 @@ mascot = Mascots[name]; mascotEl = $.el('div', { id: name, - className: __indexOf.call(Conf[g.MASCOTSTRING], name) >= 0 ? 'mascot enabled' : 'mascot', - innerHTML: "
" + (name.replace(/_/g, " ")) + "
" + className: __indexOf.call(Conf[g.MASCOTSTRING], name) >= 0 ? 'mascot enabled' : 'mascot' + }); + $.extend(div, { + innerHTML: "
" + (name.replace(/_/g, ' ')) + "
\r
" }); $.on(mascotEl, 'click', cb.select); $.on(mascotEl, 'mouseover', addoptions); $.add(categories[mascot.category] || categories[MascotTools.categories[0]], mascotEl); } batchmascots = $.el('div', { - id: "mascots_batch", - innerHTML: " Clear All\n/\nSelect All\n/\nAdd Mascot\n/\nImport Mascot\n/\nUndelete Mascots\n/\nGet More Mascots!" + id: "mascots_batch" + }); + $.extend(batchmascots, { + innerHTML: "Clear All\r/\rSelect All\r/\rAdd Mascot\r/\rImport Mascot\r/\rUndelete Mascots\r/\rGet More Mascots!" }); $.on($('#clear', batchmascots), 'click', function() { var enabledMascots, _k, _len2; @@ -18071,15 +18130,18 @@ mascot = Mascots[name]; mascotEl = $.el('div', { className: 'mascot', - id: name, - innerHTML: "
" + (name.replace(/_/g, " ")) + "
" + id: name + }); + $.extend(mascotEl, { + innerHTML: "
" + (name.replace(/_/g, ' ')) + "
\r
" }); $.on(mascotEl, 'click', cb.restore); $.add(container, mascotEl); } $.add(suboptions, container); batchmascots = $.el('div', { - id: "mascots_batch", + id: "mascots_batch" + }, { innerHTML: "Return" }); $.on($('#return', batchmascots), 'click', function() { diff --git a/src/Filtering/PostHiding.coffee b/src/Filtering/PostHiding.coffee index 9a69c12ea..72a6e1bc0 100644 --- a/src/Filtering/PostHiding.coffee +++ b/src/Filtering/PostHiding.coffee @@ -83,9 +83,9 @@ PostHiding = @cb = -> PostHiding.menu.hide post $.on @el, 'click', @cb true - thisPost = el: UI.checkbox 'thisPost', ' This post', true - replies = el: UI.checkbox 'replies', ' Hide replies', Conf['Recursive Hiding'] - makeStub = el: UI.checkbox 'makeStub', ' Make stub', Conf['Stubs'] + thisPost = el: UI.checkbox 'thisPost', 'This post', true + replies = el: UI.checkbox 'replies', 'Hide replies', Conf['Recursive Hiding'] + makeStub = el: UI.checkbox 'makeStub', 'Make stub', Conf['Stubs'] Menu.menu.addEntry el: $.el 'div', @@ -104,12 +104,12 @@ PostHiding = $.on @el, 'click', @cb true thisPost = - el: UI.checkbox 'thisPost', ' This post', false + el: UI.checkbox 'thisPost', 'This post', false open: (post) -> @el.firstChild.checked = post.isHidden true replies = - el: UI.checkbox 'replies', ' Show replies', false + el: UI.checkbox 'replies', 'Show replies', false open: (post) -> data = PostHiding.db.get {boardID: post.board.ID, threadID: post.thread.ID, postID: post.ID} @el.firstChild.checked = if 'hideRecursively' of data then data.hideRecursively else Conf['Recursive Hiding'] diff --git a/src/General/Settings.coffee b/src/General/Settings.coffee index 246580c82..5c1822c1e 100755 --- a/src/General/Settings.coffee +++ b/src/General/Settings.coffee @@ -12,18 +12,18 @@ Settings = el: el order: 1 - {addSection} = @ - addSection arr[1], Settings[arr[0]] for arr in [ - ['style', 'Style'] - ['themes', 'Themes'] - ['mascots', 'Mascots'] - ['main', 'Script'] - ['filter', 'Filter'] - ['sauce', 'Sauce'] - ['advanced', 'Advanced'] - ['keybinds', 'Keybinds'] - ] + add = @addSection + + add 'Style', @style + add 'Themes', @themes + add 'Mascots', @mascots + add 'Main', @main + add 'Filter', @filter + add 'Sauce', @sauce + add 'Advanced', @advanced + add 'Keybinds', @keybinds + $.on d, 'AddSettingsSection', Settings.addSection $.on d, 'OpenSettings', (e) -> Settings.open e.detail settings = JSON.parse(localStorage.getItem '4chan-settings') or {} @@ -84,6 +84,8 @@ Settings = close: -> return unless Settings.dialog + # Unfocus current field to trigger change event. + d.activeElement?.blur() $.rm Settings.overlay $.rm Settings.dialog delete Settings.overlay @@ -92,6 +94,8 @@ Settings = sections: [] addSection: (title, open) -> + if typeof title isnt 'string' + {title, open} = title.detail hyphenatedTitle = title.toLowerCase().replace /\s+/g, '-' Settings.sections.push {title, hyphenatedTitle, open} @@ -111,27 +115,41 @@ Settings = inputs = {} for key, obj of Config.main fs = $.el 'fieldset', - innerHTML: "#{key}" + <%= html('${key}') %> + containers = [fs] for key, arr of obj description = arr[1] - div = $.el 'div', - innerHTML: "#{description}" + div = $.el 'div' + $.add div, [ + UI.checkbox key, key, false + $.el 'span', class: 'description', textContent: ": #{description}" + ] input = $ 'input', div $.on $('label', div), 'mouseover', Settings.mouseover - $.on input, 'change', $.cb.checked + $.on input, 'change', -> + @parentNode.parentNode.dataset.checked = @checked + $.cb.checked.call @ items[key] = Conf[key] inputs[key] = input - $.add fs, div + level = arr[2] or 0 + if containers.length <= level + container = $.el 'div', className: 'suboption-list' + $.add containers[containers.length-1].lastElementChild, container + containers[level] = container + else if containers.length > level+1 + containers.splice level+1, containers.length - (level+1) + $.add containers[level], div Rice.nodes fs $.add section, fs $.get items, (items) -> for key, val of items inputs[key].checked = val + inputs[key].parentNode.parentNode.dataset.checked = val 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 'hiddenPosts', {}, ({hiddenPosts}) -> hiddenNum = 0 @@ -163,18 +181,19 @@ Settings = onImport: -> return unless file = @files[0] - return unless confirm 'Your current settings will be entirely overwritten, are you sure?' + unless confirm 'Your current settings will be entirely overwritten, are you sure?' + new Notice 'info', "Import aborted.", 1 + return reader = new FileReader() reader.onload = (e) -> try Settings.loadSettings JSON.parse e.target.result + if confirm 'Import successful. Reload now?' + window.location.reload() catch err alert 'Import failed due to an error.' c.error err.stack - return - if confirm 'Import successful. Reload now?' - window.location.reload() reader.readAsText file loadSettings: (data) -> @@ -207,28 +226,19 @@ Settings = $.add div, ta return $.extend div, <%= importHTML('Settings/Filter-guide') %> + $('.warning', div).hidden = Conf['Filter'] sauce: (section) -> $.extend section, <%= importHTML('Settings/Sauce') %> ta = $ 'textarea', section $.get 'sauces', Conf['sauces'], (item) -> - # XXX remove .replace func after 31-7-2013 (v1 transitioning) - ta.value = item['sauces'].replace /\$\d/g, (c) -> - switch c - when '$1' - '%TURL' - when '$2' - '%URL' - when '$3' - '%MD5' - when '$4' - '%board' - else - c + ta.value = item['sauces'] $.on ta, 'change', $.cb.value advanced: (section) -> $.extend section, <%= importHTML('Settings/Advanced') %> + warning.hidden = Conf[warning.dataset.feature] for warning in $$ '.warning', section + items = {} inputs = {} for name in ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss'] @@ -256,9 +266,16 @@ Settings = Settings[key].call input Rice.nodes section - $.on $('input[name=Interval]', section), 'change', ThreadUpdater.cb.interval - $.on $('input[name="Custom CSS"]', section), 'change', Settings.togglecss - $.on $.id('apply-css'), 'click', Settings.usercss + interval = $ 'input[name="Interval"]', section + customCSS = $ 'input[name="Custom CSS"]', section + + interval.value = Conf['Interval'] + customCSS.checked = Conf['Custom CSS'] + inputs['usercss'].disabled = !Conf['Custom CSS'] + + $.on interval, 'change', ThreadUpdater.cb.interval + $.on customCSS, 'change', Settings.togglecss + $.on $('#apply-css', section), 'click', Settings.usercss archBoards = {} for {name, boards, files, software, withCredentials} in Redirect.archives @@ -293,6 +310,8 @@ Settings = $.add row, Settings.addArchiveCell boardID, o, item for item in ['thread', 'post', 'file'] rows.push row + rows[0].hidden = not g.BOARD.ID of archBoards + $.add $('tbody', section), rows boardSelect = $('#archive-board-select', section) @@ -327,7 +346,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 @@ -350,7 +369,7 @@ Settings = @nextElementSibling.textContent = Time.format @value, new Date() backlink: -> - @nextElementSibling.textContent = @value.replace /%id/g, '123456789' + @nextElementSibling.textContent = @value.replace /%(?:id|%)/g, (x) -> {'%id': '123456789', '%%': '%'}[x] fileInfo: -> data = @@ -362,22 +381,20 @@ Settings = sizeInBytes: 276 * 1024 dimensions: '1280x720' isImage: true - isVideo: false isSpoiler: true - @nextElementSibling.innerHTML = FileInfo.format @value, data + FileInfo.format @value, data, @nextElementSibling favicon: -> - Favicon.init() + Favicon.switch() Unread.update() if g.VIEW is 'thread' and Conf['Unread Favicon'] - $.id('favicon-preview').innerHTML = """ - - - - - """ + img = @nextElementSibling.children + img[0].src = Favicon.default + img[1].src = Favicon.unreadSFW + img[2].src = Favicon.unreadNSFW + img[3].src = Favicon.unreadDead togglecss: -> - if $('textarea', @parentNode.parentNode).disabled = !@checked + if $('textarea[name=usercss]', $.x 'ancestor::fieldset[1]', @).disabled = !@checked CustomCSS.rmStyle() else CustomCSS.addStyle() @@ -388,13 +405,14 @@ Settings = keybinds: (section) -> $.extend section, <%= importHTML('Settings/Keybinds') %> + $('.warning', section).hidden = Conf['Keybinds'] tbody = $ 'tbody', section items = {} inputs = {} for key, arr of Config.hotkeys tr = $.el 'tr', - innerHTML: "#{arr[1]}" + <%= html('${arr[1]}') %> input = $ 'input', tr input.name = key input.spellcheck = false @@ -437,7 +455,7 @@ Settings = if type is 'text' - div.innerHTML = "
#{key}
#{description}
" + $.extend div, <%= html('
${key}
${description}
') %> input = $ "input", div else @@ -450,8 +468,17 @@ Settings = input = $ "select", div else + span = $.el 'span', + class: 'description' + textContent: description + + span.style.display = 'none' - div.innerHTML = "
#{description}" + $.add div, [ + UI.checkbox key, key + span + ] + input = $ 'input', div items[key] = Conf[key] @@ -507,7 +534,8 @@ Settings = div = $.el 'div', className: "theme #{if name is Conf[g.THEMESTRING] then 'selectedtheme' else ''}" id: name - innerHTML: """<%= grunt.file.read('src/General/html/Settings/Theme.html').replace(/>\s+<').trim() %>""" + + $.extend div, <%= importHTML('Settings/Theme') %> div.style.backgroundColor = theme['Background Color'] @@ -526,7 +554,8 @@ Settings = div = $.el 'div', id: 'addthemes' - innerHTML: """<%= grunt.file.read('src/General/html/Settings/Batch-Theme.html').replace(/>\s+<').trim() %>""" + + $.extend div, <%= importHTML('Settings/Batch-Theme') %> $.on $("#newtheme", div), 'click', -> ThemeTools.init "untitled" @@ -556,7 +585,8 @@ Settings = div = $.el 'div', id: name className: theme - innerHTML: """<%= grunt.file.read('src/General/html/Settings/Deleted-Theme.html').replace(/>\s+<').trim() %>""" + + $.extend div, <%= importHTML('Settings/Deleted-Theme') %> $.on div, 'click', cb.restore @@ -611,7 +641,7 @@ Settings = mascotHide = $.el "div", id: "mascot_hide" className: "reply" - innerHTML: "Hide Categories
" + <%= html('Hide Categories
') %> keys = Object.keys Mascots keys.sort() @@ -619,7 +649,7 @@ Settings = if mode is 'default' mascotoptions = $.el 'div', id: 'mascot-options' - innerHTML: """EditDeleteExport""" + <%= html('EditDeleteExport') %> $.on $('.edit', mascotoptions), 'click', cb.edit $.on $('.delete', mascotoptions), 'click', cb.delete @@ -635,12 +665,10 @@ Settings = categories[name] = div = $.el "div", id: name className: "mascots-container" - innerHTML: "

#{name}

" + <%= html('

${name}

') %> hidden: name in Conf["Hidden Categories"] - option = $.el "label", - name: name - innerHTML: "#{name}" + option = UI.checkbox name, name, name in Conf["Hidden Categories"] $.on $('input', option), 'change', cb.category @@ -653,7 +681,8 @@ Settings = mascotEl = $.el 'div', id: name className: if name in Conf[g.MASCOTSTRING] then 'mascot enabled' else 'mascot' - innerHTML: "<%= grunt.file.read('src/General/html/Settings/Mascot.html') %>" + + $.extend div, <%= importHTML('Settings/Mascot') %> $.on mascotEl, 'click', cb.select $.on mascotEl, 'mouseover', addoptions @@ -662,7 +691,8 @@ Settings = batchmascots = $.el 'div', id: "mascots_batch" - innerHTML: """<%= grunt.file.read('src/General/html/Settings/Batch-Mascot.html') %>""" + + $.extend batchmascots, <%= importHTML('Settings/Batch-Mascot') %> $.on $('#clear', batchmascots), 'click', -> enabledMascots = JSON.parse(JSON.stringify(Conf[g.MASCOTSTRING])) @@ -704,10 +734,8 @@ Settings = mascotEl = $.el 'div', className: 'mascot' id: name - innerHTML: " -
#{name.replace /_/g, " "} -
-" + + $.extend mascotEl, <%= importHTML('Settings/Mascot') %> $.on mascotEl, 'click', cb.restore @@ -717,7 +745,7 @@ Settings = batchmascots = $.el 'div', id: "mascots_batch" - innerHTML: """Return""" + <%= html('Return') %> $.on $('#return', batchmascots), 'click', -> mascots = diff --git a/src/General/html/Settings/Mascot.html b/src/General/html/Settings/Mascot.html index 12d8e25e2..a16a61a49 100644 --- a/src/General/html/Settings/Mascot.html +++ b/src/General/html/Settings/Mascot.html @@ -1,2 +1,2 @@ -
#{name.replace /_/g, " "}
-
\ No newline at end of file +
#{name.replace /_/g, ' '}
+
\ No newline at end of file