jquery.sceditor.bbcode.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415
  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. return element.attr('data-sceditor-emoticon') + content;
  862. },
  863. html: '{0}'
  864. },
  865. // END_COMMAND
  866. // START_COMMAND: Horizontal Rule
  867. horizontalrule: {
  868. allowsEmpty: true,
  869. tags: {
  870. hr: null
  871. },
  872. format: "[hr]{0}",
  873. html: "<hr />"
  874. },
  875. // END_COMMAND
  876. // START_COMMAND: Image
  877. img: {
  878. allowsEmpty: true,
  879. tags: {
  880. img: {
  881. src: null
  882. }
  883. },
  884. format: function(element, content) {
  885. // check if this is an emoticon image
  886. if(typeof element.attr('data-sceditor-emoticon') !== "undefined")
  887. return content;
  888. var width = ' width=' + $(element).width();
  889. var height = ' height=' + $(element).height();
  890. var alt = $(element).attr('alt') != undefined ? ' alt=' + $(element).attr('author').php_unhtmlspecialchars() : '';
  891. return '[img' + width + height + alt + ']' + element.attr('src') + '[/img]';
  892. },
  893. attrs: function () {
  894. return ['alt', 'width', 'height'];
  895. },
  896. html: function(element, attrs, content) {
  897. var attribs = "";
  898. // handle [img width=340 height=240]url[/img]
  899. if(typeof attrs.width !== "undefined")
  900. attribs += ' width="' + attrs.width + '"';
  901. if(typeof attrs.height !== "undefined")
  902. attribs += ' height="' + attrs.height + '"';
  903. if(typeof attrs.alt !== "undefined")
  904. attribs += ' alt="' + attrs.alt + '"';
  905. return '<img ' + attribs + ' src="' + content + '" />';
  906. }
  907. },
  908. // END_COMMAND
  909. // START_COMMAND: URL
  910. url: {
  911. allowsEmpty: true,
  912. tags: {
  913. a: {
  914. href: null
  915. }
  916. },
  917. format: function(element, content) {
  918. // make sure this link is not an e-mail, if it is return e-mail BBCode
  919. if(element.attr('href').substr(0, 7) === 'mailto:')
  920. return '[email=' + element.attr('href').substr(7) + ']' + content + '[/email]';
  921. if(element.attr('target') !== undefined)
  922. return '[url=' + decodeURI(element.attr('href')) + ']' + content + '[/url]';
  923. else
  924. return '[iurl=' + decodeURI(element.attr('href')) + ']' + content + '[/iurl]';
  925. },
  926. html: function(element, attrs, content) {
  927. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  928. attrs.defaultAttr = content;
  929. return '<a trget="_blank" href="' + encodeURI(attrs.defaultAttr) + '">' + content + '</a>';
  930. }
  931. },
  932. iurl: {
  933. html: function(element, attrs, content) {
  934. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  935. attrs.defaultAttr = content;
  936. return '<a href="' + encodeURI(attrs.defaultAttr) + '">' + content + '</a>';
  937. }
  938. },
  939. // END_COMMAND
  940. // START_COMMAND: E-mail
  941. email: {
  942. html: function(element, attrs, content) {
  943. if(typeof attrs.defaultAttr === "undefined")
  944. attrs.defaultAttr = content;
  945. return '<a href="mailto:' + attrs.defaultAttr + '">' + content + '</a>';
  946. }
  947. },
  948. // END_COMMAND
  949. // START_COMMAND: Quote
  950. quote: {
  951. tags: {
  952. blockquote: null
  953. },
  954. isBlock: true,
  955. format: function(element, content) {
  956. var author = '';
  957. var date = '';
  958. var link = '';
  959. if ($(element).children("cite:first").length === 1)
  960. {
  961. author = $(element).children("cite:first").find("author").text();
  962. date = $(element).children("cite:first").find("date").attr('timestamp');
  963. link = $(element).children("cite:first").find("quote_link").text();
  964. $(element).attr({'author': author.php_htmlspecialchars(), 'date': date, 'link': link});
  965. if (author != '')
  966. author = ' author=' + author;
  967. if (date != '')
  968. date = ' date=' + date;
  969. if (link != '')
  970. link = ' link=' + link;
  971. content = '';
  972. $(element).children("cite:first").remove();
  973. content = this.elementToBbcode($(element));
  974. }
  975. else
  976. {
  977. if ($(element).attr('author') != undefined);
  978. author = ' author=' + $(element).attr('author').php_unhtmlspecialchars();
  979. if ($(element).attr('date') != undefined);
  980. date = ' date=' + $(element).attr('date');
  981. if ($(element).attr('link') != undefined);
  982. link = ' link=' + $(element).attr('link');
  983. }
  984. return '[quote' + author + date + link + ']' + content + '[/quote]';
  985. },
  986. attrs: function () {
  987. return ['author', 'date', 'link'];
  988. },
  989. html: function(element, attrs, content) {
  990. var author = '';
  991. var sDate = '';
  992. var link = '';
  993. if(typeof attrs.author !== "undefined")
  994. author = bbc_quote_from + ': <author>' + attrs.author + '</author>';
  995. // Links could be in the form: link=topic=71.msg201#msg201 that would fool javascript, so we need a workaround
  996. // Probably no more necessary
  997. for (var key in attrs)
  998. {
  999. if (key.substr(0, 4) == 'link' && attrs.hasOwnProperty(key))
  1000. {
  1001. var possible_url = key.length > 4 ? key.substr(5) + '=' + attrs[key] : attrs[key];
  1002. link = possible_url.substr(0, 7) == 'http://' ? possible_url : smf_scripturl + '?' + possible_url;
  1003. author = author == '' ? '<a href="' + link + '">' + bbc_quote_from + ': <author src=">' + link + '</author></a>' : '<a href="' + link + '">' + author + '</a>';
  1004. link = '<quote_link style="display:none">' + possible_url + '</quote_link>';
  1005. }
  1006. }
  1007. if(typeof attrs.date !== "undefined")
  1008. {
  1009. var date = new Date(attrs.date * 1000);
  1010. sDate = date;
  1011. }
  1012. if (author == '' && sDate == '')
  1013. author = bbc_quote;
  1014. else
  1015. author += ' ';
  1016. content = '<blockquote><cite>' + author + bbc_search_on + ' ' + '<date timestamp="' + attrs.date + '">' + sDate + '</date>' + link + '</cite>' + content + '</blockquote>';
  1017. return content;
  1018. }
  1019. },
  1020. // END_COMMAND
  1021. // START_COMMAND: Code
  1022. code: {
  1023. tags: {
  1024. code: null
  1025. },
  1026. isBlock: true,
  1027. format: function(element, content) {
  1028. if ($(element[0]).hasClass('php'))
  1029. return '[php]' + content + '[/php]';
  1030. var from = '';
  1031. if ($(element).children("cite:first").length === 1)
  1032. {
  1033. from = $(element).children("cite:first").text();
  1034. $(element).attr({'from': from.php_htmlspecialchars()});
  1035. from = '=' + from;
  1036. content = '';
  1037. $(element).children("cite:first").remove();
  1038. content = this.elementToBbcode($(element));
  1039. }
  1040. else
  1041. {
  1042. if ($(element).attr('from') != undefined)
  1043. {
  1044. from = '=' + $(element).attr('from').php_unhtmlspecialchars();
  1045. }
  1046. }
  1047. return '[code' + from + ']' + content + '[/code]';
  1048. },
  1049. html: function(element, attrs, content) {
  1050. var from = '';
  1051. if(typeof attrs.defaultAttr !== "undefined")
  1052. from = '<cite>' + attrs.defaultAttr + '</cite>';
  1053. return '<code>' + from + content + '</code>'
  1054. }
  1055. },
  1056. php: {
  1057. isBlock: true,
  1058. format: "[php]{0}[/php]",
  1059. html: '<code class="php">{0}</code>'
  1060. },
  1061. // END_COMMAND
  1062. // START_COMMAND: Left
  1063. left: {
  1064. styles: {
  1065. "text-align": ["left", "-webkit-left", "-moz-left", "-khtml-left"]
  1066. },
  1067. isBlock: true,
  1068. format: "[left]{0}[/left]",
  1069. html: '<div align="left">{0}</div>'
  1070. },
  1071. // END_COMMAND
  1072. // START_COMMAND: Centre
  1073. center: {
  1074. styles: {
  1075. "text-align": ["center", "-webkit-center", "-moz-center", "-khtml-center"]
  1076. },
  1077. isBlock: true,
  1078. format: "[center]{0}[/center]",
  1079. html: '<div align="center">{0}</div>'
  1080. },
  1081. // END_COMMAND
  1082. // START_COMMAND: Right
  1083. right: {
  1084. styles: {
  1085. "text-align": ["right", "-webkit-right", "-moz-right", "-khtml-right"]
  1086. },
  1087. isBlock: true,
  1088. format: "[right]{0}[/right]",
  1089. html: '<div align="right">{0}</div>'
  1090. },
  1091. // END_COMMAND
  1092. // START_COMMAND: Justify
  1093. justify: {
  1094. styles: {
  1095. "text-align": ["justify", "-webkit-justify", "-moz-justify", "-khtml-justify"]
  1096. },
  1097. isBlock: true,
  1098. format: "[justify]{0}[/justify]",
  1099. html: '<div align="justify">{0}</div>'
  1100. },
  1101. // END_COMMAND
  1102. // START_COMMAND: YouTube
  1103. youtube: {
  1104. allowsEmpty: true,
  1105. tags: {
  1106. iframe: {
  1107. 'data-youtube-id': null
  1108. }
  1109. },
  1110. format: function(element, content) {
  1111. if(!element.attr('data-youtube-id'))
  1112. return content;
  1113. return '[youtube]' + element.attr('data-youtube-id') + '[/youtube]';
  1114. },
  1115. html: '<iframe width="560" height="315" src="http://www.youtube.com/embed/{0}?wmode=opaque' +
  1116. '" data-youtube-id="{0}" frameborder="0" allowfullscreen></iframe>'
  1117. },
  1118. // END_COMMAND
  1119. abbr: {
  1120. tags: {
  1121. abbr: {
  1122. title: null
  1123. }
  1124. },
  1125. format: function(element, content) {
  1126. return '[abbr=' + element.attr('title') + ']' + content + '[/abbr]';
  1127. },
  1128. html: function(element, attrs, content) {
  1129. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  1130. return content;
  1131. return '<abbr title="' + attrs.defaultAttr + '">' + content + '</abbr>';
  1132. }
  1133. },
  1134. acronym: {
  1135. tags: {
  1136. acronym: {
  1137. title: null
  1138. }
  1139. },
  1140. format: function(element, content) {
  1141. return '[acronym=' + element.attr('title') + ']' + content + '[/acronym]';
  1142. },
  1143. html: function(element, attrs, content) {
  1144. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  1145. return content;
  1146. return '<acronym title="' + attrs.defaultAttr + '">' + content + '</acronym>';
  1147. }
  1148. },
  1149. bdo: {
  1150. tags: {
  1151. bdo: {
  1152. dir: null
  1153. }
  1154. },
  1155. format: function(element, content) {
  1156. return '[bdo=' + element.attr('dir') + ']' + content + '[/bdo]';
  1157. },
  1158. html: function(element, attrs, content) {
  1159. if(typeof attrs.defaultAttr === "undefined" || attrs.defaultAttr.length === 0)
  1160. return content;
  1161. if (attrs.defaultAttr != 'rtl' && attrs.defaultAttr != 'ltr')
  1162. return '[bdo=' + attrs.defaultAttr + ']' + content + '[/bdo]';
  1163. return '<bdo dir="' + attrs.defaultAttr + '">' + content + '</bdo>';
  1164. }
  1165. },
  1166. tt: {
  1167. tags: {
  1168. tt: null
  1169. },
  1170. format: "[tt]{0}[/tt]",
  1171. html: '<tt>{0}</tt>'
  1172. },
  1173. pre: {
  1174. tags: {
  1175. pre: null
  1176. },
  1177. format: "[pre]{0}[/pre]",
  1178. html: '<pre>{0}</pre>'
  1179. },
  1180. move: {
  1181. tags: {
  1182. marquee: null
  1183. },
  1184. format: "[move]{0}[/move]",
  1185. html: '<marquee>{0}</marquee>'
  1186. },
  1187. // this is here so that commands above can be removed
  1188. // without having to remove the , after the last one.
  1189. // Needed for IE.
  1190. ignore: {}
  1191. };
  1192. /**
  1193. * Checks if a command with the specified name exists
  1194. *
  1195. * @param string name
  1196. * @return bool
  1197. */
  1198. $.sceditorBBCodePlugin.commandExists = function(name) {
  1199. return typeof $.sceditorBBCodePlugin.bbcodes[name] !== "undefined";
  1200. };
  1201. /**
  1202. * Adds/updates a BBCode.
  1203. *
  1204. * @param String name The BBCode name
  1205. * @param Object tags Any html tags this bbcode applies to, i.e. strong for [b]
  1206. * @param Object styles Any style properties this applies to, i.e. font-weight for [b]
  1207. * @param String|Function format Function or string to convert the element into BBCode
  1208. * @param String|Function html String or function to format the BBCode back into HTML.
  1209. * @param BOOL allowsEmpty If this BBCodes is allowed to be empty, e.g. [b][/b]
  1210. * @return Bool
  1211. */
  1212. $.sceditorBBCodePlugin.setCommand = function(name, tags, styles, format, html, allowsEmpty) {
  1213. if(!name || !format || !html)
  1214. return false;
  1215. if(!$.sceditorBBCodePlugin.commandExists(name))
  1216. $.sceditorBBCodePlugin.bbcodes[name] = {};
  1217. $.sceditorBBCodePlugin.bbcodes[name].format = format;
  1218. $.sceditorBBCodePlugin.bbcodes[name].html = html;
  1219. if(tags)
  1220. $.sceditorBBCodePlugin.bbcodes[name].tags = tags;
  1221. if(styles)
  1222. $.sceditorBBCodePlugin.bbcodes[name].styles = styles;
  1223. if(allowsEmpty)
  1224. $.sceditorBBCodePlugin.bbcodes[name].allowsEmpty = allowsEmpty;
  1225. return true;
  1226. };
  1227. $.fn.sceditorBBCodePlugin = function(options) {
  1228. return this.each(function() {
  1229. (new $.sceditorBBCodePlugin(this, options));
  1230. });
  1231. };
  1232. })(jQuery);