bootstrap-wysihtml5.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. !function($, wysi) {
  2. "use strict";
  3. var tpl = {
  4. "font-styles": function(locale, options) {
  5. var size = (options && options.size) ? ' btn-'+options.size : '';
  6. return "<li class='dropdown'>" +
  7. "<a class='btn dropdown-toggle" + size + "' data-toggle='dropdown' href='#'>" +
  8. "<i class='icon-font'></i>&nbsp;<span class='current-font'>" + locale.font_styles.normal + "</span>&nbsp;<b class='caret'></b>" +
  9. "</a>" +
  10. "<ul class='dropdown-menu'>" +
  11. "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='div'>" + locale.font_styles.normal + "</a></li>" +
  12. "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h1'>" + locale.font_styles.h1 + "</a></li>" +
  13. "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h2'>" + locale.font_styles.h2 + "</a></li>" +
  14. "<li><a data-wysihtml5-command='formatBlock' data-wysihtml5-command-value='h3'>" + locale.font_styles.h3 + "</a></li>" +
  15. "</ul>" +
  16. "</li>";
  17. },
  18. "emphasis": function(locale, options) {
  19. var size = (options && options.size) ? ' btn-'+options.size : '';
  20. return "<li>" +
  21. "<div class='btn-group'>" +
  22. "<a class='btn" + size + "' data-wysihtml5-command='bold' title='CTRL+B'>" + locale.emphasis.bold + "</a>" +
  23. "<a class='btn" + size + "' data-wysihtml5-command='italic' title='CTRL+I'>" + locale.emphasis.italic + "</a>" +
  24. "<a class='btn" + size + "' data-wysihtml5-command='underline' title='CTRL+U'>" + locale.emphasis.underline + "</a>" +
  25. "</div>" +
  26. "</li>";
  27. },
  28. "lists": function(locale, options) {
  29. var size = (options && options.size) ? ' btn-'+options.size : '';
  30. return "<li>" +
  31. "<div class='btn-group'>" +
  32. "<a class='btn" + size + "' data-wysihtml5-command='insertUnorderedList' title='" + locale.lists.unordered + "'><i class='icon-list'></i></a>" +
  33. "<a class='btn" + size + "' data-wysihtml5-command='insertOrderedList' title='" + locale.lists.ordered + "'><i class='icon-th-list'></i></a>" +
  34. "<a class='btn" + size + "' data-wysihtml5-command='Outdent' title='" + locale.lists.outdent + "'><i class='icon-indent-right'></i></a>" +
  35. "<a class='btn" + size + "' data-wysihtml5-command='Indent' title='" + locale.lists.indent + "'><i class='icon-indent-left'></i></a>" +
  36. "</div>" +
  37. "</li>";
  38. },
  39. "link": function(locale, options) {
  40. var size = (options && options.size) ? ' btn-'+options.size : '';
  41. return "<li>" +
  42. "<div class='bootstrap-wysihtml5-insert-link-modal modal hide fade'>" +
  43. "<div class='modal-header'>" +
  44. "<a class='close' data-dismiss='modal'></a>" +
  45. "<h3>" + locale.link.insert + "</h3>" +
  46. "</div>" +
  47. "<div class='modal-body'>" +
  48. "<input value='http://' class='bootstrap-wysihtml5-insert-link-url m-wrap large' type='text'>" +
  49. "</div>" +
  50. "<div class='modal-footer'>" +
  51. "<a href='#' class='btn' data-dismiss='modal'>" + locale.link.cancel + "</a>" +
  52. "<a href='#' class='btn green btn-primary' data-dismiss='modal'>" + locale.link.insert + "</a>" +
  53. "</div>" +
  54. "</div>" +
  55. "<a class='btn" + size + "' data-wysihtml5-command='createLink' title='" + locale.link.insert + "'><i class='icon-share'></i></a>" +
  56. "</li>";
  57. },
  58. "image": function(locale, options) {
  59. var size = (options && options.size) ? ' btn-'+options.size : '';
  60. return "<li>" +
  61. "<div class='bootstrap-wysihtml5-insert-image-modal modal hide fade'>" +
  62. "<div class='modal-header'>" +
  63. "<a class='close' data-dismiss='modal'></a>" +
  64. "<h3>" + locale.image.insert + "</h3>" +
  65. "</div>" +
  66. "<div class='modal-body'>" +
  67. "<input value='http://' class='bootstrap-wysihtml5-insert-image-url m-wrap large' type='text'>" +
  68. "</div>" +
  69. "<div class='modal-footer'>" +
  70. "<a href='#' class='btn' data-dismiss='modal'>" + locale.image.cancel + "</a>" +
  71. "<a href='#' class='btn green btn-primary' data-dismiss='modal'>" + locale.image.insert + "</a>" +
  72. "</div>" +
  73. "</div>" +
  74. "<a class='btn" + size + "' data-wysihtml5-command='insertImage' title='" + locale.image.insert + "'><i class='icon-picture'></i></a>" +
  75. "</li>";
  76. },
  77. "html": function(locale, options) {
  78. var size = (options && options.size) ? ' btn-'+options.size : '';
  79. return "<li>" +
  80. "<div class='btn-group'>" +
  81. "<a class='btn" + size + "' data-wysihtml5-action='change_view' title='" + locale.html.edit + "'><i class='icon-pencil'></i></a>" +
  82. "</div>" +
  83. "</li>";
  84. },
  85. "color": function(locale, options) {
  86. var size = (options && options.size) ? ' btn-'+options.size : '';
  87. return "<li class='dropdown'>" +
  88. "<a class='btn dropdown-toggle" + size + "' data-toggle='dropdown' href='#'>" +
  89. "<span class='current-color'>" + locale.colours.black + "</span>&nbsp;<b class='caret'></b>" +
  90. "</a>" +
  91. "<ul class='dropdown-menu'>" +
  92. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='black'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='black'>" + locale.colours.black + "</a></li>" +
  93. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='silver'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='silver'>" + locale.colours.silver + "</a></li>" +
  94. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='gray'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='gray'>" + locale.colours.gray + "</a></li>" +
  95. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='maroon'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='maroon'>" + locale.colours.maroon + "</a></li>" +
  96. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='red'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='red'>" + locale.colours.red + "</a></li>" +
  97. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='purple'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='purple'>" + locale.colours.purple + "</a></li>" +
  98. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='green'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='green'>" + locale.colours.green + "</a></li>" +
  99. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='olive'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='olive'>" + locale.colours.olive + "</a></li>" +
  100. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='navy'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='navy'>" + locale.colours.navy + "</a></li>" +
  101. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='blue'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='blue'>" + locale.colours.blue + "</a></li>" +
  102. "<li><div class='wysihtml5-colors' data-wysihtml5-command-value='orange'></div><a class='wysihtml5-colors-title' data-wysihtml5-command='foreColor' data-wysihtml5-command-value='orange'>" + locale.colours.orange + "</a></li>" +
  103. "</ul>" +
  104. "</li>";
  105. }
  106. };
  107. var templates = function(key, locale, options) {
  108. return tpl[key](locale, options);
  109. };
  110. var Wysihtml5 = function(el, options) {
  111. this.el = el;
  112. var toolbarOpts = options || defaultOptions;
  113. for(var t in toolbarOpts.customTemplates) {
  114. tpl[t] = toolbarOpts.customTemplates[t];
  115. }
  116. this.toolbar = this.createToolbar(el, toolbarOpts);
  117. this.editor = this.createEditor(options);
  118. window.editor = this.editor;
  119. $('iframe.wysihtml5-sandbox').each(function(i, el){
  120. $(el.contentWindow).off('focus.wysihtml5').on({
  121. 'focus.wysihtml5' : function(){
  122. $('li.dropdown').removeClass('open');
  123. }
  124. });
  125. });
  126. };
  127. Wysihtml5.prototype = {
  128. constructor: Wysihtml5,
  129. createEditor: function(options) {
  130. options = options || {};
  131. options.toolbar = this.toolbar[0];
  132. var editor = new wysi.Editor(this.el[0], options);
  133. if(options && options.events) {
  134. for(var eventName in options.events) {
  135. editor.on(eventName, options.events[eventName]);
  136. }
  137. }
  138. return editor;
  139. },
  140. createToolbar: function(el, options) {
  141. var self = this;
  142. var toolbar = $("<ul/>", {
  143. 'class' : "wysihtml5-toolbar",
  144. 'style': "display:none"
  145. });
  146. var culture = options.locale || defaultOptions.locale || "en";
  147. for(var key in defaultOptions) {
  148. var value = false;
  149. if(options[key] !== undefined) {
  150. if(options[key] === true) {
  151. value = true;
  152. }
  153. } else {
  154. value = defaultOptions[key];
  155. }
  156. if(value === true) {
  157. toolbar.append(templates(key, locale[culture], options));
  158. if(key === "html") {
  159. this.initHtml(toolbar);
  160. }
  161. if(key === "link") {
  162. this.initInsertLink(toolbar);
  163. }
  164. if(key === "image") {
  165. this.initInsertImage(toolbar);
  166. }
  167. }
  168. }
  169. if(options.toolbar) {
  170. for(key in options.toolbar) {
  171. toolbar.append(options.toolbar[key]);
  172. }
  173. }
  174. toolbar.find("a[data-wysihtml5-command='formatBlock']").click(function(e) {
  175. var target = e.target || e.srcElement;
  176. var el = $(target);
  177. self.toolbar.find('.current-font').text(el.html());
  178. });
  179. toolbar.find("a[data-wysihtml5-command='foreColor']").click(function(e) {
  180. var target = e.target || e.srcElement;
  181. var el = $(target);
  182. self.toolbar.find('.current-color').text(el.html());
  183. });
  184. this.el.before(toolbar);
  185. return toolbar;
  186. },
  187. initHtml: function(toolbar) {
  188. var changeViewSelector = "a[data-wysihtml5-action='change_view']";
  189. toolbar.find(changeViewSelector).click(function(e) {
  190. toolbar.find('a.btn').not(changeViewSelector).toggleClass('disabled');
  191. });
  192. },
  193. initInsertImage: function(toolbar) {
  194. var self = this;
  195. var insertImageModal = toolbar.find('.bootstrap-wysihtml5-insert-image-modal');
  196. var urlInput = insertImageModal.find('.bootstrap-wysihtml5-insert-image-url');
  197. var insertButton = insertImageModal.find('a.btn-primary');
  198. var initialValue = urlInput.val();
  199. var insertImage = function() {
  200. var url = urlInput.val();
  201. urlInput.val(initialValue);
  202. self.editor.currentView.element.focus();
  203. self.editor.composer.commands.exec("insertImage", url);
  204. };
  205. urlInput.keypress(function(e) {
  206. if(e.which == 13) {
  207. insertImage();
  208. insertImageModal.modal('hide');
  209. }
  210. });
  211. insertButton.click(insertImage);
  212. insertImageModal.on('shown', function() {
  213. urlInput.focus();
  214. });
  215. insertImageModal.on('hide', function() {
  216. self.editor.currentView.element.focus();
  217. });
  218. toolbar.find('a[data-wysihtml5-command=insertImage]').click(function() {
  219. var activeButton = $(this).hasClass("wysihtml5-command-active");
  220. if (!activeButton) {
  221. insertImageModal.modal('show');
  222. insertImageModal.on('click.dismiss.modal', '[data-dismiss="modal"]', function(e) {
  223. e.stopPropagation();
  224. });
  225. return false;
  226. }
  227. else {
  228. return true;
  229. }
  230. });
  231. },
  232. initInsertLink: function(toolbar) {
  233. var self = this;
  234. var insertLinkModal = toolbar.find('.bootstrap-wysihtml5-insert-link-modal');
  235. var urlInput = insertLinkModal.find('.bootstrap-wysihtml5-insert-link-url');
  236. var insertButton = insertLinkModal.find('a.btn-primary');
  237. var initialValue = urlInput.val();
  238. var insertLink = function() {
  239. var url = urlInput.val();
  240. urlInput.val(initialValue);
  241. self.editor.currentView.element.focus();
  242. self.editor.composer.commands.exec("createLink", {
  243. href: url,
  244. target: "_blank",
  245. rel: "nofollow"
  246. });
  247. };
  248. var pressedEnter = false;
  249. urlInput.keypress(function(e) {
  250. if(e.which == 13) {
  251. insertLink();
  252. insertLinkModal.modal('hide');
  253. }
  254. });
  255. insertButton.click(insertLink);
  256. insertLinkModal.on('shown', function() {
  257. urlInput.focus();
  258. });
  259. insertLinkModal.on('hide', function() {
  260. self.editor.currentView.element.focus();
  261. });
  262. toolbar.find('a[data-wysihtml5-command=createLink]').click(function() {
  263. var activeButton = $(this).hasClass("wysihtml5-command-active");
  264. if (!activeButton) {
  265. insertLinkModal.appendTo('body').modal('show');
  266. insertLinkModal.on('click.dismiss.modal', '[data-dismiss="modal"]', function(e) {
  267. e.stopPropagation();
  268. });
  269. return false;
  270. }
  271. else {
  272. return true;
  273. }
  274. });
  275. }
  276. };
  277. // these define our public api
  278. var methods = {
  279. resetDefaults: function() {
  280. $.fn.wysihtml5.defaultOptions = $.extend(true, {}, $.fn.wysihtml5.defaultOptionsCache);
  281. },
  282. bypassDefaults: function(options) {
  283. return this.each(function () {
  284. var $this = $(this);
  285. $this.data('wysihtml5', new Wysihtml5($this, options));
  286. });
  287. },
  288. shallowExtend: function (options) {
  289. var settings = $.extend({}, $.fn.wysihtml5.defaultOptions, options || {});
  290. var that = this;
  291. return methods.bypassDefaults.apply(that, [settings]);
  292. },
  293. deepExtend: function(options) {
  294. var settings = $.extend(true, {}, $.fn.wysihtml5.defaultOptions, options || {});
  295. var that = this;
  296. return methods.bypassDefaults.apply(that, [settings]);
  297. },
  298. init: function(options) {
  299. var that = this;
  300. return methods.shallowExtend.apply(that, [options]);
  301. }
  302. };
  303. $.fn.wysihtml5 = function ( method ) {
  304. if ( methods[method] ) {
  305. return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
  306. } else if ( typeof method === 'object' || ! method ) {
  307. return methods.init.apply( this, arguments );
  308. } else {
  309. $.error( 'Method ' + method + ' does not exist on jQuery.wysihtml5' );
  310. }
  311. };
  312. $.fn.wysihtml5.Constructor = Wysihtml5;
  313. var defaultOptions = $.fn.wysihtml5.defaultOptions = {
  314. "font-styles": true,
  315. "color": false,
  316. "emphasis": true,
  317. "lists": true,
  318. "html": false,
  319. "link": true,
  320. "image": true,
  321. events: {},
  322. parserRules: {
  323. classes: {
  324. // (path_to_project/lib/css/wysiwyg-color.css)
  325. "wysiwyg-color-silver" : 1,
  326. "wysiwyg-color-gray" : 1,
  327. "wysiwyg-color-white" : 1,
  328. "wysiwyg-color-maroon" : 1,
  329. "wysiwyg-color-red" : 1,
  330. "wysiwyg-color-purple" : 1,
  331. "wysiwyg-color-fuchsia" : 1,
  332. "wysiwyg-color-green" : 1,
  333. "wysiwyg-color-lime" : 1,
  334. "wysiwyg-color-olive" : 1,
  335. "wysiwyg-color-yellow" : 1,
  336. "wysiwyg-color-navy" : 1,
  337. "wysiwyg-color-blue" : 1,
  338. "wysiwyg-color-teal" : 1,
  339. "wysiwyg-color-aqua" : 1,
  340. "wysiwyg-color-orange" : 1
  341. },
  342. tags: {
  343. "b": {},
  344. "i": {},
  345. "br": {},
  346. "ol": {},
  347. "ul": {},
  348. "li": {},
  349. "h1": {},
  350. "h2": {},
  351. "h3": {},
  352. "blockquote": {},
  353. "u": 1,
  354. "img": {
  355. "check_attributes": {
  356. "width": "numbers",
  357. "alt": "alt",
  358. "src": "url",
  359. "height": "numbers"
  360. }
  361. },
  362. "a": {
  363. set_attributes: {
  364. target: "_blank",
  365. rel: "nofollow"
  366. },
  367. check_attributes: {
  368. href: "url" // important to avoid XSS
  369. }
  370. },
  371. "span": 1,
  372. "div": 1
  373. }
  374. },
  375. stylesheets: ["./lib/css/wysiwyg-color.css"], // (path_to_project/lib/css/wysiwyg-color.css)
  376. locale: "en"
  377. };
  378. if (typeof $.fn.wysihtml5.defaultOptionsCache === 'undefined') {
  379. $.fn.wysihtml5.defaultOptionsCache = $.extend(true, {}, $.fn.wysihtml5.defaultOptions);
  380. }
  381. var locale = $.fn.wysihtml5.locale = {
  382. en: {
  383. font_styles: {
  384. normal: "Normal text",
  385. h1: "Heading 1",
  386. h2: "Heading 2",
  387. h3: "Heading 3"
  388. },
  389. emphasis: {
  390. bold: "Bold",
  391. italic: "Italic",
  392. underline: "Underline"
  393. },
  394. lists: {
  395. unordered: "Unordered list",
  396. ordered: "Ordered list",
  397. outdent: "Outdent",
  398. indent: "Indent"
  399. },
  400. link: {
  401. insert: "Insert link",
  402. cancel: "Cancel"
  403. },
  404. image: {
  405. insert: "Insert image",
  406. cancel: "Cancel"
  407. },
  408. html: {
  409. edit: "Edit HTML"
  410. },
  411. colours: {
  412. black: "Black",
  413. silver: "Silver",
  414. gray: "Grey",
  415. maroon: "Maroon",
  416. red: "Red",
  417. purple: "Purple",
  418. green: "Green",
  419. olive: "Olive",
  420. navy: "Navy",
  421. blue: "Blue",
  422. orange: "Orange"
  423. }
  424. }
  425. };
  426. }(window.jQuery, window.wysihtml5);