diff --git a/js/comment-toolbar.js b/js/comment-toolbar.js new file mode 100644 index 00000000..7ae11a73 --- /dev/null +++ b/js/comment-toolbar.js @@ -0,0 +1,260 @@ +/* + * comment-toolbar.js + * - Adds a toolbar above the commenting area containing most of 8Chan's formatting options + * - Press Esc to close quick-reply window when it's in focus + * + * Usage: + * $config['additional_javascript'][] = 'js/jquery.min.js'; + * $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' + }, + italics: { + displayText: 'i', + altText: 'italics', + styleCSS: 'font-style: italic;', + options: { + prefix: "''", + suffix: "''" + }, + edit: function (box, options) { + wrapSelection(box, options); + }, + shortcutKey: 'i' + }, + 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' + }, + 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' + }, + strike: { + displayText: 'strike', + altText: 'strikethrough', + styleCSS: 'text-decoration: line-through;', + options: { + prefix: '~~', + suffix: '~~' + }, + edit: function (box, options) { + wrapSelection(box, options); + } + }, + heading: { + displayText: 'heading', + altText: 'redtext', + styleCSS: 'color: #AF0A0F; font-weight: bold;', + options: { + prefix: '==', + suffix: '==', + exclusiveLine: true + }, + edit: function (box, options) { + wrapSelection(box, options); + } + } + }; + + var key, name, altText, ele; + var strBuilder = []; + var subStr = ''; + var styleRules = ''; + + //not exactly mine + var wrapSelection = function (box, options) { + if (box == null) { + return; + } + var prefix = options.prefix; + var suffix = options.suffix; + var multiline = options.multiline || false; + var exclusiveLine = options.exclusiveLine || false; + + //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 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) { + // buffer the begining of the selection until a linebreak + cursor = beforeSelection.length -1; + while (cursor >= 0 && breakSpace.indexOf(beforeSelection.charAt(cursor)) == -1) { + cursor--; + } + selectedText = beforeSelection.substring(cursor +1) + selectedText; + beforeSelection = beforeSelection.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) { + cursor++; + } + selectedText += afterSelection.substring(0, cursor); + afterSelection = afterSelection.substring(cursor); + } + + box.value = beforeSelection + prefix + selectedText + suffix + trailingSpace + afterSelection; + + box.selectionEnd = beforeSelection.length + prefix.length + selectedText.length; + if (selectionStart === selectionEnd) { + box.selectionStart = box.selectionEnd; + } else { + box.selectionStart = beforeSelection.length + prefix.length; + } + box.scrollTop = scrollTop; + }; + + /* 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 +')'; + } + altText = 'title="'+ altText +'"'; + } + + subStr = ''+ name +''; + strBuilder.push(subStr); + } else { + continue; + } + } + + $( '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'; + // } + // }); + + }); +}