jquery.sceditor.smf.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. /**
  2. * Simple Machines Forum (SMF)
  3. *
  4. * @package SMF
  5. * @author Simple Machines http://www.simplemachines.org
  6. * @copyright 2014 Simple Machines and individual contributors
  7. * @license http://www.simplemachines.org/about/smf/license.php BSD
  8. *
  9. * @version 2.1 Alpha 1
  10. */
  11. (function($) {
  12. var extensionMethods = {
  13. InsertText: function(text, bClear) {
  14. var bIsSource = this.inSourceMode();
  15. // @TODO make it put the quote close to the current selection
  16. if (!bIsSource)
  17. this.toggleSourceMode();
  18. var current_value = bClear ? text + "\n" : this.getSourceEditorValue(false) + "\n" + text + "\n";
  19. this.setSourceEditorValue(current_value);
  20. if (!bIsSource)
  21. this.toggleSourceMode();
  22. },
  23. getText: function(filter) {
  24. var current_value = '';
  25. if(this.inSourceMode())
  26. current_value = this.getSourceEditorValue(false);
  27. else
  28. current_value = this.getWysiwygEditorValue(filter);
  29. return current_value;
  30. },
  31. appendEmoticon: function (code, emoticon) {
  32. if (emoticon == '')
  33. line.append($('<br>'));
  34. else
  35. line.append($('<img>')
  36. .attr({
  37. src: emoticon,
  38. alt: code,
  39. })
  40. .click(function (e) {
  41. var start = '', end = '';
  42. if (base.opts.emoticonsCompat)
  43. {
  44. start = '<span> ';
  45. end = ' </span>';
  46. }
  47. if (base.inSourceMode())
  48. base.sourceEditorInsertText(' ' + $(this).attr('alt') + ' ');
  49. else
  50. base.wysiwygEditorInsertHtml(start + '<img src="' + $(this).attr("src") + '" data-sceditor-emoticon="' + $(this).attr('alt') + '">' + end);
  51. e.preventDefault();
  52. })
  53. );
  54. if (line.children().length > 0)
  55. content.append(line);
  56. $(".sceditor-toolbar").append(content);
  57. },
  58. storeLastState: function (){
  59. this.wasSource = this.inSourceMode();
  60. },
  61. setTextMode: function () {
  62. if (!this.inSourceMode())
  63. this.toggleSourceMode();
  64. },
  65. createPermanentDropDown: function() {
  66. var emoticons = $.extend({}, this.opts.emoticons.dropdown);
  67. var popup_exists = false;
  68. content = $('<div class="sceditor-insertemoticon">');
  69. line = $('<div>');
  70. base = this;
  71. for (smiley_popup in this.opts.emoticons.popup)
  72. {
  73. popup_exists = true;
  74. break;
  75. }
  76. if (popup_exists)
  77. {
  78. this.opts.emoticons.more = this.opts.emoticons.popup;
  79. moreButton = $('<div class="sceditor-more-button sceditor-more">').text('[' + this._('More') + ']').click(function () {
  80. if ($(".sceditor-smileyPopup").length > 0)
  81. {
  82. $(".sceditor-smileyPopup").fadeIn('fast');
  83. }
  84. else
  85. {
  86. var emoticons = $.extend({}, base.opts.emoticons.popup);
  87. var popup_position;
  88. var titlebar = $('<div class="catbg sceditor-popup-grip"/>');
  89. popupContent = $('<div id="sceditor-popup"/>');
  90. allowHide = true;
  91. line = $('<div id="sceditor-popup-smiley"/>');
  92. adjheight = 0;
  93. popupContent.append(titlebar);
  94. closeButton = $('<span>').text('[' + base._('Close') + ']').click(function () {
  95. $(".sceditor-smileyPopup").fadeOut('fast');
  96. });
  97. $.each(emoticons, base.appendEmoticon);
  98. if (line.children().length > 0)
  99. popupContent.append(line);
  100. if (typeof closeButton !== "undefined")
  101. popupContent.append(closeButton);
  102. // IE needs unselectable attr to stop it from unselecting the text in the editor.
  103. // The editor can cope if IE does unselect the text it's just not nice.
  104. if(base.ieUnselectable !== false) {
  105. content = $(content);
  106. content.find(':not(input,textarea)').filter(function() { return this.nodeType===1; }).attr('unselectable', 'on');
  107. }
  108. $dropdown = $('<div class="sceditor-dropdown sceditor-smileyPopup">').append(popupContent);
  109. $dropdown.appendTo($('body'));
  110. dropdownIgnoreLastClick = true;
  111. adjheight = closeButton.height() + titlebar.height();
  112. $dropdown.css({
  113. position: "fixed",
  114. top: $(window).height() * 0.2,
  115. left: $(window).width() * 0.5 - ($dropdown.find('#sceditor-popup-smiley').width() / 2),
  116. "max-width": "50%",
  117. "max-height": "50%",
  118. }).find('#sceditor-popup-smiley').css({
  119. height: $dropdown.height() - adjheight,
  120. "overflow": "auto"
  121. });
  122. $('.sceditor-smileyPopup').animaDrag({
  123. speed: 150,
  124. interval: 120,
  125. during: function(e) {
  126. $(this).height(this.startheight);
  127. $(this).width(this.startwidth);
  128. },
  129. before: function(e) {
  130. this.startheight = $(this).innerHeight();
  131. this.startwidth = $(this).innerWidth();
  132. },
  133. grip: '.sceditor-popup-grip'
  134. });
  135. // stop clicks within the dropdown from being handled
  136. $dropdown.click(function (e) {
  137. e.stopPropagation();
  138. });
  139. }
  140. });
  141. }
  142. $.each(emoticons, base.appendEmoticon);
  143. if (typeof moreButton !== "undefined")
  144. content.append(moreButton);
  145. }
  146. };
  147. $.extend(true, $['sceditor'].prototype, extensionMethods);
  148. })(jQuery);
  149. $.sceditor.command.set(
  150. 'ftp', {
  151. tooltip: 'Insert FTP Link',
  152. txtExec: ["[ftp]", "[/ftp]"],
  153. exec: function (caller) {
  154. var editor = this,
  155. content = $(this._('<form><div><label for="link">{0}</label> <input type="text" id="link" value="ftp://"></div>' +
  156. '<div><label for="des">{1}</label> <input type="text" id="des" value=""></div></form>',
  157. this._("URL:"),
  158. this._("Description (optional):")
  159. ))
  160. .submit(function () {return false;});
  161. content.append($(
  162. this._('<div><input type="button" class="button" value="{0}"></div>',
  163. this._("Insert")
  164. )).click(function (e) {
  165. var val = $(this).parent("form").find("#link").val(),
  166. description = $(this).parent("form").find("#des").val();
  167. if(val !== "" && val !== "ftp://") {
  168. // needed for IE to reset the last range
  169. editor.focus();
  170. if(!editor.getRangeHelper().selectedHtml() || description)
  171. {
  172. if(!description)
  173. description = val;
  174. editor.wysiwygEditorInsertHtml('<a href="' + val + '">' + description + '</a>');
  175. }
  176. else
  177. editor.execCommand("createlink", val);
  178. }
  179. editor.closeDropDown(true);
  180. e.preventDefault();
  181. }));
  182. editor.createDropDown(caller, "insertlink", content);
  183. }
  184. }
  185. );
  186. $.sceditor.command.set(
  187. 'glow', {
  188. tooltip: 'Glow',
  189. txtExec: ["[glow=red,2,300]", "[/glow]"],
  190. exec: function () {
  191. this.wysiwygEditorInsertHtml('[glow=red,2,300]', '[/glow]');
  192. }
  193. }
  194. );
  195. $.sceditor.command.set(
  196. 'shadow', {
  197. tooltip: 'Shadow',
  198. txtExec: ["[shadow=red,left]", "[/shadow]"],
  199. exec: function () {
  200. this.wysiwygEditorInsertHtml('[shadow=red,left]', '[/shadow]');
  201. }
  202. }
  203. );
  204. $.sceditor.command.set(
  205. 'tt', {
  206. tooltip: 'Teletype',
  207. txtExec: ["[tt]", "[/tt]"],
  208. exec: function () {
  209. this.wysiwygEditorInsertHtml('<tt>', '</tt>');
  210. }
  211. }
  212. );
  213. $.sceditor.command.set(
  214. 'pre', {
  215. tooltip: 'Pre',
  216. txtExec: ["[pre]", "[/pre]"],
  217. exec: function () {
  218. this.wysiwygEditorInsertHtml('<pre>', '</pre>');
  219. }
  220. }
  221. );
  222. $.sceditor.command.set(
  223. 'move', {
  224. tooltip: 'Marquee',
  225. txtExec: ["[move]", "[/move]"],
  226. exec: function () {
  227. this.wysiwygEditorInsertHtml('<marquee>', '</marquee>');
  228. }
  229. }
  230. );
  231. $.sceditor.command.set(
  232. 'email', {
  233. txtExec: function(caller, selected) {
  234. var display = selected && selected.indexOf('@') > -1 ? null : selected,
  235. email = prompt(this._("Enter the e-mail address:"), (display ? '' : selected));
  236. if (email)
  237. {
  238. var text = prompt(this._("Enter the displayed text:"), display || email) || email;
  239. this.insertText("[email=" + email + "]" + text + "[/email]");
  240. }
  241. }
  242. }
  243. );
  244. $.sceditor.command.set(
  245. 'link', {
  246. txtExec: function(caller, selected) {
  247. var display = selected && selected.indexOf('http://') > -1 ? null : selected,
  248. url = prompt(this._("Enter URL:"), (display ? 'http://' : selected));
  249. if (url)
  250. {
  251. var text = prompt(this._("Enter the displayed text:"), display || url) || url;
  252. this.insertText("[url=" + url + "]" + text + "[/url]");
  253. }
  254. }
  255. }
  256. );
  257. $.sceditor.command.set(
  258. 'bulletlist', {
  259. txtExec: ["[list]\n[li]", "[/li]\n[li][/li]\n[/list]"]
  260. }
  261. );
  262. $.sceditor.command.set(
  263. 'orderedlist', {
  264. txtExec: ["[list type=decimal]\n[li]", "[/li]\n[li][/li]\n[/list]"]
  265. }
  266. );
  267. $.sceditor.command.set(
  268. 'table', {
  269. txtExec: ["[table]\n[tr]\n[td]", "[/td]\n[/tr]\n[/table]"]
  270. }
  271. );
  272. $.sceditorBBCodePlugin.bbcode.set(
  273. 'abbr', {
  274. tags: {
  275. abbr: {
  276. title: null
  277. }
  278. },
  279. format: function(element, content) {
  280. return '[abbr=' + element.attr('title') + ']' + content + '[/abbr]';
  281. },
  282. html: function(element, attrs, content) {
  283. if(typeof attrs.defaultattr === "undefined" || attrs.defaultattr.length === 0)
  284. return content;
  285. return '<abbr title="' + attrs.defaultattr + '">' + content + '</abbr>';
  286. }
  287. }
  288. );
  289. $.sceditorBBCodePlugin.bbcode.set(
  290. 'acronym', {
  291. tags: {
  292. acronym: {
  293. title: null
  294. }
  295. },
  296. format: function(element, content) {
  297. return '[abbr=' + element.attr('title') + ']' + content + '[/abbr]';
  298. },
  299. html: function(element, attrs, content) {
  300. if(typeof attrs.defaultattr === "undefined" || attrs.defaultattr.length === 0)
  301. return content;
  302. return '<abbr title="' + attrs.defaultattr + '">' + content + '</abbr>';
  303. }
  304. }
  305. );
  306. $.sceditorBBCodePlugin.bbcode.set(
  307. 'bdo', {
  308. tags: {
  309. bdo: {
  310. dir: null
  311. }
  312. },
  313. format: function(element, content) {
  314. return '[bdo=' + element.attr('dir') + ']' + content + '[/bdo]';
  315. },
  316. html: function(element, attrs, content) {
  317. if(typeof attrs.defaultattr === "undefined" || attrs.defaultattr.length === 0)
  318. return content;
  319. if (attrs.defaultattr != 'rtl' && attrs.defaultattr != 'ltr')
  320. return '[bdo=' + attrs.defaultattr + ']' + content + '[/bdo]';
  321. return '<bdo dir="' + attrs.defaultattr + '">' + content + '</bdo>';
  322. }
  323. }
  324. );
  325. $.sceditorBBCodePlugin.bbcode.set(
  326. 'black', {
  327. html: '<font color="black">{0}</font>'
  328. }
  329. );
  330. $.sceditorBBCodePlugin.bbcode.set(
  331. 'blue', {
  332. html: '<font color="blue">{0}</font>'
  333. }
  334. );
  335. $.sceditorBBCodePlugin.bbcode.set(
  336. 'green', {
  337. html: '<font color="green">{0}</font>'
  338. }
  339. );
  340. $.sceditorBBCodePlugin.bbcode.set(
  341. 'red', {
  342. html: '<font color="red">{0}</font>'
  343. }
  344. );
  345. $.sceditorBBCodePlugin.bbcode.set(
  346. 'white', {
  347. html: '<font color="white">{0}</font>'
  348. }
  349. );
  350. $.sceditorBBCodePlugin.bbcode.set(
  351. 'list', {
  352. breakStart: true,
  353. isInline: false,
  354. allowedChildren: ['*', 'li'],
  355. html: function(element, attrs, content) {
  356. var style = '';
  357. var code = 'ul';
  358. if (attrs.type)
  359. style = ' style="list-style-type: ' + attrs.type + '"';
  360. return '<' + code + style + '>' + content + '</' + code + '>';
  361. }
  362. }
  363. );
  364. $.sceditorBBCodePlugin.bbcode.set(
  365. 'ul', {
  366. tags: {
  367. ul: null
  368. },
  369. breakStart: true,
  370. isInline: false,
  371. html: '<ul>{0}</ul>',
  372. format: function(element, content) {
  373. if ($(element[0]).css('list-style-type') == 'disc')
  374. return '[list]' + content + '[/list]';
  375. else
  376. return '[list type=' + $(element[0]).css('list-style-type') + ']' + content + '[/list]';
  377. }
  378. }
  379. );
  380. $.sceditorBBCodePlugin.bbcode.set(
  381. 'ol', {
  382. tags: {
  383. ol: null
  384. },
  385. breakStart: true,
  386. isInline: false,
  387. format: "[list type=decimal]{0}[/list]",
  388. html: '<ol>{0}</ol>'
  389. }
  390. );
  391. $.sceditorBBCodePlugin.bbcode.set(
  392. 'img', {
  393. tags: {
  394. img: {
  395. src: null
  396. }
  397. },
  398. allowsEmpty: true,
  399. quoteType: $.sceditor.BBCodeParser.QuoteType.never,
  400. format: function(element, content) {
  401. var attribs = '',
  402. style = function(name) {
  403. return element.style ? element.style[name] : null;
  404. };
  405. // check if this is an emoticon image
  406. if(typeof element.attr('data-sceditor-emoticon') !== "undefined")
  407. return content;
  408. // only add width and height if one is specified
  409. if(element.attr('width') || style('width'))
  410. attribs += " width=" + $(element).width();
  411. if(element.attr('height') || style('height'))
  412. attribs += " height=" + $(element).height();
  413. if(element.attr('alt'))
  414. attribs += " alt=" + element.attr('alt');
  415. return '[img' + attribs + ']' + element.attr('src') + '[/img]';
  416. },
  417. html: function(token, attrs, content) {
  418. var parts,
  419. attribs = '';
  420. // handle [img width=340 height=240]url[/img]
  421. if(typeof attrs.width !== "undefined")
  422. attribs += ' width="' + attrs.width + '"';
  423. if(typeof attrs.height !== "undefined")
  424. attribs += ' height="' + attrs.height + '"';
  425. if(typeof attrs.alt !== "undefined")
  426. attribs += ' alt="' + attrs.alt + '"';
  427. return '<img' + attribs + ' src="' + content + '">';
  428. }
  429. }
  430. );
  431. $.sceditorBBCodePlugin.bbcode.set(
  432. 'url', {
  433. allowsEmpty: true,
  434. tags: {
  435. a: {
  436. href: null
  437. }
  438. },
  439. format: function(element, content) {
  440. var url = element.attr('href');
  441. // make sure this link is not an e-mail, if it is return e-mail BBCode
  442. if(url.substr(0, 7) === 'mailto:')
  443. return '[email=' + url.substr(7) + ']' + content + '[/email]';
  444. // make sure this link is not an ftp, if it is return ftp BBCode
  445. else if(url.substr(0, 3) === 'ftp')
  446. return '[ftp=' + url + ']' + content + '[/ftp]';
  447. if(element.attr('target') !== undefined)
  448. return '[url=' + decodeURI(url) + ']' + content + '[/url]';
  449. else
  450. return '[iurl=' + decodeURI(url) + ']' + content + '[/iurl]';
  451. },
  452. html: function(token, attrs, content) {
  453. if(typeof attrs.defaultattr === "undefined" || attrs.defaultattr.length === 0)
  454. attrs.defaultattr = content;
  455. return '<a target="_blank" href="' + encodeURI(attrs.defaultattr) + '">' + content + '</a>';
  456. }
  457. }
  458. );
  459. $.sceditorBBCodePlugin.bbcode.set(
  460. 'iurl', {
  461. allowsEmpty: true,
  462. html: function(token, attrs, content) {
  463. if(typeof attrs.defaultattr === "undefined" || attrs.defaultattr.length === 0)
  464. attrs.defaultattr = content;
  465. return '<a href="' + encodeURI(attrs.defaultattr) + '">' + content + '</a>';
  466. }
  467. }
  468. );
  469. $.sceditorBBCodePlugin.bbcode.set(
  470. 'ftp', {
  471. allowsEmpty: true,
  472. html: function(token, attrs, content) {
  473. if(typeof attrs.defaultattr === "undefined" || attrs.defaultattr.length === 0)
  474. attrs.defaultattr = content;
  475. return '<a target="_blank" href="' + encodeURI(attrs.defaultattr) + '">' + content + '</a>';
  476. }
  477. }
  478. );
  479. $.sceditorBBCodePlugin.bbcode.set(
  480. 'tt', {
  481. tags: {
  482. tt: null
  483. },
  484. format: "[tt]{0}[/tt]",
  485. html: '<tt>{0}</tt>'
  486. }
  487. );
  488. $.sceditorBBCodePlugin.bbcode.set(
  489. 'pre', {
  490. tags: {
  491. pre: null
  492. },
  493. isBlock: true,
  494. format: "[pre]{0}[/pre]",
  495. html: "<pre>{0}</pre>\n"
  496. }
  497. );
  498. $.sceditorBBCodePlugin.bbcode.set(
  499. 'move', {
  500. tags: {
  501. marquee: null
  502. },
  503. format: "[move]{0}[/move]",
  504. html: '<marquee>{0}</marquee>'
  505. }
  506. );
  507. $.sceditorBBCodePlugin.bbcode.set(
  508. 'php', {
  509. isInline: false,
  510. format: "[php]{0}[/php]",
  511. html: '<code class="php">{0}</code>'
  512. }
  513. );
  514. $.sceditorBBCodePlugin.bbcode.set(
  515. 'code', {
  516. tags: {
  517. code: null
  518. },
  519. isInline: false,
  520. allowedChildren: ['#', '#newline'],
  521. format: function(element, content) {
  522. if ($(element[0]).hasClass('php'))
  523. return '[php]' + content.replace('&#91;', '[') + '[/php]';
  524. var from = '';
  525. if ($(element).children("cite:first").length === 1)
  526. {
  527. from = $(element).children("cite:first").text();
  528. $(element).attr({'from': from.php_htmlspecialchars()});
  529. from = '=' + from;
  530. content = '';
  531. $(element).children("cite:first").remove();
  532. content = this.elementToBbcode($(element));
  533. }
  534. else
  535. {
  536. if (typeof $(element).attr('from') != 'undefined')
  537. {
  538. from = '=' + $(element).attr('from').php_unhtmlspecialchars();
  539. }
  540. }
  541. return '[code' + from + ']' + content.replace('&#91;', '[') + '[/code]';
  542. },
  543. html: function(element, attrs, content) {
  544. var from = '';
  545. if(typeof attrs.defaultattr !== "undefined")
  546. from = '<cite>' + attrs.defaultattr + '</cite>';
  547. return '<code>' + from + content.replace('[', '&#91;') + '</code>'
  548. }
  549. }
  550. );
  551. $.sceditorBBCodePlugin.bbcode.set(
  552. 'quote', {
  553. tags: {
  554. blockquote: null,
  555. cite: null
  556. },
  557. quoteType: $.sceditor.BBCodeParser.QuoteType.never,
  558. breakBefore: false,
  559. isInline: false,
  560. format: function(element, content) {
  561. var author = '';
  562. var date = '';
  563. var link = '';
  564. // The <cite> contains only the graphic for the quote, so we can skip it
  565. if(element[0].tagName.toLowerCase() === 'cite')
  566. return '';
  567. if(element.attr('author'))
  568. author = ' author=' + element.attr('author').php_unhtmlspecialchars();
  569. if(element.attr('date'))
  570. date = ' date=' + element.attr('date');
  571. if(element.attr('link'))
  572. link = ' link=' + element.attr('link');
  573. return '[quote' + author + date + link + ']' + content + '[/quote]';
  574. },
  575. html: function(element, attrs, content) {
  576. var attr_author = '', author = '';
  577. var attr_date = '', sDate = '';
  578. var attr_link = '', link = '';
  579. if(typeof attrs.author !== "undefined" && attrs.author)
  580. {
  581. attr_author = attrs.author;
  582. author = bbc_quote_from + ': ' + attr_author;
  583. }
  584. // Links could be in the form: link=topic=71.msg201#msg201 that would fool javascript, so we need a workaround
  585. // Probably no more necessary
  586. for (var key in attrs)
  587. {
  588. if (key.substr(0, 4) == 'link' && attrs.hasOwnProperty(key))
  589. {
  590. var attr_link = key.length > 4 ? key.substr(5) + '=' + attrs[key] : attrs[key];
  591. link = attr_link.substr(0, 7) == 'http://' ? attr_link : smf_scripturl + '?' + attr_link;
  592. author = author == '' ? '<a href="' + link + '">' + bbc_quote_from + ': ' + link + '</a>' : '<a href="' + link + '">' + author + '</a>';
  593. }
  594. }
  595. if(typeof attrs.date !== "undefined" && attrs.date)
  596. {
  597. attr_date = attrs.date;
  598. sDate = '<date timestamp="' + attr_date + '">' + new Date(attrs.date * 1000) + '</date>';
  599. }
  600. if (author == '' && sDate == '')
  601. author = bbc_quote;
  602. else
  603. author += ' ' + bbc_search_on;
  604. content = '<blockquote author="' + attr_author + '" date="' + attr_date + '" link="' + attr_link + '"><cite>' + author + ' ' + sDate + '</cite>' + content + '</blockquote>';
  605. return content;
  606. }
  607. }
  608. );