您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Improvements for the FastTech forums and store
// ==UserScript== // @name FastTech Forum Enhancements // @namespace ftil // @description Improvements for the FastTech forums and store // @include https://*.fasttech.com/* // @version 2.5.1 // @grant GM_getValue // @grant GM_setValue // @run-at document-start // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAOVBMVEVMaXEega+SlJeSkpgggK+Tk5gfgq+SlJeSkpkegK8egK8fgK+Tk5d1jZ6SkpcfgLEegLCTlJgfgbBPYHHZAAAAEXRSTlMApS2cl95Qi1CMvOmzFGgsdPNojJcAAACSSURBVHgBzdJlAgIwCAbQb915/7vaumG3vt8DRuAmIwheJOGQ2ywCvs8qYMVgsFLZSuiBrWhc4jsD8cIDrgh++qDNDM49MHmP4+wDDuIPHyRrE04VXbBlqZQ0hdhmKqXQZwwQaogcKD4MEt+QDMGLo8BvHe3qQTwwAJJjgyajPu/9Dwoj5MmDky6SEAlElYPGTUsirRDoa4UEWgAAAABJRU5ErkJggg== // ==/UserScript== 'use strict'; // Delay function calls until their preconditions have been met. var block = (function() { var counters = { }; return { get: function(name) { return counters[name].val !== 0; }, block: function(name) { var c = counters[name]; if (c === undefined) counters[name] = { val: 1, watches: [] }; else if (c.val !== 0) c.val++; }, unblock: function(name) { var c = counters[name]; if (c.val !== 0) c.val--; if (c.val === 0) { var w = c.watches; while (w.length !== 0) w.shift()(); } }, watch: function(name, func) { var c = counters[name]; if (c.val !== 0) c.watches.push(func); else func(); }, }; })(); /* Userscript settings: * This is just terrible. GM_[sg]etValue is used to initialize settings since * it's shared across subdomains, while localStorage is used to notify other * tabs of new settings. */ var settings = (function() { var settings = { }; var watches = { }; var prefix = 'ignore_list:'; var prere = new RegExp('^' + prefix + '(.+)'); var have_ls = (function() { try { localStorage.getItem('test'); return true; } catch (e) { return false; } })(); // Unblock after execution of the script block.block('ready'); function inner_set(name, value) { // Ugh. if (JSON.stringify(settings[name].value) === JSON.stringify(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; } if (have_ls) { window.addEventListener('storage', function(e) { var set = e.key.match(prere); if (set !== null && set[1] in settings) inner_set(set[1], JSON.parse(e.newValue)); }); } function get_sync(name) { var val = GM_getValue(name, null); if (val !== null) return JSON.parse(val); else return settings[name].def; } return { get: function(name) { return settings[name].value; }, get_sync: get_sync, set: function(name, value) { if (inner_set(name, value)) { var s = JSON.stringify(value); if (have_ls) localStorage.setItem(prefix + name, s); GM_setValue(name, s); } }, all: function() { return settings; }, register: function(name, desc, def, dep, opt) { settings[name] = { desc: desc, def: def, dep: dep, opt: opt, }; inner_set(name, get_sync(name)); }, watch: function(name, func) { if (!(name in watches)) watches[name] = []; watches[name].push(func); if (settings[name] !== undefined) func(name, settings[name].value); }, update: function() { block.unblock('ready'); }, }; })(); // Dynamic stylesheets var styles = (function() { var urls = {}; var txts = {}; var elms = {}; var active_elm; function try_gmgrt(name) { try { return GM_getResourceText('theme_' + name); } catch (e) {} } function apply() { var enable = settings.get('use_theme'); var name = settings.get('theme_name'); if (enable === true && name !== undefined) { if (active_elm !== undefined) { if (active_elm === elms[name]) return; active_elm.disabled = true; } if (name in elms) { elms[name].disabled = false; } else { var e, u; var c = try_gmgrt(name); if (c === undefined || c === null) { u = urls[name]; c = txts[name]; } if (c !== undefined) { e = document.createElement('style'); e.type = 'text/css'; e.innerHTML = c; } else { e = document.createElement('link'); e.rel = 'stylesheet'; e.type = 'text/css'; e.href = u; } document.head.appendChild(e); elms[name] = e; } active_elm = elms[name]; } else if (active_elm !== undefined) { active_elm.disabled = true; active_elm = undefined; } } return { register: function(name, url, txt) { if (url !== undefined) urls[name] = url; if (txt !== undefined) txts[name] = txt; }, update: function() { settings.watch('use_theme', apply); settings.watch('theme_name', apply); }, }; })(); // 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; }; })(); // Case-insensitive indexOf (needed in case of case errors in user input) function ci_indexof(a, n) { var l = n.toLowerCase(); for (var i = 0; i < a.length; i++) { if (a[i].toLowerCase() === l) return i; } return -1; } // Case-insensitive attribute lookup function ci_lookup(a, n) { var l = n.toLowerCase(); for (var k in a) { if (k.toLowerCase() === l) return k; } } // Assorted generic handlers for user input. var handlers = (function() { function Handler(funcs, elm, data, extra) { this.elm = elm; this.data = data; this.extra = extra; if (funcs.setting !== undefined) settings.watch(data, funcs.setting.bind(this)); for (var k in funcs) { if (k.slice(0, 2) === 'on') elm.addEventListener(k.slice(2), funcs[k].bind(this)); } } function handler(fs) { return function(e, d, x) { return new Handler(fs, e, d, x); }; } return { generic: handler, ignore_toggle: handler({ onclick: function() { ignore_list.set(this.data); } }), checkbox: handler({ onchange: function() { settings.set(this.data, this.elm.checked); }, setting: function(n, v) { this.elm.checked = v; } }), top_click: handler({ onclick: function() { scrollTo(0, 0); } }), hide_toggle: handler({ onclick: function() { unhide_posts.set(this.data); } }), }; })(); // Manage the state of the ignore list function create_ignore_list(list_var) { // Use two stages so that filters can handle updates before being called var watches = { 'early': [], 'late': [] }; var il = []; function fire_watches(user, state, idx) { ['early', 'late'].forEach(function(stage) { var w = watches[stage]; for (var i = 0; i < w.length; i++) w[i](user, state, idx); }); } function inner_set(user, state, idx) { // In order to support userscripts, we need to rebuild the list on every set if (settings.get_sync) { il = []; var l = settings.get_sync(list_var); for (var i = 0; i < l.length; i++) il.push(l[i]); } if (state) il.push(user); else il.splice(idx, 1); settings.set(list_var, il); fire_watches(user, state, idx); } settings.register(list_var, null, [], null); // Watch for ignorelist changes, and fire incremental watch events settings.watch(list_var, function(name, val) { var i, j; if (il.length === val.length) return; if (il.length === 0) { for (i = 0; i < val.length; i++) il.push(val[i]); fire_watches(il, true); return; } 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(user, state, idx); 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; settings.register('hide_ignored', 'Hide posts by ignored users', true, null); block.block('filter'); function update() { if (posts === undefined) return; for (var i = 0; i < posts.length; i++) { var post = posts[i]; var hide = false; var j; for (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 (j = 0; j < post.post_elms.length; j++) post.post_elms[j].style.display = display; for (j = 0; j < watches.length; j++) watches[j](post, hide); } block.unblock('filter'); } function builtin() { filters = []; filters.unshift((function() { settings.watch('hide_ignored', post_filter.update); ignore_list.watch('late', post_filter.update); return function(post) { if (settings.get('hide_ignored') && ignore_list.get(post.user)) return true; }; })()); unhide_posts = (function() { var posts = []; filters.unshift(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(); } }; })(); } return { // Register a list of posts to be filtered register: function(list) { posts = list; update(); }, /* Add a new filter. Filters return true (should hide), false (must not * hide), or undefined (no judgement). Does not trigger update(). */ filter: function(func) { if (filters === undefined) builtin(); filters.push(func); }, // Run all filters again update: update, /* Call func with changes to the hidden status of each post (true == * hidden). Does not trigger update(). */ watch: function(func) { watches.push(func); }, }; })(); var unhide_posts; // Generic buttons to toggle ignored state of a user var ignore_buttons = (function() { var posts; var inner_ign = ''; var inner_unign = ''; function update_btns(users, state) { var i; if (users instanceof Array) { for (i = 0; i < posts.length; i++) { posts[i].ignbtn.innerHTML = ci_indexof(users, posts[i].user) !== -1 ? inner_unign : inner_ign; } } else { var ul = users.toLowerCase(); for (i = 0; i < posts.length; i++) { if (!ul.localeCompare(posts[i].user.toLowerCase())) posts[i].ignbtn.innerHTML = state ? inner_unign : inner_ign; } } } return { // Set the (HTML) format of the (un)ignore buttons set: function(ign, unign) { inner_ign = ign; inner_unign = unign; }, // Register posts to have their ignore buttons handled register: function(p) { posts = p; for (var i = 0; i < p.length; i++) handlers.ignore_toggle(p[i].ignbtn, p[i].user); ignore_list.watch('late', update_btns); }, }; })(); // Handle RES-style tagging of users var user_tags = (function() { var posts; function update_badges(n, val) { for (var i = 0; i < posts.length; i++) { var post = posts[i]; var k = ci_lookup(val, post.user); post.badge.innerHTML = k !== undefined ? val[k] : post.saved_badge; } } var tag_prompt = handlers.generic({ onclick: function(e) { e.preventDefault(); e.stopPropagation(); var user = this.data; var tmp = (settings.get_sync ? settings.get_sync : 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 posts to have their badges handled register: function(p) { posts = p; for (var i = 0; i < p.length; i++) { var post = p[i]; post.saved_badge = post.badge.innerHTML; tag_prompt(post.badge, post.user); } settings.watch('user_tags', update_badges); }, update: function() { settings.register('user_tags', null, { }, null); }, }; })(); // Track the last viewed page and post count for threads var last_viewed = (function() { var db; var store = 'thread_data'; function GetCmd(args) { this.args = args; } GetCmd.prototype.mode = 'readonly'; GetCmd.prototype.success = function(e) { var t = e.target.result; if (t !== undefined) this.args.f(this.args.e, t.lvp, t.lpn, t.lp); }; function SetCmd(args) { this.args = args; } SetCmd.prototype.mode = 'readwrite'; SetCmd.prototype.success = function(e) { var t = e.target.result; if (t === undefined) t = {id: this.args.id}; t.lvt = this.args.lvt; t.lvp = this.args.lvp; if (t.lpn === undefined || t.lpn < this.args.lpn) { t.lpn = this.args.lpn; t.lp = this.args.lp; } this.os.put(t); }; GetCmd.prototype.exec = SetCmd.prototype.exec = function() { this.os = db.transaction(store, this.mode).objectStore(store); this.os.get(this.args.id).onsuccess = this.success.bind(this); }; // Accumulate commands and fire them once idb is open var cmds = []; function run_cmds() { if (db === undefined || db === null) return open_db(); var cmd; while ((cmd = cmds.shift()) !== undefined) cmd.exec(); } // Open idb and maybe remove old threads function open_db() { if (db === undefined) db = null; else return; var req = indexedDB.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(); /* Periodically scrub the database (FIXME this is awful & may run in * multiple tabs simultaneously) */ settings.register('lv_scrub_time', null, 0, null); settings.watch('lv_scrub_time', function(n, v) { var now = Date.now(); if (v + 24 * 60 * 60 * 1000 > now) return; var tr = db.transaction(store, 'readwrite'); var os = tr.objectStore(store); os.index('lvt') .openCursor(IDBKeyRange.upperBound(now - 60 * 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); } }; }); }; } 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(new SetCmd({id: thread_id, 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(new GetCmd({id: thread_id, f: func, e: extra})); run_cmds(); }, update: open_db, }; })(); // Generic settings menu var settings_menu = (function() { function mangle_opts(opts) { var ret = ''; for (var k in opts) ret = ret + '<option value="' + k + '">' + opts[k].name + '</option>'; return ret; } var watch_disable = handlers.generic({ setting: function(n, v) { this.elm.disabled = v ^ this.extra; }}); var select = handlers.generic({ onchange: function() { settings.set(this.data, this.elm.value); }, setting: function(n, v) { this.elm.value = v; } }); 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(); var d = document.createElement('div'); var html = ''; var vs = []; for (var i = 0; i < menus.length; i++) { var menu = menus[i]; if (i !== 0) html += '<hr></hr>'; if (menu.title !== undefined) html += '<b>' + menu.title + '</b>'; for (var j = 0; j < menu.vars.length; j++) { var v = menu.vars[j]; vs.push(v); var sv = s[v]; if (sv.opt !== undefined) { html += '<div><select style="margin-left: 21px">' + mangle_opts(sv.opt) + '</select></div>'; } else { html += '<div><input type="checkbox" id="ilcb_' + v + '"></input>' + '<label for="ilcb_' + v + '">' + sv.desc + '</label></div>'; } } } d.innerHTML = html; var e = d.firstElementChild; var depre = /(!)?(.*)/; function h(e, v) { var sv = s[v]; if (sv.opt !== undefined) select(e, v); else handlers.checkbox(e, v); var d = sv.dep; if (d) { var ds = sv.dep.match(depre); var i = !ds[1]; d = ds[2]; watch_disable(e, d, i); } } while (e !== null) { if (e.tagName === 'DIV') h(e.firstElementChild, vs.shift()); e = e.nextElementSibling; } elm.appendChild(d); } }; })(); // Display a nice menu for managing the ignorelist var manage_menu = (function() { var menu_list; var menu_elms = []; var template = document.createElement('tr'); template.innerHTML = '<td></td><td align="right"><a class="itmremove"></a></td>'; function create_elm(user) { var tr = template.cloneNode(true); tr.firstElementChild.innerHTML = user; var a = tr.lastElementChild.firstElementChild; a.href = 'javascript:void(0)'; handlers.ignore_toggle(a, user); if (this.appendChild) this.appendChild(tr); return { user: user.toLowerCase(), elm: tr }; } function lc_comp(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); } function update(user, state) { if (menu_list === undefined) return; if (user instanceof Array) { var frag = document.createDocumentFragment(); menu_elms = user.sort(lc_comp).map(create_elm, frag); menu_list.appendChild(frag); } else { var lc = user.toLowerCase(); var idx; if (state) { for (idx = 0; idx < menu_elms.length; idx++) { if (menu_elms[idx].user.localeCompare(lc) >= 0) break; } var elm = create_elm.call(0, user); menu_elms.splice(idx, 0, elm); if (idx + 1 < menu_elms.length) { menu_list.insertBefore(menu_elms[idx].elm, menu_elms[idx + 1].elm); } else { menu_list.appendChild(menu_elms[idx].elm); } } else { for (idx = 0; idx < menu_elms.length; idx++) { if (menu_elms[idx].user.localeCompare(lc) >= 0) break; } if (idx < menu_elms.length) { menu_elms[idx].elm.remove(); menu_elms.splice(idx, 1); } } } } var addbox_enter = handlers.generic({ onkeydown: function(k) { if (k.which !== 13) return true; k.stopPropagation(); k.preventDefault(); ignore_list.set(strip_tags(this.elm.value), true); this.elm.value = ''; return false; } }); var addbox_click = handlers.generic({ onclick: function() { ignore_list.set(strip_tags(this.data.value), true); this.data.value = ''; } }); return { // Attach management elements to elm register: function(elm) { var tbl = document.createElement('table'); tbl.className = 'itmanage'; tbl.innerHTML = '<tbody><tr><td colspan="2">' + '<input type="text" placeholder="Add user..." width="auto">' + '</input> <div class="il_addbtn">Add</div></td></tr></tbody>'; menu_list = tbl.firstElementChild; var td = menu_list.firstElementChild.firstElementChild; addbox_enter(td.firstElementChild); addbox_click(td.lastElementChild, td.firstElementChild); ignore_list.watch('late', update); elm.appendChild(tbl); }, }; })(); // Where are we? // In the interest of sanity, ftl.thread is true for all thread-like pages // (i.e. any page with one or more posts); ftl.threadlist is true for all // threadlist-like pages (i.e. any page with a list of threads). The // formatting for all such pages is similar enough to treat them equivalently. var ftl = { // Mobile pages mobile: '^https?://m\\.', // All forum pages forums: '^https?://(m|www)\\.fasttech\\.com/forums', // Thread-like pages (i.e. those with posts) thread: '^https?://(m|www)\\.fasttech\\.com/forums/[^/]+/t/[0-9]+/.', // Permalink pages perma: '^https?://(m|www)\\.fasttech\\.com/forums' + '/[^/]+/t/[0-9]+/[^/]+\\?[0-9]+$', // Threadlist-like pages (i.e. those with thread links) threadlist: '^https?://(m|www)\\.fasttech\\.com/forums' + '(/[^/]+(/page/[0-9]+)?(/search\\?.*)?)?$', // Lists of threads for a single author author: '^https?://(m|www)\\.fasttech\\.com/forums/author/', // Lists of threads for a single product product: '^https?://(m|www)\\.fasttech\\.com/p(roducts?)?/', // New thread page for a product pnthread: '^https?://(m|www)\\.fasttech\\.com/forums/[0-9]+/post', // Forum settings page settings: '^https?://www\\.fasttech\\.com/forums/settings$', // New arrivals pages new_arrivals: '^https?://(m|www)\\.fasttech\\.com/category/1/new-products', // Review pages reviews: '^https?://(m|www)\\.fasttech\\.com/reviews', // Product search prsearch: '^https?://(m|www)\\.fasttech\\.com/search', // Product category prcategory: '^https?://(m|www)\\.fasttech\\.com/c(ategory)?(/|$)', }; for (var k in ftl) ftl[k] = new RegExp(ftl[k]).test(location.href); // ftl.editor matches all pages with a post editor ftl.editor = ftl.thread || (ftl.threadlist && !ftl.settings && !ftl.author) || ftl.pnthread; // ftl.threadlist matches all pages listing threads ftl.threadlist = ftl.threadlist || ftl.author || ftl.product || ftl.settings; // ftl.forums matches all pages with forum elements (threads or posts) ftl.forums = ftl.forums || ftl.product; // ftl.reviews matches all pages with reviews ftl.reviews = ftl.reviews || ftl.product; if (ftl.thread || ftl.settings) { // Menu configuration var full_menus = [ { title: 'Browsing', vars: ['use_theme', 'theme_name', 'track_viewed', 'track_posts', 'always_unread', 'hide_threads'] }, { title: 'Posting', vars: ['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: ['use_theme', 'quote_quotes', 'quote_imgs'] }, { title: 'Post Hiding', vars: ['hide_ignored', 'hide_quotes', 'hide_deleted'] }, ]; } else { thread_menu = full_menus; } 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('placeholders', 'Show placeholders for hidden posts', true, null); settings.register('hide_quotes', 'Hide posts quoting ignored users', false, 'hide_ignored'); settings.register('hide_deleted', 'Hide deleted posts', true, null); settings.register('quote_quotes', 'Include quotes in quotes', true, null); settings.register('quote_imgs', 'Include images in quotes', true, null); } if (ftl.threadlist) { 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_threads', 'Hide threads by ignored users', false, null); } if (ftl.editor) { settings.register('agree_tou', null, false, null); } block.block('ready'); if (ftl.forums) { var ignore_list = create_ignore_list('ignorelist'); last_viewed.update(); if (ftl.thread || ftl.reviews) user_tags.update(); block.block('late'); block.watch('ready', function() { setTimeout(function() { block.unblock('late'); }, 0); }); } else if (ftl.new_arrivals) { var ignore_categories = create_ignore_list('ignored_categories'); } else if (ftl.reviews) { user_tags.update(); } settings.update(); if (document.readyState === 'loading') { document.addEventListener('readystatechange', function() { if (document.readyState === 'interactive') block.unblock('ready'); }); } else { block.unblock('ready'); } /* 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 */ var style = document.createElement('style'); style.innerHTML = '.PostContent span[style*="text-align:"] {display:block;width:100%}\n' + 'a.SKUAutoLink {display: inline-block}\n' + '.PopoutPanel>ul {margin: -5px -20px 0px -50px!important}\n' + '.PopoutPanel>p {margin: 0px!important}\n' + '.Badges {cursor:pointer}\n' + '.itmanage tbody tr td {padding:0px}\n' + '.itmanage .itmremove {padding-left: 16px; height: 16px; background: ' + 'url(https://www.fasttech.com/images/minus-button.png) no-repeat}'; document.head.appendChild(style); // RE to pull a thread id from a URL if (ftl.forums) var thread_id_re = new RegExp('/forums/[^/]+/t/([0-9]+)/[^?]*$'); 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; } }; })()); // Get all post bodies, desktop and mobile var posts; block.watch('ready', function() { posts = []; var elms = document.getElementsByClassName('PostContent'); for (var i = 0; i < elms.length; i++) { var pc = elms[i]; var user = pc.getAttribute('data-username'); if (user !== null) posts.push({ user: user, content_elm: pc }); } }); // Create a function to create placeholders as needed var gen_watch_filter = function(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 this thread's ID 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; } // Get the current and last pages from a pager element, desktop and mobile function parse_pager(elm) { var cur, last, sel; if (elm === undefined || elm === null) { cur = last = 1; } else if (!ftl.mobile) { sel = elm.getElementsByClassName('PageLink_Selected')[0]; if (sel !== undefined) cur = parseInt(sel.innerHTML); last = parseInt(elm.lastElementChild.previousElementSibling.innerHTML); } else { 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 fix_jtp = function() { var jtps = document.getElementsByClassName('JumpToBox'); if (jtps.length === 0) return; var lastpg = parse_pager(jtps[0].parentElement).last; function handle_kp(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; } for (var i = 0; i < jtps.length; i++) { var jtpbox = jtps[i]; 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', handle_kp); } } }; block.watch(ftl.forums ? 'late' : 'ready', fix_jtp); } if (ftl.threadlist) { // Translate a post number into a page number, handling FT's off-by-one quirk var postnr_to_pagenr = function(post, maxpage) { return Math.floor(Math.min(maxpage, post / 10 + 1)); }; // Create a link to a specific page of a thread var gen_page_link = function(base, page, hash) { var ret = base; if (page !== 1) ret += '/' + page; if (hash) ret += '#' + hash; return ret; }; } if (!ftl.mobile) { // Re-implement FT's PopoutMenu, only nicer var PM2 = (function() { var active, timeout; function show() { var p = active.par; var c = active.ch; p.className += ' focused'; c.style.left = c.style.top = '0px'; c.style.display = 'inline'; var ppb = p.parentElement.getBoundingClientRect(); var pb = p.getBoundingClientRect(); var cb = c.getBoundingClientRect(); c.style.left = Math.min(pb.left - cb.left, document.body.clientWidth - cb.width) + 'px'; c.style.top = (ppb.bottom - cb.top) + 'px'; } function hide() { if (active === undefined) return; active.par.className = active.cname; active.ch.style.display = 'none'; active = undefined; } function min(pm) { clearTimeout(timeout); if (pm !== active) hide(); active = pm; show(); } function mout() { timeout = setTimeout(hide, 300); } function add_listeners(elem, pm) { elem.addEventListener('mouseover', min.bind(null, pm)); elem.addEventListener('mouseout', mout); } /* Hack: we can't remove anonymous event listeners, so duplicate elements. * We hide the existing element to serve as a decoy for the original * PopoutMenu in case it hasn't run yet, then steal all its children. */ function dupe(id) { var elm = document.getElementById(id); var nelm = elm.cloneNode(false); elm.style.display = 'none'; while (elm.childNodes[0]) nelm.appendChild(elm.childNodes[0]); if (elm.nextElementSibling) elm.parentElement.insertBefore(nelm, elm.nextElementSibling); else elm.parentElement.appendChild(nelm); return nelm; } var def = {dupe: true}; return function(pm) { for (var k in def) { if (!(k in pm)) pm[k] = def[k]; } try { if (pm.dupe) { pm.ch = dupe(pm.ch || (pm.par.replace('Button', '') + 'Popout')); pm.par = dupe(pm.par); } else { pm.ch = document.getElementById( pm.ch || (pm.par.replace('Button', '') + 'Popout')); pm.par = document.getElementById(pm.par); } pm.cname = pm.par.className; pm.ch.style.padding = '20px'; add_listeners(pm.par, pm); add_listeners(pm.ch, pm); } catch (e) {} }; })(); block.watch('ready', function() { ['Cart', 'Support', 'Community', 'Account'] .forEach(function(n) { PM2({par: n + 'Button'}); }); if (ftl.thread) { ['RateThread', 'ForumTools'] .forEach(function(n) { PM2({par: n}); }); } }); } styles.register('nightmode', undefined, '#SearchKeywords,#images ol,#searchBarCategory,button.close{background:0 ' + '0!important}#images ol{border:0!important}.btn.active,li.active>a{backgr' + 'ound:#0d7bad!important}.FormButton.green{background:#20944b!important}#P' + 'roductDetails,#RightPanel,#categoryButton,#container .list-group li a,#f' + 'ooter,#searchBarOptionsCell,#searchBoxCell,#searchButtonCell,.AFEntry,.A' + 'ttachments,.BGShadow,.BGShadowLight,.OrdersShipmentHeading,.PageNavigati' + 'onalPanel,.ProductBackdrop,.ProductFilters,.ProductsGrid,.TicketResponse' + ',.TicketResponseFrame,.alert-info,.jumbotron,.list-group li,.mbbc-popups' + ' li,.mbbc-popups ul,.ui-dialog,body,td.Pros,ul.nav a{background:#222!imp' + 'ortant}.FormButton.blue{background:#3182b9!important}#AccountButton.focu' + 'sed,#LeftPanel,#UpdateWarningMsg,#container li a,#container li.active a.' + 'dropdown-toggle,#container ol,#navbar>tbody>tr:first-child,.CardShadow,.' + 'ControlArrows,.FilterEntry:hover,.FormButton.white,.ForumHeader,.ForumLi' + 'nk:hover,.GridItem,.PopoutPanel,.ProductGridItem,.ProductPanelBackdrop,.' + 'ReviewContent,.Steps>div,.btn,.dropdown-menu,.dropit-submenu>li,.mbbc-po' + 'pups li:hover,.navbarsearch,.panel-body,.panel-heading,.panel-title,.tab' + 'le,.table td,.tabs-min,button,div.well,input:not([valbbcode]),select,tex' + 'tarea,ul.dropdown-menu>li>a{background:#333!important}.FilterSelected,.F' + 'orumLink.Selected,.PageLink:hover,.StaticPageLink_Selected,.divider,.dro' + 'p-hover>a,.dropit-submenu,.subCategories li:hover,hr{background:#444!imp' + 'ortant}.FormButton.purple{background:#6a3b7f!important}.FormButton.red{b' + 'ackground:#9e1531!important}.FormButton.orange{background:#c4571d!import' + 'ant}.topLevelCategories li.focused{background-image:url(data:image/png;b' + 'ase64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAZklEQVR4AWPwySwkG1' + 'NPs2TjRjuyNUs0brwl0bRxj3jzei2yNEs2bfwPwkBD+sTb1wuQrhlhwCvJ5o25ZGhGYJA4KD' + 'zI0YwwpGF9LMmaQYEIsplkP0s0b4wnO7TpE88gTXRN22RjAIeVNYXGl+mUAAAAAElFTkSuQm' + 'CC)!important;background-position:right 2px!important;background-repeat:' + 'no-repeat!important}.mbbc-buttons li{background-image:url(data:image/png' + ';base64,iVBORw0KGgoAAAANSUhEUgAAAGwAAACVCAMAAACKJe8uAAAAjVBMVEVMaXG0UlJg' + 'YmS+kI/MzMzMzMzMzMzMzMzAxLuoqKiamppWV1cuLi7MzMzMzMzAx8qNjo5FRUaoRkZ8fH1N' + 'Tk/MzMzMzMyUPz55OjfMzMzMzMzMzMzMzMzMzMxWeJzJbgbGpxKCoqY8YIC5uqhzqsmwlV99' + 'pIB1lTxlghlvfMlxYbKnRy1sk7skfGsNbDRy3rXhAAAAHnRSTlMA/////42hdf//////s8L/' + '///////W6///NiMRW0Sx+uQWAAAC5klEQVR4AezSRZLAMAxEUY3SQTkM8v0vOhxG7fNdXr9A' + '09vbqo9VZIuZTVaARcGshVEUhfGNlaSZWDDAwWFsxgggylFcWSWzVGLD0lOMgKv3YqLagjVA' + '61rnHH4OGguWsnxjnQn7ZY6xEP0FxmXdjZYFcz93i/XDzS+rK5NFzXqNTz8jM39bTkwWabNK' + 'N1gBnGz+2yJr2gD4YXCI5cChxVzVzo6NzDE2IDq0SMbNm7HfgwBYYBHQR31xsnkiqToz5hvF' + 'yCkaTw/ijEVau0VeFQr8Xqg+wkiqcfNGzOsi/wyjrvtip761hllBKAzvArGSTtcS8P5v8/x+' + 'OceRk3iaPd07+QetXXsGKaX0bzQLl4kolRRhWBBnVIRRmghTB+III4yRI4yTIUzriKKV+kKk' + 'lFJKiZ7ABZyZF6CFi+JSzlyX45lCjm3Y+1eGH6jNsMaLmJNiE1yr745/JXbBM+tigAVdWaWx' + 'Sz+4MvyADxJFyJWtPxpRnW/G+MNnhu/ixsyNRsFTk/whdqVdQSU8Jw27tS5+Zl13seE561IL' + 'Oy4mUpzF8JIuu/7faLCl+BvJI0dKKaUvMbe7xXHOhm3tPax2u13BcZXmw56m1LAZMY5jcmyF' + 'Fr7F1jOGLxikD/sti55Z+AIivVuLiPVeFHa7h28jbXy3DWcJbQ971twNuduGw6aMog+bUkop' + 'fYPI7U43Xcdj2LTVWbqExHQ4UKmExDoDKDQjYnUYgDYQERsMwAZHxBY5ACaPiFWagHeyiJhT' + 'sVpF1HHYEKDTcPRR8V8jzw4OG3IPKaX/vb/aqQcchmIAAMPzmjzGC2rz/rebbfvvAb7637eX' + 'rdfYCq13DwxjnBFCMkrpLtYBoJrVYQ/HOCFUTKuegGEphNL0XtjRMwO1ENQYfS/MWps55zLv' + '/S5WjTdQG2PUV2JCmSedmZo0oEqBu2AhhCzGmKWUdjFu9CzDn4EtegIGxj/IeIzrPP7MqlVj' + '7LEhtuoWbAQUelFEFIeMmgAAAABJRU5ErkJggg==)!important;border-radius:5px!im' + 'portant}.HourGlass{background:inherit!important}.Stars{background:url(da' + 'ta:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAXVBM' + 'VEVMaXH/rVz/s2f/jCv/uGz/vXD+iSf+jSz+kjD+xH3+iiX+jCr+s2P/uG/+wXf+iif+m0T+' + 'n0f+pE7+p1L+v3X+wHj+wn3/kjP/mD3/oEj/p1P/tGX/vnT/iCX/xX3uTskzAAAAF3RSTlMA' + '/grK/i3p1yHXJX7WA3aZbPXD7o2sP+NiEhsAAABkSURBVHgBZcvFAcMwEEXBL2aFzXb/ZUYY' + 'fLdZQI0GfHaPnxo2Lt6iZls9bRDRbHwl17NGgjmOrGWeRmWBx1aVeEJ69GtSXrLyLkjdSY3S' + 'rWhUqF3mKWmXjQmM7Z1BOQ1Yx/DXE5S8Bqiv9nzRAAAAAElFTkSuQmCC)!important}.Gra' + 'yStars{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4A' + 'AAAOCAYAAAAfSC3RAAAA4UlEQVR4AWLwySzEinfu3MkGwrjkcWo8efJk76lTp8pI0njmzBn7' + 'Y8eO/T948OB/IG2AXyPCJl5AZ3RthjAARAF4F0omZBWquKfBrcPdOmyEMELk8XFxL+L5T0+n' + '04fgbDa7p5UcBk2Utt1u74fDwSE4nU6dwWCwkGW5JYpiw4coBRnwM444wtHtdh3TNB1FURye' + '558I8NNeJpNAGgpDjuPaMH5fKBEgDwmCsKd+Iz0SSEEo0UKJqVMdjUZfAmFEvWWuIwzCiGXZ' + 'fEiA+sEB9IdWKkTTiBrfF+7xDkHisNbhAqOEltIAVAofAAAAAElFTkSuQmCC)!important}' + '.FilterEntry.FilterCancellable{background:url(data:image/png;base64,iVBO' + 'Rw0KGgoAAAANSUhEUgAAAAwAAAALCAYAAABLcGxfAAAASklEQVR4AWPwySxkuCUk9J8QBqkD' + 'YbBiYjGKBigAs3GJMTAwNDBgUYDOxtSAoQmhmCwNlDmJTE8TxggNiIgjrBimAYxBHEIYpA4A' + 'F8kQ7Ga9snUAAAAASUVORK5CYII=) 10px 3px no-repeat,#444!important;border-c' + 'olor:#0d7bad!important;box-shadow:0 2px 2px #111!important}input[src="/i' + 'mages/errorreport.png"]{background:url(data:image/png;base64,iVBORw0KGgo' + 'AAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAMFBMVEX////////////////////////////' + '///////////////////////////////////9Or7hAAAAAEHRSTlMBCBIcJS45RlVkcYOVqbz' + 'NHir32QAAAGNJREFUeAEUTAkBBEEIUhMIDeAazPbvdjgvn0xVAXkCGocCwKcNaH4/9Sn08ym' + 'Qae00DUBRAtbGIHJUDc1Y1DRuKRY25Y3/gOa8BwKQFe/ugcAHpnfv7p49e/bcPab/Z8HgPgC' + 'LAx2nigq2jAAAAABJRU5ErkJggg==)!important}.CellMode .GridItemPrice{backgr' + 'ound:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHUAAAAeCAYAAAASN' + '1ElAAAAi0lEQVR4Ae3aSRXDIABAwZiIjgrAQkV0X46VUT1RFAOV0M3Gf3MYBR8IEKbXfvMhZ' + 'RG1Z4jasm7vj6kVlUswqlnaisqlFZX1Z25F5fmP2YrKHIxqlraiMovasvwjtqIyglHN0lZUR' + 'iAqolp+sVHqcKTB5QOuCXGhj19vfpLjOQsenuGJKB5zM0TtWb7rxpkaVlv9JQAAAABJRU5Er' + 'kJggg==) no-repeat!important;width:134px!important}.ForumHeader input[ty' + 'pe=image]{border:0!important;vertical-align:text-top!important}#AddedToC' + 'artPopupPanel,#container ol,.CardShadow,.ForumThread,.GridItem,.ProductG' + 'ridItem,.ProductPanelBackdrop,.TicketResponseFrame,.dropit-submenu,.mbbc' + '-buttons li,.panel-body,.panel-heading,td.Pros{border:1px solid #444!imp' + 'ortant}#searchBarOptionsCell,#searchBoxCell,#searchButtonCell,.ForumThre' + 'ad tr td,.navbarsearch{border-bottom:1px solid #444!important}#Ignorelis' + 'tPopout tr td,.PostFlags ul li{border-bottom:none!important}.FilterEntry' + ':hover,.FilterSelected,.StaticPageLink_Selected{border-color:#0d7bad!imp' + 'ortant}#categoryButton,#container li a,#container ul,.ControlArrows,.For' + 'umQuote,.ForumsNavigation,.PageLink,.PageNavigationalPanel,.PostFlags,.S' + 'KUAutoLink,.Signature,.TightInformationBox,.list-group li,.mbbc-popups u' + 'l,body .FormButton[class],div,hr,td{border-color:#444!important}.btn,but' + 'ton,input:not([valbbcode]),select,textarea{border-color:#444!important;b' + 'order-style:solid!important}#searchBarOptionsCell{border-left:1px solid ' + '#444!important}.ForumQuote{border-left:5px solid #0d7bad!important}.drop' + 'it-submenu>li,blockquote{border:none!important}#searchButtonCell{border-' + 'right:1px solid #444!important}.ForumLink.Selected,.ForumLink:hover{bord' + 'er-right:3px solid #0d7bad!important}#searchBarOptionsCell,#searchBoxCel' + 'l,#searchButtonCell{border-top:1px solid #444!important}#LeftPanel,.Popo' + 'utPanel{box-shadow:0 15px 30px #111!important}.ProductGridItem{box-shado' + 'w:0 3px 3px #111!important}.BGShadow,.BGShadowLight,.CardShadow,.FilterS' + 'elected,.ForumLink.Selected,.GridItem,.StaticPageLink_Selected,.subCateg' + 'ories li:hover{box-shadow:none!important}.SysMsgBanner{color:#000!import' + 'ant}#AccountButton a{color:#0d7bad!important}#StaticPageContent [style],' + '#categoryButton a,#content_ProductName,#footer,.AFTitle,.ControlArrows a' + ',.FieldLabel,.FlagValue,.Gray,.GrayText,.GridItemDesc>div,.GridItemModel' + 's,.Name>a,.NewList,.PageLink,.Pager,.btn,.dropit a,.navmenubutton a,.pan' + 'el-default>.panel-heading,.panel-title,.subCategories a,.topLevelCategor' + 'ies a,a.SortOrder,body,button,input:not([valbbcode]),select,textarea,ul.' + 'dropdown-menu>li>a{color:#bbb!important}.mbbc-popups li{color:#ccc!impor' + 'tant}.btn.active,body .FormButton[class],ul.dropdown-menu>li>a:hover{col' + 'or:#fff!important}.GridItemPackSize{display:inline!important;float:none!' + 'important;margin:0 0 0 -22px!important;vertical-align:sub!important}#DMC' + 'A,.logo,.navseals,a[href$="/faq/money-back-guarantee"]>img{display:none!' + 'important}#categoryButton{margin:0 20px!important}#footer,.PageContentPa' + 'nel>div:last-child[style]{margin:0!important}#CartPopupClose{margin:-10p' + 'x -20px!important}.mbbc-row1{margin-bottom:1px!important}.ForumLink:hove' + 'r{margin-right:-3px!important}#Tab{margin-top:10px!important}#AddedToCar' + 'tPopupPanel{padding:0 10px!important;position:fixed!important;right:40px' + '!important;top:40px!important}.menubuttons{padding:0 20px!important}#Ans' + 'wer{padding:0!important}#searchbar{padding-right:20px!important}#AddedTo' + 'CartPopupPanel,#categoryButton{width:auto!important}'); styles.register('darkft', undefined, '.FieldFlag,.Pager .PageLink{font-size:9pt!important}.Pager .PageLink:hov' + 'er{background:#111!important;color:#fefefe!important}.FieldFlag{backgrou' + 'nd:#1980b0!important;font-weight:400!important;padding:3px 5px!important' + '}.ForumLink.Selected{background:#222!important;border-right:3px solid #6' + '66!important}.ForumHeader,.ForumThread .ThreadCommandBar,.logo,body,div#' + 'contents{background:#333!important}.logo{visibility:hidden!important}.Fo' + 'rumThread tr td{background:#363636!important}#AccountButton.focused,#Acc' + 'ountPopout,#AddedToCartPopupPanel,#CartPopout,#CartPopupPanel,#Community' + 'Popout,#LeftPanel,#SupportPopout,#categoryButton,#categoryPopout,#navbar' + ',#searchBarOptionsButton,.CellMode .ProductGridItem,.FilterEntry:hover,.' + 'FormButton.white,.FormButton.white:hover,.LargeFilterEntry:hover,.LineMo' + 'de .ProductGridItem,.ModalDialog,.OrdersShipmentHeading,.PageNavigationP' + 'anel,.ProductBackdrop,.ProductFilters,.ProductFilters .FilterCancellable' + ',.ProductFilters .FilterSelected,.ProductPanelBackdrop,.mbbc .mbbc-edito' + 'r,div#footer,hr,tr{background:#444!important}.HourGlass{background:#444f' + 'e5!important}.Cons .FieldFlag,.Pros .FieldFlag{background:#4c9ed9!import' + 'ant}#navbar .focused,.BGShadow,.BGShadowLight,.PopoutPanel,.Steps .focus' + 'ed{background:#666!important}.PopoutPanel{border-radius:0!important;box-' + 'shadow:0 5px 5px #888!important}#searchBarCategory{background:#777!impor' + 'tant}.Pager .ControlArrows{background:#888!important;font-weight:400!imp' + 'ortant}.Pager .PageLink_Selected{background:#999!important}#ForumPopout,' + '#ForumToolsPopout,#RateThreadPopout{background:#fff!important}#CartButto' + 'n,#CommunityButton,#SupportButton,#searchBarOptionsCell,#searchButtonCel' + 'l,.GridViewIcon,.GroupViewIcon,.ProductGridMenu,.searchbar_focus #search' + 'BarOptionsCell,.searchbar_focus #searchButtonCell{background-image:url(d' + 'ata:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAJYBAMAAADh0yLyAAAAJ1B' + 'MVEVMaXFqbG11dXX9/f3l5eXd3eBadIdWbnlWc4RYieRmZ2dpaWlsbW2OPQlLAAAADXRSTlM' + 'A////////////r0YgYtO1qQAAAq9JREFUeAHt2sd160YYx9FxKgBcekc24ADsHTizVwBakEr' + 'g6+CpgBdLcHYJLsA9KTmnZ0PQof+U7j0q4Hc+fMM0KvcAALpI9xsocL2K+/t94GoT6KACNyZ' + '4oIF20AQF2kGBdtAEBdpBgXbQBAXaQYF20AQF2kGBdtAEI5UMAAAA4V9oBC43938PV/sO3My' + '1FihQoECB+yBQoECBAgUKFChQoECBAgUKFChQoEA3TcsBAAAA9CXbUEuq+pOhpNoO/fVfTZ5' + 'ga3Wqn6Y/4hLr3ToM4YFj9gRLHdIDb5VwyTuYPsFh4SExwXeGeh35YQEAAACAf/h1K/8aogq' + '8q7rt+74lB/bXhvjAfkwPrAIXHZKaHNj6oUYHXsf1/ZAcONRtn/5O0o/ZgW3rvTjx4xYAAAA' + 'ArsIEtjrWlj3BbfojHrbxOziEBw7pE0x/xKfN6+ACday1tpY8wTF7gq2168ZxfHw76CoMAAA' + 'AAM4ild+c7xL9PrBLdEiBT1ebQA8hUKBAgQIFChQoUKBAgQIFChQoUKBAgQIFChSYftN0cRE' + 'ZmAEAAOBstrJfu7melP3q5hL4J5u54gN3AgUKFChQoECBAgUKFChQoECBAgUKFChQoMCMwN1' + 'cjzgQAABgmt6bjqZJoEA8YoF4xHsgMA0AADD9ROBs2EHsoEDs4OEDAADaNH1akm1bbSXZONa' + 'jo/CvnUfvlUUcklpryz4ktT2PPiRTe/08+pCMS3/68E7yXvg7yadH4e8kU0l/Jyl3BAAAfPm' + 'T70uqzU/eTw9c/5Ae+P0jDRQoUKDArutWJrgwcP3QJ+iQdE5x+ATtoAl6oRYoUOAeCBRYUnU' + '/+aqk+vEnBQAAF9qRXGgLFChQoMBlXt9YCbSD/5+zGw7Jwgme28EFuht20Au1QIECBQp0oe1' + 'COx0AAFf2ljch8gqLTAAAAABJRU5ErkJggg==)!important}#navbar tr.navbarsearch' + ',.navsealsbg,body.stripe{background-image:url(data:image/png;base64,iVBO' + 'Rw0KGgoAAAANSUhEUgAAAGQAAABGAQMAAAAASKMqAAAAA1BMVE2.5EQ1TRdOAAAAEElEQVR4' + 'AWMYVmAUjIJRAAAD1AABVz/btAAAAABJRU5ErkJggg==)!important}.searchbar_focus' + ' #searchBoxCell{background-image:url(data:image/png;base64,iVBORw0KGgoAA' + 'AANSUhEUgAAAD0AAAAtBAMAAADvmn5BAAAAD1BMVEWYmJmzs7PBwcHGxsagoaK4fiRWAAAAI' + 'ElEQVR4AWNiwA+YBPECgcGuf6DkR+VH5UflR+UpLd8AJUsIkz10gdcAAAAASUVORK5CYII=)' + '!important}#searchBoxCell{background-image:url(data:image/png;base64,iVB' + 'ORw0KGgoAAAANSUhEUgAAAD0AAAAtAQMAAAAnevExAAAAA1BMVEV3d3dY20ihAAAADUlEQVR' + '4AWMYfGAUAAABlQABpWSZFgAAAABJRU5ErkJggg==)!important}.SysMsgBanner{borde' + 'r:1px dotted #444!important}#DMCA div.dm-1,#DMCA div.dm-2,#LeftPanel,#ca' + 'tegoryButton,#searchBarOptionsButton,.BorderedPanel,.FormButton,.ForumTh' + 'read,.ForumThread .Signature,.InformationBox,.Pager .ControlArrows,.Page' + 'r .PageLink,.Pager .PageLink_Selected,.ProductThumbnails,.Reviews .Cons,' + '.SKUAutoLink,.SingleLineTextBox,.TightInformationBox,.VerticalTextArea,.' + 'WarningBox{border:1px solid #444!important}.ForumQuote{border:1px solid ' + '#555!important;border-left:5px solid #666!important}.mbbc .mbbc-editor{b' + 'order:none!important}td{border-bottom:0!important}#ProductDetails .Attri' + 'butesTableRowGroupTitle{border-bottom:1px dashed #444!important}.Discoun' + 'tsTable,.ProductDescriptions,.fixedCategoryLink,.scHeading{border-bottom' + ':1px dotted #444!important}#navbar,.FilterHeading,.FooterBanner,.PostFla' + 'gs ul li,.tabs-min .ui-widget-header{border-bottom:1px solid #444!import' + 'ant}.SingleLineTextBox:focus,.VerticalTextArea:focus{border-color:#444!i' + 'mportant}.ForumThread .Signature,.Reviews .ReviewContent{border-left:5px' + ' solid #444!important}.ForumsNavigation{border-right:0 solid #e0e0e0!imp' + 'ortant}#ProductSpecifications,.FooterLeft,.navmenubutton_separator{borde' + 'r-right:1px dotted #444!important}.PostFlags,td.PageNavigationalPanel{bo' + 'rder-right:1px solid #444!important}.FilterEntry:hover,.LargeFilterEntry' + ':hover,.ProductFilters .FilterCancellable,.ProductFilters .FilterSelecte' + 'd,.subCategories li:hover,div.StaticPageLink_Selected{border-right:3px s' + 'olid #444!important}#navbar .hdivider,.FooterBanner,.OrdersShipmentHeadi' + 'ng,.PopoutPanel{border-top:1px solid #444!important}.BGShadow{box-shadow' + ':0 1px 1px #666!important}.BGShadowLight{box-shadow:0 3px 3px #666!impor' + 'tant}#CartPopout .ViewCart a:hover,#DMCA,#DMCA div.dm-1 a,#DMCA div.dm-2' + ' a,#ProductDetailsPanel .Price,#navbar .focused a,#searchButtonCell,.Cel' + 'lMode .GridItemPrice,.FieldFlag a,.FormButton,.FormButton.blue:hover,.Fo' + 'rmButton.green:hover,.FormButton.orange:hover,.FormButton.purple:hover,.' + 'FormButton.red:hover,.LineMode .GridItemPrice,.MemberRankIndex,.Pager .P' + 'ageLink_Selected,.Pager .PageLink_Selected:hover,.Pager .PageLink_Select' + 'ed:visited,.Steps .focused,.Steps .focused .StepText,.Steps .focused .St' + 'epTitle,.tabs-min .ui-state-default a,.unitSelector{color:#444!important' + '}.tabs-min .ui-state-active a{color:#666!important}#navbar .menulinks sp' + 'an a:active,a,a:visited{color:#d5d5d5!important}#categoryButton,#searchB' + 'arCategory,.CartTable .Name a,.CellMode .GridItemModels,.FormButton.whit' + 'e,.FormButton.white:hover,.ForumThread .ThreadCommand,.Information,.Line' + 'Mode .GridItemModels,.Pager .PageLink,.navmenubutton a,.subCategories a,' + '.topLevelCategories a,.topLevelCategories li.focused a{color:#e1e1e1!imp' + 'ortant}.Black{color:#e2e2e2!important}.PageTitle{color:#e5e5e5!important' + '}#categoryButton a,.PostFlags .FlagValue,.SingleLineTextBox:focus,.Verti' + 'calTextArea:focus{color:#f1f1f1!important}body{color:#f3f3f3!important}#' + 'content_ProductName,.mbbc .mbbc-align-popup li,.mbbc .mbbc-code-popup li' + ',.mbbc .mbbc-editor,.mbbc .mbbc-font-popup li,.mbbc .mbbc-list-popup li,' + '.mbbc .mbbc-simple-popup li,.mbbc .mbbc-size-popup li,.subCategories a:h' + 'over,.topLevelCategories a:hover,a:hover{color:#fff!important}.MemberRan' + 'kUsername{font-weight:700!important}.ForumGroup{padding:3px 10px!importa' + 'nt}.mbbc .mbbc-buttons ul{padding:5px 0 3px!important}.mbbc .mbbc-emotic' + 'on-popup .mbbc-group li{padding:8px!important}'); settings.register('use_theme', 'Use custom theme', false, null); settings.register('theme_name', null, 'nightmode', 'use_theme', { nightmode: { name: 'Night mode' }, darkft: { name: 'Pafapaf\'s DarkFT' }, }); styles.update(); // Add a "Top of page" button at the bottom of the page, aligned with the pager if ((ftl.thread && !ftl.perma) || (ftl.threadlist && !ftl.settings)) { 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>'; tbtn.firstElementChild.href = 'javascript:void(0)'; 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); } }; } block.watch('ready', add_top_btn); } if (ftl.editor) { var bbeeditor; // element to scroll to var bbetext; // textarea var bbeexpand; // expand button (mobile) var bbepanel; // parent of editor if (!ftl.mobile) { var fix_editor = function() { if (bbetext === undefined) bbetext = document.getElementsByClassName('mbbc-editor')[0]; if (bbetext !== undefined) { // Save & restore the editor text if (history.state && history.state.saved_post) bbetext.value = history.state.saved_post; window.addEventListener('pagehide', function() { var state = history.state; if (state === null) state = {}; state.saved_post = bbetext.value; history.replaceState(state, ''); }); // 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 bbetext.style.boxSizing = 'border-box'; bbetext.style.width = '100%'; // Add a YouTube button var url_btn = document.getElementsByClassName('mbbc-url')[0]; if (url_btn !== undefined) { var yt_btn = document.createElement('li'); yt_btn.innerHTML = ' '; yt_btn.className = 'mbbc-youtube'; yt_btn.addEventListener('click', function() { var sp = bbetext.selectionStart; var ep = bbetext.selectionEnd; var sel; if (ep < sp) { sp = ep; ep = bbetext.selectionStart; } if (ep !== sp) { sel = bbetext.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; bbetext.value = bbetext.value.slice(0, sp) + '[youtube]' + sel + '[/youtube]' + bbetext.value.slice(ep); bbetext.selectionStart = bbetext.selectionEnd = sp + 9 + (sel === '' ? 0 : 10 + sel.length); bbetext.focus(); }); url_btn.parentElement.insertBefore(yt_btn, url_btn); } } }; block.watch('late', function() { // Persist the terms of use checkbox var toubox = document.getElementById('AgreeTerms'); if (toubox !== null) handlers.checkbox(toubox, 'agree_tou'); bbeeditor = document.getElementById('bbEditor'); if (bbeeditor !== null) { bbetext = bbeeditor.getElementsByClassName('mbbc-editor')[0]; if (bbetext !== undefined) fix_editor(); else new MutationObserver(function(es, mo) { mo.disconnect(); fix_editor(); }).observe(bbeeditor, { childList: true }); } }); } else { block.watch('ready', function() { bbetext = document.getElementById('RawContent'); if (bbetext !== null) { bbepanel = bbetext.parentElement.parentElement.parentElement; bbeeditor = bbepanel.parentElement; bbeexpand = bbeeditor.previousElementSibling.firstElementChild .firstElementChild; } }); } } if (ftl.thread && !ftl.perma) { // Re-implement quotes in a way that sort of works var fix_quote_btns = (function() { 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 y = e.style; var t = null; var v = ''; if (y.textDecoration === 'underline') { t = 'u'; } else if (y.textDecoration === 'line-through') { t = 'y'; } else if (y.fontSize !== '') { t = 'size'; v = parseInt(y.fontSize); } else if (y.color !== '') { t = 'color'; v = y.color; } else if (y.textAlign !== '') { t = 'align'; v = y.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 { 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. 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 += e.textContent; } else { var do_quote = true; if ((depth > 0 || !settings.get('quote_quotes') && e.innerHTML.match(/\bwrote$/) !== null)) { // Is this a permalink for a quote we'll remove? var s = e; var l; do { if (s.nextSibling === null) { l = (l === undefined) ? s : l; s = s.parentElement.nextSibling; } else { s = s.nextSibling; } /* Between the <a> and the <div>, we only want to see a * colon and an unpredictable number of <br> and empty <p> * elements. The <div> may be outside the current element. */ if (s.tagName === 'DIV' && s.className === 'ForumQuote') { do_quote = false; e = (l === undefined) ? s : l; break; } else if (s.tagName === undefined) { if (s.textContent.match(/[^:\r\n]/) !== null) break; } else if (s.innerHTML !== '' || (s.tagName !== 'P' && s.tagName !== 'BR')) { break; } } while (true); } if (do_quote) tmp += wrap_tag('url', e.href, inner_quote(e, range, depth)); } 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) { if (bbetext === null) { console.error('bbEditor text box not found!'); return; } 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 (bbetext.value.length > 0 && bbetext.value[bbetext.value.length - 1] !== '\n') bbetext.value += '\n'; if (thread_id !== undefined && post_id !== undefined) bbetext.value += wrap_tag('url', '/forums/-/t/' + thread_id + '?' + post_id, user + ' wrote') + ':\n'; else bbetext.value += user + ' wrote:\n'; bbetext.value += wrap_tag('quote', '', inner_quote(elm, sel, 0) .trim().replace(/[\r\n]{3,}/mg, '\n\n')) + '\n'; if (bbeeditor) bbeeditor.scrollIntoView(); if (bbeexpand && bbepanel && bbepanel.className && !/\bin\b/.test(bbepanel.className)) bbeexpand.click(); } var quote_click = handlers.generic({ onclick: function() { quote(this.data.getAttribute('data-username'), this.data); }}); return function(elms, func) { for (var i = 0; i < elms.length; i++) { var elm = elms[i].content_elm; quote_click(func(elm), elm); } }; })(); if (!ftl.mobile) { var replace_quotebtn = function(elm) { // Ugh. var oldbtn = elm.parentElement.parentElement.parentElement .previousElementSibling.firstElementChild.lastElementChild .firstElementChild; var newbtn = document.createElement('a'); newbtn.innerHTML = 'quote/reply'; newbtn.href = 'javascript:void(0)'; oldbtn.parentElement.insertBefore(newbtn, oldbtn); oldbtn.remove(); return newbtn; }; block.watch('late', function() { fix_quote_btns(posts, replace_quotebtn); }); } else { // mobile var replace_quotebtn_m = function(elm) { // Ugh. var oldbtn = elm.parentElement.firstElementChild.lastElementChild .lastElementChild.firstElementChild; var newbtn = document.createElement('a'); newbtn.innerHTML = oldbtn.innerHTML; newbtn.href = 'javascript:void(0)'; newbtn.tabindex = '-1'; newbtn.role = 'menuitem'; oldbtn.parentElement.insertBefore(newbtn, oldbtn); oldbtn.remove(); return newbtn; }; block.watch('late', function() { fix_quote_btns(posts, replace_quotebtn_m); // 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); } }); } } if (ftl.thread && ftl.mobile) { // Fix up YT iframe size: CSS doesn't work well here. var fr_resize = (function() { var frs; var raf; function do_resize() { for (var i = 0; i < frs.length; i++) { var fr = frs[i]; fr.elm.style.height = Math.ceil(fr.elm.clientWidth * fr.ar) + 'px'; } raf = undefined; } return function(fr) { if (frs === undefined) { frs = []; window.addEventListener('resize', function() { if (raf === undefined) raf = requestAnimationFrame(do_resize); }); } fr.style.maxWidth = fr.width + 'px'; fr.style.width = '100%'; frs.push({elm: fr, ar: fr.height / fr.width}); }; })(); block.watch('ready', function() { var frs = document.getElementsByTagName('iframe'); for (var i = 0; i < frs.length; i++) fr_resize(frs[i]); }); } if (ftl.thread && !ftl.perma) { var scroll_to_hash = function() { function scroll(elm) { if (elm) { var sd = elm.style.display; elm.style.display = ''; elm.scrollIntoView(); elm.style.display = sd; } } if (location.hash === '#unread' || location.hash === '#last') last_viewed.get(thread_id, function(hash, lvp, lpn, lp) { block.watch('filter', function() { var i; var pid = 'POST' + lp; for (i = posts.length - 1; i >= 0; i--) { if (posts[i].content_elm.id === pid) break; } if (i < posts.length - 1) scroll(posts[i + 1].post_elms[0]); else if (hash === '#last') scroll(posts[posts.length - 1].post_elms[0]); }); }, location.hash); else if (location.hash !== '') block.watch('filter', function() { scroll(document.getElementById(location.hash)); }); // Drop the hash so we won't run again history.replaceState(history.state, '', location.pathname); }; } if (ftl.threadlist) { if (!ftl.mobile) { 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); var skip_margin = false; if (pelm === undefined) { skip_margin = true; if (!ftl.settings) { pelm = document.createElement('div'); pelm.className = 'Pager'; row.appendChild(pelm); } else { pelm = row.parentElement.appendChild(document.createElement('div')); pelm.className = 'Pager'; } } if (settings.get('track_viewed')) { var lv = document.createElement('a'); lv.href = gen_page_link(elm.href, last_viewed_page); lv.innerHTML = 'Last viewed'; lv.className = 'FormButton blue'; if (!skip_margin) lv.style.marginLeft = '5px'; pelm.appendChild(lv); skip_margin = false; } 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 = document.createElement('a'); if (unread) { np.innerHTML = 'Unread posts'; np.className = 'FormButton red'; np.href = gen_page_link(elm.href, postnr_to_pagenr(last_post_nr, p.last), 'unread'); } else { np.innerHTML = 'No unread posts'; np.className = 'FormButton white'; np.href = gen_page_link(elm.href, postnr_to_pagenr(last_post_nr, p.last), 'last'); } if (!skip_margin) np.style.marginLeft = '5px'; pelm.appendChild(np); skip_margin = false; } }; var mangle_list = function() { var header = document.getElementsByClassName('ForumHeader')[0]; var filter = !ftl.settings && !ftl.author && settings.get('hide_threads'); var sku_re = new RegExp('fasttechcdn.com/[0-9]+/([0-9]+)/.*'); var author_re = new RegExp('started\\W+by\\W+(\\w+)'); if (header !== undefined) { var ch = header.parentElement.children; for (var i = 1; i < ch.length; i += 2) { var tr = ch[i]; var tl = tr.getElementsByClassName('ThreadLink')[0]; var tid; if (tl !== undefined && (settings.get('track_posts') || settings.get('track_viewed'))) { 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 p = img.parentElement; var a = document.createElement('a'); a.href = '/products/' + sku[1]; a.appendChild(img); p.appendChild(a); } } if (filter) { var u = tl.parentElement.textContent .match(author_re); if (u && u[1] && ignore_list.get(u[1])) { tr.style.display = 'none'; if (tr.previousElementSibling !== header) tr.previousElementSibling.style.display = 'none'; } } } } }; block.watch('ready', mangle_list); } else { // mobile 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 = document.createElement('div'); d.style.marginLeft = '-5px'; if (settings.get('track_viewed')) { var lv = 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'; d.appendChild(lv); } 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 = document.createElement('span'); np.className = 'btn btn-default'; np.style.margin = '5px'; var npa = document.createElement('a'); if (unread) { npa.innerHTML = 'Unread posts'; npa.href = gen_page_link(elm.href, postnr_to_pagenr(last_post_nr, p.last), 'unread'); npa.style.color = '#C64148'; } else { npa.innerHTML = 'No unread posts'; npa.style.color = '#888'; npa.href = gen_page_link(elm.href, postnr_to_pagenr(last_post_nr, p.last), 'last'); } np.appendChild(npa); d.appendChild(np); } rh.appendChild(d); }; var mangle_list = function() { var filter = settings.get('hide_threads'); var elms = document.getElementsByClassName('ListRow'); var author_re = new RegExp('started\\W+by\\W+(\\w+)'); for (var i = 0; i < elms.length; i++) { var elm = elms[i]; var rh = elm.getElementsByClassName('RowHeading')[0]; if (rh === undefined) continue; var l = rh.firstElementChild; if (l !== undefined && (settings.get('track_posts') || settings.get('track_viewed'))) { var tid = parseInt(l.href.match(thread_id_re)[1]); if (!isNaN(tid)) last_viewed.get(tid, add_last_viewed, l); } if (filter) { var u = rh.parentElement.textContent.match(author_re); if (u && u[1] && ignore_list.get(u[1])) elm.style.display = 'none'; } } }; block.watch('ready', function() { if (ftl.product) { // Wait for thread list to load before mangling var div = document.getElementById('forum'); if (div !== null) new MutationObserver(function(es, mo) { mo.disconnect(); mangle_list(); }).observe(div, { childList: true }); } else { mangle_list(); } }); } } if (!ftl.mobile && ftl.thread) { var have_itmenu = false; var have_itmanage = false; var update_itmanage = function(n, itm) { if (!have_itmenu) return; var div = document.getElementById('IgnorelistPopout'); if (div === null) return; if (!have_itmanage && itm) { var frag = document.createDocumentFragment(); var hr = document.createElement('hr'); hr.className = 'itmanage'; frag.appendChild(hr); var l = 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'; frag.appendChild(l); div.appendChild(frag); have_itmanage = true; } else if (have_itmanage) { var elms = div.getElementsByClassName('itmanage'); for (var i = 0; i < elms.length; i++) elms[i].style.display = itm ? '' : 'none'; } }; var update_itmenu = function(n, itm) { if (!have_itmenu && itm) { var b = document.getElementsByClassName('ThreadCommandBar')[0]; var sp = document.createElement('span'); sp.id = 'Ignorelist'; sp.className = 'ThreadCommand'; sp.innerHTML = 'Settings'; b.appendChild(sp); block.watch('late', function() { var div = document.createElement('div'); div.id = 'IgnorelistPopout'; div.className = 'PopoutPanel'; div.align = 'left'; div.setAttribute('style', 'position: absolute; background: white; padding: 20px'); settings_menu.register(div, thread_menu); b.appendChild(div); PM2({par: 'Ignorelist', dupe: false}); have_itmenu = true; block.watch('late', function() { update_itmanage(undefined, settings.get('inthread_manage')); }); }); } else if (have_itmenu) { var elm = document.getElementById('Ignorelist'); if (elm !== null) elm.style.display = itm ? '' : 'none'; } }; post_filter.watch(gen_watch_filter(function(post) { var tbl = post.post_elms[0].parentElement; var ph = post.placeholder = document.createElement('tr'); ph.innerHTML = '<td colspan="2" class="ForumHeader" ' + 'style="padding: 10px; text-align: center"><a>Post by ' + strip_tags(post.user) + ' hidden. Click to show.</a></td>'; ph.firstElementChild.firstElementChild.href = 'javascript:void(0)'; handlers.hide_toggle(ph.firstElementChild.firstElementChild, post); tbl.insertBefore(ph, post.post_elms[0]); return ph; })); settings.watch('placeholders', post_filter.update); block.watch('ready', function() { // Wire posts & ignorebuttons up ignore_buttons.set('+ ignore', '+ unignore'); 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 = document.createElement('a'); new_a.href = 'javascript:void(0)'; head.appendChild(new_a); post.ignbtn = new_a; post.badge = tl.getElementsByClassName('Badges')[0]; } post_filter.register(posts); ignore_buttons.register(posts); user_tags.register(posts); settings.watch('inthread_manage', update_itmanage); settings.watch('inthread_menu', update_itmenu); }); block.watch('late', function() { 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)); if (!ftl.perma) last_viewed.set(thread_id, p.cur, lpn, lp); }); if (!ftl.perma && thread_id !== undefined) { if (location.hash !== '') scroll_to_hash(); } } if (ftl.mobile && ftl.thread) { post_filter.watch(gen_watch_filter(function(post) { var tbl = post.post_elms[0].parentElement; var ph = post.placeholder = document.createElement('div'); ph.className = 'col-xs-12 ListRow'; ph.style.paddingBottom = '5px'; ph.style.textAlign = 'center'; ph.innerHTML = '<a>Post by ' + strip_tags(post.user) + ' hidden. Click to show.</a>'; ph.firstElementChild.href = 'javascript:void(0)'; handlers.hide_toggle(ph.firstElementChild, post); tbl.insertBefore(ph, post.post_elms[0]); return ph; })); settings.watch('placeholders', post_filter.update); ignore_buttons.set(' Ignore', ' Unignore'); var have_itmenu = false; var have_itmanage = false; var update_itmanage = function(n, itm) { if (!have_itmenu) return; var menu = document.getElementById('hideSettings'); if (menu === null) return; if (!have_itmanage && itm) { var frag = document.createDocumentFragment(); var inner = menu.getElementsByClassName('panel-body')[0]; var hr = document.createElement('hr'); hr.className = 'itmanage'; frag.appendChild(hr); var l = 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(); frag.appendChild(l); inner.appendChild(frag); have_itmanage = true; } else if (have_itmanage) { var elms = menu.getElementsByClassName('itmanage'); for (var i = 0; i < elms.length; i++) elms[i].style.display = itm ? '' : 'none'; } }; var update_itmenu = function(n, itm) { if (!have_itmenu && itm) { var div = 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>'; block.watch('late', function() { var inner = div.getElementsByClassName('panel-body')[0]; settings_menu.register(inner, thread_menu); document.getElementsByClassName('body-content')[0].appendChild(div); have_itmenu = true; if (settings.get('inthread_manage') !== undefined) update_itmanage(undefined, settings.get('inthread_manage')); }); } else if (have_itmenu) { document.getElementById('itmenu').style.display = itm ? '' : 'none'; } }; block.watch('ready', function() { for (var i = 0; i < posts.length; i++) { var post = posts[i]; var pe = post.content_elm.parentElement; post.post_elms = [pe]; var menu = pe.firstElementChild.lastElementChild; var li = document.createElement('li'); li.setAttribute('role', 'presentation'); li.innerHTML = '<a tabindex="-1" role="menuitem">' + '<span class="glyphicon glyphicon-plus red"></span><span></span></a>'; li.firstElementChild.href = 'javascript:void(0)'; var rp = menu.lastElementChild; if (!rp.firstElementChild.lastChild.data.startsWith(' Report')) rp = rp.previousElementSibling; menu.insertBefore(li, rp); post.ignbtn = li.firstElementChild.lastElementChild; var badge = menu.firstElementChild.firstElementChild; var m = badge.innerHTML.match(/(.*)\((.*)\)/); badge.innerHTML = m[1] + '(<span>' + m[2] + '</span>)'; post.badge = badge.lastElementChild; } post_filter.register(posts); ignore_buttons.register(posts); user_tags.register(posts); settings.watch('inthread_manage', update_itmanage); settings.watch('inthread_menu', update_itmenu); }); block.watch('late', function() { 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)); if (!ftl.perma) last_viewed.set(thread_id, p.cur, lpn, lp); }); if (!ftl.perma && thread_id !== undefined) { if (location.hash !== '') scroll_to_hash(); } } if (ftl.new_arrivals) { var product_filter = (function() { var items; function update() { if (items === undefined) return; for (var i = 0; i < items.length; i++) { items[i].content_elm.style.display = ignore_categories.get(items[i].category) ? 'none' : ''; } } return { register: function(list) { items = list; ignore_categories.watch('late', update); }, }; })(); var catnr_re = new RegExp('categor(y|ies)/([0-9]+)/'); var catstr_re = new RegExp('\\bf=([a-zA-Z0-9]+)'); var catstr_cleanup_re = new RegExp('^.*=([0-9]+)$'); var ignore_category = function(elm, cat) { elm.addEventListener('click', function() { ignore_categories.set(cat); }); ignore_categories.watch('late', function(c, s) { if (c === cat) elm.checked = !s; }); elm.checked = !ignore_categories.get(cat); }; var add_ign_cb = function(elm) { try { var cat = atob(elm.lastElementChild.href.match(catstr_re)[1]) .match(catstr_cleanup_re)[1]; var cb = document.createElement('input'); cb.type = 'checkbox'; cb.style.float = 'right'; cb.style.margin = '2px -2px 0px -11px'; ignore_category(cb, cat); elm.insertBefore(cb, elm.firstElementChild); } catch (e) {} }; block.watch('ready', function() { var container = ftl.mobile ? document.getElementsByClassName('ProductsGrid')[0] : document.getElementById('Products_Grid'); if (container === null || container === undefined) return; var list = []; for (var elm = container.firstElementChild; elm !== null; elm = elm.nextElementSibling) { try { var cat = elm.lastElementChild.lastElementChild.firstElementChild.href .match(catnr_re)[2]; list.push({ content_elm: elm, category: cat }); } catch (e) {} } product_filter.register(list); var cats, i; if (ftl.mobile) { var heads = document.getElementsByClassName('panel-heading'); for (i = 0; i < heads.length; i++) { if (heads[i].textContent.trim() === 'Category') { cats = heads[i]; break; } } if (cats === undefined) return; cats = cats.parentElement.lastElementChild.firstElementChild; while (cats !== null) { add_ign_cb(cats.lastElementChild); cats = cats.nextElementSibling; } } else { var groups = document.getElementsByClassName('FilterGroup'); for (i = 0; i < groups.length; i++) { if (groups[i].textContent.trim() === 'Category') { cats = groups[i]; break; } } if (cats === undefined) return; cats = cats.nextElementSibling; while (cats !== null && cats.classList.contains('FilterEntry')) { add_ign_cb(cats); cats = cats.nextElementSibling; } } }); } // Mobile reviews don't show tags if (ftl.reviews && !ftl.mobile) { var reg_tags = function(badges) { var posts = []; for (var i in badges) { var badge = badges[i]; if (badge.innerText === '' || !badge.previousElementSibling || badge.previousElementSibling.className !== 'Nickname') continue; posts.push({ user: badge.previousElementSibling.innerText.trim(), badge: badge, }); } if (posts.length !== 0) user_tags.register(posts); }; block.watch('ready', function() { var badges = document.getElementsByClassName('Badges'); if (badges.length !== 0) { reg_tags(badges); } else { var elm; if (!ftl.mobile) elm = document.getElementById('Reviews-tab'); else elm = document.getElementById('reviews'); if (elm !== null) { new MutationObserver(function(es, mo) { mo.disconnect(); reg_tags(document.getElementsByClassName('Badges')); }).observe(elm, { childlist: true }); } } }); } if (!ftl.mobile) { if (ftl.prsearch) { var search = location.href.match(/\/search(\/([0-9]+)?-?(.*))?\?([^&]+)/); if (search !== null) { if (!search[2]) search[2] = '0'; var sortcookie = document.cookie.match(/\bsort=([a-zA-Z0-9]+)/); sortcookie = sortcookie ? sortcookie[1] : 'r'; location.replace(location.origin + '/category/' + search[2] + '/search/-/p/0?f=-&keywords=' + search[4] + '&sort=' + sortcookie); } } if (ftl.prcategory && location.search) { block.watch('ready', function() { var sortelm = document.getElementById('content_SortOrder'); if (sortelm) { sortelm.onchange = function() { document.cookie = 'sort=' + this.value + ';path=/'; location.href = location.href.replace(/\bsort=[^&]*/, '') + '&sort=' + this.value; }; } var searchcat = document.getElementById('searchBarCategory'); var urlcat = location.search.match(/\bf=([a-zA-Z0-9]+)/); if (searchcat !== null && urlcat !== null) { searchcat.value = atob(urlcat[1]).replace(/^.*=/, ''); if (!searchcat.value) searchcat.value = ''; } }); } } else { if (ftl.prsearch) { var search = location.href.match(/\/search\/([0-9]+).*\?(.*)/); if (search !== null) { var sortcookie = document.cookie.match(/\bsort=([a-zA-Z0-9]+)/); sortcookie = sortcookie ? sortcookie[1] : '0'; location.replace(location.origin + '/c/' + search[1] + '/-/-/p/0?f=-&sort=' + sortcookie + '&' + search[2]); } } if (ftl.prcategory) { var fix_onclick = function(elm, sortnr) { elm.onclick = function() { document.cookie = 'sort=' + sortnr + ';path=/'; location.href = location.href.replace(/\bsort=[^&]*/, '') + '&sort=' + sortnr; }; }; block.watch('ready', function () { try { var li = document.querySelector('.clearfix button .glyphicon-sort') .parentElement.parentElement.lastElementChild.firstElementChild; for ( ; li !== null; li = li.nextElementSibling) { console.log(li); var inner = li.firstElementChild; var sortnr = String.match(inner.onclick, /\bsort=([0-9]+)/); if (sortnr === null) continue; fix_onclick(inner, sortnr[1]); } } catch (e) {} }); } } if (ftl.settings) { block.watch('ready', function() { var panel = document.getElementsByClassName('PageContentPanel')[0]; var div = document.createElement('div'); div.style.marginTop = '5px'; div.innerHTML = '<table style="width: auto"><tbody><tr><td class="MediumL' + 'abel Bold EndOfInlineSection" style="padding: 5px">Ignored Users</td' + '><td width=15></td><td class="MediumLabel Bold EndOfInlineSection" s' + 'tyle="padding: 5px">FastTech Forum Enhancements Settings</td></tr><t' + 'r><td style="padding: 0px"><div class="BGShadow" style="padding: 10p' + 'x"></div></td><td width=15></td><td style="padding:10px"><div class=' + '"BGShadow" style="padding: 0px"></div></td></tr></tbody></table>'; var divs = div.getElementsByTagName('div'); settings_menu.register(divs[1], full_menus); manage_menu.register(divs[0]); divs[0].getElementsByClassName('il_addbtn')[0].className = 'FormButton blue'; panel.appendChild(div); }); }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址