From b4f9ac2bf4799f7cd16b185a105c8e27601238b3 Mon Sep 17 00:00:00 2001 From: topkek Date: Sat, 1 Nov 2014 03:04:58 +0000 Subject: [PATCH 01/22] max width for upload field --- stylesheets/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stylesheets/style.css b/stylesheets/style.css index f21409a0..6b385a3f 100644 --- a/stylesheets/style.css +++ b/stylesheets/style.css @@ -100,6 +100,10 @@ input[type="text"],input[type="password"],textarea { max-width: 100%; } +#upload_file { + max-width: 230px; +} + form table tr td { text-align: left; margin: 0; From 47348bd3365e44728a7f5273f8eb35fdd31588e5 Mon Sep 17 00:00:00 2001 From: topkek Date: Sat, 1 Nov 2014 23:05:56 +0000 Subject: [PATCH 02/22] Comment out example --- js/options/user-css.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/options/user-css.js b/js/options/user-css.js index 840bcd1f..3a94e0d7 100644 --- a/js/options/user-css.js +++ b/js/options/user-css.js @@ -45,7 +45,7 @@ var update_textarea = function() { textarea.text("/* "+_("Enter here your own CSS rules...")+" */\n" + "/* "+_("If you want to make a redistributable style, be sure to\nhave a Yotsuba B theme selected.")+" */\n" + "/* "+_("You can include CSS files from remote servers, for example:")+" */\n" + - '@import "http://example.com/style.css";'); + '/* @import "http://example.com/style.css"; */'); } else { textarea.text(localStorage.user_css); From 331c10d2f2f706cd4fcbbd259a8cf39b2ea20ed3 Mon Sep 17 00:00:00 2001 From: topkek Date: Sat, 1 Nov 2014 23:06:49 +0000 Subject: [PATCH 03/22] Comment out example --- js/options/user-js.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/options/user-js.js b/js/options/user-js.js index 0b7d140f..338e66d6 100644 --- a/js/options/user-js.js +++ b/js/options/user-js.js @@ -54,7 +54,7 @@ var update_textarea = function() { textarea.text("/* "+_("Enter here your own Javascript code...")+" */\n" + "/* "+_("Have a backup of your storage somewhere, as messing here\nmay render you this website unusable.")+" */\n" + "/* "+_("You can include JS files from remote servers, for example:")+" */\n" + - 'load_js("http://example.com/script.js");'); + '/* load_js("http://example.com/script.js"); */'); } else { textarea.text(localStorage.user_js); From 68890326e137470b866d8df0c4930fcb8449d97e Mon Sep 17 00:00:00 2001 From: Forkless Date: Sat, 1 Nov 2014 22:28:16 -0500 Subject: [PATCH 04/22] Fix for update to comment toolbar (should work now) --- js/comment-toolbar.js | 563 +++++++++++++++++++++++++---------------- js/options/user-css.js | 2 +- js/options/user-js.js | 2 +- 3 files changed, 351 insertions(+), 216 deletions(-) diff --git a/js/comment-toolbar.js b/js/comment-toolbar.js index 7ae11a73..be58c1f6 100644 --- a/js/comment-toolbar.js +++ b/js/comment-toolbar.js @@ -8,253 +8,388 @@ * $config['additional_javascript'][] = 'js/comment-toolbar.js'; */ if (active_page == 'thread' || active_page == 'index') { - $(document).ready(function () { - 'use strict'; - var formats = { - bold: { - displayText: 'B', - altText: 'bold', - styleCSS: 'font-weight: bold;', - options: { - prefix: "'''", - suffix: "'''" - }, - edit: function (box, options) { - wrapSelection(box, options); - }, - shortcutKey: 'b' + var formatText = (function($){ + "use strict"; + var self = {}; + self.rules = { + spoiler: { + text: 'Spoiler', + key: 's', + multiline: false, + exclusiveline: false, + prefix:'**', + suffix:'**' }, italics: { - displayText: 'i', - altText: 'italics', - styleCSS: 'font-style: italic;', - options: { - prefix: "''", - suffix: "''" - }, - edit: function (box, options) { - wrapSelection(box, options); - }, - shortcutKey: 'i' + text: 'Italics', + key: 'i', + multiline: false, + exclusiveline: false, + prefix: "''", + suffix: "''" + }, + bold: { + text: 'Bold', + key: 'b', + multiline: false, + exclusiveline: false, + prefix: "'''", + suffix: "'''" }, - under: { - displayText: 'U', - altText: 'underline', - styleCSS: 'text-decoration: underline;', - options: { - prefix: '__', - suffix: '__' - }, - edit: function (box, options) { - wrapSelection(box, options); - }, - shortcutKey: 'u' - }, - spoiler: { - displayText: 'spoiler', - altText: 'mark as spoiler', - styleCSS: '', - options: { - prefix: '[spoiler]', - suffix: '[/spoiler]' - }, - edit: function (box, options) { - wrapSelection(box, options); - }, - shortcutKey: 's' + underline: { + text: 'Underline', + key: 'u', + multiline: false, + exclusiveline: false, + prefix:'__', + suffix:'__' }, code: { - displayText: 'code', - altText: "code formatting", - styleCSS: 'font-family: "Courier New", Courier, monospace;', - options: { - prefix: '[code]', - suffix: '[/code]', - multiline: true - }, - edit: function (box, options) { - wrapSelection(box, options); - }, - shortcutKey: 'd' + text: 'Code', + key: 'f', + multiline: true, + exclusiveline: false, + prefix: '[code]', + suffix: '[/code]' }, strike: { - displayText: 'strike', - altText: 'strikethrough', - styleCSS: 'text-decoration: line-through;', - options: { - prefix: '~~', - suffix: '~~' - }, - edit: function (box, options) { - wrapSelection(box, options); - } + text: 'Strike', + key: 'd', + multiline:false, + exclusiveline:false, + prefix:'~~', + suffix:'~~' }, heading: { - displayText: 'heading', - altText: 'redtext', - styleCSS: 'color: #AF0A0F; font-weight: bold;', - options: { - prefix: '==', - suffix: '==', - exclusiveLine: true - }, - edit: function (box, options) { - wrapSelection(box, options); - } + text: 'Heading', + key: 'r', + multiline:false, + exclusiveline:true, + prefix:'==', + suffix:'==' } }; - - var key, name, altText, ele; - var strBuilder = []; - var subStr = ''; - var styleRules = ''; - - //not exactly mine - var wrapSelection = function (box, options) { - if (box == null) { - return; + + self.toolbar_wrap = function(node) { + if (!localStorage.formatText_enable || localStorage.formatText_enable == 'false') return; + var parent = $(node).parents('form[name="post"]'); + self.wrap(parent.find('#body')[0],'textarea[name="body"]', parent.find('.format-text > select')[0].value, false); + }; + + self.wrap = function(ref, target, option, expandedwrap) { + if (!localStorage.formatText_enable || localStorage.formatText_enable == 'false') return; + // clean and validate arguments + if (ref == null) return; + var settings = {multiline: false, exclusiveline: false, prefix:'', suffix: null}; + $.extend(settings,JSON.parse(localStorage.formatText_rules)[option]); + + // resolve targets into array of proper node elements + // yea, this is overly verbose, oh well. + var res = []; + if (target instanceof Array) { + for (var indexa in target) { + if (target.hasOwnProperty(indexa)) { + if (typeof target[indexa] == 'string') { + var nodes = $(target[indexa]); + for (var indexb in nodes) { + if (indexa.hasOwnProperty(indexb)) res.push(nodes[indexb]); + } + } else { + res.push(target[indexa]); + } + } + } + } else { + if (typeof target == 'string') { + var nodes = $(target); + for (var index in nodes) { + if (nodes.hasOwnProperty(index)) res.push(nodes[index]); + } + } else { + res.push(target); + } } - var prefix = options.prefix; - var suffix = options.suffix; - var multiline = options.multiline || false; - var exclusiveLine = options.exclusiveLine || false; - + target = res; //record scroll top to restore it later. - var scrollTop = box.scrollTop; - var selectionStart = box.selectionStart; - var selectionEnd = box.selectionEnd; - var text = box.value; - var beforeSelection = text.substring(0, selectionStart); - var selectedText = text.substring(selectionStart, selectionEnd); - var afterSelection = text.substring(selectionEnd); + var scrollTop = ref.scrollTop; + //We will restore the selection later, so record the current selection + var selectionStart = ref.selectionStart; + var selectionEnd = ref.selectionEnd; + + var text = ref.value; + var before = text.substring(0, selectionStart); + var selected = text.substring(selectionStart, selectionEnd); + var after = text.substring(selectionEnd); + var whiteSpace = [" ","\t"]; var breakSpace = ["\r","\n"]; - var trailingSpace = ""; - var cursor = selectedText.length - 1; - - //remove trailing space - while (cursor > 0 && selectedText[cursor] === " ") { - trailingSpace += " "; - cursor--; - } - selectedText = selectedText.substring(0, cursor + 1); - - if (!multiline) - selectedText = selectedText.replace(/(\r|\n|\r\n)/g, suffix +"$1"+ prefix); - - if (exclusiveLine) { + var cursor; + + // handles multiline selections on formatting that doesn't support spanning over multiple lines + if (!settings.multiline) selected = selected.replace(/(\r|\n|\r\n)/g,settings.suffix +"$1"+ settings.prefix); + + // handles formatting that requires it to be on it's own line OR if the user wishes to expand the wrap to the nearest linebreak + if (settings.exclusiveline || expandedwrap) { // buffer the begining of the selection until a linebreak - cursor = beforeSelection.length -1; - while (cursor >= 0 && breakSpace.indexOf(beforeSelection.charAt(cursor)) == -1) { + cursor = before.length -1; + while (cursor >= 0 && breakSpace.indexOf(before.charAt(cursor)) == -1) { cursor--; } - selectedText = beforeSelection.substring(cursor +1) + selectedText; - beforeSelection = beforeSelection.substring(0, cursor +1); + selected = before.substring(cursor +1) + selected; + before = before.substring(0, cursor +1); // buffer the end of the selection until a linebreak cursor = 0; - while (cursor < afterSelection.length && breakSpace.indexOf(afterSelection.charAt(cursor)) == -1) { + while (cursor < after.length && breakSpace.indexOf(after.charAt(cursor)) == -1) { cursor++; } - selectedText += afterSelection.substring(0, cursor); - afterSelection = afterSelection.substring(cursor); + selected += after.substring(0, cursor); + after = after.substring(cursor); } - - box.value = beforeSelection + prefix + selectedText + suffix + trailingSpace + afterSelection; - - box.selectionEnd = beforeSelection.length + prefix.length + selectedText.length; + + // set values + var res = before + settings.prefix + selected + settings.suffix + after; + $(target).val(res); + + // restore the selection area and scroll of the reference + ref.selectionEnd = before.length + settings.prefix.length + selected.length; if (selectionStart === selectionEnd) { - box.selectionStart = box.selectionEnd; + ref.selectionStart = ref.selectionEnd; } else { - box.selectionStart = beforeSelection.length + prefix.length; + ref.selectionStart = before.length + settings.prefix.length; } - box.scrollTop = scrollTop; + ref.scrollTop = scrollTop; }; + + self.build_toolbars = function(){ + if (!localStorage.formatText_enable || localStorage.formatText_enable == 'false') return; + if (localStorage.formatText_toolbar == 'true'){ + // remove existing toolbars + if ($('.format-text').length > 0) $('.format-text').remove(); + + // Place toolbar above each textarea input + var name, options = '', rules = JSON.parse(localStorage.formatText_rules); + for (var index in rules) { + if (!rules.hasOwnProperty(index)) continue; + name = rules[index].text; - /* Generate the HTML for the toolbar - */ - for (ele in formats) { - if (formats.hasOwnProperty(ele) && formats[ele].displayText != null) { - name = formats[ele].displayText; - altText = formats[ele].altText || ''; - key = formats[ele].shortcutKey; - - //add tooltip text - if (altText) { - if (key) { - altText += ' (ctrl+'+ key +')'; + //add hint if key exists + if (rules[index].key) { + name += ' (CTRL + '+ rules[index].key.toUpperCase() +')'; } - altText = 'title="'+ altText +'"'; + options += ''; } - - subStr = ''+ name +''; - strBuilder.push(subStr); + $('[name="body"]').before('
Wrap
'); + $('body').append(''); + } + }; + + self.add_rule = function(rule, index){ + if (rule === undefined) rule = { + text: 'New Rule', + key: '', + multiline:false, + exclusiveline:false, + prefix:'', + suffix:'' + } + + // generate an id for the rule + if (index === undefined) { + var rules = JSON.parse(localStorage.formatText_rules); + while (rules[index] || index === undefined) { + index = '' + index +='abcdefghijklmnopqrstuvwxyz'.substr(Math.floor(Math.random()*26),1); + index +='abcdefghijklmnopqrstuvwxyz'.substr(Math.floor(Math.random()*26),1); + index +='abcdefghijklmnopqrstuvwxyz'.substr(Math.floor(Math.random()*26),1); + } + } + if (window.Options && Options.get_tab('formatting')){ + var html = $('
').html('\ + \ + \ + \ + \ + \ + \ + \ + '); + + if ($('.format_rule').length > 0) { + $('.format_rule').last().after(html); + } else { + Options.extend_tab('formatting', html); + } + } + }; + + self.save_rules = function(){ + var rule, newrules = {}, rules = $('.format_rule'); + for (var index=0;rules[index];index++) { + rule = $(rules[index]); + newrules[rule.attr('name')] = { + text: rule.find('[name="text"]').val(), + key: rule.find('[name="key"]').val(), + prefix: rule.find('[name="prefix"]').val(), + suffix: rule.find('[name="suffix"]').val(), + multiline: rule.find('[name="multiline"]').is(':checked'), + exclusiveline: rule.find('[name="exclusiveline"]').is(':checked') + }; + } + localStorage.formatText_rules = JSON.stringify(newrules); + self.build_toolbars(); + }; + + self.reset_rules = function(to_default) { + $('.format_rule').remove(); + var rules; + if (to_default) rules = self.rules; + else rules = JSON.parse(localStorage.formatText_rules); + for (var index in rules){ + if (!rules.hasOwnProperty(index)) continue; + self.add_rule(rules[index], index); + } + }; + + // setup default rules for customizing + if (!localStorage.formatText_rules) localStorage.formatText_rules = JSON.stringify(self.rules); + + // Add settings to Options panel general tab + if (window.Options && Options.get_tab('general')) { + var s1 = '#formatText_enable>input', s2 = '#formatText_keybinds>input', s3 = '#formatText_toolbar>input', e = 'change'; + Options.extend_tab('general', '\ +
\ + Formatting Options\ + \ + \ + \ +
\ + '); + } else { + var s1 = '#formatText_enable', s2 = '#formatText_keybinds', s3 = '#formatText_toolbar', e = 'click'; + $('hr:first').before(''); + $('hr:first').before(''); + $('hr:first').before(''); + } + + // setting for enableing text formatting + $(s1).on(e, function(e) { + if (!localStorage.formatText_enable || localStorage.formatText_enable == 'false') { + localStorage.formatText_enable = 'true'; + if (window.Options && Options.get_tab('general')) e.target.checked = true; } else { - continue; + localStorage.formatText_enable = 'false'; + if (window.Options && Options.get_tab('general')) e.target.checked = false; + } + }); + + // setting for enableing formatting keybinds + $(s2).on(e, function(e) { + if (!localStorage.formatText_keybinds || localStorage.formatText_keybinds == 'false') { + localStorage.formatText_keybinds = 'true'; + if (window.Options && Options.get_tab('general')) e.target.checked = true; + } else { + localStorage.formatText_keybinds = 'false'; + if (window.Options && Options.get_tab('general')) e.target.checked = false; + } + }); + + // setting for toolbar injection + $(s3).on(e, function(e) { + if (!localStorage.formatText_toolbar || localStorage.formatText_toolbar == 'false') { + localStorage.formatText_toolbar = 'true'; + if (window.Options && Options.get_tab('general')) e.target.checked = true; + formatText.build_toolbars(); + } else { + localStorage.formatText_toolbar = 'false'; + if (window.Options && Options.get_tab('general')) e.target.checked = false; + $('.format-text').remove(); + } + }); + + // make sure the tab settings are switch properly at loadup + if (window.Options && Options.get_tab('general')) { + if (localStorage.formatText_enable == 'true') $(s1)[0].checked = true; + else $(s1)[0].checked = false; + if (localStorage.formatText_keybinds == 'true') $(s2)[0].checked = true; + else $(s2)[0].checked = false; + if (localStorage.formatText_toolbar == 'true') $(s2)[0].checked = true; + else $(s3)[0].checked = false; + } + + // add the tab for customizing the format settings + if (window.Options && !Options.get_tab('formatting')) { + Options.add_tab('formatting', 'angle-right', 'Customize Formatting'); + Options.extend_tab('formatting', '\ + \ + '); + + // Data control row + Options.extend_tab('formatting', '\ + \ + \ + \ + \ + '); + + // Descriptor row + Options.extend_tab('formatting', '\ + Name\ + ML\ + EL\ + Prefix\ + Suffix\ + Key\ + '); + + // Rule rows + var rules = JSON.parse(localStorage.formatText_rules); + for (var index in rules){ + if (!rules.hasOwnProperty(index)) continue; + self.add_rule(rules[index], index); } } - - $( 'textarea[name="body"]' ).before( '
' ); - $( '.tf-toolbar' ).html( strBuilder.join(' | ') ); - - /* Sets the CSS style - */ - styleRules = '\n/* generated by 8chan Formatting Tools */'+ - '\n.tf-toolbar {padding: 0px 5px 1px 5px;}'+ - '\n.tf-toolbar :link {text-decoration: none;}'; - for (ele in formats) { - if (formats.hasOwnProperty(ele) && formats[ele].styleCSS) { - styleRules += ' \n#tf-' + ele + ' {' + formats[ele].styleCSS + '}'; - } - } - //add CSS rule to user's custom CSS if it exist - if ($( '.user-css' ).length !== 0) { - $( '.user-css' ).append( styleRules ); - } else { - $( 'body' ).append( '' ); - } - - /* Attach event listeners - */ - $( 'body' ).on( 'keydown', 'textarea[name="body"]', {formats: formats}, function (e) { - //shortcuts - if (e.ctrlKey) { - var ch = String.fromCharCode(e.which).toLowerCase(); - var box = e.target; - var formats = e.data.formats; - for (var ele in formats) { - if (formats.hasOwnProperty(ele) && (ch === formats[ele].shortcutKey)) { - formats[ele].edit(box, formats[ele].options); - e.preventDefault(); - } - } - } - }); - $( 'body' ).on( 'keydown', '#quick-reply textarea[name="body"]', {formats: formats}, function (e) { - //close quick reply when esc is prssed - if (e.which === 27) { - $( '.close-btn' ).trigger( 'click' ); - } - }); - $( 'body' ).on( 'click', '.tf-toolbar a[id]', {formats: formats}, function (e) { - //toolbar buttons - var formats = e.data.formats; - var box = $(e.target).parent().next()[0]; - - for (var ele in formats) { - if (formats.hasOwnProperty(ele) && (e.target.id === 'tf-' + ele)) { - formats[ele].edit(box, formats[ele].options); - } - } - }); - // $( 'body' ).on( 'keydown', function (e) { - // if (e.which === 67 && - // e.target.nodeName !== 'INPUT' && //The C, the whole C, and nothing but the C - // e.target.nodeName !== 'TEXTAREA' && - // !(e.ctrlKey || e.altKey || e.shiftKey)) { - // document.location.href = '//'+ document.location.host +'/'+ board_name +'/catalog.html'; - // } - // }); + + return self; + })(jQuery); + // run initial toolbar injection + formatText.build_toolbars(); + + //attach listeners to so it also works on quick-reply box + $('body').on('keydown', '#body, #quick-reply #body', function(e) { + if (!localStorage.formatText_enable || localStorage.formatText_enable == 'false') return; + if (!localStorage.formatText_keybinds || localStorage.formatText_keybinds == 'false') return; + var key = String.fromCharCode(e.which).toLowerCase(); + var rules = JSON.parse(localStorage.formatText_rules); + for (var index in rules) { + if (!rules.hasOwnProperty(index)) continue; + if (key === rules[index].key && e.ctrlKey) { + e.preventDefault(); + if (e.shiftKey) { + formatText.wrap(e.target, 'textarea[name="body"]', index, true); + } else { + formatText.wrap(e.target, 'textarea[name="body"]', index, false); + } + } + } }); -} + $(document).trigger('formatText'); +} \ No newline at end of file diff --git a/js/options/user-css.js b/js/options/user-css.js index 3a94e0d7..22b005fa 100644 --- a/js/options/user-css.js +++ b/js/options/user-css.js @@ -17,7 +17,7 @@ var textarea = $("").css({ "font-size": 12, position: "absolute", top: 35, bottom: 35, - width: "calc(100% - 12px)", margin: 0, padding: 0, border: "1px solid black", + width: "calc(100% - 20px)", margin: 0, padding: "4px", border: "1px solid black", left: 5, right: 5 }).appendTo(tab.content); var submit = $("").css({ diff --git a/js/options/user-js.js b/js/options/user-js.js index 338e66d6..221ca5d9 100644 --- a/js/options/user-js.js +++ b/js/options/user-js.js @@ -17,7 +17,7 @@ var textarea = $("").css({ "font-size": 12, position: "absolute", top: 35, bottom: 35, - width: "calc(100% - 12px)", margin: 0, padding: 0, border: "1px solid black", + width: "calc(100% - 20px)", margin: 0, padding: "4px", border: "1px solid black", left: 5, right: 5 }).appendTo(tab.content); var submit = $("").css({ From 7a8fd3c100ebde4fc5a8d00ed6339e3a8e1f0146 Mon Sep 17 00:00:00 2001 From: Forkless Date: Sat, 1 Nov 2014 22:34:21 -0500 Subject: [PATCH 05/22] Added thread statistics. --- js/thread-stats.js | 109 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 js/thread-stats.js diff --git a/js/thread-stats.js b/js/thread-stats.js new file mode 100644 index 00000000..59fed912 --- /dev/null +++ b/js/thread-stats.js @@ -0,0 +1,109 @@ +/* + * thread-stats.js + * - Adds statistics of the thread below the posts area + * - Shows ID post count beside each postID on hover + * + * Usage: + * $config['additional_javascript'][] = 'js/jquery.min.js'; + * $config['additional_javascript'][] = 'js/thread-stats.js'; + */ +if (active_page == 'thread') { + //check if page uses unique ID + var IDsupport = ($('.poster_id').length > 0); + var thread_id = (document.location.pathname + document.location.search).split('/'); + thread_id = thread_id[thread_id.length -1].split('+')[0].split('.')[0]; + + $('form[name="postcontrols"] > .delete') + .first() + .before('
'); + var el = $('#thread_stats'); + el.prepend('Page ?'); + if (IDsupport){ + el.prepend('0 UIDs | '); + } + el.prepend('0 images | '); + el.prepend('0 replies | '); + delete el; + function update_thread_stats(){ + var op = $('#thread_'+ thread_id +' > div.post.op:not(.post-hover):not(.inline)').first(); + var replies = $('#thread_'+ thread_id +' > div.post.reply:not(.post-hover):not(.inline)'); + // post count + $('#thread_stats_posts').text(replies.length); + // image count + $('#thread_stats_images').text(replies.filter(function(){ + return $(this).find('> .files').text().trim() != false; + }).length); + // unique ID count + if (IDsupport) { + var opID = op.find('> .intro > .poster_id').text(); + var ids = {}; + replies.each(function(){ + var cur = $(this).find('> .intro > .poster_id'); + var curID = cur.text(); + if (ids[curID] === undefined) { + ids[curID] = 0; + } + ids[curID]++; + }); + if (ids[opID] === undefined) { + ids[opID] = 0; + } + ids[opID]++; + replies.each(function(){ + var cur = $(this).find('> .intro > .poster_id'); + cur.find('+ .posts_by_id').remove(); + cur.after(' ('+ ids[cur.text()] +')'); + }); + var size = function(obj) { + var size = 0, key; + for (key in obj) { + if (obj.hasOwnProperty(key)) size++; + } + return size; + }; + $('#thread_stats_uids').text(size(ids)); + } + $.getJSON('//'+ document.location.host +'/'+ board_name +'/threads.json').success(function(data){ + var found, page = 'Pruned or Deleted'; + for (var i=0;data[i];i++){ + var threads = data[i].threads; + for (var j=0; threads[j]; j++){ + if (parseInt(threads[j].no) == parseInt(thread_id)) { + page = data[i].page +1; + found = true; + break; + } + } + if (found) break; + } + $('#thread_stats_page').text(page); + if (!found) $('#thread_stats_page').css('color','red'); + }); + } + // load the current page the thread is on. + // uses ajax call so it gets loaded on a delay (depending on network resources available) + var thread_stats_page_timer = setInterval(function(){ + $.getJSON('//'+ document.location.host +'/'+ board_name +'/threads.json').success(function(data){ + var found, page = 'Pruned or Deleted'; + for (var i=0;data[i];i++){ + var threads = data[i].threads; + for (var j=0; threads[j]; j++){ + if (parseInt(threads[j].no) == parseInt(thread_id)) { + page = data[i].page +1; + found = true; + break; + } + } + if (found) break; + } + $('#thread_stats_page').text(page); + if (!found) $('#thread_stats_page').css('color','red'); + }); + },30000); + $(document).ready(function(){ + $('body').append(''); + update_thread_stats(); + $('#update_thread').click(update_thread_stats); + $(document).on('new_post',update_thread_stats); + }); +} \ No newline at end of file From 6b1f55d9a0117251f99e4921ce9379e778f854e2 Mon Sep 17 00:00:00 2001 From: Markerov Date: Sun, 2 Nov 2014 22:55:35 +0800 Subject: [PATCH 06/22] Catalog Search --- js/catalog-search.js | 82 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 js/catalog-search.js diff --git a/js/catalog-search.js b/js/catalog-search.js new file mode 100644 index 00000000..97121e3b --- /dev/null +++ b/js/catalog-search.js @@ -0,0 +1,82 @@ +/* + * catalog-search.js + * - Search and filters threads when on catalog view + * - Optional shortcuts 's' and 'esc' to open and close the search. + * + * Usage: + * $config['additional_javascript'][] = 'js/jquery.min.js'; + * $config['additional_javascript'][] = 'js/comment-toolbar.js'; + */ +if (active_page == 'catalog') { + $(document).ready(function () { + 'use strict'; + + // 'true' = enable shortcuts + var useKeybinds = true; + + // trigger the search 400ms after last keystroke + var delay = 400; + var timeoutHandle; + + //search and hide none matching threads + function filter(search_term) { + $('.replies').each(function () { + var subject = $(this).children('.intro').text().toLowerCase(); + var comment = $(this).clone().children().remove(':lt(2)').end().text().trim().toLowerCase(); + search_term = search_term.toLowerCase(); + + if (subject.indexOf(search_term) == -1 && comment.indexOf(search_term) == -1) { + $(this).parents('div[id="Grid"]>.mix').css('display', 'none'); + } else { + $(this).parents('div[id="Grid"]>.mix').css('display', 'inline-block'); + } + }); + } + + function searchToggle() { + var button = $('#catalog_search_button')[0]; + + if (!button.dataset.expanded) { + button.dataset.expanded = '1'; + button.innerText = 'Close'; + $('.catalog_search').append(' '); + $('#search_field').focus(); + } else { + delete button.dataset.expanded; + button.innerText = 'Search'; + $('.catalog_search').children().last().remove(); + $('div[id="Grid"]>.mix').each(function () { $(this).css('display', 'inline-block'); }); + } + } + + $('.threads').before('[]'); + $('#catalog_search_button').text('Search'); + + $('#catalog_search_button').on('click', searchToggle); + $('.catalog_search').on('keyup', 'input#search_field', function (e) { + window.clearTimeout(timeoutHandle); + timeoutHandle = window.setTimeout(filter, 400, e.target.value); + }); + + if (useKeybinds) { + // 's' + $('body').on('keydown', function (e) { + if (e.which === 83 && e.target.tagName === 'BODY' && !(e.ctrlKey || e.altKey || e.shiftKey)) { + e.preventDefault(); + if ($('#search_field').length !== 0) { + $('#search_field').focus(); + } else { + searchToggle(); + } + } + }); + // 'esc' + $('.catalog_search').on('keydown', 'input#search_field', function (e) { + if (e.which === 27 && !(e.ctrlKey || e.altKey || e.shiftKey)) { + window.clearTimeout(timeoutHandle); + searchToggle(); + } + }); + } + }); +} From 69bea13a20dae51620aaa4687b090e924445d647 Mon Sep 17 00:00:00 2001 From: Forkless Date: Sun, 2 Nov 2014 23:22:19 -0600 Subject: [PATCH 07/22] Added support for youtube's timeslice embed parameters (start and end) --- inc/config.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/config.php b/inc/config.php index ddf23279..655c69cc 100644 --- a/inc/config.php +++ b/inc/config.php @@ -999,8 +999,8 @@ // It's very important that you match the entire input (with ^ and $) or things will not work correctly. $config['embedding'] = array( array( - '/^https?:\/\/(\w+\.)?youtube\.com\/watch\?v=([a-zA-Z0-9\-_]{10,11})(&.+)?$/i', - '' + '/^https?:\/\/(?:\w+\.)?youtube\.com\/watch\?(?:&?v=([a-zA-Z0-9\-_]{10,11})|(&?start=\d*)|(&?end=\d*)|(?:&?[^&]+))*$/i', + '' ), array( '/^https?:\/\/(\w+\.)?vimeo\.com\/(\d{2,10})(\?.+)?$/i', From cfa01786bd952dcb187d046dade4e16fab4ff905 Mon Sep 17 00:00:00 2001 From: Forkless Date: Sun, 2 Nov 2014 23:46:13 -0600 Subject: [PATCH 08/22] Missed a couple things for the youtube timeslice support. --- inc/config.php | 6 +++--- js/youtube.js | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/inc/config.php b/inc/config.php index 655c69cc..9f62a19d 100644 --- a/inc/config.php +++ b/inc/config.php @@ -1000,7 +1000,7 @@ $config['embedding'] = array( array( '/^https?:\/\/(?:\w+\.)?youtube\.com\/watch\?(?:&?v=([a-zA-Z0-9\-_]{10,11})|(&?start=\d*)|(&?end=\d*)|(?:&?[^&]+))*$/i', - '' + '' ), array( '/^https?:\/\/(\w+\.)?vimeo\.com\/(\d{2,10})(\?.+)?$/i', @@ -1657,7 +1657,7 @@ $config['board_regex'] = '[0-9a-zA-Z$_\x{0080}-\x{FFFF}]{1,58}'; // Youtube.js embed HTML code - $config['youtube_js_html'] = '
'. + $config['youtube_js_html'] = ''; diff --git a/js/youtube.js b/js/youtube.js index 9fe81b60..c4ef77fb 100644 --- a/js/youtube.js +++ b/js/youtube.js @@ -26,11 +26,9 @@ onready(function(){ var do_embed_yt = function(tag) { $('div.video-container a', tag).click(function() { - var videoID = $(this.parentNode).data('video'); - $(this.parentNode).html('' ), array( @@ -1657,7 +1657,7 @@ $config['board_regex'] = '[0-9a-zA-Z$_\x{0080}-\x{FFFF}]{1,58}'; // Youtube.js embed HTML code - $config['youtube_js_html'] = '
'. + $config['youtube_js_html'] = ''; diff --git a/inc/instance-config.php b/inc/instance-config.php index f6b4d0b6..5076da2a 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -162,7 +162,7 @@ $config['embedding'] = array( array( - '/^https?:\/\/(\w+\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9\-_]{10,11})(&.+)?$/i', + '/^https?:\/\/(?:\w+\.)?(?:youtube\.com\/watch\?|youtu\.be\/)(?:(?:&?v=)?([a-zA-Z0-9\-_]{10,11})\??|&?(start=\d*)|&?(end=\d*)|(?:&?[^&]+))*$/i', $config['youtube_js_html'] ), array( diff --git a/js/youtube.js b/js/youtube.js index 8aa03211..c4ef77fb 100644 --- a/js/youtube.js +++ b/js/youtube.js @@ -28,7 +28,7 @@ onready(function(){ $('div.video-container a', tag).click(function() { $(this.parentNode).html('