Browse Source

Fix for update to comment toolbar (should work now)

pull/40/head
Forkless 10 years ago
committed by czaks
parent
commit
1663efcf9d
  1. 549
      js/comment-toolbar.js
  2. 2
      js/options/user-css.js
  3. 2
      js/options/user-js.js

549
js/comment-toolbar.js

@ -8,253 +8,388 @@
* $config['additional_javascript'][] = 'js/comment-toolbar.js'; * $config['additional_javascript'][] = 'js/comment-toolbar.js';
*/ */
if (active_page == 'thread' || active_page == 'index') { if (active_page == 'thread' || active_page == 'index') {
$(document).ready(function () { var formatText = (function($){
'use strict'; "use strict";
var formats = { var self = {};
bold: { self.rules = {
displayText: 'B', spoiler: {
altText: 'bold', text: 'Spoiler',
styleCSS: 'font-weight: bold;', key: 's',
options: { multiline: false,
prefix: "'''", exclusiveline: false,
suffix: "'''" prefix:'**',
}, suffix:'**'
edit: function (box, options) {
wrapSelection(box, options);
},
shortcutKey: 'b'
}, },
italics: { italics: {
displayText: 'i', text: 'Italics',
altText: 'italics', key: 'i',
styleCSS: 'font-style: italic;', multiline: false,
options: { exclusiveline: false,
prefix: "''", prefix: "''",
suffix: "''" suffix: "''"
}, },
edit: function (box, options) { bold: {
wrapSelection(box, options); text: 'Bold',
}, key: 'b',
shortcutKey: 'i' multiline: false,
}, exclusiveline: false,
under: { prefix: "'''",
displayText: 'U', suffix: "'''"
altText: 'underline',
styleCSS: 'text-decoration: underline;',
options: {
prefix: '__',
suffix: '__'
},
edit: function (box, options) {
wrapSelection(box, options);
},
shortcutKey: 'u'
}, },
spoiler: { underline: {
displayText: 'spoiler', text: 'Underline',
altText: 'mark as spoiler', key: 'u',
styleCSS: '', multiline: false,
options: { exclusiveline: false,
prefix: '[spoiler]', prefix:'__',
suffix: '[/spoiler]' suffix:'__'
},
edit: function (box, options) {
wrapSelection(box, options);
},
shortcutKey: 's'
}, },
code: { code: {
displayText: 'code', text: 'Code',
altText: "code formatting", key: 'f',
styleCSS: 'font-family: "Courier New", Courier, monospace;', multiline: true,
options: { exclusiveline: false,
prefix: '[code]', prefix: '[code]',
suffix: '[/code]', suffix: '[/code]'
multiline: true
},
edit: function (box, options) {
wrapSelection(box, options);
},
shortcutKey: 'd'
}, },
strike: { strike: {
displayText: 'strike', text: 'Strike',
altText: 'strikethrough', key: 'd',
styleCSS: 'text-decoration: line-through;', multiline:false,
options: { exclusiveline:false,
prefix: '~~', prefix:'~~',
suffix: '~~' suffix:'~~'
},
edit: function (box, options) {
wrapSelection(box, options);
}
}, },
heading: { heading: {
displayText: 'heading', text: 'Heading',
altText: 'redtext', key: 'r',
styleCSS: 'color: #AF0A0F; font-weight: bold;', multiline:false,
options: { exclusiveline:true,
prefix: '==', prefix:'==',
suffix: '==', suffix:'=='
exclusiveLine: true
},
edit: function (box, options) {
wrapSelection(box, options);
}
} }
}; };
var key, name, altText, ele; self.toolbar_wrap = function(node) {
var strBuilder = []; if (!localStorage.formatText_enable || localStorage.formatText_enable == 'false') return;
var subStr = ''; var parent = $(node).parents('form[name="post"]');
var styleRules = ''; self.wrap(parent.find('#body')[0],'textarea[name="body"]', parent.find('.format-text > select')[0].value, false);
};
//not exactly mine
var wrapSelection = function (box, options) { self.wrap = function(ref, target, option, expandedwrap) {
if (box == null) { if (!localStorage.formatText_enable || localStorage.formatText_enable == 'false') return;
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; target = res;
var suffix = options.suffix;
var multiline = options.multiline || false;
var exclusiveLine = options.exclusiveLine || false;
//record scroll top to restore it later. //record scroll top to restore it later.
var scrollTop = box.scrollTop; var scrollTop = ref.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"]; //We will restore the selection later, so record the current selection
var trailingSpace = ""; var selectionStart = ref.selectionStart;
var cursor = selectedText.length - 1; var selectionEnd = ref.selectionEnd;
//remove trailing space var text = ref.value;
while (cursor > 0 && selectedText[cursor] === " ") { var before = text.substring(0, selectionStart);
trailingSpace += " "; var selected = text.substring(selectionStart, selectionEnd);
cursor--; var after = text.substring(selectionEnd);
} var whiteSpace = [" ","\t"];
selectedText = selectedText.substring(0, cursor + 1); var breakSpace = ["\r","\n"];
var cursor;
if (!multiline)
selectedText = selectedText.replace(/(\r|\n|\r\n)/g, suffix +"$1"+ prefix); // 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);
if (exclusiveLine) {
// 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 // buffer the begining of the selection until a linebreak
cursor = beforeSelection.length -1; cursor = before.length -1;
while (cursor >= 0 && breakSpace.indexOf(beforeSelection.charAt(cursor)) == -1) { while (cursor >= 0 && breakSpace.indexOf(before.charAt(cursor)) == -1) {
cursor--; cursor--;
} }
selectedText = beforeSelection.substring(cursor +1) + selectedText; selected = before.substring(cursor +1) + selected;
beforeSelection = beforeSelection.substring(0, cursor +1); before = before.substring(0, cursor +1);
// buffer the end of the selection until a linebreak // buffer the end of the selection until a linebreak
cursor = 0; cursor = 0;
while (cursor < afterSelection.length && breakSpace.indexOf(afterSelection.charAt(cursor)) == -1) { while (cursor < after.length && breakSpace.indexOf(after.charAt(cursor)) == -1) {
cursor++; cursor++;
} }
selectedText += afterSelection.substring(0, cursor); selected += after.substring(0, cursor);
afterSelection = afterSelection.substring(cursor); after = after.substring(cursor);
} }
box.value = beforeSelection + prefix + selectedText + suffix + trailingSpace + afterSelection; // set values
var res = before + settings.prefix + selected + settings.suffix + after;
box.selectionEnd = beforeSelection.length + prefix.length + selectedText.length; $(target).val(res);
// restore the selection area and scroll of the reference
ref.selectionEnd = before.length + settings.prefix.length + selected.length;
if (selectionStart === selectionEnd) { if (selectionStart === selectionEnd) {
box.selectionStart = box.selectionEnd; ref.selectionStart = ref.selectionEnd;
} else { } 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 //add hint if key exists
*/ if (rules[index].key) {
for (ele in formats) { name += ' (CTRL + '+ rules[index].key.toUpperCase() +')';
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 +'"'; options += '<option value="'+ index +'">'+ name +'</option>';
} }
$('[name="body"]').before('<div class="format-text"><a href="javascript:;" onclick="formatText.toolbar_wrap(this);">Wrap</a><select>'+ options +'</select></div>');
subStr = '<a href="javascript:void(0)" '+ altText +' id="tf-'+ ele +'">'+ name +'</a>'; $('body').append('<style>#quick-reply .format-text>a{width:15%;display:inline-block;text-align:center;}#quick-reply .format-text>select{width:85%;};</style>');
strBuilder.push(subStr);
} else {
continue;
} }
} };
$( 'textarea[name="body"]' ).before( '<div class="tf-toolbar"></div>' ); self.add_rule = function(rule, index){
$( '.tf-toolbar' ).html( strBuilder.join(' | ') ); if (rule === undefined) rule = {
text: 'New Rule',
/* Sets the CSS style key: '',
*/ multiline:false,
styleRules = '\n/* generated by 8chan Formatting Tools */'+ exclusiveline:false,
'\n.tf-toolbar {padding: 0px 5px 1px 5px;}'+ prefix:'',
'\n.tf-toolbar :link {text-decoration: none;}'; suffix:''
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) { // generate an id for the rule
$( '.user-css' ).append( styleRules ); if (index === undefined) {
} else { var rules = JSON.parse(localStorage.formatText_rules);
$( 'body' ).append( '<style>'+ styleRules +'\n</style>' ); 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')){
/* Attach event listeners var html = $('<div class="format_rule" name="'+ index +'"></div>').html('\
*/ <input type="text" name="text" class="format_option" size="10" value=\"'+ rule.text.replace(/"/g, '&quot;') +'\">\
$( 'body' ).on( 'keydown', 'textarea[name="body"]', {formats: formats}, function (e) { <input type="checkbox" name="multiline" class="format_option" '+ (rule.multiline ? 'checked' : '') +'>\
//shortcuts <input type="checkbox" name="exclusiveline" class="format_option" '+ (rule.exclusiveline ? 'checked' : '') +'>\
if (e.ctrlKey) { <input type="text" name="prefix" class="format_option" size="8" value=\"'+ (rule.prefix ? rule.prefix.replace(/"/g, '&quot;') : '') +'\">\
var ch = String.fromCharCode(e.which).toLowerCase(); <input type="text" name="suffix" class="format_option" size="8" value=\"'+ (rule.suffix ? rule.suffix.replace(/"/g, '&quot;') : '') +'\">\
var box = e.target; <input type="text" name="key" class="format_option" size="2" maxlength="1" value=\"'+ rule.key +'\">\
var formats = e.data.formats; <input type="button" value="X" onclick="if(confirm(\'Do you wish to remove the '+ rule.text +' formatting rule?\'))$(this).parent().remove();">\
for (var ele in formats) { ');
if (formats.hasOwnProperty(ele) && (ch === formats[ele].shortcutKey)) {
formats[ele].edit(box, formats[ele].options); if ($('.format_rule').length > 0) {
e.preventDefault(); $('.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', '\
<fieldset>\
<legend>Formatting Options</legend>\
<label id="formatText_enable"><input type="checkbox" checked="checked" id="formatText_enable">' + _('Enable post formatting') + '</label>\
<label id="formatText_keybinds"><input type="checkbox" checked="checked" id="formatText_keybinds">' + _('Enable formatting keybinds') + '</label>\
<label id="formatText_toolbar"><input type="checkbox" checked="checked" id="formatText_toolbar">' + _('Show formatting toolbar') + '</label>\
</fieldset>\
');
} else {
var s1 = '#formatText_enable', s2 = '#formatText_keybinds', s3 = '#formatText_toolbar', e = 'click';
$('hr:first').before('<div id="formatText_enable" style="text-align:right"><a class="unimportant" href="javascript:void(0)">'+ _('Enable post formatting') +'</a></div>');
$('hr:first').before('<div id="formatText_keybinds" style="text-align:right"><a class="unimportant" href="javascript:void(0)">'+ _('Enable formatting keybinds') +'</a></div>');
$('hr:first').before('<div id="formatText_toolbar" style="text-align:right"><a class="unimportant" href="javascript:void(0)">'+ _('Show formatting toolbar') +'</a></div>');
}
// 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 {
localStorage.formatText_enable = 'false';
if (window.Options && Options.get_tab('general')) e.target.checked = false;
}
}); });
$( 'body' ).on( 'keydown', '#quick-reply textarea[name="body"]', {formats: formats}, function (e) {
//close quick reply when esc is prssed // setting for enableing formatting keybinds
if (e.which === 27) { $(s2).on(e, function(e) {
$( '.close-btn' ).trigger( 'click' ); 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;
} }
}); });
$( 'body' ).on( 'click', '.tf-toolbar a[id]', {formats: formats}, function (e) {
//toolbar buttons // setting for toolbar injection
var formats = e.data.formats; $(s3).on(e, function(e) {
var box = $(e.target).parent().next()[0]; if (!localStorage.formatText_toolbar || localStorage.formatText_toolbar == 'false') {
localStorage.formatText_toolbar = 'true';
for (var ele in formats) { if (window.Options && Options.get_tab('general')) e.target.checked = true;
if (formats.hasOwnProperty(ele) && (e.target.id === 'tf-' + ele)) { formatText.build_toolbars();
formats[ele].edit(box, formats[ele].options); } else {
} localStorage.formatText_toolbar = 'false';
if (window.Options && Options.get_tab('general')) e.target.checked = false;
$('.format-text').remove();
} }
}); });
// $( 'body' ).on( 'keydown', function (e) {
// if (e.which === 67 && // make sure the tab settings are switch properly at loadup
// e.target.nodeName !== 'INPUT' && //The C, the whole C, and nothing but the C if (window.Options && Options.get_tab('general')) {
// e.target.nodeName !== 'TEXTAREA' && if (localStorage.formatText_enable == 'true') $(s1)[0].checked = true;
// !(e.ctrlKey || e.altKey || e.shiftKey)) { else $(s1)[0].checked = false;
// document.location.href = '//'+ document.location.host +'/'+ board_name +'/catalog.html'; 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', '\
<style>\
.format_option{\
margin-right:5px;\
overflow:initial;\
font-size:15px;\
}\
.format_option[type="text"]{\
text-align:center;\
padding-bottom: 2px;\
padding-top: 2px;\
}\
.format_option:last-child{\
margin-right:0;\
}\
fieldset{\
margin-top:5px;\
}\
</style>\
');
// Data control row
Options.extend_tab('formatting', '\
<button onclick="formatText.add_rule();">Add Rule</button>\
<button onclick="formatText.save_rules();">Save Rules</button>\
<button onclick="formatText.reset_rules(false);">Revert</button>\
<button onclick="formatText.reset_rules(true);">Reset to Default</button>\
');
// Descriptor row
Options.extend_tab('formatting', '\
<span class="format_option" style="margin-left:25px;">Name</span>\
<span class="format_option" style="margin-left:45px;" title="Multi-line: Allow formatted area to contain linebreaks.">ML</span>\
<span class="format_option" style="margin-left:0px;" title="Exclusive-line: Require formatted area to start after and end before a linebreak.">EL</span>\
<span class="format_option" style="margin-left:25px;" title="Text injected at the start of a format area.">Prefix</span>\
<span class="format_option" style="margin-left:60px;" title="Text injected at the end of a format area.">Suffix</span>\
<span class="format_option" style="margin-left:40px;" title="Optional keybind value to allow keyboard shortcut access.">Key</span>\
');
// 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);
}
}
return self;
})(jQuery);
// run initial toolbar injection
formatText.build_toolbars();
//attach listeners to <body> 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');
}

2
js/options/user-css.js

@ -17,7 +17,7 @@ var textarea = $("<textarea></textarea>").css({
"font-size": 12, "font-size": 12,
position: "absolute", position: "absolute",
top: 35, bottom: 35, 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 left: 5, right: 5
}).appendTo(tab.content); }).appendTo(tab.content);
var submit = $("<input type='button' value='"+_("Update custom CSS")+"'>").css({ var submit = $("<input type='button' value='"+_("Update custom CSS")+"'>").css({

2
js/options/user-js.js

@ -17,7 +17,7 @@ var textarea = $("<textarea></textarea>").css({
"font-size": 12, "font-size": 12,
position: "absolute", position: "absolute",
top: 35, bottom: 35, 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 left: 5, right: 5
}).appendTo(tab.content); }).appendTo(tab.content);
var submit = $("<input type='button' value='"+_("Update custom Javascript")+"'>").css({ var submit = $("<input type='button' value='"+_("Update custom Javascript")+"'>").css({

Loading…
Cancel
Save