From e461c842537b5c48e6f99fa658236948b850276b Mon Sep 17 00:00:00 2001
From: Robin Ward " + content + "
";
- },
- "li": function(_, content) {
- return "" + content + "
";
- },
- "code": function(_, content) {
- return "" + content + "
";
- },
- "url": function(_, url) {
- return "" + url + "";
- },
- "email": function(_, address) {
- return "" + address + "";
- },
- "img": function(_, src) {
- return "";
- }
- },
- withArgs: {
- "url": function(_, href, title) {
- return "" + title + "";
- },
- "email": function(_, address, title) {
- return "" + title + "";
- },
- "color": function(_, color, content) {
- if (!/^(\#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?)|(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|purple|red|silver|teal|white|yellow)$/.test(color)) {
- return content;
- }
- return "" + content + "";
+ QUOTE_REGEXP: /\[quote=([^\]]*)\]([\s\S]*?)\[\/quote\]/im,
+
+ // Define our replacers
+ replacers: {
+ base: {
+ withoutArgs: {
+ "ol": function(_, content) { return "" + content + "
"; },
+ "li": function(_, content) { return "" + content + "
"; },
+ "code": function(_, content) { return "" + content + "
"; },
+ "url": function(_, url) { return "" + url + ""; },
+ "email": function(_, address) { return "" + address + ""; },
+ "img": function(_, src) { return ""; }
+ },
+ withArgs: {
+ "url": function(_, href, title) { return "" + title + ""; },
+ "email": function(_, address, title) { return "" + title + ""; },
+ "color": function(_, color, content) {
+ if (!/^(\#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?)|(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|purple|red|silver|teal|white|yellow)$/.test(color)) {
+ return content;
}
+ return "" + content + "";
+ }
+ }
+ },
+
+ // For HTML emails
+ email: {
+ withoutArgs: {
+ "b": function(_, content) { return "" + content + ""; },
+ "i": function(_, content) { return "" + content + ""; },
+ "u": function(_, content) { return "" + content + ""; },
+ "s": function(_, content) { return "" + content + ""; },
+ "spoiler": function(_, content) { return "" + content + ""; }
+ },
+ withArgs: {
+ "size": function(_, size, content) {
+ return "" + content + "";
+ }
+ }
+ },
+
+ // For sane environments that support CSS
+ "default": {
+ withoutArgs: {
+ "b": function(_, content) { return "" + content + ""; },
+ "i": function(_, content) { return "" + content + ""; },
+ "u": function(_, content) { return "" + content + ""; },
+ "s": function(_, content) { return "" + content + ""; },
+ "spoiler": function(_, content) { return "" + content + "";
}
},
- /* For HTML emails
- */
-
- email: {
- withoutArgs: {
- "b": function(_, content) {
- return "" + content + "";
- },
- "i": function(_, content) {
- return "" + content + "";
- },
- "u": function(_, content) {
- return "" + content + "";
- },
- "s": function(_, content) {
- return "" + content + "";
- },
- "spoiler": function(_, content) {
- return "" + content + "";
- }
- },
- withArgs: {
- "size": function(_, size, content) {
- return "" + content + "";
- }
- }
- },
- /* For sane environments that support CSS
- */
-
- "default": {
- withoutArgs: {
- "b": function(_, content) {
- return "" + content + "";
- },
- "i": function(_, content) {
- return "" + content + "";
- },
- "u": function(_, content) {
- return "" + content + "";
- },
- "s": function(_, content) {
- return "" + content + "";
- },
- "spoiler": function(_, content) {
- return "" + content + "";
- }
- },
- withArgs: {
- "size": function(_, size, content) {
- return "" + content + "";
- }
+ withArgs: {
+ "size": function(_, size, content) {
+ return "" + content + "";
}
}
- },
-
- /* Apply a particular set of replacers */
- apply: function(text, environment) {
- var replacer;
- replacer = Discourse.BBCode.parsedReplacers()[environment];
-
- replacer.forEach(function(r) {
- text = text.replace(r.regexp, r.fn);
- });
- return text;
- },
-
- parsedReplacers: function() {
- var result;
- if (this.parsed) {
- return this.parsed;
- }
- result = {};
- Object.keys(Discourse.BBCode.replacers, function(name, rules) {
- var parsed;
- parsed = result[name] = [];
- Object.keys(Object.merge(Discourse.BBCode.replacers.base.withoutArgs, rules.withoutArgs), function(tag, val) {
- return parsed.push({
- regexp: new RegExp("\\[" + tag + "\\]([\\s\\S]*?)\\[\\/" + tag + "\\]", "igm"),
- fn: val
- });
- });
- return Object.keys(Object.merge(Discourse.BBCode.replacers.base.withArgs, rules.withArgs), function(tag, val) {
- return parsed.push({
- regexp: new RegExp("\\[" + tag + "=?(.+?)\\]([\\s\\S]*?)\\[\\/" + tag + "\\]", "igm"),
- fn: val
- });
- });
- });
- this.parsed = result;
- return this.parsed;
- },
-
- buildQuoteBBCode: function(post, contents) {
- var contents_hashed, result, sansQuotes, stripped, stripped_hashed, tmp;
- if (!contents) contents = "";
-
- sansQuotes = contents.replace(this.QUOTE_REGEXP, '').trim();
- if (sansQuotes.length === 0) return "";
-
- /* Strip the HTML from cooked */
- tmp = document.createElement('div');
- tmp.innerHTML = post.get('cooked');
- stripped = tmp.textContent || tmp.innerText;
-
- /*
- Let's remove any non alphanumeric characters as a kind of hash. Yes it's
- not accurate but it should work almost every time we need it to. It would be unlikely
- that the user would quote another post that matches in exactly this way.
- */
- stripped_hashed = stripped.replace(/[^a-zA-Z0-9]/g, '');
- contents_hashed = contents.replace(/[^a-zA-Z0-9]/g, '');
- result = "[quote=\"" + (post.get('username')) + ", post:" + (post.get('post_number')) + ", topic:" + (post.get('topic_id'));
-
- /* If the quote is the full message, attribute it as such */
- if (stripped_hashed === contents_hashed) {
- result += ", full:true";
- }
- result += "\"]\n" + sansQuotes + "\n[/quote]\n\n";
- return result;
- },
-
- formatQuote: function(text, opts) {
-
- /* Replace quotes with appropriate markup */
- var args, matches, params, paramsSplit, paramsString, templateName, username;
- while (matches = this.QUOTE_REGEXP.exec(text)) {
- paramsString = matches[1];
- paramsString = paramsString.replace(/\"/g, '');
- paramsSplit = paramsString.split(/\, */);
- params = [];
- paramsSplit.each(function(p, i) {
- var assignment;
- if (i > 0) {
- assignment = p.split(':');
- if (assignment[0] && assignment[1]) {
- return params.push({
- key: assignment[0],
- value: assignment[1].trim()
- });
- }
- }
- });
- username = paramsSplit[0];
-
- /* Arguments for formatting */
- args = {
- username: username,
- params: params,
- quote: matches[2].trim(),
- avatarImg: opts.lookupAvatar ? opts.lookupAvatar(username) : void 0
- };
- templateName = 'quote';
- if (opts && opts.environment) {
- templateName = "quote_" + opts.environment;
- }
- text = text.replace(matches[0], "
"); - } - return text; - }, - format: function(text, opts) { - var environment; - if (opts && opts.environment) environment = opts.environment; - if (!environment) environment = 'default'; - - text = Discourse.BBCode.apply(text, environment); - // Add quotes - text = Discourse.BBCode.formatQuote(text, opts); - return text; } - }; + }, -}).call(this); + // Apply a particular set of replacers + apply: function(text, environment) { + var replacer; + replacer = Discourse.BBCode.parsedReplacers()[environment]; + + replacer.forEach(function(r) { + text = text.replace(r.regexp, r.fn); + }); + return text; + }, + + parsedReplacers: function() { + var result; + if (this.parsed) return this.parsed; + + result = {}; + Object.keys(Discourse.BBCode.replacers, function(name, rules) { + var parsed; + parsed = result[name] = []; + Object.keys(Object.merge(Discourse.BBCode.replacers.base.withoutArgs, rules.withoutArgs), function(tag, val) { + return parsed.push({ + regexp: new RegExp("\\[" + tag + "\\]([\\s\\S]*?)\\[\\/" + tag + "\\]", "igm"), + fn: val + }); + }); + return Object.keys(Object.merge(Discourse.BBCode.replacers.base.withArgs, rules.withArgs), function(tag, val) { + return parsed.push({ + regexp: new RegExp("\\[" + tag + "=?(.+?)\\]([\\s\\S]*?)\\[\\/" + tag + "\\]", "igm"), + fn: val + }); + }); + }); + this.parsed = result; + return this.parsed; + }, + + buildQuoteBBCode: function(post, contents) { + var contents_hashed, result, sansQuotes, stripped, stripped_hashed, tmp; + if (!contents) contents = ""; + + sansQuotes = contents.replace(this.QUOTE_REGEXP, '').trim(); + if (sansQuotes.length === 0) return ""; + + /* Strip the HTML from cooked */ + tmp = document.createElement('div'); + tmp.innerHTML = post.get('cooked'); + stripped = tmp.textContent || tmp.innerText; + + /* + Let's remove any non alphanumeric characters as a kind of hash. Yes it's + not accurate but it should work almost every time we need it to. It would be unlikely + that the user would quote another post that matches in exactly this way. + */ + stripped_hashed = stripped.replace(/[^a-zA-Z0-9]/g, ''); + contents_hashed = contents.replace(/[^a-zA-Z0-9]/g, ''); + result = "[quote=\"" + (post.get('username')) + ", post:" + (post.get('post_number')) + ", topic:" + (post.get('topic_id')); + + /* If the quote is the full message, attribute it as such */ + if (stripped_hashed === contents_hashed) { + result += ", full:true"; + } + result += "\"]\n" + sansQuotes + "\n[/quote]\n\n"; + return result; + }, + + formatQuote: function(text, opts) { + + /* Replace quotes with appropriate markup */ + var args, matches, params, paramsSplit, paramsString, templateName, username; + while (matches = this.QUOTE_REGEXP.exec(text)) { + paramsString = matches[1]; + paramsString = paramsString.replace(/\"/g, ''); + paramsSplit = paramsString.split(/\, */); + params = []; + paramsSplit.each(function(p, i) { + var assignment; + if (i > 0) { + assignment = p.split(':'); + if (assignment[0] && assignment[1]) { + return params.push({ + key: assignment[0], + value: assignment[1].trim() + }); + } + } + }); + username = paramsSplit[0]; + + /* Arguments for formatting */ + args = { + username: username, + params: params, + quote: matches[2].trim(), + avatarImg: opts.lookupAvatar ? opts.lookupAvatar(username) : void 0 + }; + templateName = 'quote'; + if (opts && opts.environment) { + templateName = "quote_" + opts.environment; + } + text = text.replace(matches[0], "
" + HANDLEBARS_TEMPLATES[templateName](args) + ""); + } + return text; + }, + + /** + Format a text string using BBCode + + @method format + @param {String} text The text we want to format + @param {Object} opts Rendering options + **/ + format: function(text, opts) { + var environment; + if (opts && opts.environment) environment = opts.environment; + if (!environment) environment = 'default'; + + text = Discourse.BBCode.apply(text, environment); + // Add quotes + text = Discourse.BBCode.formatQuote(text, opts); + return text; + } +}; \ No newline at end of file diff --git a/app/assets/javascripts/discourse/components/caret_position.js b/app/assets/javascripts/discourse/components/caret_position.js index 5081d1f30..4bdb9b316 100644 --- a/app/assets/javascripts/discourse/components/caret_position.js +++ b/app/assets/javascripts/discourse/components/caret_position.js @@ -1,135 +1,134 @@ +// http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea +var clone, getCaret; +getCaret = function(el) { + var r, rc, re; + if (el.selectionStart) { + return el.selectionStart; + } else if (document.selection) { + el.focus(); + r = document.selection.createRange(); + if (!r) return 0; + re = el.createTextRange(); + rc = re.duplicate(); + re.moveToBookmark(r.getBookmark()); + rc.setEndPoint("EndToStart", re); + return rc.text.length; + } + return 0; +}; -/* caret position in textarea ... very hacky ... sorry -*/ +clone = null; +/** + This is a jQuery plugin to retrieve the caret position in a textarea -(function() { + @module $.fn.caretPosition +**/ +$.fn.caretPosition = function(options) { + var after, before, getStyles, guard, html, important, insertSpaceAfterBefore, letter, makeCursor, p, pPos, pos, span, styles, textarea, val; + if (clone) { + clone.remove(); + } + span = $("#pos span"); + textarea = $(this); - (function($) { - /* http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea - */ + getStyles = function(el, prop) { + if (el.currentStyle) { + return el.currentStyle; + } else { + return document.defaultView.getComputedStyle(el, ""); + } + }; - var clone, getCaret; - getCaret = function(el) { - var r, rc, re; - if (el.selectionStart) { - return el.selectionStart; - } else if (document.selection) { - el.focus(); - r = document.selection.createRange(); - if (!r) return 0; - re = el.createTextRange(); - rc = re.duplicate(); - re.moveToBookmark(r.getBookmark()); - rc.setEndPoint("EndToStart", re); - return rc.text.length; - } - return 0; - }; - clone = null; - $.fn.caretPosition = function(options) { - var after, before, getStyles, guard, html, important, insertSpaceAfterBefore, letter, makeCursor, p, pPos, pos, span, styles, textarea, val; - if (clone) { - clone.remove(); - } - span = jQuery("#pos span"); - textarea = jQuery(this); - getStyles = function(el, prop) { - if (el.currentStyle) { - return el.currentStyle; - } else { - return document.defaultView.getComputedStyle(el, ""); - } - }; - styles = getStyles(textarea[0]); - clone = jQuery("
" + escaped + "
";
- });
+ // github style fenced code
+ converter.hooks.chain("preConversion", function(text) {
+ return text.replace(/^`{3}(?:(.*$)\n)?([\s\S]*?)^`{3}/gm, function(wholeMatch, m1, m2) {
+ var escaped;
+ escaped = Handlebars.Utils.escapeExpression(m2);
+ return "" + escaped + "
";
});
+ });
+
+ converter.hooks.chain("postConversion", function(text) {
+ if (!text) return "";
+
+ // don't to mention voodoo in pres
+ text = text.replace(/([\s\S]*@[\s\S]*)<\/pre>/gi, function(wholeMatch, inner) { + return "" + (inner.replace(/@/g, '@')) + ""; + }); + + // Add @mentions of names + text = text.replace(/([\s\t>,:'|";\]])(@[A-Za-z0-9_-|\.]*[A-Za-z0-9_-|]+)(?=[\s\t<\!:|;',"\?\.])/g, function(x, pre, name) { + if (mentionLookup(name.substr(1))) { + return "" + pre + "" + name + ""; + } else { + return "" + pre + "" + name + ""; + } + }); + + // a primitive attempt at oneboxing, this regex gives me much eye sores + text = text.replace(/(
|
)[\s\n\r]*)(]*)>([^<]+<\/a>[\s\n\r]*(?=<\/p>|
))/gi, function() {
+ // We don't onebox items in a list
+ var onebox, url;
+ if (arguments[1]) {
+ return arguments[0];
+ }
+ url = arguments[5];
+ if (Discourse && Discourse.Onebox) {
+ onebox = Discourse.Onebox.lookupCache(url);
+ }
+ if (onebox && !onebox.isBlank()) {
+ return arguments[2] + onebox;
+ } else {
+ return arguments[2] + arguments[4] + " class=\"onebox\" target=\"_blank\">" + arguments[6];
+ }
+ });
+
+ return(text);
+ });
+
+ converter.hooks.chain("postConversion", function(text) {
+ return Discourse.BBCode.format(text, opts);
+ });
+
+ if (opts.sanitize) {
converter.hooks.chain("postConversion", function(text) {
- if (!text) {
+ if (!window.sanitizeHtml) {
return "";
}
- /* don't to mention voodoo in pres
- */
-
- text = text.replace(/([\s\S]*@[\s\S]*)<\/pre>/gi, function(wholeMatch, inner) {
- return "
" + (inner.replace(/@/g, '@')) + "
";
- });
- /* Add @mentions of names
- */
-
- text = text.replace(/([\s\t>,:'|";\]])(@[A-Za-z0-9_-|\.]*[A-Za-z0-9_-|]+)(?=[\s\t<\!:|;',"\?\.])/g, function(x, pre, name) {
- if (mentionLookup(name.substr(1))) {
- return "" + pre + "" + name + "";
- } else {
- return "" + pre + "" + name + "";
- }
- });
- /* a primitive attempt at oneboxing, this regex gives me much eye sores
- */
-
- text = text.replace(/(