From 1bdad6b3e5f942ada9e62a886b7812d47cf2d5f5 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Fri, 22 Jun 2012 23:57:11 +0200 Subject: [PATCH 01/30] Start working on a per-post menu. --- 4chan_x.user.js | 85 ++++++++++++++++++++++++++++++++++++++++++++++++- script.coffee | 72 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 4289cfe40..424a3a6ae 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -77,7 +77,7 @@ */ (function() { - var $, $$, Anonymize, AutoGif, Conf, Config, DeleteButton, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; + var $, $$, Anonymize, AutoGif, Conf, Config, DeleteButton, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; Config = { main: { @@ -1071,6 +1071,73 @@ } }; + Menu = { + entries: [], + init: function() { + this.a = $.el('a', { + className: 'menu_button', + href: 'javascript:;', + innerHTML: '[▼]' + }); + this.el = $.el('div', { + className: 'reply dialog', + id: 'menu' + }); + $.on(this.el, 'click', function(e) { + return e.stopPropagation(); + }); + return Main.callbacks.push(this.node); + }, + node: function(post) { + var a; + if (!(a = $('.menu_button', post.el))) { + a = Menu.a.cloneNode(true); + $.add($('.postInfo', post.el), a); + } + return $.on(a, 'click', Menu.toggle); + }, + toggle: function(e) { + var lastOpener, s; + e.preventDefault(); + e.stopPropagation(); + if (Menu.el.parentNode) { + lastOpener = Menu.lastOpener; + Menu.close(); + if (lastOpener === this) { + return; + } + } + s = Menu.el.style; + s.top = this.offsetTop + this.offsetHeight + 2 + 'px'; + s.left = this.offsetLeft + 'px'; + Menu.lastOpener = this; + return Menu.open(Main.preParse($.x('ancestor::div[contains(@class,"postContainer")][1]', this))); + }, + open: function(post) { + var entry, i, _i, _len, _ref; + for (i in post) { + $.add(Menu.el, $.el('code', { + className: 'entry', + textContent: "" + i + ": " + post[i] + })); + } + _ref = Menu.entries; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + entry = _ref[_i]; + $.add(Menu.el, entry.el); + $.addClass(entry.el, 'entry'); + } + $.add(d.body, Menu.el); + return $.on(d, 'click', Menu.close); + }, + close: function() { + $.rm(Menu.el); + Menu.el.innerHTML = null; + delete Menu.lastOpener; + return $.off(d, 'click', Menu.close); + } + }; + Keybinds = { init: function() { var node, _i, _len, _ref; @@ -4429,6 +4496,7 @@ if (Conf['Anonymize']) { Anonymize.init(); } + Menu.init(); if (Conf['Time Formatting']) { Time.init(); } @@ -4715,6 +4783,21 @@ a[href="javascript:;"] {\ display: none !important;\ }\ \ +#menu {\ + position: absolute;\ +}\ +.entry {\ + border-bottom: 1px solid rgba(0, 0, 0, .25);\ + display: block;\ + padding: 3px 4px;\ +}\ +.entry:last-child {\ + border: none;\ +}\ +.entry:hover, .entry:focus {\ + background: rgba(255, 255, 255, .33);\ +}\ +\ h1 {\ text-align: center;\ }\ diff --git a/script.coffee b/script.coffee index ea61bfd78..ceac5dc77 100644 --- a/script.coffee +++ b/script.coffee @@ -814,6 +814,61 @@ ReplyHiding = $('.sideArrows', root).hidden = false $('.post', root).hidden = false +Menu = + entries: [] + init: -> + @a = $.el 'a', + className: 'menu_button' + href: 'javascript:;' + innerHTML: '[▼]' + @el = $.el 'div', + className: 'reply dialog' + id: 'menu' + + $.on @el, 'click', (e) -> e.stopPropagation() + + Main.callbacks.push @node + node: (post) -> + unless a = $ '.menu_button', post.el + a = Menu.a.cloneNode true + $.add $('.postInfo', post.el), a + $.on a, 'click', Menu.toggle + + toggle: (e) -> + e.preventDefault() + e.stopPropagation() + + if Menu.el.parentNode + # Close if it's already opened. + # Reopen if we clicked on another button. + {lastOpener} = Menu + Menu.close() + return if lastOpener is @ + + # Position + s = Menu.el.style + s.top = @offsetTop + @offsetHeight + 2 + 'px' + s.left = @offsetLeft + 'px' + + Menu.lastOpener = @ + Menu.open Main.preParse $.x 'ancestor::div[contains(@class,"postContainer")][1]', @ + open: (post) -> + for i of post + $.add Menu.el, $.el 'code', + className: 'entry' + textContent: "#{i}: #{post[i]}" + for entry in Menu.entries + # if the entry matches this post... + $.add Menu.el, entry.el + $.addClass entry.el, 'entry' # XXX ??? + $.add d.body, Menu.el + $.on d, 'click', Menu.close + close: -> + $.rm Menu.el + Menu.el.innerHTML = null + delete Menu.lastOpener + $.off d, 'click', Menu.close + Keybinds = init: -> for node in $$ '[accesskey]' @@ -3435,6 +3490,8 @@ Main = if Conf['Anonymize'] Anonymize.init() + Menu.init() + if Conf['Time Formatting'] Time.init() @@ -3663,6 +3720,21 @@ a[href="javascript:;"] { display: none !important; } +#menu { + position: absolute; +} +.entry { + border-bottom: 1px solid rgba(0, 0, 0, .25); + display: block; + padding: 3px 4px; +} +.entry:last-child { + border: none; +} +.entry:hover, .entry:focus { + background: rgba(255, 255, 255, .33); +} + h1 { text-align: center; } From cb3504c1d79db2af1bc9b9999ecccfe230d47b10 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 23 Jun 2012 23:52:34 +0200 Subject: [PATCH 02/30] Move the Report Buttons into the menu as Report Links. More work on the menu. --- 4chan_x.user.js | 77 +++++++++++++++++++++++++++++-------------------- script.coffee | 64 +++++++++++++++++++++++++--------------- 2 files changed, 86 insertions(+), 55 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 424a3a6ae..4eed52eb7 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -86,7 +86,7 @@ 'Keybinds': [true, 'Binds actions to keys'], 'Time Formatting': [true, 'Arbitrarily formatted timestamps, using your local time'], 'File Info Formatting': [true, 'Reformats the file information'], - 'Report Button': [true, 'Add report buttons'], + 'Report Link': [true, 'Add report links'], 'Delete Button': [false, 'Add delete buttons'], 'Comment Expansion': [true, 'Expand too long comments'], 'Thread Expansion': [true, 'View all replies'], @@ -1114,20 +1114,29 @@ return Menu.open(Main.preParse($.x('ancestor::div[contains(@class,"postContainer")][1]', this))); }, open: function(post) { - var entry, i, _i, _len, _ref; - for (i in post) { - $.add(Menu.el, $.el('code', { - className: 'entry', - textContent: "" + i + ": " + post[i] - })); - } + var el, entry, _i, _len, _ref; + el = Menu.el; + el.setAttribute('data-id', post.ID); + el.setAttribute('data-rootid', post.root.id); _ref = Menu.entries; for (_i = 0, _len = _ref.length; _i < _len; _i++) { entry = _ref[_i]; - $.add(Menu.el, entry.el); - $.addClass(entry.el, 'entry'); + if ((function() { + var requirement, val, _ref1; + _ref1 = entry.requirements; + for (requirement in _ref1) { + val = _ref1[requirement]; + if (val !== post[requirement]) { + return false; + } + } + return true; + })()) { + $.add(el, entry.el); + $.event(entry.el, new CustomEvent('context')); + } } - $.add(d.body, Menu.el); + $.add(d.body, el); return $.on(d, 'click', Menu.close); }, close: function() { @@ -1135,6 +1144,12 @@ Menu.el.innerHTML = null; delete Menu.lastOpener; return $.off(d, 'click', Menu.close); + }, + newEntry: function(name) { + return $.el(name, { + className: 'entry', + tabIndex: 0 + }); } }; @@ -3379,13 +3394,13 @@ })); $.after((isOP ? piM : pi), file); } - $.replace(root.firstChild, pc); + $.replace(root.firstChild, Get.cleanPost(pc)); if (cb) { return cb(); } }, cleanPost: function(root) { - var child, el, now, post, _i, _j, _len, _len1, _ref, _ref1; + var child, el, els, now, post, _i, _j, _len, _len1, _ref; post = $('.post', root); _ref = Array.prototype.slice.call(root.childNodes); for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -3395,9 +3410,10 @@ } } now = Date.now(); - _ref1 = $$('[id]', root); - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - el = _ref1[_j]; + els = $$('[id]', root); + els.push(root); + for (_j = 0, _len1 = els.length; _j < _len1; _j++) { + el = els[_j]; el.id = "" + now + "_" + el.id; } $.rmClass(root, 'forwarded'); @@ -3849,24 +3865,23 @@ ReportButton = { init: function() { - this.a = $.el('a', { - className: 'report_button', - innerHTML: '[ ! ]', - href: 'javascript:;' - }); - return Main.callbacks.push(this.node); - }, - node: function(post) { var a; - if (!(a = $('.report_button', post.el))) { - a = ReportButton.a.cloneNode(true); - $.add($('.postInfo', post.el), a); - } - return $.on(a, 'click', ReportButton.report); + a = Menu.newEntry('a'); + a.href = 'javascript:;'; + a.textContent = 'Report this post'; + $.addClass(a, 'report_button'); + $.on(a, 'click', ReportButton.report); + return Menu.entries.push({ + el: a, + requirements: { + isArchived: false + } + }); }, report: function() { - var id, set, url; - url = "//sys.4chan.org/" + g.BOARD + "/imgboard.php?mode=report&no=" + ($.x('preceding-sibling::input', this).name); + var a, id, set, url; + a = $('.postNum > a[title="Highlight this post"]', $.id(this.parentNode.dataset.rootid)); + url = "//sys.4chan.org/" + (a.pathname.split('/')[1]) + "/imgboard.php?mode=report&no=" + this.parentNode.dataset.id; id = Date.now(); set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200"; return window.open(url, id, set); diff --git a/script.coffee b/script.coffee index ceac5dc77..6aea157b7 100644 --- a/script.coffee +++ b/script.coffee @@ -5,7 +5,7 @@ Config = 'Keybinds': [true, 'Binds actions to keys'] 'Time Formatting': [true, 'Arbitrarily formatted timestamps, using your local time'] 'File Info Formatting': [true, 'Reformats the file information'] - 'Report Button': [true, 'Add report buttons'] + 'Report Link': [true, 'Add report links'] 'Delete Button': [false, 'Add delete buttons'] 'Comment Expansion': [true, 'Expand too long comments'] 'Thread Expansion': [true, 'View all replies'] @@ -824,7 +824,6 @@ Menu = @el = $.el 'div', className: 'reply dialog' id: 'menu' - $.on @el, 'click', (e) -> e.stopPropagation() Main.callbacks.push @node @@ -847,21 +846,31 @@ Menu = # Position s = Menu.el.style + # XXX prevent overflows s.top = @offsetTop + @offsetHeight + 2 + 'px' s.left = @offsetLeft + 'px' Menu.lastOpener = @ Menu.open Main.preParse $.x 'ancestor::div[contains(@class,"postContainer")][1]', @ open: (post) -> - for i of post - $.add Menu.el, $.el 'code', - className: 'entry' - textContent: "#{i}: #{post[i]}" + {el} = Menu + # XXX GM/Scriptish require setAttribute + el.setAttribute 'data-id', post.ID + el.setAttribute 'data-rootid', post.root.id + # for i of post + # $.add Menu.el, $.el 'code', + # className: 'entry' + # textContent: "#{i}: #{post[i]}" for entry in Menu.entries - # if the entry matches this post... - $.add Menu.el, entry.el - $.addClass entry.el, 'entry' # XXX ??? - $.add d.body, Menu.el + if (-> + for requirement, val of entry.requirements + return false if val isnt post[requirement] + true + )() + $.add el, entry.el + # XXX 'context' event? + $.event entry.el, new CustomEvent 'context' + $.add d.body, el $.on d, 'click', Menu.close close: -> $.rm Menu.el @@ -869,6 +878,11 @@ Menu = delete Menu.lastOpener $.off d, 'click', Menu.close + newEntry: (name) -> + $.el name, + className: 'entry' + tabIndex: 0 + Keybinds = init: -> for node in $$ '[accesskey]' @@ -2654,7 +2668,7 @@ Get = innerHTML: "#{if data.media_status isnt " $.after (if isOP then piM else pi), file - $.replace root.firstChild, pc + $.replace root.firstChild, Get.cleanPost pc cb() if cb cleanPost: (root) -> post = $ '.post', root @@ -2663,7 +2677,9 @@ Get = # Don't mess with other features now = Date.now() - for el in $$ '[id]', root + els = $$ '[id]', root + els.push root + for el in els el.id = "#{now}_#{el.id}" $.rmClass root, 'forwarded' @@ -3018,20 +3034,20 @@ DeleteButton = ReportButton = init: -> - @a = $.el 'a', - className: 'report_button' - innerHTML: '[ ! ]' - href: 'javascript:;' - Main.callbacks.push @node - node: (post) -> - unless a = $ '.report_button', post.el - a = ReportButton.a.cloneNode true - $.add $('.postInfo', post.el), a + a = Menu.newEntry 'a' + a.href = 'javascript:;' + a.textContent = 'Report this post' + $.addClass a, 'report_button' $.on a, 'click', ReportButton.report + Menu.entries.push + el: a + requirements: + isArchived: false report: -> - url = "//sys.4chan.org/#{g.BOARD}/imgboard.php?mode=report&no=#{$.x('preceding-sibling::input', @).name}" - id = Date.now() - set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200" + a = $ '.postNum > a[title="Highlight this post"]', $.id @parentNode.dataset.rootid + url = "//sys.4chan.org/#{a.pathname.split('/')[1]}/imgboard.php?mode=report&no=#{@parentNode.dataset.id}" + id = Date.now() + set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200" window.open url, id, set ThreadStats = From f23915b5e78f70ee0f92162cfe6cedffe9eff191 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sat, 23 Jun 2012 23:54:29 +0200 Subject: [PATCH 03/30] Oops, forgot to rename these. --- 4chan_x.user.js | 12 ++++++------ script.coffee | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 4eed52eb7..c42034b81 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -77,7 +77,7 @@ */ (function() { - var $, $$, Anonymize, AutoGif, Conf, Config, DeleteButton, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportButton, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; + var $, $$, Anonymize, AutoGif, Conf, Config, DeleteButton, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; Config = { main: { @@ -3863,14 +3863,14 @@ } }; - ReportButton = { + ReportLink = { init: function() { var a; a = Menu.newEntry('a'); a.href = 'javascript:;'; a.textContent = 'Report this post'; - $.addClass(a, 'report_button'); - $.on(a, 'click', ReportButton.report); + $.addClass(a, 'report_link'); + $.on(a, 'click', this.report); return Menu.entries.push({ el: a, requirements: { @@ -4530,8 +4530,8 @@ if (Conf['Image Hover']) { ImageHover.init(); } - if (Conf['Report Button']) { - ReportButton.init(); + if (Conf['Report Link']) { + ReportLink.init(); } if (Conf['Delete Button']) { DeleteButton.init(); diff --git a/script.coffee b/script.coffee index 6aea157b7..7e900ef64 100644 --- a/script.coffee +++ b/script.coffee @@ -3032,13 +3032,13 @@ DeleteButton = self.innerHTML = '[ Connection error, please retry. ]' $.on self, 'click', DeleteButton.delete -ReportButton = +ReportLink = init: -> a = Menu.newEntry 'a' a.href = 'javascript:;' a.textContent = 'Report this post' - $.addClass a, 'report_button' - $.on a, 'click', ReportButton.report + $.addClass a, 'report_link' + $.on a, 'click', @report Menu.entries.push el: a requirements: @@ -3526,8 +3526,8 @@ Main = if Conf['Image Hover'] ImageHover.init() - if Conf['Report Button'] - ReportButton.init() + if Conf['Report Link'] + ReportLink.init() if Conf['Delete Button'] DeleteButton.init() From 7b22bb4839c57defe609fd93f26976c5fcead560 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 24 Jun 2012 00:22:47 +0200 Subject: [PATCH 04/30] Move the Delete Buttons into the menu as Delete Links. Enable by default. Fix cross-board deleting. --- 4chan_x.user.js | 64 +++++++++++++++++++++++-------------------------- script.coffee | 62 ++++++++++++++++++++++++----------------------- 2 files changed, 62 insertions(+), 64 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index c42034b81..96fbcfe08 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -77,7 +77,7 @@ */ (function() { - var $, $$, Anonymize, AutoGif, Conf, Config, DeleteButton, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; + var $, $$, Anonymize, AutoGif, Conf, Config, DeleteLink, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; Config = { main: { @@ -87,7 +87,7 @@ 'Time Formatting': [true, 'Arbitrarily formatted timestamps, using your local time'], 'File Info Formatting': [true, 'Reformats the file information'], 'Report Link': [true, 'Add report links'], - 'Delete Button': [false, 'Add delete buttons'], + 'Delete Link': [true, 'Add delete links'], 'Comment Expansion': [true, 'Expand too long comments'], 'Thread Expansion': [true, 'View all replies'], 'Index Navigation': [true, 'Navigate to previous / next thread'], @@ -3798,46 +3798,42 @@ } }; - DeleteButton = { + DeleteLink = { init: function() { - this.a = $.el('a', { - className: 'delete_button', - innerHTML: '[ × ]', - href: 'javascript:;' - }); - return Main.callbacks.push(this.node); - }, - node: function(post) { var a; - if (!(a = $('.delete_button', post.el))) { - a = DeleteButton.a.cloneNode(true); - $.add($('.postInfo', post.el), a); - } - return $.on(a, 'click', DeleteButton["delete"]); + a = Menu.newEntry('a'); + a.href = 'javascript:;'; + $.addClass(a, 'delete_link'); + $.on(a, 'context', function() { + a.textContent = 'Delete this post'; + return $.on(a, 'click', DeleteLink["delete"]); + }); + return Menu.entries.push({ + el: a, + requirements: { + isArchived: false + } + }); }, "delete": function() { var board, form, id, m, pwd, self; - $.off(this, 'click', DeleteButton["delete"]); - this.innerHTML = '[ Deleting... ]'; - if (m = d.cookie.match(/4chan_pass=([^;]+)/)) { - pwd = decodeURIComponent(m[1]); - } else { - pwd = $.id('delPassword').value; - } - id = $.x('preceding-sibling::input', this).name; - board = $.x('preceding-sibling::span[1]/a', this).pathname.match(/\w+/)[0]; + $.off(this, 'click', DeleteLink["delete"]); + this.textContent = 'Deleting...'; + pwd = (m = d.cookie.match(/4chan_pass=([^;]+)/)) ? decodeURIComponent(m[1]) : $.id('delPassword').value; + id = this.parentNode.dataset.id; + board = $('.postNum > a[title="Highlight this post"]', $.id(this.parentNode.dataset.rootid)).pathname.split('/')[1]; self = this; form = { mode: 'usrdel', pwd: pwd }; form[id] = 'delete'; - return $.ajax("https://sys.4chan.org/" + board + "/imgboard.php", { + return $.ajax($.id('delform').action.replace("/" + g.BOARD + "/", "/" + board + "/"), { onload: function() { - return DeleteButton.load(self, this.response); + return DeleteLink.load(self, this.response); }, onerror: function() { - return DeleteButton.error(self); + return DeleteLink.error(self); } }, { form: $.formData(form) @@ -3851,15 +3847,15 @@ s = 'Banned!'; } else if (msg = doc.getElementById('errmsg')) { s = msg.textContent; - $.on(self, 'click', DeleteButton["delete"]); + $.on(self, 'click', DeleteLink["delete"]); } else { s = 'Deleted'; } - return self.innerHTML = "[ " + s + " ]"; + return self.textContent = s; }, error: function(self) { - self.innerHTML = '[ Connection error, please retry. ]'; - return $.on(self, 'click', DeleteButton["delete"]); + self.textContent = 'Connection error, please retry.'; + return $.on(self, 'click', DeleteLink["delete"]); } }; @@ -4533,8 +4529,8 @@ if (Conf['Report Link']) { ReportLink.init(); } - if (Conf['Delete Button']) { - DeleteButton.init(); + if (Conf['Delete Link']) { + DeleteLink.init(); } if (Conf['Resurrect Quotes']) { Quotify.init(); diff --git a/script.coffee b/script.coffee index 7e900ef64..99786ad35 100644 --- a/script.coffee +++ b/script.coffee @@ -6,7 +6,7 @@ Config = 'Time Formatting': [true, 'Arbitrarily formatted timestamps, using your local time'] 'File Info Formatting': [true, 'Reformats the file information'] 'Report Link': [true, 'Add report links'] - 'Delete Button': [false, 'Add delete buttons'] + 'Delete Link': [true, 'Add delete links'] 'Comment Expansion': [true, 'Expand too long comments'] 'Thread Expansion': [true, 'View all replies'] 'Index Navigation': [true, 'Navigate to previous / next thread'] @@ -2981,28 +2981,31 @@ Quotify = $.replace node, nodes return -DeleteButton = +DeleteLink = init: -> - @a = $.el 'a', - className: 'delete_button' - innerHTML: '[ × ]' - href: 'javascript:;' - Main.callbacks.push @node - node: (post) -> - unless a = $ '.delete_button', post.el - a = DeleteButton.a.cloneNode true - $.add $('.postInfo', post.el), a - $.on a, 'click', DeleteButton.delete + a = Menu.newEntry 'a' + a.href = 'javascript:;' + $.addClass a, 'delete_link' + $.on a, 'context', -> + a.textContent = 'Delete this post' + $.on a, 'click', DeleteLink.delete + Menu.entries.push + el: a + requirements: + isArchived: false delete: -> - $.off @, 'click', DeleteButton.delete - @innerHTML = '[ Deleting... ]' + $.off @, 'click', DeleteLink.delete + @textContent = 'Deleting...' - if m = d.cookie.match /4chan_pass=([^;]+)/ - pwd = decodeURIComponent m[1] - else - pwd = $.id('delPassword').value - id = $.x('preceding-sibling::input', @).name - board = $.x('preceding-sibling::span[1]/a', @).pathname.match(/\w+/)[0] + pwd = + if m = d.cookie.match /4chan_pass=([^;]+)/ + decodeURIComponent m[1] + else + $.id('delPassword').value + + id = @parentNode.dataset.id + board = $('.postNum > a[title="Highlight this post"]', + $.id @parentNode.dataset.rootid).pathname.split('/')[1] self = this form = @@ -3010,13 +3013,12 @@ DeleteButton = pwd: pwd form[id] = 'delete' - $.ajax "https://sys.4chan.org/#{board}/imgboard.php", { - onload: -> DeleteButton.load self, @response - onerror: -> DeleteButton.error self + $.ajax $.id('delform').action.replace("/#{g.BOARD}/", "/#{board}/"), { + onload: -> DeleteLink.load self, @response + onerror: -> DeleteLink.error self }, { form: $.formData form } - load: (self, html) -> doc = d.implementation.createHTMLDocument '' doc.documentElement.innerHTML = html @@ -3024,13 +3026,13 @@ DeleteButton = s = 'Banned!' else if msg = doc.getElementById 'errmsg' # error! s = msg.textContent - $.on self, 'click', DeleteButton.delete + $.on self, 'click', DeleteLink.delete else s = 'Deleted' - self.innerHTML = "[ #{s} ]" + self.textContent = s error: (self) -> - self.innerHTML = '[ Connection error, please retry. ]' - $.on self, 'click', DeleteButton.delete + self.textContent = 'Connection error, please retry.' + $.on self, 'click', DeleteLink.delete ReportLink = init: -> @@ -3529,8 +3531,8 @@ Main = if Conf['Report Link'] ReportLink.init() - if Conf['Delete Button'] - DeleteButton.init() + if Conf['Delete Link'] + DeleteLink.init() if Conf['Resurrect Quotes'] Quotify.init() From d6068ddc8f57c547600a1248b05483a535d92b25 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 24 Jun 2012 00:26:00 +0200 Subject: [PATCH 05/30] Two shorter selectors. --- 4chan_x.user.js | 4 ++-- script.coffee | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 96fbcfe08..7cc063e91 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -902,7 +902,7 @@ quote.href = "res/" + href; } id = reply.id.slice(2); - link = $('.postInfo > .postNum > a[title="Highlight this post"]', reply); + link = $('.postNum > a[title="Highlight this post"]', reply); link.href = "res/" + threadID + "#p" + id; link.nextSibling.href = "res/" + threadID + "#q" + id; nodes.push(reply); @@ -1374,7 +1374,7 @@ }, qr: function(thread, quote) { if (quote) { - QR.quote.call($('.postInfo > .postNum > a[title="Quote this post"]', $('.post.highlight', thread) || thread)); + QR.quote.call($('.postNum > a[title="Quote this post"]', $('.post.highlight', thread) || thread)); } else { QR.open(); } diff --git a/script.coffee b/script.coffee index 99786ad35..17c00eabe 100644 --- a/script.coffee +++ b/script.coffee @@ -690,7 +690,7 @@ ExpandThread = continue if href[0] is '/' # Cross-board quote quote.href = "res/#{href}" # Fix pathnames id = reply.id[2..] - link = $ '.postInfo > .postNum > a[title="Highlight this post"]', reply + link = $ '.postNum > a[title="Highlight this post"]', reply link.href = "res/#{threadID}#p#{id}" link.nextSibling.href = "res/#{threadID}#q#{id}" nodes.push reply @@ -1014,7 +1014,7 @@ Keybinds = qr: (thread, quote) -> if quote - QR.quote.call $ '.postInfo > .postNum > a[title="Quote this post"]', $('.post.highlight', thread) or thread + QR.quote.call $ '.postNum > a[title="Quote this post"]', $('.post.highlight', thread) or thread else QR.open() $('textarea', QR.el).focus() From 6fcb7253c00f647fbd628a101bc4f7d382c64252 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 24 Jun 2012 00:27:42 +0200 Subject: [PATCH 06/30] Forgot to save the save, have three more shorter selectors. --- 4chan_x.user.js | 6 +++--- script.coffee | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 7cc063e91..a5d48798f 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1529,7 +1529,7 @@ return $.on(d, 'dragstart dragend', QR.drag); }, node: function(post) { - return $.on($('.postInfo > .postNum > a[title="Quote this post"]', post.el), 'click', QR.quote); + return $.on($('.postNum > a[title="Quote this post"]', post.el), 'click', QR.quote); }, open: function() { if (QR.el) { @@ -3236,7 +3236,7 @@ } quote.href = "/" + board + "/res/" + href; } - link = $('.postInfo > .postNum > a[title="Highlight this post"]', pc); + link = $('.postNum > a[title="Highlight this post"]', pc); link.href = "/" + board + "/res/" + threadID + "#p" + postID; link.nextSibling.href = "/" + board + "/res/" + threadID + "#q" + postID; $.replace(root.firstChild, pc); @@ -3770,7 +3770,7 @@ nodes.push($.tn(text)); } id = quote.match(/\d+$/)[0]; - board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : $('.postInfo > .postNum > a[title="Highlight this post"]', post.el).pathname.split('/')[1]; + board = (m = quote.match(/^>>>\/([a-z\d]+)/)) ? m[1] : $('.postNum > a[title="Highlight this post"]', post.el).pathname.split('/')[1]; nodes.push(a = $.el('a', { textContent: "" + quote + "\u00A0(Dead)" })); diff --git a/script.coffee b/script.coffee index 17c00eabe..00b644e39 100644 --- a/script.coffee +++ b/script.coffee @@ -1135,7 +1135,7 @@ QR = $.on d, 'dragstart dragend', QR.drag node: (post) -> - $.on $('.postInfo > .postNum > a[title="Quote this post"]', post.el), 'click', QR.quote + $.on $('.postNum > a[title="Quote this post"]', post.el), 'click', QR.quote open: -> if QR.el @@ -2500,7 +2500,7 @@ Get = href = quote.getAttribute 'href' continue if href[0] is '/' # Cross-board quote, or board link quote.href = "/#{board}/res/#{href}" # Fix pathnames - link = $ '.postInfo > .postNum > a[title="Highlight this post"]', pc + link = $ '.postNum > a[title="Highlight this post"]', pc link.href = "/#{board}/res/#{threadID}#p#{postID}" link.nextSibling.href = "/#{board}/res/#{threadID}#q#{postID}" @@ -2948,7 +2948,7 @@ Quotify = m[1] else # Get the post's board, whether it's inlined or not. - $('.postInfo > .postNum > a[title="Highlight this post"]', post.el).pathname.split('/')[1] + $('.postNum > a[title="Highlight this post"]', post.el).pathname.split('/')[1] nodes.push a = $.el 'a', # \u00A0 is nbsp From 48b3a099cd5296a352bf43e330abba072a625f80 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 24 Jun 2012 22:34:08 +0200 Subject: [PATCH 07/30] Move some Menu things around. --- 4chan_x.user.js | 48 +++++++++++++++++++----------------------------- script.coffee | 39 +++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 51 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index c3dbdf329..63a134d3c 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1121,17 +1121,7 @@ _ref = Menu.entries; for (_i = 0, _len = _ref.length; _i < _len; _i++) { entry = _ref[_i]; - if ((function() { - var requirement, val, _ref1; - _ref1 = entry.requirements; - for (requirement in _ref1) { - val = _ref1[requirement]; - if (val !== post[requirement]) { - return false; - } - } - return true; - })()) { + if (entry.requirement(post)) { $.add(el, entry.el); $.event(entry.el, new CustomEvent('context')); } @@ -1145,11 +1135,9 @@ delete Menu.lastOpener; return $.off(d, 'click', Menu.close); }, - newEntry: function(name) { - return $.el(name, { - className: 'entry', - tabIndex: 0 - }); + addEntry: function(entry) { + $.addClass(entry.el, 'entry'); + return Menu.entries.push(entry); } }; @@ -3811,17 +3799,18 @@ DeleteLink = { init: function() { var a; - a = Menu.newEntry('a'); - a.href = 'javascript:;'; - $.addClass(a, 'delete_link'); + a = $.el('a', { + className: 'delete_link', + href: 'javascript:;' + }); $.on(a, 'context', function() { a.textContent = 'Delete this post'; return $.on(a, 'click', DeleteLink["delete"]); }); - return Menu.entries.push({ + return Menu.addEntry({ el: a, - requirements: { - isArchived: false + requirement: function(post) { + return post.isArchived === false; } }); }, @@ -3872,15 +3861,16 @@ ReportLink = { init: function() { var a; - a = Menu.newEntry('a'); - a.href = 'javascript:;'; - a.textContent = 'Report this post'; - $.addClass(a, 'report_link'); + a = $.el('a', { + className: 'report_link', + href: 'javascript:;', + textContent: 'Report this post' + }); $.on(a, 'click', this.report); - return Menu.entries.push({ + return Menu.addEntry({ el: a, - requirements: { - isArchived: false + requirement: function(post) { + return post.isArchived === false; } }); }, diff --git a/script.coffee b/script.coffee index d4132c8e0..014697cfb 100644 --- a/script.coffee +++ b/script.coffee @@ -862,11 +862,7 @@ Menu = # className: 'entry' # textContent: "#{i}: #{post[i]}" for entry in Menu.entries - if (-> - for requirement, val of entry.requirements - return false if val isnt post[requirement] - true - )() + if entry.requirement post $.add el, entry.el # XXX 'context' event? $.event entry.el, new CustomEvent 'context' @@ -878,10 +874,9 @@ Menu = delete Menu.lastOpener $.off d, 'click', Menu.close - newEntry: (name) -> - $.el name, - className: 'entry' - tabIndex: 0 + addEntry: (entry) -> + $.addClass entry.el, 'entry' + Menu.entries.push entry Keybinds = init: -> @@ -2989,16 +2984,16 @@ Quotify = DeleteLink = init: -> - a = Menu.newEntry 'a' - a.href = 'javascript:;' - $.addClass a, 'delete_link' + a = $.el 'a', + className: 'delete_link' + href: 'javascript:;' $.on a, 'context', -> a.textContent = 'Delete this post' $.on a, 'click', DeleteLink.delete - Menu.entries.push + Menu.addEntry el: a - requirements: - isArchived: false + requirement: (post) -> + post.isArchived is false delete: -> $.off @, 'click', DeleteLink.delete @textContent = 'Deleting...' @@ -3042,15 +3037,15 @@ DeleteLink = ReportLink = init: -> - a = Menu.newEntry 'a' - a.href = 'javascript:;' - a.textContent = 'Report this post' - $.addClass a, 'report_link' + a = $.el 'a', + className: 'report_link' + href: 'javascript:;' + textContent: 'Report this post' $.on a, 'click', @report - Menu.entries.push + Menu.addEntry el: a - requirements: - isArchived: false + requirement: (post) -> + post.isArchived is false report: -> a = $ '.postNum > a[title="Highlight this post"]', $.id @parentNode.dataset.rootid url = "//sys.4chan.org/#{a.pathname.split('/')[1]}/imgboard.php?mode=report&no=#{@parentNode.dataset.id}" From 4dec4bed208346cafaa3da67cda7377cc73fcae7 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 24 Jun 2012 23:25:04 +0200 Subject: [PATCH 08/30] Add post-relevant archive link in the Menu. Close #356 --- 4chan_x.user.js | 42 ++++++++++++++++++++++++++++++++++++------ script.coffee | 28 +++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 63a134d3c..b2824dd0a 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -77,7 +77,7 @@ */ (function() { - var $, $$, Anonymize, AutoGif, Conf, Config, DeleteLink, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; + var $, $$, Anonymize, ArchiveLink, AutoGif, Conf, Config, DeleteLink, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; Config = { main: { @@ -88,6 +88,7 @@ 'File Info Formatting': [true, 'Reformats the file information'], 'Report Link': [true, 'Add report links'], 'Delete Link': [true, 'Add delete links'], + 'Archive Link': [true, 'Add archive links'], 'Comment Expansion': [true, 'Expand too long comments'], 'Thread Expansion': [true, 'View all replies'], 'Index Navigation': [true, 'Navigate to previous / next thread'], @@ -1122,8 +1123,10 @@ for (_i = 0, _len = _ref.length; _i < _len; _i++) { entry = _ref[_i]; if (entry.requirement(post)) { + if (typeof entry.open === "function") { + entry.open(post); + } $.add(el, entry.el); - $.event(entry.el, new CustomEvent('context')); } } $.add(d.body, el); @@ -3803,12 +3806,12 @@ className: 'delete_link', href: 'javascript:;' }); - $.on(a, 'context', function() { - a.textContent = 'Delete this post'; - return $.on(a, 'click', DeleteLink["delete"]); - }); return Menu.addEntry({ el: a, + open: function() { + a.textContent = 'Delete this post'; + return $.on(a, 'click', DeleteLink["delete"]); + }, requirement: function(post) { return post.isArchived === false; } @@ -3884,6 +3887,30 @@ } }; + ArchiveLink = { + init: function() { + var a; + a = $.el('a', { + className: 'archive_link', + target: '_blank' + }); + return Menu.addEntry({ + el: a, + open: function(post) { + var path; + path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split('/'); + a.href = Redirect.thread(path[1], path[3], post.ID); + return a.textContent = "Archived post No." + post.ID; + }, + requirement: function(post) { + var path; + path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split('/'); + return Redirect.thread(path[1], path[3]) !== ("//boards.4chan.org/" + path[1] + "/"); + } + }); + } + }; + ThreadStats = { init: function() { var dialog; @@ -4532,6 +4559,9 @@ if (Conf['Delete Link']) { DeleteLink.init(); } + if (Conf['Archive Link']) { + ArchiveLink.init(); + } if (Conf['Resurrect Quotes']) { Quotify.init(); } diff --git a/script.coffee b/script.coffee index 014697cfb..f5fe96525 100644 --- a/script.coffee +++ b/script.coffee @@ -7,6 +7,7 @@ Config = 'File Info Formatting': [true, 'Reformats the file information'] 'Report Link': [true, 'Add report links'] 'Delete Link': [true, 'Add delete links'] + 'Archive Link': [true, 'Add archive links'] 'Comment Expansion': [true, 'Expand too long comments'] 'Thread Expansion': [true, 'View all replies'] 'Index Navigation': [true, 'Navigate to previous / next thread'] @@ -863,9 +864,8 @@ Menu = # textContent: "#{i}: #{post[i]}" for entry in Menu.entries if entry.requirement post + entry.open? post $.add el, entry.el - # XXX 'context' event? - $.event entry.el, new CustomEvent 'context' $.add d.body, el $.on d, 'click', Menu.close close: -> @@ -2987,11 +2987,11 @@ DeleteLink = a = $.el 'a', className: 'delete_link' href: 'javascript:;' - $.on a, 'context', -> - a.textContent = 'Delete this post' - $.on a, 'click', DeleteLink.delete Menu.addEntry el: a + open: -> + a.textContent = 'Delete this post' + $.on a, 'click', DeleteLink.delete requirement: (post) -> post.isArchived is false delete: -> @@ -3053,6 +3053,21 @@ ReportLink = set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200" window.open url, id, set +ArchiveLink = + init: -> + a = $.el 'a', + className: 'archive_link' + target: '_blank' + Menu.addEntry + el: a + open: (post) -> + path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split '/' + a.href = Redirect.thread path[1], path[3], post.ID + a.textContent = "Archived post No.#{post.ID}" + requirement: (post) -> + path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split '/' + Redirect.thread(path[1], path[3]) isnt "//boards.4chan.org/#{path[1]}/" + ThreadStats = init: -> dialog = UI.dialog 'stats', 'bottom: 0; left: 0;', '
0 / 0
' @@ -3535,6 +3550,9 @@ Main = if Conf['Delete Link'] DeleteLink.init() + if Conf['Archive Link'] + ArchiveLink.init() + if Conf['Resurrect Quotes'] Quotify.init() From faef1520209a99ea1852f8df05bf9c6c52ab73d6 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 24 Jun 2012 23:42:41 +0200 Subject: [PATCH 09/30] Move some confs around, add conf to toggle the drop-down menu. --- 4chan_x.user.js | 29 +++++++++++++++++------------ script.coffee | 25 ++++++++++++++----------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index b2824dd0a..c0f4be73f 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -86,9 +86,6 @@ 'Keybinds': [true, 'Binds actions to keys'], 'Time Formatting': [true, 'Arbitrarily formatted timestamps, using your local time'], 'File Info Formatting': [true, 'Reformats the file information'], - 'Report Link': [true, 'Add report links'], - 'Delete Link': [true, 'Add delete links'], - 'Archive Link': [true, 'Add archive links'], 'Comment Expansion': [true, 'Expand too long comments'], 'Thread Expansion': [true, 'View all replies'], 'Index Navigation': [true, 'Navigate to previous / next thread'], @@ -111,6 +108,12 @@ 'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail'], 'Expand From Current': [false, 'Expand images from current position to thread end.'] }, + Menu: { + 'Menu': [true, 'Add a drop-down menu in posts.'], + 'Report Link': [true, 'Add a report link to the menu.'], + 'Delete Link': [true, 'Add a delete link to the menu.'], + 'Archive Link': [true, 'Add a archive link to the menu.'] + }, Monitoring: { 'Thread Updater': [true, 'Update threads. Has more options in its own dialog.'], 'Unread Count': [true, 'Show unread post count in tab title'], @@ -4534,7 +4537,6 @@ if (Conf['Anonymize']) { Anonymize.init(); } - Menu.init(); if (Conf['Time Formatting']) { Time.init(); } @@ -4553,14 +4555,17 @@ if (Conf['Image Hover']) { ImageHover.init(); } - if (Conf['Report Link']) { - ReportLink.init(); - } - if (Conf['Delete Link']) { - DeleteLink.init(); - } - if (Conf['Archive Link']) { - ArchiveLink.init(); + if (Conf['Menu']) { + Menu.init(); + if (Conf['Report Link']) { + ReportLink.init(); + } + if (Conf['Delete Link']) { + DeleteLink.init(); + } + if (Conf['Archive Link']) { + ArchiveLink.init(); + } } if (Conf['Resurrect Quotes']) { Quotify.init(); diff --git a/script.coffee b/script.coffee index f5fe96525..d94d530e5 100644 --- a/script.coffee +++ b/script.coffee @@ -5,9 +5,6 @@ Config = 'Keybinds': [true, 'Binds actions to keys'] 'Time Formatting': [true, 'Arbitrarily formatted timestamps, using your local time'] 'File Info Formatting': [true, 'Reformats the file information'] - 'Report Link': [true, 'Add report links'] - 'Delete Link': [true, 'Add delete links'] - 'Archive Link': [true, 'Add archive links'] 'Comment Expansion': [true, 'Expand too long comments'] 'Thread Expansion': [true, 'View all replies'] 'Index Navigation': [true, 'Navigate to previous / next thread'] @@ -27,6 +24,11 @@ Config = 'Sauce': [true, 'Add sauce to images'] 'Reveal Spoilers': [false, 'Replace spoiler thumbnails by the original thumbnail'] 'Expand From Current': [false, 'Expand images from current position to thread end.'] + Menu: + 'Menu': [true, 'Add a drop-down menu in posts.'] + 'Report Link': [true, 'Add a report link to the menu.'] + 'Delete Link': [true, 'Add a delete link to the menu.'] + 'Archive Link': [true, 'Add a archive link to the menu.'] Monitoring: 'Thread Updater': [true, 'Update threads. Has more options in its own dialog.'] 'Unread Count': [true, 'Show unread post count in tab title'] @@ -3524,8 +3526,6 @@ Main = if Conf['Anonymize'] Anonymize.init() - Menu.init() - if Conf['Time Formatting'] Time.init() @@ -3544,14 +3544,17 @@ Main = if Conf['Image Hover'] ImageHover.init() - if Conf['Report Link'] - ReportLink.init() + if Conf['Menu'] + Menu.init() - if Conf['Delete Link'] - DeleteLink.init() + if Conf['Report Link'] + ReportLink.init() - if Conf['Archive Link'] - ArchiveLink.init() + if Conf['Delete Link'] + DeleteLink.init() + + if Conf['Archive Link'] + ArchiveLink.init() if Conf['Resurrect Quotes'] Quotify.init() From 909e8dc4d95cd4bae089f50d84073f7419aff350 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 25 Jun 2012 13:00:22 +0200 Subject: [PATCH 10/30] Fix Menu position on Firefox. --- 4chan_x.user.js | 7 ++++--- script.coffee | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index c0f4be73f..488373617 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1101,7 +1101,7 @@ return $.on(a, 'click', Menu.toggle); }, toggle: function(e) { - var lastOpener, s; + var lastOpener, rect, s; e.preventDefault(); e.stopPropagation(); if (Menu.el.parentNode) { @@ -1112,8 +1112,9 @@ } } s = Menu.el.style; - s.top = this.offsetTop + this.offsetHeight + 2 + 'px'; - s.left = this.offsetLeft + 'px'; + rect = this.getBoundingClientRect(); + s.top = d.documentElement.scrollTop + d.body.scrollTop + rect.top + rect.height + 2 + 'px'; + s.left = d.documentElement.scrollLeft + d.body.scrollLeft + rect.left + 'px'; Menu.lastOpener = this; return Menu.open(Main.preParse($.x('ancestor::div[contains(@class,"postContainer")][1]', this))); }, diff --git a/script.coffee b/script.coffee index d94d530e5..a2ff84de5 100644 --- a/script.coffee +++ b/script.coffee @@ -850,8 +850,9 @@ Menu = # Position s = Menu.el.style # XXX prevent overflows - s.top = @offsetTop + @offsetHeight + 2 + 'px' - s.left = @offsetLeft + 'px' + rect = @getBoundingClientRect() + s.top = d.documentElement.scrollTop + d.body.scrollTop + rect.top + rect.height + 2 + 'px' + s.left = d.documentElement.scrollLeft + d.body.scrollLeft + rect.left + 'px' Menu.lastOpener = @ Menu.open Main.preParse $.x 'ancestor::div[contains(@class,"postContainer")][1]', @ From c8176435fb62a88b615fb024b15bcfb5c721a342 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 26 Jun 2012 17:51:02 +0200 Subject: [PATCH 11/30] =?UTF-8?q?Replace=20×=20and=20\u00d7=20by=20?= =?UTF-8?q?=C3=97.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 4chan_x.user.js | 14 +++++++------- script.coffee | 17 +++++++---------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 4d3b4036b..66a42e77a 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -850,13 +850,13 @@ a = $('.summary', thread); switch (a.textContent[0]) { case '+': - a.textContent = a.textContent.replace('+', '\u00d7 Loading...'); + a.textContent = a.textContent.replace('+', '× Loading...'); $.cache(pathname, function() { return ExpandThread.parse(this, thread, a); }); break; - case '\u00d7': - a.textContent = a.textContent.replace('\u00d7 Loading...', '+'); + case '×': + a.textContent = a.textContent.replace('× Loading...', '+'); $.cache.requests[pathname].abort(); break; case '-': @@ -887,7 +887,7 @@ $.off(a, 'click', ExpandThread.cb.toggle); return; } - a.textContent = a.textContent.replace('\u00d7 Loading...', '-'); + a.textContent = a.textContent.replace('× Loading...', '-'); doc = d.implementation.createHTMLDocument(''); doc.documentElement.innerHTML = req.response; threadID = thread.id.slice(1); @@ -1770,7 +1770,7 @@ className: 'thumbnail', draggable: true, href: 'javascript:;', - innerHTML: '×' + innerHTML: '×' }); $('input', this.el).checked = this.spoiler; $.on(this.el, 'click', function() { @@ -2052,7 +2052,7 @@ QR.el = UI.dialog('qr', 'top:0;right:0;', '\
\ Quick Reply \ - ×\ + ×\
\
\
\ @@ -2834,7 +2834,7 @@ for (id in _ref) { props = _ref[id]; x = $.el('a', { - textContent: '\u00d7', + textContent: '×', href: 'javascript:;' }); $.on(x, 'click', Watcher.cb.x); diff --git a/script.coffee b/script.coffee index eaf950afb..71134d244 100644 --- a/script.coffee +++ b/script.coffee @@ -649,15 +649,13 @@ ExpandThread = pathname = "/#{g.BOARD}/res/#{thread.id[1..]}" a = $ '.summary', thread - # \u00d7 is × - switch a.textContent[0] when '+' - a.textContent = a.textContent.replace '+', '\u00d7 Loading...' + a.textContent = a.textContent.replace '+', '× Loading...' $.cache pathname, -> ExpandThread.parse @, thread, a - when '\u00d7' - a.textContent = a.textContent.replace '\u00d7 Loading...', '+' + when '×' + a.textContent = a.textContent.replace '× Loading...', '+' $.cache.requests[pathname].abort() when '-' @@ -679,7 +677,7 @@ ExpandThread = $.off a, 'click', ExpandThread.cb.toggle return - a.textContent = a.textContent.replace '\u00d7 Loading...', '-' + a.textContent = a.textContent.replace '× Loading...', '-' doc = d.implementation.createHTMLDocument '' doc.documentElement.innerHTML = req.response @@ -1328,7 +1326,7 @@ QR = className: 'thumbnail' draggable: true href: 'javascript:;' - innerHTML: '×' + innerHTML: '×' $('input', @el).checked = @spoiler $.on @el, 'click', => @select() $.on $('.remove', @el), 'click', (e) => @@ -1533,7 +1531,7 @@ QR = QR.el = UI.dialog 'qr', 'top:0;right:0;', '
Quick Reply - × + ×
@@ -2233,8 +2231,7 @@ Watcher = for board of watched for id, props of watched[board] x = $.el 'a', - # \u00d7 is × - textContent: '\u00d7' + textContent: '×' href: 'javascript:;' $.on x, 'click', Watcher.cb.x link = $.el 'a', props From 5f39f374c8f40978277ad0f3709b2eeeb084bbdc Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 26 Jun 2012 21:20:49 +0200 Subject: [PATCH 12/30] The menu should always be entirely visible on click. --- 4chan_x.user.js | 27 ++++++++++++++++----------- script.coffee | 36 ++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 66a42e77a..a0651bc4b 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1101,7 +1101,7 @@ return $.on(a, 'click', Menu.toggle); }, toggle: function(e) { - var lastOpener, rect, s; + var lastOpener; e.preventDefault(); e.stopPropagation(); if (Menu.el.parentNode) { @@ -1111,15 +1111,11 @@ return; } } - s = Menu.el.style; - rect = this.getBoundingClientRect(); - s.top = d.documentElement.scrollTop + d.body.scrollTop + rect.top + rect.height + 2 + 'px'; - s.left = d.documentElement.scrollLeft + d.body.scrollLeft + rect.left + 'px'; Menu.lastOpener = this; - return Menu.open(Main.preParse($.x('ancestor::div[contains(@class,"postContainer")][1]', this))); + return Menu.open(this, Main.preParse($.x('ancestor::div[contains(@class,"postContainer")][1]', this))); }, - open: function(post) { - var el, entry, _i, _len, _ref; + open: function(button, post) { + var bLeft, bRect, bTop, el, entry, mRect, _i, _len, _ref; el = Menu.el; el.setAttribute('data-id', post.ID); el.setAttribute('data-rootid', post.root.id); @@ -1133,12 +1129,21 @@ $.add(el, entry.el); } } + $.on(d, 'click', Menu.close); $.add(d.body, el); - return $.on(d, 'click', Menu.close); + mRect = el.getBoundingClientRect(); + bRect = button.getBoundingClientRect(); + bTop = d.documentElement.scrollTop + d.body.scrollTop + bRect.top; + bLeft = d.documentElement.scrollLeft + d.body.scrollLeft + bRect.left; + el.style.top = bRect.top + bRect.height + mRect.height < d.documentElement.clientHeight ? bTop + bRect.height + 2 + 'px' : bTop - mRect.height - 2 + 'px'; + return el.style.left = bRect.left + mRect.width < d.documentElement.clientWidth ? bLeft + 'px' : bLeft + bRect.width - mRect.width + 'px'; }, close: function() { - $.rm(Menu.el); - Menu.el.innerHTML = null; + var el; + el = Menu.el; + $.rm(el); + el.innerHTML = null; + el.removeAttribute('style'); delete Menu.lastOpener; return $.off(d, 'click', Menu.close); }, diff --git a/script.coffee b/script.coffee index 71134d244..3b4afdee3 100644 --- a/script.coffee +++ b/script.coffee @@ -845,16 +845,9 @@ Menu = Menu.close() return if lastOpener is @ - # Position - s = Menu.el.style - # XXX prevent overflows - rect = @getBoundingClientRect() - s.top = d.documentElement.scrollTop + d.body.scrollTop + rect.top + rect.height + 2 + 'px' - s.left = d.documentElement.scrollLeft + d.body.scrollLeft + rect.left + 'px' - Menu.lastOpener = @ - Menu.open Main.preParse $.x 'ancestor::div[contains(@class,"postContainer")][1]', @ - open: (post) -> + Menu.open @, Main.preParse $.x 'ancestor::div[contains(@class,"postContainer")][1]', @ + open: (button, post) -> {el} = Menu # XXX GM/Scriptish require setAttribute el.setAttribute 'data-id', post.ID @@ -867,11 +860,30 @@ Menu = if entry.requirement post entry.open? post $.add el, entry.el - $.add d.body, el + $.on d, 'click', Menu.close + $.add d.body, el + + # Position + mRect = el.getBoundingClientRect() + bRect = button.getBoundingClientRect() + bTop = d.documentElement.scrollTop + d.body.scrollTop + bRect.top + bLeft = d.documentElement.scrollLeft + d.body.scrollLeft + bRect.left + el.style.top = + if bRect.top + bRect.height + mRect.height < d.documentElement.clientHeight + bTop + bRect.height + 2 + 'px' + else + bTop - mRect.height - 2 + 'px' + el.style.left = + if bRect.left + mRect.width < d.documentElement.clientWidth + bLeft + 'px' + else + bLeft + bRect.width - mRect.width + 'px' close: -> - $.rm Menu.el - Menu.el.innerHTML = null + {el} = Menu + $.rm el + el.innerHTML = null + el.removeAttribute 'style' delete Menu.lastOpener $.off d, 'click', Menu.close From e5149a0af276c39ce0ce6a0f3a936c9cc63260a3 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 27 Jun 2012 17:45:07 +0200 Subject: [PATCH 13/30] Some sort of keybind navigation for the menu. --- 4chan_x.user.js | 73 +++++++++++++++++++++++++++++++++++++++++++++---- script.coffee | 50 +++++++++++++++++++++++++++++++-- 2 files changed, 115 insertions(+), 8 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index a0651bc4b..7a8d4eb84 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1085,11 +1085,13 @@ }); this.el = $.el('div', { className: 'reply dialog', - id: 'menu' + id: 'menu', + tabIndex: 0 }); $.on(this.el, 'click', function(e) { return e.stopPropagation(); }); + $.on(this.el, 'keydown', this.keybinds); return Main.callbacks.push(this.node); }, node: function(post) { @@ -1129,6 +1131,7 @@ $.add(el, entry.el); } } + $.addClass($('.entry', Menu.el), 'focused'); $.on(d, 'click', Menu.close); $.add(d.body, el); mRect = el.getBoundingClientRect(); @@ -1136,19 +1139,77 @@ bTop = d.documentElement.scrollTop + d.body.scrollTop + bRect.top; bLeft = d.documentElement.scrollLeft + d.body.scrollLeft + bRect.left; el.style.top = bRect.top + bRect.height + mRect.height < d.documentElement.clientHeight ? bTop + bRect.height + 2 + 'px' : bTop - mRect.height - 2 + 'px'; - return el.style.left = bRect.left + mRect.width < d.documentElement.clientWidth ? bLeft + 'px' : bLeft + bRect.width - mRect.width + 'px'; + el.style.left = bRect.left + mRect.width < d.documentElement.clientWidth ? bLeft + 'px' : bLeft + bRect.width - mRect.width + 'px'; + return el.focus(); }, close: function() { - var el; + var el, focused; el = Menu.el; $.rm(el); + if (focused = $('.focused.entry', el)) { + $.rmClass(focused, 'focused'); + } el.innerHTML = null; el.removeAttribute('style'); delete Menu.lastOpener; return $.off(d, 'click', Menu.close); }, + keybinds: function(e) { + var el, next; + el = $('.focused.entry', Menu.el); + switch (Keybinds.keyCode(e) || e.keyCode) { + case 'Esc': + Menu.lastOpener.focus(); + Menu.close(); + break; + case 13: + case 32: + el.click(); + break; + case 'Up': + if (next = el.previousElementSibling) { + Menu.focus(next); + } + break; + case 'Right': + if (next = el.firstElementChild) { + Menu.focus(next); + } + break; + case 'Down': + if (next = el.nextElementSibling) { + Menu.focus(next); + } + break; + case 'Left': + if ((next = el.parentNode) && next.id !== 'menu') { + Menu.focus(next); + } + break; + default: + return; + } + e.preventDefault(); + return e.stopPropagation(); + }, + focus: function(el) { + var focused; + if (focused = $('.focused.entry', Menu.el)) { + $.rmClass(focused, 'focused'); + } + return $.addClass(el, 'focused'); + }, addEntry: function(entry) { - $.addClass(entry.el, 'entry'); + var el, els, _i, _len; + els = $$('*', entry.el); + els.push(entry.el); + for (_i = 0, _len = els.length; _i < _len; _i++) { + el = els[_i]; + $.addClass(el, 'entry'); + $.on(el, 'focus mouseover', function() { + return Menu.focus(this); + }); + } return Menu.entries.push(entry); } }; @@ -4865,16 +4926,18 @@ a[href="javascript:;"] {\ \ #menu {\ position: absolute;\ + outline: none;\ }\ .entry {\ border-bottom: 1px solid rgba(0, 0, 0, .25);\ display: block;\ + outline: none;\ padding: 3px 4px;\ }\ .entry:last-child {\ border: none;\ }\ -.entry:hover, .entry:focus {\ +.focused.entry {\ background: rgba(255, 255, 255, .33);\ }\ \ diff --git a/script.coffee b/script.coffee index 3b4afdee3..f6d9b1ebf 100644 --- a/script.coffee +++ b/script.coffee @@ -825,7 +825,9 @@ Menu = @el = $.el 'div', className: 'reply dialog' id: 'menu' - $.on @el, 'click', (e) -> e.stopPropagation() + tabIndex: 0 + $.on @el, 'click', (e) -> e.stopPropagation() + $.on @el, 'keydown', @keybinds Main.callbacks.push @node node: (post) -> @@ -861,6 +863,7 @@ Menu = entry.open? post $.add el, entry.el + $.addClass $('.entry', Menu.el), 'focused' $.on d, 'click', Menu.close $.add d.body, el @@ -879,16 +882,55 @@ Menu = bLeft + 'px' else bLeft + bRect.width - mRect.width + 'px' + + el.focus() close: -> {el} = Menu $.rm el + if focused = $ '.focused.entry', el + $.rmClass focused, 'focused' el.innerHTML = null el.removeAttribute 'style' delete Menu.lastOpener $.off d, 'click', Menu.close + keybinds: (e) -> + el = $ '.focused.entry', Menu.el + + switch Keybinds.keyCode(e) or e.keyCode + when 'Esc' + Menu.lastOpener.focus() + Menu.close() + when 13, 32 # 'Enter', 'Space' + el.click() + when 'Up' + if next = el.previousElementSibling + Menu.focus next + when 'Right' + if next = el.firstElementChild + Menu.focus next + when 'Down' + if next = el.nextElementSibling + Menu.focus next + when 'Left' + if (next = el.parentNode) and next.id isnt 'menu' + Menu.focus next + else + return + + e.preventDefault() + e.stopPropagation() + focus: (el) -> + if focused = $ '.focused.entry', Menu.el + $.rmClass focused, 'focused' + $.addClass el, 'focused' + addEntry: (entry) -> - $.addClass entry.el, 'entry' + els = $$ '*', entry.el + els.push entry.el + for el in els + $.addClass el, 'entry' + $.on el, 'focus mouseover', -> Menu.focus @ Menu.entries.push entry Keybinds = @@ -3789,16 +3831,18 @@ a[href="javascript:;"] { #menu { position: absolute; + outline: none; } .entry { border-bottom: 1px solid rgba(0, 0, 0, .25); display: block; + outline: none; padding: 3px 4px; } .entry:last-child { border: none; } -.entry:hover, .entry:focus { +.focused.entry { background: rgba(255, 255, 255, .33); } From 5430a1198b58ae8f259f7c53fd72e9efc91cf72d Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 27 Jun 2012 18:19:14 +0200 Subject: [PATCH 14/30] Close #567 --- 4chan_x.user.js | 10 ++++++++-- script.coffee | 18 ++++++++---------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 7a8d4eb84..a78d7d6db 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -4325,10 +4325,13 @@ } url = "//images.4chan.org/" + src[3] + "/src/" + src[5] + "?" + (Date.now()); } + if ($.engine !== 'webkit' && url.split('/')[2] === 'images.4chan.org') { + return; + } timeoutID = setTimeout((function() { return _this.src = url; }), 3000); - if (!($.engine === 'webkit' && src[2] === 'images.4chan.org')) { + if ($.engine !== 'webkit' || url.split('/')[2] !== 'images.4chan.org') { return; } return $.ajax(url, { @@ -4510,8 +4513,11 @@ } url = "//images.4chan.org/" + src[3] + "/src/" + src[5] + "?" + (Date.now()); } + if ($.engine !== 'webkit' && url.split('/')[2] === 'images.4chan.org') { + return; + } timeoutID = setTimeout(ImageExpand.expand, 10000, thumb, url); - if (!($.engine === 'webkit' && url.split('/')[2] === 'images.4chan.org')) { + if ($.engine !== 'webkit' || url.split('/')[2] !== 'images.4chan.org') { return; } return $.ajax(url, { diff --git a/script.coffee b/script.coffee index f6d9b1ebf..61078555e 100644 --- a/script.coffee +++ b/script.coffee @@ -3368,14 +3368,13 @@ ImageHover = src = @src.replace(/\?\d+$/, '').split '/' unless src[2] is 'images.4chan.org' and url = Redirect.image src[3], src[5] return if g.dead - # CloudFlare may cache banned pages instead of images. # This will fool CloudFlare's cache. url = "//images.4chan.org/#{src[3]}/src/#{src[5]}?#{Date.now()}" - # navigator.online is not x-browser/os yet + return if $.engine isnt 'webkit' and url.split('/')[2] is 'images.4chan.org' timeoutID = setTimeout (=> @src = url), 3000 - # Only Chrome let userscript break through cross domain requests. - # Don't check it 404s in the archivers. - return unless $.engine is 'webkit' and src[2] is 'images.4chan.org' + # Only Chrome let userscripts do cross domain requests. + # Don't check for 404'd status in the archivers. + return if $.engine isnt 'webkit' or url.split('/')[2] isnt 'images.4chan.org' $.ajax url, onreadystatechange: (-> clearTimeout timeoutID if @status is 404), type: 'head' mouseout: -> @@ -3489,14 +3488,13 @@ ImageExpand = src = @src.replace(/\?\d+$/, '').split '/' unless src[2] is 'images.4chan.org' and url = Redirect.image src[3], src[5] return if g.dead - # CloudFlare may cache banned pages instead of images. # This will fool CloudFlare's cache. url = "//images.4chan.org/#{src[3]}/src/#{src[5]}?#{Date.now()}" - #navigator.online is not x-browser/os yet + return if $.engine isnt 'webkit' and url.split('/')[2] is 'images.4chan.org' timeoutID = setTimeout ImageExpand.expand, 10000, thumb, url - # Only Chrome let userscript break through cross domain requests. - # Don't check it 404s in the archivers. - return unless $.engine is 'webkit' and url.split('/')[2] is 'images.4chan.org' + # Only Chrome let userscripts do cross domain requests. + # Don't check for 404'd status in the archivers. + return if $.engine isnt 'webkit' or url.split('/')[2] isnt 'images.4chan.org' $.ajax url, onreadystatechange: (-> clearTimeout timeoutID if @status is 404), type: 'head' From 469b60a4c539b9692fe77da9acc9ffff082e42f0 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 27 Jun 2012 22:23:47 +0200 Subject: [PATCH 15/30] Add a menu button next to stubs. Close #502. --- 4chan_x.user.js | 40 ++++++++++++++++++++++++++-------------- script.coffee | 36 +++++++++++++++++++++++++----------- 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index a78d7d6db..ea228dc50 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -947,7 +947,7 @@ } }, cb: function() { - return ThreadHiding.toggle(this.parentNode); + return ThreadHiding.toggle($.x('ancestor::div[parent::div[@class="board"]]', this)); }, toggle: function(thread) { var hiddenThreads, id; @@ -963,7 +963,7 @@ return $.set("hiddenThreads/" + g.BOARD + "/", hiddenThreads); }, hide: function(thread, show_stub) { - var a, num, opInfo, span, text; + var a, menuButton, num, opInfo, span, stub, text; if (show_stub == null) { show_stub = Conf['Show Stubs']; } @@ -982,19 +982,24 @@ num += $$('.opContainer ~ .replyContainer', thread).length; text = num === 1 ? '1 reply' : "" + num + " replies"; opInfo = $('.op > .postInfo > .nameBlock', thread).textContent; - a = $.el('a', { + stub = $.el('div', { className: 'hide_thread_button hidden_thread', - innerHTML: '[ + ]', - href: 'javascript:;' + innerHTML: '[ + ] ' }); - $.add(a, $.tn(" " + opInfo + " (" + text + ")")); + a = stub.firstChild; $.on(a, 'click', ThreadHiding.cb); - return $.prepend(thread, a); + $.add(a, $.tn("" + opInfo + " (" + text + ")")); + if (Conf['Menu']) { + menuButton = Menu.a.cloneNode(true); + $.on(menuButton, 'click', Menu.toggle); + $.add(stub, [$.tn(' '), menuButton]); + } + return $.prepend(thread, stub); }, show: function(thread) { - var a; - if (a = $('.hidden_thread', thread)) { - $.rm(a); + var stub; + if (stub = $('.hidden_thread', thread)) { + $.rm(stub); } thread.hidden = false; return thread.nextElementSibling.hidden = false; @@ -1042,7 +1047,7 @@ return $.set("hiddenReplies/" + g.BOARD + "/", g.hiddenReplies); }, hide: function(root, show_stub) { - var a, el, side, stub; + var a, el, menuButton, side, stub; if (show_stub == null) { show_stub = Conf['Show Stubs']; } @@ -1061,8 +1066,13 @@ innerHTML: '[ + ] ' }); a = stub.firstChild; - $.add(a, $.tn($('.nameBlock', el).textContent)); $.on(a, 'click', ReplyHiding.toggle); + $.add(a, $.tn($('.nameBlock', el).textContent)); + if (Conf['Menu']) { + menuButton = Menu.a.cloneNode(true); + $.on(menuButton, 'click', Menu.toggle); + $.add(stub, [$.tn(' '), menuButton]); + } return $.prepend(root, stub); }, show: function(root) { @@ -1103,7 +1113,7 @@ return $.on(a, 'click', Menu.toggle); }, toggle: function(e) { - var lastOpener; + var lastOpener, post; e.preventDefault(); e.stopPropagation(); if (Menu.el.parentNode) { @@ -1114,7 +1124,9 @@ } } Menu.lastOpener = this; - return Menu.open(this, Main.preParse($.x('ancestor::div[contains(@class,"postContainer")][1]', this))); + post = /\bhidden_thread\b/.test(this.parentNode.className) ? $.x('ancestor::div[parent::div[@class="board"]]/child::div[contains(@class,"opContainer")]', this) : $.x('ancestor::div[contains(@class,"postContainer")][1]', this); + $.log(postContainer); + return Menu.open(this, Main.preParse(post)); }, open: function(button, post) { var bLeft, bRect, bTop, el, entry, mRect, _i, _len, _ref; diff --git a/script.coffee b/script.coffee index 61078555e..6bb23c206 100644 --- a/script.coffee +++ b/script.coffee @@ -719,7 +719,7 @@ ThreadHiding = return cb: -> - ThreadHiding.toggle @parentNode + ThreadHiding.toggle $.x 'ancestor::div[parent::div[@class="board"]]', @ toggle: (thread) -> hiddenThreads = $.get "hiddenThreads/#{g.BOARD}/", {} @@ -747,17 +747,21 @@ ThreadHiding = text = if num is 1 then '1 reply' else "#{num} replies" opInfo = $('.op > .postInfo > .nameBlock', thread).textContent - a = $.el 'a', + stub = $.el 'div', className: 'hide_thread_button hidden_thread' - innerHTML: '[ + ]' - href: 'javascript:;' - $.add a, $.tn " #{opInfo} (#{text})" - $.on a, 'click', ThreadHiding.cb - $.prepend thread, a + innerHTML: '[ + ] ' + a = stub.firstChild + $.on a, 'click', ThreadHiding.cb + $.add a, $.tn "#{opInfo} (#{text})" + if Conf['Menu'] + menuButton = Menu.a.cloneNode true + $.on menuButton, 'click', Menu.toggle + $.add stub, [$.tn(' '), menuButton] + $.prepend thread, stub show: (thread) -> - if a = $ '.hidden_thread', thread - $.rm a + if stub = $ '.hidden_thread', thread + $.rm stub thread.hidden = false thread.nextElementSibling.hidden = false @@ -805,8 +809,12 @@ ReplyHiding = className: 'hide_reply_button stub' innerHTML: '[ + ] ' a = stub.firstChild - $.add a, $.tn $('.nameBlock', el).textContent $.on a, 'click', ReplyHiding.toggle + $.add a, $.tn $('.nameBlock', el).textContent + if Conf['Menu'] + menuButton = Menu.a.cloneNode true + $.on menuButton, 'click', Menu.toggle + $.add stub, [$.tn(' '), menuButton] $.prepend root, stub show: (root) -> @@ -848,7 +856,13 @@ Menu = return if lastOpener is @ Menu.lastOpener = @ - Menu.open @, Main.preParse $.x 'ancestor::div[contains(@class,"postContainer")][1]', @ + post = + if /\bhidden_thread\b/.test @parentNode.className + $.x 'ancestor::div[parent::div[@class="board"]]/child::div[contains(@class,"opContainer")]', @ + else + $.x 'ancestor::div[contains(@class,"postContainer")][1]', @ + $.log postContainer + Menu.open @, Main.preParse post open: (button, post) -> {el} = Menu # XXX GM/Scriptish require setAttribute From 8dd9300541f285187502d9993fea77213193243b Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 27 Jun 2012 22:43:56 +0200 Subject: [PATCH 16/30] remove a $.log, shorter archive link text. --- 4chan_x.user.js | 3 +-- script.coffee | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index ea228dc50..17d614004 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1125,7 +1125,6 @@ } Menu.lastOpener = this; post = /\bhidden_thread\b/.test(this.parentNode.className) ? $.x('ancestor::div[parent::div[@class="board"]]/child::div[contains(@class,"opContainer")]', this) : $.x('ancestor::div[contains(@class,"postContainer")][1]', this); - $.log(postContainer); return Menu.open(this, Main.preParse(post)); }, open: function(button, post) { @@ -4010,7 +4009,7 @@ var path; path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split('/'); a.href = Redirect.thread(path[1], path[3], post.ID); - return a.textContent = "Archived post No." + post.ID; + return a.textContent = 'Archived post'; }, requirement: function(post) { var path; diff --git a/script.coffee b/script.coffee index 6bb23c206..f3b4084e2 100644 --- a/script.coffee +++ b/script.coffee @@ -861,7 +861,6 @@ Menu = $.x 'ancestor::div[parent::div[@class="board"]]/child::div[contains(@class,"opContainer")]', @ else $.x 'ancestor::div[contains(@class,"postContainer")][1]', @ - $.log postContainer Menu.open @, Main.preParse post open: (button, post) -> {el} = Menu @@ -3148,7 +3147,7 @@ ArchiveLink = open: (post) -> path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split '/' a.href = Redirect.thread path[1], path[3], post.ID - a.textContent = "Archived post No.#{post.ID}" + a.textContent = 'Archived post' requirement: (post) -> path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split '/' Redirect.thread(path[1], path[3]) isnt "//boards.4chan.org/#{path[1]}/" From 08e104f0fa47ca9faf9df83adc0e61b44102c92b Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Thu, 28 Jun 2012 21:01:56 +0200 Subject: [PATCH 17/30] Add a download link to the menu for compatible browsers. Close #102. --- 4chan_x.user.js | 37 ++++++++++++++++++++++++++++++++++--- script.coffee | 31 +++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 17d614004..1b67152e6 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -77,7 +77,7 @@ */ (function() { - var $, $$, Anonymize, ArchiveLink, AutoGif, Conf, Config, DeleteLink, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; + var $, $$, Anonymize, ArchiveLink, AutoGif, Conf, Config, DeleteLink, DownloadLink, ExpandComment, ExpandThread, Favicon, FileInfo, Filter, Get, ImageExpand, ImageHover, Keybinds, Main, Menu, Nav, Options, QR, QuoteBacklink, QuoteCT, QuoteInline, QuoteOP, QuotePreview, Quotify, Redirect, ReplyHiding, ReportLink, RevealSpoilers, Sauce, StrikethroughQuotes, ThreadHiding, ThreadStats, Time, TitlePost, UI, Unread, Updater, Watcher, d, g, _base; Config = { main: { @@ -112,7 +112,8 @@ 'Menu': [true, 'Add a drop-down menu in posts.'], 'Report Link': [true, 'Add a report link to the menu.'], 'Delete Link': [true, 'Add a delete link to the menu.'], - 'Archive Link': [true, 'Add a archive link to the menu.'] + 'Download Link': [true, 'Add a download with original filename link to the menu. Chrome-only currently.'], + 'Archive Link': [true, 'Add an archive link to the menu.'] }, Monitoring: { 'Thread Updater': [true, 'Update threads. Has more options in its own dialog.'], @@ -3203,6 +3204,7 @@ fullname: span.title, shortname: span.textContent }; + node.setAttribute('data-filename', span.title); return node.innerHTML = FileInfo.funk(FileInfo); }, setFormats: function() { @@ -3996,6 +3998,31 @@ } }; + DownloadLink = { + init: function() { + var a; + if ($.el('a').download === void 0) { + return; + } + a = $.el('a', { + className: 'download_link', + textContent: 'Download file' + }); + return Menu.addEntry({ + el: a, + open: function(post) { + var fileText; + a.href = post.img.parentNode.href; + fileText = post.fileInfo.firstElementChild; + return a.download = Conf['File Info Formatting'] ? fileText.dataset.filename : $('span', fileText).title; + }, + requirement: function(post) { + return post.img; + } + }); + } + }; + ArchiveLink = { init: function() { var a; @@ -4675,6 +4702,9 @@ if (Conf['Delete Link']) { DeleteLink.init(); } + if (Conf['Download Link']) { + DownloadLink.init(); + } if (Conf['Archive Link']) { ArchiveLink.init(); } @@ -4949,7 +4979,8 @@ a[href="javascript:;"] {\ border-bottom: 1px solid rgba(0, 0, 0, .25);\ display: block;\ outline: none;\ - padding: 3px 4px;\ + padding: 3px 7px;\ + text-decoration: none;\ }\ .entry:last-child {\ border: none;\ diff --git a/script.coffee b/script.coffee index f3b4084e2..8ed4fff4a 100644 --- a/script.coffee +++ b/script.coffee @@ -28,7 +28,8 @@ Config = 'Menu': [true, 'Add a drop-down menu in posts.'] 'Report Link': [true, 'Add a report link to the menu.'] 'Delete Link': [true, 'Add a delete link to the menu.'] - 'Archive Link': [true, 'Add a archive link to the menu.'] + 'Download Link': [true, 'Add a download with original filename link to the menu. Chrome-only currently.'] + 'Archive Link': [true, 'Add an archive link to the menu.'] Monitoring: 'Thread Updater': [true, 'Update threads. Has more options in its own dialog.'] 'Unread Count': [true, 'Show unread post count in tab title'] @@ -2497,6 +2498,8 @@ FileInfo = resolution: span.previousSibling.textContent.match(/\d+x\d+|PDF/)[0] fullname: span.title shortname: span.textContent + # XXX GM/Scriptish + node.setAttribute 'data-filename', span.title node.innerHTML = FileInfo.funk FileInfo setFormats: -> code = Conf['fileInfo'].replace /%([BKlLMnNprs])/g, (s, c) -> @@ -3137,6 +3140,26 @@ ReportLink = set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200" window.open url, id, set +DownloadLink = + init: -> + # Test for download feature support. + return if $.el('a').download is undefined + a = $.el 'a', + className: 'download_link' + textContent: 'Download file' + Menu.addEntry + el: a + open: (post) -> + a.href = post.img.parentNode.href + fileText = post.fileInfo.firstElementChild + a.download = + if Conf['File Info Formatting'] + fileText.dataset.filename + else + $('span', fileText).title + requirement: (post) -> + post.img + ArchiveLink = init: -> a = $.el 'a', @@ -3633,6 +3656,9 @@ Main = if Conf['Delete Link'] DeleteLink.init() + if Conf['Download Link'] + DownloadLink.init() + if Conf['Archive Link'] ArchiveLink.init() @@ -3848,7 +3874,8 @@ a[href="javascript:;"] { border-bottom: 1px solid rgba(0, 0, 0, .25); display: block; outline: none; - padding: 3px 4px; + padding: 3px 7px; + text-decoration: none; } .entry:last-child { border: none; From abfc9cbe49ea191e3024bd9bc8b36151103245d9 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Sun, 1 Jul 2012 23:38:59 +0200 Subject: [PATCH 18/30] Remove entry.requirement, somply use entry.open to determine if the entry is valid for the selected post or not. --- 4chan_x.user.js | 48 ++++++++++++++++++++++++------------------------ script.coffee | 35 +++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 3772ed544..1c227ee56 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1107,7 +1107,9 @@ }, node: function(post) { var a; - if (!(a = $('.menu_button', post.el))) { + if (post.isInlined && !post.isCrosspost) { + a = $('.menu_button', post.el); + } else { a = Menu.a.cloneNode(true); $.add($('.postInfo', post.el), a); } @@ -1136,10 +1138,7 @@ _ref = Menu.entries; for (_i = 0, _len = _ref.length; _i < _len; _i++) { entry = _ref[_i]; - if (entry.requirement(post)) { - if (typeof entry.open === "function") { - entry.open(post); - } + if (entry.open(post)) { $.add(el, entry.el); } } @@ -3927,12 +3926,13 @@ }); return Menu.addEntry({ el: a, - open: function() { + open: function(post) { + if (post.isArchived) { + return false; + } a.textContent = 'Delete this post'; - return $.on(a, 'click', DeleteLink["delete"]); - }, - requirement: function(post) { - return post.isArchived === false; + $.on(a, 'click', DeleteLink["delete"]); + return true; } }); }, @@ -3991,7 +3991,7 @@ $.on(a, 'click', this.report); return Menu.addEntry({ el: a, - requirement: function(post) { + open: function(post) { return post.isArchived === false; } }); @@ -4020,12 +4020,13 @@ el: a, open: function(post) { var fileText; + if (!post.img) { + return false; + } a.href = post.img.parentNode.href; fileText = post.fileInfo.firstElementChild; - return a.download = Conf['File Info Formatting'] ? fileText.dataset.filename : $('span', fileText).title; - }, - requirement: function(post) { - return post.img; + a.download = Conf['File Info Formatting'] ? fileText.dataset.filename : $('span', fileText).title; + return true; } }); } @@ -4036,20 +4037,19 @@ var a; a = $.el('a', { className: 'archive_link', - target: '_blank' + target: '_blank', + textContent: 'Archived post' }); return Menu.addEntry({ el: a, open: function(post) { - var path; + var href, path; path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split('/'); - a.href = Redirect.thread(path[1], path[3], post.ID); - return a.textContent = 'Archived post'; - }, - requirement: function(post) { - var path; - path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split('/'); - return Redirect.thread(path[1], path[3]) !== ("//boards.4chan.org/" + path[1] + "/"); + if ((href = Redirect.thread(path[1], path[3], post.ID)) === ("//boards.4chan.org/" + path[1] + "/")) { + return false; + } + a.href = href; + return true; } }); } diff --git a/script.coffee b/script.coffee index 31c274714..bbc65d994 100644 --- a/script.coffee +++ b/script.coffee @@ -840,7 +840,9 @@ Menu = Main.callbacks.push @node node: (post) -> - unless a = $ '.menu_button', post.el + if post.isInlined and !post.isCrosspost + a = $ '.menu_button', post.el + else a = Menu.a.cloneNode true $.add $('.postInfo', post.el), a $.on a, 'click', Menu.toggle @@ -873,8 +875,7 @@ Menu = # className: 'entry' # textContent: "#{i}: #{post[i]}" for entry in Menu.entries - if entry.requirement post - entry.open? post + if entry.open post $.add el, entry.el $.addClass $('.entry', Menu.el), 'focused' @@ -3082,11 +3083,12 @@ DeleteLink = href: 'javascript:;' Menu.addEntry el: a - open: -> + open: (post) -> + if post.isArchived + return false a.textContent = 'Delete this post' $.on a, 'click', DeleteLink.delete - requirement: (post) -> - post.isArchived is false + true delete: -> $.off @, 'click', DeleteLink.delete @textContent = 'Deleting...' @@ -3137,7 +3139,7 @@ ReportLink = $.on a, 'click', @report Menu.addEntry el: a - requirement: (post) -> + open: (post) -> post.isArchived is false report: -> a = $ '.postNum > a[title="Highlight this post"]', $.id @parentNode.dataset.rootid @@ -3156,6 +3158,8 @@ DownloadLink = Menu.addEntry el: a open: (post) -> + unless post.img + return false a.href = post.img.parentNode.href fileText = post.fileInfo.firstElementChild a.download = @@ -3163,23 +3167,22 @@ DownloadLink = fileText.dataset.filename else $('span', fileText).title - requirement: (post) -> - post.img + true ArchiveLink = init: -> a = $.el 'a', - className: 'archive_link' - target: '_blank' + className: 'archive_link' + target: '_blank' + textContent: 'Archived post' Menu.addEntry el: a open: (post) -> path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split '/' - a.href = Redirect.thread path[1], path[3], post.ID - a.textContent = 'Archived post' - requirement: (post) -> - path = $('.postNum > a[title="Highlight this post"]', post.el).pathname.split '/' - Redirect.thread(path[1], path[3]) isnt "//boards.4chan.org/#{path[1]}/" + if (href = Redirect.thread path[1], path[3], post.ID) is "//boards.4chan.org/#{path[1]}/" + return false + a.href = href + true ThreadStats = init: -> From 9f329ecd26a4493024ca3d7acfdd09ed1abcb803 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 2 Jul 2012 09:55:25 +0200 Subject: [PATCH 19/30] Add Country filtering. --- 4chan_x.user.js | 9 +++++++++ changelog | 2 ++ script.coffee | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/4chan_x.user.js b/4chan_x.user.js index 1c227ee56..6a3332061 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -156,6 +156,7 @@ email: ['# Filter any e-mails that are not `sage` on /a/ and /jp/:', '#/^(?!sage$)/;boards:a,jp'].join('\n'), subject: ['# Filter Generals on /v/:', '#/general/i;boards:v;op:only'].join('\n'), comment: ['# Filter Stallman copypasta on /g/:', '#/what you\'re refer+ing to as linux/i;boards:g'].join('\n'), + country: [''].join('\n'), filename: [''].join('\n'), dimensions: ['# Highlight potential wallpapers:', '#/1920x1080/;op:yes;highlight;top:no;boards:w,wg'].join('\n'), filesize: [''].join('\n'), @@ -706,6 +707,13 @@ } return text.join(''); }, + country: function(post) { + var flag; + if (flag = $('.countryFlag', post.el)) { + return flag.title.replace('Country: ', ''); + } + return false; + }, filename: function(post) { var file, fileInfo; fileInfo = post.fileInfo; @@ -2476,6 +2484,7 @@ \ \ \ + \ \ \ \ diff --git a/changelog b/changelog index 26ea54032..772521b37 100644 --- a/changelog +++ b/changelog @@ -1,4 +1,6 @@ master +- Mayhem + Add Country filtering. 2.33.7 - Mayhem diff --git a/script.coffee b/script.coffee index bbc65d994..c4557a867 100644 --- a/script.coffee +++ b/script.coffee @@ -90,6 +90,9 @@ Config = '# Filter Stallman copypasta on /g/:' '#/what you\'re refer+ing to as linux/i;boards:g' ].join '\n' + country: [ + '' + ].join '\n' filename: [ '' ].join '\n' @@ -556,6 +559,10 @@ Filter = for i in [0...nodes.snapshotLength] text.push if data = nodes.snapshotItem(i).data then data else '\n' text.join '' + country: (post) -> + if flag = $ '.countryFlag', post.el + return flag.title.replace 'Country: ', '' + false filename: (post) -> {fileInfo} = post if fileInfo and file = $ '.fileText > span', fileInfo @@ -1914,6 +1921,7 @@ Options = + From dded916590784b93b6b4d8b0256c2a82e6af1e7e Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 2 Jul 2012 18:53:36 +0200 Subject: [PATCH 20/30] Try a smaller arrow for the menu button. #565 --- 4chan_x.user.js | 5 ++++- script.coffee | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 7c3080166..8595be025 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1100,7 +1100,7 @@ this.a = $.el('a', { className: 'menu_button', href: 'javascript:;', - innerHTML: '[▼]' + innerHTML: '[▾]' }); this.el = $.el('div', { className: 'reply dialog', @@ -4999,6 +4999,9 @@ a[href="javascript:;"] {\ display: none !important;\ }\ \ +.menu_button {\ + letter-spacing: 2px;\ +}\ #menu {\ position: absolute;\ outline: none;\ diff --git a/script.coffee b/script.coffee index d03d5ca02..608b49e37 100644 --- a/script.coffee +++ b/script.coffee @@ -837,7 +837,7 @@ Menu = @a = $.el 'a', className: 'menu_button' href: 'javascript:;' - innerHTML: '[▼]' + innerHTML: '[▾]' @el = $.el 'div', className: 'reply dialog' id: 'menu' @@ -3891,6 +3891,9 @@ a[href="javascript:;"] { display: none !important; } +.menu_button { + letter-spacing: 2px; +} #menu { position: absolute; outline: none; From 7b47a89b47a1e48c0f8a64d609a869ce41790cec Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 2 Jul 2012 18:53:36 +0200 Subject: [PATCH 21/30] Try a smaller arrow for the menu button. #565 --- 4chan_x.user.js | 5 ++++- script.coffee | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 7c3080166..8595be025 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1100,7 +1100,7 @@ this.a = $.el('a', { className: 'menu_button', href: 'javascript:;', - innerHTML: '[▼]' + innerHTML: '[▾]' }); this.el = $.el('div', { className: 'reply dialog', @@ -4999,6 +4999,9 @@ a[href="javascript:;"] {\ display: none !important;\ }\ \ +.menu_button {\ + letter-spacing: 2px;\ +}\ #menu {\ position: absolute;\ outline: none;\ diff --git a/script.coffee b/script.coffee index d03d5ca02..608b49e37 100644 --- a/script.coffee +++ b/script.coffee @@ -837,7 +837,7 @@ Menu = @a = $.el 'a', className: 'menu_button' href: 'javascript:;' - innerHTML: '[▼]' + innerHTML: '[▾]' @el = $.el 'div', className: 'reply dialog' id: 'menu' @@ -3891,6 +3891,9 @@ a[href="javascript:;"] { display: none !important; } +.menu_button { + letter-spacing: 2px; +} #menu { position: absolute; outline: none; From 232c1edabfa93b1bdd6ec58e52afa9769c9b57c6 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 2 Jul 2012 20:30:27 +0200 Subject: [PATCH 22/30] Revert "Try a smaller arrow for the menu button. #565" This reverts commit 7b47a89b47a1e48c0f8a64d609a869ce41790cec. --- 4chan_x.user.js | 5 +---- script.coffee | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 8595be025..7c3080166 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1100,7 +1100,7 @@ this.a = $.el('a', { className: 'menu_button', href: 'javascript:;', - innerHTML: '[▾]' + innerHTML: '[▼]' }); this.el = $.el('div', { className: 'reply dialog', @@ -4999,9 +4999,6 @@ a[href="javascript:;"] {\ display: none !important;\ }\ \ -.menu_button {\ - letter-spacing: 2px;\ -}\ #menu {\ position: absolute;\ outline: none;\ diff --git a/script.coffee b/script.coffee index 608b49e37..d03d5ca02 100644 --- a/script.coffee +++ b/script.coffee @@ -837,7 +837,7 @@ Menu = @a = $.el 'a', className: 'menu_button' href: 'javascript:;' - innerHTML: '[▾]' + innerHTML: '[▼]' @el = $.el 'div', className: 'reply dialog' id: 'menu' @@ -3891,9 +3891,6 @@ a[href="javascript:;"] { display: none !important; } -.menu_button { - letter-spacing: 2px; -} #menu { position: absolute; outline: none; From 41abe8b5d57a1105b0b79d582a1c269fc33339a7 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Mon, 2 Jul 2012 20:55:44 +0200 Subject: [PATCH 23/30] Close #565 --- 4chan_x.user.js | 13 ++++++++++++- script.coffee | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 7c3080166..30aa5470a 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -1100,7 +1100,7 @@ this.a = $.el('a', { className: 'menu_button', href: 'javascript:;', - innerHTML: '[▼]' + innerHTML: '[]' }); this.el = $.el('div', { className: 'reply dialog', @@ -4999,6 +4999,17 @@ a[href="javascript:;"] {\ display: none !important;\ }\ \ +.menu_button {\ + display: inline-block;\ +}\ +.menu_button > span {\ + border-top: .5em solid;\ + border-right: .3em solid transparent;\ + border-left: .3em solid transparent;\ + display: inline-block;\ + margin: 2px;\ + vertical-align: middle;\ +}\ #menu {\ position: absolute;\ outline: none;\ diff --git a/script.coffee b/script.coffee index d03d5ca02..6d29c21cc 100644 --- a/script.coffee +++ b/script.coffee @@ -837,7 +837,7 @@ Menu = @a = $.el 'a', className: 'menu_button' href: 'javascript:;' - innerHTML: '[▼]' + innerHTML: '[]' @el = $.el 'div', className: 'reply dialog' id: 'menu' @@ -3891,6 +3891,17 @@ a[href="javascript:;"] { display: none !important; } +.menu_button { + display: inline-block; +} +.menu_button > span { + border-top: .5em solid; + border-right: .3em solid transparent; + border-left: .3em solid transparent; + display: inline-block; + margin: 2px; + vertical-align: middle; +} #menu { position: absolute; outline: none; From 76d56e3f70cd4945fccb48ea478ac61c97a02d2f Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 3 Jul 2012 17:49:34 +0200 Subject: [PATCH 24/30] Add unlimited sub menu support to the Menu. Add Filter entries to the Menu. Close #511. You should be able to solve this. --- 4chan_x.user.js | 169 ++++++++++++++++++++++++++++++++++++++------- script.coffee | 179 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 295 insertions(+), 53 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 48af764b7..d2ec34fa1 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -717,8 +717,12 @@ filename: function(post) { var file, fileInfo; fileInfo = post.fileInfo; - if (fileInfo && (file = $('.fileText > span', fileInfo))) { - return file.title; + if (fileInfo) { + if (file = $('.fileText > span', fileInfo)) { + return file.title; + } else { + return fileInfo.firstElementChild.dataset.filename; + } } return false; }, @@ -745,6 +749,73 @@ return img.dataset.md5; } return false; + }, + menuInit: function() { + var div, entry, type, _i, _len, _ref; + div = $.el('div'); + entry = { + el: div, + open: function() { + div.textContent = 'Filter'; + return true; + }, + children: [] + }; + _ref = [['Name', 'name'], ['Unique ID', 'uniqueid'], ['Tripcode', 'tripcode'], ['Admin/Mod', 'mod'], ['E-mail', 'email'], ['Subject', 'subject'], ['Comment', 'comment'], ['Country', 'country'], ['Filename', 'filename'], ['Image dimensions', 'dimensions'], ['Filesize', 'filesize'], ['Image MD5', 'md5']]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + type = _ref[_i]; + entry.children.push(Filter.createSubEntry(type[0], type[1])); + } + return Menu.addEntry(entry); + }, + createSubEntry: function(text, type) { + var el, onclick, open; + el = $.el('a', { + href: 'javascript:;', + textContent: text + }); + onclick = null; + open = function(post) { + var value; + value = Filter[type](post); + if (value === false) { + return false; + } + $.off(el, 'click', onclick); + onclick = function() { + var re, save, select, ta, tl; + re = type === 'md5' ? value : value.replace(/\/|\\|\^|\$|\n|\.|\(|\)|\{|\}|\[|\]|\?|\*|\+|\|/g, function(c) { + if (c === '\n') { + return '\\n'; + } else if (c === '\\') { + return '\\\\'; + } else { + return "\\" + c; + } + }); + re = "/^" + re + "$/"; + if (/\bop\b/.test(post["class"])) { + re += ';op:yes'; + } + save = (save = $.get(type, '')) ? "" + save + "\n" + re : re; + $.set(type, save); + Options.dialog(); + select = $('select[name=filter]', $.id('options')); + select.value = type; + $.event(select, new Event('change')); + $.id('filter_tab').checked = true; + ta = select.nextElementSibling; + tl = ta.textLength; + ta.setSelectionRange(tl, tl); + return ta.focus(); + }; + $.on(el, 'click', onclick); + return true; + }; + return { + el: el, + open: open + }; } }; @@ -1139,18 +1210,35 @@ return Menu.open(this, Main.preParse(post)); }, open: function(button, post) { - var bLeft, bRect, bTop, el, entry, mRect, _i, _len, _ref; + var bLeft, bRect, bTop, el, entry, funk, mRect, _i, _len, _ref; el = Menu.el; el.setAttribute('data-id', post.ID); el.setAttribute('data-rootid', post.root.id); + funk = function(entry, parent) { + var child, children, open, subMenu, _i, _len; + open = entry.open, children = entry.children; + if (!open(post)) { + return; + } + $.add(parent, entry.el); + if (!children) { + return; + } + subMenu = $.el('div', { + className: 'reply dialog subMenu' + }); + $.add(entry.el, subMenu); + for (_i = 0, _len = children.length; _i < _len; _i++) { + child = children[_i]; + funk(child, subMenu); + } + }; _ref = Menu.entries; for (_i = 0, _len = _ref.length; _i < _len; _i++) { entry = _ref[_i]; - if (entry.open(post)) { - $.add(el, entry.el); - } + funk(entry, el); } - $.addClass($('.entry', Menu.el), 'focused'); + Menu.focus($('.entry', Menu.el)); $.on(d, 'click', Menu.close); $.add(d.body, el); mRect = el.getBoundingClientRect(); @@ -1162,20 +1250,23 @@ return el.focus(); }, close: function() { - var el, focused; + var el, focused, _i, _len, _ref; el = Menu.el; $.rm(el); - if (focused = $('.focused.entry', el)) { + _ref = $$('.focused.entry', el); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + focused = _ref[_i]; $.rmClass(focused, 'focused'); } el.innerHTML = null; el.removeAttribute('style'); delete Menu.lastOpener; + delete Menu.focusedEntry; return $.off(d, 'click', Menu.close); }, keybinds: function(e) { - var el, next; - el = $('.focused.entry', Menu.el); + var el, next, subMenu; + el = Menu.focusedEntry; switch (Keybinds.keyCode(e) || e.keyCode) { case 'Esc': Menu.lastOpener.focus(); @@ -1190,18 +1281,18 @@ Menu.focus(next); } break; - case 'Right': - if (next = el.firstElementChild) { - Menu.focus(next); - } - break; case 'Down': if (next = el.nextElementSibling) { Menu.focus(next); } break; + case 'Right': + if ((subMenu = $('.subMenu', el)) && (next = subMenu.firstElementChild)) { + Menu.focus(next); + } + break; case 'Left': - if ((next = el.parentNode) && next.id !== 'menu') { + if (next = $.x('parent::*[contains(@class,"subMenu")]/parent::*', el)) { Menu.focus(next); } break; @@ -1212,23 +1303,35 @@ return e.stopPropagation(); }, focus: function(el) { - var focused; - if (focused = $('.focused.entry', Menu.el)) { + var focused, _i, _len, _ref; + if (focused = $.x('parent::*/child::*[contains(@class,"focused")]', el)) { $.rmClass(focused, 'focused'); } + _ref = $$('.focused', el); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + focused = _ref[_i]; + $.rmClass(focused, 'focused'); + } + Menu.focusedEntry = el; return $.addClass(el, 'focused'); }, addEntry: function(entry) { - var el, els, _i, _len; - els = $$('*', entry.el); - els.push(entry.el); - for (_i = 0, _len = els.length; _i < _len; _i++) { - el = els[_i]; + var funk; + funk = function(entry) { + var child, children, el, _i, _len, _ref; + el = entry.el, children = entry.children; $.addClass(el, 'entry'); - $.on(el, 'focus mouseover', function() { + $.on(el, 'focus mouseover', function(e) { + e.stopPropagation(); return Menu.focus(this); }); - } + _ref = children || []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + funk(child); + } + }; + funk(entry); return Menu.entries.push(entry); } }; @@ -4730,6 +4833,9 @@ if (Conf['Delete Link']) { DeleteLink.init(); } + if (Conf['Filter']) { + Filter.menuInit(); + } if (Conf['Download Link']) { DownloadLink.init(); } @@ -5019,7 +5125,9 @@ a[href="javascript:;"] {\ display: block;\ outline: none;\ padding: 3px 7px;\ + position: relative;\ text-decoration: none;\ + white-space: nowrap;\ }\ .entry:last-child {\ border: none;\ @@ -5027,6 +5135,15 @@ a[href="javascript:;"] {\ .focused.entry {\ background: rgba(255, 255, 255, .33);\ }\ +.entry:not(.focused) > .subMenu {\ + display: none;\ +}\ +.subMenu {\ + position: absolute;\ + left: 100%;\ + top: 0;\ + margin-top: -1px;\ +}\ \ h1 {\ text-align: center;\ diff --git a/script.coffee b/script.coffee index f112f33c1..56c811150 100644 --- a/script.coffee +++ b/script.coffee @@ -565,8 +565,11 @@ Filter = false filename: (post) -> {fileInfo} = post - if fileInfo and file = $ '.fileText > span', fileInfo - return file.title + if fileInfo + if file = $ '.fileText > span', fileInfo + return file.title + else + return fileInfo.firstElementChild.dataset.filename false dimensions: (post) -> {fileInfo} = post @@ -584,6 +587,98 @@ Filter = return img.dataset.md5 false + menuInit: -> + div = $.el 'div' + + entry = + el: div + open: -> + # Reset the container's content, + # don't keep irrelevant entries. + div.textContent = 'Filter' + true + children: [] + + for type in [ + ['Name', 'name'] + ['Unique ID', 'uniqueid'] + ['Tripcode', 'tripcode'] + ['Admin/Mod', 'mod'] + ['E-mail', 'email'] + ['Subject', 'subject'] + ['Comment', 'comment'] + ['Country', 'country'] + ['Filename', 'filename'] + ['Image dimensions', 'dimensions'] + ['Filesize', 'filesize'] + ['Image MD5', 'md5'] + ] + # Add a sub entry for each filter type. + entry.children.push Filter.createSubEntry type[0], type[1] + + Menu.addEntry entry + + createSubEntry: (text, type) -> + el = $.el 'a', + href: 'javascript:;' + textContent: text + # Define the onclick var outside of open's scope to $.off it properly. + onclick = null + + open = (post) -> + value = Filter[type] post + return false if value is false + $.off el, 'click', onclick + onclick = -> + # Convert value -> regexp, unless type is md5 + re = if type is 'md5' then value else value.replace /// + / + | \\ + | \^ + | \$ + | \n + | \. + | \( + | \) + | \{ + | \} + | \[ + | \] + | \? + | \* + | \+ + | \| + ///g, (c) -> + if c is '\n' + '\\n' + else if c is '\\' + '\\\\' + else + "\\#{c}" + + re = "/^#{re}$/" + if /\bop\b/.test post.class + re += ';op:yes' + + # Add a new line before the regexp unless the text is empty. + save = if save = $.get type, '' then "#{save}\n#{re}" else re + $.set type, save + + # Open the options and display & focus the relevant filter textarea. + Options.dialog() + select = $ 'select[name=filter]', $.id 'options' + select.value = type + $.event select, new Event 'change' + $.id('filter_tab').checked = true + ta = select.nextElementSibling + tl = ta.textLength + ta.setSelectionRange tl, tl + ta.focus() + $.on el, 'click', onclick + true + + return el: el, open: open + StrikethroughQuotes = init: -> Main.callbacks.push @node @@ -877,15 +972,22 @@ Menu = # XXX GM/Scriptish require setAttribute el.setAttribute 'data-id', post.ID el.setAttribute 'data-rootid', post.root.id - # for i of post - # $.add Menu.el, $.el 'code', - # className: 'entry' - # textContent: "#{i}: #{post[i]}" - for entry in Menu.entries - if entry.open post - $.add el, entry.el - $.addClass $('.entry', Menu.el), 'focused' + funk = (entry, parent) -> + {open, children} = entry + return unless open post + $.add parent, entry.el + return unless children + subMenu = $.el 'div', + className: 'reply dialog subMenu' + $.add entry.el, subMenu + for child in children + funk child, subMenu + return + for entry in Menu.entries + funk entry, el + + Menu.focus $ '.entry', Menu.el $.on d, 'click', Menu.close $.add d.body, el @@ -909,15 +1011,16 @@ Menu = close: -> {el} = Menu $.rm el - if focused = $ '.focused.entry', el + for focused in $$ '.focused.entry', el $.rmClass focused, 'focused' el.innerHTML = null el.removeAttribute 'style' delete Menu.lastOpener + delete Menu.focusedEntry $.off d, 'click', Menu.close keybinds: (e) -> - el = $ '.focused.entry', Menu.el + el = Menu.focusedEntry switch Keybinds.keyCode(e) or e.keyCode when 'Esc' @@ -928,14 +1031,14 @@ Menu = when 'Up' if next = el.previousElementSibling Menu.focus next - when 'Right' - if next = el.firstElementChild - Menu.focus next when 'Down' if next = el.nextElementSibling Menu.focus next + when 'Right' + if (subMenu = $ '.subMenu', el) and next = subMenu.firstElementChild + Menu.focus next when 'Left' - if (next = el.parentNode) and next.id isnt 'menu' + if next = $.x 'parent::*[contains(@class,"subMenu")]/parent::*', el Menu.focus next else return @@ -943,16 +1046,24 @@ Menu = e.preventDefault() e.stopPropagation() focus: (el) -> - if focused = $ '.focused.entry', Menu.el + if focused = $.x 'parent::*/child::*[contains(@class,"focused")]', el $.rmClass focused, 'focused' + for focused in $$ '.focused', el + $.rmClass focused, 'focused' + Menu.focusedEntry = el $.addClass el, 'focused' addEntry: (entry) -> - els = $$ '*', entry.el - els.push entry.el - for el in els + funk = (entry) -> + {el, children} = entry $.addClass el, 'entry' - $.on el, 'focus mouseover', -> Menu.focus @ + $.on el, 'focus mouseover', (e) -> + e.stopPropagation() + Menu.focus @ + for child in children or [] + funk child + return + funk entry Menu.entries.push entry Keybinds = @@ -1493,9 +1604,9 @@ QR = QR.characterCount.call $ 'textarea', QR.el $('#spoiler', QR.el).checked = @spoiler dragStart: -> - $.addClass @, 'drag' + $.addClass @, 'drag' dragEnter: -> - $.addClass @, 'over' + $.addClass @, 'over' dragLeave: -> $.rmClass @, 'over' dragOver: (e) -> @@ -3151,10 +3262,10 @@ ReportLink = open: (post) -> post.isArchived is false report: -> - a = $ '.postNum > a[title="Highlight this post"]', $.id @parentNode.dataset.rootid - url = "//sys.4chan.org/#{a.pathname.split('/')[1]}/imgboard.php?mode=report&no=#{@parentNode.dataset.id}" - id = Date.now() - set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200" + a = $ '.postNum > a[title="Highlight this post"]', $.id @parentNode.dataset.rootid + url = "//sys.4chan.org/#{a.pathname.split('/')[1]}/imgboard.php?mode=report&no=#{@parentNode.dataset.id}" + id = Date.now() + set = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,resizable=1,width=685,height=200" window.open url, id, set DownloadLink = @@ -3682,6 +3793,9 @@ Main = if Conf['Delete Link'] DeleteLink.init() + if Conf['Filter'] + Filter.menuInit() + if Conf['Download Link'] DownloadLink.init() @@ -3912,7 +4026,9 @@ a[href="javascript:;"] { display: block; outline: none; padding: 3px 7px; + position: relative; text-decoration: none; + white-space: nowrap; } .entry:last-child { border: none; @@ -3920,6 +4036,15 @@ a[href="javascript:;"] { .focused.entry { background: rgba(255, 255, 255, .33); } +.entry:not(.focused) > .subMenu { + display: none; +} +.subMenu { + position: absolute; + left: 100%; + top: 0; + margin-top: -1px; +} h1 { text-align: center; From 71cdaabc48cba348547859cd86c5f9637ff6c780 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 3 Jul 2012 18:06:01 +0200 Subject: [PATCH 25/30] Fix md5 auto-filtering. cursor: pointer; --- 4chan_x.user.js | 5 ++++- script.coffee | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index d2ec34fa1..8d789900f 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -793,7 +793,9 @@ return "\\" + c; } }); - re = "/^" + re + "$/"; + if (type !== 'md5') { + re = "/^" + re + "$/"; + } if (/\bop\b/.test(post["class"])) { re += ';op:yes'; } @@ -5122,6 +5124,7 @@ a[href="javascript:;"] {\ }\ .entry {\ border-bottom: 1px solid rgba(0, 0, 0, .25);\ + cursor: pointer;\ display: block;\ outline: none;\ padding: 3px 7px;\ diff --git a/script.coffee b/script.coffee index 56c811150..84f86e394 100644 --- a/script.coffee +++ b/script.coffee @@ -656,7 +656,7 @@ Filter = else "\\#{c}" - re = "/^#{re}$/" + re = "/^#{re}$/" unless type is 'md5' if /\bop\b/.test post.class re += ';op:yes' @@ -4023,6 +4023,7 @@ a[href="javascript:;"] { } .entry { border-bottom: 1px solid rgba(0, 0, 0, .25); + cursor: pointer; display: block; outline: none; padding: 3px 7px; From a8d4f652cc4764e04ada2f2f8ef7ba3d5552ef60 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Tue, 3 Jul 2012 18:09:32 +0200 Subject: [PATCH 26/30] derp --- 4chan_x.user.js | 4 +--- script.coffee | 6 +++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 8d789900f..1f57b7104 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -793,9 +793,7 @@ return "\\" + c; } }); - if (type !== 'md5') { - re = "/^" + re + "$/"; - } + re = type === 'md5' ? "/" + value + "/" : "/^" + re + "$/"; if (/\bop\b/.test(post["class"])) { re += ';op:yes'; } diff --git a/script.coffee b/script.coffee index 84f86e394..23818f01f 100644 --- a/script.coffee +++ b/script.coffee @@ -656,7 +656,11 @@ Filter = else "\\#{c}" - re = "/^#{re}$/" unless type is 'md5' + re = + if type is 'md5' + "/#{value}/" + else + "/^#{re}$/" if /\bop\b/.test post.class re += ';op:yes' From 3c3ed3070986f8860aa8812235297b9e2ae87960 Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 4 Jul 2012 18:06:10 +0200 Subject: [PATCH 27/30] Close #578. --- 4chan_x.user.js | 11 +++++++---- script.coffee | 12 +++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 26f3f1246..32b20bb3d 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -424,7 +424,7 @@ }, nodes: function(nodes) { var frag, node, _i, _len; - if (nodes instanceof Node) { + if (!(nodes instanceof Array)) { return nodes; } frag = d.createDocumentFragment(); @@ -1182,6 +1182,9 @@ return e.stopPropagation(); }); $.on(this.el, 'keydown', this.keybinds); + $.on(d, 'AddMenuEntry', function(e) { + return Menu.addEntry(e.detail); + }); return Main.callbacks.push(this.node); }, node: function(post) { @@ -1215,9 +1218,9 @@ el.setAttribute('data-id', post.ID); el.setAttribute('data-rootid', post.root.id); funk = function(entry, parent) { - var child, children, open, subMenu, _i, _len; - open = entry.open, children = entry.children; - if (!open(post)) { + var child, children, subMenu, _i, _len; + children = entry.children; + if (!entry.open(post)) { return; } $.add(parent, entry.el); diff --git a/script.coffee b/script.coffee index 6cdb5a0f4..ed0741c31 100644 --- a/script.coffee +++ b/script.coffee @@ -329,7 +329,10 @@ $.extend $, tn: (s) -> d.createTextNode s nodes: (nodes) -> - if nodes instanceof Node + # In (at least) Chrome, elements created inside different + # scripts/window contexts inherit from unequal prototypes. + # window_ext1.Node !== window_ext2.Node + unless nodes instanceof Array return nodes frag = d.createDocumentFragment() for node in nodes @@ -945,6 +948,9 @@ Menu = $.on @el, 'click', (e) -> e.stopPropagation() $.on @el, 'keydown', @keybinds + # Doc here: https://github.com/MayhemYDG/4chan-x/wiki/Menu-API + $.on d, 'AddMenuEntry', (e) -> Menu.addEntry e.detail + Main.callbacks.push @node node: (post) -> if post.isInlined and !post.isCrosspost @@ -979,8 +985,8 @@ Menu = el.setAttribute 'data-rootid', post.root.id funk = (entry, parent) -> - {open, children} = entry - return unless open post + {children} = entry + return unless entry.open post $.add parent, entry.el return unless children subMenu = $.el 'div', From 4fc58ad0533a4d98f8fb759b3c12a922fb8a168d Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 4 Jul 2012 18:43:23 +0200 Subject: [PATCH 28/30] Add an arrow indicating submenus. $.rm .subMenu on open(). --- 4chan_x.user.js | 38 +++++++++++++++++++++++++++++--------- script.coffee | 36 ++++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index 32b20bb3d..aa1530aa4 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -752,11 +752,12 @@ }, menuInit: function() { var div, entry, type, _i, _len, _ref; - div = $.el('div'); + div = $.el('div', { + textContent: 'Filter' + }); entry = { el: div, open: function() { - div.textContent = 'Filter'; return true; }, children: [] @@ -1227,6 +1228,9 @@ if (!children) { return; } + if (subMenu = $('.subMenu', entry.el)) { + $.rm(subMenu); + } subMenu = $.el('div', { className: 'reply dialog subMenu' }); @@ -1321,16 +1325,19 @@ addEntry: function(entry) { var funk; funk = function(entry) { - var child, children, el, _i, _len, _ref; + var child, children, el, _i, _len; el = entry.el, children = entry.children; $.addClass(el, 'entry'); $.on(el, 'focus mouseover', function(e) { e.stopPropagation(); return Menu.focus(this); }); - _ref = children || []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - child = _ref[_i]; + if (!children) { + return; + } + $.addClass(el, 'hasSubMenu'); + for (_i = 0, _len = children.length; _i < _len; _i++) { + child = children[_i]; funk(child); } }; @@ -5112,9 +5119,9 @@ a[href="javascript:;"] {\ display: inline-block;\ }\ .menu_button > span {\ - border-top: .5em solid;\ + border-top: .5em solid;\ border-right: .3em solid transparent;\ - border-left: .3em solid transparent;\ + border-left: .3em solid transparent;\ display: inline-block;\ margin: 2px;\ vertical-align: middle;\ @@ -5139,7 +5146,20 @@ a[href="javascript:;"] {\ .focused.entry {\ background: rgba(255, 255, 255, .33);\ }\ -.entry:not(.focused) > .subMenu {\ +.entry.hasSubMenu {\ + padding-right: 1.5em;\ +}\ +.hasSubMenu::after {\ + content: "";\ + border-left: .5em solid;\ + border-top: .3em solid transparent;\ + border-bottom: .3em solid transparent;\ + display: inline-block;\ + margin: .3em;\ + position: absolute;\ + right: 3px;\ +}\ +.hasSubMenu:not(.focused) > .subMenu {\ display: none;\ }\ .subMenu {\ diff --git a/script.coffee b/script.coffee index ed0741c31..ac7591e29 100644 --- a/script.coffee +++ b/script.coffee @@ -592,15 +592,12 @@ Filter = false menuInit: -> - div = $.el 'div' + div = $.el 'div', + textContent: 'Filter' entry = el: div - open: -> - # Reset the container's content, - # don't keep irrelevant entries. - div.textContent = 'Filter' - true + open: -> true children: [] for type in [ @@ -988,7 +985,11 @@ Menu = {children} = entry return unless entry.open post $.add parent, entry.el + return unless children + if subMenu = $ '.subMenu', entry.el + # Reset sub menu, remove irrelevant entries. + $.rm subMenu subMenu = $.el 'div', className: 'reply dialog subMenu' $.add entry.el, subMenu @@ -1071,7 +1072,9 @@ Menu = $.on el, 'focus mouseover', (e) -> e.stopPropagation() Menu.focus @ - for child in children or [] + return unless children + $.addClass el, 'hasSubMenu' + for child in children funk child return funk entry @@ -4021,9 +4024,9 @@ a[href="javascript:;"] { display: inline-block; } .menu_button > span { - border-top: .5em solid; + border-top: .5em solid; border-right: .3em solid transparent; - border-left: .3em solid transparent; + border-left: .3em solid transparent; display: inline-block; margin: 2px; vertical-align: middle; @@ -4048,7 +4051,20 @@ a[href="javascript:;"] { .focused.entry { background: rgba(255, 255, 255, .33); } -.entry:not(.focused) > .subMenu { +.entry.hasSubMenu { + padding-right: 1.5em; +} +.hasSubMenu::after { + content: ""; + border-left: .5em solid; + border-top: .3em solid transparent; + border-bottom: .3em solid transparent; + display: inline-block; + margin: .3em; + position: absolute; + right: 3px; +} +.hasSubMenu:not(.focused) > .subMenu { display: none; } .subMenu { From a0fe0ce827945f4efef8f3ee76ae0b8b645d73aa Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 4 Jul 2012 21:50:45 +0200 Subject: [PATCH 29/30] Changelog. --- changelog | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/changelog b/changelog index e1316186a..b79b0b077 100644 --- a/changelog +++ b/changelog @@ -1,6 +1,11 @@ master - Mayhem - Add Country filtering. + New feature: Menu, which + - replaces and includes Report Button and Delete Button. + - add one-click Filter buttons. + - add download links to automatically save the file with its original filename. Chrome-only currently. + - add archive links. + - can integrate features from external userscripts/extensions, see https://github.com/MayhemYDG/4chan-x/wiki/Menu-API 2.33.8 - Mayhem From fc9a5b1bae173cc0068aa15f15f5c7e400fee85a Mon Sep 17 00:00:00 2001 From: Nicolas Stepien Date: Wed, 4 Jul 2012 21:25:45 +0200 Subject: [PATCH 30/30] No need to .replace('Country: ', '') anymore. --- 4chan_x.user.js | 2 +- script.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/4chan_x.user.js b/4chan_x.user.js index aa1530aa4..1eed3e295 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -710,7 +710,7 @@ country: function(post) { var flag; if (flag = $('.countryFlag', post.el)) { - return flag.title.replace('Country: ', ''); + return flag.title; } return false; }, diff --git a/script.coffee b/script.coffee index ac7591e29..f60404d7b 100644 --- a/script.coffee +++ b/script.coffee @@ -565,7 +565,7 @@ Filter = text.join '' country: (post) -> if flag = $ '.countryFlag', post.el - return flag.title.replace 'Country: ', '' + return flag.title false filename: (post) -> {fileInfo} = post