您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Ignore users on the FastTech forums, and more
当前为
// ==UserScript== // @name FastTech Ignore List (BETA) // @namespace ftil // @description Ignore users on the FastTech forums, and more // @include https://*.fasttech.com/* // @version 2.1.0 // @grant GM_getValue // @grant GM_setValue // @run-at document-end // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNvyMY98AAAFvSURBVFjD7Zc9S8NAGMcDvnSUggjFkoTiJA6u+gGk2Nmhg4Ozk10Ul+s5lg65l95LxC8QaC/Uoh/Fwc3RyW9Q79RIK5hwGI1iDv4QAnfP7//cc8k9jlP0wFgEGHNpLco5QqiygW4q/oXiXnckrQVV4BAiOlpTQu2ECO8lJvRCPQ/GU2uBUSfJQsu4QkSEWdLOTQbaHzOpHbX1osZVmMiF6u4t2MPse+PehXHr27f3NWA89aG6LaS+/gZAwNiWLqpHW2Ei7s0p+DIApeG27QnAhD8NBmIvlwzMARBxhKhopgqJJmNsLbctmAVg7Krx4zVQApQAUsolhMK6EQBgMU8A92xSXQfDeg2MV60n66NWNVD9vkyffBAtmCBGmyBazu+eQPnly0eHiutUl+fDWvJ7dYHaLQFKgBKgBPjfAB4c7xQL0I3juQYkW0HOGciW//6sDPDJpwsjyg5NG6YvqsdpAI3TaMW+KY2FB9S+8xvGM2SF001PVSN6AAAAAElFTkSuQmCC // ==/UserScript== 'use strict'; var settings = (function() { var settings = { }; var watches = { }; function inner_set(name, value) { if (settings[name].value === value) return false; settings[name].value = value; if (name in watches) { var w = watches[name]; for (var i = 0; i < w.length; i++) w[i](name, value); } return true; } window.addEventListener('storage', function(e) { if (e.key in settings) inner_set(e.key, JSON.parse(e.newValue)); }); return { get: function(name) { return settings[name].value }, set: function(name, value) { if (inner_set(name, value)) localStorage.setItem(name, JSON.stringify(value)); }, all: function() { return settings }, register: function(name, desc, def, dep) { var val = localStorage.getItem(name); if (val === null) val = GM_getValue(name, null); if (val !== null) val = JSON.parse(val); else val = def; settings[name] = { value: val, desc: desc, dep: dep, }; this.set(name, val); }, watch: function(name, func) { if (!(name in watches)) watches[name] = []; watches[name].push(func); if (settings[name] !== undefined) func(name, settings[name].value); }, }; })(); // Workaround for AMO warnings about javascript in innerHTML function fix_as(e) { var as = e.getElementsByTagName('a'); for (var i = 0; i < as.length; i++) { if (!as[i].hasAttribute('href')) as[i].href = 'javascript:void(0)'; } } // Sanitize external values that may be fed into an innerHTML var strip_tags = (function() { var div = document.createElement('div'); return function(t) { div.innerHTML = t; return div.textContent; } })(); /* Block functions that need the entire state set up before running. In * particular, this is used by post_filter to avoid repeatedly filtering posts. */ var block = (function() { var block = 1; var watches = []; return { get: function() { return block !== 0 }, block: function() { block++ }, unblock: function() { if (!(--block)) { for (var i = 0; i < watches.length; i++) watches[i]() delete this.block delete this.unblock; this.watch = function(func) { func() }; } }, watch: function(func) { watches.push(func) }, }; })(); // Assorted generic handlers for user input. var handlers = (function() { return { // A click on elm toggles the ignored state of user ignore_toggle: function(elm, user) { elm.setAttribute('username', strip_tags(user)); elm.addEventListener('click', function() { ignore_list.set(user) }); }, // A click on elm toggles the state of setting; setting changes update elm checkbox: function(elm, setting) { elm.addEventListener('change', function() { settings.set(setting, this.checked) }); settings.watch(setting, function(name, val) { elm.checked = val; }); }, // Handle enter keypress on manage_menu's text box addbox_enter: function(elm) { elm.addEventListener('keydown', function(k) { if (k.which !== 13) return true; k.stopPropagation(); k.preventDefault(); ignore_list.set(strip_tags(this.value), true); this.value = ''; return false; }); }, // Handle click on manage_menu's add button addbox_click: function(elm, tgt) { elm.addEventListener('click', function() { ignore_list.set(strip_tags(tgt.value), true); tgt.value = ''; }); }, top_click: function(elm) { elm.addEventListener('click', function() { scrollTo(0, 0) }); }, hide_toggle: function(elm, post) { elm.addEventListener('click', function() { unhide_posts.set(post) }); }, }; })(); // Manage the state of the ignore list var ignore_list = (function() { settings.register('ignorelist', null, [], null); // Use two stages so that filters can handle updates before being called var watches = { 'early': [], 'late': [] }; var il = []; // Case-insensitive indexOf (needed in case of case errors in user input) function ci_indexof(a, n) { for (var i = 0; i < a.length; i++) { if (a[i].toLowerCase() === n.toLowerCase()) return i; } return -1; } function fire_watches(stage, user, state, idx) { var w = watches[stage]; for (var i = 0; i < w.length; i++) w[i](user, state, idx); } // In order to support userscripts, we need to rebuild the list on every set function inner_set(user, state, idx) { var nl = []; var tmp = settings.get('ignorelist'); for (var i = 0; i < tmp.length; i++) nl.push(tmp[i]); if (state) nl.push(user); else nl.splice(idx, 1); settings.set('ignorelist', nl); } // Watch for ignorelist changes, and fire incremental watch events settings.watch('ignorelist', function(name, val) { var i = 0, j = 0; while (true) { if (il[i] === undefined && val[j] === undefined) break; if (il[i] !== val[j]) { var user, state, idx; if (il[i] === undefined) { // New entry @ end of list: user added user = val[j]; il.push(user); state = true; idx = i; } else { // Missing entry inside list: user removed user = il[i]; il.splice(i, 1); state = false; idx = i; } fire_watches('early', user, state, idx); fire_watches('late', user, state, idx); // Userscript compat: handle multiple changes from other tabs continue; } i++; j++; } }); return { // Get ignored state of user (true == ignored) get: function(user) { return (ci_indexof(il, user) !== -1) }, // Set ignored state of user (undefined == toggle) set: function(user, state) { var idx = ci_indexof(il, user); if (state === undefined) state = (idx === -1); if ((state && (idx === -1)) || (!state && (idx !== -1))) inner_set(user, state, idx); }, /* Watch the ignorelist for changes (stage == ('early'|'late')). * func may be called with a single user and a new state, or an array of * users (with implicit ignored state for all) */ watch: function(stage, func) { watches[stage].push(func); if (il !== []) func(il, true, -1); }, }; })(); /* Core post filtering logic * post vars are expected to be in the format * { user: string, content_elm: element, post_elms: [element] } */ var post_filter = (function() { var posts = []; var watches = []; var filters = []; function update() { if (block.get()) return; for (var i = 0; i < posts.length; i++) { var post = posts[i]; var hide = false; for (var j = 0; j < filters.length; j++) { var tmp = filters[j](post); if (tmp === true) { hide = true; } else if (tmp === false) { hide = false; break; } } var display = hide ? 'none' : ''; for (var j = 0; j < post.post_elms.length; j++) post.post_elms[j].style.display = display; for (var j = 0; j < watches.length; j++) watches[j](post, hide); } } return { // Register a list of posts to be filtered register: function(list) { posts = list; block.watch(update); }, /* Add a new filter. Must be called before the unblock. Filters return * true (should hide), false (must not hide), or undefined (no judgement). */ filter: function(func) { filters.push(func) }, // Run all filters again update: update, /* Call func with changes to the hidden status of each post (true == * hidden). Must be called before the unblock. */ watch: function(func) { watches.push(func) }, }; })(); // General-purpose built-in filters: // Hide posts by ignored users post_filter.filter((function() { settings.watch('hide_ignored', post_filter.update); settings.register('hide_ignored', 'Hide posts by ignored users', true, null); ignore_list.watch('late', post_filter.update); return function(post) { if (settings.get('hide_ignored') && ignore_list.get(post.user)) return true; } })()); // Temporarily unhide hidden posts var unhide_posts = (function() { var posts = []; post_filter.filter(function(post) { if (posts.indexOf(post) !== -1) return false; }); return { // Set the unhide state of a post (true == unhide, undefined == toggle) set: function(post, state) { var idx = posts.indexOf(post); if (state === undefined) state = (idx === -1); if (state && idx === -1) posts.push(post); else if (!state && idx !== -1) posts.splice(idx, 1); else return; post_filter.update(); } }; })(); // Generic buttons to toggle ignored state of a user var ignore_buttons = (function() { var ignbtns = { }; var inner_ign = ''; var inner_unign = ''; function inner_update(user, state) { var elms = ignbtns[user]; if (elms === undefined) return; var fmt = inner_ign; if (state) fmt = inner_unign; for (var i = 0; i < elms.length; i++) elms[i].innerHTML = fmt; } function update_btns(users, state) { if (users instanceof Array) { for (var i = 0; i < users.length; i++) inner_update(users[i], state); } else { inner_update(users, state); } } return { // Set the (HTML) format of the (un)ignore buttons set: function(ign, unign) { inner_ign = ign; inner_unign = unign; }, // Register an element to serve as an ignore button for user register: function(user, elm) { if (ignbtns[user] === undefined) ignbtns[user] = []; ignbtns[user].push(elm); handlers.ignore_toggle(elm, user); elm.innerHTML = inner_ign; }, // Wire ignore_buttons to ignore_list to keep all buttons updated update: function() { ignore_list.watch('late', update_btns) } }; })(); // Handle RES-style tagging of users var user_tags = (function() { settings.register('user_tags', null, { }, null); var badges = []; function update_badges(n, val) { for (var i = 0; i < badges.length; i++) { var b = badges[i]; var t = val[b.user]; if (t === undefined) t = b.saved; for (var j = 0; j < b.elms.length; j++) b.elms[j].innerHTML = strip_tags(t); } } // Return a function to prompt for a new tag for user function gen_prompt(user) { return (function(e) { e.preventDefault(); e.stopPropagation(); var tmp = settings.get('user_tags'); var def = ''; if (tmp[user] !== undefined) def = tmp[user]; var tag = prompt('Tag for ' + user + ' or blank for default:', def); if (tag === null) return; tag = strip_tags(tag); // Ugh. var tags = { }; for (var u in tmp) tags[u] = tmp[u]; if (tags[user] !== undefined && tag === '') delete tags[user]; else if (tag !== '') tags[user] = tag; settings.set('user_tags', tags); }); } return { // Register elm as the element showing user's tag register: function(user, elm) { user = strip_tags(user); var i; for (i = 0; i < badges.length; i++) { if (badges[i].user === user) break; } if (i === badges.length) badges.push({ user: user, saved: elm.innerHTML, func: gen_prompt(user), elms: [] }); var b = badges[i]; b.elms.push(elm); elm.addEventListener('click', b.func); }, // Wire user_tags to settings to track changes update: function() { settings.watch('user_tags', update_badges) }, }; })(); // Track the last viewed page and post count for threads var last_viewed = (function() { var db; var store = 'thread_data'; // Accumulate commands and fire them once idb is open var cmds = []; function run_cmds() { if (db === undefined) return; var cmd; while (cmd = cmds.shift()) { if (cmd.cmd === 'set') { delete cmd.cmd; var tr = db.transaction(store, 'readwrite'); var os = tr.objectStore(store); var req = os.get(cmd.id); req.onsuccess = (function(os, cmd) { return function(e) { var t = e.target.result; if (t === undefined) { t = cmd; } else { t.lvt = cmd.lvt; t.lvp = cmd.lvp; if (cmd.lpn > t.lpn) { t.lpn = cmd.lpn; t.lp = cmd.lp; } } os.put(t); } })(os, cmd); } else if (cmd.cmd === 'get') { var os = db.transaction(store, 'readonly').objectStore(store); os.get(cmd.id).onsuccess = (function(cmd) { return function(e) { var t = e.target.result; if (t === undefined) return; cmd.f(cmd.e, t.lvp, t.lpn, t.lp); } })(cmd); } } } /* Periodically scrub the database (FIXME this is awful & may run in multiple * tabs simultaneously) */ settings.register('lv_scrub_time', null, 0, null); // Open idb and maybe remove old threads (function() { var idb = indexedDB; var req = idb.open('ignore_list', 2); req.onupgradeneeded = function(e) { var tmp = e.target.result; if (!tmp.objectStoreNames.contains(store)) { var os = tmp.createObjectStore(store, { keyPath: 'id' }); os.createIndex('lvt', 'lvt'); } }; req.onsuccess = function(e) { db = e.target.result; run_cmds(); setTimeout(function() { var now = Date.now(); // This is awful, but it's better than scrubbing every page load. if (settings.get('lv_scrub_time') + 24 * 60 * 60 * 1000 > now) return; var tr = db.transaction(store, 'readwrite'); var os = tr.objectStore(store); os.index('lvt') .openCursor(IDBKeyRange.upperBound(now - 14 * 24 * 60 * 60 * 1000)) .onsuccess = function(e) { var c = e.target.result; if (c) { os.delete(c.primaryKey); c.continue(); } else { settings.set('lv_scrub_time', now); } }; }, 5000); }; })(); return { /* Record a visit to thread_id, on page last_viewed_page, with up to * last_post_nr (of id last_post_id) visible. Should be called on every * (thread) pageview. */ set: function(thread_id, last_viewed_page, last_post_nr, last_post_id) { cmds.push({ cmd: 'set', id: thread_id, lvt: Date.now(), lvp: last_viewed_page, lpn: last_post_nr, lp: last_post_id, }); run_cmds(); }, // Call func with the view history of thread_id (and pass extra as well) get: function(thread_id, func, extra) { cmds.push({ cmd: 'get', id: thread_id, f: func, e: extra }); run_cmds(); }, }; })(); // Generic settings menu var settings_menu = (function() { return { /* Attach a settings menu to elm. Menus is a list of submenus, in the form * [ { title: 'str' | undefined, vars: [ "name" ] } ] */ register: function(elm, menus) { var s = settings.all(); for (var i = 0; i < menus.length; i++) { var menu = menus[i]; var d; if (i !== 0) elm.appendChild(document.createElement('hr')); if (menu.title !== undefined) { d = elm.appendChild(document.createElement('div')); d.innerHTML = '<b>' + menu.title + '</b>'; } for (var j = 0; j < menu.vars.length; j++) { var v = menu.vars[j]; d = elm.appendChild(document.createElement('div')); d.innerHTML = '<input type="checkbox"></input><label></label>'; var e = d.children[0]; handlers.checkbox(e, v); if (s[v].dep !== null) { var ds = s[v].dep.match(/(!)?(.*)/); settings.watch(ds[2], (function(e, i) { return function(n, v) { e.disabled = v ^ i } })(e, !ds[1])); } e.id = 'ilcb_' + v; d.children[1].innerHTML = s[v].desc; d.children[1].setAttribute('for', 'ilcb_' + v); } } } }; })(); // Display a nice menu for managing the ignorelist var manage_menu = (function() { var menu_list; var menu_elms = []; var remfmt = ''; function inner_update(user, state) { var i; for (i = 0; i < menu_elms.length; i++) { // Keep menu alphabetical var cmp = user.toLowerCase() .localeCompare(menu_elms[i].user.toLowerCase()); if (cmp === 0) { if (!state) { menu_elms[i].elm.remove(); menu_elms.splice(i, 1); } return; } else if (cmp === -1) { break; } } if (state) { var tr = document.createElement('tr'); tr.innerHTML = '<td style="padding: 0px">' + user + '</td>' + '<td align="right" style="padding: 0px"><a username=' + user + '>' + remfmt + '</a></td>'; fix_as(tr); handlers.ignore_toggle(tr.getElementsByTagName('a')[0], user); if (i < menu_elms.length) { menu_list.insertBefore(tr, menu_elms[i].elm); menu_elms.splice(i, 0, { user: user, elm: tr }); } else { menu_list.appendChild(tr); menu_elms.push({ user: user, elm: tr}); } } } function update(user, state) { if (menu_list === undefined) return; if (user instanceof Array) { for (var i = 0; i < user.length; i++) inner_update(user[i], true); } else { inner_update(user, state); } } return { // Set the format of the remove/unignore button set: function(fmt) { remfmt = fmt }, // Attach management elements to elm register: function(elm) { var tbl = elm.appendChild(document.createElement('table')); tbl.innerHTML = '<tbody><tr><td colspan="2" style="padding: 0px">' + '<input type="text" placeholder="Add user..." width="auto">' + '</input> <div class="il_addbtn">Add</div></td></tr></tbody>'; menu_list = tbl.children[0]; var td = menu_list.children[0].children[0]; handlers.addbox_enter(td.children[0]); handlers.addbox_click(td.children[1], td.children[0]); ignore_list.watch('late', update); }, }; })(); // Where are we? // N.B. Permalink URLs are matched by both thread and perma. var ftl = { mobile: '^https?://m\\.', thread: '^https?://(m|www)\\.fasttech\\.com/forums/[^/]+/t/[0-9]+/.', perma: '^https?://(m|www)\\.fasttech\\.com/forums' + '/[^/]+/t/[0-9]+/[^/]+\\?[0-9]+$', threadlist: '^https?://(m|www)\\.fasttech\\.com/forums' + '(/[^/]+(/page/[0-9]+)?(/search\\?.*)?)?$', author: '^https?://(m|www)\\.fasttech\\.com/forums/author/', product: '^https?://(m|www)\\.fasttech\\.com/p(roducts?)?/', settings: '^https?://www\\.fasttech\\.com/forums/settings$', }; for (var k in ftl) ftl[k] = new RegExp(ftl[k]).test(location.href); // Menu configuration var full_menus = [ { title: 'General', vars: ['track_viewed', 'track_posts', 'always_unread', 'hide_threads', 'quote_quotes', 'quote_imgs'] }, { title: 'Thread Menu', vars: ['inthread_menu', 'inthread_manage'] }, { title: 'Post Hiding', vars: ['placeholders', 'hide_ignored', 'hide_quotes', 'hide_deleted'] }, ]; var thread_menu; if (true) { thread_menu = [ { title: 'General', vars: ['quote_quotes', 'quote_imgs'] }, { title: 'Post Hiding', vars: ['hide_ignored', 'hide_quotes', 'hide_deleted'] }, ]; } else { thread_menu = full_menus; } manage_menu.set('<img alt="Remove" ' + 'src="https://www.fasttech.com/images/minus-button.png"></img></a></td>'); settings.register('inthread_menu', 'Show menu in thread toolbar', true, null); settings.register('inthread_manage', 'Manage ignored users from the menu', false, 'inthread_menu'); settings.register('agree_tou', null, false, null); settings.register('placeholders', 'Show placeholders for hidden posts', true, null); settings.register('track_viewed', 'Link to last viewed page', false, null); settings.register('track_posts', 'Link to unread posts', true, null); settings.register('always_unread', 'Always show unread posts button', false, 'track_posts'); settings.register('hide_quotes', 'Hide posts quoting ignored users', false, 'hide_ignored'); settings.register('hide_deleted', 'Hide deleted posts', true, null); settings.register('hide_threads', 'Hide threads by ignored users', false, null); settings.register('quote_quotes', 'Include quotes in quotes', true, null); settings.register('quote_imgs', 'Include images in quotes', true, null); if (ftl.thread) { // Add a filter for posts quoting ignored users post_filter.filter((function() { var quote_res = []; function new_qre(user) { quote_res.push( new RegExp('\\b' + user + ' wrote(</a>)?:?[\r\n]*<br>', 'i')); } function update(user, state, idx) { if (user instanceof Array) { quote_res = []; for (var i = 0; i < user.length; i++) new_qre(user[i]); } else { if (state) new_qre(user); else quote_res.splice(idx, 1); } } ignore_list.watch('early', update); settings.watch('hide_quotes', post_filter.update); return function(post) { if (settings.get('hide_ignored') && settings.get('hide_quotes')) { for (var i = 0; i < quote_res.length; i++) { if (quote_res[i].test(post.content_elm.innerHTML)) return true; } } } })()); // Add a filter for posts that the admins have deleted post_filter.filter((function() { var deleted_re = /post deleted/i; var edited_re = /^[ \r\n]*$/; settings.watch('hide_deleted', post_filter.update); return function(post) { if (settings.get('hide_deleted')) { var wb = post.content_elm.getElementsByClassName('WarningBox')[0]; if ((wb !== undefined && deleted_re.test(wb.innerHTML)) || edited_re.test(post.content_elm.innerHTML)) return true; } } })()); } window.setTimeout(block.unblock, 0); // Get all post bodies, desktop and mobile function get_posts() { var ret = []; var posts = document.getElementsByClassName('PostContent'); for (var i = 0; i < posts.length; i++) { var pc = posts[i]; var user = pc.getAttribute('data-username'); if (user !== null) ret.push({ user: user, content_elm: pc }); } return ret; } // Create a function to create placeholders as needed function gen_watch_filter(ph_func) { return function(post, state) { var placeholders = settings.get('placeholders'); if (state && placeholders) { if (!post.placeholder) post.placeholder = ph_func(post); post.placeholder.style.display = ''; } else { if (post.placeholder !== undefined) post.placeholder.style.display = 'none'; } } } // Get the current and last pages from a pager element, desktop and mobile function parse_pager(elm) { var cur, last; if (elm === undefined || elm === null) { cur = last = 1; } else if (!ftl.mobile) { var sel = elm.getElementsByClassName('PageLink_Selected')[0]; if (sel !== undefined) cur = parseInt(sel.innerHTML); last = parseInt(elm.lastElementChild.previousElementSibling.innerHTML); } else { var sel = elm.getElementsByClassName('active')[0]; if (sel !== undefined) cur = parseInt(sel.firstElementChild.innerHTML); last = parseInt(elm.lastElementChild.firstElementChild.innerHTML); } return { cur: cur, last: last }; } if (!ftl.mobile) { /* Jump-to-page boxes are broken site-wide. Fix them. * The existing JumpToPage box could be fixed by changing "function (e)" to * "function (event)" in the event listener, but instead, I'm doing all this. */ var jtpbox = document.getElementById('JumpToPage'); if (jtpbox !== null) { var lastpg = parse_pager(jtpbox.parentElement).last; var as = jtpbox.parentElement.getElementsByTagName('a'); if (as[0] !== undefined) { var base = as[0].href.match(new RegExp('(.*/)[0-9]+($|[?#/].*)')); if (base !== null) { jtpbox.addEventListener('keypress', function(k) { if (k.which === 13) { k.preventDefault(); k.stopPropagation(); var pg = parseInt(this.value); if (pg > 0 && pg < lastpg) location.href = base[1] + pg + base[2]; } return false; }); } } } } // RE to pull a thread id from a URL var thread_id_re = new RegExp('/forums/[^/]+/t/([0-9]+)/[^?]*$'); if (ftl.thread) { var thread_id = location.href.match(thread_id_re); if (thread_id !== null && thread_id[1] !== undefined) thread_id = parseInt(thread_id[1]); else thread_id = undefined; } // Translate a post number into a page number, handling FT's off-by-one quirk function postnr_to_pagenr(post, maxpage) { return Math.floor(Math.min(maxpage, post / 10 + 1)); } // Create a link to a specific page of a thread function gen_page_link(base, pn, unread) { var ret = base; if (pn !== 1) ret += '/' + pn; if (unread === true) ret += '#unread'; return ret; } // Find the reply button for a post var replace_quotebtn; // Add a "Top of page" button at the bottom of the page, aligned with the pager var add_top_btn; if (!ftl.mobile) { add_top_btn = function() { var elm = document.getElementsByClassName('ForumThread TightTable')[0]; if (elm) elm = elm.nextElementSibling; if (!elm) return; elm.className = 'Pager'; var tbtn = document.createElement('span'); tbtn.className = 'ControlArrows'; tbtn.setAttribute('style', 'float: left; min-width: 150px; ' + 'max-width: 250px; text-align: center'); tbtn.innerHTML = '<a>Top of page</a>'; fix_as(tbtn); handlers.top_click(tbtn); var pager = elm.firstElementChild; elm.appendChild(tbtn); if (pager) { var pspan = document.createElement('span'); pspan.appendChild(pager); elm.appendChild(pspan); } else { elm.parentElement .insertBefore(document.createElement('br'), elm.nextElementSibling); } }; } else { add_top_btn = function() { var elm = document.getElementsByClassName('ListRow')[0]; if (elm) elm = elm.parentElement.nextElementSibling; if (!elm) return; var tbtn = document.createElement('button'); tbtn.className = 'btn btn-default'; tbtn.innerHTML = 'Top'; handlers.top_click(tbtn); if (elm.firstElementChild) { tbtn.style.marginTop = '-5px'; elm = elm.firstElementChild; var td = document.createElement('td'); td.style.textAlign = 'right'; td.appendChild(tbtn); elm.style.width = '100%'; elm.firstElementChild.firstElementChild.appendChild(td); } else { var d = elm.appendChild(document.createElement('div')); d.setAttribute('style', 'width: 100%; text-align: right; ' + 'margin-bottom: 8px'); d.appendChild(tbtn); } }; } if (ftl.thread) { add_top_btn(); if (!ftl.mobile) { // Persist the terms of use checkbox var toubox = document.getElementById('AgreeTerms'); if (toubox !== null) handlers.checkbox(toubox, 'agree_tou'); replace_quotebtn = function(elm) { // Ugh. var oldbtn = elm.parentElement.parentElement.parentElement .previousElementSibling.firstElementChild.lastElementChild .firstElementChild; var newbtn = oldbtn.parentElement .insertBefore(document.createElement('a'), oldbtn); oldbtn.remove(); newbtn.innerHTML = 'quote/reply'; newbtn.href = 'javascript:void(0)'; return newbtn; }; setTimeout(function() { var ta = document.getElementsByClassName('mbbc-editor')[0]; if (ta !== undefined) { // Remove the broken size=[67] buttons var size_popup = document.getElementsByClassName('mbbc-size-popup')[0]; if (size_popup !== undefined) { size_popup.lastElementChild.remove(); size_popup.lastElementChild.remove(); } // Fix the editor width ta.style.boxSizing = 'border-box'; ta.style.width = '100%'; // Add a YouTube button var url_btn = document.getElementsByClassName('mbbc-url')[0]; if (url_btn !== undefined) { var yt_btn = url_btn.parentElement .insertBefore(document.createElement('li'), url_btn); yt_btn.className = 'mbbc-youtube'; yt_btn.addEventListener('click', function() { var sp = ta.selectionStart; var ep = ta.selectionEnd; var sel; if (ep < sp) { sp = ep; ep = ta.selectionStart; } if (ep !== sp) { sel = ta.value.slice(sp, ep); } else { sel = prompt('Video URL:', ''); if (sel === null) return; var vid = sel.match(new RegExp('[/?=#]([-\\w]{11})([^-\\w]|$)')); if (vid !== null) sel = 'https://www.youtube.com/watch?v=' + vid[1]; } if (sel === undefined) return; ta.value = ta.value.slice(0, sp) + '[youtube]' + sel + '[/youtube]' + ta.value.slice(ep); ta.selectionStart = ta.selectionEnd = sp + 9 + (sel === '' ? 0 : 10 + sel.length); ta.focus(); }); } } }, 1000); } else { // mobile // Add a link to forum settings to allow ignorelist management // Ugh. There's no semi-robust way to select this. var li = document.querySelector('ul.nav:nth-child(1)>li:nth-child(3)' + '>ul:nth-child(2)>li:nth-child(8)'); if (li !== null) { var new_li = document.createElement('li'); new_li.innerHTML = '<a title="Forum Settings" ' + 'href="https://www.fasttech.com/forums/settings">Forum Settings</a>'; li.parentElement.insertBefore(new_li, li); } // Fix up YT iframe size: CSS doesn't work well here. var ifrs = document.getElementsByTagName('iframe'); for (var i = 0; i < ifrs.length; i++) { var ifr = ifrs[i]; if (ifr.clientWidth > ifr.parentElement.clientWidth) { var ar = ifr.clientHeight / ifr.clientWidth; ifr.style.width = '100%'; ifr.style.height = Math.ceil(ifr.clientWidth * ar) + 'px'; } } replace_quotebtn = function(elm) { // Ugh. var oldbtn = elm.parentElement.firstElementChild.lastElementChild .lastElementChild.firstElementChild; var newbtn = oldbtn.parentElement .insertBefore(document.createElement('a'), oldbtn); newbtn.innerHTML = oldbtn.innerHTML; oldbtn.remove(); newbtn.href = 'javascript:void(0)'; newbtn.tabindex = '-1'; newbtn.role = 'menuitem'; return newbtn; }; } } /* Fix up display of certain post elements: * - Fix align tags (which should have been 100% divs to begin with) * - Avoid breaking SKU links across lines */ document.body.appendChild(document.createElement('style')).innerHTML = '.PostContent span[style*="text-align:"] {display:block;width:100%}' + 'a.SKUAutoLink {display=inline-block}'; // Re-implement quotes in a way that sort of works var fix_quote_btns = (function() { var textarea; var editor; var edit_panel; var expand; var thread_id; function init(elms) { if (!ftl.mobile) textarea = document.getElementsByName('mbbc-editor')[0]; else textarea = document.getElementById('RawContent'); if (!textarea || !replace_quotebtn) return; if (!ftl.mobile) { editor = textarea.parentElement.parentElement; } else { edit_panel = document.getElementById('writePost'); if (edit_panel) { editor = edit_panel.parentElement; expand = edit_panel.previousElementSibling.firstElementChild .firstElementChild; } } var tid = location.href.match(new RegExp('/t/([0-9]+)/')); if (tid !== null) thread_id = tid[1]; for (var i = 0; i < elms.length; i++) { var elm = elms[i]; var newbtn = replace_quotebtn(elm.content_elm); newbtn.addEventListener('click', (function(elm) { return (function() { quote(elm.user, elm.content_elm) })})(elm)); } } function wrap_tag(tag, val, str) { return '[' + tag + (val ? '=' + val : '') + ']' + str + '[/' + tag + ']'; } function inner_quote(elm, range, depth) { var tmp = ''; for (var e = elm.firstChild; e !== null; e = e.nextSibling) { var sp, ep; if (range !== undefined) { if (range.startContainer.compareDocumentPosition(e) === 2) continue; if (range.endContainer.compareDocumentPosition(e) === 4) break; if (range.startContainer === e) sp = range.startOffset; if (range.endContainer === e) ep = range.endOffset; } switch (e.tagName) { case 'BR': tmp += '\n'; break; case 'STRONG': tmp += wrap_tag('b', '', inner_quote(e, range, depth)); break; case 'EM': tmp += wrap_tag('i', '', inner_quote(e, range, depth)); break; case 'HR': tmp += '[hr]'; break; case 'P': if (e.innerHTML !== '') tmp += '\n' + inner_quote(e, range, depth); break; case 'SPAN': var s = e.style; var t = null; var v = ''; if (s.textDecoration === 'underline') { t = 'u'; } else if (s.textDecoration === 'line-through') { t = 's'; } else if (s.fontSize !== '') { t = 'size'; v = parseInt(s.fontSize); } else if (s.color !== '') { t = 'color'; v = s.color; } else if (s.textAlign !== '') { t = 'align'; v = s.textAlign; } else { console.log('Unknown span!'); } tmp += wrap_tag(t, v, inner_quote(e, range, depth)); break; case 'DIV': if (e.className === 'ForumQuote') { if (tmp[tmp.length - 1] !== '\n') tmp += '\n'; if (depth === 0 && settings.get('quote_quotes')) tmp += wrap_tag('quote', '', inner_quote(e.firstElementChild, range, depth + 1).trim()); else tmp += '(snip)\n'; } else { console.log('Unknown div!'); } break; case 'IFRAME': var m = e.src.match(/youtube[^\/]*.com.*\/embed\/([^?]+)/); if (m !== null && m[1] !== undefined) { /* A YouTube tag inside a quote breaks the parsing of the quote tag. tmp += wrap_tag('youtube', '', 'https://www.youtube.com/watch?v=' + m[1]) */ tmp += wrap_tag('url', 'https://www.youtube.com/watch?v=' + m[1], 'YouTube video'); } else { console.warn('Unknown iframe!'); } break; case 'IMG': if (settings.get('quote_imgs')) { tmp += wrap_tag('img', '', e.src); } else if (e.parentElement.tagName !== 'A') { tmp += wrap_tag('url', e.src, 'Image'); } else { tmp += 'Image'; } break; case 'A': if (e.className !== 'SKUAutoLink') { tmp += wrap_tag('url', e.href, inner_quote(e, range, depth)); } else { tmp += e.textContent; } break; case undefined: // Filter out newlines and empty tags tmp += e.wholeText.slice(sp ? sp : 0, ep) .replace(/[\r\n]+/mg, '') .replace(/\[([^\]=]*)(=[^\]]*)?\]\[\/\1\]/mg, ''); break; default: console.warn('Unhandled tag ' + e.tagName); } } return tmp; } function quote(user, elm) { var post_id; var pid = elm.id.match(/^POST([0-9]+)$/); if (pid !== null) post_id = pid[1]; var sel = getSelection(); if (sel.rangeCount > 0) { sel = sel.getRangeAt(0); if (sel.collapsed || !sel.intersectsNode(elm)) sel = undefined; } else { sel = undefined; } if (textarea.value.length > 0 && textarea.value[textarea.value.length - 1] !== '\n') textarea.value += '\n'; if (thread_id !== undefined && post_id !== undefined) textarea.value += wrap_tag('url', '/forums/-/t/' + thread_id + '?' + post_id, user + ' wrote') + ':\n'; else textarea.value += user + ' wrote:\n'; textarea.value += wrap_tag('quote', '', inner_quote(elm, sel, 0).trim().replace(/[\r\n]{3,}/mg, '\n\n')) + '\n'; if (editor) editor.scrollIntoView(); if (expand && edit_panel && edit_panel.className && !/\bin\b/.test(edit_panel.className)) expand.click(); } return function(elms) { // Delay fixing links until the editor has loaded. 1s should be enough. setTimeout(function() { init(elms) }, 1000); } })(); function scroll_to_unread(tid, posts) { last_viewed.get(tid, function(posts, lvp, lpn, lp) { var i; var pid = 'POST' + lp; for (i = posts.length - 1; i >= 0; i--) { if (posts[i].content_elm.id === pid) break; } // Drop the #unread so we won't run again history.replaceState({}, '', location.pathname); // We've already seen all posts if (i === posts.length - 1) return; i = Math.min(i + 1, posts.length - 1); // scrollIntoView() doesn't work on hidden elements. var elm = posts[i].post_elms[0]; var sd = elm.style.display; elm.style.display = ''; elm.scrollIntoView(); elm.style.display = sd; }, posts); } if (ftl.threadlist || ftl.product || ftl.author || ftl.settings) { if (!ftl.mobile) { if (!ftl.settings) add_top_btn(); block.watch(function() { var add_last_viewed = function(elm, last_viewed_page, last_post_nr, last_post_id) { var row = elm.parentElement; var pelm = row.parentElement.getElementsByClassName('Pager')[0]; var p = parse_pager(pelm); if (pelm === undefined) { if (!ftl.settings) { pelm = row.lastElementChild; pelm.style.display = ''; pelm = pelm.appendChild(document.createElement('div')); pelm.className = 'Pager'; } else { pelm = row.parentElement.appendChild(document.createElement('div')); pelm.className = 'Pager'; } } if (settings.get('track_viewed')) { var lv = pelm.appendChild(document.createElement('span')); lv.innerHTML = '<a href="' + gen_page_link(elm.href, last_viewed_page) + '">Last viewed</a>'; lv.className = 'FieldFlag'; lv.style.padding = '7px 8px 5px'; lv.firstElementChild.style.fontSize = '10pt'; } var tr = row.parentElement.parentElement; var ts = tr.getElementsByClassName('ThreadStats')[0]; var pc; if (ts !== undefined) pc = (pc = ts.innerHTML.match(/Replies: ([0-9]+)/)) ? parseInt(pc[1]) : undefined; var unread = pc > 0 && pc >= last_post_nr; if ((unread || settings.get('always_unread')) && settings.get('track_posts')) { var np = pelm.appendChild(document.createElement('span')); np.className = 'FieldFlag'; var npa = np.appendChild(document.createElement('a')); if (unread) { np.style.backgroundColor = '#AA0D0D'; npa.innerHTML = 'Unread posts'; npa.href = gen_page_link(elm.href, postnr_to_pagenr(last_post_nr, p.last), true); } else { np.style.backgroundColor = '#F0F0F0'; npa.innerHTML = 'No unread posts'; npa.style.color = '#333'; } np.style.padding = '7px 8px 5px'; npa.style.fontSize = '10pt'; } }; var header = document.getElementsByClassName('ForumHeader')[0]; var filter = settings.get('hide_threads'); var sku_re = new RegExp('fasttechcdn.com/[0-9]+/([0-9]+)/.*'); if (header !== undefined) { var tbl = header.parentElement; for (var i = 1; i < tbl.children.length; i += 2) { var tr = tbl.children[i]; var tl = tr.getElementsByClassName('ThreadLink')[0]; var tid; if (tl !== undefined && (settings.get('track_posts') || settings.get('track_threads'))) { tid = parseInt(tl.href.match(thread_id_re)[1]); if (!isNaN(tid)) last_viewed.get(tid, add_last_viewed, tl); } var img = tr.firstElementChild.firstElementChild; if (img && img.tagName === 'IMG') { var sku = img.src.match(sku_re); if (sku !== null) { var a = img.parentElement .appendChild(document.createElement('a')); a.href = '/products/' + sku[1]; a.appendChild(img); } } if (filter) { var u = tl.parentElement.textContent.match(/started\W+by\W+(\w+)/); if (u && u[1] && ignore_list.get(u[1])) { tr.style.display = 'none'; if (tr.previousElementSibling !== header) tr.previousElementSibling.style.display = 'none'; } } } } }); } else { // mobile block.watch(function() { var add_last_viewed = function(elm, last_viewed_page, last_post_nr, last_post_id) { var rh = elm.parentElement; var tr = rh.parentElement; var p = parse_pager(tr.getElementsByClassName('pagination')[0]); var d = rh.appendChild(document.createElement('div')); d.style.marginLeft = '-5px'; if (settings.get('track_viewed')) { var lv = d.appendChild(document.createElement('span')); lv.innerHTML = '<a href="' + gen_page_link(elm.href, last_viewed_page) + '">Last viewed</a>'; lv.className = 'btn btn-default'; lv.style.margin = '5px'; } var pc = (pc = tr.textContent.match(/R(eplies)?: ([0-9]+)/)) ? parseInt(pc[2]) : undefined; var unread = pc > 0 && pc >= last_post_nr; if ((unread || settings.get('always_unread')) && settings.get('track_posts')) { var np = d.appendChild(document.createElement('span')); np.className = 'btn btn-default'; np.style.margin = '5px'; var npa = np.appendChild(document.createElement('a')); if (unread) { npa.innerHTML = 'Unread posts'; npa.href = gen_page_link(elm.href, postnr_to_pagenr(last_post_nr, p.last), true); npa.style.color = '#C64148'; } else { npa.innerHTML = 'No unread posts'; npa.style.color = '#888'; } } }; var mangle_list = function() { add_top_btn(); var filter = settings.get('hide_threads'); var elms = document.getElementsByClassName('ListRow'); for (i = 0; i < elms.length; i++) { var elm = elms[i]; if (elm === undefined) continue; var rh = elm.getElementsByClassName('RowHeading')[0]; if (rh === undefined) continue; var l = rh.firstElementChild; last_viewed.get(parseInt(l.href.match(thread_id_re)[1]), add_last_viewed, l); if (filter) { var u = tl.parentElement.textContent.match(/started\W+by\W+(\w+)/); if (u && u[1] && ignore_list.get(u[1])) elm.style.display = 'none'; } } }; if (ftl.product) { // Wait for thread list to load before mangling var div = document.getElementById('forum'); if (div !== null) (new MutationObserver(mangle_list)).observe(div, { childList: true }); } else { mangle_list(); } }); } } if (!ftl.mobile && ftl.thread) { // Re-implement FT's PopoutMenu, only nicer var PM2 = (function() { var saved_class, saved_par, saved_ch, timeout; function add_listener(elem, type, val, par, ch) { elem.addEventListener(type, function() { act(val, par, ch) }); } function add_listeners(elem, par, ch) { add_listener(elem, 'mouseover', 1, par, ch); add_listener(elem, 'mouseout', 0, par, ch); } function show() { saved_par.className += ' focused'; saved_ch.style.display = 'inline'; // Hack: this is the easiest way to fix up the element locations. saved_ch.style.left = saved_ch.style.top = '0px'; var pb = saved_par.getBoundingClientRect(); var cb = saved_ch.getBoundingClientRect(); var pad = parseInt(saved_ch.style.padding); pad = pad ? pad : 0; saved_ch.style.left = Math.min(pb.left - cb.left, document.body.clientWidth - cb.width + pad) + 'px'; saved_ch.style.top = (pb.top - cb.top + 35) + 'px'; saved_ch.style.width = (cb.width - pad * 2) + 'px'; } function hide() { if (!saved_class) return; saved_par.className = saved_class; saved_ch.style.display = 'none'; saved_class = undefined; } function act(val, par, ch) { if (val) { clearTimeout(timeout); if (saved_par != par) hide(); saved_par = par; saved_ch = ch; if (saved_class === undefined) { saved_class = par.className; show(); } } else { timeout = setTimeout(function() { hide() }, 300); } } // Hack: we can't remove anonymous event listeners, so duplicate elements function dupe(id, raw) { var elm = document.getElementById(id); var nelm = document.createElement(elm.tagName); nelm.align = elm.align; if (raw) { nelm.innerHTML = elm.innerHTML; } else { nelm.setAttribute('style', 'position: absolute; height: auto; ' + 'background-color: white; padding: 20px'); while (elm.childNodes[0]) nelm.appendChild(elm.childNodes[0]); } nelm.id = elm.id + '2'; nelm.className = elm.className; elm.parentElement.insertBefore(nelm, elm); elm.remove(); return nelm; } return function(par) { var ch = dupe(par + 'Popout', false); par = dupe(par, true); add_listeners(par, par, ch); add_listeners(ch, par, ch); } })(); var update_itmanage = function() { var sp = document.getElementById('Ignorelist2'); if (sp === null) return; var div = document.getElementById('IgnorelistPopout2'); if (div === null) return; var elms = div.getElementsByClassName('itmanage'); switch ((elms.length === 0) * 2 + settings.get('inthread_manage')) { case 3: div.appendChild(document.createElement('hr')).className = 'itmanage'; var l = div.appendChild(document.createElement('div')); l.innerHTML = '<b>Ignored Users</b>'; l.className = 'itmanage'; l.style.marginTop = '10px'; manage_menu.register(l); l.getElementsByClassName('il_addbtn')[0].className = 'FormButton blue'; break; case 1: for (var i = 0; i < elms.length; i++) elms[i].style.display = ''; break; case 0: for (var i = 0; i < elms.length; i++) elms[i].style.display = 'none'; break; } }; settings.watch('inthread_manage', update_itmanage); var update_itmenu = function() { var sp = document.getElementById('Ignorelist2'); var div; switch ((sp === null) * 2 + settings.get('inthread_menu')) { case 3: var b = document.getElementsByClassName('ThreadCommandBar')[0]; sp = b.appendChild(document.createElement('span')); sp.id = 'Ignorelist'; sp.className = 'ThreadCommand'; sp.innerHTML = 'Settings'; div = b.appendChild(document.createElement('div')); div.id = 'IgnorelistPopout'; div.className = 'PopoutPanel'; div.align = 'left'; settings_menu.register(div, thread_menu); PM2('Ignorelist'); if (settings.get('inthread_manage') !== undefined) update_itmanage(); break; case 1: sp.style.display = ''; break; case 0: sp.style.display = 'none'; break; } }; settings.watch('inthread_menu', update_itmenu); PM2('RateThread'); PM2('ForumTools'); post_filter.watch(gen_watch_filter(function(post) { var tbl = post.post_elms[0].parentElement; var ph = post.placeholder = tbl .insertBefore(document.createElement('tr'), post.post_elms[0]); ph.innerHTML = '<td colspan="2" class="ForumHeader" ' + 'style="padding: 10px; text-align: center"><a>Post by ' + strip_tags(post.content_elm.getAttribute('data-username')) + ' hidden. Click to show.</a></td>'; fix_as(ph); handlers.hide_toggle(ph.firstElementChild.firstElementChild, post); return ph; })); settings.watch('placeholders', post_filter.update); // Wire posts & ignorebuttons up ignore_buttons.set('+ ignore', '+ unignore'); var posts = get_posts(); for (var i = 0; i < posts.length; i++) { var post = posts[i]; var tl = post.content_elm.parentElement.parentElement.parentElement; post.post_elms = [tl.previousElementSibling, tl]; var head = tl.previousElementSibling.firstElementChild.lastElementChild; var new_a = head.appendChild(document.createElement('a')); new_a.href = 'javascript:void(0)'; ignore_buttons.register(post.user, new_a); var badge = tl.getElementsByClassName('Badges')[0]; if (badge !== undefined) user_tags.register(post.user, badge); } post_filter.register(posts); ignore_buttons.update(); user_tags.update(); fix_quote_btns(posts); if (!ftl.perma && thread_id !== undefined) { if (location.hash === '#unread') scroll_to_unread(thread_id, posts); var p = parse_pager(document.getElementsByClassName('Pager')[1]); var lpn = (p.cur - 1) * 10 + posts.length; var lp = parseInt(posts[posts.length - 1].content_elm.id.slice(4)); last_viewed.set(thread_id, p.cur, lpn, lp); } } if (ftl.mobile && ftl.thread) { post_filter.watch(gen_watch_filter(function(post) { var tbl = post.post_elms[0].parentElement; var ph = post.placeholder = tbl.insertBefore(document.createElement('div'), post.post_elms[0]); ph.className = 'col-xs-12 ListRow'; ph.style.paddingBottom = '5px'; ph.style.textAlign = 'center'; ph.innerHTML = '<a>Post by ' + strip_tags(post.content_elm.getAttribute('data-username')) + ' hidden. Click to show.</a>'; fix_as(ph); handlers.hide_toggle(ph.firstElementChild, post); return ph; })); settings.watch('placeholders', post_filter.update); var posts = get_posts(); for (var i = 0; i < posts.length; i++) posts[i].post_elms = [posts[i].content_elm.parentElement]; post_filter.register(posts); fix_quote_btns(posts); ignore_buttons.set( '<span class="glyphicon glyphicon-plus red"></span> Ignore', '<span class="glyphicon glyphicon-plus red"></span> Unignore'); var cont = document.getElementsByClassName('container-fluid')[0]; var menus = cont.getElementsByClassName('dropdown-menu'); for (var i = 0; i < menus.length; i++) { var user = menus[i].getElementsByTagName('b')[0].innerHTML; var li = document.createElement('li'); li.role = 'presentation'; li.innerHTML = '<a tabindex="-1" role="menuitem"></a>'; fix_as(li); for (var j = 0; j < menus[i].children.length; j++) { if (menus[i].children[j].innerHTML.match('Report')) break; } menus[i].insertBefore(li, menus[i].children[j]); ignore_buttons.register(user, li.firstElementChild); var badge = menus[i].firstElementChild.firstElementChild; var m = badge.innerHTML.match(/(.*)\((.*)\)/); badge.innerHTML = m[1] + '(<span>' + m[2] + '</span>)'; user_tags.register(user, badge.lastElementChild); } ignore_buttons.update(); user_tags.update(); if (!ftl.perma && thread_id !== undefined) { if (location.hash === '#unread') scroll_to_unread(thread_id, posts); var p = parse_pager(document.getElementsByClassName('pagination')[0]); var lpn = (p.cur - 1) * 10 + posts.length; var lp = parseInt(posts[posts.length - 1].content_elm.id.slice(4)); last_viewed.set(thread_id, p.cur, lpn, lp); } var update_itmanage = function() { var menu = document.getElementById('hideSettings'); if (menu === null) return; var elms = menu.getElementsByClassName('itmanage'); switch ((elms.length === 0) * 2 + settings.get('inthread_manage')) { case 3: var inner = menu.getElementsByClassName('panel-body')[0]; inner.appendChild(document.createElement('hr')).className = 'itmanage'; var l = inner.appendChild(document.createElement('div')); l.className = 'itmanage'; l.innerHTML = '<b>Ignored Users</b>'; l.style.marginTop = '10px'; manage_menu.register(l); l.getElementsByClassName('il_addbtn')[0].remove(); break; case 1: for (var i = 0; i < elms.length; i++) elms[i].style.display = ''; break; case 0: for (var i = 0; i < elms.length; i++) elms[i].style.display = 'none'; break; } }; settings.watch('inthread_manage', update_itmanage); var update_itmenu = function() { var menu = document.getElementById('itmenu'); switch ((menu === null) * 2 + settings.get('inthread_menu')) { case 3: var div = document.getElementsByClassName('body-content')[0] .appendChild(document.createElement('div')); div.id = 'itmenu'; div.className = 'panel panel-default'; div.innerHTML = '<div class="panel-heading"><h5 class="panel-title">' + '<a href="#hideSettings" data-toggle="collapse">Settings ' + '<span class="caret"></span></a></h5>' + '<div id="hideSettings" class="panel-collapse collapse">' + '<div class="panel-body"></div></div>'; var inner = div.getElementsByClassName('panel-body')[0]; settings_menu.register(inner, thread_menu); if (settings.get('inthread_manage') !== undefined) update_itmanage(); break; case 1: menu.style.display = ''; break; case 0: menu.style.display = 'none'; break; } }; settings.watch('inthread_menu', update_itmenu); } if (ftl.settings) { var div = document.getElementsByClassName('PageContentPanel')[0] .appendChild(document.createElement('div')); div.style.marginTop = '5px'; var tbl = div.appendChild(document.createElement('table')); tbl.style.width = 'auto'; tbl.innerHTML = '<tbody><tr><td></td><td width="15"></td><td></td></tr>' + '<tr><td></td><td></td><td></td></tr></tbody>'; var cells = tbl.getElementsByTagName('td'); cells[0].className = 'MediumLabel Bold EndOfInlineSection'; cells[0].style.padding = '5px'; cells[0].innerHTML = 'Ignored Users'; cells[2].className = 'MediumLabel Bold EndOfInlineSection'; cells[2].style.padding = '5px'; cells[2].innerHTML = 'FastTech Ignore List Settings'; cells[5].style.padding = '0px'; div = cells[5].appendChild(document.createElement('div')); div.className = 'BGShadow'; div.style.padding = '10px'; settings_menu.register(div, full_menus); cells[3].style.padding = '0px'; div = cells[3].appendChild(document.createElement('div')); div.className = 'BGShadow'; div.style.padding = '10px'; manage_menu.register(div); div.getElementsByClassName('il_addbtn')[0].className = 'FormButton blue'; }