jquery.sceditor.bbcode.js 37 KB

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