Add unlimited sub menu support to the Menu.
Add Filter entries to the Menu. Close #511. You should be able to solve this.
This commit is contained in:
parent
356bad36ef
commit
76d56e3f70
169
4chan_x.user.js
169
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;\
|
||||
|
||||
179
script.coffee
179
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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user