diff --git a/.gitignore b/.gitignore index 777eaf649..c1aa4a09c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ +builds/ node_modules/ tmp/ 4chan_x.user.js Cakefile script.coffee *~ -*.db \ No newline at end of file +*.db diff --git a/4chan_x.meta.js b/4chan-X.meta.js similarity index 97% rename from 4chan_x.meta.js rename to 4chan-X.meta.js index e0f534ab9..bb417e306 100644 --- a/4chan_x.meta.js +++ b/4chan-X.meta.js @@ -6,16 +6,16 @@ // @copyright 2009-2011 James Campos // @copyright 2012-2013 Nicolas Stepien // @license MIT; http://en.wikipedia.org/wiki/Mit_license +// @match *://api.4chan.org/* // @match *://boards.4chan.org/* // @match *://images.4chan.org/* // @match *://sys.4chan.org/* -// @match *://api.4chan.org/* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_openInTab // @run-at document-start -// @updateURL https://github.com/MayhemYDG/4chan-x/raw/v3/4chan_x.meta.js -// @downloadURL https://github.com/MayhemYDG/4chan-x/raw/v3/4chan_x.user.js +// @updateURL https://github.com/MayhemYDG/4chan-x/raw/v3/4chan-X.meta.js +// @downloadURL https://github.com/MayhemYDG/4chan-x/raw/v3/4chan-X.user.js // @icon data:image/gif;base64,R0lGODlhEAAQAKECAAAAAGbMM////////yH5BAEKAAIALAAAAAAQABAAAAIxlI+pq+D9DAgUoFkPDlbs7lGiI2bSVnKglnJMOL6omczxVZK3dH/41AG6Lh7i6qUoAAA7 // ==/UserScript== diff --git a/4chan_x.user.js b/4chan-X.user.js similarity index 99% rename from 4chan_x.user.js rename to 4chan-X.user.js index 4acbcf453..08ee9792a 100644 --- a/4chan_x.user.js +++ b/4chan-X.user.js @@ -6,21 +6,21 @@ // @copyright 2009-2011 James Campos // @copyright 2012-2013 Nicolas Stepien // @license MIT; http://en.wikipedia.org/wiki/Mit_license +// @match *://api.4chan.org/* // @match *://boards.4chan.org/* // @match *://images.4chan.org/* // @match *://sys.4chan.org/* -// @match *://api.4chan.org/* // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_openInTab // @run-at document-start -// @updateURL https://github.com/MayhemYDG/4chan-x/raw/v3/4chan_x.meta.js -// @downloadURL https://github.com/MayhemYDG/4chan-x/raw/v3/4chan_x.user.js +// @updateURL https://github.com/MayhemYDG/4chan-x/raw/v3/4chan-X.meta.js +// @downloadURL https://github.com/MayhemYDG/4chan-x/raw/v3/4chan-X.user.js // @icon data:image/gif;base64,R0lGODlhEAAQAKECAAAAAGbMM////////yH5BAEKAAIALAAAAAAQABAAAAIxlI+pq+D9DAgUoFkPDlbs7lGiI2bSVnKglnJMOL6omczxVZK3dH/41AG6Lh7i6qUoAAA7 // ==/UserScript== -/* 4chan X Beta - Version 3.0.0 - 2013-03-17 +/* 4chan X Beta - Version 3.0.0 - 2013-03-18 * http://mayhemydg.github.com/4chan-x/ * * Copyright (c) 2009-2011 James Campos @@ -213,7 +213,7 @@ g = { VERSION: '3.0.0', - NAMESPACE: "4chan X Beta.", + NAMESPACE: '4chan X Beta.', boards: {}, threads: {}, posts: {} @@ -221,8 +221,10 @@ UI = (function() { var Menu, dialog, drag, dragend, dragstart, hover, hoverend, hoverstart, touchend, touchmove; + dialog = function(id, position, html) { var el, move; + el = d.createElement('div'); el.className = 'dialog'; el.innerHTML = html; @@ -249,6 +251,7 @@ Menu.prototype.makeMenu = function() { var menu; + menu = $.el('div', { className: 'dialog', id: 'menu', @@ -263,6 +266,7 @@ Menu.prototype.toggle = function(e, button, data) { var previousButton; + e.preventDefault(); e.stopPropagation(); if (currentMenu) { @@ -280,6 +284,7 @@ Menu.prototype.open = function(button, data) { var bLeft, bRect, bTop, bottom, cHeight, cWidth, entry, left, mRect, menu, prevEntry, right, style, top, _i, _len, _ref, _ref1, _ref2; + menu = this.makeMenu(); currentMenu = menu; lastToggledButton = button; @@ -314,6 +319,7 @@ Menu.prototype.insertEntry = function(entry, parent, data) { var subEntry, submenu, _i, _len, _ref; + if (typeof entry.open === 'function') { if (!entry.open(data)) { return; @@ -347,6 +353,7 @@ Menu.prototype.findNextEntry = function(entry, direction) { var entries; + entries = __slice.call(entry.parentNode.children); entries.sort(function(first, second) { return +(first.style.order || first.style.webkitOrder) - +(second.style.order || second.style.webkitOrder); @@ -356,6 +363,7 @@ Menu.prototype.keybinds = function(e) { var entry, next, nextPrev, subEntry, submenu; + entry = $('.focused', currentMenu); while (subEntry = $('.focused', entry)) { entry = subEntry; @@ -401,6 +409,7 @@ Menu.prototype.focus = function(entry) { var bottom, cHeight, cWidth, eRect, focused, left, right, sRect, style, submenu, top, _i, _len, _ref; + while (focused = $.x('parent::*/child::*[contains(@class,"focused")]', entry)) { $.rmClass(focused, 'focused'); } @@ -440,6 +449,7 @@ Menu.prototype.addEntry = function(e) { var entry; + entry = e.detail; if (entry.type !== this.type) { return; @@ -450,6 +460,7 @@ Menu.prototype.parseEntry = function(entry) { var el, style, subEntries, subEntry, _i, _len; + el = entry.el, subEntries = entry.subEntries; $.addClass(el, 'entry'); $.on(el, 'focus mouseover', (function(e) { @@ -473,6 +484,7 @@ })(); dragstart = function(e) { var el, isTouching, o, rect, screenHeight, screenWidth; + if (e.type === 'mousedown' && e.button !== 0) { return; } @@ -511,6 +523,7 @@ }; touchmove = function(e) { var touch, _i, _len, _ref; + _ref = e.changedTouches; for (_i = 0, _len = _ref.length; _i < _len; _i++) { touch = _ref[_i]; @@ -522,6 +535,7 @@ }; drag = function(e) { var bottom, clientX, clientY, left, right, style, top; + clientX = e.clientX, clientY = e.clientY; left = clientX - this.dx; left = left < 10 ? 0 : this.width - left < 10 ? null : left / this.screenWidth * 100 + '%'; @@ -537,6 +551,7 @@ }; touchend = function(e) { var touch, _i, _len, _ref; + _ref = e.changedTouches; for (_i = 0, _len = _ref.length; _i < _len; _i++) { touch = _ref[_i]; @@ -559,6 +574,7 @@ }; hoverstart = function(_arg) { var asap, asapTest, cb, el, endEvents, event, latestEvent, o, root, _i, _len, _ref; + root = _arg.root, el = _arg.el, latestEvent = _arg.latestEvent, endEvents = _arg.endEvents, asapTest = _arg.asapTest, cb = _arg.cb; o = { root: root, @@ -589,6 +605,7 @@ }; hover = function(e) { var clientX, clientY, height, left, right, style, top; + this.latestEvent = e; height = this.el.offsetHeight; clientX = e.clientX, clientY = e.clientY; @@ -608,6 +625,7 @@ }; hoverend = function() { var event, _i, _len, _ref; + this.el.parentNode.removeChild(this.el); _ref = this.endEvents; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -643,6 +661,7 @@ $.extend = function(object, properties) { var key, val; + for (key in properties) { val = properties[key]; object[key] = val; @@ -652,6 +671,7 @@ $.extend(Array.prototype, { add: function(object, position) { var keep; + keep = this.slice(position); this.length = position; this.push(object); @@ -662,6 +682,7 @@ }, indexOf: function(object) { var i; + i = this.length; while (i--) { if (this[i] === object) { @@ -672,6 +693,7 @@ }, pushArrays: function() { var arg, args, _i, _len; + args = arguments; for (_i = 0, _len = args.length; _i < _len; _i++) { arg = args[_i]; @@ -681,6 +703,7 @@ }, remove: function(object) { var index; + if ((index = this.indexOf(object)) > -1) { return this.splice(index, 1); } else { @@ -707,6 +730,7 @@ }, ready: function(fc) { var cb, _ref; + if ((_ref = d.readyState) === 'interactive' || _ref === 'complete') { $.queueTask(fc); return; @@ -727,6 +751,7 @@ }, formData: function(form) { var fd, key, val; + if (form instanceof HTMLFormElement) { return new FormData(form); } @@ -746,6 +771,7 @@ }, ajax: function(url, callbacks, opts) { var form, headers, key, r, type, upCallbacks, val; + if (opts == null) { opts = {}; } @@ -765,9 +791,11 @@ }, cache: (function() { var reqs; + reqs = {}; return function(url, cb) { var req; + if (req = reqs[url]) { if (req.readyState === 4) { cb.call(req); @@ -779,6 +807,7 @@ req = $.ajax(url, { onload: function() { var _i, _len, _ref; + _ref = this.callbacks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { cb = _ref[_i]; @@ -816,6 +845,7 @@ }, addStyle: function(css) { var style; + style = $.el('style', { textContent: css }); @@ -857,6 +887,7 @@ }, nodes: function(nodes) { var frag, node, _i, _len; + if (!(nodes instanceof Array)) { return nodes; } @@ -884,6 +915,7 @@ }, el: function(tag, properties) { var el; + el = d.createElement(tag); if (properties) { $.extend(el, properties); @@ -892,6 +924,7 @@ }, on: function(el, events, handler) { var event, _i, _len, _ref; + _ref = events.split(' '); for (_i = 0, _len = _ref.length; _i < _len; _i++) { event = _ref[_i]; @@ -900,6 +933,7 @@ }, off: function(el, events, handler) { var event, _i, _len, _ref; + _ref = events.split(' '); for (_i = 0, _len = _ref.length; _i < _len; _i++) { event = _ref[_i]; @@ -919,6 +953,7 @@ if (typeof GM_openInTab !== "undefined" && GM_openInTab !== null) { return function(URL) { var a; + a = $.el('a', { href: URL }); @@ -932,6 +967,7 @@ })(), debounce: function(wait, fn) { var args, exec, that, timeout; + timeout = null; that = null; args = null; @@ -952,9 +988,11 @@ }, queueTask: (function() { var execTask, taskChannel, taskQueue; + taskQueue = []; execTask = function() { var args, func, task; + task = taskQueue.shift(); func = task[0]; args = Array.prototype.slice.call(task, 1); @@ -976,20 +1014,23 @@ })(), globalEval: function(code) { var script; + script = $.el('script', { textContent: code }); $.add(d.head, script); return $.rm(script); }, - unsafeWindow: window.opera ? window : unsafeWindow !== window ? unsafeWindow : (function() { + unsafeWindow: window.opera ? window : typeof unsafeWindow !== "undefined" && unsafeWindow !== null ? unsafeWindow : (function() { var p; + p = d.createElement('p'); p.setAttribute('onclick', 'return window'); return p.onclick(); })(), bytesToString: function(size) { var unit; + unit = 0; while (size >= 1024) { size /= 1024; @@ -1006,6 +1047,7 @@ }; $.get = function(name, defaultValue) { var value; + if (value = GM_getValue(g.NAMESPACE + name)) { return JSON.parse(value); } else { @@ -1021,12 +1063,14 @@ } else if (window.opera) { (function() { var scriptStorage; + scriptStorage = opera.scriptStorage; $["delete"] = function(name) { return delete scriptStorage[g.NAMESPACE + name]; }; $.get = function(name, defaultValue) { var value; + if (value = scriptStorage[g.NAMESPACE + name]) { return JSON.parse(value); } else { @@ -1046,6 +1090,7 @@ }; $.get = function(name, defaultValue) { var value; + if (value = localStorage.getItem(g.NAMESPACE + name)) { return JSON.parse(value); } else { @@ -1063,6 +1108,7 @@ }, visibility: function() { var event, prefix, property; + if ('visibilityState' in document) { return; } @@ -1106,6 +1152,7 @@ }, setBoardList: function() { var a, btn, customBoardList, fullBoardList, nav; + Header.nav = nav = $.id('boardNavDesktop'); if (a = $("a[href*='/" + g.BOARD + "/']", nav)) { a.className = 'current'; @@ -1136,6 +1183,7 @@ }, generateBoardList: function(text) { var as, list, nodes; + list = $('#custom-board-list', Header.nav); list.innerHTML = null; if (!text) { @@ -1144,6 +1192,7 @@ as = $$('#full-board-list a', Header.nav); nodes = text.match(/[\w@]+(-(all|title|full|text:"[^"]+"))?|[^\w@]+/g).map(function(t) { var a, board, m, _i, _len; + if (/^[^\w@]/.test(t)) { return $.tn(t); } @@ -1179,6 +1228,7 @@ }, toggleBoardList: function() { var custom, full, nav, showBoardList; + nav = Header.nav; custom = $('#custom-board-list', nav); full = $('#full-board-list', nav); @@ -1188,6 +1238,7 @@ }, addShortcut: function(el) { var shortcut; + shortcut = $.el('span', { className: 'shortcut' }); @@ -1247,6 +1298,7 @@ CatalogLinks = { init: function() { var el; + if (!Conf['Catalog Links']) { return; } @@ -1274,6 +1326,7 @@ }, toggle: function() { var a, board, useCatalog, _i, _len, _ref; + $.set('Header catalog links', useCatalog = this.className === 'disabled'); $.toggleClass(this, 'disabled'); _ref = $$('a', $.id('boardNavDesktop')); @@ -1300,6 +1353,7 @@ Settings = { init: function() { var link, settings; + link = $.el('a', { className: 'settings-link', textContent: '4chan X Beta Settings', @@ -1343,6 +1397,7 @@ }, open: function(openSection) { var html, link, links, overlay, section, sectionToOpen, _i, _len, _ref; + if (Settings.dialog) { return; } @@ -1394,6 +1449,7 @@ sections: [], addSection: function(title, open) { var _ref; + if (typeof title !== 'string') { _ref = title.detail, title = _ref.title, open = _ref.open; } @@ -1404,6 +1460,7 @@ }, openSection: function() { var section; + section = $('section', Settings.dialog); section.innerHTML = null; section.className = "section-" + (this.title.toLowerCase().replace(/\s+/g, '-')); @@ -1412,6 +1469,7 @@ }, main: function(section) { var ID, arr, checked, description, div, fs, hiddenNum, key, obj, post, thread, _ref, _ref1, _ref2; + section.innerHTML = "
\n \n \n \n
\n

