jquery.sceditor.bbcode.js 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429
  1. /**
  2. * SCEditor BBCode Plugin v1.3.4
  3. * http://www.samclarke.com/2011/07/sceditor/
  4. *
  5. * Copyright (C) 2011-2012, Sam Clarke (samclarke.com)
  6. *
  7. * SCEditor is licensed under the MIT license:
  8. * http://www.opensource.org/licenses/mit-license.php
  9. */
  10. // ==ClosureCompiler==
  11. // @output_file_name jquery.sceditor.min.js
  12. // @compilation_level SIMPLE_OPTIMIZATIONS
  13. // ==/ClosureCompiler==
  14. /*jshint smarttabs: true, jquery: true, eqnull:true, curly: false */
  15. (function($) {
  16. 'use strict';
  17. $.sceditorBBCodePlugin = function(element, options) {
  18. var base = this;
  19. /**
  20. * Private methods
  21. * @private
  22. */
  23. var init,
  24. buildBbcodeCache,
  25. handleStyles,
  26. handleTags,
  27. formatString,
  28. getStyle,
  29. wrapInDivs,
  30. mergeTextModeCommands;
  31. base.bbcodes = $.sceditorBBCodePlugin.bbcodes;
  32. /**
  33. * cache of all the tags pointing to their bbcodes to enable
  34. * faster lookup of which bbcode a tag should have
  35. * @private
  36. */
  37. var tagsToBbcodes = {};
  38. /**
  39. * Same as tagsToBbcodes but instead of HTML tags it's styles
  40. * @private
  41. */
  42. var stylesToBbcodes = {};
  43. /**
  44. * Allowed children of specific HTML tags. Empty array if no
  45. * children other than text nodes are allowed
  46. * @private
  47. */
  48. var validChildren = {
  49. list: ['li'],
  50. table: ['tr'],
  51. tr: ['td', 'th'],
  52. code: ['br', 'p', 'div'],
  53. youtube: []
  54. };
  55. /**
  56. * Initializer
  57. */
  58. init = function() {
  59. $.data(element, "sceditorbbcode", base);
  60. base.options = $.extend({}, $.sceditor.defaultOptions, options);
  61. // build the BBCode cache
  62. buildBbcodeCache();
  63. (new $.sceditor(element,
  64. $.extend({}, base.options, {
  65. getHtmlHandler: base.getHtmlHandler,
  66. getTextHandler: base.getTextHandler,
  67. commands: mergeTextModeCommands()
  68. })
  69. ));
  70. };
  71. mergeTextModeCommands = function() {
  72. // TODO: use selection as display text if is one.
  73. // TODO: add translations of the prompts
  74. var merge = {
  75. bold: { txtExec: ["[b]", "[/b]"] },
  76. italic: { txtExec: ["[i]", "[/i]"] },
  77. underline: { txtExec: ["[u]", "[/u]"] },
  78. strike: { txtExec: ["[s]", "[/s]"] },
  79. subscript: { txtExec: ["[sub]", "[/sub]"] },
  80. superscript: { txtExec: ["[sup]", "[/sup]"] },
  81. left: { txtExec: ["[left]", "[/left]"] },
  82. center: { txtExec: ["[center]", "[/center]"] },
  83. right: { txtExec: ["[right]", "[/right]"] },
  84. justify: { txtExec: ["[justify]", "[/justify]"] },
  85. bulletlist: { txtExec: ["[list]\n[li]", "[/li]\n[li][/li]\n[/list]"] },
  86. orderedlist: { txtExec: ["[list type=decimal]\n[li]", "[/li]\n[li][/li]\n[/list]"] },
  87. ftp: { txtExec: ["[ftp]", "[/ftp]"] },
  88. tt: { txtExec: ["[tt]", "[/tt]"] },
  89. glow: { txtExec: ["[glow=red,2,300]", "[/glow]"] },
  90. shadow: { txtExec: ["[shadow=red,left]", "[/shadow]"] },
  91. font: {
  92. txtExec: function (caller) {
  93. var editor = this,
  94. fonts = editor.options.fonts.split(","),
  95. content = $("<div />"),
  96. clickFunc = function (e) {
  97. editor.textEditorInsertText("[font=" + $(this).data('sceditor-font') + "]", "[/font]");
  98. editor.closeDropDown(true);
  99. e.preventDefault();
  100. };
  101. for (var i=0; i < fonts.length; i++) {
  102. content.append(
  103. $('<a class="sceditor-font-option" href="#"><font face="' + fonts[i] + '">' + fonts[i] + '</font></a>')
  104. .data('sceditor-font', fonts[i])
  105. .click(clickFunc));
  106. }
  107. editor.createDropDown(caller, "font-picker", content);
  108. },
  109. tooltip: "Font Name"
  110. },
  111. size: {
  112. txtExec: function (caller) {
  113. var sizes = [0, 8, 10, 12, 14, 18, 24, 36];
  114. var editor = this,
  115. content = $("<div />"),
  116. clickFunc = function (e) {
  117. editor.textEditorInsertText("[size=" + sizes[$(this).data('sceditor-fontsize')] + "pt]", "[/font]");
  118. editor.closeDropDown(true);
  119. e.preventDefault();
  120. };
  121. for (var i=1; i<= 7; i++) {
  122. content.append(
  123. $('<a class="sceditor-fontsize-option" style="line-height:' + sizes[i] + 'pt" href="#"><font size="' + i + '">' + sizes[i] + 'pt</font></a>')
  124. .data('sceditor-fontsize', i)
  125. .click(clickFunc));
  126. }
  127. editor.createDropDown(caller, "fontsize-picker", content);
  128. },
  129. tooltip: "Font Size"
  130. },
  131. color: {
  132. txtExec: function (caller) {
  133. var editor = this,
  134. genColor = {r: 255, g: 255, b: 255},
  135. content = $("<div />"),
  136. colorColumns = this.options.colors?this.options.colors.split("|"):new Array(21),
  137. // IE is slow at string concation so use an array
  138. html = [],
  139. htmlIndex = 0;
  140. for (var i=0; i < colorColumns.length; ++i) {
  141. var colors = (typeof colorColumns[i] !== "undefined")?colorColumns[i].split(","):new Array(21);
  142. html[htmlIndex++] = '<div class="sceditor-color-column">';
  143. for (var x=0; x < colors.length; ++x) {
  144. // use pre defined colour if can otherwise use the generated color
  145. var color = (typeof colors[x] !== "undefined")?colors[x]:"#" + genColor.r.toString(16) + genColor.g.toString(16) + genColor.b.toString(16);
  146. html[htmlIndex++] = '<a href="#" class="sceditor-color-option" style="background-color: '+color+'" data-color="'+color+'"></a>';
  147. // calculate the next generated color
  148. if(x%5===0)
  149. genColor = {r: genColor.r, g: genColor.g-51, b: 255};
  150. else
  151. genColor = {r: genColor.r, g: genColor.g, b: genColor.b-51};
  152. }
  153. html[htmlIndex++] = '</div>';
  154. // calculate the next generated color
  155. if(i%5===0)
  156. genColor = {r: genColor.r-51, g: 255, b: 255};
  157. else
  158. genColor = {r: genColor.r, g: 255, b: 255};
  159. }
  160. content.append(html.join(''))
  161. .find('a')
  162. .click(function (e) {
  163. editor.textEditorInsertText("[color=" + $(this).attr('data-color') + "]", "[/color]");
  164. editor.closeDropDown(true);
  165. e.preventDefault();
  166. });
  167. editor.createDropDown(caller, "color-picker", content);
  168. },
  169. tooltip: "Font Color"
  170. },
  171. table: { txtExec: ["[table]\n[tr]\n[td]", "[/td]\n[/tr]\n[/table]"] },
  172. horizontalrule: { txtExec: ["[hr]"] },
  173. code: { txtExec: ["[code]", "[/code]"] },
  174. image: { txtExec: function() {
  175. var url = prompt(this._("Enter the images URL:"));
  176. if(url)
  177. this.textEditorInsertText("[img]" + url + "[/img]");
  178. } },
  179. email: { txtExec: function() {
  180. var email = prompt(this._("Enter the e-mail address:"), "@"),
  181. text = prompt(this._("Enter the displayed text:"), email) || email;
  182. if(email)
  183. this.textEditorInsertText("[email=" + email + "]" + text + "[/email]");
  184. } },
  185. link: { txtExec: function() {
  186. var url = prompt(this._("Enter the links URL:"), "http://"),
  187. text = prompt(this._("Enter the displayed text:"), url) || url;
  188. if(url)
  189. this.textEditorInsertText("[url=" + url + "]" + text + "[/url]");
  190. } },
  191. quote: { txtExec: ["[quote]", "[/quote]"] },
  192. youtube: { txtExec: function() {
  193. var url = prompt(this._("Enter the YouTube video URL or ID:"));
  194. if(url)
  195. {
  196. if(url.indexOf("://") > -1)
  197. url = url.replace(/^[^v]+v.(.{11}).*/,"$1");
  198. this.textEditorInsertText("[youtube]" + url + "[/youtube]");
  199. }
  200. } }
  201. };
  202. return $.extend(true, {}, merge, $.sceditor.commands);
  203. };
  204. /**
  205. * Populates tagsToBbcodes and stylesToBbcodes to enable faster lookups
  206. *
  207. * @private
  208. */
  209. buildBbcodeCache = function() {
  210. $.each(base.bbcodes, function(bbcode, info) {
  211. if(typeof base.bbcodes[bbcode].tags !== "undefined")
  212. $.each(base.bbcodes[bbcode].tags, function(tag, values) {
  213. var isBlock = !!base.bbcodes[bbcode].isBlock;
  214. tagsToBbcodes[tag] = (tagsToBbcodes[tag] || {});
  215. tagsToBbcodes[tag][isBlock] = (tagsToBbcodes[tag][isBlock] || {});
  216. tagsToBbcodes[tag][isBlock][bbcode] = values;
  217. });
  218. if(typeof base.bbcodes[bbcode].styles !== "undefined")
  219. $.each(base.bbcodes[bbcode].styles, function(style, values) {
  220. var isBlock = !!base.bbcodes[bbcode].isBlock;
  221. stylesToBbcodes[isBlock] = (stylesToBbcodes[isBlock] || {});
  222. stylesToBbcodes[isBlock][style] = (stylesToBbcodes[isBlock][style] || {});
  223. stylesToBbcodes[isBlock][style][bbcode] = values;
  224. });
  225. });
  226. };
  227. getStyle = function(element, property) {
  228. var name = $.camelCase(property),
  229. $elm;
  230. // add exception for align
  231. if("text-align" === property)
  232. {
  233. $elm = $(element);
  234. if($elm.parent().css(property) !== $elm.css(property) &&
  235. $elm.css('display') === "block" && !$elm.is('hr') && !$elm.is('th'))
  236. return $elm.css(property);
  237. }
  238. if(element.style)
  239. return element.style[name];
  240. return null;
  241. };
  242. /**
  243. * Checks if any bbcode styles match the elements styles
  244. *
  245. * @private
  246. * @return string Content with any matching bbcode tags wrapped around it.
  247. */
  248. handleStyles = function(element, content, blockLevel) {
  249. var elementPropVal,
  250. tag = element[0].nodeName.toLowerCase();
  251. // convert blockLevel to boolean
  252. blockLevel = !!blockLevel;
  253. if(!stylesToBbcodes[blockLevel])
  254. return content;
  255. $.each(stylesToBbcodes[blockLevel], function(property, bbcodes) {
  256. elementPropVal = getStyle(element[0], property);
  257. if(elementPropVal == null || elementPropVal === "")
  258. return;
  259. // if the parent has the same style use that instead of this one
  260. // so you dont end up with [i]parent[i]child[/i][/i]
  261. if(getStyle(element.parent()[0], property) === elementPropVal)
  262. return;
  263. $.each(bbcodes, function(bbcode, values) {
  264. if((element[0].childNodes.length === 0 || element[0].childNodes[0].nodeName.toLowerCase() === "br") &&
  265. !base.bbcodes[bbcode].allowsEmpty)
  266. return;
  267. if(values === null || $.inArray(elementPropVal.toString(), values) > -1) {
  268. if($.isFunction(base.bbcodes[bbcode].format))
  269. content = base.bbcodes[bbcode].format.call(base, element, content);
  270. else
  271. content = formatString(base.bbcodes[bbcode].format, content);
  272. }
  273. });
  274. });
  275. return content;
  276. };
  277. /**
  278. * Handles a HTML tag and finds any matching bbcodes
  279. *
  280. * @private
  281. * @param jQuery element element The element to convert
  282. * @param string content The Tags text content
  283. * @param bool blockLevel If to convert block level tags
  284. * @return string Content with any matching bbcode tags wrapped around it.
  285. */
  286. handleTags = function(element, content, blockLevel) {
  287. var tag = element[0].nodeName.toLowerCase();
  288. // convert blockLevel to boolean
  289. blockLevel = !!blockLevel;
  290. if(tagsToBbcodes[tag] && tagsToBbcodes[tag][blockLevel]) {
  291. // loop all bbcodes for this tag
  292. $.each(tagsToBbcodes[tag][blockLevel], function(bbcode, bbcodeAttribs) {
  293. if(!base.bbcodes[bbcode].allowsEmpty &&
  294. (element[0].childNodes.length === 0 || (element[0].childNodes[0].nodeName.toLowerCase() === "br" && element[0].childNodes.length === 1)) )
  295. return;
  296. // if the bbcode requires any attributes then check this has
  297. // all needed
  298. if(bbcodeAttribs !== null) {
  299. var runBbcode = false;
  300. // loop all the bbcode attribs
  301. $.each(bbcodeAttribs, function(attrib, values)
  302. {
  303. // check if has the bbcodes attrib
  304. if(element.attr(attrib) == null)
  305. return;
  306. // if the element has the bbcodes attribute and the bbcode attribute
  307. // has values check one of the values matches
  308. if(values !== null && $.inArray(element.attr(attrib), values) < 0)
  309. return;
  310. // break this loop as we have matched this bbcode
  311. runBbcode = true;
  312. return false;
  313. });
  314. if(!runBbcode)
  315. return;
  316. }
  317. if($.isFunction(base.bbcodes[bbcode].format))
  318. content = base.bbcodes[bbcode].format.call(base, element, content);
  319. else
  320. content = formatString(base.bbcodes[bbcode].format, content);
  321. });
  322. }
  323. // add newline after paragraph elements p and div (WebKit uses divs) and br tags
  324. if(blockLevel && /^(br|div|p)$/.test(tag))
  325. {
  326. var parentChildren = element[0].parentNode.childNodes;
  327. // if it's a <p><br /></p> the paragraph will put the newline so skip the br
  328. if(!("br" === tag && parentChildren.length === 1) &&
  329. !("br" === tag && parentChildren[parentChildren.length-1] === element[0])) {
  330. content += "\n";
  331. }
  332. // needed for browsers that enter textnode then when return is pressed put the rest in a div, i.e.:
  333. // text<div>line 2</div>
  334. if("br" !== tag && !$.sceditor.dom.isInline(element.parent()[0]) && element[0].previousSibling &&
  335. element[0].previousSibling.nodeType === 3) {
  336. content = "\n" + content;
  337. }
  338. }
  339. return content;
  340. };
  341. /**
  342. * Formats a string in the format
  343. * {0}, {1}, {2}, ect. with the params provided
  344. * @private
  345. * @return string
  346. */
  347. formatString = function() {
  348. var args = arguments;
  349. return args[0].replace(/\{(\d+)\}/g, function(str, p1) {
  350. return typeof args[p1-0+1] !== "undefined"?
  351. args[p1-0+1] :
  352. '{' + p1 + '}';
  353. });
  354. };
  355. /**
  356. * Removes any leading or trailing quotes ('")
  357. *
  358. * @return string
  359. */
  360. base.stripQuotes = function(str) {
  361. return str.replace(/^["']+/, "").replace(/["']+$/, "");
  362. };
  363. /**
  364. * Converts HTML to BBCode
  365. * @param string html Html string, this function ignores this, it works off domBody
  366. * @param HtmlElement domBody Editors dom body object to convert
  367. * @return string BBCode which has been converted from HTML
  368. */
  369. base.getHtmlHandler = function(html, domBody) {
  370. $.sceditor.dom.removeWhiteSpace(domBody[0]);
  371. return $.trim(base.elementToBbcode(domBody));
  372. };
  373. /**
  374. * Converts a HTML dom element to BBCode starting from
  375. * the innermost element and working backwards
  376. *
  377. * @private
  378. * @param HtmlElement element The element to convert to BBCode
  379. * @param array vChildren Valid child tags allowed
  380. * @return string BBCode
  381. */
  382. base.elementToBbcode = function($element) {
  383. return (function toBBCode(node, vChildren) {
  384. var ret = '';
  385. $.sceditor.dom.traverse(node, function(node) {
  386. var $node = $(node),
  387. curTag = '',
  388. tag = node.nodeName.toLowerCase(),
  389. vChild = validChildren[tag],
  390. isValidChild = true;
  391. if(typeof vChildren === 'object')
  392. {
  393. isValidChild = $.inArray(tag, vChildren) > -1;
  394. // if this tag is one of the parents allowed children
  395. // then set this tags allowed children to whatever it allows,
  396. // otherwise set to what the parent allows
  397. if(!isValidChild)
  398. vChild = vChildren;
  399. }
  400. // 3 is text element
  401. if(node.nodeType !== 3)
  402. {
  403. // skip ignored elments
  404. if($node.hasClass("sceditor-ignore"))
  405. return;
  406. // don't loop inside iframes
  407. if(tag !== 'iframe')
  408. curTag = toBBCode(node, vChild);
  409. if(isValidChild)
  410. {
  411. // code tags should skip most styles
  412. if(!$node.is('code'))
  413. {
  414. // handle inline bbcodes
  415. curTag = handleStyles($node, curTag);
  416. curTag = handleTags($node, curTag);
  417. // handle blocklevel bbcodes
  418. curTag = handleStyles($node, curTag, true);
  419. }
  420. ret += handleTags($node, curTag, true);
  421. }
  422. else
  423. ret += curTag;
  424. }
  425. else if(node.wholeText && (!node.previousSibling || node.previousSibling.nodeType !== 3))
  426. {
  427. if($(node).parents('code').length === 0)
  428. ret += node.wholeText.replace(/ +/g, " ");
  429. else
  430. ret += node.wholeText;
  431. }
  432. else if(!node.wholeText)
  433. ret += node.nodeValue;
  434. }, false, true);
  435. return ret;
  436. }($element.get(0)));
  437. };
  438. /**
  439. * Converts BBCode to HTML
  440. *
  441. * @param {String} text
  442. * @param {Bool} isPaste
  443. * @return {String} HTML
  444. */
  445. base.getTextHandler = function(text, isPaste) {
  446. var oldText, replaceBBCodeFunc,
  447. bbcodeRegex = /\[([^\[\s=]*?)(?:([\s=][^\[]*?))?\]((?:[\s\S(?!=\[\\\1)](?!\[\1))*?)\[\/(\1)\]/g,
  448. atribsRegex = /(\S+)=((?:(?:(["'])(?:\\\3|[^\3])*?\3))|(?:[^'"\s]+))/g;
  449. replaceBBCodeFunc = function(str, bbcode, attrs, content)
  450. {
  451. var attrsMap = {},
  452. matches;
  453. if(attrs)
  454. {
  455. attrs = $.trim(attrs);
  456. // if only one attribute then remove the = from the start and strip any quotes
  457. if((attrs.charAt(0) === "=" && (attrs.split("=").length - 1) <= 1) || bbcode === 'url')
  458. attrsMap.defaultAttr = base.stripQuotes(attrs.substr(1));
  459. else
  460. {
  461. if(attrs.charAt(0) === "=")
  462. attrs = "defaultAttr" + attrs;
  463. if (typeof base.bbcodes[bbcode].attrs == 'function')
  464. {
  465. var declaredAttrs = base.bbcodes[bbcode].attrs();
  466. var attrArray = new Array;
  467. var compatArray = new Array;
  468. for (var i = 0; i < declaredAttrs.length; i++)
  469. {
  470. var attrPos = attrs.indexOf(declaredAttrs[i]);
  471. if (attrPos != -1)
  472. {
  473. attrArray[attrPos] = [declaredAttrs[i], attrPos + declaredAttrs[i].length + 1];
  474. }
  475. }
  476. for (var attrElem in attrArray)
  477. compatArray.push(attrArray[attrElem]);
  478. for (var i = 0; i < compatArray.length; i++)
  479. {
  480. if (typeof compatArray[i+1] != 'undefined')
  481. attrsMap[compatArray[i][0].toLowerCase()] = attrs.substr(compatArray[i][1], attrs.indexOf(compatArray[i+1][0]) - compatArray[i][1]).trim();
  482. else
  483. attrsMap[compatArray[i][0].toLowerCase()] = attrs.substr(compatArray[i][1], attrs.length);
  484. }
  485. }
  486. else
  487. while((matches = atribsRegex.exec(attrs)))
  488. attrsMap[matches[1].toLowerCase()] = base.stripQuotes(matches[2]);
  489. }
  490. }
  491. if(!base.bbcodes[bbcode])
  492. return str;
  493. if($.isFunction(base.bbcodes[bbcode].html))
  494. return base.bbcodes[bbcode].html.call(base, bbcode, attrsMap, content);
  495. else
  496. return formatString(base.bbcodes[bbcode].html, content);
  497. };
  498. text = text.replace(/&/g, "&amp;")
  499. .replace(/</g, "&lt;")
  500. .replace(/>/g, "&gt;")
  501. .replace(/\r/g, "")
  502. .replace(/(\[\/?(?:left|center|right|justify)\])\n/g, "$1")
  503. .replace(/\n/g, "<br />");
  504. while(text !== oldText)
  505. {
  506. oldText = text;
  507. text = text.replace(bbcodeRegex, replaceBBCodeFunc);
  508. }
  509. // As hr is the only bbcode not to have a start and end tag it's
  510. // just being replace here instead of adding support for it above.
  511. text = text.replace(/\[hr\]/gi, "<hr>");
  512. // replace multi-spaces which are not inside tags with a non-breaking space
  513. // to preserve them. Otherwise they will just be converted to 1!
  514. text = text.replace(/ {2}(?=([^<\>]*?<|[^<\>]*?$))/g, " &nbsp;");
  515. return wrapInDivs(text, isPaste);
  516. };
  517. /**
  518. * Wraps divs around inline HTML. Needed for IE
  519. *
  520. * @param string html
  521. * @return string HTML
  522. */
  523. wrapInDivs = function(html, excludeFirstLast)
  524. {
  525. var d = document,
  526. inlineFrag = d.createDocumentFragment(),
  527. outputDiv = d.createElement('div'),
  528. tmpDiv = d.createElement('div'),
  529. div, node, next, nodeName;
  530. $(tmpDiv).hide().appendTo(d.body);
  531. tmpDiv.innerHTML = html;
  532. node = tmpDiv.firstChild;
  533. while(node)
  534. {
  535. next = node.nextSibling;
  536. nodeName = node.nodeName.toLowerCase();
  537. if((node.nodeType === 1 && !$.sceditor.dom.isInline(node)) || nodeName === "br")
  538. {
  539. if(inlineFrag.childNodes.length > 0 || nodeName === "br")
  540. {
  541. div = d.createElement('div');
  542. div.appendChild(inlineFrag);
  543. // Putting BR in a div in IE9 causes it to do a double line break,
  544. // as much as I hate browser UA sniffing, to do feature detection would
  545. // be more code than it's worth for this specific bug.
  546. if(nodeName === "br" && (!$.sceditor.ie || $.sceditor.ie < 9))
  547. div.appendChild(d.createElement('br'));
  548. outputDiv.appendChild(div);
  549. inlineFrag = d.createDocumentFragment();
  550. }
  551. if(nodeName !== "br")
  552. outputDiv.appendChild(node);
  553. }
  554. else
  555. inlineFrag.appendChild(node);
  556. node = next;
  557. }
  558. if(inlineFrag.childNodes.length > 0)
  559. {
  560. div = d.createElement('div');
  561. div.appendChild(inlineFrag);
  562. outputDiv.appendChild(div);
  563. }
  564. // needed for paste, the first shouldn't be wrapped in a div
  565. if(excludeFirstLast)
  566. {
  567. node = outputDiv.firstChild;
  568. if(node && node.nodeName.toLowerCase() === "div")
  569. {
  570. while((next = node.firstChild))
  571. outputDiv.insertBefore(next, node);
  572. if($.sceditor.ie >= 9)
  573. outputDiv.insertBefore(d.createElement('br'), node);
  574. outputDiv.removeChild(node);
  575. }
  576. node = outputDiv.lastChild;
  577. if(node && node.nodeName.toLowerCase() === "div")
  578. {
  579. while((next = node.firstChild))
  580. outputDiv.insertBefore(next, node);
  581. if($.sceditor.ie >= 9)
  582. outputDiv.insertBefore(d.createElement('br'), node);
  583. outputDiv.removeChild(node);
  584. }
  585. }
  586. $(tmpDiv).remove();
  587. return outputDiv.innerHTML;
  588. };
  589. init();
  590. };
  591. $.sceditorBBCodePlugin.bbcodes = {
  592. // START_COMMAND: Bold
  593. b: {
  594. tags: {
  595. b: null,
  596. strong: null
  597. },
  598. styles: {
  599. // 401 is for FF 3.5
  600. "font-weight": ["bold", "bolder", "401", "700", "800", "900"]
  601. },
  602. format: "[b]{0}[/b]",
  603. html: '<strong>{0}</strong>'
  604. },
  605. // END_COMMAND
  606. // START_COMMAND: Italic
  607. i: {
  608. tags: {
  609. i: null,
  610. em: null
  611. },
  612. styles: {
  613. "font-style": ["italic", "oblique"]
  614. },
  615. format: "[i]{0}[/i]",
  616. html: '<em>{0}</em>'
  617. },
  618. // END_COMMAND
  619. // START_COMMAND: Underline
  620. u: {
  621. tags: {
  622. u: null
  623. },
  624. styles: {
  625. "text-decoration": ["underline"]
  626. },
  627. format: "[u]{0}[/u]",
  628. html: '<u>{0}</u>'
  629. },
  630. // END_COMMAND
  631. // START_COMMAND: Strikethrough
  632. s: {
  633. tags: {
  634. s: null,
  635. strike: null
  636. },
  637. styles: {
  638. "text-decoration": ["line-through"]
  639. },
  640. format: "[s]{0}[/s]",
  641. html: '<s>{0}</s>'
  642. },
  643. // END_COMMAND
  644. // START_COMMAND: Subscript
  645. sub: {
  646. tags: {
  647. sub: null
  648. },
  649. format: "[sub]{0}[/sub]",
  650. html: '<sub>{0}</sub>'
  651. },
  652. // END_COMMAND
  653. // START_COMMAND: Superscript
  654. sup: {
  655. tags: {
  656. sup: null
  657. },
  658. format: "[sup]{0}[/sup]",
  659. html: '<sup>{0}</sup>'
  660. },
  661. // END_COMMAND
  662. // START_COMMAND: Font
  663. font: {
  664. tags: {
  665. font: {
  666. face: null
  667. }
  668. },
  669. styles: {
  670. "font-family": null
  671. },
  672. format: function(element, content) {
  673. if(element[0].nodeName.toLowerCase() === "font" && element.attr('face'))
  674. return '[font=' + this.stripQuotes(element.attr('face')) + ']' + content + '[/font]';
  675. return '[font=' + this.stripQuotes(element.css('font-family')) + ']' + content + '[/font]';
  676. },
  677. html: function(element, attrs, content) {
  678. return '<font face="' + attrs.defaultAttr + '">' + content + '</font>';
  679. }
  680. },
  681. // END_COMMAND
  682. // START_COMMAND: Size
  683. size: {
  684. tags: {
  685. font: {
  686. size: null
  687. }
  688. },
  689. styles: {
  690. "font-size": null
  691. },
  692. format: function(element, content) {
  693. var fontSize = element.css('fontSize'),
  694. size = 1;
  695. // Most browsers return px value but IE returns 1-7
  696. if(fontSize.indexOf("px") > -1) {
  697. // convert size to an int
  698. fontSize = fontSize.replace("px", "") - 0;
  699. if(fontSize > 12)
  700. size = 2;
  701. if(fontSize > 15)
  702. size = 3;
  703. if(fontSize > 17)
  704. size = 4;
  705. if(fontSize > 23)
  706. size = 5;
  707. if(fontSize > 31)
  708. size = 6;
  709. if(fontSize > 47)
  710. size = 7;
  711. }
  712. else
  713. size = fontSize;
  714. return '[size=' + size + ']' + content + '[/size]';
  715. },
  716. html: function(element, attrs, content) {
  717. return '<font size="' + attrs.defaultAttr + '">' + content + '</font>';
  718. }
  719. },
  720. // END_COMMAND
  721. // START_COMMAND: Color
  722. color: {
  723. tags: {
  724. font: {
  725. color: null
  726. }
  727. },
  728. styles: {
  729. color: null
  730. },
  731. format: function(element, content) {
  732. /**
  733. * Converts CSS rgb value into hex
  734. * @private
  735. * @return string Hex color
  736. */
  737. var rgbToHex = function(rgbStr) {
  738. var m;
  739. function toHex(n) {
  740. n = parseInt(n,10);
  741. if(isNaN(n))
  742. return "00";
  743. n = Math.max(0,Math.min(n,255)).toString(16);
  744. return n.length<2 ? '0'+n : n;
  745. }
  746. // rgb(n,n,n);
  747. if((m = rgbStr.match(/rgb\((\d+),\s*?(\d+),\s*?(\d+)\)/i)))
  748. return '#' + toHex(m[1]) + toHex(m[2]-0) + toHex(m[3]-0);
  749. // expand shorthand
  750. if((m = rgbStr.match(/#([0-f])([0-f])([0-f])\s*?$/i)))
  751. return '#' + m[1] + m[1] + m[2] + m[2] + m[3] + m[3];
  752. return rgbStr;
  753. };
  754. var color = element.css('color');
  755. if(element[0].nodeName.toLowerCase() === "font" && element.attr('color'))
  756. color = element.attr('color');
  757. color = rgbToHex(color);
  758. return '[color=' + color + ']' + content + '[/color]';
  759. },
  760. html: function(element, attrs, content) {
  761. return '<font color="' + attrs.defaultAttr + '">' + content + '</font>';
  762. }
  763. },
  764. black: {
  765. html: '<font color="black">{0}</font>'
  766. },
  767. blue: {
  768. html: '<font color="blue">{0}</font>'
  769. },
  770. green: {
  771. html: '<font color="green">{0}</font>'
  772. },
  773. red: {
  774. html: '<font color="red">{0}</font>'
  775. },
  776. white: {
  777. html: '<font color="white">{0}</font>'
  778. },
  779. // END_COMMAND
  780. // START_COMMAND: Lists
  781. list: {
  782. isBlock: true,
  783. html: function(element, attrs, content) {
  784. var style = '';
  785. var code = 'ul';
  786. if (attrs.type)
  787. style = ' style="list-style-type: ' + attrs.type + '"';
  788. return '<' + code + style + '>' + content + '</' + code + '>';
  789. }
  790. },
  791. ul: {
  792. tags: {
  793. ul: null
  794. },
  795. isBlock: true,
  796. format: function(element, content) {
  797. if ($(element[0]).css('list-style-type') == 'disc')
  798. return '[list]' + content + '[/list]';
  799. else
  800. return '[list type=' + $(element[0]).css('list-style-type') + ']' + content + '[/list]';
  801. },
  802. html: '<ul>{0}</ul>'
  803. },
  804. ol: {
  805. tags: {
  806. ol: null
  807. },
  808. isBlock: true,
  809. format: '[list type=decimal]{0}[/list]',
  810. html: '<ol>{0}</ol>'
  811. },
  812. li: {
  813. tags: {
  814. li: null
  815. },
  816. format: "[li]{0}[/li]",
  817. html: '<li>{0}</li>'
  818. },
  819. "*": {
  820. html: '<li>{0}</li>'
  821. },
  822. // END_COMMAND
  823. // START_COMMAND: Table
  824. table: {
  825. tags: {
  826. table: null
  827. },
  828. format: "[table]{0}[/table]",
  829. html: '<table>{0}</table>'
  830. },
  831. tr: {
  832. tags: {
  833. tr: null
  834. },
  835. format: "[tr]{0}[/tr]",
  836. html: '<tr>{0}</tr>'
  837. },
  838. th: {
  839. tags: {
  840. th: null
  841. },
  842. isBlock: true,
  843. format: "[th]{0}[/th]",
  844. html: '<th>{0}</th>'
  845. },
  846. td: {
  847. tags: {
  848. td: null
  849. },
  850. isBlock: true,
  851. format: "[td]{0}[/td]",
  852. html: '<td>{0}<br class="sceditor-ignore" /></td>'
  853. },
  854. // END_COMMAND
  855. // START_COMMAND: Emoticons
  856. emoticon: {
  857. allowsEmpty: true,
  858. tags: {
  859. img: {
  860. src: null,
  861. "data-sceditor-emoticon": null
  862. }
  863. },
  864. format: function(element, content) {
  865. if (element.attr('data-sceditor-emoticon') == '')
  866. return content;
  867. return element.attr('data-sceditor-emoticon') + content;
  868. },
  869. html: '{0}'
  870. },
  871. // END_COMMAND
  872. // START_COMMAND: Horizontal Rule
  873. horizontalrule: {
  874. allowsEmpty: true,
  875. tags: {
  876. hr: null
  877. },
  878. format: "[hr]{0}",
  879. html: "<hr />"
  880. },
  881. // END_COMMAND
  882. // START_COMMAND: Image
  883. img: {
  884. allowsEmpty: true,
  885. tags: {
  886. img: {
  887. src: null
  888. }
  889. },
  890. format: function(element, content) {
  891. // check if this is an emoticon image
  892. if(typeof element.attr('data-sceditor-emoticon') !== "undefined")
  893. return content;
  894. var width = ' width=' + $(element).width();
  895. var height = ' height=' + $(element).height();
  896. var alt = $(element).attr('alt') != undefined ? ' alt=' + $(element).attr('author').php_unhtmlspecialchars() : '';
  897. return '[img' + width + height + alt + ']' + element.attr('src') + '[/img]';
  898. },
  899. attrs: function () {
  900. return ['alt', 'width', 'height'];
  901. },
  902. html: function(element, attrs, content) {
  903. var attribs = "";
  904. // handle [img width=340 height=240]url[/img]
  905. if(typeof attrs.width !== "undefined")
  906. attribs += ' width="' + attrs.width + '"';
  907. if(typeof attrs.height !== "undefined")
  908. attribs += ' height="' + attrs.height + '"';
  909. if(typeof attrs.alt !== "undefined")
  910. attribs += ' alt="' + attrs.alt + '"';
  911. return '<img ' + attribs + ' src="' + content + '" />';
  912. }
  913. },
  914. // END_COMMAND
  915. // START_COMMAND: URL
  916. url: {
  917. allowsEmpty: true,
  918. tags: {
  919. a: {
  920. href: null
  921. }
  922. },
  923. format: function(element, content) {
  924. // make sure this link is not an e-mail, if it is return e-mail BBCode
  925. if(element.attr('href').substr(0, 7) === 'mailto:')
  926. return '[email=' + element.attr('href').substr(7) + ']' + content + '[/email]';
  927. if(element.attr('target') !== undefined)
  928. return '[url=' + decodeURI(element.attr('href')) + ']' + content + '[/url]';
  929. else
  930. return '[iurl=' + decodeURI(element.attr('href')) + ']' + content + '[/iurl]';
  931. },
  932. html: function(element, attrs, content) {
  933. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  934. attrs.defaultAttr = content;
  935. return '<a trget="_blank" href="' + encodeURI(attrs.defaultAttr) + '">' + content + '</a>';
  936. }
  937. },
  938. iurl: {
  939. html: function(element, attrs, content) {
  940. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  941. attrs.defaultAttr = content;
  942. return '<a href="' + encodeURI(attrs.defaultAttr) + '">' + content + '</a>';
  943. }
  944. },
  945. ftp: {
  946. html: function(element, attrs, content) {
  947. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  948. attrs.defaultAttr = content;
  949. return '<a trget="_blank" href="' + encodeURI(attrs.defaultAttr) + '">' + content + '</a>';
  950. }
  951. },
  952. // END_COMMAND
  953. // START_COMMAND: E-mail
  954. email: {
  955. html: function(element, attrs, content) {
  956. if(typeof attrs.defaultAttr === "undefined")
  957. attrs.defaultAttr = content;
  958. return '<a href="mailto:' + attrs.defaultAttr + '">' + content + '</a>';
  959. }
  960. },
  961. // END_COMMAND
  962. // START_COMMAND: Quote
  963. quote: {
  964. tags: {
  965. blockquote: null
  966. },
  967. isBlock: true,
  968. format: function(element, content) {
  969. var author = '';
  970. var date = '';
  971. var link = '';
  972. if ($(element).children("cite:first").length === 1)
  973. {
  974. author = $(element).children("cite:first").find("author").text();
  975. date = $(element).children("cite:first").find("date").attr('timestamp');
  976. link = $(element).children("cite:first").find("quote_link").text();
  977. $(element).attr({'author': author.php_htmlspecialchars(), 'date': date, 'link': link});
  978. if (author != '')
  979. author = ' author=' + author;
  980. if (date != '')
  981. date = ' date=' + date;
  982. if (link != '')
  983. link = ' link=' + link;
  984. content = '';
  985. $(element).children("cite:first").remove();
  986. content = this.elementToBbcode($(element));
  987. }
  988. else
  989. {
  990. if ($(element).attr('author') != undefined)
  991. author = ' author=' + $(element).attr('author').php_unhtmlspecialchars();
  992. if ($(element).attr('date') != undefined)
  993. date = ' date=' + $(element).attr('date');
  994. if ($(element).attr('link') != undefined)
  995. link = ' link=' + $(element).attr('link');
  996. }
  997. return '[quote' + author + date + link + ']' + content + '[/quote]';
  998. },
  999. attrs: function () {
  1000. return ['author', 'date', 'link'];
  1001. },
  1002. html: function(element, attrs, content) {
  1003. var author = '';
  1004. var sDate = '';
  1005. var link = '';
  1006. if(typeof attrs.author !== "undefined")
  1007. author = bbc_quote_from + ': <author>' + attrs.author + '</author>';
  1008. // Links could be in the form: link=topic=71.msg201#msg201 that would fool javascript, so we need a workaround
  1009. // Probably no more necessary
  1010. for (var key in attrs)
  1011. {
  1012. if (key.substr(0, 4) == 'link' && attrs.hasOwnProperty(key))
  1013. {
  1014. var possible_url = key.length > 4 ? key.substr(5) + '=' + attrs[key] : attrs[key];
  1015. link = possible_url.substr(0, 7) == 'http://' ? possible_url : smf_scripturl + '?' + possible_url;
  1016. author = author == '' ? '<a href="' + link + '">' + bbc_quote_from + ': <author src=">' + link + '</author></a>' : '<a href="' + link + '">' + author + '</a>';
  1017. link = '<quote_link style="display:none">' + possible_url + '</quote_link>';
  1018. }
  1019. }
  1020. if(typeof attrs.date !== "undefined")
  1021. {
  1022. var date = new Date(attrs.date * 1000);
  1023. sDate = date;
  1024. }
  1025. if (author == '' && sDate == '')
  1026. author = bbc_quote;
  1027. else
  1028. author += ' ';
  1029. content = '<blockquote><cite>' + author + bbc_search_on + ' ' + '<date timestamp="' + attrs.date + '">' + sDate + '</date>' + link + '</cite>' + content + '</blockquote>';
  1030. return content;
  1031. }
  1032. },
  1033. // END_COMMAND
  1034. // START_COMMAND: Code
  1035. code: {
  1036. tags: {
  1037. code: null
  1038. },
  1039. isBlock: true,
  1040. format: function(element, content) {
  1041. if ($(element[0]).hasClass('php'))
  1042. return '[php]' + content + '[/php]';
  1043. var from = '';
  1044. if ($(element).children("cite:first").length === 1)
  1045. {
  1046. from = $(element).children("cite:first").text();
  1047. $(element).attr({'from': from.php_htmlspecialchars()});
  1048. from = '=' + from;
  1049. content = '';
  1050. $(element).children("cite:first").remove();
  1051. content = this.elementToBbcode($(element));
  1052. }
  1053. else
  1054. {
  1055. if ($(element).attr('from') != undefined)
  1056. {
  1057. from = '=' + $(element).attr('from').php_unhtmlspecialchars();
  1058. }
  1059. }
  1060. return '[code' + from + ']' + content + '[/code]';
  1061. },
  1062. html: function(element, attrs, content) {
  1063. var from = '';
  1064. if(typeof attrs.defaultAttr !== "undefined")
  1065. from = '<cite>' + attrs.defaultAttr + '</cite>';
  1066. return '<code>' + from + content + '</code>'
  1067. }
  1068. },
  1069. php: {
  1070. isBlock: true,
  1071. format: "[php]{0}[/php]",
  1072. html: '<code class="php">{0}</code>'
  1073. },
  1074. // END_COMMAND
  1075. // START_COMMAND: Left
  1076. left: {
  1077. styles: {
  1078. "text-align": ["left", "-webkit-left", "-moz-left", "-khtml-left"]
  1079. },
  1080. isBlock: true,
  1081. format: "[left]{0}[/left]",
  1082. html: '<div align="left">{0}</div>'
  1083. },
  1084. // END_COMMAND
  1085. // START_COMMAND: Centre
  1086. center: {
  1087. styles: {
  1088. "text-align": ["center", "-webkit-center", "-moz-center", "-khtml-center"]
  1089. },
  1090. isBlock: true,
  1091. format: "[center]{0}[/center]",
  1092. html: '<div align="center">{0}</div>'
  1093. },
  1094. // END_COMMAND
  1095. // START_COMMAND: Right
  1096. right: {
  1097. styles: {
  1098. "text-align": ["right", "-webkit-right", "-moz-right", "-khtml-right"]
  1099. },
  1100. isBlock: true,
  1101. format: "[right]{0}[/right]",
  1102. html: '<div align="right">{0}</div>'
  1103. },
  1104. // END_COMMAND
  1105. // START_COMMAND: Justify
  1106. justify: {
  1107. styles: {
  1108. "text-align": ["justify", "-webkit-justify", "-moz-justify", "-khtml-justify"]
  1109. },
  1110. isBlock: true,
  1111. format: "[justify]{0}[/justify]",
  1112. html: '<div align="justify">{0}</div>'
  1113. },
  1114. // END_COMMAND
  1115. // START_COMMAND: YouTube
  1116. youtube: {
  1117. allowsEmpty: true,
  1118. tags: {
  1119. iframe: {
  1120. 'data-youtube-id': null
  1121. }
  1122. },
  1123. format: function(element, content) {
  1124. if(!element.attr('data-youtube-id'))
  1125. return content;
  1126. return '[youtube]' + element.attr('data-youtube-id') + '[/youtube]';
  1127. },
  1128. html: '<iframe width="560" height="315" src="http://www.youtube.com/embed/{0}?wmode=opaque' +
  1129. '" data-youtube-id="{0}" frameborder="0" allowfullscreen></iframe>'
  1130. },
  1131. // END_COMMAND
  1132. abbr: {
  1133. tags: {
  1134. abbr: {
  1135. title: null
  1136. }
  1137. },
  1138. format: function(element, content) {
  1139. return '[abbr=' + element.attr('title') + ']' + content + '[/abbr]';
  1140. },
  1141. html: function(element, attrs, content) {
  1142. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  1143. return content;
  1144. return '<abbr title="' + attrs.defaultAttr + '">' + content + '</abbr>';
  1145. }
  1146. },
  1147. acronym: {
  1148. tags: {
  1149. acronym: {
  1150. title: null
  1151. }
  1152. },
  1153. format: function(element, content) {
  1154. return '[acronym=' + element.attr('title') + ']' + content + '[/acronym]';
  1155. },
  1156. html: function(element, attrs, content) {
  1157. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  1158. return content;
  1159. return '<acronym title="' + attrs.defaultAttr + '">' + content + '</acronym>';
  1160. }
  1161. },
  1162. bdo: {
  1163. tags: {
  1164. bdo: {
  1165. dir: null
  1166. }
  1167. },
  1168. format: function(element, content) {
  1169. return '[bdo=' + element.attr('dir') + ']' + content + '[/bdo]';
  1170. },
  1171. html: function(element, attrs, content) {
  1172. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  1173. return content;
  1174. if (attrs.defaultAttr != 'rtl' && attrs.defaultAttr != 'ltr')
  1175. return '[bdo=' + attrs.defaultAttr + ']' + content + '[/bdo]';
  1176. return '<bdo dir="' + attrs.defaultAttr + '">' + content + '</bdo>';
  1177. }
  1178. },
  1179. tt: {
  1180. tags: {
  1181. tt: null
  1182. },
  1183. format: "[tt]{0}[/tt]",
  1184. html: '<tt>{0}</tt>'
  1185. },
  1186. pre: {
  1187. tags: {
  1188. pre: null
  1189. },
  1190. format: "[pre]{0}[/pre]",
  1191. html: '<pre>{0}</pre>'
  1192. },
  1193. move: {
  1194. tags: {
  1195. marquee: null
  1196. },
  1197. format: "[move]{0}[/move]",
  1198. html: '<marquee>{0}</marquee>'
  1199. },
  1200. // this is here so that commands above can be removed
  1201. // without having to remove the , after the last one.
  1202. // Needed for IE.
  1203. ignore: {}
  1204. };
  1205. /**
  1206. * Checks if a command with the specified name exists
  1207. *
  1208. * @param string name
  1209. * @return bool
  1210. */
  1211. $.sceditorBBCodePlugin.commandExists = function(name) {
  1212. return typeof $.sceditorBBCodePlugin.bbcodes[name] !== "undefined";
  1213. };
  1214. /**
  1215. * Adds/updates a BBCode.
  1216. *
  1217. * @param String name The BBCode name
  1218. * @param Object tags Any html tags this bbcode applies to, i.e. strong for [b]
  1219. * @param Object styles Any style properties this applies to, i.e. font-weight for [b]
  1220. * @param String|Function format Function or string to convert the element into BBCode
  1221. * @param String|Function html String or function to format the BBCode back into HTML.
  1222. * @param BOOL allowsEmpty If this BBCodes is allowed to be empty, e.g. [b][/b]
  1223. * @return Bool
  1224. */
  1225. $.sceditorBBCodePlugin.setCommand = function(name, tags, styles, format, html, allowsEmpty) {
  1226. if(!name || !format || !html)
  1227. return false;
  1228. if(!$.sceditorBBCodePlugin.commandExists(name))
  1229. $.sceditorBBCodePlugin.bbcodes[name] = {};
  1230. $.sceditorBBCodePlugin.bbcodes[name].format = format;
  1231. $.sceditorBBCodePlugin.bbcodes[name].html = html;
  1232. if(tags)
  1233. $.sceditorBBCodePlugin.bbcodes[name].tags = tags;
  1234. if(styles)
  1235. $.sceditorBBCodePlugin.bbcodes[name].styles = styles;
  1236. if(allowsEmpty)
  1237. $.sceditorBBCodePlugin.bbcodes[name].allowsEmpty = allowsEmpty;
  1238. return true;
  1239. };
  1240. $.fn.sceditorBBCodePlugin = function(options) {
  1241. return this.each(function() {
  1242. (new $.sceditorBBCodePlugin(this, options));
  1243. });
  1244. };
  1245. })(jQuery);