diff --git a/4chan_x.js b/4chan_x.js index 3aa8ead83..b6324dfdd 100644 --- a/4chan_x.js +++ b/4chan_x.js @@ -59,7 +59,7 @@ */ (function() { - var $, $$, NAMESPACE, a, as, autoWatch, callback, changeCheckbox, changeValue, config, d, delform, el, expand, expandComment, expandThread, g, imageClick, imageExpand, imageExpandClick, imageHover, imageResize, imageThumb, imageToggle, imageType, imageTypeChange, keyModeInsert, keyModeNormal, keydown, keypress, log, nav, navtopr, nodeInserted, omitted, onloadComment, onloadThread, option, options, parseResponse, pathname, qr, recaptcha, recaptchaListener, recaptchaReload, redirect, replyHiding, replyNav, report, request, scroll, scrollThread, span, temp, text, threadHiding, tzOffset, ui, updateAuto, updateCallback, updateFavicon, updateInterval, updateNow, updateTime, updateTitle, updateVerbose, updater, updaterMake, watcher, _i, _j, _k, _l, _len, _len2, _len3, _len4, _len5, _m, _ref, _ref2, _ref3, _ref4; + var $, $$, NAMESPACE, a, as, autoWatch, callback, changeCheckbox, changeValue, config, d, delform, el, expand, expandComment, expandThread, g, imageClick, imageExpand, imageExpandClick, imageHover, imageResize, imageThumb, imageToggle, imageType, imageTypeChange, keyModeNormal, keybinds, log, nav, navtopr, nodeInserted, omitted, onloadComment, onloadThread, option, options, parseResponse, pathname, qr, recaptcha, recaptchaListener, recaptchaReload, redirect, replyHiding, replyNav, report, request, scroll, scrollThread, span, temp, text, threadHiding, tzOffset, ui, updateAuto, updateCallback, updateFavicon, updateInterval, updateNow, updateTime, updateTitle, updateVerbose, updater, updaterMake, watcher, _i, _j, _k, _l, _len, _len2, _len3, _len4, _len5, _m, _ref, _ref2, _ref3, _ref4; var __slice = Array.prototype.slice; if (typeof console != "undefined" && console !== null) { log = console.log; @@ -75,7 +75,7 @@ 'Image Expansion': [true, 'Expand images'], 'Image Hover': [false, 'Show full image on mouseover'], 'Image Preloading': [false, 'Preload Images'], - 'Keybinds': [false, 'Binds actions to keys'], + 'Keybinds': [true, 'Binds actions to keys'], 'Localize Time': [true, 'Show times based on your timezone'], 'Persistent QR': [false, 'Quick reply won\'t disappear after posting. Only in replies.'], 'Post in Title': [true, 'Show the op\'s post in the tab title'], @@ -662,42 +662,90 @@ thumb.className = ''; return $.remove(thumb.nextSibling); }; - keydown = function(e) { - var kc; - kc = e.keyCode; - g.keyCode = kc; - return g.char = String.fromCharCode(kc); - }; - keypress = function(e) { - var _ref; - if ((_ref = d.activeElement.nodeName) === 'TEXTAREA' || _ref === 'INPUT') { - return keyModeInsert(e); - } else { - return keyModeNormal(e); - } - }; - keyModeInsert = function(e) { - var char, kc, range, selEnd, selStart, ta, valEnd, valMid, valStart, value; - kc = g.keyCode; - char = g.char; - if (kc === 27) { - $.remove($('#qr')); - return e.preventDefault(); - } else if (e.ctrlKey && char === "S") { - ta = d.activeElement; - if (ta.nodeName !== 'TEXTAREA') { - return; + keybinds = { + init: function() { + $.bind(d, 'keydown', keybinds.cb.keydown); + return $.bind(d, 'keypress', keybinds.cb.keypress); + }, + cb: { + keydown: function(e) { + var kc, key, _ref; + if ((_ref = d.activeElement.nodeName) === 'TEXTAREA' || _ref === 'INPUT') { + keybinds.mode = keybinds.insert; + } else { + keybinds.mode = keybinds.normal; + } + kc = e.keyCode; + if ((65 <= kc && kc <= 90)) { + key = String.fromCharCode(kc); + if (!e.shiftKey) { + key = key.toLowerCase(); + } + if (e.ctrlKey) { + key = '^' + key; + } + } else { + if (kc === 27) { + key = ''; + } + } + return keybinds.key = key; + }, + keypress: function(e) { + return keybinds.mode(e); + } + }, + insert: function(e) { + var range, selEnd, selStart, ta, valEnd, valMid, valStart, value; + switch (keybinds.key) { + case '': + e.preventDefault(); + return $.remove($('#qr')); + case '^s': + ta = d.activeElement; + if (ta.nodeName !== 'TEXTAREA') { + return; + } + e.preventDefault(); + value = ta.value; + selStart = ta.selectionStart; + selEnd = ta.selectionEnd; + valStart = value.slice(0, selStart) + '[spoiler]'; + valMid = value.slice(selStart, selEnd); + valEnd = '[/spoiler]' + value.slice(selEnd); + ta.value = valStart + valMid + valEnd; + range = valStart.length + valMid.length; + return ta.setSelectionRange(range, range); + } + }, + normal: function(e) { + var thread; + switch (keybinds.key) { + case 'I': + break; + case 'J': + break; + case 'K': + break; + case 'M': + break; + case 'i': + break; + case 'm': + break; + case 'n': + return nav.down(); + case 'o': + break; + case 'p': + return nav.up(); + case 'u': + break; + case 'w': + thread = nav.getThread()[0]; + return watcher.toggle(thread); + case 'x': } - value = ta.value; - selStart = ta.selectionStart; - selEnd = ta.selectionEnd; - valStart = value.slice(0, selStart) + '[spoiler]'; - valMid = value.slice(selStart, selEnd); - valEnd = '[/spoiler]' + value.slice(selEnd); - ta.value = valStart + valMid + valEnd; - range = valStart.length + valMid.length; - ta.setSelectionRange(range, range); - return e.preventDefault(); } }; keyModeNormal = function(e) { @@ -708,16 +756,6 @@ char = g.char; hash = location.hash; switch (char) { - case "0": - return location.pathname = "/" + g.BOARD; - case "G": - if (e.shiftKey) { - return window.scrollTo(0, 99999); - } else { - window.scrollTo(0, 0); - return location.hash = ''; - } - break; case "I": if (g.REPLY) { if (!(qrLink = $('td.replyhl span[id] a:not(:first-child)'))) { @@ -1729,7 +1767,7 @@ } favicon = $.el('img', { src: src, - className: 'pointer' + className: 'favicon' }); $.bind(favicon, 'click', watcher.cb.toggle); _results.push($.before(input, favicon)); @@ -1750,7 +1788,7 @@ }, cb: { toggle: function(e) { - return watcher.toggle(e.target); + return watcher.toggle(e.target.parentNode); }, x: function(e) { var board, id, _, _ref; @@ -1758,8 +1796,9 @@ return watcher.unwatch(board, id); } }, - toggle: function(favicon) { - var id; + toggle: function(thread) { + var favicon, id; + favicon = $('img.favicon', thread); id = favicon.nextSibling.name; if (favicon.src === g.favEmpty) { return watcher.watch(id, favicon); @@ -1848,7 +1887,7 @@ div.dialog > div.move {\ cursor: move;\ }\ - label, a, .pointer {\ + label, a, {\ cursor: pointer;\ }\ \ @@ -1931,6 +1970,9 @@ .new {\ background: lime;\ }\ + .favicon {\ + cursor: pointer;\ + }\ '); if (location.hostname === 'sys.4chan.org') { qr.sys(); @@ -2088,8 +2130,7 @@ for (_i = 0, _len = arr.length; _i < _len; _i++) { el = arr[_i]; a = $.el('a', { - textContent: '[ ! ]', - className: 'pointer' + textContent: '[ ! ]' }); $.bind(a, 'click', report); $.after(el, a); @@ -2119,8 +2160,7 @@ }); } if ($.config('Keybinds')) { - $.bind(d, 'keydown', keydown); - $.bind(d, 'keypress', keypress); + keybinds.init(); } if ($.config('Thread Updater')) { updater.init(); @@ -2176,7 +2216,7 @@ for (_k = 0, _len3 = omitted.length; _k < _len3; _k++) { span = omitted[_k]; a = $.el('a', { - className: 'pointer omittedposts', + className: 'omittedposts', textContent: "+ " + span.textContent }); $.bind(a, 'click', expandThread); diff --git a/script.coffee b/script.coffee index 75adaef09..6890701b4 100644 --- a/script.coffee +++ b/script.coffee @@ -1,4 +1,4 @@ -# TODO floating nav buttons FUCKING AWESOME +# TODO # option to skip post form directly to contents on first page, # like what happens when using thread nav to go to next page # (floating) qr no-quote button? @@ -6,6 +6,7 @@ # XXX error on FUCKING CHROME {log} = console if console? +# TODO put keybinds back to false when done config = main: checkbox: @@ -17,7 +18,7 @@ config = 'Image Expansion': [true, 'Expand images'] 'Image Hover': [false, 'Show full image on mouseover'] 'Image Preloading': [false, 'Preload Images'] - 'Keybinds': [false, 'Binds actions to keys'] + 'Keybinds': [true, 'Binds actions to keys'] 'Localize Time': [true, 'Show times based on your timezone'] 'Persistent QR': [false, 'Quick reply won\'t disappear after posting. Only in replies.'] 'Post in Title': [true, 'Show the op\'s post in the tab title'] @@ -481,53 +482,98 @@ imageThumb = (thumb) -> thumb.className = '' $.remove thumb.nextSibling -keydown = (e) -> - kc = e.keyCode - g.keyCode = kc - g.char = String.fromCharCode kc +keybinds = + init: -> + $.bind d, 'keydown', keybinds.cb.keydown + $.bind d, 'keypress', keybinds.cb.keypress -keypress = (e) -> - if d.activeElement.nodeName in ['TEXTAREA', 'INPUT'] - keyModeInsert e - else - keyModeNormal e + cb: + keydown: (e) -> + if d.activeElement.nodeName in ['TEXTAREA', 'INPUT'] + keybinds.mode = keybinds.insert + else + keybinds.mode = keybinds.normal -keyModeInsert = (e) -> - kc = g.keyCode - char = g.char - if kc is 27 #escape - $.remove $ '#qr' - e.preventDefault() - else if e.ctrlKey and char is "S" - ta = d.activeElement - return unless ta.nodeName is 'TEXTAREA' + kc = e.keyCode + if 65 <= kc <= 90 #A-Z + key = String.fromCharCode kc + if !e.shiftKey + key = key.toLowerCase() + if e.ctrlKey then key = '^' + key + else + if kc is 27 + key = '' + keybinds.key = key - value = ta.value - selStart = ta.selectionStart - selEnd = ta.selectionEnd + keypress: (e) -> + keybinds.mode e - valStart = value[0...selStart] + '[spoiler]' - valMid = value[selStart...selEnd] - valEnd = '[/spoiler]' + value[selEnd..] + insert: (e) -> + switch keybinds.key + when '' + e.preventDefault() + $.remove $ '#qr' + when '^s' + ta = d.activeElement + return unless ta.nodeName is 'TEXTAREA' + + e.preventDefault() + + value = ta.value + selStart = ta.selectionStart + selEnd = ta.selectionEnd + + valStart = value[0...selStart] + '[spoiler]' + valMid = value[selStart...selEnd] + valEnd = '[/spoiler]' + value[selEnd..] + + ta.value = valStart + valMid + valEnd + range = valStart.length + valMid.length + ta.setSelectionRange range, range + + normal: (e) -> + switch keybinds.key + when 'I' + #qr no text + return + when 'J' + #highlight next + return + when 'K' + #highlight prev + return + when 'M' + #expand all + return + when 'i' + #qr + return + when 'm' + #expand img + return + when 'n' + nav.down() + when 'o' + #open in new tab + return + when 'p' + nav.up() + when 'u' + #update now + return + when 'w' + [thread] = nav.getThread() + watcher.toggle thread + when 'x' + #toggle hide thread + return - ta.value = valStart + valMid + valEnd - range = valStart.length + valMid.length - ta.setSelectionRange range, range - e.preventDefault() keyModeNormal = (e) -> return if e.ctrlKey or e.altKey char = g.char hash = location.hash switch char - when "0" - location.pathname = "/#{g.BOARD}" - when "G" - if e.shiftKey - window.scrollTo 0, 99999 - else - window.scrollTo 0, 0 - location.hash = '' when "I" if g.REPLY unless qrLink = $ 'td.replyhl span[id] a:not(:first-child)' @@ -1337,7 +1383,7 @@ watcher = src = g.favEmpty favicon = $.el 'img', src: src - className: 'pointer' + className: 'favicon' $.bind favicon, 'click', watcher.cb.toggle $.before input, favicon @@ -1354,13 +1400,14 @@ watcher = cb: toggle: (e) -> - watcher.toggle e.target + watcher.toggle e.target.parentNode x: (e) -> [board, _, id] = e.target.nextElementSibling .getAttribute('href').substring(1).split('/') watcher.unwatch board, id - toggle: (favicon) -> + toggle: (thread) -> + favicon = $ 'img.favicon', thread id = favicon.nextSibling.name if favicon.src == g.favEmpty watcher.watch id, favicon @@ -1453,7 +1500,7 @@ $.addStyle ' div.dialog > div.move { cursor: move; } - label, a, .pointer { + label, a, { cursor: pointer; } @@ -1536,6 +1583,9 @@ $.addStyle ' .new { background: lime; } + .favicon { + cursor: pointer; + } ' if location.hostname is 'sys.4chan.org' @@ -1653,7 +1703,6 @@ if $.config 'Quick Report' for el in arr a = $.el 'a', textContent: '[ ! ]' - className: 'pointer' $.bind a, 'click', report $.after el, a $.after el, $.tn(' ') @@ -1674,8 +1723,7 @@ if $.config 'Anonymize' $.remove trip if $.config 'Keybinds' - $.bind d, 'keydown', keydown - $.bind d, 'keypress', keypress + keybinds.init() if $.config 'Thread Updater' updater.init() @@ -1716,7 +1764,7 @@ else #not reply omitted = $$('span.omittedposts') for span in omitted a = $.el 'a', - className: 'pointer omittedposts' + className: 'omittedposts' textContent: "+ #{span.textContent}" $.bind a, 'click', expandThread $.replace(span, a)