"; $.on($('.export', section), 'click', Settings["export"]); $.on($('.import', section), 'click', Settings["import"]); @@ -1460,6 +1518,7 @@ }, "export": function() { var a, data, now, output; + now = Date.now(); data = { version: g.VERSION, @@ -1487,6 +1546,7 @@ }, onImport: function() { var file, output, reader; + if (!(file = this.files[0])) { return; } @@ -1497,14 +1557,16 @@ } reader = new FileReader(); reader.onload = function(e) { - var data; + var data, err; + try { data = JSON.parse(decodeURIComponent(escape(e.target.result))); Settings.loadSettings(data); if (confirm('Import successful. Refresh now?')) { return window.location.reload(); } - } catch (err) { + } catch (_error) { + err = _error; output.textContent = 'Import failed due to an error.'; return c.log(err.stack); } @@ -1513,6 +1575,7 @@ }, loadSettings: function(data) { var key, val, version, _ref, _ref1; + version = data.version.split('.'); if (version[0] === '2') { data = Settings.convertSettings(data, { @@ -1597,6 +1660,7 @@ }, convertSettings: function(data, map) { var newKey, prevKey; + for (prevKey in map) { newKey = map[prevKey]; if (newKey) { @@ -1608,6 +1672,7 @@ }, filter: function(section) { var select; + section.innerHTML = "\n
"; select = $('select', section); $.on(select, 'change', Settings.selectFilter); @@ -1615,6 +1680,7 @@ }, selectFilter: function() { var div, name, ta; + div = this.nextElementSibling; if ((name = this.value) !== 'guide') { div.innerHTML = null; @@ -1632,6 +1698,7 @@ }, sauce: function(section) { var sauce; + section.innerHTML = "
Sauce is disabled.
\n
Lines starting with a # will be ignored.
\n
You can specify a display text by appending ;text:[text] to the URL.
\n
    These parameters will be replaced by their corresponding values:\n
  • %TURL: Thumbnail URL.
  • \n
  • %URL: Full image URL.
  • \n
  • %MD5: MD5 hash.
  • \n
  • %board: Current board.
  • \n
