diff --git a/4chan_x.user.js b/4chan_x.user.js index 66d2f2f0e..77fda1b8f 100644 --- a/4chan_x.user.js +++ b/4chan_x.user.js @@ -212,7 +212,7 @@ function Menu(type) { this.type = type; - $.on(d, 'AddMenuEntry', this.addEntryListener.bind(this)); + $.on(d, 'AddMenuEntry', this.addEntry.bind(this)); this.close = close.bind(this); this.entries = []; } @@ -276,14 +276,14 @@ }; Menu.prototype.insertEntry = function(entry, parent, data) { - var child, submenu, _i, _len, _ref; + var subEntry, submenu, _i, _len, _ref; if (typeof entry.open === 'function') { if (!entry.open(data)) { return; } } $.add(parent, entry.el); - if (!entry.children) { + if (!entry.subEntries) { return; } if (submenu = $('.submenu', entry.el)) { @@ -292,10 +292,10 @@ submenu = $.el('div', { className: 'reply dialog submenu' }); - _ref = entry.children; + _ref = entry.subEntries; for (_i = 0, _len = _ref.length; _i < _len; _i++) { - child = _ref[_i]; - this.insertEntry(child, submenu, data); + subEntry = _ref[_i]; + this.insertEntry(subEntry, submenu, data); } $.add(entry.el, submenu); }; @@ -388,36 +388,32 @@ return style.right = right; }; - Menu.prototype.addEntry = function(entry) { - this.parseEntry(entry); - return this.entries.push(entry); - }; - - Menu.prototype.parseEntry = function(entry) { - var child, children, el, _i, _len; - el = entry.el, children = entry.children; - $.addClass(el, 'entry'); - $.on(el, 'focus mouseover', (function(e) { - e.stopPropagation(); - return this.focus(el); - }).bind(this)); - if (!children) { - return; - } - $.addClass(el, 'has-submenu'); - for (_i = 0, _len = children.length; _i < _len; _i++) { - child = children[_i]; - this.parseEntry(child); - } - }; - - Menu.prototype.addEntryListener = function(e) { + Menu.prototype.addEntry = function(e) { var entry; entry = e.detail; if (entry.type !== this.type) { return; } - return this.addEntry(entry); + this.parseEntry(entry); + return this.entries.push(entry); + }; + + Menu.prototype.parseEntry = function(entry) { + var el, subEntries, subEntry, _i, _len; + el = entry.el, subEntries = entry.subEntries; + $.addClass(el, 'entry'); + $.on(el, 'focus mouseover', (function(e) { + e.stopPropagation(); + return this.focus(el); + }).bind(this)); + if (!subEntries) { + return; + } + $.addClass(el, 'has-submenu'); + for (_i = 0, _len = subEntries.length; _i < _len; _i++) { + subEntry = subEntries[_i]; + this.parseEntry(subEntry); + } }; return Menu; @@ -1021,9 +1017,12 @@ href: 'javascript:;' }); $.on(link, 'click', Settings.open); - Header.menu.addEntry({ - el: link - }); + d.dispatchEvent(new CustomEvent('AddMenuEntry', { + detail: { + type: 'header', + el: link + } + })); link = $.el('a', { className: 'fourchan-settings-link', textContent: '4chan Settings', @@ -1032,12 +1031,15 @@ $.on(link, 'click', function() { return $.id('settingsWindowLink').click(); }); - Header.menu.addEntry({ - el: link, - open: function() { - return !Conf['Disable 4chan\'s extension']; + d.dispatchEvent(new CustomEvent('AddMenuEntry', { + detail: { + type: 'header', + el: link, + open: function() { + return !Conf['Disable 4chan\'s extension']; + } } - }); + })); if (!Conf['Disable 4chan\'s extension']) { return; } @@ -1260,19 +1262,22 @@ textContent: 'Filter' }); entry = { + type: 'post', el: div, open: function(post) { Filter.menu.post = post; return true; }, - children: [] + subEntries: [] }; _ref = [['Name', 'name'], ['Unique ID', 'uniqueID'], ['Tripcode', 'tripcode'], ['Capcode', 'capcode'], ['E-mail', 'email'], ['Subject', 'subject'], ['Comment', 'comment'], ['Flag', 'flag'], ['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.menu.createSubEntry(type[0], type[1])); + entry.subEntries.push(Filter.menu.createSubEntry(type[0], type[1])); } - return Menu.menu.addEntry(entry); + return d.dispatchEvent(new CustomEvent('AddMenuEntry', { + detail: entry + })); }, createSubEntry: function(text, type) { var el; @@ -1418,25 +1423,28 @@ makeStub = $.el('label', { innerHTML: " Make stub" }); - return Menu.menu.addEntry({ - el: div, - open: function(post) { - var thread; - thread = post.thread; - if (post.isReply || thread.isHidden) { - return false; - } - ThreadHiding.menu.thread = thread; - return true; - }, - children: [ - { - el: apply - }, { - el: makeStub - } - ] - }); + return d.dispatchEvent(new CustomEvent('AddMenuEntry', { + detail: { + type: 'post', + el: div, + open: function(post) { + var thread; + thread = post.thread; + if (post.isReply || thread.isHidden) { + return false; + } + ThreadHiding.menu.thread = thread; + return true; + }, + subEntries: [ + { + el: apply + }, { + el: makeStub + } + ] + } + })); }, hide: function() { var makeStub, thread; @@ -1626,27 +1634,30 @@ makeStub = $.el('label', { innerHTML: " Make stub" }); - return Menu.menu.addEntry({ - el: div, - open: function(post) { - if (!post.isReply || post.isClone) { - return false; - } - ReplyHiding.menu.post = post; - return true; - }, - children: [ - { - el: apply - }, { - el: thisPost - }, { - el: replies - }, { - el: makeStub - } - ] - }); + return d.dispatchEvent(new CustomEvent('AddMenuEntry', { + detail: { + type: 'post', + el: div, + open: function(post) { + if (!post.isReply || post.isClone) { + return false; + } + ReplyHiding.menu.post = post; + return true; + }, + subEntries: [ + { + el: apply + }, { + el: thisPost + }, { + el: replies + }, { + el: makeStub + } + ] + } + })); }, hide: function() { var makeStub, parent, post, replies, thisPost; @@ -1864,13 +1875,16 @@ textContent: 'Report this post' }); $.on(a, 'click', ReportLink.report); - return Menu.menu.addEntry({ - el: a, - open: function(post) { - ReportLink.post = post; - return !post.isDead; + return d.dispatchEvent(new CustomEvent('AddMenuEntry', { + detail: { + type: 'post', + el: a, + open: function(post) { + ReportLink.post = post; + return !post.isDead; + } } - }); + })); }, report: function() { var id, post, set, url; @@ -1916,26 +1930,29 @@ return !!post.file; } }; - Menu.menu.addEntry({ - el: div, - open: function(post) { - var node, seconds; - if (post.isDead) { - return false; - } - DeleteLink.post = post; - node = div.firstChild; - if (seconds = DeleteLink.cooldown[post.fullID]) { - node.textContent = "Delete (" + seconds + ")"; - DeleteLink.cooldown.el = node; - } else { - node.textContent = 'Delete'; - delete DeleteLink.cooldown.el; - } - return true; - }, - children: [postEntry, fileEntry] - }); + d.dispatchEvent(new CustomEvent('AddMenuEntry', { + detail: { + type: 'post', + el: div, + open: function(post) { + var node, seconds; + if (post.isDead) { + return false; + } + DeleteLink.post = post; + node = div.firstChild; + if (seconds = DeleteLink.cooldown[post.fullID]) { + node.textContent = "Delete (" + seconds + ")"; + DeleteLink.cooldown.el = node; + } else { + node.textContent = 'Delete'; + delete DeleteLink.cooldown.el; + } + return true; + }, + subEntries: [postEntry, fileEntry] + } + })); return $.on(d, 'QRPostSuccessful', this.cooldown.start); }, "delete": function() { @@ -2026,17 +2043,20 @@ className: 'download-link', textContent: 'Download file' }); - return Menu.menu.addEntry({ - el: a, - open: function(post) { - if (!post.file) { - return false; + return d.dispatchEvent(new CustomEvent('AddMenuEntry', { + detail: { + type: 'post', + el: a, + open: function(post) { + if (!post.file) { + return false; + } + a.href = post.file.URL; + a.download = post.file.name; + return true; } - a.href = post.file.URL; - a.download = post.file.name; - return true; } - }); + })); } }; @@ -2050,6 +2070,7 @@ textContent: 'Archive' }); entry = { + type: 'post', el: div, open: function(post) { var redirect; @@ -2060,14 +2081,16 @@ }); return redirect !== ("//boards.4chan.org/" + post.board + "/"); }, - children: [] + subEntries: [] }; _ref = [['Post', 'post'], ['Name', 'name'], ['Tripcode', 'tripcode'], ['E-mail', 'email'], ['Subject', 'subject'], ['Filename', 'filename'], ['Image MD5', 'MD5']]; for (_i = 0, _len = _ref.length; _i < _len; _i++) { type = _ref[_i]; - entry.children.push(this.createSubEntry(type[0], type[1])); + entry.subEntries.push(this.createSubEntry(type[0], type[1])); } - return Menu.menu.addEntry(entry); + return d.dispatchEvent(new CustomEvent('AddMenuEntry', { + detail: entry + })); }, createSubEntry: function(text, type) { var el, open; diff --git a/lib/ui.coffee b/lib/ui.coffee index 2db8dad3c..593960a7e 100644 --- a/lib/ui.coffee +++ b/lib/ui.coffee @@ -17,7 +17,7 @@ UI = (-> constructor: (@type) -> # Doc here: https://github.com/MayhemYDG/4chan-x/wiki/Menu-API - $.on d, 'AddMenuEntry', @addEntryListener.bind @ + $.on d, 'AddMenuEntry', @addEntry.bind @ @close = close.bind @ @entries = [] @@ -84,14 +84,14 @@ UI = (-> return unless entry.open data $.add parent, entry.el - return unless entry.children + return unless entry.subEntries if submenu = $ '.submenu', entry.el # Reset sub menu, remove irrelevant entries. $.rm submenu submenu = $.el 'div', className: 'reply dialog submenu' - for child in entry.children - @insertEntry child, submenu, data + for subEntry in entry.subEntries + @insertEntry subEntry, submenu, data $.add entry.el, submenu return @@ -161,28 +161,25 @@ UI = (-> style.left = left style.right = right - addEntry: (entry) -> + addEntry: (e) -> + entry = e.detail + return if entry.type isnt @type @parseEntry entry @entries.push entry parseEntry: (entry) -> - {el, children} = entry + {el, subEntries} = entry $.addClass el, 'entry' $.on el, 'focus mouseover', ((e) -> e.stopPropagation() @focus el ).bind @ - return unless children + return unless subEntries $.addClass el, 'has-submenu' - for child in children - @parseEntry child + for subEntry in subEntries + @parseEntry subEntry return - addEntryListener: (e) -> - entry = e.detail - return if entry.type isnt @type - @addEntry entry - dragstart = (e) -> # prevent text selection diff --git a/src/features.coffee b/src/features.coffee index e46cb6655..67b7e1e11 100644 --- a/src/features.coffee +++ b/src/features.coffee @@ -101,8 +101,10 @@ Settings = textContent: '4chan X Settings' href: 'javascript:;' $.on link, 'click', Settings.open - Header.menu.addEntry - el: link + d.dispatchEvent new CustomEvent 'AddMenuEntry', + detail: + type: 'header' + el: link # 4chan settings link link = $.el 'a', @@ -110,9 +112,11 @@ Settings = textContent: '4chan Settings' href: 'javascript:;' $.on link, 'click', -> $.id('settingsWindowLink').click() - Header.menu.addEntry - el: link - open: -> !Conf['Disable 4chan\'s extension'] + d.dispatchEvent new CustomEvent 'AddMenuEntry', + detail: + type: 'header' + el: link + open: -> !Conf['Disable 4chan\'s extension'] return unless Conf['Disable 4chan\'s extension'] settings = JSON.parse(localStorage.getItem '4chan-settings') or {} @@ -300,11 +304,12 @@ Filter = textContent: 'Filter' entry = + type: 'post' el: div open: (post) -> Filter.menu.post = post true - children: [] + subEntries: [] for type in [ ['Name', 'name'] @@ -321,9 +326,10 @@ Filter = ['Image MD5', 'MD5'] ] # Add a sub entry for each filter type. - entry.children.push Filter.menu.createSubEntry type[0], type[1] + entry.subEntries.push Filter.menu.createSubEntry type[0], type[1] - Menu.menu.addEntry entry + d.dispatchEvent new CustomEvent 'AddMenuEntry', + detail: entry createSubEntry: (text, type) -> el = $.el 'a', @@ -475,15 +481,17 @@ ThreadHiding = makeStub = $.el 'label', innerHTML: " Make stub" - Menu.menu.addEntry - el: div - open: (post) -> - {thread} = post - if post.isReply or thread.isHidden - return false - ThreadHiding.menu.thread = thread - true - children: [{el: apply}, {el: makeStub}] + d.dispatchEvent new CustomEvent 'AddMenuEntry', + detail: + type: 'post' + el: div + open: (post) -> + {thread} = post + if post.isReply or thread.isHidden + return false + ThreadHiding.menu.thread = thread + true + subEntries: [{el: apply}, {el: makeStub}] hide: -> makeStub = $('input', @parentNode).checked {thread} = ThreadHiding.menu @@ -627,14 +635,16 @@ ReplyHiding = makeStub = $.el 'label', innerHTML: " Make stub" - Menu.menu.addEntry - el: div - open: (post) -> - if !post.isReply or post.isClone - return false - ReplyHiding.menu.post = post - true - children: [{el: apply}, {el: thisPost}, {el: replies}, {el: makeStub}] + d.dispatchEvent new CustomEvent 'AddMenuEntry', + detail: + type: 'post' + el: div + open: (post) -> + if !post.isReply or post.isClone + return false + ReplyHiding.menu.post = post + true + subEntries: [{el: apply}, {el: thisPost}, {el: replies}, {el: makeStub}] hide: -> parent = @parentNode thisPost = $('input[name=thisPost]', parent).checked @@ -795,11 +805,13 @@ ReportLink = href: 'javascript:;' textContent: 'Report this post' $.on a, 'click', ReportLink.report - Menu.menu.addEntry - el: a - open: (post) -> - ReportLink.post = post - !post.isDead + d.dispatchEvent new CustomEvent 'AddMenuEntry', + detail: + type: 'post' + el: a + open: (post) -> + ReportLink.post = post + !post.isDead report: -> {post} = ReportLink url = "//sys.4chan.org/#{post.board}/imgboard.php?mode=report&no=#{post}" @@ -834,20 +846,22 @@ DeleteLink = $.on fileEl, 'click', DeleteLink.delete !!post.file - Menu.menu.addEntry - el: div - open: (post) -> - return false if post.isDead - DeleteLink.post = post - node = div.firstChild - if seconds = DeleteLink.cooldown[post.fullID] - node.textContent = "Delete (#{seconds})" - DeleteLink.cooldown.el = node - else - node.textContent = 'Delete' - delete DeleteLink.cooldown.el - true - children: [postEntry, fileEntry] + d.dispatchEvent new CustomEvent 'AddMenuEntry', + detail: + type: 'post' + el: div + open: (post) -> + return false if post.isDead + DeleteLink.post = post + node = div.firstChild + if seconds = DeleteLink.cooldown[post.fullID] + node.textContent = "Delete (#{seconds})" + DeleteLink.cooldown.el = node + else + node.textContent = 'Delete' + delete DeleteLink.cooldown.el + true + subEntries: [postEntry, fileEntry] $.on d, 'QRPostSuccessful', @cooldown.start @@ -922,13 +936,15 @@ DownloadLink = a = $.el 'a', className: 'download-link' textContent: 'Download file' - Menu.menu.addEntry - el: a - open: (post) -> - return false unless post.file - a.href = post.file.URL - a.download = post.file.name - true + d.dispatchEvent new CustomEvent 'AddMenuEntry', + detail: + type: 'post' + el: a + open: (post) -> + return false unless post.file + a.href = post.file.URL + a.download = post.file.name + true ArchiveLink = init: -> @@ -938,6 +954,7 @@ ArchiveLink = textContent: 'Archive' entry = + type: 'post' el: div open: (post) -> redirect = Redirect.to @@ -945,7 +962,7 @@ ArchiveLink = threadID: post.thread postID: post.ID redirect isnt "//boards.4chan.org/#{post.board}/" - children: [] + subEntries: [] for type in [ ['Post', 'post'] @@ -957,9 +974,10 @@ ArchiveLink = ['Image MD5', 'MD5'] ] # Add a sub entry for each type. - entry.children.push @createSubEntry type[0], type[1] + entry.subEntries.push @createSubEntry type[0], type[1] - Menu.menu.addEntry entry + d.dispatchEvent new CustomEvent 'AddMenuEntry', + detail: entry createSubEntry: (text, type) -> el = $.el 'a',