bootstrap3-wysihtml5.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /* jshint expr: true */
  2. !(function($, wysi) {
  3. 'use strict';
  4. var templates = function(key, locale, options) {
  5. return wysi.tpl[key]({locale: locale, options: options});
  6. };
  7. var Wysihtml5 = function(el, options) {
  8. this.el = el;
  9. var toolbarOpts = options || defaultOptions;
  10. for(var t in toolbarOpts.customTemplates) {
  11. wysi.tpl[t] = toolbarOpts.customTemplates[t];
  12. }
  13. this.toolbar = this.createToolbar(el, toolbarOpts);
  14. this.editor = this.createEditor(options);
  15. window.editor = this.editor;
  16. $('iframe.wysihtml5-sandbox').each(function(i, el){
  17. $(el.contentWindow).off('focus.wysihtml5').on({
  18. 'focus.wysihtml5' : function(){
  19. $('li.dropdown').removeClass('open');
  20. }
  21. });
  22. });
  23. };
  24. Wysihtml5.prototype = {
  25. constructor: Wysihtml5,
  26. createEditor: function(options) {
  27. options = options || {};
  28. // Add the toolbar to a clone of the options object so multiple instances
  29. // of the WYISYWG don't break because 'toolbar' is already defined
  30. options = $.extend(true, {}, options);
  31. options.toolbar = this.toolbar[0];
  32. var editor = new wysi.Editor(this.el[0], options);
  33. if(options && options.events) {
  34. for(var eventName in options.events) {
  35. editor.on(eventName, options.events[eventName]);
  36. }
  37. }
  38. return editor;
  39. },
  40. createToolbar: function(el, options) {
  41. var self = this;
  42. var toolbar = $('<ul/>', {
  43. 'class' : 'wysihtml5-toolbar',
  44. 'style': 'display:none'
  45. });
  46. var culture = options.locale || defaultOptions.locale || 'en';
  47. for(var key in defaultOptions) {
  48. var value = false;
  49. if(options[key] !== undefined) {
  50. if(options[key] === true) {
  51. value = true;
  52. }
  53. } else {
  54. value = defaultOptions[key];
  55. }
  56. if(value === true) {
  57. toolbar.append(templates(key, locale[culture], options));
  58. if(key === 'html') {
  59. this.initHtml(toolbar);
  60. }
  61. if(key === 'link') {
  62. this.initInsertLink(toolbar);
  63. }
  64. if(key === 'image') {
  65. this.initInsertImage(toolbar);
  66. }
  67. }
  68. }
  69. if(options.toolbar) {
  70. for(key in options.toolbar) {
  71. toolbar.append(options.toolbar[key]);
  72. }
  73. }
  74. toolbar.find('a[data-wysihtml5-command="formatBlock"]').click(function(e) {
  75. var target = e.target || e.srcElement;
  76. var el = $(target);
  77. self.toolbar.find('.current-font').text(el.html());
  78. });
  79. toolbar.find('a[data-wysihtml5-command="foreColor"]').click(function(e) {
  80. var target = e.target || e.srcElement;
  81. var el = $(target);
  82. self.toolbar.find('.current-color').text(el.html());
  83. });
  84. this.el.before(toolbar);
  85. return toolbar;
  86. },
  87. initHtml: function(toolbar) {
  88. var changeViewSelector = 'a[data-wysihtml5-action="change_view"]';
  89. toolbar.find(changeViewSelector).click(function(e) {
  90. toolbar.find('a.btn').not(changeViewSelector).toggleClass('disabled');
  91. });
  92. },
  93. initInsertImage: function(toolbar) {
  94. var self = this;
  95. var insertImageModal = toolbar.find('.bootstrap-wysihtml5-insert-image-modal');
  96. var urlInput = insertImageModal.find('.bootstrap-wysihtml5-insert-image-url');
  97. var insertButton = insertImageModal.find('a.btn-primary');
  98. var initialValue = urlInput.val();
  99. var caretBookmark;
  100. var insertImage = function() {
  101. var url = urlInput.val();
  102. urlInput.val(initialValue);
  103. self.editor.currentView.element.focus();
  104. if (caretBookmark) {
  105. self.editor.composer.selection.setBookmark(caretBookmark);
  106. caretBookmark = null;
  107. }
  108. self.editor.composer.commands.exec('insertImage', url);
  109. };
  110. urlInput.keypress(function(e) {
  111. if(e.which == 13) {
  112. insertImage();
  113. insertImageModal.modal('hide');
  114. }
  115. });
  116. insertButton.click(insertImage);
  117. insertImageModal.on('shown', function() {
  118. urlInput.focus();
  119. });
  120. insertImageModal.on('hide', function() {
  121. self.editor.currentView.element.focus();
  122. });
  123. toolbar.find('a[data-wysihtml5-command=insertImage]').click(function() {
  124. var activeButton = $(this).hasClass('wysihtml5-command-active');
  125. if (!activeButton) {
  126. self.editor.currentView.element.focus(false);
  127. caretBookmark = self.editor.composer.selection.getBookmark();
  128. insertImageModal.appendTo('body').modal('show');
  129. insertImageModal.on('click.dismiss.modal', '[data-dismiss="modal"]', function(e) {
  130. e.stopPropagation();
  131. });
  132. return false;
  133. }
  134. else {
  135. return true;
  136. }
  137. });
  138. },
  139. initInsertLink: function(toolbar) {
  140. var self = this;
  141. var insertLinkModal = toolbar.find('.bootstrap-wysihtml5-insert-link-modal');
  142. var urlInput = insertLinkModal.find('.bootstrap-wysihtml5-insert-link-url');
  143. var targetInput = insertLinkModal.find('.bootstrap-wysihtml5-insert-link-target');
  144. var insertButton = insertLinkModal.find('a.btn-primary');
  145. var initialValue = urlInput.val();
  146. var caretBookmark;
  147. var insertLink = function() {
  148. var url = urlInput.val();
  149. urlInput.val(initialValue);
  150. self.editor.currentView.element.focus();
  151. if (caretBookmark) {
  152. self.editor.composer.selection.setBookmark(caretBookmark);
  153. caretBookmark = null;
  154. }
  155. var newWindow = targetInput.prop('checked');
  156. self.editor.composer.commands.exec('createLink', {
  157. 'href' : url,
  158. 'target' : (newWindow ? '_blank' : '_self'),
  159. 'rel' : (newWindow ? 'nofollow' : '')
  160. });
  161. };
  162. var pressedEnter = false;
  163. urlInput.keypress(function(e) {
  164. if(e.which == 13) {
  165. insertLink();
  166. insertLinkModal.modal('hide');
  167. }
  168. });
  169. insertButton.click(insertLink);
  170. insertLinkModal.on('shown', function() {
  171. urlInput.focus();
  172. });
  173. insertLinkModal.on('hide', function() {
  174. self.editor.currentView.element.focus();
  175. });
  176. toolbar.find('a[data-wysihtml5-command=createLink]').click(function() {
  177. var activeButton = $(this).hasClass('wysihtml5-command-active');
  178. if (!activeButton) {
  179. self.editor.currentView.element.focus(false);
  180. caretBookmark = self.editor.composer.selection.getBookmark();
  181. insertLinkModal.appendTo('body').modal('show');
  182. insertLinkModal.on('click.dismiss.modal', '[data-dismiss="modal"]', function(e) {
  183. e.stopPropagation();
  184. });
  185. return false;
  186. }
  187. else {
  188. return true;
  189. }
  190. });
  191. }
  192. };
  193. // these define our public api
  194. var methods = {
  195. resetDefaults: function() {
  196. $.fn.wysihtml5.defaultOptions = $.extend(true, {}, $.fn.wysihtml5.defaultOptionsCache);
  197. },
  198. bypassDefaults: function(options) {
  199. return this.each(function () {
  200. var $this = $(this);
  201. $this.data('wysihtml5', new Wysihtml5($this, options));
  202. });
  203. },
  204. shallowExtend: function (options) {
  205. var settings = $.extend({}, $.fn.wysihtml5.defaultOptions, options || {}, $(this).data());
  206. var that = this;
  207. return methods.bypassDefaults.apply(that, [settings]);
  208. },
  209. deepExtend: function(options) {
  210. var settings = $.extend(true, {}, $.fn.wysihtml5.defaultOptions, options || {});
  211. var that = this;
  212. return methods.bypassDefaults.apply(that, [settings]);
  213. },
  214. init: function(options) {
  215. var that = this;
  216. return methods.shallowExtend.apply(that, [options]);
  217. }
  218. };
  219. $.fn.wysihtml5 = function ( method ) {
  220. if ( methods[method] ) {
  221. return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
  222. } else if ( typeof method === 'object' || ! method ) {
  223. return methods.init.apply( this, arguments );
  224. } else {
  225. $.error( 'Method ' + method + ' does not exist on jQuery.wysihtml5' );
  226. }
  227. };
  228. $.fn.wysihtml5.Constructor = Wysihtml5;
  229. var defaultOptions = $.fn.wysihtml5.defaultOptions = {
  230. 'font-styles': true,
  231. 'color': false,
  232. 'emphasis': true,
  233. 'lists': true,
  234. 'html': false,
  235. 'link': true,
  236. 'image': true,
  237. events: {},
  238. parserRules: {
  239. classes: {
  240. 'wysiwyg-color-silver' : 1,
  241. 'wysiwyg-color-gray' : 1,
  242. 'wysiwyg-color-white' : 1,
  243. 'wysiwyg-color-maroon' : 1,
  244. 'wysiwyg-color-red' : 1,
  245. 'wysiwyg-color-purple' : 1,
  246. 'wysiwyg-color-fuchsia' : 1,
  247. 'wysiwyg-color-green' : 1,
  248. 'wysiwyg-color-lime' : 1,
  249. 'wysiwyg-color-olive' : 1,
  250. 'wysiwyg-color-yellow' : 1,
  251. 'wysiwyg-color-navy' : 1,
  252. 'wysiwyg-color-blue' : 1,
  253. 'wysiwyg-color-teal' : 1,
  254. 'wysiwyg-color-aqua' : 1,
  255. 'wysiwyg-color-orange' : 1
  256. },
  257. tags: {
  258. 'b': {},
  259. 'i': {},
  260. 'strong': {},
  261. 'em': {},
  262. 'p': {},
  263. 'br': {},
  264. 'ol': {},
  265. 'ul': {},
  266. 'li': {},
  267. 'h1': {},
  268. 'h2': {},
  269. 'h3': {},
  270. 'h4': {},
  271. 'h5': {},
  272. 'h6': {},
  273. 'blockquote': {},
  274. 'u': 1,
  275. 'img': {
  276. 'check_attributes': {
  277. 'width': 'numbers',
  278. 'alt': 'alt',
  279. 'src': 'url',
  280. 'height': 'numbers'
  281. }
  282. },
  283. 'a': {
  284. check_attributes: {
  285. 'href': 'url' // important to avoid XSS
  286. },
  287. 'set_attributes': {
  288. 'target': '_blank',
  289. 'rel': 'nofollow'
  290. }
  291. },
  292. 'span': 1,
  293. 'div': 1,
  294. // to allow save and edit files with code tag hacks
  295. 'code': 1,
  296. 'pre': 1
  297. }
  298. },
  299. locale: 'en'
  300. };
  301. if (typeof $.fn.wysihtml5.defaultOptionsCache === 'undefined') {
  302. $.fn.wysihtml5.defaultOptionsCache = $.extend(true, {}, $.fn.wysihtml5.defaultOptions);
  303. }
  304. var locale = $.fn.wysihtml5.locale = {};
  305. })(window.jQuery, window.wysihtml5);