\n"; sauce = $('textarea', section); sauce.value = $.get('sauces', Conf['sauces']); @@ -1639,6 +1706,7 @@ }, rice: function(section) { var event, input, name, _i, _len, _ref; + section.innerHTML = "
\n Custom Board Navigation is disabled.\n
\n
In the following, board can translate to a board ID (a, b, etc...), the current board (current), or the Status/Twitter link (status, @).
\n
Board link: board
\n
Title link: board-title
\n
Full text link: board-full
\n
Custom text link: board-text:\"VIP Board\"
\n
Full board list toggle: toggle-all
\n
\n\n
\n Time Formatting is disabled.\n
:
\n \n
Day: %a, %A, %d, %e
\n
Month: %m, %b, %B
\n
Year: %y
\n
Hour: %k, %H, %l, %I, %p, %P
\n
Minute: %M
\n
Second: %S
\n
\n\n
\n Quote Backlinks formatting is disabled.\n
:
\n
\n\n
\n File Info Formatting is disabled.\n
:
\n
Link: %l (truncated), %L (untruncated), %T (Unix timestamp)
\n
Original file name: %n (truncated), %N (untruncated), %t (Unix timestamp)
\n
Spoiler indicator: %p
\n
Size: %B (Bytes), %K (KB), %M (MB), %s (4chan default)
\n
Resolution: %r (Displays 'PDF' for PDF files)
\n
\n\n
\n Unread Tab Icon is disabled.\n \n \n
\n\n
\n Custom CSS is disabled.\n \n \n
"; _ref = ['boardnav', 'time', 'backlink', 'fileInfo', 'favicon', 'usercss']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -1659,6 +1727,7 @@ }, time: function() { var funk; + funk = Time.createFunc(this.value); return this.nextElementSibling.textContent = funk(Time, new Date()); }, @@ -1667,6 +1736,7 @@ }, fileInfo: function() { var data, funk; + data = { isReply: true, file: { @@ -1698,6 +1768,7 @@ }, keybinds: function(section) { var arr, input, key, tbody, tr, _ref; + section.innerHTML = "
Keybinds are disabled.
\n
Allowed keys: a-z, 0-9, Ctrl, Shift, Alt, Meta, Enter, Esc, Up, Down, Right, Left.
\n
Press Backspace to disable a keybind.
\n\n \n
ActionsKeybinds
"; tbody = $('tbody', section); _ref = Config.hotkeys; @@ -1716,6 +1787,7 @@ }, keybind: function(e) { var key; + if (e.keyCode === 9) { return; } @@ -1732,6 +1804,7 @@ Fourchan = { init: function() { var board; + if (g.VIEW === 'catalog') { return; } @@ -1751,6 +1824,7 @@ }, code: function() { var pre, _i, _len, _ref; + if (this.isClone) { return; } @@ -1762,6 +1836,7 @@ }, math: function() { var jsMath; + if (this.isClone || !$('.math', this.nodes.comment)) { return; } @@ -1810,7 +1885,8 @@ Filter = { filters: {}, init: function() { - var boards, filter, hl, key, op, regexp, stub, top, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4; + var boards, err, filter, hl, key, op, regexp, stub, top, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4; + if (g.VIEW === 'catalog' || !Conf['Filter']) { return; } @@ -1835,7 +1911,8 @@ } else { try { regexp = RegExp(regexp[1], regexp[2]); - } catch (err) { + } catch (_error) { + err = _error; new Notification('warning', err.message, 60); continue; } @@ -1843,6 +1920,7 @@ op = ((_ref2 = filter.match(/[^t]op:(yes|no|only)/)) != null ? _ref2[1] : void 0) || 'yes'; stub = (function() { var _ref3; + switch ((_ref3 = filter.match(/stub:(yes|no)/)) != null ? _ref3[1] : void 0) { case 'yes': return true; @@ -1873,6 +1951,7 @@ }, createFilter: function(regexp, op, stub, hl, top) { var settings, test; + test = typeof regexp === 'string' ? function(value) { return regexp === value; } : function(value) { @@ -1896,6 +1975,7 @@ }, node: function() { var filter, firstThread, key, result, thisThread, value, _i, _len, _ref; + if (this.isClone) { return; } @@ -2007,6 +2087,7 @@ menu: { init: function() { var div, entry, type, _i, _len, _ref; + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Filter']) { return; } @@ -2032,6 +2113,7 @@ }, createSubEntry: function(text, type) { var el; + el = $.el('a', { href: 'javascript:;', textContent: text @@ -2042,6 +2124,7 @@ el: el, open: function(post) { var value; + value = Filter[type](post); return value !== false; } @@ -2049,6 +2132,7 @@ }, makeFilter: function() { var re, save, section, select, ta, tl, type, value; + type = this.dataset.type; value = Filter[type](Filter.menu.post); re = ['uniqueID', 'MD5'].contains(type) ? value : value.replace(/\/|\\|\^|\$|\n|\.|\(|\)|\{|\}|\[|\]|\?|\*|\+|\|/g, function(c) { @@ -2095,6 +2179,7 @@ }, node: function() { var data; + if (data = ThreadHiding.hiddenThreads.threads[this]) { ThreadHiding.hide(this, data.makeStub); } @@ -2110,6 +2195,7 @@ }, syncFromCatalog: function() { var hiddenThreadsOnCatalog, threadID, threads; + hiddenThreadsOnCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {}; threads = ThreadHiding.hiddenThreads.threads; for (threadID in hiddenThreadsOnCatalog) { @@ -2133,6 +2219,7 @@ menu: { init: function() { var apply, div, makeStub; + if (g.VIEW !== 'index' || !Conf['Menu'] || !Conf['Thread Hiding']) { return; } @@ -2154,6 +2241,7 @@ order: 20, open: function(_arg) { var isReply, thread; + thread = _arg.thread, isReply = _arg.isReply; if (isReply || thread.isHidden) { return false; @@ -2172,6 +2260,7 @@ }, hide: function() { var makeStub, thread; + makeStub = $('input', this.parentNode).checked; thread = ThreadHiding.menu.thread; ThreadHiding.hide(thread, makeStub); @@ -2181,6 +2270,7 @@ }, makeButton: function(thread, type) { var a; + a = $.el('a', { className: "" + type + "-thread-button", innerHTML: "[ " + (type === 'hide' ? '-' : '+') + " ]", @@ -2192,6 +2282,7 @@ }, saveHiddenState: function(thread, makeStub) { var hiddenThreads, hiddenThreadsCatalog; + hiddenThreads = ThreadHiding.getHiddenThreads(); hiddenThreadsCatalog = JSON.parse(localStorage.getItem("4chan-hide-t-" + g.BOARD)) || {}; if (thread.isHidden) { @@ -2219,6 +2310,7 @@ }, hide: function(thread, makeStub) { var OP, a, numReplies, opInfo, span, threadRoot; + if (makeStub == null) { makeStub = Conf['Stubs']; } @@ -2252,6 +2344,7 @@ }, show: function(thread) { var threadRoot; + if (thread.stub) { $.rm(thread.stub); delete thread.stub; @@ -2275,6 +2368,7 @@ }, node: function() { var data, thread; + if (!this.isReply || this.isClone) { return; } @@ -2301,6 +2395,7 @@ menu: { init: function() { var apply, div, makeStub, replies, thisPost; + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Reply Hiding']) { return; } @@ -2366,6 +2461,7 @@ order: 20, open: function(post) { var data, thread; + if (!post.isReply || post.isClone) { return false; } @@ -2391,6 +2487,7 @@ }, hide: function() { var makeStub, parent, post, replies, thisPost; + parent = this.parentNode; thisPost = $('input[name=thisPost]', parent).checked; replies = $('input[name=replies]', parent).checked; @@ -2409,6 +2506,7 @@ }, show: function() { var data, parent, post, replies, thisPost, thread; + parent = this.parentNode; thisPost = $('input[name=thisPost]', parent).checked; replies = $('input[name=replies]', parent).checked; @@ -2431,6 +2529,7 @@ }, makeButton: function(post, type) { var a; + a = $.el('a', { className: "" + type + "-reply-button", innerHTML: "[ " + (type === 'hide' ? '-' : '+') + " ]", @@ -2441,6 +2540,7 @@ }, saveHiddenState: function(post, isHiding, thisPost, makeStub, hideRecursively) { var hiddenPosts, thread; + hiddenPosts = ReplyHiding.getHiddenPosts(); if (isHiding) { if (!(thread = hiddenPosts.threads[post.thread])) { @@ -2462,6 +2562,7 @@ }, toggle: function() { var post; + post = Get.postFromNode(this); if (post.isHidden) { ReplyHiding.show(post); @@ -2472,6 +2573,7 @@ }, hide: function(post, makeStub, hideRecursively) { var a, postInfo, quotelink, _i, _len, _ref; + if (makeStub == null) { makeStub = Conf['Stubs']; } @@ -2509,6 +2611,7 @@ }, show: function(post, showRecursively) { var quotelink, _i, _len, _ref; + if (showRecursively == null) { showRecursively = Conf['Recursive Hiding']; } @@ -2544,6 +2647,7 @@ }, node: function() { var i, obj, quote, recursive, _i, _j, _len, _len1, _ref, _ref1; + if (this.isClone) { return; } @@ -2561,6 +2665,7 @@ }, add: function() { var args, obj, post, recursive, _base, _name; + recursive = arguments[0], post = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; obj = (_base = Recursive.recursives)[_name = post.fullID] || (_base[_name] = { recursives: [], @@ -2571,6 +2676,7 @@ }, rm: function(recursive, post) { var i, obj, rec, _i, _len, _ref; + if (!(obj = Recursive.recursives[post.fullID])) { return; } @@ -2585,6 +2691,7 @@ }, apply: function() { var ID, args, fullID, post, recursive, _ref; + recursive = arguments[0], post = arguments[1], args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; fullID = post.fullID; _ref = g.posts; @@ -2609,6 +2716,7 @@ }, node: function() { var board, postID, quotelink, _i, _len, _ref, _ref1, _ref2; + if (this.isClone) { return; } @@ -2636,6 +2744,7 @@ }, node: function() { var button; + button = Menu.makeButton(this); if (this.isClone) { $.replace($('.menu-button', this.nodes.info), button); @@ -2645,9 +2754,11 @@ }, makeButton: (function() { var a; + a = null; return function(post) { var clone; + a || (a = $.el('a', { className: 'menu-button', innerHTML: '[]', @@ -2664,6 +2775,7 @@ })(), toggle: function(e) { var post; + post = this.dataset.clone ? Get.postFromNode(this) : g.posts[this.dataset.postid]; return Menu.menu.toggle(e, this, post); } @@ -2672,6 +2784,7 @@ ReportLink = { init: function() { var a; + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Report Link']) { return; } @@ -2693,6 +2806,7 @@ }, report: function() { var id, post, set, url; + post = ReportLink.post; url = "//sys.4chan.org/" + post.board + "/imgboard.php?mode=report&no=" + post; id = Date.now(); @@ -2704,6 +2818,7 @@ DeleteLink = { init: function() { var div, fileEl, fileEntry, postEl, postEntry; + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Delete Link'] || !Conf['Quick Reply']) { return; } @@ -2731,6 +2846,7 @@ el: fileEl, open: function(_arg) { var file; + file = _arg.file; if (!file || file.isDead) { return false; @@ -2746,6 +2862,7 @@ order: 40, open: function(post) { var node, seconds, thread; + if (post.isDead || !((thread = QR.yourPosts.threads[post.thread]) && thread.contains(post.ID))) { return false; } @@ -2766,6 +2883,7 @@ }, "delete": function() { var form, link, m, post, pwd; + post = DeleteLink.post; if (DeleteLink.cooldown[post.fullID]) { return; @@ -2793,6 +2911,7 @@ }, load: function(link, html) { var msg, s, tmpDoc; + tmpDoc = d.implementation.createHTMLDocument(''); tmpDoc.documentElement.innerHTML = html; if (tmpDoc.title === '4chan - Banned') { @@ -2812,6 +2931,7 @@ cooldown: { start: function(post) { var length, seconds; + if (post.fullID in DeleteLink.cooldown) { return; } @@ -2821,6 +2941,7 @@ }, count: function(fullID, seconds, length) { var el; + if (!((0 <= seconds && seconds <= length))) { return; } @@ -2845,6 +2966,7 @@ DownloadLink = { init: function() { var a; + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Download Link']) { return; } @@ -2861,6 +2983,7 @@ order: 70, open: function(_arg) { var file; + file = _arg.file; if (!file) { return false; @@ -2876,6 +2999,7 @@ ArchiveLink = { init: function() { var div, entry, type, _i, _len, _ref; + if (g.VIEW === 'catalog' || !Conf['Menu'] || !Conf['Archive Link']) { return; } @@ -2888,6 +3012,7 @@ order: 90, open: function(_arg) { var board, postID, redirect, threadID; + postID = _arg.ID, threadID = _arg.thread, board = _arg.board; redirect = Redirect.to({ postID: postID, @@ -2907,6 +3032,7 @@ }, createSubEntry: function(text, type) { var el, open; + el = $.el('a', { textContent: text, target: '_blank' @@ -2914,6 +3040,7 @@ if (type === 'post') { open = function(_arg) { var board, postID, threadID; + postID = _arg.ID, threadID = _arg.thread, board = _arg.board; el.href = Redirect.to({ postID: postID, @@ -2925,6 +3052,7 @@ } else { open = function(post) { var value; + value = Filter[type](post); if (!value) { return false; @@ -2952,6 +3080,7 @@ } return $.on(d, '4chanXInitFinished', function() { var node, _i, _len, _ref; + $.on(d, 'keydown', Keybinds.keydown); _ref = $$('[accesskey]'); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -2962,6 +3091,7 @@ }, keydown: function(e) { var form, key, notification, notifications, target, thread, threadRoot, _i, _len; + if (!(key = Keybinds.keyCode(e))) { return; } @@ -3097,6 +3227,7 @@ }, keyCode: function(e) { var kc, key; + key = (function() { switch (kc = e.keyCode) { case 8: @@ -3149,6 +3280,7 @@ }, tags: function(tag, ta) { var range, selEnd, selStart, value; + value = ta.value; selStart = ta.selectionStart; selEnd = ta.selectionEnd; @@ -3159,6 +3291,7 @@ }, img: function(thread, all) { var post; + if (all) { return ImageExpand.cb.toggleAll(); } else { @@ -3168,6 +3301,7 @@ }, open: function(thread, tab) { var url; + if (g.VIEW !== 'index') { return; } @@ -3180,6 +3314,7 @@ }, hl: function(delta, thread) { var headRect, next, postEl, rect, replies, reply, root, topMargin, _i, _len; + headRect = Header.bar.getBoundingClientRect(); topMargin = headRect.top + headRect.height; if (postEl = $('.reply.highlight', thread)) { @@ -3229,6 +3364,7 @@ Nav = { init: function() { var next, prev, span; + if (g.VIEW !== 'index' || !Conf['Index Navigation']) { return; } @@ -3258,6 +3394,7 @@ }, getThread: function(full) { var headRect, i, rect, thread, threads, topMargin, _i, _len; + headRect = Header.bar.getBoundingClientRect(); topMargin = headRect.top + headRect.height; threads = $$('.thread:not([hidden])'); @@ -3276,6 +3413,7 @@ }, scroll: function(delta) { var i, rect, thread, threads, top, topMargin, _ref, _ref1; + _ref = Nav.getThread(true), threads = _ref[0], thread = _ref[1], i = _ref[2], rect = _ref[3], topMargin = _ref[4]; top = rect.top - topMargin; if (!((delta === -1 && Math.ceil(top) < 0) || (delta === +1 && top > 1))) { @@ -3342,6 +3480,7 @@ }, to: function(data) { var board, url; + board = data.board; switch ("" + board) { case 'a': @@ -3399,6 +3538,7 @@ }, path: function(base, archiver, data) { var board, path, postID, threadID, type, value; + if (data.isSearch) { board = data.board, type = data.type, value = data.value; type = type === 'name' ? 'username' : type === 'MD5' ? 'image' : type; @@ -3430,6 +3570,7 @@ spoilerRange: {}, shortFilename: function(filename, isReply) { var threshold; + threshold = isReply ? 30 : 40; if (filename.length - 4 > threshold) { return "" + filename.slice(0, threshold - 5) + "(...)." + filename.slice(-3); @@ -3439,6 +3580,7 @@ }, postFromObject: function(data, board) { var o; + o = { postID: data.no, threadID: data.resto || data.no, @@ -3482,6 +3624,7 @@ */ var a, board, capcode, capcodeClass, capcodeStart, closed, comment, container, date, dateUTC, email, emailEnd, emailStart, ext, file, fileDims, fileHTML, fileInfo, fileSize, fileThumb, filename, flag, flagCode, flagName, href, imgSrc, isClosed, isOP, isSticky, name, postID, quote, shortFilename, spoilerRange, staticPath, sticky, subject, threadID, tripcode, uniqueID, userID, _i, _len, _ref; + postID = o.postID, threadID = o.threadID, board = o.board, name = o.name, capcode = o.capcode, tripcode = o.tripcode, uniqueID = o.uniqueID, email = o.email, subject = o.subject, flagCode = o.flagCode, flagName = o.flagName, date = o.date, dateUTC = o.dateUTC, isSticky = o.isSticky, isClosed = o.isClosed, comment = o.comment, file = o.file; isOP = postID === threadID; staticPath = '//static.4chan.org'; @@ -3579,12 +3722,14 @@ Get = { threadExcerpt: function(thread) { var OP, excerpt, _ref; + OP = thread.OP; excerpt = ((_ref = OP.info.subject) != null ? _ref.trim() : void 0) || OP.info.comment.replace(/\n+/g, ' // ') || Conf['Anonymize'] && 'Anonymous' || $('.nameBlock', OP.nodes.info).textContent.trim(); return "/" + thread.board + "/ - " + excerpt; }, postFromRoot: function(root) { var board, index, link, post, postID; + link = $('a[title="Highlight this post"]', root); board = link.pathname.split('/')[1]; postID = link.hash.slice(2); @@ -3604,6 +3749,7 @@ }, postDataFromLink: function(link) { var board, path, postID, threadID; + if (link.hostname === 'boards.4chan.org') { path = link.pathname.split('/'); board = path[1]; @@ -3622,6 +3768,7 @@ }, allQuotelinksLinkingTo: function(post) { var ID, quote, quotedPost, quotelinks, quoterPost, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; + quotelinks = []; _ref = g.posts; for (ID in _ref) { @@ -3650,12 +3797,14 @@ } return quotelinks.filter(function(quotelink) { var board, postID, _ref4; + _ref4 = Get.postDataFromLink(quotelink), board = _ref4.board, postID = _ref4.postID; return board === post.board.ID && postID === post.ID; }); }, postClone: function(board, threadID, postID, root, context) { var post, url; + if (post = g.posts["" + board + "." + postID]) { Get.insert(post, root, context); return; @@ -3673,6 +3822,7 @@ }, insert: function(post, root, context) { var clone, nodes; + if (!root.parentNode) { return; } @@ -3686,6 +3836,7 @@ }, fetchedPost: function(req, board, threadID, postID, root, context) { var post, posts, status, thread, url, _i, _len; + if (post = g.posts["" + board + "." + postID]) { Get.insert(post, root, context); return; @@ -3729,6 +3880,7 @@ }, archivedPost: function(req, board, postID, root, context) { var bq, comment, data, o, post, thread, threadID, _ref; + if (post = g.posts["" + board + "." + postID]) { Get.insert(post, root, context); return; @@ -3823,6 +3975,7 @@ Misc = { clearThreads: function(key) { var data; + if (!(data = $.get(key))) { return; } @@ -3836,6 +3989,7 @@ return $.ajax("//api.4chan.org/" + g.BOARD + "/catalog.json", { onload: function() { var obj, thread, threads, _i, _j, _len, _len1, _ref, _ref1; + threads = {}; _ref = JSON.parse(this.response); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -3875,6 +4029,7 @@ }, node: function() { var ID, a, board, deadlink, m, post, quote, quoteID, redirect, _i, _len, _ref, _ref1; + if (this.isClone) { return; } @@ -3956,6 +4111,7 @@ }, node: function() { var link, _i, _j, _len, _len1, _ref, _ref1; + _ref = this.nodes.quotelinks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { link = _ref[_i]; @@ -3969,6 +4125,7 @@ }, toggle: function(e) { var board, context, postID, threadID, _ref; + if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { return; } @@ -3994,6 +4151,7 @@ }, add: function(quotelink, board, threadID, postID, context) { var i, inline, isBacklink, post; + isBacklink = $.hasClass(quotelink, 'backlink'); inline = $.el('div', { id: "i" + postID, @@ -4015,6 +4173,7 @@ }, rm: function(quotelink, board, threadID, postID, context) { var el, inlined, isBacklink, post, root, _ref; + isBacklink = $.hasClass(quotelink, 'backlink'); root = QuoteInline.findRoot(quotelink, isBacklink); root = $.x("following-sibling::div[@id='i" + postID + "'][1]", root); @@ -4051,6 +4210,7 @@ }, node: function() { var link, _i, _j, _len, _len1, _ref, _ref1; + _ref = this.nodes.quotelinks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { link = _ref[_i]; @@ -4064,6 +4224,7 @@ }, mouseover: function(e) { var board, clone, origin, post, postID, posts, qp, quote, quoterID, threadID, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2; + if ($.hasClass(this, 'inlined')) { return; } @@ -4114,6 +4275,7 @@ }, mouseout: function() { var clone, post, root, _i, _len, _ref; + if (!(root = this.el.firstElementChild)) { return; } @@ -4134,6 +4296,7 @@ QuoteBacklink = { init: function() { var format; + if (g.VIEW === 'catalog' || !Conf['Quote Backlinks']) { return; } @@ -4151,6 +4314,7 @@ }, firstNode: function() { var a, clone, container, containers, link, post, quote, _i, _j, _k, _len, _len1, _len2, _ref, _ref1; + if (this.isClone || !this.quotes.length) { return; } @@ -4185,6 +4349,7 @@ }, secondNode: function() { var container; + if (this.isClone && (this.origin.isReply || Conf['OP Backlinks'])) { this.nodes.backlinkContainer = $('.container', this.nodes.info); return; @@ -4198,6 +4363,7 @@ }, getContainer: function(id) { var _base; + return (_base = this.containers)[id] || (_base[id] = $.el('span', { className: 'container' })); @@ -4217,6 +4383,7 @@ }, node: function() { var postID, quotelink, quotelinks, quotes, thread, threadID, _i, _len, _ref; + if (this.isClone) { return; } @@ -4250,6 +4417,7 @@ }, node: function() { var board, op, postID, quotelink, quotelinks, quotes, _i, _j, _len, _len1, _ref; + if (this.isClone && this.thread === this.context.thread) { return; } @@ -4293,6 +4461,7 @@ }, node: function() { var board, data, quotelink, quotelinks, quotes, thread, _i, _len, _ref; + if (this.isClone && this.thread === this.context.thread) { return; } @@ -4329,6 +4498,7 @@ }, node: function() { var email, name, tripcode, _ref; + if (this.info.capcode || this.isClone) { return; } @@ -4370,6 +4540,7 @@ }, createFunc: function(format) { var code; + code = format.replace(/%([A-Za-z])/g, function(s, c) { if (c in Time.formatters) { return "' + Time.formatters." + c + ".call(date) + '"; @@ -4463,6 +4634,7 @@ }, node: function() { var dateEl; + if (this.isClone) { return; } @@ -4472,6 +4644,7 @@ }, relative: function(diff, now, date) { var days, months, number, rounded, unit, years; + unit = (number = diff / $.DAY) >= 1 ? (years = now.getYear() - date.getYear(), months = now.getMonth() - date.getMonth(), days = now.getDate() - date.getDate(), years > 1 ? (number = years - (months < 0 || months === 0 && days < 0), 'year') : years === 1 && (months > 0 || months === 0 && days >= 0) ? (number = years, 'year') : (months = (months + 12) % 12) > 1 ? (number = months - (days < 0), 'month') : months === 1 && days >= 0 ? (number = months, 'month') : 'day') : (number = diff / $.HOUR) >= 1 ? 'hour' : (number = diff / $.MINUTE) >= 1 ? 'minute' : (number = Math.max(0, diff) / $.SECOND, 'second'); rounded = Math.round(number); if (rounded !== 1) { @@ -4482,6 +4655,7 @@ stale: [], flush: function() { var now, update, _i, _len, _ref; + if (d.hidden) { return; } @@ -4497,13 +4671,16 @@ }, setUpdate: function(post) { var markStale, setOwnTimeout, update; + setOwnTimeout = function(diff) { var delay; + delay = diff < $.MINUTE ? $.SECOND - (diff + $.SECOND / 2) % $.SECOND : diff < $.HOUR ? $.MINUTE - (diff + $.MINUTE / 2) % $.MINUTE : diff < $.DAY ? $.HOUR - (diff + $.HOUR / 2) % $.HOUR : $.DAY - (diff + $.DAY / 2) % $.DAY; return setTimeout(markStale, delay); }; update = function(now) { var date, diff, relative, singlePost, _i, _len, _ref; + date = post.info.date; diff = now - date; relative = RelativeDates.relative(diff, now, date); @@ -4540,6 +4717,7 @@ }, createFunc: function(format) { var code; + code = format.replace(/%(.)/g, function(s, c) { if (c in FileInfo.formatters) { return "' + FileInfo.formatters." + c + ".call(post) + '"; @@ -4551,6 +4729,7 @@ }, convertUnit: function(size, unit) { var i; + if (unit === 'B') { return "" + (size.toFixed()) + " Bytes"; } @@ -4581,6 +4760,7 @@ }, n: function() { var fullname, shortname; + fullname = this.file.name; shortname = Build.shortFilename(this.file.name, this.isReply); if (fullname === shortname) { @@ -4624,6 +4804,7 @@ Sauce = { init: function() { var link, links, _i, _len, _ref; + if (g.VIEW === 'catalog' || !Conf['Sauce']) { return; } @@ -4650,6 +4831,7 @@ }, createSauceLink: function(link) { var m, text; + link = link.replace(/%(T?URL|MD5|board)/ig, function(parameter) { switch (parameter) { case '%TURL': @@ -4673,6 +4855,7 @@ }, node: function() { var link, nodes, _i, _len, _ref; + if (this.isClone || !this.file) { return; } @@ -4689,6 +4872,7 @@ ImageExpand = { init: function() { var config, input, label, type, wrapper, _ref; + if (g.VIEW === 'catalog' || !Conf['Image Expansion']) { return; } @@ -4750,6 +4934,7 @@ }, toggleAll: function() { var ID, file, func, post, _i, _len, _ref, _ref1; + $.event('CloseMenu'); if (ImageExpand.on = $.hasClass(ImageExpand.EAI, 'expand-all-shortcut')) { ImageExpand.EAI.className = 'contract-all-shortcut'; @@ -4779,6 +4964,7 @@ }, setFitness: function() { var checked; + checked = this.checked; (checked ? $.addClass : $.rmClass)(doc, this.name.toLowerCase().replace(/\s+/g, '-')); if (this.name !== 'Fit height') { @@ -4797,6 +4983,7 @@ }, toggle: function(post) { var headRect, postRect, rect, root, thumb, top; + thumb = post.file.thumb; if (!(post.file.isExpanded || $.hasClass(thumb, 'expanding'))) { ImageExpand.expand(post); @@ -4824,6 +5011,7 @@ }, expand: function(post, src) { var img, thumb; + thumb = post.file.thumb; if (post.isHidden || post.file.isExpanded || $.hasClass(thumb, 'expanding')) { return; @@ -4851,6 +5039,7 @@ }, completeExpand: function(post) { var rect, root, thumb; + thumb = post.file.thumb; if (!$.hasClass(thumb, 'expanding')) { return; @@ -4866,6 +5055,7 @@ }, error: function() { var URL, post, src, timeoutID; + post = Get.postFromNode(this); $.rm(this); delete post.file.fullImage; @@ -4887,6 +5077,7 @@ return $.ajax("//api.4chan.org/" + post.board + "/res/" + post.thread + ".json", { onload: function() { var postObj, _i, _len, _ref; + if (this.status !== 200) { return; } @@ -4927,6 +5118,7 @@ }, node: function() { var thumb, _ref; + if (this.isClone || !((_ref = this.file) != null ? _ref.isSpoiler : void 0)) { return; } @@ -4948,6 +5140,7 @@ }, node: function() { var URL, img, style, thumb, type, _ref, _ref1; + if (this.isClone || this.isHidden || this.thread.isHidden || !((_ref = this.file) != null ? _ref.isImage : void 0)) { return; } @@ -4979,6 +5172,7 @@ }, node: function() { var _ref; + if (!((_ref = this.file) != null ? _ref.isImage : void 0)) { return; } @@ -4986,6 +5180,7 @@ }, mouseover: function(e) { var el, post; + post = Get.postFromNode(this); el = $.el('img', { id: 'ihover', @@ -5007,6 +5202,7 @@ error: function() { var URL, post, src, timeoutID, _this = this; + if (!doc.contains(this)) { return; } @@ -5027,6 +5223,7 @@ return $.ajax("//api.4chan.org/" + post.board + "/res/" + post.thread + ".json", { onload: function() { var postObj, _i, _len, _ref; + if (this.status !== 200) { return; } @@ -5067,6 +5264,7 @@ }, node: function() { var a; + if (a = $('.abbr > a', this.nodes.comment)) { return $.on(a, 'click', ExpandComment.cb); } @@ -5074,12 +5272,14 @@ callbacks: [], cb: function(e) { var post; + e.preventDefault(); post = Get.postFromNode(this); return ExpandComment.expand(post); }, expand: function(post) { var a; + if (post.nodes.longComment) { $.replace(post.nodes.shortComment, post.nodes.longComment); post.nodes.comment = post.nodes.longComment; @@ -5095,6 +5295,7 @@ }, contract: function(post) { var a; + if (!post.nodes.shortComment) { return; } @@ -5105,6 +5306,7 @@ }, parse: function(req, a, post) { var callback, clone, comment, href, postObj, posts, quote, spoilerRange, status, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _results; + status = req.status; if (![200, 304].contains(status)) { a.textContent = "Error " + req.statusText + " (" + status + ")"; @@ -5163,6 +5365,7 @@ }, node: function() { var a, span; + if (!(span = $('.summary', this.OP.nodes.root.parentNode))) { return; } @@ -5176,11 +5379,13 @@ }, cbToggle: function() { var op; + op = Get.postFromRoot(this.previousElementSibling); return ExpandThread.toggle(op.thread); }, toggle: function(thread) { var a, inlined, num, post, replies, reply, text, threadRoot, url, _i, _j, _k, _len, _len1, _len2, _ref, _ref1; + threadRoot = thread.OP.nodes.root.parentNode; url = "//api.4chan.org/" + thread.board + "/res/" + thread + ".json"; a = $('.summary', threadRoot); @@ -5237,6 +5442,7 @@ }, parse: function(req, thread, a) { var link, node, nodes, post, posts, replies, reply, spoilerRange, status, _i, _len; + if (a.textContent[0] === '+') { return; } @@ -5309,6 +5515,7 @@ }, node: function() { var ID, post, posts, _ref; + Unread.thread = this; Unread.lastReadPost = $.get("lastReadPosts." + this.board, { threads: {} @@ -5338,6 +5545,7 @@ }, addPosts: function(newPosts) { var ID, post, youInThisThread, yourPosts, _i, _len; + if (Conf['Quick Reply']) { yourPosts = QR.yourPosts; youInThisThread = yourPosts.threads[Unread.thread]; @@ -5361,6 +5569,7 @@ }, addPostQuotingYou: function(post, yourPosts) { var board, postIDs, quote, quoteID, thread, _i, _len, _ref, _ref1, _ref2; + _ref = post.quotes; for (_i = 0, _len = _ref.length; _i < _len; _i++) { quote = _ref[_i]; @@ -5387,6 +5596,7 @@ }, read: function(e) { var bottom, height, i, post, _i, _j, _len, _len1, _ref, _ref1; + if (d.hidden || !Unread.posts.length) { return; } @@ -5419,6 +5629,7 @@ }, saveLastReadPost: $.debounce($.SECOND, function() { var lastReadPosts; + lastReadPosts = $.get("lastReadPosts." + Unread.thread.board, { threads: {} }); @@ -5427,6 +5638,7 @@ }), setLine: function(force) { var post, root; + if (!(d.hidden || force === true)) { return; } @@ -5441,6 +5653,7 @@ }, update: function() { var count; + count = Unread.posts.length; if (Conf['Unread Count']) { d.title = g.DEAD ? "(" + Unread.posts.length + ") /" + g.BOARD + "/ - 404" : "(" + Unread.posts.length + ") " + Unread.title; @@ -5457,6 +5670,7 @@ init: function() { return $.ready(function() { var href; + Favicon.el = $('link[rel="shortcut icon"]', d.head); Favicon.el.type = 'image/x-icon'; href = Favicon.el.href; @@ -5526,6 +5740,7 @@ }, node: function() { var ID, fileCount, post, postCount, _ref; + postCount = 0; fileCount = 0; _ref = this.posts; @@ -5543,6 +5758,7 @@ }, onUpdate: function(e) { var fileCount, fileLimit, postCount, postLimit, _ref; + if (e.detail[404]) { return; } @@ -5560,6 +5776,7 @@ ThreadUpdater = { init: function() { var checked, conf, html, name, _ref; + if (g.VIEW !== 'thread' || !Conf['Thread Updater']) { return; } @@ -5583,6 +5800,7 @@ }, node: function() { var input, _i, _len, _ref; + ThreadUpdater.thread = this; ThreadUpdater.root = this.OP.nodes.root.parentNode; ThreadUpdater.lastPost = +ThreadUpdater.root.lastElementChild.id.match(/\d+/)[0]; @@ -5680,12 +5898,14 @@ }, interval: function() { var val; + val = parseInt(this.value, 10); ThreadUpdater.interval = this.value = val; return $.cb.value.call(this); }, load: function() { var klass, req, text, _ref; + req = ThreadUpdater.req; switch (req.status) { case 200: @@ -5725,6 +5945,7 @@ }, getInterval: function() { var i, j; + i = ThreadUpdater.interval; j = Math.min(ThreadUpdater.outdateCount, 10); if (!d.hidden) { @@ -5734,6 +5955,7 @@ }, set: function(name, text, klass) { var el, node; + el = ThreadUpdater[name]; if (node = el.firstChild) { node.data = text; @@ -5746,6 +5968,7 @@ }, timeout: function() { var n; + ThreadUpdater.timeoutID = setTimeout(ThreadUpdater.timeout, 1000); if (!(n = --ThreadUpdater.seconds)) { return ThreadUpdater.update(); @@ -5758,6 +5981,7 @@ }, update: function() { var url; + if (!ThreadUpdater.online) { return; } @@ -5778,6 +6002,7 @@ }, updateThreadStatus: function(title, OP) { var icon, message, root, titleLC; + titleLC = title.toLowerCase(); if (ThreadUpdater.thread["is" + title] === !!OP[titleLC]) { return; @@ -5804,6 +6029,7 @@ }, parse: function(postObjects) { var ID, OP, count, deletedFiles, deletedPosts, files, index, node, nodes, num, post, postObject, posts, scroll, _i, _len, _ref; + OP = postObjects[0]; Build.spoilerRange[ThreadUpdater.thread.board] = OP.custom_spoiler; ThreadUpdater.updateThreadStatus('Sticky', OP); @@ -5876,6 +6102,7 @@ } $.queueTask(function() { var length, threadID; + threadID = ThreadUpdater.thread.ID; length = ThreadUpdater.root.children.length; if (Conf['Enable 4chan\'s Extension']) { @@ -5915,6 +6142,7 @@ }, node: function() { var favicon; + favicon = $.el('img', { className: 'favicon' }); @@ -5931,6 +6159,7 @@ }, refresh: function(watched) { var ID, board, div, favicon, id, link, nodes, props, thread, x, _ref, _ref1; + watched || (watched = $.get('WatchedThreads', {})); nodes = [$('.move', ThreadWatcher.dialog)]; for (board in watched) { @@ -5965,11 +6194,13 @@ }, x: function() { var thread; + thread = this.nextElementSibling.pathname.split('/'); return ThreadWatcher.unwatch(thread[1], thread[3]); }, post: function(e) { var board, postID, threadID, _ref; + _ref = e.detail, board = _ref.board, postID = _ref.postID, threadID = _ref.threadID; if (postID === threadID) { if (Conf['Auto Watch']) { @@ -5989,6 +6220,7 @@ }, unwatch: function(board, threadID) { var watched; + watched = $.get('WatchedThreads', {}); delete watched[board][threadID]; if (!Object.keys(watched[board]).length) { @@ -5999,6 +6231,7 @@ }, watch: function(thread) { var watched, _name; + watched = $.get('WatchedThreads', {}); watched[_name = thread.board] || (watched[_name] = {}); watched[thread.board][thread] = { @@ -6027,6 +6260,7 @@ cypher: $.el('div'), node: function() { var a, child, cypher, cypherText, data, embedder, i, index, len, link, links, lookahead, name, next, node, nodes, snapshot, spoiler, text, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _results; + if (this.isClone && Conf['Embedding']) { _ref = $$('.embedder', this.nodes.comment); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -6106,6 +6340,7 @@ }, toggle: function() { var el, embed, style, type, url; + embed = this.previousElementSibling; if (this.className.contains("embedded")) { el = $.el('a', { @@ -6188,6 +6423,7 @@ regExp: /.*(?:soundcloud.com\/|snd.sc\/)([^#\&\?]*).*/, el: function() { var div; + div = $.el('div', { className: "soundcloud", name: "soundcloud" @@ -6204,6 +6440,7 @@ regExp: /.*(?:pastebin.com\/)([^#\&\?]*).*/, el: function() { var div; + return div = $.el('iframe', { src: "http://pastebin.com/embed_iframe.php?i=" + this.name }); @@ -6211,12 +6448,14 @@ } }, embedder: function(a) { - var callbacks, embed, key, match, service, title, titles, type, _ref; + var callbacks, embed, err, key, match, service, title, titles, type, _ref; + if (!Conf['Embedding']) { return [a]; } callbacks = function() { var title; + return a.textContent = (function() { switch (this.status) { case 200: @@ -6258,7 +6497,8 @@ } else { try { $.cache(service.api.call(a), callbacks); - } catch (err) { + } catch (_error) { + err = _error; a.innerHTML = "[" + key + "] Title Link Blocked (are you using NoScript?)"; } } @@ -6272,6 +6512,7 @@ QR = { init: function() { var sc; + if (g.VIEW === 'catalog' || !Conf['Quick Reply']) { return; } @@ -6342,6 +6583,8 @@ } }, open: function() { + var err; + if (QR.nodes) { QR.nodes.el.hidden = false; QR.unhide(); @@ -6349,7 +6592,8 @@ } try { return QR.dialog(); - } catch (err) { + } catch (_error) { + err = _error; delete QR.nodes; return Main.handleErrors({ message: 'Quick Reply dialog creation crashed.', @@ -6359,6 +6603,7 @@ }, close: function() { var i, _i, _len, _ref; + if (QR.req) { QR.abort(); return; @@ -6406,6 +6651,7 @@ }, error: function(err) { var el; + QR.open(); if (typeof err === 'string') { el = $.tn(err); @@ -6424,6 +6670,7 @@ notifications: [], cleanNotifications: function() { var notification, _i, _len, _ref; + _ref = QR.notifications; for (_i = 0, _len = _ref.length; _i < _len; _i++) { notification = _ref[_i]; @@ -6433,6 +6680,7 @@ }, status: function() { var disabled, status, value; + if (!QR.nodes) { return; } @@ -6449,6 +6697,7 @@ cooldown: { init: function() { var board; + board = g.BOARD.ID; QR.cooldown.types = { thread: (function() { @@ -6482,6 +6731,7 @@ }, sync: function(cooldowns) { var id; + for (id in cooldowns) { QR.cooldown.cooldowns[id] = cooldowns[id]; } @@ -6489,6 +6739,7 @@ }, set: function(data) { var cooldown, delay, hasFile, isReply, isSage, post, req, start, type, upSpd; + req = data.req, post = data.post, isReply = data.isReply, delay = data.delay; start = req ? req.uploadEndTime : Date.now(); if (delay) { @@ -6525,6 +6776,7 @@ }, count: function() { var cooldown, cooldowns, elapsed, hasFile, isReply, isSage, now, post, seconds, start, type, types, upSpd, upSpdAccuracy, update, _ref; + if (!Object.keys(QR.cooldown.cooldowns).length) { $["delete"]("" + g.BOARD + ".cooldown"); delete QR.cooldown.isCounting; @@ -6578,6 +6830,7 @@ }, quote: function(e) { var OP, caretPos, post, range, s, sel, selectionRoot, ta, text; + if (e != null) { e.preventDefault(); } @@ -6607,6 +6860,7 @@ }, characterCount: function() { var count, counter; + counter = QR.nodes.charCount; count = QR.nodes.com.textLength; counter.textContent = count; @@ -6615,6 +6869,7 @@ }, drag: function(e) { var toggle; + toggle = e.type === 'dragstart' ? $.off : $.on; toggle(d, 'dragover', QR.dragOver); return toggle(d, 'drop', QR.dropFile); @@ -6634,6 +6889,7 @@ }, paste: function(e) { var blob, files, item, _i, _len, _ref; + files = []; _ref = e.clipboardData.items; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -6658,6 +6914,7 @@ }, fileInput: function(files) { var file, length, max, post, _i, _len; + if (this instanceof Element) { files = __slice.call(this.files); QR.nodes.fileInput.value = null; @@ -6710,10 +6967,10 @@ }, posts: [], post: (function() { - function _Class() { var el, event, persona, prev, _i, _len, _ref, _this = this; + prev = QR.posts[QR.posts.length - 1]; persona = $.get('QR.persona', {}); this.name = prev ? prev.name : persona.name || null; @@ -6761,6 +7018,7 @@ _Class.prototype.rm = function() { var index; + $.rm(this.nodes.el); index = QR.posts.indexOf(this); if (QR.posts.length === 1) { @@ -6777,6 +7035,7 @@ _Class.prototype.lock = function(lock) { var name, _i, _len, _ref; + if (lock == null) { lock = true; } @@ -6801,6 +7060,7 @@ _Class.prototype.select = function() { var name, rectEl, rectList, _i, _len, _ref; + if (QR.selected) { QR.selected.nodes.el.id = null; QR.selected.forceSave(); @@ -6822,6 +7082,7 @@ _Class.prototype.save = function(input) { var value, _ref; + value = input.value; this[input.dataset.name] = value; if (input.nodeName !== 'TEXTAREA') { @@ -6836,6 +7097,7 @@ _Class.prototype.forceSave = function() { var name, _i, _len, _ref; + if (this !== QR.selected) { return; } @@ -6867,6 +7129,7 @@ _Class.prototype.setThumbnail = function(fileURL) { var img, reader, _this = this; + if (!window.URL) { if (!fileURL) { reader = new FileReader(); @@ -6882,6 +7145,7 @@ img = $.el('img'); img.onload = function() { var applyBlob, cv, data, height, i, l, s, ui8a, width, _i; + s = 90 * 2; height = img.height, width = img.width; if (height < s || width < s) { @@ -6960,9 +7224,11 @@ _Class.prototype.pasteText = function(file) { var reader, _this = this; + reader = new FileReader(); reader.onload = function(e) { var text; + text = e.target.result; if (_this.com) { _this.com += "\n" + text; @@ -7000,6 +7266,7 @@ _Class.prototype.drop = function() { var el, index, newIndex, oldIndex, post; + el = $('.drag', this.parentNode); $.rmClass(el, 'drag'); $.rmClass(this, 'over'); @@ -7033,6 +7300,7 @@ }, ready: function() { var MutationObserver, imgContainer, input, observer; + imgContainer = $.el('div', { className: 'captcha-img', title: 'Reload', @@ -7071,6 +7339,7 @@ }, getOne: function() { var captcha, challenge, response; + this.clear(); if (captcha = this.captchas.shift()) { challenge = captcha.challenge, response = captcha.response; @@ -7095,6 +7364,7 @@ }, save: function() { var response; + if (!(response = this.nodes.input.value.trim())) { return; } @@ -7109,6 +7379,7 @@ }, clear: function() { var captcha, i, now, _i, _len, _ref; + now = Date.now(); _ref = this.captchas; for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { @@ -7126,6 +7397,7 @@ }, load: function() { var challenge; + if (!this.nodes.challenge.firstChild) { return; } @@ -7138,6 +7410,7 @@ }, count: function() { var count; + count = this.captchas.length; this.nodes.input.placeholder = (function() { switch (count) { @@ -7170,6 +7443,7 @@ }, dialog: function() { var dialog, mimeTypes, name, node, nodes, thread, _i, _j, _len, _len1, _ref, _ref1; + dialog = UI.dialog('qr', 'top:0;right:0;', "
\n \n \n \n ×\n
\n
\n
\n \n \n \n \n
\n
\n
\n +\n
\n
\n \n \n
\n
\n \n \n No selected file\n \n \n ×\n \n \n
\n \n
".replace(/>\s+<')); QR.nodes = nodes = { el: dialog, @@ -7266,6 +7540,7 @@ }, submit: function(e) { var callbacks, challenge, err, filetag, m, opts, post, postData, response, textOnly, threadID, _ref; + if (e != null) { e.preventDefault(); } @@ -7364,6 +7639,7 @@ }, response: function() { var URL, ban, board, err, h1, persona, post, postID, req, threadID, tmpDoc, _, _base, _ref, _ref1; + req = QR.req; delete QR.req; post = QR.posts[0]; @@ -7465,6 +7741,7 @@ }, ready: function() { var field, form; + form = $('form'); field = $.id('recaptcha_response_field'); $.on(field, 'keydown', function(e) { @@ -7474,6 +7751,7 @@ }); return $.on(form, 'submit', function(e) { var response; + e.preventDefault(); response = field.value.trim(); if (!/\s/.test(response)) { @@ -7485,7 +7763,6 @@ }; Board = (function() { - Board.prototype.toString = function() { return this.ID; }; @@ -7502,7 +7779,6 @@ })(); Thread = (function() { - Thread.prototype.callbacks = []; Thread.prototype.toString = function() { @@ -7527,7 +7803,6 @@ })(); Post = (function() { - Post.prototype.callbacks = []; Post.prototype.toString = function() { @@ -7536,6 +7811,7 @@ function Post(root, thread, board, that) { var alt, anchor, capcode, date, email, file, fileInfo, flag, info, name, post, size, subject, thumb, tripcode, uniqueID, unit, _ref; + this.thread = thread; this.board = board; if (that == null) { @@ -7630,6 +7906,7 @@ Post.prototype.parseComment = function() { var bq, data, i, node, nodes, text, _i, _j, _len, _ref, _ref1; + bq = this.nodes.comment.cloneNode(true); _ref = $$('.abbr, .capcodeReplies, .exif, b', bq); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -7646,6 +7923,7 @@ Post.prototype.parseQuotes = function() { var hash, pathname, quotelink, quotes, _i, _len, _ref; + quotes = {}; _ref = $$('.quotelink', this.nodes.comment); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -7675,6 +7953,7 @@ Post.prototype.kill = function(file, now) { var clone, quotelink, strong, _i, _j, _len, _len1, _ref, _ref1; + now || (now = new Date()); if (file) { this.file.isDead = true; @@ -7717,6 +7996,7 @@ Post.prototype.resurrect = function() { var clone, quotelink, strong, _i, _j, _len, _len1, _ref, _ref1; + delete this.isDead; delete this.timeOfDeath; $.rmClass(this.nodes.root, 'deleted-post'); @@ -7750,6 +8030,7 @@ Post.prototype.rmClone = function(index) { var clone, _i, _len, _ref; + this.clones.splice(index, 1); _ref = this.clones.slice(index); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -7763,11 +8044,11 @@ })(); Clone = (function(_super) { - __extends(Clone, _super); function Clone(origin, context) { var file, index, info, inline, inlined, key, nodes, post, root, val, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; + this.origin = origin; this.context = context; _ref = ['ID', 'fullID', 'board', 'thread', 'info', 'quotes', 'isReply']; @@ -7853,6 +8134,7 @@ Main = { init: function() { var flatten, initFeatures, key, pathname, val; + $.asap((function() { return d.documentElement; }), function() { @@ -7860,6 +8142,7 @@ }); flatten = function(parent, obj) { var key, val; + if (obj instanceof Array) { Conf[parent] = obj[0]; } else if (typeof obj === 'object') { @@ -7898,6 +8181,7 @@ case 'images.4chan.org': $.ready(function() { var url; + if (Conf['404 Redirect'] && d.title === '4chan - 404 Not Found') { url = Redirect.image(pathname[1], pathname[3]); if (url) { @@ -7908,12 +8192,14 @@ return; } initFeatures = function(features) { - var module, name; + var err, module, name; + for (name in features) { module = features[name]; try { module.init(); - } catch (err) { + } catch (_error) { + err = _error; Main.handleErrors({ message: "\"" + name + "\" initialization crashed.", error: err @@ -7976,6 +8262,7 @@ }, initStyle: function() { var MutationObserver, mainStyleSheet, observer, setStyle, style, styleSheets, _ref; + if (!Main.isThisPageLegit()) { return; } @@ -7994,6 +8281,7 @@ styleSheets = $$('link[rel="alternate stylesheet"]', d.head); setStyle = function() { var styleSheet, _i, _len; + $.rmClass(doc, style); for (_i = 0, _len = styleSheets.length; _i < _len; _i++) { styleSheet = styleSheets[_i]; @@ -8019,7 +8307,8 @@ } }, initReady: function() { - var board, boardChild, errors, href, posts, thread, threadChild, threads, _i, _j, _len, _len1, _ref, _ref1; + var board, boardChild, err, errors, href, posts, thread, threadChild, threads, _i, _j, _len, _len1, _ref, _ref1; + if (d.title === '4chan - 404 Not Found') { if (Conf['404 Redirect'] && g.VIEW === 'thread') { href = Redirect.to({ @@ -8053,7 +8342,8 @@ } try { posts.push(new Post(threadChild, thread, g.BOARD)); - } catch (err) { + } catch (_error) { + err = _error; if (!errors) { errors = []; } @@ -8073,7 +8363,8 @@ return $.event('4chanXInitFinished'); }, callbackNodes: function(klass, nodes) { - var callback, errors, i, len, node, _i, _j, _len, _ref; + var callback, err, errors, i, len, node, _i, _j, _len, _ref; + len = nodes.length; _ref = klass.prototype.callbacks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -8082,7 +8373,8 @@ node = nodes[i]; try { callback.cb.call(node); - } catch (err) { + } catch (_error) { + err = _error; if (!errors) { errors = []; } @@ -8099,6 +8391,7 @@ }, addCallback: function(e) { var Klass, obj; + obj = e.detail; Klass = obj.type === 'Post' ? Post : Thread; obj.callback.isAddon = true; @@ -8106,6 +8399,7 @@ }, handleErrors: function(errors) { var div, error, logs, _i, _len; + if (!('length' in errors)) { error = errors; } else if (errors.length === 1) { @@ -8138,6 +8432,7 @@ }, parseError: function(data) { var error, message; + message = data.message, error = data.error; c.log(message, error); c.log(message, error.stack); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8bcc068df..42fa67ad2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,5 @@ Note: this is only used to release new 4chan X versions, and is **not** needed o ### Contribute - Edit the CoffeeScript sources. -- Build the JavaScript. - If the edits affect regular users, edit the changelog. -- Fork the repository. - Open a pull request. diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 396f6f0c0..cc031785f 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -23,7 +23,31 @@ module.exports = (grunt) -> ] dest: 'tmp/script.coffee' - script: + manifest: + options: + process: + data: pkg + src: 'src/manifest.json', + dest: 'builds/crx/manifest.json' + + metadata: + options: + process: + data: pkg + src: 'src/metadata.js', + dest: '<%= pkg.name %>.meta.js' + + crx: + options: + process: + data: pkg + src: [ + 'src/banner.js' + 'tmp/script.js' + ] + dest: 'builds/crx/script.js' + + userscript: options: process: data: pkg @@ -32,14 +56,12 @@ module.exports = (grunt) -> 'src/banner.js' 'tmp/script.js' ] - dest: '<%= pkg.meta.files.userjs %>' + dest: '<%= pkg.name %>.user.js' - metadata: - options: - process: - data: pkg - src: 'src/metadata.js', - dest: '<%= pkg.meta.files.metajs %>' + userjs: + # Lazily copy the userscript + src: '<%= pkg.name %>.user.js' + dest: 'builds/<%= pkg.name %>.js' coffee: script: @@ -88,10 +110,13 @@ module.exports = (grunt) -> grunt.loadNpmTasks 'grunt-exec' grunt.registerTask 'default', [ - 'concat:coffee' - 'coffee:script' - 'concat:script' - 'concat:metadata' + 'concat:coffee', + 'coffee:script', + 'concat:manifest', + 'concat:crx', + 'concat:userscript', + 'concat:userjs', + 'concat:metadata', 'clean' ] @@ -103,8 +128,24 @@ module.exports = (grunt) -> grunt.registerTask 'patch', [ 'bump' + 'updcl:3' ] - grunt.registerTask 'upgrade', [ + grunt.registerTask 'minor', [ 'bump:minor' + 'updcl:2' ] + + grunt.registerTask 'major', [ + 'bump:major' + 'updcl:1' + ] + grunt.registerTask 'updcl', 'Update the changelog', (i) -> + # Update the `pkg` object with the new version. + pkg = grunt.file.readJSON('package.json'); + # i is the number of #s for markdown. + version = [] + version.length = +i + 1 + version = version.join('#') + ' ' + pkg.version + ' *(' + grunt.template.today('yyyy-mm-dd') + ')*' + grunt.file.write 'CHANGELOG.md', version + '\n' + grunt.file.read('CHANGELOG.md') + grunt.log.ok 'Changelog updated for v' + pkg.version + '.' \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index ce7ec379c..000000000 --- a/Gruntfile.js +++ /dev/null @@ -1,106 +0,0 @@ -module.exports = function(grunt) { - - var pkg = grunt.file.readJSON('package.json'); - - // Project configuration. - grunt.initConfig({ - pkg: pkg, - concat: { - coffee: { - options: { process: { data: pkg } }, - src: [ - 'src/config.coffee', - 'src/globals.coffee', - 'lib/ui.coffee', - 'lib/$.coffee', - 'lib/polyfill.coffee', - 'src/features.coffee', - 'src/qr.coffee', - 'src/report.coffee', - 'src/main.coffee' - ], - dest: 'tmp/script.coffee' - }, - script: { - options: { process: { data: pkg } }, - src: [ - 'src/metadata.js', - 'src/banner.js', - 'tmp/script.js' - ], - dest: '<%= pkg.meta.files.userjs %>' - }, - metadata: { - options: { process: { data: pkg } }, - src: 'src/metadata.js', - dest: '<%= pkg.meta.files.metajs %>' - } - }, - coffee: { - script: { - src: 'tmp/script.coffee', - dest: 'tmp/script.js' - } - }, - exec: { - commit: { - command: function() { - var release = pkg.meta.name + ' v' + pkg.version; - return [ - 'git checkout ' + pkg.meta.mainBranch, - 'git commit -am "Release ' + release + '."', - 'git tag -a ' + pkg.version + ' -m "' + release + '."', - 'git tag -af stable-v3 -m "' + release + '."' - ].join(' && '); - }, - stdout: true - }, - push: { - command: 'git push origin --all && git push origin --tags', - stdout: true - } - }, - watch: { - all: { - options: { - interrupt: true - }, - files: [ - 'Gruntfile.js', - 'package.json', - 'lib/**/*.coffee', - 'src/**/*.coffee', - 'src/**/*.js', - 'css/**/*.css', - 'img/*' - ], - tasks: 'default' - } - }, - clean: { - tmp: 'tmp' - } - }); - - grunt.loadNpmTasks('grunt-bump'); - grunt.loadNpmTasks('grunt-contrib-clean'); - grunt.loadNpmTasks('grunt-contrib-coffee'); - grunt.loadNpmTasks('grunt-contrib-concat'); - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-exec'); - - grunt.registerTask('default', ['concat:coffee', 'coffee:script', 'concat:script', 'concat:metadata', 'clean']); - grunt.registerTask('release', ['default', 'exec:commit', 'exec:push']); - grunt.registerTask('patch', ['bump', 'updcl:3']); - grunt.registerTask('minor', ['bump:minor', 'updcl:2']); - grunt.registerTask('major', ['bump:major', 'updcl:1']); - grunt.registerTask('updcl', 'Update the changelog', function(i) { - // Update the `pkg` object with the new version. - pkg = grunt.file.readJSON('package.json'); - // i is the number of #s for markdown. - var version = new Array(+i + 1).join('#') + ' ' + pkg.version + ' *(' + grunt.template.today('yyyy-mm-dd') + ')*'; - grunt.file.write('CHANGELOG.md', version + '\n' + grunt.file.read('CHANGELOG.md')); - grunt.log.ok('Changelog updated for v' + pkg.version + '.'); - }); - -}; diff --git a/lib/$.coffee b/lib/$.coffee index 941d1008a..447bb1c3d 100644 --- a/lib/$.coffee +++ b/lib/$.coffee @@ -232,7 +232,7 @@ $.extend $, unsafeWindow: if window.opera # Opera window - else if unsafeWindow isnt window # Firefox + else if unsafeWindow? # Firefox unsafeWindow else # Chrome do -> diff --git a/package.json b/package.json index 23a7825b2..a099124d4 100644 --- a/package.json +++ b/package.json @@ -7,16 +7,18 @@ "repo": "https://github.com/MayhemYDG/4chan-x/", "page": "http://mayhemydg.github.com/4chan-x/", "mainBranch": "v3", - "files": { - "metajs": "4chan_x.meta.js", - "userjs": "4chan_x.user.js" - } + "matches": [ + "*://api.4chan.org/*", + "*://boards.4chan.org/*", + "*://images.4chan.org/*", + "*://sys.4chan.org/*" + ] }, "devDependencies": { "grunt": "~0.4.0", "grunt-bump": "~0.0.0", "grunt-contrib-clean": "~0.4.0", - "grunt-contrib-coffee": "~0.6.0", + "grunt-contrib-coffee": "~0.6.2", "grunt-contrib-concat": "~0.1.0", "grunt-contrib-watch": "~0.3.0", "grunt-exec": "~0.4.0" diff --git a/src/globals.coffee b/src/globals.coffee index 46065812e..a17a6f7a0 100644 --- a/src/globals.coffee +++ b/src/globals.coffee @@ -8,7 +8,7 @@ d = document doc = null g = VERSION: '<%= version %>' - NAMESPACE: "<%= meta.name %>." + NAMESPACE: '<%= meta.name %>.' boards: {} threads: {} posts: {} diff --git a/src/manifest.json b/src/manifest.json new file mode 100644 index 000000000..f125f6cb5 --- /dev/null +++ b/src/manifest.json @@ -0,0 +1,14 @@ +{ + "name": "<%= meta.name %>", + "version": "<%= version %>", + "manifest_version": 2, + "description": "<%= description %>", + "content_scripts": [{ + "js": ["script.js"], + "matches": <%= JSON.stringify(meta.matches) %>, + "run_at": "document_start" + }], + "homepage_url": "<%= meta.page %>", + "incognito": "spanning", + "minimum_chrome_version": "25" +} diff --git a/src/metadata.js b/src/metadata.js index 5f190df0f..415133169 100644 --- a/src/metadata.js +++ b/src/metadata.js @@ -6,16 +6,17 @@ // @copyright 2009-2011 James Campos // @copyright 2012-<%= grunt.template.today('yyyy') %> Nicolas Stepien // @license MIT; http://en.wikipedia.org/wiki/Mit_license -// @match *://boards.4chan.org/* -// @match *://images.4chan.org/* -// @match *://sys.4chan.org/* -// @match *://api.4chan.org/* +<%= + meta.matches.map(function(match) { + return '// @match ' + match; + }).join('\n') +%> // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @grant GM_openInTab // @run-at document-start -// @updateURL <%= meta.repo %>raw/<%= meta.mainBranch %>/<%= meta.files.metajs %> -// @downloadURL <%= meta.repo %>raw/<%= meta.mainBranch %>/<%= meta.files.userjs %> +// @updateURL <%= meta.repo %>raw/<%= meta.mainBranch %>/<%= name %>.meta.js +// @downloadURL <%= meta.repo %>raw/<%= meta.mainBranch %>/<%= name %>.user.js // @icon data:image/gif;base64,<%= grunt.file.read('img/icon.gif', {encoding: 'base64'}) %> // ==/UserScript==