· 7 years ago · Jan 30, 2019, 10:20 AM
1/**
2 * Super simple wysiwyg editor v0.8.11
3 * https://summernote.org
4 *
5 * Copyright 2013- Alan Hong. and other contributors
6 * summernote may be freely distributed under the MIT license.
7 *
8 * Date: 2018-11-24T12:13Z
9 */
10(function (global, factory) {
11 typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery')) :
12 typeof define === 'function' && define.amd ? define(['jquery'], factory) :
13 (factory(global.jQuery));
14}(this, (function ($$1) { 'use strict';
15
16 $$1 = $$1 && $$1.hasOwnProperty('default') ? $$1['default'] : $$1;
17
18 var Renderer = /** @class */ (function () {
19 function Renderer(markup, children, options, callback) {
20 this.markup = markup;
21 this.children = children;
22 this.options = options;
23 this.callback = callback;
24 }
25 Renderer.prototype.render = function ($parent) {
26 var $node = $$1(this.markup);
27 if (this.options && this.options.contents) {
28 $node.html(this.options.contents);
29 }
30 if (this.options && this.options.className) {
31 $node.addClass(this.options.className);
32 }
33 if (this.options && this.options.data) {
34 $$1.each(this.options.data, function (k, v) {
35 $node.attr('data-' + k, v);
36 });
37 }
38 if (this.options && this.options.click) {
39 $node.on('click', this.options.click);
40 }
41 if (this.children) {
42 var $container_1 = $node.find('.note-children-container');
43 this.children.forEach(function (child) {
44 child.render($container_1.length ? $container_1 : $node);
45 });
46 }
47 if (this.callback) {
48 this.callback($node, this.options);
49 }
50 if (this.options && this.options.callback) {
51 this.options.callback($node);
52 }
53 if ($parent) {
54 $parent.append($node);
55 }
56 return $node;
57 };
58 return Renderer;
59 }());
60 var renderer = {
61 create: function (markup, callback) {
62 return function () {
63 var options = typeof arguments[1] === 'object' ? arguments[1] : arguments[0];
64 var children = $$1.isArray(arguments[0]) ? arguments[0] : [];
65 if (options && options.children) {
66 children = options.children;
67 }
68 return new Renderer(markup, children, options, callback);
69 };
70 }
71 };
72
73 var editor = renderer.create('<div class="note-editor note-frame card"/>');
74 var toolbar = renderer.create('<div class="note-toolbar card-header" role="toolbar"></div>');
75 var editingArea = renderer.create('<div class="note-editing-area"/>');
76 var codable = renderer.create('<textarea class="note-codable" role="textbox" aria-multiline="true"/>');
77 var editable = renderer.create('<div class="note-editable card-block" contentEditable="true" role="textbox" aria-multiline="true"/>');
78 var statusbar = renderer.create([
79 '<output class="note-status-output" aria-live="polite"/>',
80 '<div class="note-statusbar" role="status">',
81 ' <output class="note-status-output" aria-live="polite"></output>',
82 ' <div class="note-resizebar" role="seperator" aria-orientation="horizontal" aria-label="Resize">',
83 ' <div class="note-icon-bar"/>',
84 ' <div class="note-icon-bar"/>',
85 ' <div class="note-icon-bar"/>',
86 ' </div>',
87 '</div>'
88 ].join(''));
89 var airEditor = renderer.create('<div class="note-editor"/>');
90 var airEditable = renderer.create([
91 '<div class="note-editable" contentEditable="true" role="textbox" aria-multiline="true"/>',
92 '<output class="note-status-output" aria-live="polite"/>'
93 ].join(''));
94 var buttonGroup = renderer.create('<div class="note-btn-group btn-group">');
95 var dropdown = renderer.create('<div class="dropdown-menu" role="list">', function ($node, options) {
96 var markup = $$1.isArray(options.items) ? options.items.map(function (item) {
97 var value = (typeof item === 'string') ? item : (item.value || '');
98 var content = options.template ? options.template(item) : item;
99 var option = (typeof item === 'object') ? item.option : undefined;
100 var dataValue = 'data-value="' + value + '"';
101 var dataOption = (option !== undefined) ? ' data-option="' + option + '"' : '';
102 return '<a class="dropdown-item" href="#" ' + (dataValue + dataOption) + ' role="listitem" aria-label="' + item + '">' + content + '</a>';
103 }).join('') : options.items;
104 $node.html(markup).attr({ 'aria-label': options.title });
105 });
106 var dropdownButtonContents = function (contents) {
107 return contents;
108 };
109 var dropdownCheck = renderer.create('<div class="dropdown-menu note-check" role="list">', function ($node, options) {
110 var markup = $$1.isArray(options.items) ? options.items.map(function (item) {
111 var value = (typeof item === 'string') ? item : (item.value || '');
112 var content = options.template ? options.template(item) : item;
113 return '<a class="dropdown-item" href="#" data-value="' + value + '" role="listitem" aria-label="' + item + '">' + icon(options.checkClassName) + ' ' + content + '</a>';
114 }).join('') : options.items;
115 $node.html(markup).attr({ 'aria-label': options.title });
116 });
117 var palette = renderer.create('<div class="note-color-palette"/>', function ($node, options) {
118 var contents = [];
119 for (var row = 0, rowSize = options.colors.length; row < rowSize; row++) {
120 var eventName = options.eventName;
121 var colors = options.colors[row];
122 var colorsName = options.colorsName[row];
123 var buttons = [];
124 for (var col = 0, colSize = colors.length; col < colSize; col++) {
125 var color = colors[col];
126 var colorName = colorsName[col];
127 buttons.push([
128 '<button type="button" class="note-color-btn"',
129 'style="background-color:', color, '" ',
130 'data-event="', eventName, '" ',
131 'data-value="', color, '" ',
132 'title="', colorName, '" ',
133 'aria-label="', colorName, '" ',
134 'data-toggle="button" tabindex="-1"></button>'
135 ].join(''));
136 }
137 contents.push('<div class="note-color-row">' + buttons.join('') + '</div>');
138 }
139 $node.html(contents.join(''));
140 if (options.tooltip) {
141 $node.find('.note-color-btn').tooltip({
142 container: options.container,
143 trigger: 'hover',
144 placement: 'bottom'
145 });
146 }
147 });
148 var dialog = renderer.create('<div class="modal" aria-hidden="false" tabindex="-1" role="dialog"/>', function ($node, options) {
149 if (options.fade) {
150 $node.addClass('fade');
151 }
152 $node.attr({
153 'aria-label': options.title
154 });
155 $node.html([
156 '<div class="modal-dialog">',
157 ' <div class="modal-content">',
158 (options.title
159 ? ' <div class="modal-header">' +
160 ' <h4 class="modal-title">' + options.title + '</h4>' +
161 ' <button type="button" class="close" data-dismiss="modal" aria-label="Close" aria-hidden="true">×</button>' +
162 ' </div>' : ''),
163 ' <div class="modal-body">' + options.body + '</div>',
164 (options.footer
165 ? ' <div class="modal-footer">' + options.footer + '</div>' : ''),
166 ' </div>',
167 '</div>'
168 ].join(''));
169 });
170 var popover = renderer.create([
171 '<div class="note-popover popover in">',
172 ' <div class="arrow"/>',
173 ' <div class="popover-content note-children-container"/>',
174 '</div>'
175 ].join(''), function ($node, options) {
176 var direction = typeof options.direction !== 'undefined' ? options.direction : 'bottom';
177 $node.addClass(direction);
178 if (options.hideArrow) {
179 $node.find('.arrow').hide();
180 }
181 });
182 var checkbox = renderer.create('<div class="form-check"></div>', function ($node, options) {
183 $node.html([
184 '<label class="form-check-label"' + (options.id ? ' for="' + options.id + '"' : '') + '>',
185 ' <input role="checkbox" type="checkbox" class="form-check-input"' + (options.id ? ' id="' + options.id + '"' : ''),
186 (options.checked ? ' checked' : ''),
187 ' aria-label="' + (options.text ? options.text : '') + '"',
188 ' aria-checked="' + (options.checked ? 'true' : 'false') + '"/>',
189 ' ' + (options.text ? options.text : '') + '</label>'
190 ].join(''));
191 });
192 var icon = function (iconClassName, tagName) {
193 tagName = tagName || 'i';
194 return '<' + tagName + ' class="' + iconClassName + '"/>';
195 };
196 var ui = {
197 editor: editor,
198 toolbar: toolbar,
199 editingArea: editingArea,
200 codable: codable,
201 editable: editable,
202 statusbar: statusbar,
203 airEditor: airEditor,
204 airEditable: airEditable,
205 buttonGroup: buttonGroup,
206 dropdown: dropdown,
207 dropdownButtonContents: dropdownButtonContents,
208 dropdownCheck: dropdownCheck,
209 palette: palette,
210 dialog: dialog,
211 popover: popover,
212 icon: icon,
213 checkbox: checkbox,
214 options: {},
215 button: function ($node, options) {
216 return renderer.create('<button type="button" class="note-btn btn btn-light btn-sm" role="button" tabindex="-1">', function ($node, options) {
217 if (options && options.tooltip) {
218 $node.attr({
219 title: options.tooltip,
220 'aria-label': options.tooltip
221 }).tooltip({
222 container: (options.container !== undefined) ? options.container : 'body',
223 trigger: 'hover',
224 placement: 'bottom'
225 });
226 }
227 })($node, options);
228 },
229 toggleBtn: function ($btn, isEnable) {
230 $btn.toggleClass('disabled', !isEnable);
231 $btn.attr('disabled', !isEnable);
232 },
233 toggleBtnActive: function ($btn, isActive) {
234 $btn.toggleClass('active', isActive);
235 },
236 onDialogShown: function ($dialog, handler) {
237 $dialog.one('shown.bs.modal', handler);
238 },
239 onDialogHidden: function ($dialog, handler) {
240 $dialog.one('hidden.bs.modal', handler);
241 },
242 showDialog: function ($dialog) {
243 $dialog.modal('show');
244 },
245 hideDialog: function ($dialog) {
246 $dialog.modal('hide');
247 },
248 createLayout: function ($note, options) {
249 var $editor = (options.airMode ? ui.airEditor([
250 ui.editingArea([
251 ui.airEditable()
252 ])
253 ]) : ui.editor([
254 ui.toolbar(),
255 ui.editingArea([
256 ui.codable(),
257 ui.editable()
258 ]),
259 ui.statusbar()
260 ])).render();
261 $editor.insertAfter($note);
262 return {
263 note: $note,
264 editor: $editor,
265 toolbar: $editor.find('.note-toolbar'),
266 editingArea: $editor.find('.note-editing-area'),
267 editable: $editor.find('.note-editable'),
268 codable: $editor.find('.note-codable'),
269 statusbar: $editor.find('.note-statusbar')
270 };
271 },
272 removeLayout: function ($note, layoutInfo) {
273 $note.html(layoutInfo.editable.html());
274 layoutInfo.editor.remove();
275 $note.show();
276 }
277 };
278
279 /**
280 * @class core.func
281 *
282 * func utils (for high-order func's arg)
283 *
284 * @singleton
285 * @alternateClassName func
286 */
287 function eq(itemA) {
288 return function (itemB) {
289 return itemA === itemB;
290 };
291 }
292 function eq2(itemA, itemB) {
293 return itemA === itemB;
294 }
295 function peq2(propName) {
296 return function (itemA, itemB) {
297 return itemA[propName] === itemB[propName];
298 };
299 }
300 function ok() {
301 return true;
302 }
303 function fail() {
304 return false;
305 }
306 function not(f) {
307 return function () {
308 return !f.apply(f, arguments);
309 };
310 }
311 function and(fA, fB) {
312 return function (item) {
313 return fA(item) && fB(item);
314 };
315 }
316 function self(a) {
317 return a;
318 }
319 function invoke(obj, method) {
320 return function () {
321 return obj[method].apply(obj, arguments);
322 };
323 }
324 var idCounter = 0;
325 /**
326 * generate a globally-unique id
327 *
328 * @param {String} [prefix]
329 */
330 function uniqueId(prefix) {
331 var id = ++idCounter + '';
332 return prefix ? prefix + id : id;
333 }
334 /**
335 * returns bnd (bounds) from rect
336 *
337 * - IE Compatibility Issue: http://goo.gl/sRLOAo
338 * - Scroll Issue: http://goo.gl/sNjUc
339 *
340 * @param {Rect} rect
341 * @return {Object} bounds
342 * @return {Number} bounds.top
343 * @return {Number} bounds.left
344 * @return {Number} bounds.width
345 * @return {Number} bounds.height
346 */
347 function rect2bnd(rect) {
348 var $document = $(document);
349 return {
350 top: rect.top + $document.scrollTop(),
351 left: rect.left + $document.scrollLeft(),
352 width: rect.right - rect.left,
353 height: rect.bottom - rect.top
354 };
355 }
356 /**
357 * returns a copy of the object where the keys have become the values and the values the keys.
358 * @param {Object} obj
359 * @return {Object}
360 */
361 function invertObject(obj) {
362 var inverted = {};
363 for (var key in obj) {
364 if (obj.hasOwnProperty(key)) {
365 inverted[obj[key]] = key;
366 }
367 }
368 return inverted;
369 }
370 /**
371 * @param {String} namespace
372 * @param {String} [prefix]
373 * @return {String}
374 */
375 function namespaceToCamel(namespace, prefix) {
376 prefix = prefix || '';
377 return prefix + namespace.split('.').map(function (name) {
378 return name.substring(0, 1).toUpperCase() + name.substring(1);
379 }).join('');
380 }
381 /**
382 * Returns a function, that, as long as it continues to be invoked, will not
383 * be triggered. The function will be called after it stops being called for
384 * N milliseconds. If `immediate` is passed, trigger the function on the
385 * leading edge, instead of the trailing.
386 * @param {Function} func
387 * @param {Number} wait
388 * @param {Boolean} immediate
389 * @return {Function}
390 */
391 function debounce(func, wait, immediate) {
392 var timeout;
393 return function () {
394 var context = this;
395 var args = arguments;
396 var later = function () {
397 timeout = null;
398 if (!immediate) {
399 func.apply(context, args);
400 }
401 };
402 var callNow = immediate && !timeout;
403 clearTimeout(timeout);
404 timeout = setTimeout(later, wait);
405 if (callNow) {
406 func.apply(context, args);
407 }
408 };
409 }
410 var func = {
411 eq: eq,
412 eq2: eq2,
413 peq2: peq2,
414 ok: ok,
415 fail: fail,
416 self: self,
417 not: not,
418 and: and,
419 invoke: invoke,
420 uniqueId: uniqueId,
421 rect2bnd: rect2bnd,
422 invertObject: invertObject,
423 namespaceToCamel: namespaceToCamel,
424 debounce: debounce
425 };
426
427 /**
428 * returns the first item of an array.
429 *
430 * @param {Array} array
431 */
432 function head(array) {
433 return array[0];
434 }
435 /**
436 * returns the last item of an array.
437 *
438 * @param {Array} array
439 */
440 function last(array) {
441 return array[array.length - 1];
442 }
443 /**
444 * returns everything but the last entry of the array.
445 *
446 * @param {Array} array
447 */
448 function initial(array) {
449 return array.slice(0, array.length - 1);
450 }
451 /**
452 * returns the rest of the items in an array.
453 *
454 * @param {Array} array
455 */
456 function tail(array) {
457 return array.slice(1);
458 }
459 /**
460 * returns item of array
461 */
462 function find(array, pred) {
463 for (var idx = 0, len = array.length; idx < len; idx++) {
464 var item = array[idx];
465 if (pred(item)) {
466 return item;
467 }
468 }
469 }
470 /**
471 * returns true if all of the values in the array pass the predicate truth test.
472 */
473 function all(array, pred) {
474 for (var idx = 0, len = array.length; idx < len; idx++) {
475 if (!pred(array[idx])) {
476 return false;
477 }
478 }
479 return true;
480 }
481 /**
482 * returns index of item
483 */
484 function indexOf(array, item) {
485 return $$1.inArray(item, array);
486 }
487 /**
488 * returns true if the value is present in the list.
489 */
490 function contains(array, item) {
491 return indexOf(array, item) !== -1;
492 }
493 /**
494 * get sum from a list
495 *
496 * @param {Array} array - array
497 * @param {Function} fn - iterator
498 */
499 function sum(array, fn) {
500 fn = fn || func.self;
501 return array.reduce(function (memo, v) {
502 return memo + fn(v);
503 }, 0);
504 }
505 /**
506 * returns a copy of the collection with array type.
507 * @param {Collection} collection - collection eg) node.childNodes, ...
508 */
509 function from(collection) {
510 var result = [];
511 var length = collection.length;
512 var idx = -1;
513 while (++idx < length) {
514 result[idx] = collection[idx];
515 }
516 return result;
517 }
518 /**
519 * returns whether list is empty or not
520 */
521 function isEmpty(array) {
522 return !array || !array.length;
523 }
524 /**
525 * cluster elements by predicate function.
526 *
527 * @param {Array} array - array
528 * @param {Function} fn - predicate function for cluster rule
529 * @param {Array[]}
530 */
531 function clusterBy(array, fn) {
532 if (!array.length) {
533 return [];
534 }
535 var aTail = tail(array);
536 return aTail.reduce(function (memo, v) {
537 var aLast = last(memo);
538 if (fn(last(aLast), v)) {
539 aLast[aLast.length] = v;
540 }
541 else {
542 memo[memo.length] = [v];
543 }
544 return memo;
545 }, [[head(array)]]);
546 }
547 /**
548 * returns a copy of the array with all false values removed
549 *
550 * @param {Array} array - array
551 * @param {Function} fn - predicate function for cluster rule
552 */
553 function compact(array) {
554 var aResult = [];
555 for (var idx = 0, len = array.length; idx < len; idx++) {
556 if (array[idx]) {
557 aResult.push(array[idx]);
558 }
559 }
560 return aResult;
561 }
562 /**
563 * produces a duplicate-free version of the array
564 *
565 * @param {Array} array
566 */
567 function unique(array) {
568 var results = [];
569 for (var idx = 0, len = array.length; idx < len; idx++) {
570 if (!contains(results, array[idx])) {
571 results.push(array[idx]);
572 }
573 }
574 return results;
575 }
576 /**
577 * returns next item.
578 * @param {Array} array
579 */
580 function next(array, item) {
581 var idx = indexOf(array, item);
582 if (idx === -1) {
583 return null;
584 }
585 return array[idx + 1];
586 }
587 /**
588 * returns prev item.
589 * @param {Array} array
590 */
591 function prev(array, item) {
592 var idx = indexOf(array, item);
593 if (idx === -1) {
594 return null;
595 }
596 return array[idx - 1];
597 }
598 /**
599 * @class core.list
600 *
601 * list utils
602 *
603 * @singleton
604 * @alternateClassName list
605 */
606 var lists = {
607 head: head,
608 last: last,
609 initial: initial,
610 tail: tail,
611 prev: prev,
612 next: next,
613 find: find,
614 contains: contains,
615 all: all,
616 sum: sum,
617 from: from,
618 isEmpty: isEmpty,
619 clusterBy: clusterBy,
620 compact: compact,
621 unique: unique
622 };
623
624 var isSupportAmd = typeof define === 'function' && define.amd; // eslint-disable-line
625 /**
626 * returns whether font is installed or not.
627 *
628 * @param {String} fontName
629 * @return {Boolean}
630 */
631 function isFontInstalled(fontName) {
632 var testFontName = fontName === 'Comic Sans MS' ? 'Courier New' : 'Comic Sans MS';
633 var $tester = $$1('<div>').css({
634 position: 'absolute',
635 left: '-9999px',
636 top: '-9999px',
637 fontSize: '200px'
638 }).text('mmmmmmmmmwwwwwww').appendTo(document.body);
639 var originalWidth = $tester.css('fontFamily', testFontName).width();
640 var width = $tester.css('fontFamily', fontName + ',' + testFontName).width();
641 $tester.remove();
642 return originalWidth !== width;
643 }
644 var userAgent = navigator.userAgent;
645 var isMSIE = /MSIE|Trident/i.test(userAgent);
646 var browserVersion;
647 if (isMSIE) {
648 var matches = /MSIE (\d+[.]\d+)/.exec(userAgent);
649 if (matches) {
650 browserVersion = parseFloat(matches[1]);
651 }
652 matches = /Trident\/.*rv:([0-9]{1,}[.0-9]{0,})/.exec(userAgent);
653 if (matches) {
654 browserVersion = parseFloat(matches[1]);
655 }
656 }
657 var isEdge = /Edge\/\d+/.test(userAgent);
658 var hasCodeMirror = !!window.CodeMirror;
659 if (!hasCodeMirror && isSupportAmd) {
660 // Webpack
661 if (typeof __webpack_require__ === 'function') { // eslint-disable-line
662 try {
663 // If CodeMirror can't be resolved, `require.resolve` will throw an
664 // exception and `hasCodeMirror` won't be set to `true`.
665 require.resolve('codemirror');
666 hasCodeMirror = true;
667 }
668 catch (e) {
669 // do nothing
670 }
671 }
672 else if (typeof require !== 'undefined') {
673 // Browserify
674 if (typeof require.resolve !== 'undefined') {
675 try {
676 // If CodeMirror can't be resolved, `require.resolve` will throw an
677 // exception and `hasCodeMirror` won't be set to `true`.
678 require.resolve('codemirror');
679 hasCodeMirror = true;
680 }
681 catch (e) {
682 // do nothing
683 }
684 // Almond/Require
685 }
686 else if (typeof require.specified !== 'undefined') {
687 hasCodeMirror = require.specified('codemirror');
688 }
689 }
690 }
691 var isSupportTouch = (('ontouchstart' in window) ||
692 (navigator.MaxTouchPoints > 0) ||
693 (navigator.msMaxTouchPoints > 0));
694 // [workaround] IE doesn't have input events for contentEditable
695 // - see: https://goo.gl/4bfIvA
696 var inputEventName = (isMSIE || isEdge) ? 'DOMCharacterDataModified DOMSubtreeModified DOMNodeInserted' : 'input';
697 /**
698 * @class core.env
699 *
700 * Object which check platform and agent
701 *
702 * @singleton
703 * @alternateClassName env
704 */
705 var env = {
706 isMac: navigator.appVersion.indexOf('Mac') > -1,
707 isMSIE: isMSIE,
708 isEdge: isEdge,
709 isFF: !isEdge && /firefox/i.test(userAgent),
710 isPhantom: /PhantomJS/i.test(userAgent),
711 isWebkit: !isEdge && /webkit/i.test(userAgent),
712 isChrome: !isEdge && /chrome/i.test(userAgent),
713 isSafari: !isEdge && /safari/i.test(userAgent),
714 browserVersion: browserVersion,
715 jqueryVersion: parseFloat($$1.fn.jquery),
716 isSupportAmd: isSupportAmd,
717 isSupportTouch: isSupportTouch,
718 hasCodeMirror: hasCodeMirror,
719 isFontInstalled: isFontInstalled,
720 isW3CRangeSupport: !!document.createRange,
721 inputEventName: inputEventName
722 };
723
724 var NBSP_CHAR = String.fromCharCode(160);
725 var ZERO_WIDTH_NBSP_CHAR = '\ufeff';
726 /**
727 * @method isEditable
728 *
729 * returns whether node is `note-editable` or not.
730 *
731 * @param {Node} node
732 * @return {Boolean}
733 */
734 function isEditable(node) {
735 return node && $$1(node).hasClass('note-editable');
736 }
737 /**
738 * @method isControlSizing
739 *
740 * returns whether node is `note-control-sizing` or not.
741 *
742 * @param {Node} node
743 * @return {Boolean}
744 */
745 function isControlSizing(node) {
746 return node && $$1(node).hasClass('note-control-sizing');
747 }
748 /**
749 * @method makePredByNodeName
750 *
751 * returns predicate which judge whether nodeName is same
752 *
753 * @param {String} nodeName
754 * @return {Function}
755 */
756 function makePredByNodeName(nodeName) {
757 nodeName = nodeName.toUpperCase();
758 return function (node) {
759 return node && node.nodeName.toUpperCase() === nodeName;
760 };
761 }
762 /**
763 * @method isText
764 *
765 *
766 *
767 * @param {Node} node
768 * @return {Boolean} true if node's type is text(3)
769 */
770 function isText(node) {
771 return node && node.nodeType === 3;
772 }
773 /**
774 * @method isElement
775 *
776 *
777 *
778 * @param {Node} node
779 * @return {Boolean} true if node's type is element(1)
780 */
781 function isElement(node) {
782 return node && node.nodeType === 1;
783 }
784 /**
785 * ex) br, col, embed, hr, img, input, ...
786 * @see http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
787 */
788 function isVoid(node) {
789 return node && /^BR|^IMG|^HR|^IFRAME|^BUTTON|^INPUT|^VIDEO|^EMBED/.test(node.nodeName.toUpperCase());
790 }
791 function isPara(node) {
792 if (isEditable(node)) {
793 return false;
794 }
795 // Chrome(v31.0), FF(v25.0.1) use DIV for paragraph
796 return node && /^DIV|^P|^LI|^H[1-7]/.test(node.nodeName.toUpperCase());
797 }
798 function isHeading(node) {
799 return node && /^H[1-7]/.test(node.nodeName.toUpperCase());
800 }
801 var isPre = makePredByNodeName('PRE');
802 var isLi = makePredByNodeName('LI');
803 function isPurePara(node) {
804 return isPara(node) && !isLi(node);
805 }
806 var isTable = makePredByNodeName('TABLE');
807 var isData = makePredByNodeName('DATA');
808 function isInline(node) {
809 return !isBodyContainer(node) &&
810 !isList(node) &&
811 !isHr(node) &&
812 !isPara(node) &&
813 !isTable(node) &&
814 !isBlockquote(node) &&
815 !isData(node);
816 }
817 function isList(node) {
818 return node && /^UL|^OL/.test(node.nodeName.toUpperCase());
819 }
820 var isHr = makePredByNodeName('HR');
821 function isCell(node) {
822 return node && /^TD|^TH/.test(node.nodeName.toUpperCase());
823 }
824 var isBlockquote = makePredByNodeName('BLOCKQUOTE');
825 function isBodyContainer(node) {
826 return isCell(node) || isBlockquote(node) || isEditable(node);
827 }
828 var isAnchor = makePredByNodeName('A');
829 function isParaInline(node) {
830 return isInline(node) && !!ancestor(node, isPara);
831 }
832 function isBodyInline(node) {
833 return isInline(node) && !ancestor(node, isPara);
834 }
835 var isBody = makePredByNodeName('BODY');
836 /**
837 * returns whether nodeB is closest sibling of nodeA
838 *
839 * @param {Node} nodeA
840 * @param {Node} nodeB
841 * @return {Boolean}
842 */
843 function isClosestSibling(nodeA, nodeB) {
844 return nodeA.nextSibling === nodeB ||
845 nodeA.previousSibling === nodeB;
846 }
847 /**
848 * returns array of closest siblings with node
849 *
850 * @param {Node} node
851 * @param {function} [pred] - predicate function
852 * @return {Node[]}
853 */
854 function withClosestSiblings(node, pred) {
855 pred = pred || func.ok;
856 var siblings = [];
857 if (node.previousSibling && pred(node.previousSibling)) {
858 siblings.push(node.previousSibling);
859 }
860 siblings.push(node);
861 if (node.nextSibling && pred(node.nextSibling)) {
862 siblings.push(node.nextSibling);
863 }
864 return siblings;
865 }
866 /**
867 * blank HTML for cursor position
868 * - [workaround] old IE only works with
869 * - [workaround] IE11 and other browser works with bogus br
870 */
871 var blankHTML = env.isMSIE && env.browserVersion < 11 ? ' ' : '<br>';
872 /**
873 * @method nodeLength
874 *
875 * returns #text's text size or element's childNodes size
876 *
877 * @param {Node} node
878 */
879 function nodeLength(node) {
880 if (isText(node)) {
881 return node.nodeValue.length;
882 }
883 if (node) {
884 return node.childNodes.length;
885 }
886 return 0;
887 }
888 /**
889 * returns whether node is empty or not.
890 *
891 * @param {Node} node
892 * @return {Boolean}
893 */
894 function isEmpty$1(node) {
895 var len = nodeLength(node);
896 if (len === 0) {
897 return true;
898 }
899 else if (!isText(node) && len === 1 && node.innerHTML === blankHTML) {
900 // ex) <p><br></p>, <span><br></span>
901 return true;
902 }
903 else if (lists.all(node.childNodes, isText) && node.innerHTML === '') {
904 // ex) <p></p>, <span></span>
905 return true;
906 }
907 return false;
908 }
909 /**
910 * padding blankHTML if node is empty (for cursor position)
911 */
912 function paddingBlankHTML(node) {
913 if (!isVoid(node) && !nodeLength(node)) {
914 node.innerHTML = blankHTML;
915 }
916 }
917 /**
918 * find nearest ancestor predicate hit
919 *
920 * @param {Node} node
921 * @param {Function} pred - predicate function
922 */
923 function ancestor(node, pred) {
924 while (node) {
925 if (pred(node)) {
926 return node;
927 }
928 if (isEditable(node)) {
929 break;
930 }
931 node = node.parentNode;
932 }
933 return null;
934 }
935 /**
936 * find nearest ancestor only single child blood line and predicate hit
937 *
938 * @param {Node} node
939 * @param {Function} pred - predicate function
940 */
941 function singleChildAncestor(node, pred) {
942 node = node.parentNode;
943 while (node) {
944 if (nodeLength(node) !== 1) {
945 break;
946 }
947 if (pred(node)) {
948 return node;
949 }
950 if (isEditable(node)) {
951 break;
952 }
953 node = node.parentNode;
954 }
955 return null;
956 }
957 /**
958 * returns new array of ancestor nodes (until predicate hit).
959 *
960 * @param {Node} node
961 * @param {Function} [optional] pred - predicate function
962 */
963 function listAncestor(node, pred) {
964 pred = pred || func.fail;
965 var ancestors = [];
966 ancestor(node, function (el) {
967 if (!isEditable(el)) {
968 ancestors.push(el);
969 }
970 return pred(el);
971 });
972 return ancestors;
973 }
974 /**
975 * find farthest ancestor predicate hit
976 */
977 function lastAncestor(node, pred) {
978 var ancestors = listAncestor(node);
979 return lists.last(ancestors.filter(pred));
980 }
981 /**
982 * returns common ancestor node between two nodes.
983 *
984 * @param {Node} nodeA
985 * @param {Node} nodeB
986 */
987 function commonAncestor(nodeA, nodeB) {
988 var ancestors = listAncestor(nodeA);
989 for (var n = nodeB; n; n = n.parentNode) {
990 if ($$1.inArray(n, ancestors) > -1) {
991 return n;
992 }
993 }
994 return null; // difference document area
995 }
996 /**
997 * listing all previous siblings (until predicate hit).
998 *
999 * @param {Node} node
1000 * @param {Function} [optional] pred - predicate function
1001 */
1002 function listPrev(node, pred) {
1003 pred = pred || func.fail;
1004 var nodes = [];
1005 while (node) {
1006 if (pred(node)) {
1007 break;
1008 }
1009 nodes.push(node);
1010 node = node.previousSibling;
1011 }
1012 return nodes;
1013 }
1014 /**
1015 * listing next siblings (until predicate hit).
1016 *
1017 * @param {Node} node
1018 * @param {Function} [pred] - predicate function
1019 */
1020 function listNext(node, pred) {
1021 pred = pred || func.fail;
1022 var nodes = [];
1023 while (node) {
1024 if (pred(node)) {
1025 break;
1026 }
1027 nodes.push(node);
1028 node = node.nextSibling;
1029 }
1030 return nodes;
1031 }
1032 /**
1033 * listing descendant nodes
1034 *
1035 * @param {Node} node
1036 * @param {Function} [pred] - predicate function
1037 */
1038 function listDescendant(node, pred) {
1039 var descendants = [];
1040 pred = pred || func.ok;
1041 // start DFS(depth first search) with node
1042 (function fnWalk(current) {
1043 if (node !== current && pred(current)) {
1044 descendants.push(current);
1045 }
1046 for (var idx = 0, len = current.childNodes.length; idx < len; idx++) {
1047 fnWalk(current.childNodes[idx]);
1048 }
1049 })(node);
1050 return descendants;
1051 }
1052 /**
1053 * wrap node with new tag.
1054 *
1055 * @param {Node} node
1056 * @param {Node} tagName of wrapper
1057 * @return {Node} - wrapper
1058 */
1059 function wrap(node, wrapperName) {
1060 var parent = node.parentNode;
1061 var wrapper = $$1('<' + wrapperName + '>')[0];
1062 parent.insertBefore(wrapper, node);
1063 wrapper.appendChild(node);
1064 return wrapper;
1065 }
1066 /**
1067 * insert node after preceding
1068 *
1069 * @param {Node} node
1070 * @param {Node} preceding - predicate function
1071 */
1072 function insertAfter(node, preceding) {
1073 var next = preceding.nextSibling;
1074 var parent = preceding.parentNode;
1075 if (next) {
1076 parent.insertBefore(node, next);
1077 }
1078 else {
1079 parent.appendChild(node);
1080 }
1081 return node;
1082 }
1083 /**
1084 * append elements.
1085 *
1086 * @param {Node} node
1087 * @param {Collection} aChild
1088 */
1089 function appendChildNodes(node, aChild) {
1090 $$1.each(aChild, function (idx, child) {
1091 node.appendChild(child);
1092 });
1093 return node;
1094 }
1095 /**
1096 * returns whether boundaryPoint is left edge or not.
1097 *
1098 * @param {BoundaryPoint} point
1099 * @return {Boolean}
1100 */
1101 function isLeftEdgePoint(point) {
1102 return point.offset === 0;
1103 }
1104 /**
1105 * returns whether boundaryPoint is right edge or not.
1106 *
1107 * @param {BoundaryPoint} point
1108 * @return {Boolean}
1109 */
1110 function isRightEdgePoint(point) {
1111 return point.offset === nodeLength(point.node);
1112 }
1113 /**
1114 * returns whether boundaryPoint is edge or not.
1115 *
1116 * @param {BoundaryPoint} point
1117 * @return {Boolean}
1118 */
1119 function isEdgePoint(point) {
1120 return isLeftEdgePoint(point) || isRightEdgePoint(point);
1121 }
1122 /**
1123 * returns whether node is left edge of ancestor or not.
1124 *
1125 * @param {Node} node
1126 * @param {Node} ancestor
1127 * @return {Boolean}
1128 */
1129 function isLeftEdgeOf(node, ancestor) {
1130 while (node && node !== ancestor) {
1131 if (position(node) !== 0) {
1132 return false;
1133 }
1134 node = node.parentNode;
1135 }
1136 return true;
1137 }
1138 /**
1139 * returns whether node is right edge of ancestor or not.
1140 *
1141 * @param {Node} node
1142 * @param {Node} ancestor
1143 * @return {Boolean}
1144 */
1145 function isRightEdgeOf(node, ancestor) {
1146 if (!ancestor) {
1147 return false;
1148 }
1149 while (node && node !== ancestor) {
1150 if (position(node) !== nodeLength(node.parentNode) - 1) {
1151 return false;
1152 }
1153 node = node.parentNode;
1154 }
1155 return true;
1156 }
1157 /**
1158 * returns whether point is left edge of ancestor or not.
1159 * @param {BoundaryPoint} point
1160 * @param {Node} ancestor
1161 * @return {Boolean}
1162 */
1163 function isLeftEdgePointOf(point, ancestor) {
1164 return isLeftEdgePoint(point) && isLeftEdgeOf(point.node, ancestor);
1165 }
1166 /**
1167 * returns whether point is right edge of ancestor or not.
1168 * @param {BoundaryPoint} point
1169 * @param {Node} ancestor
1170 * @return {Boolean}
1171 */
1172 function isRightEdgePointOf(point, ancestor) {
1173 return isRightEdgePoint(point) && isRightEdgeOf(point.node, ancestor);
1174 }
1175 /**
1176 * returns offset from parent.
1177 *
1178 * @param {Node} node
1179 */
1180 function position(node) {
1181 var offset = 0;
1182 while ((node = node.previousSibling)) {
1183 offset += 1;
1184 }
1185 return offset;
1186 }
1187 function hasChildren(node) {
1188 return !!(node && node.childNodes && node.childNodes.length);
1189 }
1190 /**
1191 * returns previous boundaryPoint
1192 *
1193 * @param {BoundaryPoint} point
1194 * @param {Boolean} isSkipInnerOffset
1195 * @return {BoundaryPoint}
1196 */
1197 function prevPoint(point, isSkipInnerOffset) {
1198 var node;
1199 var offset;
1200 if (point.offset === 0) {
1201 if (isEditable(point.node)) {
1202 return null;
1203 }
1204 node = point.node.parentNode;
1205 offset = position(point.node);
1206 }
1207 else if (hasChildren(point.node)) {
1208 node = point.node.childNodes[point.offset - 1];
1209 offset = nodeLength(node);
1210 }
1211 else {
1212 node = point.node;
1213 offset = isSkipInnerOffset ? 0 : point.offset - 1;
1214 }
1215 return {
1216 node: node,
1217 offset: offset
1218 };
1219 }
1220 /**
1221 * returns next boundaryPoint
1222 *
1223 * @param {BoundaryPoint} point
1224 * @param {Boolean} isSkipInnerOffset
1225 * @return {BoundaryPoint}
1226 */
1227 function nextPoint(point, isSkipInnerOffset) {
1228 var node, offset;
1229 if (nodeLength(point.node) === point.offset) {
1230 if (isEditable(point.node)) {
1231 return null;
1232 }
1233 node = point.node.parentNode;
1234 offset = position(point.node) + 1;
1235 }
1236 else if (hasChildren(point.node)) {
1237 node = point.node.childNodes[point.offset];
1238 offset = 0;
1239 }
1240 else {
1241 node = point.node;
1242 offset = isSkipInnerOffset ? nodeLength(point.node) : point.offset + 1;
1243 }
1244 return {
1245 node: node,
1246 offset: offset
1247 };
1248 }
1249 /**
1250 * returns whether pointA and pointB is same or not.
1251 *
1252 * @param {BoundaryPoint} pointA
1253 * @param {BoundaryPoint} pointB
1254 * @return {Boolean}
1255 */
1256 function isSamePoint(pointA, pointB) {
1257 return pointA.node === pointB.node && pointA.offset === pointB.offset;
1258 }
1259 /**
1260 * returns whether point is visible (can set cursor) or not.
1261 *
1262 * @param {BoundaryPoint} point
1263 * @return {Boolean}
1264 */
1265 function isVisiblePoint(point) {
1266 if (isText(point.node) || !hasChildren(point.node) || isEmpty$1(point.node)) {
1267 return true;
1268 }
1269 var leftNode = point.node.childNodes[point.offset - 1];
1270 var rightNode = point.node.childNodes[point.offset];
1271 if ((!leftNode || isVoid(leftNode)) && (!rightNode || isVoid(rightNode))) {
1272 return true;
1273 }
1274 return false;
1275 }
1276 /**
1277 * @method prevPointUtil
1278 *
1279 * @param {BoundaryPoint} point
1280 * @param {Function} pred
1281 * @return {BoundaryPoint}
1282 */
1283 function prevPointUntil(point, pred) {
1284 while (point) {
1285 if (pred(point)) {
1286 return point;
1287 }
1288 point = prevPoint(point);
1289 }
1290 return null;
1291 }
1292 /**
1293 * @method nextPointUntil
1294 *
1295 * @param {BoundaryPoint} point
1296 * @param {Function} pred
1297 * @return {BoundaryPoint}
1298 */
1299 function nextPointUntil(point, pred) {
1300 while (point) {
1301 if (pred(point)) {
1302 return point;
1303 }
1304 point = nextPoint(point);
1305 }
1306 return null;
1307 }
1308 /**
1309 * returns whether point has character or not.
1310 *
1311 * @param {Point} point
1312 * @return {Boolean}
1313 */
1314 function isCharPoint(point) {
1315 if (!isText(point.node)) {
1316 return false;
1317 }
1318 var ch = point.node.nodeValue.charAt(point.offset - 1);
1319 return ch && (ch !== ' ' && ch !== NBSP_CHAR);
1320 }
1321 /**
1322 * @method walkPoint
1323 *
1324 * @param {BoundaryPoint} startPoint
1325 * @param {BoundaryPoint} endPoint
1326 * @param {Function} handler
1327 * @param {Boolean} isSkipInnerOffset
1328 */
1329 function walkPoint(startPoint, endPoint, handler, isSkipInnerOffset) {
1330 var point = startPoint;
1331 while (point) {
1332 handler(point);
1333 if (isSamePoint(point, endPoint)) {
1334 break;
1335 }
1336 var isSkipOffset = isSkipInnerOffset &&
1337 startPoint.node !== point.node &&
1338 endPoint.node !== point.node;
1339 point = nextPoint(point, isSkipOffset);
1340 }
1341 }
1342 /**
1343 * @method makeOffsetPath
1344 *
1345 * return offsetPath(array of offset) from ancestor
1346 *
1347 * @param {Node} ancestor - ancestor node
1348 * @param {Node} node
1349 */
1350 function makeOffsetPath(ancestor, node) {
1351 var ancestors = listAncestor(node, func.eq(ancestor));
1352 return ancestors.map(position).reverse();
1353 }
1354 /**
1355 * @method fromOffsetPath
1356 *
1357 * return element from offsetPath(array of offset)
1358 *
1359 * @param {Node} ancestor - ancestor node
1360 * @param {array} offsets - offsetPath
1361 */
1362 function fromOffsetPath(ancestor, offsets) {
1363 var current = ancestor;
1364 for (var i = 0, len = offsets.length; i < len; i++) {
1365 if (current.childNodes.length <= offsets[i]) {
1366 current = current.childNodes[current.childNodes.length - 1];
1367 }
1368 else {
1369 current = current.childNodes[offsets[i]];
1370 }
1371 }
1372 return current;
1373 }
1374 /**
1375 * @method splitNode
1376 *
1377 * split element or #text
1378 *
1379 * @param {BoundaryPoint} point
1380 * @param {Object} [options]
1381 * @param {Boolean} [options.isSkipPaddingBlankHTML] - default: false
1382 * @param {Boolean} [options.isNotSplitEdgePoint] - default: false
1383 * @param {Boolean} [options.isDiscardEmptySplits] - default: false
1384 * @return {Node} right node of boundaryPoint
1385 */
1386 function splitNode(point, options) {
1387 var isSkipPaddingBlankHTML = options && options.isSkipPaddingBlankHTML;
1388 var isNotSplitEdgePoint = options && options.isNotSplitEdgePoint;
1389 var isDiscardEmptySplits = options && options.isDiscardEmptySplits;
1390 if (isDiscardEmptySplits) {
1391 isSkipPaddingBlankHTML = true;
1392 }
1393 // edge case
1394 if (isEdgePoint(point) && (isText(point.node) || isNotSplitEdgePoint)) {
1395 if (isLeftEdgePoint(point)) {
1396 return point.node;
1397 }
1398 else if (isRightEdgePoint(point)) {
1399 return point.node.nextSibling;
1400 }
1401 }
1402 // split #text
1403 if (isText(point.node)) {
1404 return point.node.splitText(point.offset);
1405 }
1406 else {
1407 var childNode = point.node.childNodes[point.offset];
1408 var clone = insertAfter(point.node.cloneNode(false), point.node);
1409 appendChildNodes(clone, listNext(childNode));
1410 if (!isSkipPaddingBlankHTML) {
1411 paddingBlankHTML(point.node);
1412 paddingBlankHTML(clone);
1413 }
1414 if (isDiscardEmptySplits) {
1415 if (isEmpty$1(point.node)) {
1416 remove(point.node);
1417 }
1418 if (isEmpty$1(clone)) {
1419 remove(clone);
1420 return point.node.nextSibling;
1421 }
1422 }
1423 return clone;
1424 }
1425 }
1426 /**
1427 * @method splitTree
1428 *
1429 * split tree by point
1430 *
1431 * @param {Node} root - split root
1432 * @param {BoundaryPoint} point
1433 * @param {Object} [options]
1434 * @param {Boolean} [options.isSkipPaddingBlankHTML] - default: false
1435 * @param {Boolean} [options.isNotSplitEdgePoint] - default: false
1436 * @return {Node} right node of boundaryPoint
1437 */
1438 function splitTree(root, point, options) {
1439 // ex) [#text, <span>, <p>]
1440 var ancestors = listAncestor(point.node, func.eq(root));
1441 if (!ancestors.length) {
1442 return null;
1443 }
1444 else if (ancestors.length === 1) {
1445 return splitNode(point, options);
1446 }
1447 return ancestors.reduce(function (node, parent) {
1448 if (node === point.node) {
1449 node = splitNode(point, options);
1450 }
1451 return splitNode({
1452 node: parent,
1453 offset: node ? position(node) : nodeLength(parent)
1454 }, options);
1455 });
1456 }
1457 /**
1458 * split point
1459 *
1460 * @param {Point} point
1461 * @param {Boolean} isInline
1462 * @return {Object}
1463 */
1464 function splitPoint(point, isInline) {
1465 // find splitRoot, container
1466 // - inline: splitRoot is a child of paragraph
1467 // - block: splitRoot is a child of bodyContainer
1468 var pred = isInline ? isPara : isBodyContainer;
1469 var ancestors = listAncestor(point.node, pred);
1470 var topAncestor = lists.last(ancestors) || point.node;
1471 var splitRoot, container;
1472 if (pred(topAncestor)) {
1473 splitRoot = ancestors[ancestors.length - 2];
1474 container = topAncestor;
1475 }
1476 else {
1477 splitRoot = topAncestor;
1478 container = splitRoot.parentNode;
1479 }
1480 // if splitRoot is exists, split with splitTree
1481 var pivot = splitRoot && splitTree(splitRoot, point, {
1482 isSkipPaddingBlankHTML: isInline,
1483 isNotSplitEdgePoint: isInline
1484 });
1485 // if container is point.node, find pivot with point.offset
1486 if (!pivot && container === point.node) {
1487 pivot = point.node.childNodes[point.offset];
1488 }
1489 return {
1490 rightNode: pivot,
1491 container: container
1492 };
1493 }
1494 function create(nodeName) {
1495 return document.createElement(nodeName);
1496 }
1497 function createText(text) {
1498 return document.createTextNode(text);
1499 }
1500 /**
1501 * @method remove
1502 *
1503 * remove node, (isRemoveChild: remove child or not)
1504 *
1505 * @param {Node} node
1506 * @param {Boolean} isRemoveChild
1507 */
1508 function remove(node, isRemoveChild) {
1509 if (!node || !node.parentNode) {
1510 return;
1511 }
1512 if (node.removeNode) {
1513 return node.removeNode(isRemoveChild);
1514 }
1515 var parent = node.parentNode;
1516 if (!isRemoveChild) {
1517 var nodes = [];
1518 for (var i = 0, len = node.childNodes.length; i < len; i++) {
1519 nodes.push(node.childNodes[i]);
1520 }
1521 for (var i = 0, len = nodes.length; i < len; i++) {
1522 parent.insertBefore(nodes[i], node);
1523 }
1524 }
1525 parent.removeChild(node);
1526 }
1527 /**
1528 * @method removeWhile
1529 *
1530 * @param {Node} node
1531 * @param {Function} pred
1532 */
1533 function removeWhile(node, pred) {
1534 while (node) {
1535 if (isEditable(node) || !pred(node)) {
1536 break;
1537 }
1538 var parent = node.parentNode;
1539 remove(node);
1540 node = parent;
1541 }
1542 }
1543 /**
1544 * @method replace
1545 *
1546 * replace node with provided nodeName
1547 *
1548 * @param {Node} node
1549 * @param {String} nodeName
1550 * @return {Node} - new node
1551 */
1552 function replace(node, nodeName) {
1553 if (node.nodeName.toUpperCase() === nodeName.toUpperCase()) {
1554 return node;
1555 }
1556 var newNode = create(nodeName);
1557 if (node.style.cssText) {
1558 newNode.style.cssText = node.style.cssText;
1559 }
1560 appendChildNodes(newNode, lists.from(node.childNodes));
1561 insertAfter(newNode, node);
1562 remove(node);
1563 return newNode;
1564 }
1565 var isTextarea = makePredByNodeName('TEXTAREA');
1566 /**
1567 * @param {jQuery} $node
1568 * @param {Boolean} [stripLinebreaks] - default: false
1569 */
1570 function value($node, stripLinebreaks) {
1571 var val = isTextarea($node[0]) ? $node.val() : $node.html();
1572 if (stripLinebreaks) {
1573 return val.replace(/[\n\r]/g, '');
1574 }
1575 return val;
1576 }
1577 /**
1578 * @method html
1579 *
1580 * get the HTML contents of node
1581 *
1582 * @param {jQuery} $node
1583 * @param {Boolean} [isNewlineOnBlock]
1584 */
1585 function html($node, isNewlineOnBlock) {
1586 var markup = value($node);
1587 if (isNewlineOnBlock) {
1588 var regexTag = /<(\/?)(\b(?!!)[^>\s]*)(.*?)(\s*\/?>)/g;
1589 markup = markup.replace(regexTag, function (match, endSlash, name) {
1590 name = name.toUpperCase();
1591 var isEndOfInlineContainer = /^DIV|^TD|^TH|^P|^LI|^H[1-7]/.test(name) &&
1592 !!endSlash;
1593 var isBlockNode = /^BLOCKQUOTE|^TABLE|^TBODY|^TR|^HR|^UL|^OL/.test(name);
1594 return match + ((isEndOfInlineContainer || isBlockNode) ? '\n' : '');
1595 });
1596 markup = $$1.trim(markup);
1597 }
1598 return markup;
1599 }
1600 function posFromPlaceholder(placeholder) {
1601 var $placeholder = $$1(placeholder);
1602 var pos = $placeholder.offset();
1603 var height = $placeholder.outerHeight(true); // include margin
1604 return {
1605 left: pos.left,
1606 top: pos.top + height
1607 };
1608 }
1609 function attachEvents($node, events) {
1610 Object.keys(events).forEach(function (key) {
1611 $node.on(key, events[key]);
1612 });
1613 }
1614 function detachEvents($node, events) {
1615 Object.keys(events).forEach(function (key) {
1616 $node.off(key, events[key]);
1617 });
1618 }
1619 /**
1620 * @method isCustomStyleTag
1621 *
1622 * assert if a node contains a "note-styletag" class,
1623 * which implies that's a custom-made style tag node
1624 *
1625 * @param {Node} an HTML DOM node
1626 */
1627 function isCustomStyleTag(node) {
1628 return node && !isText(node) && lists.contains(node.classList, 'note-styletag');
1629 }
1630 var dom = {
1631 /** @property {String} NBSP_CHAR */
1632 NBSP_CHAR: NBSP_CHAR,
1633 /** @property {String} ZERO_WIDTH_NBSP_CHAR */
1634 ZERO_WIDTH_NBSP_CHAR: ZERO_WIDTH_NBSP_CHAR,
1635 /** @property {String} blank */
1636 blank: blankHTML,
1637 /** @property {String} emptyPara */
1638 emptyPara: "<p>" + blankHTML + "</p>",
1639 makePredByNodeName: makePredByNodeName,
1640 isEditable: isEditable,
1641 isControlSizing: isControlSizing,
1642 isText: isText,
1643 isElement: isElement,
1644 isVoid: isVoid,
1645 isPara: isPara,
1646 isPurePara: isPurePara,
1647 isHeading: isHeading,
1648 isInline: isInline,
1649 isBlock: func.not(isInline),
1650 isBodyInline: isBodyInline,
1651 isBody: isBody,
1652 isParaInline: isParaInline,
1653 isPre: isPre,
1654 isList: isList,
1655 isTable: isTable,
1656 isData: isData,
1657 isCell: isCell,
1658 isBlockquote: isBlockquote,
1659 isBodyContainer: isBodyContainer,
1660 isAnchor: isAnchor,
1661 isDiv: makePredByNodeName('DIV'),
1662 isLi: isLi,
1663 isBR: makePredByNodeName('BR'),
1664 isSpan: makePredByNodeName('SPAN'),
1665 isB: makePredByNodeName('B'),
1666 isU: makePredByNodeName('U'),
1667 isS: makePredByNodeName('S'),
1668 isI: makePredByNodeName('I'),
1669 isImg: makePredByNodeName('IMG'),
1670 isTextarea: isTextarea,
1671 isEmpty: isEmpty$1,
1672 isEmptyAnchor: func.and(isAnchor, isEmpty$1),
1673 isClosestSibling: isClosestSibling,
1674 withClosestSiblings: withClosestSiblings,
1675 nodeLength: nodeLength,
1676 isLeftEdgePoint: isLeftEdgePoint,
1677 isRightEdgePoint: isRightEdgePoint,
1678 isEdgePoint: isEdgePoint,
1679 isLeftEdgeOf: isLeftEdgeOf,
1680 isRightEdgeOf: isRightEdgeOf,
1681 isLeftEdgePointOf: isLeftEdgePointOf,
1682 isRightEdgePointOf: isRightEdgePointOf,
1683 prevPoint: prevPoint,
1684 nextPoint: nextPoint,
1685 isSamePoint: isSamePoint,
1686 isVisiblePoint: isVisiblePoint,
1687 prevPointUntil: prevPointUntil,
1688 nextPointUntil: nextPointUntil,
1689 isCharPoint: isCharPoint,
1690 walkPoint: walkPoint,
1691 ancestor: ancestor,
1692 singleChildAncestor: singleChildAncestor,
1693 listAncestor: listAncestor,
1694 lastAncestor: lastAncestor,
1695 listNext: listNext,
1696 listPrev: listPrev,
1697 listDescendant: listDescendant,
1698 commonAncestor: commonAncestor,
1699 wrap: wrap,
1700 insertAfter: insertAfter,
1701 appendChildNodes: appendChildNodes,
1702 position: position,
1703 hasChildren: hasChildren,
1704 makeOffsetPath: makeOffsetPath,
1705 fromOffsetPath: fromOffsetPath,
1706 splitTree: splitTree,
1707 splitPoint: splitPoint,
1708 create: create,
1709 createText: createText,
1710 remove: remove,
1711 removeWhile: removeWhile,
1712 replace: replace,
1713 html: html,
1714 value: value,
1715 posFromPlaceholder: posFromPlaceholder,
1716 attachEvents: attachEvents,
1717 detachEvents: detachEvents,
1718 isCustomStyleTag: isCustomStyleTag
1719 };
1720
1721 /**
1722 * return boundaryPoint from TextRange, inspired by Andy Na's HuskyRange.js
1723 *
1724 * @param {TextRange} textRange
1725 * @param {Boolean} isStart
1726 * @return {BoundaryPoint}
1727 *
1728 * @see http://msdn.microsoft.com/en-us/library/ie/ms535872(v=vs.85).aspx
1729 */
1730 function textRangeToPoint(textRange, isStart) {
1731 var container = textRange.parentElement();
1732 var offset;
1733 var tester = document.body.createTextRange();
1734 var prevContainer;
1735 var childNodes = lists.from(container.childNodes);
1736 for (offset = 0; offset < childNodes.length; offset++) {
1737 if (dom.isText(childNodes[offset])) {
1738 continue;
1739 }
1740 tester.moveToElementText(childNodes[offset]);
1741 if (tester.compareEndPoints('StartToStart', textRange) >= 0) {
1742 break;
1743 }
1744 prevContainer = childNodes[offset];
1745 }
1746 if (offset !== 0 && dom.isText(childNodes[offset - 1])) {
1747 var textRangeStart = document.body.createTextRange();
1748 var curTextNode = null;
1749 textRangeStart.moveToElementText(prevContainer || container);
1750 textRangeStart.collapse(!prevContainer);
1751 curTextNode = prevContainer ? prevContainer.nextSibling : container.firstChild;
1752 var pointTester = textRange.duplicate();
1753 pointTester.setEndPoint('StartToStart', textRangeStart);
1754 var textCount = pointTester.text.replace(/[\r\n]/g, '').length;
1755 while (textCount > curTextNode.nodeValue.length && curTextNode.nextSibling) {
1756 textCount -= curTextNode.nodeValue.length;
1757 curTextNode = curTextNode.nextSibling;
1758 }
1759 // [workaround] enforce IE to re-reference curTextNode, hack
1760 var dummy = curTextNode.nodeValue; // eslint-disable-line
1761 if (isStart && curTextNode.nextSibling && dom.isText(curTextNode.nextSibling) &&
1762 textCount === curTextNode.nodeValue.length) {
1763 textCount -= curTextNode.nodeValue.length;
1764 curTextNode = curTextNode.nextSibling;
1765 }
1766 container = curTextNode;
1767 offset = textCount;
1768 }
1769 return {
1770 cont: container,
1771 offset: offset
1772 };
1773 }
1774 /**
1775 * return TextRange from boundary point (inspired by google closure-library)
1776 * @param {BoundaryPoint} point
1777 * @return {TextRange}
1778 */
1779 function pointToTextRange(point) {
1780 var textRangeInfo = function (container, offset) {
1781 var node, isCollapseToStart;
1782 if (dom.isText(container)) {
1783 var prevTextNodes = dom.listPrev(container, func.not(dom.isText));
1784 var prevContainer = lists.last(prevTextNodes).previousSibling;
1785 node = prevContainer || container.parentNode;
1786 offset += lists.sum(lists.tail(prevTextNodes), dom.nodeLength);
1787 isCollapseToStart = !prevContainer;
1788 }
1789 else {
1790 node = container.childNodes[offset] || container;
1791 if (dom.isText(node)) {
1792 return textRangeInfo(node, 0);
1793 }
1794 offset = 0;
1795 isCollapseToStart = false;
1796 }
1797 return {
1798 node: node,
1799 collapseToStart: isCollapseToStart,
1800 offset: offset
1801 };
1802 };
1803 var textRange = document.body.createTextRange();
1804 var info = textRangeInfo(point.node, point.offset);
1805 textRange.moveToElementText(info.node);
1806 textRange.collapse(info.collapseToStart);
1807 textRange.moveStart('character', info.offset);
1808 return textRange;
1809 }
1810 /**
1811 * Wrapped Range
1812 *
1813 * @constructor
1814 * @param {Node} sc - start container
1815 * @param {Number} so - start offset
1816 * @param {Node} ec - end container
1817 * @param {Number} eo - end offset
1818 */
1819 var WrappedRange = /** @class */ (function () {
1820 function WrappedRange(sc, so, ec, eo) {
1821 this.sc = sc;
1822 this.so = so;
1823 this.ec = ec;
1824 this.eo = eo;
1825 // isOnEditable: judge whether range is on editable or not
1826 this.isOnEditable = this.makeIsOn(dom.isEditable);
1827 // isOnList: judge whether range is on list node or not
1828 this.isOnList = this.makeIsOn(dom.isList);
1829 // isOnAnchor: judge whether range is on anchor node or not
1830 this.isOnAnchor = this.makeIsOn(dom.isAnchor);
1831 // isOnCell: judge whether range is on cell node or not
1832 this.isOnCell = this.makeIsOn(dom.isCell);
1833 // isOnData: judge whether range is on data node or not
1834 this.isOnData = this.makeIsOn(dom.isData);
1835 }
1836 // nativeRange: get nativeRange from sc, so, ec, eo
1837 WrappedRange.prototype.nativeRange = function () {
1838 if (env.isW3CRangeSupport) {
1839 var w3cRange = document.createRange();
1840 w3cRange.setStart(this.sc, this.so);
1841 w3cRange.setEnd(this.ec, this.eo);
1842 return w3cRange;
1843 }
1844 else {
1845 var textRange = pointToTextRange({
1846 node: this.sc,
1847 offset: this.so
1848 });
1849 textRange.setEndPoint('EndToEnd', pointToTextRange({
1850 node: this.ec,
1851 offset: this.eo
1852 }));
1853 return textRange;
1854 }
1855 };
1856 WrappedRange.prototype.getPoints = function () {
1857 return {
1858 sc: this.sc,
1859 so: this.so,
1860 ec: this.ec,
1861 eo: this.eo
1862 };
1863 };
1864 WrappedRange.prototype.getStartPoint = function () {
1865 return {
1866 node: this.sc,
1867 offset: this.so
1868 };
1869 };
1870 WrappedRange.prototype.getEndPoint = function () {
1871 return {
1872 node: this.ec,
1873 offset: this.eo
1874 };
1875 };
1876 /**
1877 * select update visible range
1878 */
1879 WrappedRange.prototype.select = function () {
1880 var nativeRng = this.nativeRange();
1881 if (env.isW3CRangeSupport) {
1882 var selection = document.getSelection();
1883 if (selection.rangeCount > 0) {
1884 selection.removeAllRanges();
1885 }
1886 selection.addRange(nativeRng);
1887 }
1888 else {
1889 nativeRng.select();
1890 }
1891 return this;
1892 };
1893 /**
1894 * Moves the scrollbar to start container(sc) of current range
1895 *
1896 * @return {WrappedRange}
1897 */
1898 WrappedRange.prototype.scrollIntoView = function (container) {
1899 var height = $$1(container).height();
1900 if (container.scrollTop + height < this.sc.offsetTop) {
1901 container.scrollTop += Math.abs(container.scrollTop + height - this.sc.offsetTop);
1902 }
1903 return this;
1904 };
1905 /**
1906 * @return {WrappedRange}
1907 */
1908 WrappedRange.prototype.normalize = function () {
1909 /**
1910 * @param {BoundaryPoint} point
1911 * @param {Boolean} isLeftToRight
1912 * @return {BoundaryPoint}
1913 */
1914 var getVisiblePoint = function (point, isLeftToRight) {
1915 if ((dom.isVisiblePoint(point) && !dom.isEdgePoint(point)) ||
1916 (dom.isVisiblePoint(point) && dom.isRightEdgePoint(point) && !isLeftToRight) ||
1917 (dom.isVisiblePoint(point) && dom.isLeftEdgePoint(point) && isLeftToRight) ||
1918 (dom.isVisiblePoint(point) && dom.isBlock(point.node) && dom.isEmpty(point.node))) {
1919 return point;
1920 }
1921 // point on block's edge
1922 var block = dom.ancestor(point.node, dom.isBlock);
1923 if (((dom.isLeftEdgePointOf(point, block) || dom.isVoid(dom.prevPoint(point).node)) && !isLeftToRight) ||
1924 ((dom.isRightEdgePointOf(point, block) || dom.isVoid(dom.nextPoint(point).node)) && isLeftToRight)) {
1925 // returns point already on visible point
1926 if (dom.isVisiblePoint(point)) {
1927 return point;
1928 }
1929 // reverse direction
1930 isLeftToRight = !isLeftToRight;
1931 }
1932 var nextPoint = isLeftToRight ? dom.nextPointUntil(dom.nextPoint(point), dom.isVisiblePoint)
1933 : dom.prevPointUntil(dom.prevPoint(point), dom.isVisiblePoint);
1934 return nextPoint || point;
1935 };
1936 var endPoint = getVisiblePoint(this.getEndPoint(), false);
1937 var startPoint = this.isCollapsed() ? endPoint : getVisiblePoint(this.getStartPoint(), true);
1938 return new WrappedRange(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset);
1939 };
1940 /**
1941 * returns matched nodes on range
1942 *
1943 * @param {Function} [pred] - predicate function
1944 * @param {Object} [options]
1945 * @param {Boolean} [options.includeAncestor]
1946 * @param {Boolean} [options.fullyContains]
1947 * @return {Node[]}
1948 */
1949 WrappedRange.prototype.nodes = function (pred, options) {
1950 pred = pred || func.ok;
1951 var includeAncestor = options && options.includeAncestor;
1952 var fullyContains = options && options.fullyContains;
1953 // TODO compare points and sort
1954 var startPoint = this.getStartPoint();
1955 var endPoint = this.getEndPoint();
1956 var nodes = [];
1957 var leftEdgeNodes = [];
1958 dom.walkPoint(startPoint, endPoint, function (point) {
1959 if (dom.isEditable(point.node)) {
1960 return;
1961 }
1962 var node;
1963 if (fullyContains) {
1964 if (dom.isLeftEdgePoint(point)) {
1965 leftEdgeNodes.push(point.node);
1966 }
1967 if (dom.isRightEdgePoint(point) && lists.contains(leftEdgeNodes, point.node)) {
1968 node = point.node;
1969 }
1970 }
1971 else if (includeAncestor) {
1972 node = dom.ancestor(point.node, pred);
1973 }
1974 else {
1975 node = point.node;
1976 }
1977 if (node && pred(node)) {
1978 nodes.push(node);
1979 }
1980 }, true);
1981 return lists.unique(nodes);
1982 };
1983 /**
1984 * returns commonAncestor of range
1985 * @return {Element} - commonAncestor
1986 */
1987 WrappedRange.prototype.commonAncestor = function () {
1988 return dom.commonAncestor(this.sc, this.ec);
1989 };
1990 /**
1991 * returns expanded range by pred
1992 *
1993 * @param {Function} pred - predicate function
1994 * @return {WrappedRange}
1995 */
1996 WrappedRange.prototype.expand = function (pred) {
1997 var startAncestor = dom.ancestor(this.sc, pred);
1998 var endAncestor = dom.ancestor(this.ec, pred);
1999 if (!startAncestor && !endAncestor) {
2000 return new WrappedRange(this.sc, this.so, this.ec, this.eo);
2001 }
2002 var boundaryPoints = this.getPoints();
2003 if (startAncestor) {
2004 boundaryPoints.sc = startAncestor;
2005 boundaryPoints.so = 0;
2006 }
2007 if (endAncestor) {
2008 boundaryPoints.ec = endAncestor;
2009 boundaryPoints.eo = dom.nodeLength(endAncestor);
2010 }
2011 return new WrappedRange(boundaryPoints.sc, boundaryPoints.so, boundaryPoints.ec, boundaryPoints.eo);
2012 };
2013 /**
2014 * @param {Boolean} isCollapseToStart
2015 * @return {WrappedRange}
2016 */
2017 WrappedRange.prototype.collapse = function (isCollapseToStart) {
2018 if (isCollapseToStart) {
2019 return new WrappedRange(this.sc, this.so, this.sc, this.so);
2020 }
2021 else {
2022 return new WrappedRange(this.ec, this.eo, this.ec, this.eo);
2023 }
2024 };
2025 /**
2026 * splitText on range
2027 */
2028 WrappedRange.prototype.splitText = function () {
2029 var isSameContainer = this.sc === this.ec;
2030 var boundaryPoints = this.getPoints();
2031 if (dom.isText(this.ec) && !dom.isEdgePoint(this.getEndPoint())) {
2032 this.ec.splitText(this.eo);
2033 }
2034 if (dom.isText(this.sc) && !dom.isEdgePoint(this.getStartPoint())) {
2035 boundaryPoints.sc = this.sc.splitText(this.so);
2036 boundaryPoints.so = 0;
2037 if (isSameContainer) {
2038 boundaryPoints.ec = boundaryPoints.sc;
2039 boundaryPoints.eo = this.eo - this.so;
2040 }
2041 }
2042 return new WrappedRange(boundaryPoints.sc, boundaryPoints.so, boundaryPoints.ec, boundaryPoints.eo);
2043 };
2044 /**
2045 * delete contents on range
2046 * @return {WrappedRange}
2047 */
2048 WrappedRange.prototype.deleteContents = function () {
2049 if (this.isCollapsed()) {
2050 return this;
2051 }
2052 var rng = this.splitText();
2053 var nodes = rng.nodes(null, {
2054 fullyContains: true
2055 });
2056 // find new cursor point
2057 var point = dom.prevPointUntil(rng.getStartPoint(), function (point) {
2058 return !lists.contains(nodes, point.node);
2059 });
2060 var emptyParents = [];
2061 $$1.each(nodes, function (idx, node) {
2062 // find empty parents
2063 var parent = node.parentNode;
2064 if (point.node !== parent && dom.nodeLength(parent) === 1) {
2065 emptyParents.push(parent);
2066 }
2067 dom.remove(node, false);
2068 });
2069 // remove empty parents
2070 $$1.each(emptyParents, function (idx, node) {
2071 dom.remove(node, false);
2072 });
2073 return new WrappedRange(point.node, point.offset, point.node, point.offset).normalize();
2074 };
2075 /**
2076 * makeIsOn: return isOn(pred) function
2077 */
2078 WrappedRange.prototype.makeIsOn = function (pred) {
2079 return function () {
2080 var ancestor = dom.ancestor(this.sc, pred);
2081 return !!ancestor && (ancestor === dom.ancestor(this.ec, pred));
2082 };
2083 };
2084 /**
2085 * @param {Function} pred
2086 * @return {Boolean}
2087 */
2088 WrappedRange.prototype.isLeftEdgeOf = function (pred) {
2089 if (!dom.isLeftEdgePoint(this.getStartPoint())) {
2090 return false;
2091 }
2092 var node = dom.ancestor(this.sc, pred);
2093 return node && dom.isLeftEdgeOf(this.sc, node);
2094 };
2095 /**
2096 * returns whether range was collapsed or not
2097 */
2098 WrappedRange.prototype.isCollapsed = function () {
2099 return this.sc === this.ec && this.so === this.eo;
2100 };
2101 /**
2102 * wrap inline nodes which children of body with paragraph
2103 *
2104 * @return {WrappedRange}
2105 */
2106 WrappedRange.prototype.wrapBodyInlineWithPara = function () {
2107 if (dom.isBodyContainer(this.sc) && dom.isEmpty(this.sc)) {
2108 this.sc.innerHTML = dom.emptyPara;
2109 return new WrappedRange(this.sc.firstChild, 0, this.sc.firstChild, 0);
2110 }
2111 /**
2112 * [workaround] firefox often create range on not visible point. so normalize here.
2113 * - firefox: |<p>text</p>|
2114 * - chrome: <p>|text|</p>
2115 */
2116 var rng = this.normalize();
2117 if (dom.isParaInline(this.sc) || dom.isPara(this.sc)) {
2118 return rng;
2119 }
2120 // find inline top ancestor
2121 var topAncestor;
2122 if (dom.isInline(rng.sc)) {
2123 var ancestors = dom.listAncestor(rng.sc, func.not(dom.isInline));
2124 topAncestor = lists.last(ancestors);
2125 if (!dom.isInline(topAncestor)) {
2126 topAncestor = ancestors[ancestors.length - 2] || rng.sc.childNodes[rng.so];
2127 }
2128 }
2129 else {
2130 topAncestor = rng.sc.childNodes[rng.so > 0 ? rng.so - 1 : 0];
2131 }
2132 // siblings not in paragraph
2133 var inlineSiblings = dom.listPrev(topAncestor, dom.isParaInline).reverse();
2134 inlineSiblings = inlineSiblings.concat(dom.listNext(topAncestor.nextSibling, dom.isParaInline));
2135 // wrap with paragraph
2136 if (inlineSiblings.length) {
2137 var para = dom.wrap(lists.head(inlineSiblings), 'p');
2138 dom.appendChildNodes(para, lists.tail(inlineSiblings));
2139 }
2140 return this.normalize();
2141 };
2142 /**
2143 * insert node at current cursor
2144 *
2145 * @param {Node} node
2146 * @return {Node}
2147 */
2148 WrappedRange.prototype.insertNode = function (node) {
2149 var rng = this.wrapBodyInlineWithPara().deleteContents();
2150 var info = dom.splitPoint(rng.getStartPoint(), dom.isInline(node));
2151 if (info.rightNode) {
2152 info.rightNode.parentNode.insertBefore(node, info.rightNode);
2153 }
2154 else {
2155 info.container.appendChild(node);
2156 }
2157 return node;
2158 };
2159 /**
2160 * insert html at current cursor
2161 */
2162 WrappedRange.prototype.pasteHTML = function (markup) {
2163 var contentsContainer = $$1('<div></div>').html(markup)[0];
2164 var childNodes = lists.from(contentsContainer.childNodes);
2165 var rng = this.wrapBodyInlineWithPara().deleteContents();
2166 if (rng.so > 0) {
2167 childNodes = childNodes.reverse();
2168 }
2169 childNodes = childNodes.map(function (childNode) {
2170 return rng.insertNode(childNode);
2171 });
2172 if (rng.so > 0) {
2173 childNodes = childNodes.reverse();
2174 }
2175 return childNodes;
2176 };
2177 /**
2178 * returns text in range
2179 *
2180 * @return {String}
2181 */
2182 WrappedRange.prototype.toString = function () {
2183 var nativeRng = this.nativeRange();
2184 return env.isW3CRangeSupport ? nativeRng.toString() : nativeRng.text;
2185 };
2186 /**
2187 * returns range for word before cursor
2188 *
2189 * @param {Boolean} [findAfter] - find after cursor, default: false
2190 * @return {WrappedRange}
2191 */
2192 WrappedRange.prototype.getWordRange = function (findAfter) {
2193 var endPoint = this.getEndPoint();
2194 if (!dom.isCharPoint(endPoint)) {
2195 return this;
2196 }
2197 var startPoint = dom.prevPointUntil(endPoint, function (point) {
2198 return !dom.isCharPoint(point);
2199 });
2200 if (findAfter) {
2201 endPoint = dom.nextPointUntil(endPoint, function (point) {
2202 return !dom.isCharPoint(point);
2203 });
2204 }
2205 return new WrappedRange(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset);
2206 };
2207 /**
2208 * create offsetPath bookmark
2209 *
2210 * @param {Node} editable
2211 */
2212 WrappedRange.prototype.bookmark = function (editable) {
2213 return {
2214 s: {
2215 path: dom.makeOffsetPath(editable, this.sc),
2216 offset: this.so
2217 },
2218 e: {
2219 path: dom.makeOffsetPath(editable, this.ec),
2220 offset: this.eo
2221 }
2222 };
2223 };
2224 /**
2225 * create offsetPath bookmark base on paragraph
2226 *
2227 * @param {Node[]} paras
2228 */
2229 WrappedRange.prototype.paraBookmark = function (paras) {
2230 return {
2231 s: {
2232 path: lists.tail(dom.makeOffsetPath(lists.head(paras), this.sc)),
2233 offset: this.so
2234 },
2235 e: {
2236 path: lists.tail(dom.makeOffsetPath(lists.last(paras), this.ec)),
2237 offset: this.eo
2238 }
2239 };
2240 };
2241 /**
2242 * getClientRects
2243 * @return {Rect[]}
2244 */
2245 WrappedRange.prototype.getClientRects = function () {
2246 var nativeRng = this.nativeRange();
2247 return nativeRng.getClientRects();
2248 };
2249 return WrappedRange;
2250 }());
2251 /**
2252 * Data structure
2253 * * BoundaryPoint: a point of dom tree
2254 * * BoundaryPoints: two boundaryPoints corresponding to the start and the end of the Range
2255 *
2256 * See to http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Position
2257 */
2258 var range = {
2259 /**
2260 * create Range Object From arguments or Browser Selection
2261 *
2262 * @param {Node} sc - start container
2263 * @param {Number} so - start offset
2264 * @param {Node} ec - end container
2265 * @param {Number} eo - end offset
2266 * @return {WrappedRange}
2267 */
2268 create: function (sc, so, ec, eo) {
2269 if (arguments.length === 4) {
2270 return new WrappedRange(sc, so, ec, eo);
2271 }
2272 else if (arguments.length === 2) { // collapsed
2273 ec = sc;
2274 eo = so;
2275 return new WrappedRange(sc, so, ec, eo);
2276 }
2277 else {
2278 var wrappedRange = this.createFromSelection();
2279 if (!wrappedRange && arguments.length === 1) {
2280 wrappedRange = this.createFromNode(arguments[0]);
2281 return wrappedRange.collapse(dom.emptyPara === arguments[0].innerHTML);
2282 }
2283 return wrappedRange;
2284 }
2285 },
2286 createFromSelection: function () {
2287 var sc, so, ec, eo;
2288 if (env.isW3CRangeSupport) {
2289 var selection = document.getSelection();
2290 if (!selection || selection.rangeCount === 0) {
2291 return null;
2292 }
2293 else if (dom.isBody(selection.anchorNode)) {
2294 // Firefox: returns entire body as range on initialization.
2295 // We won't never need it.
2296 return null;
2297 }
2298 var nativeRng = selection.getRangeAt(0);
2299 sc = nativeRng.startContainer;
2300 so = nativeRng.startOffset;
2301 ec = nativeRng.endContainer;
2302 eo = nativeRng.endOffset;
2303 }
2304 else { // IE8: TextRange
2305 var textRange = document.selection.createRange();
2306 var textRangeEnd = textRange.duplicate();
2307 textRangeEnd.collapse(false);
2308 var textRangeStart = textRange;
2309 textRangeStart.collapse(true);
2310 var startPoint = textRangeToPoint(textRangeStart, true);
2311 var endPoint = textRangeToPoint(textRangeEnd, false);
2312 // same visible point case: range was collapsed.
2313 if (dom.isText(startPoint.node) && dom.isLeftEdgePoint(startPoint) &&
2314 dom.isTextNode(endPoint.node) && dom.isRightEdgePoint(endPoint) &&
2315 endPoint.node.nextSibling === startPoint.node) {
2316 startPoint = endPoint;
2317 }
2318 sc = startPoint.cont;
2319 so = startPoint.offset;
2320 ec = endPoint.cont;
2321 eo = endPoint.offset;
2322 }
2323 return new WrappedRange(sc, so, ec, eo);
2324 },
2325 /**
2326 * @method
2327 *
2328 * create WrappedRange from node
2329 *
2330 * @param {Node} node
2331 * @return {WrappedRange}
2332 */
2333 createFromNode: function (node) {
2334 var sc = node;
2335 var so = 0;
2336 var ec = node;
2337 var eo = dom.nodeLength(ec);
2338 // browsers can't target a picture or void node
2339 if (dom.isVoid(sc)) {
2340 so = dom.listPrev(sc).length - 1;
2341 sc = sc.parentNode;
2342 }
2343 if (dom.isBR(ec)) {
2344 eo = dom.listPrev(ec).length - 1;
2345 ec = ec.parentNode;
2346 }
2347 else if (dom.isVoid(ec)) {
2348 eo = dom.listPrev(ec).length;
2349 ec = ec.parentNode;
2350 }
2351 return this.create(sc, so, ec, eo);
2352 },
2353 /**
2354 * create WrappedRange from node after position
2355 *
2356 * @param {Node} node
2357 * @return {WrappedRange}
2358 */
2359 createFromNodeBefore: function (node) {
2360 return this.createFromNode(node).collapse(true);
2361 },
2362 /**
2363 * create WrappedRange from node after position
2364 *
2365 * @param {Node} node
2366 * @return {WrappedRange}
2367 */
2368 createFromNodeAfter: function (node) {
2369 return this.createFromNode(node).collapse();
2370 },
2371 /**
2372 * @method
2373 *
2374 * create WrappedRange from bookmark
2375 *
2376 * @param {Node} editable
2377 * @param {Object} bookmark
2378 * @return {WrappedRange}
2379 */
2380 createFromBookmark: function (editable, bookmark) {
2381 var sc = dom.fromOffsetPath(editable, bookmark.s.path);
2382 var so = bookmark.s.offset;
2383 var ec = dom.fromOffsetPath(editable, bookmark.e.path);
2384 var eo = bookmark.e.offset;
2385 return new WrappedRange(sc, so, ec, eo);
2386 },
2387 /**
2388 * @method
2389 *
2390 * create WrappedRange from paraBookmark
2391 *
2392 * @param {Object} bookmark
2393 * @param {Node[]} paras
2394 * @return {WrappedRange}
2395 */
2396 createFromParaBookmark: function (bookmark, paras) {
2397 var so = bookmark.s.offset;
2398 var eo = bookmark.e.offset;
2399 var sc = dom.fromOffsetPath(lists.head(paras), bookmark.s.path);
2400 var ec = dom.fromOffsetPath(lists.last(paras), bookmark.e.path);
2401 return new WrappedRange(sc, so, ec, eo);
2402 }
2403 };
2404
2405 $$1.summernote = $$1.summernote || {
2406 lang: {}
2407 };
2408 $$1.extend($$1.summernote.lang, {
2409 'en-US': {
2410 font: {
2411 bold: 'Bold',
2412 italic: 'Italic',
2413 underline: 'Underline',
2414 clear: 'Remove Font Style',
2415 height: 'Line Height',
2416 name: 'Font Family',
2417 strikethrough: 'Strikethrough',
2418 subscript: 'Subscript',
2419 superscript: 'Superscript',
2420 size: 'Font Size'
2421 },
2422 image: {
2423 image: 'Picture',
2424 insert: 'Insert Image',
2425 resizeFull: 'Resize Full',
2426 resizeHalf: 'Resize Half',
2427 resizeQuarter: 'Resize Quarter',
2428 floatLeft: 'Float Left',
2429 floatRight: 'Float Right',
2430 floatNone: 'Float None',
2431 shapeRounded: 'Shape: Rounded',
2432 shapeCircle: 'Shape: Circle',
2433 shapeThumbnail: 'Shape: Thumbnail',
2434 shapeNone: 'Shape: None',
2435 dragImageHere: 'Drag image or text here',
2436 dropImage: 'Drop image or Text',
2437 selectFromFiles: 'Select from files',
2438 maximumFileSize: 'Maximum file size',
2439 maximumFileSizeError: 'Maximum file size exceeded.',
2440 url: 'Image URL',
2441 remove: 'Remove Image',
2442 original: 'Original'
2443 },
2444 video: {
2445 video: 'Video',
2446 videoLink: 'Video Link',
2447 insert: 'Insert Video',
2448 url: 'Video URL',
2449 providers: '(YouTube, Vimeo, Vine, Instagram, DailyMotion or Youku)'
2450 },
2451 link: {
2452 link: 'Link',
2453 insert: 'Insert Link',
2454 unlink: 'Unlink',
2455 edit: 'Edit',
2456 textToDisplay: 'Text to display',
2457 url: 'To what URL should this link go?',
2458 openInNewWindow: 'Open in new window'
2459 },
2460 table: {
2461 table: 'Table',
2462 addRowAbove: 'Add row above',
2463 addRowBelow: 'Add row below',
2464 addColLeft: 'Add column left',
2465 addColRight: 'Add column right',
2466 delRow: 'Delete row',
2467 delCol: 'Delete column',
2468 delTable: 'Delete table'
2469 },
2470 hr: {
2471 insert: 'Insert Horizontal Rule'
2472 },
2473 style: {
2474 style: 'Style',
2475 p: 'Normal',
2476 blockquote: 'Quote',
2477 pre: 'Code',
2478 h1: 'Header 1',
2479 h2: 'Header 2',
2480 h3: 'Header 3',
2481 h4: 'Header 4',
2482 h5: 'Header 5',
2483 h6: 'Header 6'
2484 },
2485 lists: {
2486 unordered: 'Unordered list',
2487 ordered: 'Ordered list'
2488 },
2489 options: {
2490 help: 'Help',
2491 fullscreen: 'Full Screen',
2492 codeview: 'Code View'
2493 },
2494 paragraph: {
2495 paragraph: 'Paragraph',
2496 outdent: 'Outdent',
2497 indent: 'Indent',
2498 left: 'Align left',
2499 center: 'Align center',
2500 right: 'Align right',
2501 justify: 'Justify full'
2502 },
2503 color: {
2504 recent: 'Recent Color',
2505 more: 'More Color',
2506 background: 'Background Color',
2507 foreground: 'Foreground Color',
2508 transparent: 'Transparent',
2509 setTransparent: 'Set transparent',
2510 reset: 'Reset',
2511 resetToDefault: 'Reset to default',
2512 cpSelect: 'Select'
2513 },
2514 shortcut: {
2515 shortcuts: 'Keyboard shortcuts',
2516 close: 'Close',
2517 textFormatting: 'Text formatting',
2518 action: 'Action',
2519 paragraphFormatting: 'Paragraph formatting',
2520 documentStyle: 'Document Style',
2521 extraKeys: 'Extra keys'
2522 },
2523 help: {
2524 'insertParagraph': 'Insert Paragraph',
2525 'undo': 'Undoes the last command',
2526 'redo': 'Redoes the last command',
2527 'tab': 'Tab',
2528 'untab': 'Untab',
2529 'bold': 'Set a bold style',
2530 'italic': 'Set a italic style',
2531 'underline': 'Set a underline style',
2532 'strikethrough': 'Set a strikethrough style',
2533 'removeFormat': 'Clean a style',
2534 'justifyLeft': 'Set left align',
2535 'justifyCenter': 'Set center align',
2536 'justifyRight': 'Set right align',
2537 'justifyFull': 'Set full align',
2538 'insertUnorderedList': 'Toggle unordered list',
2539 'insertOrderedList': 'Toggle ordered list',
2540 'outdent': 'Outdent on current paragraph',
2541 'indent': 'Indent on current paragraph',
2542 'formatPara': 'Change current block\'s format as a paragraph(P tag)',
2543 'formatH1': 'Change current block\'s format as H1',
2544 'formatH2': 'Change current block\'s format as H2',
2545 'formatH3': 'Change current block\'s format as H3',
2546 'formatH4': 'Change current block\'s format as H4',
2547 'formatH5': 'Change current block\'s format as H5',
2548 'formatH6': 'Change current block\'s format as H6',
2549 'insertHorizontalRule': 'Insert horizontal rule',
2550 'linkDialog.show': 'Show Link Dialog'
2551 },
2552 history: {
2553 undo: 'Undo',
2554 redo: 'Redo'
2555 },
2556 specialChar: {
2557 specialChar: 'SPECIAL CHARACTERS',
2558 select: 'Select Special characters'
2559 }
2560 }
2561 });
2562
2563 var KEY_MAP = {
2564 'BACKSPACE': 8,
2565 'TAB': 9,
2566 'ENTER': 13,
2567 'SPACE': 32,
2568 'DELETE': 46,
2569 // Arrow
2570 'LEFT': 37,
2571 'UP': 38,
2572 'RIGHT': 39,
2573 'DOWN': 40,
2574 // Number: 0-9
2575 'NUM0': 48,
2576 'NUM1': 49,
2577 'NUM2': 50,
2578 'NUM3': 51,
2579 'NUM4': 52,
2580 'NUM5': 53,
2581 'NUM6': 54,
2582 'NUM7': 55,
2583 'NUM8': 56,
2584 // Alphabet: a-z
2585 'B': 66,
2586 'E': 69,
2587 'I': 73,
2588 'J': 74,
2589 'K': 75,
2590 'L': 76,
2591 'R': 82,
2592 'S': 83,
2593 'U': 85,
2594 'V': 86,
2595 'Y': 89,
2596 'Z': 90,
2597 'SLASH': 191,
2598 'LEFTBRACKET': 219,
2599 'BACKSLASH': 220,
2600 'RIGHTBRACKET': 221
2601 };
2602 /**
2603 * @class core.key
2604 *
2605 * Object for keycodes.
2606 *
2607 * @singleton
2608 * @alternateClassName key
2609 */
2610 var key = {
2611 /**
2612 * @method isEdit
2613 *
2614 * @param {Number} keyCode
2615 * @return {Boolean}
2616 */
2617 isEdit: function (keyCode) {
2618 return lists.contains([
2619 KEY_MAP.BACKSPACE,
2620 KEY_MAP.TAB,
2621 KEY_MAP.ENTER,
2622 KEY_MAP.SPACE,
2623 KEY_MAP.DELETE
2624 ], keyCode);
2625 },
2626 /**
2627 * @method isMove
2628 *
2629 * @param {Number} keyCode
2630 * @return {Boolean}
2631 */
2632 isMove: function (keyCode) {
2633 return lists.contains([
2634 KEY_MAP.LEFT,
2635 KEY_MAP.UP,
2636 KEY_MAP.RIGHT,
2637 KEY_MAP.DOWN
2638 ], keyCode);
2639 },
2640 /**
2641 * @property {Object} nameFromCode
2642 * @property {String} nameFromCode.8 "BACKSPACE"
2643 */
2644 nameFromCode: func.invertObject(KEY_MAP),
2645 code: KEY_MAP
2646 };
2647
2648 /**
2649 * @method readFileAsDataURL
2650 *
2651 * read contents of file as representing URL
2652 *
2653 * @param {File} file
2654 * @return {Promise} - then: dataUrl
2655 */
2656 function readFileAsDataURL(file) {
2657 return $$1.Deferred(function (deferred) {
2658 $$1.extend(new FileReader(), {
2659 onload: function (e) {
2660 var dataURL = e.target.result;
2661 deferred.resolve(dataURL);
2662 },
2663 onerror: function (err) {
2664 deferred.reject(err);
2665 }
2666 }).readAsDataURL(file);
2667 }).promise();
2668 }
2669 /**
2670 * @method createImage
2671 *
2672 * create `<image>` from url string
2673 *
2674 * @param {String} url
2675 * @return {Promise} - then: $image
2676 */
2677 function createImage(url) {
2678 return $$1.Deferred(function (deferred) {
2679 var $img = $$1('<img>');
2680 $img.one('load', function () {
2681 $img.off('error abort');
2682 deferred.resolve($img);
2683 }).one('error abort', function () {
2684 $img.off('load').detach();
2685 deferred.reject($img);
2686 }).css({
2687 display: 'none'
2688 }).appendTo(document.body).attr('src', url);
2689 }).promise();
2690 }
2691
2692 var History = /** @class */ (function () {
2693 function History($editable) {
2694 this.stack = [];
2695 this.stackOffset = -1;
2696 this.$editable = $editable;
2697 this.editable = $editable[0];
2698 }
2699 History.prototype.makeSnapshot = function () {
2700 var rng = range.create(this.editable);
2701 var emptyBookmark = { s: { path: [], offset: 0 }, e: { path: [], offset: 0 } };
2702 return {
2703 contents: this.$editable.html(),
2704 bookmark: (rng ? rng.bookmark(this.editable) : emptyBookmark)
2705 };
2706 };
2707 History.prototype.applySnapshot = function (snapshot) {
2708 if (snapshot.contents !== null) {
2709 this.$editable.html(snapshot.contents);
2710 }
2711 if (snapshot.bookmark !== null) {
2712 range.createFromBookmark(this.editable, snapshot.bookmark).select();
2713 }
2714 };
2715 /**
2716 * @method rewind
2717 * Rewinds the history stack back to the first snapshot taken.
2718 * Leaves the stack intact, so that "Redo" can still be used.
2719 */
2720 History.prototype.rewind = function () {
2721 // Create snap shot if not yet recorded
2722 if (this.$editable.html() !== this.stack[this.stackOffset].contents) {
2723 this.recordUndo();
2724 }
2725 // Return to the first available snapshot.
2726 this.stackOffset = 0;
2727 // Apply that snapshot.
2728 this.applySnapshot(this.stack[this.stackOffset]);
2729 };
2730 /**
2731 * @method commit
2732 * Resets history stack, but keeps current editor's content.
2733 */
2734 History.prototype.commit = function () {
2735 // Clear the stack.
2736 this.stack = [];
2737 // Restore stackOffset to its original value.
2738 this.stackOffset = -1;
2739 // Record our first snapshot (of nothing).
2740 this.recordUndo();
2741 };
2742 /**
2743 * @method reset
2744 * Resets the history stack completely; reverting to an empty editor.
2745 */
2746 History.prototype.reset = function () {
2747 // Clear the stack.
2748 this.stack = [];
2749 // Restore stackOffset to its original value.
2750 this.stackOffset = -1;
2751 // Clear the editable area.
2752 this.$editable.html('');
2753 // Record our first snapshot (of nothing).
2754 this.recordUndo();
2755 };
2756 /**
2757 * undo
2758 */
2759 History.prototype.undo = function () {
2760 // Create snap shot if not yet recorded
2761 if (this.$editable.html() !== this.stack[this.stackOffset].contents) {
2762 this.recordUndo();
2763 }
2764 if (this.stackOffset > 0) {
2765 this.stackOffset--;
2766 this.applySnapshot(this.stack[this.stackOffset]);
2767 }
2768 };
2769 /**
2770 * redo
2771 */
2772 History.prototype.redo = function () {
2773 if (this.stack.length - 1 > this.stackOffset) {
2774 this.stackOffset++;
2775 this.applySnapshot(this.stack[this.stackOffset]);
2776 }
2777 };
2778 /**
2779 * recorded undo
2780 */
2781 History.prototype.recordUndo = function () {
2782 this.stackOffset++;
2783 // Wash out stack after stackOffset
2784 if (this.stack.length > this.stackOffset) {
2785 this.stack = this.stack.slice(0, this.stackOffset);
2786 }
2787 // Create new snapshot and push it to the end
2788 this.stack.push(this.makeSnapshot());
2789 };
2790 return History;
2791 }());
2792
2793 var Style = /** @class */ (function () {
2794 function Style() {
2795 }
2796 /**
2797 * @method jQueryCSS
2798 *
2799 * [workaround] for old jQuery
2800 * passing an array of style properties to .css()
2801 * will result in an object of property-value pairs.
2802 * (compability with version < 1.9)
2803 *
2804 * @private
2805 * @param {jQuery} $obj
2806 * @param {Array} propertyNames - An array of one or more CSS properties.
2807 * @return {Object}
2808 */
2809 Style.prototype.jQueryCSS = function ($obj, propertyNames) {
2810 if (env.jqueryVersion < 1.9) {
2811 var result_1 = {};
2812 $$1.each(propertyNames, function (idx, propertyName) {
2813 result_1[propertyName] = $obj.css(propertyName);
2814 });
2815 return result_1;
2816 }
2817 return $obj.css(propertyNames);
2818 };
2819 /**
2820 * returns style object from node
2821 *
2822 * @param {jQuery} $node
2823 * @return {Object}
2824 */
2825 Style.prototype.fromNode = function ($node) {
2826 var properties = ['font-family', 'font-size', 'text-align', 'list-style-type', 'line-height'];
2827 var styleInfo = this.jQueryCSS($node, properties) || {};
2828 styleInfo['font-size'] = parseInt(styleInfo['font-size'], 10);
2829 return styleInfo;
2830 };
2831 /**
2832 * paragraph level style
2833 *
2834 * @param {WrappedRange} rng
2835 * @param {Object} styleInfo
2836 */
2837 Style.prototype.stylePara = function (rng, styleInfo) {
2838 $$1.each(rng.nodes(dom.isPara, {
2839 includeAncestor: true
2840 }), function (idx, para) {
2841 $$1(para).css(styleInfo);
2842 });
2843 };
2844 /**
2845 * insert and returns styleNodes on range.
2846 *
2847 * @param {WrappedRange} rng
2848 * @param {Object} [options] - options for styleNodes
2849 * @param {String} [options.nodeName] - default: `SPAN`
2850 * @param {Boolean} [options.expandClosestSibling] - default: `false`
2851 * @param {Boolean} [options.onlyPartialContains] - default: `false`
2852 * @return {Node[]}
2853 */
2854 Style.prototype.styleNodes = function (rng, options) {
2855 rng = rng.splitText();
2856 var nodeName = (options && options.nodeName) || 'SPAN';
2857 var expandClosestSibling = !!(options && options.expandClosestSibling);
2858 var onlyPartialContains = !!(options && options.onlyPartialContains);
2859 if (rng.isCollapsed()) {
2860 return [rng.insertNode(dom.create(nodeName))];
2861 }
2862 var pred = dom.makePredByNodeName(nodeName);
2863 var nodes = rng.nodes(dom.isText, {
2864 fullyContains: true
2865 }).map(function (text) {
2866 return dom.singleChildAncestor(text, pred) || dom.wrap(text, nodeName);
2867 });
2868 if (expandClosestSibling) {
2869 if (onlyPartialContains) {
2870 var nodesInRange_1 = rng.nodes();
2871 // compose with partial contains predication
2872 pred = func.and(pred, function (node) {
2873 return lists.contains(nodesInRange_1, node);
2874 });
2875 }
2876 return nodes.map(function (node) {
2877 var siblings = dom.withClosestSiblings(node, pred);
2878 var head = lists.head(siblings);
2879 var tails = lists.tail(siblings);
2880 $$1.each(tails, function (idx, elem) {
2881 dom.appendChildNodes(head, elem.childNodes);
2882 dom.remove(elem);
2883 });
2884 return lists.head(siblings);
2885 });
2886 }
2887 else {
2888 return nodes;
2889 }
2890 };
2891 /**
2892 * get current style on cursor
2893 *
2894 * @param {WrappedRange} rng
2895 * @return {Object} - object contains style properties.
2896 */
2897 Style.prototype.current = function (rng) {
2898 var $cont = $$1(!dom.isElement(rng.sc) ? rng.sc.parentNode : rng.sc);
2899 var styleInfo = this.fromNode($cont);
2900 // document.queryCommandState for toggle state
2901 // [workaround] prevent Firefox nsresult: "0x80004005 (NS_ERROR_FAILURE)"
2902 try {
2903 styleInfo = $$1.extend(styleInfo, {
2904 'font-bold': document.queryCommandState('bold') ? 'bold' : 'normal',
2905 'font-italic': document.queryCommandState('italic') ? 'italic' : 'normal',
2906 'font-underline': document.queryCommandState('underline') ? 'underline' : 'normal',
2907 'font-subscript': document.queryCommandState('subscript') ? 'subscript' : 'normal',
2908 'font-superscript': document.queryCommandState('superscript') ? 'superscript' : 'normal',
2909 'font-strikethrough': document.queryCommandState('strikethrough') ? 'strikethrough' : 'normal',
2910 'font-family': document.queryCommandValue('fontname') || styleInfo['font-family']
2911 });
2912 }
2913 catch (e) { }
2914 // list-style-type to list-style(unordered, ordered)
2915 if (!rng.isOnList()) {
2916 styleInfo['list-style'] = 'none';
2917 }
2918 else {
2919 var orderedTypes = ['circle', 'disc', 'disc-leading-zero', 'square'];
2920 var isUnordered = $$1.inArray(styleInfo['list-style-type'], orderedTypes) > -1;
2921 styleInfo['list-style'] = isUnordered ? 'unordered' : 'ordered';
2922 }
2923 var para = dom.ancestor(rng.sc, dom.isPara);
2924 if (para && para.style['line-height']) {
2925 styleInfo['line-height'] = para.style.lineHeight;
2926 }
2927 else {
2928 var lineHeight = parseInt(styleInfo['line-height'], 10) / parseInt(styleInfo['font-size'], 10);
2929 styleInfo['line-height'] = lineHeight.toFixed(1);
2930 }
2931 styleInfo.anchor = rng.isOnAnchor() && dom.ancestor(rng.sc, dom.isAnchor);
2932 styleInfo.ancestors = dom.listAncestor(rng.sc, dom.isEditable);
2933 styleInfo.range = rng;
2934 return styleInfo;
2935 };
2936 return Style;
2937 }());
2938
2939 var Bullet = /** @class */ (function () {
2940 function Bullet() {
2941 }
2942 /**
2943 * toggle ordered list
2944 */
2945 Bullet.prototype.insertOrderedList = function (editable) {
2946 this.toggleList('OL', editable);
2947 };
2948 /**
2949 * toggle unordered list
2950 */
2951 Bullet.prototype.insertUnorderedList = function (editable) {
2952 this.toggleList('UL', editable);
2953 };
2954 /**
2955 * indent
2956 */
2957 Bullet.prototype.indent = function (editable) {
2958 var _this = this;
2959 var rng = range.create(editable).wrapBodyInlineWithPara();
2960 var paras = rng.nodes(dom.isPara, { includeAncestor: true });
2961 var clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
2962 $$1.each(clustereds, function (idx, paras) {
2963 var head = lists.head(paras);
2964 if (dom.isLi(head)) {
2965 var previousList_1 = _this.findList(head.previousSibling);
2966 if (previousList_1) {
2967 paras
2968 .map(function (para) { return previousList_1.appendChild(para); });
2969 }
2970 else {
2971 _this.wrapList(paras, head.parentNode.nodeName);
2972 paras
2973 .map(function (para) { return para.parentNode; })
2974 .map(function (para) { return _this.appendToPrevious(para); });
2975 }
2976 }
2977 else {
2978 $$1.each(paras, function (idx, para) {
2979 $$1(para).css('marginLeft', function (idx, val) {
2980 return (parseInt(val, 10) || 0) + 25;
2981 });
2982 });
2983 }
2984 });
2985 rng.select();
2986 };
2987 /**
2988 * outdent
2989 */
2990 Bullet.prototype.outdent = function (editable) {
2991 var _this = this;
2992 var rng = range.create(editable).wrapBodyInlineWithPara();
2993 var paras = rng.nodes(dom.isPara, { includeAncestor: true });
2994 var clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
2995 $$1.each(clustereds, function (idx, paras) {
2996 var head = lists.head(paras);
2997 if (dom.isLi(head)) {
2998 _this.releaseList([paras]);
2999 }
3000 else {
3001 $$1.each(paras, function (idx, para) {
3002 $$1(para).css('marginLeft', function (idx, val) {
3003 val = (parseInt(val, 10) || 0);
3004 return val > 25 ? val - 25 : '';
3005 });
3006 });
3007 }
3008 });
3009 rng.select();
3010 };
3011 /**
3012 * toggle list
3013 *
3014 * @param {String} listName - OL or UL
3015 */
3016 Bullet.prototype.toggleList = function (listName, editable) {
3017 var _this = this;
3018 var rng = range.create(editable).wrapBodyInlineWithPara();
3019 var paras = rng.nodes(dom.isPara, { includeAncestor: true });
3020 var bookmark = rng.paraBookmark(paras);
3021 var clustereds = lists.clusterBy(paras, func.peq2('parentNode'));
3022 // paragraph to list
3023 if (lists.find(paras, dom.isPurePara)) {
3024 var wrappedParas_1 = [];
3025 $$1.each(clustereds, function (idx, paras) {
3026 wrappedParas_1 = wrappedParas_1.concat(_this.wrapList(paras, listName));
3027 });
3028 paras = wrappedParas_1;
3029 // list to paragraph or change list style
3030 }
3031 else {
3032 var diffLists = rng.nodes(dom.isList, {
3033 includeAncestor: true
3034 }).filter(function (listNode) {
3035 return !$$1.nodeName(listNode, listName);
3036 });
3037 if (diffLists.length) {
3038 $$1.each(diffLists, function (idx, listNode) {
3039 dom.replace(listNode, listName);
3040 });
3041 }
3042 else {
3043 paras = this.releaseList(clustereds, true);
3044 }
3045 }
3046 range.createFromParaBookmark(bookmark, paras).select();
3047 };
3048 /**
3049 * @param {Node[]} paras
3050 * @param {String} listName
3051 * @return {Node[]}
3052 */
3053 Bullet.prototype.wrapList = function (paras, listName) {
3054 var head = lists.head(paras);
3055 var last = lists.last(paras);
3056 var prevList = dom.isList(head.previousSibling) && head.previousSibling;
3057 var nextList = dom.isList(last.nextSibling) && last.nextSibling;
3058 var listNode = prevList || dom.insertAfter(dom.create(listName || 'UL'), last);
3059 // P to LI
3060 paras = paras.map(function (para) {
3061 return dom.isPurePara(para) ? dom.replace(para, 'LI') : para;
3062 });
3063 // append to list(<ul>, <ol>)
3064 dom.appendChildNodes(listNode, paras);
3065 if (nextList) {
3066 dom.appendChildNodes(listNode, lists.from(nextList.childNodes));
3067 dom.remove(nextList);
3068 }
3069 return paras;
3070 };
3071 /**
3072 * @method releaseList
3073 *
3074 * @param {Array[]} clustereds
3075 * @param {Boolean} isEscapseToBody
3076 * @return {Node[]}
3077 */
3078 Bullet.prototype.releaseList = function (clustereds, isEscapseToBody) {
3079 var _this = this;
3080 var releasedParas = [];
3081 $$1.each(clustereds, function (idx, paras) {
3082 var head = lists.head(paras);
3083 var last = lists.last(paras);
3084 var headList = isEscapseToBody ? dom.lastAncestor(head, dom.isList) : head.parentNode;
3085 var parentItem = headList.parentNode;
3086 if (headList.parentNode.nodeName === 'LI') {
3087 paras.map(function (para) {
3088 var newList = _this.findNextSiblings(para);
3089 if (parentItem.nextSibling) {
3090 parentItem.parentNode.insertBefore(para, parentItem.nextSibling);
3091 }
3092 else {
3093 parentItem.parentNode.appendChild(para);
3094 }
3095 if (newList.length) {
3096 _this.wrapList(newList, headList.nodeName);
3097 para.appendChild(newList[0].parentNode);
3098 }
3099 });
3100 if (headList.children.length === 0) {
3101 parentItem.removeChild(headList);
3102 }
3103 if (parentItem.childNodes.length === 0) {
3104 parentItem.parentNode.removeChild(parentItem);
3105 }
3106 }
3107 else {
3108 var lastList = headList.childNodes.length > 1 ? dom.splitTree(headList, {
3109 node: last.parentNode,
3110 offset: dom.position(last) + 1
3111 }, {
3112 isSkipPaddingBlankHTML: true
3113 }) : null;
3114 var middleList = dom.splitTree(headList, {
3115 node: head.parentNode,
3116 offset: dom.position(head)
3117 }, {
3118 isSkipPaddingBlankHTML: true
3119 });
3120 paras = isEscapseToBody ? dom.listDescendant(middleList, dom.isLi)
3121 : lists.from(middleList.childNodes).filter(dom.isLi);
3122 // LI to P
3123 if (isEscapseToBody || !dom.isList(headList.parentNode)) {
3124 paras = paras.map(function (para) {
3125 return dom.replace(para, 'P');
3126 });
3127 }
3128 $$1.each(lists.from(paras).reverse(), function (idx, para) {
3129 dom.insertAfter(para, headList);
3130 });
3131 // remove empty lists
3132 var rootLists = lists.compact([headList, middleList, lastList]);
3133 $$1.each(rootLists, function (idx, rootList) {
3134 var listNodes = [rootList].concat(dom.listDescendant(rootList, dom.isList));
3135 $$1.each(listNodes.reverse(), function (idx, listNode) {
3136 if (!dom.nodeLength(listNode)) {
3137 dom.remove(listNode, true);
3138 }
3139 });
3140 });
3141 }
3142 releasedParas = releasedParas.concat(paras);
3143 });
3144 return releasedParas;
3145 };
3146 /**
3147 * @method appendToPrevious
3148 *
3149 * Appends list to previous list item, if
3150 * none exist it wraps the list in a new list item.
3151 *
3152 * @param {HTMLNode} ListItem
3153 * @return {HTMLNode}
3154 */
3155 Bullet.prototype.appendToPrevious = function (node) {
3156 return node.previousSibling
3157 ? dom.appendChildNodes(node.previousSibling, [node])
3158 : this.wrapList([node], 'LI');
3159 };
3160 /**
3161 * @method findList
3162 *
3163 * Finds an existing list in list item
3164 *
3165 * @param {HTMLNode} ListItem
3166 * @return {Array[]}
3167 */
3168 Bullet.prototype.findList = function (node) {
3169 return node
3170 ? lists.find(node.children, function (child) { return ['OL', 'UL'].indexOf(child.nodeName) > -1; })
3171 : null;
3172 };
3173 /**
3174 * @method findNextSiblings
3175 *
3176 * Finds all list item siblings that follow it
3177 *
3178 * @param {HTMLNode} ListItem
3179 * @return {HTMLNode}
3180 */
3181 Bullet.prototype.findNextSiblings = function (node) {
3182 var siblings = [];
3183 while (node.nextSibling) {
3184 siblings.push(node.nextSibling);
3185 node = node.nextSibling;
3186 }
3187 return siblings;
3188 };
3189 return Bullet;
3190 }());
3191
3192 /**
3193 * @class editing.Typing
3194 *
3195 * Typing
3196 *
3197 */
3198 var Typing = /** @class */ (function () {
3199 function Typing(context) {
3200 // a Bullet instance to toggle lists off
3201 this.bullet = new Bullet();
3202 this.options = context.options;
3203 }
3204 /**
3205 * insert tab
3206 *
3207 * @param {WrappedRange} rng
3208 * @param {Number} tabsize
3209 */
3210 Typing.prototype.insertTab = function (rng, tabsize) {
3211 var tab = dom.createText(new Array(tabsize + 1).join(dom.NBSP_CHAR));
3212 rng = rng.deleteContents();
3213 rng.insertNode(tab, true);
3214 rng = range.create(tab, tabsize);
3215 rng.select();
3216 };
3217 /**
3218 * insert paragraph
3219 *
3220 * @param {jQuery} $editable
3221 * @param {WrappedRange} rng Can be used in unit tests to "mock" the range
3222 *
3223 * blockquoteBreakingLevel
3224 * 0 - No break, the new paragraph remains inside the quote
3225 * 1 - Break the first blockquote in the ancestors list
3226 * 2 - Break all blockquotes, so that the new paragraph is not quoted (this is the default)
3227 */
3228 Typing.prototype.insertParagraph = function (editable, rng) {
3229 rng = rng || range.create(editable);
3230 // deleteContents on range.
3231 rng = rng.deleteContents();
3232 // Wrap range if it needs to be wrapped by paragraph
3233 rng = rng.wrapBodyInlineWithPara();
3234 // finding paragraph
3235 var splitRoot = dom.ancestor(rng.sc, dom.isPara);
3236 var nextPara;
3237 // on paragraph: split paragraph
3238 if (splitRoot) {
3239 // if it is an empty line with li
3240 if (dom.isEmpty(splitRoot) && dom.isLi(splitRoot)) {
3241 // toogle UL/OL and escape
3242 this.bullet.toggleList(splitRoot.parentNode.nodeName);
3243 return;
3244 }
3245 else {
3246 var blockquote = null;
3247 if (this.options.blockquoteBreakingLevel === 1) {
3248 blockquote = dom.ancestor(splitRoot, dom.isBlockquote);
3249 }
3250 else if (this.options.blockquoteBreakingLevel === 2) {
3251 blockquote = dom.lastAncestor(splitRoot, dom.isBlockquote);
3252 }
3253 if (blockquote) {
3254 // We're inside a blockquote and options ask us to break it
3255 nextPara = $$1(dom.emptyPara)[0];
3256 // If the split is right before a <br>, remove it so that there's no "empty line"
3257 // after the split in the new blockquote created
3258 if (dom.isRightEdgePoint(rng.getStartPoint()) && dom.isBR(rng.sc.nextSibling)) {
3259 $$1(rng.sc.nextSibling).remove();
3260 }
3261 var split = dom.splitTree(blockquote, rng.getStartPoint(), { isDiscardEmptySplits: true });
3262 if (split) {
3263 split.parentNode.insertBefore(nextPara, split);
3264 }
3265 else {
3266 dom.insertAfter(nextPara, blockquote); // There's no split if we were at the end of the blockquote
3267 }
3268 }
3269 else {
3270 nextPara = dom.splitTree(splitRoot, rng.getStartPoint());
3271 // not a blockquote, just insert the paragraph
3272 var emptyAnchors = dom.listDescendant(splitRoot, dom.isEmptyAnchor);
3273 emptyAnchors = emptyAnchors.concat(dom.listDescendant(nextPara, dom.isEmptyAnchor));
3274 $$1.each(emptyAnchors, function (idx, anchor) {
3275 dom.remove(anchor);
3276 });
3277 // replace empty heading, pre or custom-made styleTag with P tag
3278 if ((dom.isHeading(nextPara) || dom.isPre(nextPara) || dom.isCustomStyleTag(nextPara)) && dom.isEmpty(nextPara)) {
3279 nextPara = dom.replace(nextPara, 'p');
3280 }
3281 }
3282 }
3283 // no paragraph: insert empty paragraph
3284 }
3285 else {
3286 var next = rng.sc.childNodes[rng.so];
3287 nextPara = $$1(dom.emptyPara)[0];
3288 if (next) {
3289 rng.sc.insertBefore(nextPara, next);
3290 }
3291 else {
3292 rng.sc.appendChild(nextPara);
3293 }
3294 }
3295 range.create(nextPara, 0).normalize().select().scrollIntoView(editable);
3296 };
3297 return Typing;
3298 }());
3299
3300 /**
3301 * @class Create a virtual table to create what actions to do in change.
3302 * @param {object} startPoint Cell selected to apply change.
3303 * @param {enum} where Where change will be applied Row or Col. Use enum: TableResultAction.where
3304 * @param {enum} action Action to be applied. Use enum: TableResultAction.requestAction
3305 * @param {object} domTable Dom element of table to make changes.
3306 */
3307 var TableResultAction = function (startPoint, where, action, domTable) {
3308 var _startPoint = { 'colPos': 0, 'rowPos': 0 };
3309 var _virtualTable = [];
3310 var _actionCellList = [];
3311 /// ///////////////////////////////////////////
3312 // Private functions
3313 /// ///////////////////////////////////////////
3314 /**
3315 * Set the startPoint of action.
3316 */
3317 function setStartPoint() {
3318 if (!startPoint || !startPoint.tagName || (startPoint.tagName.toLowerCase() !== 'td' && startPoint.tagName.toLowerCase() !== 'th')) {
3319 console.error('Impossible to identify start Cell point.', startPoint);
3320 return;
3321 }
3322 _startPoint.colPos = startPoint.cellIndex;
3323 if (!startPoint.parentElement || !startPoint.parentElement.tagName || startPoint.parentElement.tagName.toLowerCase() !== 'tr') {
3324 console.error('Impossible to identify start Row point.', startPoint);
3325 return;
3326 }
3327 _startPoint.rowPos = startPoint.parentElement.rowIndex;
3328 }
3329 /**
3330 * Define virtual table position info object.
3331 *
3332 * @param {int} rowIndex Index position in line of virtual table.
3333 * @param {int} cellIndex Index position in column of virtual table.
3334 * @param {object} baseRow Row affected by this position.
3335 * @param {object} baseCell Cell affected by this position.
3336 * @param {bool} isSpan Inform if it is an span cell/row.
3337 */
3338 function setVirtualTablePosition(rowIndex, cellIndex, baseRow, baseCell, isRowSpan, isColSpan, isVirtualCell) {
3339 var objPosition = {
3340 'baseRow': baseRow,
3341 'baseCell': baseCell,
3342 'isRowSpan': isRowSpan,
3343 'isColSpan': isColSpan,
3344 'isVirtual': isVirtualCell
3345 };
3346 if (!_virtualTable[rowIndex]) {
3347 _virtualTable[rowIndex] = [];
3348 }
3349 _virtualTable[rowIndex][cellIndex] = objPosition;
3350 }
3351 /**
3352 * Create action cell object.
3353 *
3354 * @param {object} virtualTableCellObj Object of specific position on virtual table.
3355 * @param {enum} resultAction Action to be applied in that item.
3356 */
3357 function getActionCell(virtualTableCellObj, resultAction, virtualRowPosition, virtualColPosition) {
3358 return {
3359 'baseCell': virtualTableCellObj.baseCell,
3360 'action': resultAction,
3361 'virtualTable': {
3362 'rowIndex': virtualRowPosition,
3363 'cellIndex': virtualColPosition
3364 }
3365 };
3366 }
3367 /**
3368 * Recover free index of row to append Cell.
3369 *
3370 * @param {int} rowIndex Index of row to find free space.
3371 * @param {int} cellIndex Index of cell to find free space in table.
3372 */
3373 function recoverCellIndex(rowIndex, cellIndex) {
3374 if (!_virtualTable[rowIndex]) {
3375 return cellIndex;
3376 }
3377 if (!_virtualTable[rowIndex][cellIndex]) {
3378 return cellIndex;
3379 }
3380 var newCellIndex = cellIndex;
3381 while (_virtualTable[rowIndex][newCellIndex]) {
3382 newCellIndex++;
3383 if (!_virtualTable[rowIndex][newCellIndex]) {
3384 return newCellIndex;
3385 }
3386 }
3387 }
3388 /**
3389 * Recover info about row and cell and add information to virtual table.
3390 *
3391 * @param {object} row Row to recover information.
3392 * @param {object} cell Cell to recover information.
3393 */
3394 function addCellInfoToVirtual(row, cell) {
3395 var cellIndex = recoverCellIndex(row.rowIndex, cell.cellIndex);
3396 var cellHasColspan = (cell.colSpan > 1);
3397 var cellHasRowspan = (cell.rowSpan > 1);
3398 var isThisSelectedCell = (row.rowIndex === _startPoint.rowPos && cell.cellIndex === _startPoint.colPos);
3399 setVirtualTablePosition(row.rowIndex, cellIndex, row, cell, cellHasRowspan, cellHasColspan, false);
3400 // Add span rows to virtual Table.
3401 var rowspanNumber = cell.attributes.rowSpan ? parseInt(cell.attributes.rowSpan.value, 10) : 0;
3402 if (rowspanNumber > 1) {
3403 for (var rp = 1; rp < rowspanNumber; rp++) {
3404 var rowspanIndex = row.rowIndex + rp;
3405 adjustStartPoint(rowspanIndex, cellIndex, cell, isThisSelectedCell);
3406 setVirtualTablePosition(rowspanIndex, cellIndex, row, cell, true, cellHasColspan, true);
3407 }
3408 }
3409 // Add span cols to virtual table.
3410 var colspanNumber = cell.attributes.colSpan ? parseInt(cell.attributes.colSpan.value, 10) : 0;
3411 if (colspanNumber > 1) {
3412 for (var cp = 1; cp < colspanNumber; cp++) {
3413 var cellspanIndex = recoverCellIndex(row.rowIndex, (cellIndex + cp));
3414 adjustStartPoint(row.rowIndex, cellspanIndex, cell, isThisSelectedCell);
3415 setVirtualTablePosition(row.rowIndex, cellspanIndex, row, cell, cellHasRowspan, true, true);
3416 }
3417 }
3418 }
3419 /**
3420 * Process validation and adjust of start point if needed
3421 *
3422 * @param {int} rowIndex
3423 * @param {int} cellIndex
3424 * @param {object} cell
3425 * @param {bool} isSelectedCell
3426 */
3427 function adjustStartPoint(rowIndex, cellIndex, cell, isSelectedCell) {
3428 if (rowIndex === _startPoint.rowPos && _startPoint.colPos >= cell.cellIndex && cell.cellIndex <= cellIndex && !isSelectedCell) {
3429 _startPoint.colPos++;
3430 }
3431 }
3432 /**
3433 * Create virtual table of cells with all cells, including span cells.
3434 */
3435 function createVirtualTable() {
3436 var rows = domTable.rows;
3437 for (var rowIndex = 0; rowIndex < rows.length; rowIndex++) {
3438 var cells = rows[rowIndex].cells;
3439 for (var cellIndex = 0; cellIndex < cells.length; cellIndex++) {
3440 addCellInfoToVirtual(rows[rowIndex], cells[cellIndex]);
3441 }
3442 }
3443 }
3444 /**
3445 * Get action to be applied on the cell.
3446 *
3447 * @param {object} cell virtual table cell to apply action
3448 */
3449 function getDeleteResultActionToCell(cell) {
3450 switch (where) {
3451 case TableResultAction.where.Column:
3452 if (cell.isColSpan) {
3453 return TableResultAction.resultAction.SubtractSpanCount;
3454 }
3455 break;
3456 case TableResultAction.where.Row:
3457 if (!cell.isVirtual && cell.isRowSpan) {
3458 return TableResultAction.resultAction.AddCell;
3459 }
3460 else if (cell.isRowSpan) {
3461 return TableResultAction.resultAction.SubtractSpanCount;
3462 }
3463 break;
3464 }
3465 return TableResultAction.resultAction.RemoveCell;
3466 }
3467 /**
3468 * Get action to be applied on the cell.
3469 *
3470 * @param {object} cell virtual table cell to apply action
3471 */
3472 function getAddResultActionToCell(cell) {
3473 switch (where) {
3474 case TableResultAction.where.Column:
3475 if (cell.isColSpan) {
3476 return TableResultAction.resultAction.SumSpanCount;
3477 }
3478 else if (cell.isRowSpan && cell.isVirtual) {
3479 return TableResultAction.resultAction.Ignore;
3480 }
3481 break;
3482 case TableResultAction.where.Row:
3483 if (cell.isRowSpan) {
3484 return TableResultAction.resultAction.SumSpanCount;
3485 }
3486 else if (cell.isColSpan && cell.isVirtual) {
3487 return TableResultAction.resultAction.Ignore;
3488 }
3489 break;
3490 }
3491 return TableResultAction.resultAction.AddCell;
3492 }
3493 function init() {
3494 setStartPoint();
3495 createVirtualTable();
3496 }
3497 /// ///////////////////////////////////////////
3498 // Public functions
3499 /// ///////////////////////////////////////////
3500 /**
3501 * Recover array os what to do in table.
3502 */
3503 this.getActionList = function () {
3504 var fixedRow = (where === TableResultAction.where.Row) ? _startPoint.rowPos : -1;
3505 var fixedCol = (where === TableResultAction.where.Column) ? _startPoint.colPos : -1;
3506 var actualPosition = 0;
3507 var canContinue = true;
3508 while (canContinue) {
3509 var rowPosition = (fixedRow >= 0) ? fixedRow : actualPosition;
3510 var colPosition = (fixedCol >= 0) ? fixedCol : actualPosition;
3511 var row = _virtualTable[rowPosition];
3512 if (!row) {
3513 canContinue = false;
3514 return _actionCellList;
3515 }
3516 var cell = row[colPosition];
3517 if (!cell) {
3518 canContinue = false;
3519 return _actionCellList;
3520 }
3521 // Define action to be applied in this cell
3522 var resultAction = TableResultAction.resultAction.Ignore;
3523 switch (action) {
3524 case TableResultAction.requestAction.Add:
3525 resultAction = getAddResultActionToCell(cell);
3526 break;
3527 case TableResultAction.requestAction.Delete:
3528 resultAction = getDeleteResultActionToCell(cell);
3529 break;
3530 }
3531 _actionCellList.push(getActionCell(cell, resultAction, rowPosition, colPosition));
3532 actualPosition++;
3533 }
3534 return _actionCellList;
3535 };
3536 init();
3537 };
3538 /**
3539 *
3540 * Where action occours enum.
3541 */
3542 TableResultAction.where = { 'Row': 0, 'Column': 1 };
3543 /**
3544 *
3545 * Requested action to apply enum.
3546 */
3547 TableResultAction.requestAction = { 'Add': 0, 'Delete': 1 };
3548 /**
3549 *
3550 * Result action to be executed enum.
3551 */
3552 TableResultAction.resultAction = { 'Ignore': 0, 'SubtractSpanCount': 1, 'RemoveCell': 2, 'AddCell': 3, 'SumSpanCount': 4 };
3553 /**
3554 *
3555 * @class editing.Table
3556 *
3557 * Table
3558 *
3559 */
3560 var Table = /** @class */ (function () {
3561 function Table() {
3562 }
3563 /**
3564 * handle tab key
3565 *
3566 * @param {WrappedRange} rng
3567 * @param {Boolean} isShift
3568 */
3569 Table.prototype.tab = function (rng, isShift) {
3570 var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
3571 var table = dom.ancestor(cell, dom.isTable);
3572 var cells = dom.listDescendant(table, dom.isCell);
3573 var nextCell = lists[isShift ? 'prev' : 'next'](cells, cell);
3574 if (nextCell) {
3575 range.create(nextCell, 0).select();
3576 }
3577 };
3578 /**
3579 * Add a new row
3580 *
3581 * @param {WrappedRange} rng
3582 * @param {String} position (top/bottom)
3583 * @return {Node}
3584 */
3585 Table.prototype.addRow = function (rng, position) {
3586 var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
3587 var currentTr = $$1(cell).closest('tr');
3588 var trAttributes = this.recoverAttributes(currentTr);
3589 var html = $$1('<tr' + trAttributes + '></tr>');
3590 var vTable = new TableResultAction(cell, TableResultAction.where.Row, TableResultAction.requestAction.Add, $$1(currentTr).closest('table')[0]);
3591 var actions = vTable.getActionList();
3592 for (var idCell = 0; idCell < actions.length; idCell++) {
3593 var currentCell = actions[idCell];
3594 var tdAttributes = this.recoverAttributes(currentCell.baseCell);
3595 switch (currentCell.action) {
3596 case TableResultAction.resultAction.AddCell:
3597 html.append('<td' + tdAttributes + '>' + dom.blank + '</td>');
3598 break;
3599 case TableResultAction.resultAction.SumSpanCount:
3600 if (position === 'top') {
3601 var baseCellTr = currentCell.baseCell.parent;
3602 var isTopFromRowSpan = (!baseCellTr ? 0 : currentCell.baseCell.closest('tr').rowIndex) <= currentTr[0].rowIndex;
3603 if (isTopFromRowSpan) {
3604 var newTd = $$1('<div></div>').append($$1('<td' + tdAttributes + '>' + dom.blank + '</td>').removeAttr('rowspan')).html();
3605 html.append(newTd);
3606 break;
3607 }
3608 }
3609 var rowspanNumber = parseInt(currentCell.baseCell.rowSpan, 10);
3610 rowspanNumber++;
3611 currentCell.baseCell.setAttribute('rowSpan', rowspanNumber);
3612 break;
3613 }
3614 }
3615 if (position === 'top') {
3616 currentTr.before(html);
3617 }
3618 else {
3619 var cellHasRowspan = (cell.rowSpan > 1);
3620 if (cellHasRowspan) {
3621 var lastTrIndex = currentTr[0].rowIndex + (cell.rowSpan - 2);
3622 $$1($$1(currentTr).parent().find('tr')[lastTrIndex]).after($$1(html));
3623 return;
3624 }
3625 currentTr.after(html);
3626 }
3627 };
3628 /**
3629 * Add a new col
3630 *
3631 * @param {WrappedRange} rng
3632 * @param {String} position (left/right)
3633 * @return {Node}
3634 */
3635 Table.prototype.addCol = function (rng, position) {
3636 var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
3637 var row = $$1(cell).closest('tr');
3638 var rowsGroup = $$1(row).siblings();
3639 rowsGroup.push(row);
3640 var vTable = new TableResultAction(cell, TableResultAction.where.Column, TableResultAction.requestAction.Add, $$1(row).closest('table')[0]);
3641 var actions = vTable.getActionList();
3642 for (var actionIndex = 0; actionIndex < actions.length; actionIndex++) {
3643 var currentCell = actions[actionIndex];
3644 var tdAttributes = this.recoverAttributes(currentCell.baseCell);
3645 switch (currentCell.action) {
3646 case TableResultAction.resultAction.AddCell:
3647 if (position === 'right') {
3648 $$1(currentCell.baseCell).after('<td' + tdAttributes + '>' + dom.blank + '</td>');
3649 }
3650 else {
3651 $$1(currentCell.baseCell).before('<td' + tdAttributes + '>' + dom.blank + '</td>');
3652 }
3653 break;
3654 case TableResultAction.resultAction.SumSpanCount:
3655 if (position === 'right') {
3656 var colspanNumber = parseInt(currentCell.baseCell.colSpan, 10);
3657 colspanNumber++;
3658 currentCell.baseCell.setAttribute('colSpan', colspanNumber);
3659 }
3660 else {
3661 $$1(currentCell.baseCell).before('<td' + tdAttributes + '>' + dom.blank + '</td>');
3662 }
3663 break;
3664 }
3665 }
3666 };
3667 /*
3668 * Copy attributes from element.
3669 *
3670 * @param {object} Element to recover attributes.
3671 * @return {string} Copied string elements.
3672 */
3673 Table.prototype.recoverAttributes = function (el) {
3674 var resultStr = '';
3675 if (!el) {
3676 return resultStr;
3677 }
3678 var attrList = el.attributes || [];
3679 for (var i = 0; i < attrList.length; i++) {
3680 if (attrList[i].name.toLowerCase() === 'id') {
3681 continue;
3682 }
3683 if (attrList[i].specified) {
3684 resultStr += ' ' + attrList[i].name + '=\'' + attrList[i].value + '\'';
3685 }
3686 }
3687 return resultStr;
3688 };
3689 /**
3690 * Delete current row
3691 *
3692 * @param {WrappedRange} rng
3693 * @return {Node}
3694 */
3695 Table.prototype.deleteRow = function (rng) {
3696 var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
3697 var row = $$1(cell).closest('tr');
3698 var cellPos = row.children('td, th').index($$1(cell));
3699 var rowPos = row[0].rowIndex;
3700 var vTable = new TableResultAction(cell, TableResultAction.where.Row, TableResultAction.requestAction.Delete, $$1(row).closest('table')[0]);
3701 var actions = vTable.getActionList();
3702 for (var actionIndex = 0; actionIndex < actions.length; actionIndex++) {
3703 if (!actions[actionIndex]) {
3704 continue;
3705 }
3706 var baseCell = actions[actionIndex].baseCell;
3707 var virtualPosition = actions[actionIndex].virtualTable;
3708 var hasRowspan = (baseCell.rowSpan && baseCell.rowSpan > 1);
3709 var rowspanNumber = (hasRowspan) ? parseInt(baseCell.rowSpan, 10) : 0;
3710 switch (actions[actionIndex].action) {
3711 case TableResultAction.resultAction.Ignore:
3712 continue;
3713 case TableResultAction.resultAction.AddCell:
3714 var nextRow = row.next('tr')[0];
3715 if (!nextRow) {
3716 continue;
3717 }
3718 var cloneRow = row[0].cells[cellPos];
3719 if (hasRowspan) {
3720 if (rowspanNumber > 2) {
3721 rowspanNumber--;
3722 nextRow.insertBefore(cloneRow, nextRow.cells[cellPos]);
3723 nextRow.cells[cellPos].setAttribute('rowSpan', rowspanNumber);
3724 nextRow.cells[cellPos].innerHTML = '';
3725 }
3726 else if (rowspanNumber === 2) {
3727 nextRow.insertBefore(cloneRow, nextRow.cells[cellPos]);
3728 nextRow.cells[cellPos].removeAttribute('rowSpan');
3729 nextRow.cells[cellPos].innerHTML = '';
3730 }
3731 }
3732 continue;
3733 case TableResultAction.resultAction.SubtractSpanCount:
3734 if (hasRowspan) {
3735 if (rowspanNumber > 2) {
3736 rowspanNumber--;
3737 baseCell.setAttribute('rowSpan', rowspanNumber);
3738 if (virtualPosition.rowIndex !== rowPos && baseCell.cellIndex === cellPos) {
3739 baseCell.innerHTML = '';
3740 }
3741 }
3742 else if (rowspanNumber === 2) {
3743 baseCell.removeAttribute('rowSpan');
3744 if (virtualPosition.rowIndex !== rowPos && baseCell.cellIndex === cellPos) {
3745 baseCell.innerHTML = '';
3746 }
3747 }
3748 }
3749 continue;
3750 case TableResultAction.resultAction.RemoveCell:
3751 // Do not need remove cell because row will be deleted.
3752 continue;
3753 }
3754 }
3755 row.remove();
3756 };
3757 /**
3758 * Delete current col
3759 *
3760 * @param {WrappedRange} rng
3761 * @return {Node}
3762 */
3763 Table.prototype.deleteCol = function (rng) {
3764 var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
3765 var row = $$1(cell).closest('tr');
3766 var cellPos = row.children('td, th').index($$1(cell));
3767 var vTable = new TableResultAction(cell, TableResultAction.where.Column, TableResultAction.requestAction.Delete, $$1(row).closest('table')[0]);
3768 var actions = vTable.getActionList();
3769 for (var actionIndex = 0; actionIndex < actions.length; actionIndex++) {
3770 if (!actions[actionIndex]) {
3771 continue;
3772 }
3773 switch (actions[actionIndex].action) {
3774 case TableResultAction.resultAction.Ignore:
3775 continue;
3776 case TableResultAction.resultAction.SubtractSpanCount:
3777 var baseCell = actions[actionIndex].baseCell;
3778 var hasColspan = (baseCell.colSpan && baseCell.colSpan > 1);
3779 if (hasColspan) {
3780 var colspanNumber = (baseCell.colSpan) ? parseInt(baseCell.colSpan, 10) : 0;
3781 if (colspanNumber > 2) {
3782 colspanNumber--;
3783 baseCell.setAttribute('colSpan', colspanNumber);
3784 if (baseCell.cellIndex === cellPos) {
3785 baseCell.innerHTML = '';
3786 }
3787 }
3788 else if (colspanNumber === 2) {
3789 baseCell.removeAttribute('colSpan');
3790 if (baseCell.cellIndex === cellPos) {
3791 baseCell.innerHTML = '';
3792 }
3793 }
3794 }
3795 continue;
3796 case TableResultAction.resultAction.RemoveCell:
3797 dom.remove(actions[actionIndex].baseCell, true);
3798 continue;
3799 }
3800 }
3801 };
3802 /**
3803 * create empty table element
3804 *
3805 * @param {Number} rowCount
3806 * @param {Number} colCount
3807 * @return {Node}
3808 */
3809 Table.prototype.createTable = function (colCount, rowCount, options) {
3810 var tds = [];
3811 var tdHTML;
3812 for (var idxCol = 0; idxCol < colCount; idxCol++) {
3813 tds.push('<td>' + dom.blank + '</td>');
3814 }
3815 tdHTML = tds.join('');
3816 var trs = [];
3817 var trHTML;
3818 for (var idxRow = 0; idxRow < rowCount; idxRow++) {
3819 trs.push('<tr>' + tdHTML + '</tr>');
3820 }
3821 trHTML = trs.join('');
3822 var $table = $$1('<table>' + trHTML + '</table>');
3823 if (options && options.tableClassName) {
3824 $table.addClass(options.tableClassName);
3825 }
3826 return $table[0];
3827 };
3828 /**
3829 * Delete current table
3830 *
3831 * @param {WrappedRange} rng
3832 * @return {Node}
3833 */
3834 Table.prototype.deleteTable = function (rng) {
3835 var cell = dom.ancestor(rng.commonAncestor(), dom.isCell);
3836 $$1(cell).closest('table').remove();
3837 };
3838 return Table;
3839 }());
3840
3841 var KEY_BOGUS = 'bogus';
3842 /**
3843 * @class Editor
3844 */
3845 var Editor = /** @class */ (function () {
3846 function Editor(context) {
3847 var _this = this;
3848 this.context = context;
3849 this.$note = context.layoutInfo.note;
3850 this.$editor = context.layoutInfo.editor;
3851 this.$editable = context.layoutInfo.editable;
3852 this.options = context.options;
3853 this.lang = this.options.langInfo;
3854 this.editable = this.$editable[0];
3855 this.lastRange = null;
3856 this.style = new Style();
3857 this.table = new Table();
3858 this.typing = new Typing(context);
3859 this.bullet = new Bullet();
3860 this.history = new History(this.$editable);
3861 this.context.memo('help.undo', this.lang.help.undo);
3862 this.context.memo('help.redo', this.lang.help.redo);
3863 this.context.memo('help.tab', this.lang.help.tab);
3864 this.context.memo('help.untab', this.lang.help.untab);
3865 this.context.memo('help.insertParagraph', this.lang.help.insertParagraph);
3866 this.context.memo('help.insertOrderedList', this.lang.help.insertOrderedList);
3867 this.context.memo('help.insertUnorderedList', this.lang.help.insertUnorderedList);
3868 this.context.memo('help.indent', this.lang.help.indent);
3869 this.context.memo('help.outdent', this.lang.help.outdent);
3870 this.context.memo('help.formatPara', this.lang.help.formatPara);
3871 this.context.memo('help.insertHorizontalRule', this.lang.help.insertHorizontalRule);
3872 this.context.memo('help.fontName', this.lang.help.fontName);
3873 // native commands(with execCommand), generate function for execCommand
3874 var commands = [
3875 'bold', 'italic', 'underline', 'strikethrough', 'superscript', 'subscript',
3876 'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull',
3877 'formatBlock', 'removeFormat', 'backColor'
3878 ];
3879 for (var idx = 0, len = commands.length; idx < len; idx++) {
3880 this[commands[idx]] = (function (sCmd) {
3881 return function (value) {
3882 _this.beforeCommand();
3883 document.execCommand(sCmd, false, value);
3884 _this.afterCommand(true);
3885 };
3886 })(commands[idx]);
3887 this.context.memo('help.' + commands[idx], this.lang.help[commands[idx]]);
3888 }
3889 this.fontName = this.wrapCommand(function (value) {
3890 return _this.fontStyling('font-family', "\'" + value + "\'");
3891 });
3892 this.fontSize = this.wrapCommand(function (value) {
3893 return _this.fontStyling('font-size', value + 'px');
3894 });
3895 for (var idx = 1; idx <= 6; idx++) {
3896 this['formatH' + idx] = (function (idx) {
3897 return function () {
3898 _this.formatBlock('H' + idx);
3899 };
3900 })(idx);
3901 this.context.memo('help.formatH' + idx, this.lang.help['formatH' + idx]);
3902 }
3903 this.insertParagraph = this.wrapCommand(function () {
3904 _this.typing.insertParagraph(_this.editable);
3905 });
3906 this.insertOrderedList = this.wrapCommand(function () {
3907 _this.bullet.insertOrderedList(_this.editable);
3908 });
3909 this.insertUnorderedList = this.wrapCommand(function () {
3910 _this.bullet.insertUnorderedList(_this.editable);
3911 });
3912 this.indent = this.wrapCommand(function () {
3913 _this.bullet.indent(_this.editable);
3914 });
3915 this.outdent = this.wrapCommand(function () {
3916 _this.bullet.outdent(_this.editable);
3917 });
3918 /**
3919 * insertNode
3920 * insert node
3921 * @param {Node} node
3922 */
3923 this.insertNode = this.wrapCommand(function (node) {
3924 if (_this.isLimited($$1(node).text().length)) {
3925 return;
3926 }
3927 var rng = _this.createRange();
3928 rng.insertNode(node);
3929 range.createFromNodeAfter(node).select();
3930 });
3931 /**
3932 * insert text
3933 * @param {String} text
3934 */
3935 this.insertText = this.wrapCommand(function (text) {
3936 if (_this.isLimited(text.length)) {
3937 return;
3938 }
3939 var rng = _this.createRange();
3940 var textNode = rng.insertNode(dom.createText(text));
3941 range.create(textNode, dom.nodeLength(textNode)).select();
3942 });
3943 /**
3944 * paste HTML
3945 * @param {String} markup
3946 */
3947 this.pasteHTML = this.wrapCommand(function (markup) {
3948 if (_this.isLimited(markup.length)) {
3949 return;
3950 }
3951 var contents = _this.createRange().pasteHTML(markup);
3952 range.createFromNodeAfter(lists.last(contents)).select();
3953 });
3954 /**
3955 * formatBlock
3956 *
3957 * @param {String} tagName
3958 */
3959 this.formatBlock = this.wrapCommand(function (tagName, $target) {
3960 var onApplyCustomStyle = _this.options.callbacks.onApplyCustomStyle;
3961 if (onApplyCustomStyle) {
3962 onApplyCustomStyle.call(_this, $target, _this.context, _this.onFormatBlock);
3963 }
3964 else {
3965 _this.onFormatBlock(tagName, $target);
3966 }
3967 });
3968 /**
3969 * insert horizontal rule
3970 */
3971 this.insertHorizontalRule = this.wrapCommand(function () {
3972 var hrNode = _this.createRange().insertNode(dom.create('HR'));
3973 if (hrNode.nextSibling) {
3974 range.create(hrNode.nextSibling, 0).normalize().select();
3975 }
3976 });
3977 /**
3978 * lineHeight
3979 * @param {String} value
3980 */
3981 this.lineHeight = this.wrapCommand(function (value) {
3982 _this.style.stylePara(_this.createRange(), {
3983 lineHeight: value
3984 });
3985 });
3986 /**
3987 * create link (command)
3988 *
3989 * @param {Object} linkInfo
3990 */
3991 this.createLink = this.wrapCommand(function (linkInfo) {
3992 var linkUrl = linkInfo.url;
3993 var linkText = linkInfo.text;
3994 var isNewWindow = linkInfo.isNewWindow;
3995 var rng = linkInfo.range || _this.createRange();
3996 var additionalTextLength = linkText.length - rng.toString().length;
3997 if (additionalTextLength > 0 && _this.isLimited(additionalTextLength)) {
3998 return;
3999 }
4000 var isTextChanged = rng.toString() !== linkText;
4001 // handle spaced urls from input
4002 if (typeof linkUrl === 'string') {
4003 linkUrl = linkUrl.trim();
4004 }
4005 if (_this.options.onCreateLink) {
4006 linkUrl = _this.options.onCreateLink(linkUrl);
4007 }
4008 else {
4009 // if url is not relative,
4010 if (!/^\.?\/(.*)/.test(linkUrl)) {
4011 // if url doesn't match an URL schema, set http:// as default
4012 linkUrl = /^[A-Za-z][A-Za-z0-9+-.]*\:[\/\/]?/.test(linkUrl)
4013 ? linkUrl : 'http://' + linkUrl;
4014 }
4015 }
4016 var anchors = [];
4017 if (isTextChanged) {
4018 rng = rng.deleteContents();
4019 var anchor = rng.insertNode($$1('<A>' + linkText + '</A>')[0]);
4020 anchors.push(anchor);
4021 }
4022 else {
4023 anchors = _this.style.styleNodes(rng, {
4024 nodeName: 'A',
4025 expandClosestSibling: true,
4026 onlyPartialContains: true
4027 });
4028 }
4029 $$1.each(anchors, function (idx, anchor) {
4030 $$1(anchor).attr('href', linkUrl);
4031 if (isNewWindow) {
4032 $$1(anchor).attr('target', '_blank');
4033 }
4034 else {
4035 $$1(anchor).removeAttr('target');
4036 }
4037 });
4038 var startRange = range.createFromNodeBefore(lists.head(anchors));
4039 var startPoint = startRange.getStartPoint();
4040 var endRange = range.createFromNodeAfter(lists.last(anchors));
4041 var endPoint = endRange.getEndPoint();
4042 range.create(startPoint.node, startPoint.offset, endPoint.node, endPoint.offset).select();
4043 });
4044 /**
4045 * setting color
4046 *
4047 * @param {Object} sObjColor color code
4048 * @param {String} sObjColor.foreColor foreground color
4049 * @param {String} sObjColor.backColor background color
4050 */
4051 this.color = this.wrapCommand(function (colorInfo) {
4052 var foreColor = colorInfo.foreColor;
4053 var backColor = colorInfo.backColor;
4054 if (foreColor) {
4055 document.execCommand('foreColor', false, foreColor);
4056 }
4057 if (backColor) {
4058 document.execCommand('backColor', false, backColor);
4059 }
4060 });
4061 /**
4062 * Set foreground color
4063 *
4064 * @param {String} colorCode foreground color code
4065 */
4066 this.foreColor = this.wrapCommand(function (colorInfo) {
4067 document.execCommand('styleWithCSS', false, true);
4068 document.execCommand('foreColor', false, colorInfo);
4069 });
4070 /**
4071 * insert Table
4072 *
4073 * @param {String} dimension of table (ex : "5x5")
4074 */
4075 this.insertTable = this.wrapCommand(function (dim) {
4076 var dimension = dim.split('x');
4077 var rng = _this.createRange().deleteContents();
4078 rng.insertNode(_this.table.createTable(dimension[0], dimension[1], _this.options));
4079 });
4080 /**
4081 * remove media object and Figure Elements if media object is img with Figure.
4082 */
4083 this.removeMedia = this.wrapCommand(function () {
4084 var $target = $$1(_this.restoreTarget()).parent();
4085 if ($target.parent('figure').length) {
4086 $target.parent('figure').remove();
4087 }
4088 else {
4089 $target = $$1(_this.restoreTarget()).detach();
4090 }
4091 _this.context.triggerEvent('media.delete', $target, _this.$editable);
4092 });
4093 /**
4094 * float me
4095 *
4096 * @param {String} value
4097 */
4098 this.floatMe = this.wrapCommand(function (value) {
4099 var $target = $$1(_this.restoreTarget());
4100 $target.toggleClass('note-float-left', value === 'left');
4101 $target.toggleClass('note-float-right', value === 'right');
4102 $target.css('float', value);
4103 });
4104 /**
4105 * resize overlay element
4106 * @param {String} value
4107 */
4108 this.resize = this.wrapCommand(function (value) {
4109 var $target = $$1(_this.restoreTarget());
4110 $target.css({
4111 width: value * 100 + '%',
4112 height: ''
4113 });
4114 });
4115 }
4116 Editor.prototype.initialize = function () {
4117 var _this = this;
4118 // bind custom events
4119 this.$editable.on('keydown', function (event) {
4120 if (event.keyCode === key.code.ENTER) {
4121 _this.context.triggerEvent('enter', event);
4122 }
4123 _this.context.triggerEvent('keydown', event);
4124 if (!event.isDefaultPrevented()) {
4125 if (_this.options.shortcuts) {
4126 _this.handleKeyMap(event);
4127 }
4128 else {
4129 _this.preventDefaultEditableShortCuts(event);
4130 }
4131 }
4132 if (_this.isLimited(1, event)) {
4133 return false;
4134 }
4135 }).on('keyup', function (event) {
4136 _this.context.triggerEvent('keyup', event);
4137 }).on('focus', function (event) {
4138 _this.context.triggerEvent('focus', event);
4139 }).on('blur', function (event) {
4140 _this.context.triggerEvent('blur', event);
4141 }).on('mousedown', function (event) {
4142 _this.context.triggerEvent('mousedown', event);
4143 }).on('mouseup', function (event) {
4144 _this.context.triggerEvent('mouseup', event);
4145 }).on('scroll', function (event) {
4146 _this.context.triggerEvent('scroll', event);
4147 }).on('paste', function (event) {
4148 _this.context.triggerEvent('paste', event);
4149 });
4150 // init content before set event
4151 this.$editable.html(dom.html(this.$note) || dom.emptyPara);
4152 this.$editable.on(env.inputEventName, func.debounce(function () {
4153 _this.context.triggerEvent('change', _this.$editable.html());
4154 }, 10));
4155 this.$editor.on('focusin', function (event) {
4156 _this.context.triggerEvent('focusin', event);
4157 }).on('focusout', function (event) {
4158 _this.context.triggerEvent('focusout', event);
4159 });
4160 if (!this.options.airMode) {
4161 if (this.options.width) {
4162 this.$editor.outerWidth(this.options.width);
4163 }
4164 if (this.options.height) {
4165 this.$editable.outerHeight(this.options.height);
4166 }
4167 if (this.options.maxHeight) {
4168 this.$editable.css('max-height', this.options.maxHeight);
4169 }
4170 if (this.options.minHeight) {
4171 this.$editable.css('min-height', this.options.minHeight);
4172 }
4173 }
4174 this.history.recordUndo();
4175 };
4176 Editor.prototype.destroy = function () {
4177 this.$editable.off();
4178 };
4179 Editor.prototype.handleKeyMap = function (event) {
4180 var keyMap = this.options.keyMap[env.isMac ? 'mac' : 'pc'];
4181 var keys = [];
4182 if (event.metaKey) {
4183 keys.push('CMD');
4184 }
4185 if (event.ctrlKey && !event.altKey) {
4186 keys.push('CTRL');
4187 }
4188 if (event.shiftKey) {
4189 keys.push('SHIFT');
4190 }
4191 var keyName = key.nameFromCode[event.keyCode];
4192 if (keyName) {
4193 keys.push(keyName);
4194 }
4195 var eventName = keyMap[keys.join('+')];
4196 if (eventName) {
4197 if (this.context.invoke(eventName) !== false) {
4198 event.preventDefault();
4199 }
4200 }
4201 else if (key.isEdit(event.keyCode)) {
4202 this.afterCommand();
4203 }
4204 };
4205 Editor.prototype.preventDefaultEditableShortCuts = function (event) {
4206 // B(Bold, 66) / I(Italic, 73) / U(Underline, 85)
4207 if ((event.ctrlKey || event.metaKey) &&
4208 lists.contains([66, 73, 85], event.keyCode)) {
4209 event.preventDefault();
4210 }
4211 };
4212 Editor.prototype.isLimited = function (pad, event) {
4213 pad = pad || 0;
4214 if (typeof event !== 'undefined') {
4215 if (key.isMove(event.keyCode) ||
4216 (event.ctrlKey || event.metaKey) ||
4217 lists.contains([key.code.BACKSPACE, key.code.DELETE], event.keyCode)) {
4218 return false;
4219 }
4220 }
4221 if (this.options.maxTextLength > 0) {
4222 if ((this.$editable.text().length + pad) >= this.options.maxTextLength) {
4223 return true;
4224 }
4225 }
4226 return false;
4227 };
4228 /**
4229 * create range
4230 * @return {WrappedRange}
4231 */
4232 Editor.prototype.createRange = function () {
4233 this.focus();
4234 return range.create(this.editable);
4235 };
4236 /**
4237 * saveRange
4238 *
4239 * save current range
4240 *
4241 * @param {Boolean} [thenCollapse=false]
4242 */
4243 Editor.prototype.saveRange = function (thenCollapse) {
4244 this.lastRange = this.createRange();
4245 if (thenCollapse) {
4246 this.lastRange.collapse().select();
4247 }
4248 };
4249 /**
4250 * restoreRange
4251 *
4252 * restore lately range
4253 */
4254 Editor.prototype.restoreRange = function () {
4255 if (this.lastRange) {
4256 this.lastRange.select();
4257 this.focus();
4258 }
4259 };
4260 Editor.prototype.saveTarget = function (node) {
4261 this.$editable.data('target', node);
4262 };
4263 Editor.prototype.clearTarget = function () {
4264 this.$editable.removeData('target');
4265 };
4266 Editor.prototype.restoreTarget = function () {
4267 return this.$editable.data('target');
4268 };
4269 /**
4270 * currentStyle
4271 *
4272 * current style
4273 * @return {Object|Boolean} unfocus
4274 */
4275 Editor.prototype.currentStyle = function () {
4276 var rng = range.create();
4277 if (rng) {
4278 rng = rng.normalize();
4279 }
4280 return rng ? this.style.current(rng) : this.style.fromNode(this.$editable);
4281 };
4282 /**
4283 * style from node
4284 *
4285 * @param {jQuery} $node
4286 * @return {Object}
4287 */
4288 Editor.prototype.styleFromNode = function ($node) {
4289 return this.style.fromNode($node);
4290 };
4291 /**
4292 * undo
4293 */
4294 Editor.prototype.undo = function () {
4295 this.context.triggerEvent('before.command', this.$editable.html());
4296 this.history.undo();
4297 this.context.triggerEvent('change', this.$editable.html());
4298 };
4299 /*
4300 * commit
4301 */
4302 Editor.prototype.commit = function () {
4303 this.context.triggerEvent('before.command', this.$editable.html());
4304 this.history.commit();
4305 this.context.triggerEvent('change', this.$editable.html());
4306 };
4307 /**
4308 * redo
4309 */
4310 Editor.prototype.redo = function () {
4311 this.context.triggerEvent('before.command', this.$editable.html());
4312 this.history.redo();
4313 this.context.triggerEvent('change', this.$editable.html());
4314 };
4315 /**
4316 * before command
4317 */
4318 Editor.prototype.beforeCommand = function () {
4319 this.context.triggerEvent('before.command', this.$editable.html());
4320 // keep focus on editable before command execution
4321 this.focus();
4322 };
4323 /**
4324 * after command
4325 * @param {Boolean} isPreventTrigger
4326 */
4327 Editor.prototype.afterCommand = function (isPreventTrigger) {
4328 this.normalizeContent();
4329 this.history.recordUndo();
4330 if (!isPreventTrigger) {
4331 this.context.triggerEvent('change', this.$editable.html());
4332 }
4333 };
4334 /**
4335 * handle tab key
4336 */
4337 Editor.prototype.tab = function () {
4338 var rng = this.createRange();
4339 if (rng.isCollapsed() && rng.isOnCell()) {
4340 this.table.tab(rng);
4341 }
4342 else {
4343 if (this.options.tabSize === 0) {
4344 return false;
4345 }
4346 if (!this.isLimited(this.options.tabSize)) {
4347 this.beforeCommand();
4348 this.typing.insertTab(rng, this.options.tabSize);
4349 this.afterCommand();
4350 }
4351 }
4352 };
4353 /**
4354 * handle shift+tab key
4355 */
4356 Editor.prototype.untab = function () {
4357 var rng = this.createRange();
4358 if (rng.isCollapsed() && rng.isOnCell()) {
4359 this.table.tab(rng, true);
4360 }
4361 else {
4362 if (this.options.tabSize === 0) {
4363 return false;
4364 }
4365 }
4366 };
4367 /**
4368 * run given function between beforeCommand and afterCommand
4369 */
4370 Editor.prototype.wrapCommand = function (fn) {
4371 return function () {
4372 this.beforeCommand();
4373 fn.apply(this, arguments);
4374 this.afterCommand();
4375 };
4376 };
4377 /**
4378 * insert image
4379 *
4380 * @param {String} src
4381 * @param {String|Function} param
4382 * @return {Promise}
4383 */
4384 Editor.prototype.insertImage = function (src, param) {
4385 var _this = this;
4386 return createImage(src, param).then(function ($image) {
4387 _this.beforeCommand();
4388 if (typeof param === 'function') {
4389 param($image);
4390 }
4391 else {
4392 if (typeof param === 'string') {
4393 $image.attr('data-filename', param);
4394 }
4395 $image.css('width', Math.min(_this.$editable.width(), $image.width()));
4396 }
4397 $image.show();
4398 range.create(_this.editable).insertNode($image[0]);
4399 range.createFromNodeAfter($image[0]).select();
4400 _this.afterCommand();
4401 }).fail(function (e) {
4402 _this.context.triggerEvent('image.upload.error', e);
4403 });
4404 };
4405 /**
4406 * insertImages
4407 * @param {File[]} files
4408 */
4409 Editor.prototype.insertImagesAsDataURL = function (files) {
4410 var _this = this;
4411 $$1.each(files, function (idx, file) {
4412 var filename = file.name;
4413 if (_this.options.maximumImageFileSize && _this.options.maximumImageFileSize < file.size) {
4414 _this.context.triggerEvent('image.upload.error', _this.lang.image.maximumFileSizeError);
4415 }
4416 else {
4417 readFileAsDataURL(file).then(function (dataURL) {
4418 return _this.insertImage(dataURL, filename);
4419 }).fail(function () {
4420 _this.context.triggerEvent('image.upload.error');
4421 });
4422 }
4423 });
4424 };
4425 /**
4426 * return selected plain text
4427 * @return {String} text
4428 */
4429 Editor.prototype.getSelectedText = function () {
4430 var rng = this.createRange();
4431 // if range on anchor, expand range with anchor
4432 if (rng.isOnAnchor()) {
4433 rng = range.createFromNode(dom.ancestor(rng.sc, dom.isAnchor));
4434 }
4435 return rng.toString();
4436 };
4437 Editor.prototype.onFormatBlock = function (tagName, $target) {
4438 // [workaround] for MSIE, IE need `<`
4439 tagName = env.isMSIE ? '<' + tagName + '>' : tagName;
4440 document.execCommand('FormatBlock', false, tagName);
4441 // support custom class
4442 if ($target && $target.length) {
4443 var className = $target[0].className || '';
4444 if (className) {
4445 var currentRange = this.createRange();
4446 var $parent = $$1([currentRange.sc, currentRange.ec]).closest(tagName);
4447 $parent.addClass(className);
4448 }
4449 }
4450 };
4451 Editor.prototype.formatPara = function () {
4452 this.formatBlock('P');
4453 };
4454 Editor.prototype.fontStyling = function (target, value) {
4455 var rng = this.createRange();
4456 if (rng) {
4457 var spans = this.style.styleNodes(rng);
4458 $$1(spans).css(target, value);
4459 // [workaround] added styled bogus span for style
4460 // - also bogus character needed for cursor position
4461 if (rng.isCollapsed()) {
4462 var firstSpan = lists.head(spans);
4463 if (firstSpan && !dom.nodeLength(firstSpan)) {
4464 firstSpan.innerHTML = dom.ZERO_WIDTH_NBSP_CHAR;
4465 range.createFromNodeAfter(firstSpan.firstChild).select();
4466 this.$editable.data(KEY_BOGUS, firstSpan);
4467 }
4468 }
4469 }
4470 };
4471 /**
4472 * unlink
4473 *
4474 * @type command
4475 */
4476 Editor.prototype.unlink = function () {
4477 var rng = this.createRange();
4478 if (rng.isOnAnchor()) {
4479 var anchor = dom.ancestor(rng.sc, dom.isAnchor);
4480 rng = range.createFromNode(anchor);
4481 rng.select();
4482 this.beforeCommand();
4483 document.execCommand('unlink');
4484 this.afterCommand();
4485 }
4486 };
4487 /**
4488 * returns link info
4489 *
4490 * @return {Object}
4491 * @return {WrappedRange} return.range
4492 * @return {String} return.text
4493 * @return {Boolean} [return.isNewWindow=true]
4494 * @return {String} [return.url=""]
4495 */
4496 Editor.prototype.getLinkInfo = function () {
4497 var rng = this.createRange().expand(dom.isAnchor);
4498 // Get the first anchor on range(for edit).
4499 var $anchor = $$1(lists.head(rng.nodes(dom.isAnchor)));
4500 var linkInfo = {
4501 range: rng,
4502 text: rng.toString(),
4503 url: $anchor.length ? $anchor.attr('href') : ''
4504 };
4505 // When anchor exists,
4506 if ($anchor.length) {
4507 // Set isNewWindow by checking its target.
4508 linkInfo.isNewWindow = $anchor.attr('target') === '_blank';
4509 }
4510 return linkInfo;
4511 };
4512 Editor.prototype.addRow = function (position) {
4513 var rng = this.createRange(this.$editable);
4514 if (rng.isCollapsed() && rng.isOnCell()) {
4515 this.beforeCommand();
4516 this.table.addRow(rng, position);
4517 this.afterCommand();
4518 }
4519 };
4520 Editor.prototype.addCol = function (position) {
4521 var rng = this.createRange(this.$editable);
4522 if (rng.isCollapsed() && rng.isOnCell()) {
4523 this.beforeCommand();
4524 this.table.addCol(rng, position);
4525 this.afterCommand();
4526 }
4527 };
4528 Editor.prototype.deleteRow = function () {
4529 var rng = this.createRange(this.$editable);
4530 if (rng.isCollapsed() && rng.isOnCell()) {
4531 this.beforeCommand();
4532 this.table.deleteRow(rng);
4533 this.afterCommand();
4534 }
4535 };
4536 Editor.prototype.deleteCol = function () {
4537 var rng = this.createRange(this.$editable);
4538 if (rng.isCollapsed() && rng.isOnCell()) {
4539 this.beforeCommand();
4540 this.table.deleteCol(rng);
4541 this.afterCommand();
4542 }
4543 };
4544 Editor.prototype.deleteTable = function () {
4545 var rng = this.createRange(this.$editable);
4546 if (rng.isCollapsed() && rng.isOnCell()) {
4547 this.beforeCommand();
4548 this.table.deleteTable(rng);
4549 this.afterCommand();
4550 }
4551 };
4552 /**
4553 * @param {Position} pos
4554 * @param {jQuery} $target - target element
4555 * @param {Boolean} [bKeepRatio] - keep ratio
4556 */
4557 Editor.prototype.resizeTo = function (pos, $target, bKeepRatio) {
4558 var imageSize;
4559 if (bKeepRatio) {
4560 var newRatio = pos.y / pos.x;
4561 var ratio = $target.data('ratio');
4562 imageSize = {
4563 width: ratio > newRatio ? pos.x : pos.y / ratio,
4564 height: ratio > newRatio ? pos.x * ratio : pos.y
4565 };
4566 }
4567 else {
4568 imageSize = {
4569 width: pos.x,
4570 height: pos.y
4571 };
4572 }
4573 $target.css(imageSize);
4574 };
4575 /**
4576 * returns whether editable area has focus or not.
4577 */
4578 Editor.prototype.hasFocus = function () {
4579 return this.$editable.is(':focus');
4580 };
4581 /**
4582 * set focus
4583 */
4584 Editor.prototype.focus = function () {
4585 // [workaround] Screen will move when page is scolled in IE.
4586 // - do focus when not focused
4587 if (!this.hasFocus()) {
4588 this.$editable.focus();
4589 }
4590 };
4591 /**
4592 * returns whether contents is empty or not.
4593 * @return {Boolean}
4594 */
4595 Editor.prototype.isEmpty = function () {
4596 return dom.isEmpty(this.$editable[0]) || dom.emptyPara === this.$editable.html();
4597 };
4598 /**
4599 * Removes all contents and restores the editable instance to an _emptyPara_.
4600 */
4601 Editor.prototype.empty = function () {
4602 this.context.invoke('code', dom.emptyPara);
4603 };
4604 /**
4605 * normalize content
4606 */
4607 Editor.prototype.normalizeContent = function () {
4608 this.$editable[0].normalize();
4609 };
4610 return Editor;
4611 }());
4612
4613 var Clipboard = /** @class */ (function () {
4614 function Clipboard(context) {
4615 this.context = context;
4616 this.$editable = context.layoutInfo.editable;
4617 }
4618 Clipboard.prototype.initialize = function () {
4619 this.$editable.on('paste', this.pasteByEvent.bind(this));
4620 };
4621 /**
4622 * paste by clipboard event
4623 *
4624 * @param {Event} event
4625 */
4626 Clipboard.prototype.pasteByEvent = function (event) {
4627 var clipboardData = event.originalEvent.clipboardData;
4628 if (clipboardData && clipboardData.items && clipboardData.items.length) {
4629 // paste img file
4630 var item = clipboardData.items.length > 1 ? clipboardData.items[1] : lists.head(clipboardData.items);
4631 if (item.kind === 'file' && item.type.indexOf('image/') !== -1) {
4632 this.context.invoke('editor.insertImagesOrCallback', [item.getAsFile()]);
4633 }
4634 this.context.invoke('editor.afterCommand');
4635 }
4636 };
4637 return Clipboard;
4638 }());
4639
4640 var Dropzone = /** @class */ (function () {
4641 function Dropzone(context) {
4642 this.context = context;
4643 this.$eventListener = $$1(document);
4644 this.$editor = context.layoutInfo.editor;
4645 this.$editable = context.layoutInfo.editable;
4646 this.options = context.options;
4647 this.lang = this.options.langInfo;
4648 this.documentEventHandlers = {};
4649 this.$dropzone = $$1([
4650 '<div class="note-dropzone">',
4651 ' <div class="note-dropzone-message"/>',
4652 '</div>'
4653 ].join('')).prependTo(this.$editor);
4654 }
4655 /**
4656 * attach Drag and Drop Events
4657 */
4658 Dropzone.prototype.initialize = function () {
4659 if (this.options.disableDragAndDrop) {
4660 // prevent default drop event
4661 this.documentEventHandlers.onDrop = function (e) {
4662 e.preventDefault();
4663 };
4664 // do not consider outside of dropzone
4665 this.$eventListener = this.$dropzone;
4666 this.$eventListener.on('drop', this.documentEventHandlers.onDrop);
4667 }
4668 else {
4669 this.attachDragAndDropEvent();
4670 }
4671 };
4672 /**
4673 * attach Drag and Drop Events
4674 */
4675 Dropzone.prototype.attachDragAndDropEvent = function () {
4676 var _this = this;
4677 var collection = $$1();
4678 var $dropzoneMessage = this.$dropzone.find('.note-dropzone-message');
4679 this.documentEventHandlers.onDragenter = function (e) {
4680 var isCodeview = _this.context.invoke('codeview.isActivated');
4681 var hasEditorSize = _this.$editor.width() > 0 && _this.$editor.height() > 0;
4682 if (!isCodeview && !collection.length && hasEditorSize) {
4683 _this.$editor.addClass('dragover');
4684 _this.$dropzone.width(_this.$editor.width());
4685 _this.$dropzone.height(_this.$editor.height());
4686 $dropzoneMessage.text(_this.lang.image.dragImageHere);
4687 }
4688 collection = collection.add(e.target);
4689 };
4690 this.documentEventHandlers.onDragleave = function (e) {
4691 collection = collection.not(e.target);
4692 if (!collection.length) {
4693 _this.$editor.removeClass('dragover');
4694 }
4695 };
4696 this.documentEventHandlers.onDrop = function () {
4697 collection = $$1();
4698 _this.$editor.removeClass('dragover');
4699 };
4700 // show dropzone on dragenter when dragging a object to document
4701 // -but only if the editor is visible, i.e. has a positive width and height
4702 this.$eventListener.on('dragenter', this.documentEventHandlers.onDragenter)
4703 .on('dragleave', this.documentEventHandlers.onDragleave)
4704 .on('drop', this.documentEventHandlers.onDrop);
4705 // change dropzone's message on hover.
4706 this.$dropzone.on('dragenter', function () {
4707 _this.$dropzone.addClass('hover');
4708 $dropzoneMessage.text(_this.lang.image.dropImage);
4709 }).on('dragleave', function () {
4710 _this.$dropzone.removeClass('hover');
4711 $dropzoneMessage.text(_this.lang.image.dragImageHere);
4712 });
4713 // attach dropImage
4714 this.$dropzone.on('drop', function (event) {
4715 var dataTransfer = event.originalEvent.dataTransfer;
4716 // stop the browser from opening the dropped content
4717 event.preventDefault();
4718 if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
4719 _this.$editable.focus();
4720 _this.context.invoke('editor.insertImagesOrCallback', dataTransfer.files);
4721 }
4722 else {
4723 $$1.each(dataTransfer.types, function (idx, type) {
4724 var content = dataTransfer.getData(type);
4725 if (type.toLowerCase().indexOf('text') > -1) {
4726 _this.context.invoke('editor.pasteHTML', content);
4727 }
4728 else {
4729 $$1(content).each(function (idx, item) {
4730 _this.context.invoke('editor.insertNode', item);
4731 });
4732 }
4733 });
4734 }
4735 }).on('dragover', false); // prevent default dragover event
4736 };
4737 Dropzone.prototype.destroy = function () {
4738 var _this = this;
4739 Object.keys(this.documentEventHandlers).forEach(function (key) {
4740 _this.$eventListener.off(key.substr(2).toLowerCase(), _this.documentEventHandlers[key]);
4741 });
4742 this.documentEventHandlers = {};
4743 };
4744 return Dropzone;
4745 }());
4746
4747 var CodeMirror;
4748 if (env.hasCodeMirror) {
4749 if (env.isSupportAmd) {
4750 require(['codemirror'], function (cm) {
4751 CodeMirror = cm;
4752 });
4753 }
4754 else {
4755 CodeMirror = window.CodeMirror;
4756 }
4757 }
4758 /**
4759 * @class Codeview
4760 */
4761 var CodeView = /** @class */ (function () {
4762 function CodeView(context) {
4763 this.context = context;
4764 this.$editor = context.layoutInfo.editor;
4765 this.$editable = context.layoutInfo.editable;
4766 this.$codable = context.layoutInfo.codable;
4767 this.options = context.options;
4768 }
4769 CodeView.prototype.sync = function () {
4770 var isCodeview = this.isActivated();
4771 if (isCodeview && env.hasCodeMirror) {
4772 this.$codable.data('cmEditor').save();
4773 }
4774 };
4775 /**
4776 * @return {Boolean}
4777 */
4778 CodeView.prototype.isActivated = function () {
4779 return this.$editor.hasClass('codeview');
4780 };
4781 /**
4782 * toggle codeview
4783 */
4784 CodeView.prototype.toggle = function () {
4785 if (this.isActivated()) {
4786 this.deactivate();
4787 }
4788 else {
4789 this.activate();
4790 }
4791 this.context.triggerEvent('codeview.toggled');
4792 };
4793 /**
4794 * activate code view
4795 */
4796 CodeView.prototype.activate = function () {
4797 var _this = this;
4798 this.$codable.val(dom.html(this.$editable, this.options.prettifyHtml));
4799 this.$codable.height(this.$editable.height());
4800 this.context.invoke('toolbar.updateCodeview', true);
4801 this.$editor.addClass('codeview');
4802 this.$codable.focus();
4803 // activate CodeMirror as codable
4804 if (env.hasCodeMirror) {
4805 var cmEditor_1 = CodeMirror.fromTextArea(this.$codable[0], this.options.codemirror);
4806 // CodeMirror TernServer
4807 if (this.options.codemirror.tern) {
4808 var server_1 = new CodeMirror.TernServer(this.options.codemirror.tern);
4809 cmEditor_1.ternServer = server_1;
4810 cmEditor_1.on('cursorActivity', function (cm) {
4811 server_1.updateArgHints(cm);
4812 });
4813 }
4814 cmEditor_1.on('blur', function (event) {
4815 _this.context.triggerEvent('blur.codeview', cmEditor_1.getValue(), event);
4816 });
4817 // CodeMirror hasn't Padding.
4818 cmEditor_1.setSize(null, this.$editable.outerHeight());
4819 this.$codable.data('cmEditor', cmEditor_1);
4820 }
4821 else {
4822 this.$codable.on('blur', function (event) {
4823 _this.context.triggerEvent('blur.codeview', _this.$codable.val(), event);
4824 });
4825 }
4826 };
4827 /**
4828 * deactivate code view
4829 */
4830 CodeView.prototype.deactivate = function () {
4831 // deactivate CodeMirror as codable
4832 if (env.hasCodeMirror) {
4833 var cmEditor = this.$codable.data('cmEditor');
4834 this.$codable.val(cmEditor.getValue());
4835 cmEditor.toTextArea();
4836 }
4837 var value = dom.value(this.$codable, this.options.prettifyHtml) || dom.emptyPara;
4838 var isChange = this.$editable.html() !== value;
4839 this.$editable.html(value);
4840 this.$editable.height(this.options.height ? this.$codable.height() : 'auto');
4841 this.$editor.removeClass('codeview');
4842 if (isChange) {
4843 this.context.triggerEvent('change', this.$editable.html(), this.$editable);
4844 }
4845 this.$editable.focus();
4846 this.context.invoke('toolbar.updateCodeview', false);
4847 };
4848 CodeView.prototype.destroy = function () {
4849 if (this.isActivated()) {
4850 this.deactivate();
4851 }
4852 };
4853 return CodeView;
4854 }());
4855
4856 var EDITABLE_PADDING = 24;
4857 var Statusbar = /** @class */ (function () {
4858 function Statusbar(context) {
4859 this.$document = $$1(document);
4860 this.$statusbar = context.layoutInfo.statusbar;
4861 this.$editable = context.layoutInfo.editable;
4862 this.options = context.options;
4863 }
4864 Statusbar.prototype.initialize = function () {
4865 var _this = this;
4866 if (this.options.airMode || this.options.disableResizeEditor) {
4867 this.destroy();
4868 return;
4869 }
4870 this.$statusbar.on('mousedown', function (event) {
4871 event.preventDefault();
4872 event.stopPropagation();
4873 var editableTop = _this.$editable.offset().top - _this.$document.scrollTop();
4874 var onMouseMove = function (event) {
4875 var height = event.clientY - (editableTop + EDITABLE_PADDING);
4876 height = (_this.options.minheight > 0) ? Math.max(height, _this.options.minheight) : height;
4877 height = (_this.options.maxHeight > 0) ? Math.min(height, _this.options.maxHeight) : height;
4878 _this.$editable.height(height);
4879 };
4880 _this.$document.on('mousemove', onMouseMove).one('mouseup', function () {
4881 _this.$document.off('mousemove', onMouseMove);
4882 });
4883 });
4884 };
4885 Statusbar.prototype.destroy = function () {
4886 this.$statusbar.off();
4887 this.$statusbar.addClass('locked');
4888 };
4889 return Statusbar;
4890 }());
4891
4892 var Fullscreen = /** @class */ (function () {
4893 function Fullscreen(context) {
4894 var _this = this;
4895 this.context = context;
4896 this.$editor = context.layoutInfo.editor;
4897 this.$toolbar = context.layoutInfo.toolbar;
4898 this.$editable = context.layoutInfo.editable;
4899 this.$codable = context.layoutInfo.codable;
4900 this.$window = $$1(window);
4901 this.$scrollbar = $$1('html, body');
4902 this.onResize = function () {
4903 _this.resizeTo({
4904 h: _this.$window.height() - _this.$toolbar.outerHeight()
4905 });
4906 };
4907 }
4908 Fullscreen.prototype.resizeTo = function (size) {
4909 this.$editable.css('height', size.h);
4910 this.$codable.css('height', size.h);
4911 if (this.$codable.data('cmeditor')) {
4912 this.$codable.data('cmeditor').setsize(null, size.h);
4913 }
4914 };
4915 /**
4916 * toggle fullscreen
4917 */
4918 Fullscreen.prototype.toggle = function () {
4919 this.$editor.toggleClass('fullscreen');
4920 if (this.isFullscreen()) {
4921 this.$editable.data('orgHeight', this.$editable.css('height'));
4922 this.$editable.data('orgMaxHeight', this.$editable.css('maxHeight'));
4923 this.$editable.css('maxHeight', '');
4924 this.$window.on('resize', this.onResize).trigger('resize');
4925 this.$scrollbar.css('overflow', 'hidden');
4926 }
4927 else {
4928 this.$window.off('resize', this.onResize);
4929 this.resizeTo({ h: this.$editable.data('orgHeight') });
4930 this.$editable.css('maxHeight', this.$editable.css('orgMaxHeight'));
4931 this.$scrollbar.css('overflow', 'visible');
4932 }
4933 this.context.invoke('toolbar.updateFullscreen', this.isFullscreen());
4934 };
4935 Fullscreen.prototype.isFullscreen = function () {
4936 return this.$editor.hasClass('fullscreen');
4937 };
4938 return Fullscreen;
4939 }());
4940
4941 var Handle = /** @class */ (function () {
4942 function Handle(context) {
4943 var _this = this;
4944 this.context = context;
4945 this.$document = $$1(document);
4946 this.$editingArea = context.layoutInfo.editingArea;
4947 this.options = context.options;
4948 this.lang = this.options.langInfo;
4949 this.events = {
4950 'summernote.mousedown': function (we, e) {
4951 if (_this.update(e.target,e)) {
4952 e.preventDefault();
4953 }
4954 },
4955 'summernote.keyup summernote.scroll summernote.change summernote.dialog.shown': function () {
4956 _this.update();
4957 },
4958 'summernote.disable': function () {
4959 _this.hide();
4960 },
4961 'summernote.codeview.toggled': function () {
4962 _this.update();
4963 }
4964 };
4965 }
4966 Handle.prototype.initialize = function () {
4967 var _this = this;
4968 this.$handle = $$1([
4969 '<div class="note-handle">',
4970 '<div class="note-control-selection">',
4971 '<div class="note-control-selection-bg"></div>',
4972 '<div class="note-control-holder note-control-nw"></div>',
4973 '<div class="note-control-holder note-control-ne"></div>',
4974 '<div class="note-control-holder note-control-sw"></div>',
4975 '<div class="',
4976 (this.options.disableResizeImage ? 'note-control-holder' : 'note-control-sizing'),
4977 ' note-control-se"></div>',
4978 (this.options.disableResizeImage ? '' : '<div class="note-control-selection-info"></div>'),
4979 '</div>',
4980 '</div>'
4981 ].join('')).prependTo(this.$editingArea);
4982 this.$handle.on('mousedown', function (event) {
4983 if (dom.isControlSizing(event.target)) {
4984 event.preventDefault();
4985 event.stopPropagation();
4986 var $target_1 = _this.$handle.find('.note-control-selection').data('target');
4987 var posStart_1 = $target_1.offset();
4988 var scrollTop_1 = _this.$document.scrollTop();
4989 var onMouseMove_1 = function (event) {
4990 _this.context.invoke('editor.resizeTo', {
4991 x: event.clientX - posStart_1.left,
4992 y: event.clientY - (posStart_1.top - scrollTop_1)
4993 }, $target_1, !event.shiftKey);
4994 _this.update($target_1[0]);
4995 };
4996 _this.$document
4997 .on('mousemove', onMouseMove_1)
4998 .one('mouseup', function (e) {
4999 e.preventDefault();
5000 _this.$document.off('mousemove', onMouseMove_1);
5001 _this.context.invoke('editor.afterCommand');
5002 });
5003 if (!$target_1.data('ratio')) { // original ratio.
5004 $target_1.data('ratio', $target_1.height() / $target_1.width());
5005 }
5006 }
5007 });
5008 // Listen for scrolling on the handle overlay.
5009 this.$handle.on('wheel', function (e) {
5010 e.preventDefault();
5011 _this.update();
5012 });
5013 };
5014 Handle.prototype.destroy = function () {
5015 this.$handle.remove();
5016 };
5017 Handle.prototype.update = function (target,event) {
5018 if (this.context.isDisabled()) {
5019 return false;
5020 }
5021 var isImage = dom.isImg(target);
5022 var $selection = this.$handle.find('.note-control-selection');
5023 this.context.invoke('imagePopover.update', target,event);
5024 if (isImage) {
5025 var $image = $$1(target);
5026 var position = $image.position();
5027 var pos = {
5028 left: position.left + parseInt($image.css('marginLeft'), 10),
5029 top: position.top + parseInt($image.css('marginTop'), 10)
5030 };
5031 // exclude margin
5032 var imageSize = {
5033 w: $image.outerWidth(false),
5034 h: $image.outerHeight(false)
5035 };
5036 $selection.css({
5037 display: 'block',
5038 left: pos.left,
5039 top: pos.top,
5040 width: imageSize.w,
5041 height: imageSize.h
5042 }).data('target', $image); // save current image element.
5043 var origImageObj = new Image();
5044 origImageObj.src = $image.attr('src');
5045 var sizingText = imageSize.w + 'x' + imageSize.h + ' (' + this.lang.image.original + ': ' + origImageObj.width + 'x' + origImageObj.height + ')';
5046 $selection.find('.note-control-selection-info').text(sizingText);
5047 this.context.invoke('editor.saveTarget', target);
5048 }
5049 else {
5050 this.hide();
5051 }
5052 return isImage;
5053 };
5054 /**
5055 * hide
5056 *
5057 * @param {jQuery} $handle
5058 */
5059 Handle.prototype.hide = function () {
5060 this.context.invoke('editor.clearTarget');
5061 this.$handle.children().hide();
5062 };
5063 return Handle;
5064 }());
5065
5066 var defaultScheme = 'http://';
5067 var linkPattern = /^([A-Za-z][A-Za-z0-9+-.]*\:[\/]{2}|mailto:[A-Z0-9._%+-]+@)?(www\.)?(.+)$/i;
5068 var AutoLink = /** @class */ (function () {
5069 function AutoLink(context) {
5070 var _this = this;
5071 this.context = context;
5072 this.events = {
5073 'summernote.keyup': function (we, e) {
5074 if (!e.isDefaultPrevented()) {
5075 _this.handleKeyup(e);
5076 }
5077 },
5078 'summernote.keydown': function (we, e) {
5079 _this.handleKeydown(e);
5080 }
5081 };
5082 }
5083 AutoLink.prototype.initialize = function () {
5084 this.lastWordRange = null;
5085 };
5086 AutoLink.prototype.destroy = function () {
5087 this.lastWordRange = null;
5088 };
5089 AutoLink.prototype.replace = function () {
5090 if (!this.lastWordRange) {
5091 return;
5092 }
5093 var keyword = this.lastWordRange.toString();
5094 var match = keyword.match(linkPattern);
5095 if (match && (match[1] || match[2])) {
5096 var link = match[1] ? keyword : defaultScheme + keyword;
5097 var node = $$1('<a />').html(keyword).attr('href', link)[0];
5098 if (this.context.options.linkTargetBlank) {
5099 $$1(node).attr('target', '_blank');
5100 }
5101 this.lastWordRange.insertNode(node);
5102 this.lastWordRange = null;
5103 this.context.invoke('editor.focus');
5104 }
5105 };
5106 AutoLink.prototype.handleKeydown = function (e) {
5107 if (lists.contains([key.code.ENTER, key.code.SPACE], e.keyCode)) {
5108 var wordRange = this.context.invoke('editor.createRange').getWordRange();
5109 this.lastWordRange = wordRange;
5110 }
5111 };
5112 AutoLink.prototype.handleKeyup = function (e) {
5113 if (lists.contains([key.code.ENTER, key.code.SPACE], e.keyCode)) {
5114 this.replace();
5115 }
5116 };
5117 return AutoLink;
5118 }());
5119
5120 /**
5121 * textarea auto sync.
5122 */
5123 var AutoSync = /** @class */ (function () {
5124 function AutoSync(context) {
5125 var _this = this;
5126 this.$note = context.layoutInfo.note;
5127 this.events = {
5128 'summernote.change': function () {
5129 _this.$note.val(context.invoke('code'));
5130 }
5131 };
5132 }
5133 AutoSync.prototype.shouldInitialize = function () {
5134 return dom.isTextarea(this.$note[0]);
5135 };
5136 return AutoSync;
5137 }());
5138
5139 var Placeholder = /** @class */ (function () {
5140 function Placeholder(context) {
5141 var _this = this;
5142 this.context = context;
5143 this.$editingArea = context.layoutInfo.editingArea;
5144 this.options = context.options;
5145 this.events = {
5146 'summernote.init summernote.change': function () {
5147 _this.update();
5148 },
5149 'summernote.codeview.toggled': function () {
5150 _this.update();
5151 }
5152 };
5153 }
5154 Placeholder.prototype.shouldInitialize = function () {
5155 return !!this.options.placeholder;
5156 };
5157 Placeholder.prototype.initialize = function () {
5158 var _this = this;
5159 this.$placeholder = $$1('<div class="note-placeholder">');
5160 this.$placeholder.on('click', function () {
5161 _this.context.invoke('focus');
5162 }).html(this.options.placeholder).prependTo(this.$editingArea);
5163 this.update();
5164 };
5165 Placeholder.prototype.destroy = function () {
5166 this.$placeholder.remove();
5167 };
5168 Placeholder.prototype.update = function () {
5169 var isShow = !this.context.invoke('codeview.isActivated') && this.context.invoke('editor.isEmpty');
5170 this.$placeholder.toggle(isShow);
5171 };
5172 return Placeholder;
5173 }());
5174
5175 var Buttons = /** @class */ (function () {
5176 function Buttons(context) {
5177 this.ui = $$1.summernote.ui;
5178 this.context = context;
5179 this.$toolbar = context.layoutInfo.toolbar;
5180 this.options = context.options;
5181 this.lang = this.options.langInfo;
5182 this.invertedKeyMap = func.invertObject(this.options.keyMap[env.isMac ? 'mac' : 'pc']);
5183 }
5184 Buttons.prototype.representShortcut = function (editorMethod) {
5185 var shortcut = this.invertedKeyMap[editorMethod];
5186 if (!this.options.shortcuts || !shortcut) {
5187 return '';
5188 }
5189 if (env.isMac) {
5190 shortcut = shortcut.replace('CMD', '⌘').replace('SHIFT', '⇧');
5191 }
5192 shortcut = shortcut.replace('BACKSLASH', '\\')
5193 .replace('SLASH', '/')
5194 .replace('LEFTBRACKET', '[')
5195 .replace('RIGHTBRACKET', ']');
5196 return ' (' + shortcut + ')';
5197 };
5198 Buttons.prototype.button = function (o) {
5199 if (!this.options.tooltip && o.tooltip) {
5200 delete o.tooltip;
5201 }
5202 o.container = this.options.container;
5203 return this.ui.button(o);
5204 };
5205 Buttons.prototype.initialize = function () {
5206 this.addToolbarButtons();
5207 this.addImagePopoverButtons();
5208 this.addLinkPopoverButtons();
5209 this.addTablePopoverButtons();
5210 this.fontInstalledMap = {};
5211 };
5212 Buttons.prototype.destroy = function () {
5213 delete this.fontInstalledMap;
5214 };
5215 Buttons.prototype.isFontInstalled = function (name) {
5216 if (!this.fontInstalledMap.hasOwnProperty(name)) {
5217 this.fontInstalledMap[name] = env.isFontInstalled(name) ||
5218 lists.contains(this.options.fontNamesIgnoreCheck, name);
5219 }
5220 return this.fontInstalledMap[name];
5221 };
5222 Buttons.prototype.isFontDeservedToAdd = function (name) {
5223 var genericFamilies = ['sans-serif', 'serif', 'monospace', 'cursive', 'fantasy'];
5224 name = name.toLowerCase();
5225 return ((name !== '') && this.isFontInstalled(name) && ($$1.inArray(name, genericFamilies) === -1));
5226 };
5227 Buttons.prototype.colorPalette = function (className, tooltip, backColor, foreColor) {
5228 var _this = this;
5229 return this.ui.buttonGroup({
5230 className: 'note-color ' + className,
5231 children: [
5232 this.button({
5233 className: 'note-current-color-button',
5234 contents: this.ui.icon(this.options.icons.font + ' note-recent-color'),
5235 tooltip: tooltip,
5236 click: function (e) {
5237 var $button = $$1(e.currentTarget);
5238 if (backColor && foreColor) {
5239 _this.context.invoke('editor.color', {
5240 backColor: $button.attr('data-backColor'),
5241 foreColor: $button.attr('data-foreColor')
5242 });
5243 }
5244 else if (backColor) {
5245 _this.context.invoke('editor.color', {
5246 backColor: $button.attr('data-backColor')
5247 });
5248 }
5249 else if (foreColor) {
5250 _this.context.invoke('editor.color', {
5251 foreColor: $button.attr('data-foreColor')
5252 });
5253 }
5254 },
5255 callback: function ($button) {
5256 var $recentColor = $button.find('.note-recent-color');
5257 if (backColor) {
5258 $recentColor.css('background-color', '#FFFF00');
5259 $button.attr('data-backColor', '#FFFF00');
5260 }
5261 if (!foreColor) {
5262 $recentColor.css('color', 'transparent');
5263 }
5264 }
5265 }),
5266 this.button({
5267 className: 'dropdown-toggle',
5268 contents: this.ui.dropdownButtonContents('', this.options),
5269 tooltip: this.lang.color.more,
5270 data: {
5271 toggle: 'dropdown'
5272 }
5273 }),
5274 this.ui.dropdown({
5275 items: (backColor ? [
5276 '<div class="note-palette">',
5277 ' <div class="note-palette-title">' + this.lang.color.background + '</div>',
5278 ' <div>',
5279 ' <button type="button" class="note-color-reset btn btn-light" data-event="backColor" data-value="inherit">',
5280 this.lang.color.transparent,
5281 ' </button>',
5282 ' </div>',
5283 ' <div class="note-holder" data-event="backColor"/>',
5284 ' <div>',
5285 ' <button type="button" class="note-color-select btn" data-event="openPalette" data-value="backColorPicker">',
5286 this.lang.color.cpSelect,
5287 ' </button>',
5288 ' <input type="color" id="backColorPicker" class="note-btn note-color-select-btn" value="#FFFF00" data-event="backColorPalette">',
5289 ' </div>',
5290 ' <div class="note-holder-custom" id="backColorPalette" data-event="backColor"/>',
5291 '</div>'
5292 ].join('') : '') +
5293 (foreColor ? [
5294 '<div class="note-palette">',
5295 ' <div class="note-palette-title">' + this.lang.color.foreground + '</div>',
5296 ' <div>',
5297 ' <button type="button" class="note-color-reset btn btn-light" data-event="removeFormat" data-value="foreColor">',
5298 this.lang.color.resetToDefault,
5299 ' </button>',
5300 ' </div>',
5301 ' <div class="note-holder" data-event="foreColor"/>',
5302 ' <div>',
5303 ' <button type="button" class="note-color-select btn" data-event="openPalette" data-value="foreColorPicker">',
5304 this.lang.color.cpSelect,
5305 ' </button>',
5306 ' <input type="color" id="foreColorPicker" class="note-btn note-color-select-btn" value="#000000" data-event="foreColorPalette">',
5307 ' <div class="note-holder-custom" id="foreColorPalette" data-event="foreColor"/>',
5308 '</div>'
5309 ].join('') : ''),
5310 callback: function ($dropdown) {
5311 $dropdown.find('.note-holder').each(function (idx, item) {
5312 var $holder = $$1(item);
5313 $holder.append(_this.ui.palette({
5314 colors: _this.options.colors,
5315 colorsName: _this.options.colorsName,
5316 eventName: $holder.data('event'),
5317 container: _this.options.container,
5318 tooltip: _this.options.tooltip
5319 }).render());
5320 });
5321 /* TODO: do we have to record recent custom colors within cookies? */
5322 var customColors = [
5323 ['#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF', '#FFFFFF']
5324 ];
5325 $dropdown.find('.note-holder-custom').each(function (idx, item) {
5326 var $holder = $$1(item);
5327 $holder.append(_this.ui.palette({
5328 colors: customColors,
5329 colorsName: customColors,
5330 eventName: $holder.data('event'),
5331 container: _this.options.container,
5332 tooltip: _this.options.tooltip
5333 }).render());
5334 });
5335 $dropdown.find('input[type=color]').each(function (idx, item) {
5336 $$1(item).change(function () {
5337 var $chip = $dropdown.find('#' + $$1(this).data('event')).find('.note-color-btn').first();
5338 var color = this.value.toUpperCase();
5339 $chip.css('background-color', color)
5340 .attr('aria-label', color)
5341 .attr('data-value', color)
5342 .attr('data-original-title', color);
5343 $chip.click();
5344 });
5345 });
5346 },
5347 click: function (event) {
5348 event.stopPropagation();
5349 var $parent = $$1('.' + className);
5350 var $button = $$1(event.target);
5351 var eventName = $button.data('event');
5352 var value = $button.attr('data-value');
5353 if (eventName === 'openPalette') {
5354 var $picker = $parent.find('#' + value);
5355 var $palette = $$1($parent.find('#' + $picker.data('event')).find('.note-color-row')[0]);
5356 // Shift palette chips
5357 var $chip = $palette.find('.note-color-btn').last().detach();
5358 // Set chip attributes
5359 var color = $picker.val();
5360 $chip.css('background-color', color)
5361 .attr('aria-label', color)
5362 .attr('data-value', color)
5363 .attr('data-original-title', color);
5364 $palette.prepend($chip);
5365 $picker.click();
5366 }
5367 else if (lists.contains(['backColor', 'foreColor'], eventName)) {
5368 var key = eventName === 'backColor' ? 'background-color' : 'color';
5369 var $color = $button.closest('.note-color').find('.note-recent-color');
5370 var $currentButton = $button.closest('.note-color').find('.note-current-color-button');
5371 $color.css(key, value);
5372 $currentButton.attr('data-' + eventName, value);
5373 _this.context.invoke('editor.' + eventName, value);
5374 }
5375 }
5376 })
5377 ]
5378 }).render();
5379 };
5380 Buttons.prototype.addToolbarButtons = function () {
5381 var _this = this;
5382 this.context.memo('button.style', function () {
5383 return _this.ui.buttonGroup([
5384 _this.button({
5385 className: 'dropdown-toggle',
5386 contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.magic), _this.options),
5387 tooltip: _this.lang.style.style,
5388 data: {
5389 toggle: 'dropdown'
5390 }
5391 }),
5392 _this.ui.dropdown({
5393 className: 'dropdown-style',
5394 items: _this.options.styleTags,
5395 title: _this.lang.style.style,
5396 template: function (item) {
5397 if (typeof item === 'string') {
5398 item = { tag: item, title: (_this.lang.style.hasOwnProperty(item) ? _this.lang.style[item] : item) };
5399 }
5400 var tag = item.tag;
5401 var title = item.title;
5402 var style = item.style ? ' style="' + item.style + '" ' : '';
5403 var className = item.className ? ' class="' + item.className + '"' : '';
5404 return '<' + tag + style + className + '>' + title + '</' + tag + '>';
5405 },
5406 click: _this.context.createInvokeHandler('editor.formatBlock')
5407 })
5408 ]).render();
5409 });
5410 var _loop_1 = function (styleIdx, styleLen) {
5411 var item = this_1.options.styleTags[styleIdx];
5412 this_1.context.memo('button.style.' + item, function () {
5413 return _this.button({
5414 className: 'note-btn-style-' + item,
5415 contents: '<div data-value="' + item + '">' + item.toUpperCase() + '</div>',
5416 tooltip: _this.lang.style[item],
5417 click: _this.context.createInvokeHandler('editor.formatBlock')
5418 }).render();
5419 });
5420 };
5421 var this_1 = this;
5422 for (var styleIdx = 0, styleLen = this.options.styleTags.length; styleIdx < styleLen; styleIdx++) {
5423 _loop_1(styleIdx, styleLen);
5424 }
5425 this.context.memo('button.bold', function () {
5426 return _this.button({
5427 className: 'note-btn-bold',
5428 contents: _this.ui.icon(_this.options.icons.bold),
5429 tooltip: _this.lang.font.bold + _this.representShortcut('bold'),
5430 click: _this.context.createInvokeHandlerAndUpdateState('editor.bold')
5431 }).render();
5432 });
5433 this.context.memo('button.italic', function () {
5434 return _this.button({
5435 className: 'note-btn-italic',
5436 contents: _this.ui.icon(_this.options.icons.italic),
5437 tooltip: _this.lang.font.italic + _this.representShortcut('italic'),
5438 click: _this.context.createInvokeHandlerAndUpdateState('editor.italic')
5439 }).render();
5440 });
5441 this.context.memo('button.underline', function () {
5442 return _this.button({
5443 className: 'note-btn-underline',
5444 contents: _this.ui.icon(_this.options.icons.underline),
5445 tooltip: _this.lang.font.underline + _this.representShortcut('underline'),
5446 click: _this.context.createInvokeHandlerAndUpdateState('editor.underline')
5447 }).render();
5448 });
5449 this.context.memo('button.clear', function () {
5450 return _this.button({
5451 contents: _this.ui.icon(_this.options.icons.eraser),
5452 tooltip: _this.lang.font.clear + _this.representShortcut('removeFormat'),
5453 click: _this.context.createInvokeHandler('editor.removeFormat')
5454 }).render();
5455 });
5456 this.context.memo('button.strikethrough', function () {
5457 return _this.button({
5458 className: 'note-btn-strikethrough',
5459 contents: _this.ui.icon(_this.options.icons.strikethrough),
5460 tooltip: _this.lang.font.strikethrough + _this.representShortcut('strikethrough'),
5461 click: _this.context.createInvokeHandlerAndUpdateState('editor.strikethrough')
5462 }).render();
5463 });
5464 this.context.memo('button.superscript', function () {
5465 return _this.button({
5466 className: 'note-btn-superscript',
5467 contents: _this.ui.icon(_this.options.icons.superscript),
5468 tooltip: _this.lang.font.superscript,
5469 click: _this.context.createInvokeHandlerAndUpdateState('editor.superscript')
5470 }).render();
5471 });
5472 this.context.memo('button.subscript', function () {
5473 return _this.button({
5474 className: 'note-btn-subscript',
5475 contents: _this.ui.icon(_this.options.icons.subscript),
5476 tooltip: _this.lang.font.subscript,
5477 click: _this.context.createInvokeHandlerAndUpdateState('editor.subscript')
5478 }).render();
5479 });
5480 this.context.memo('button.fontname', function () {
5481 var styleInfo = _this.context.invoke('editor.currentStyle');
5482 // Add 'default' fonts into the fontnames array if not exist
5483 $$1.each(styleInfo['font-family'].split(','), function (idx, fontname) {
5484 fontname = fontname.trim().replace(/['"]+/g, '');
5485 if (_this.isFontDeservedToAdd(fontname)) {
5486 if ($$1.inArray(fontname, _this.options.fontNames) === -1) {
5487 _this.options.fontNames.push(fontname);
5488 }
5489 }
5490 });
5491 return _this.ui.buttonGroup([
5492 _this.button({
5493 className: 'dropdown-toggle',
5494 contents: _this.ui.dropdownButtonContents('<span class="note-current-fontname"/>', _this.options),
5495 tooltip: _this.lang.font.name,
5496 data: {
5497 toggle: 'dropdown'
5498 }
5499 }),
5500 _this.ui.dropdownCheck({
5501 className: 'dropdown-fontname',
5502 checkClassName: _this.options.icons.menuCheck,
5503 items: _this.options.fontNames.filter(_this.isFontInstalled.bind(_this)),
5504 title: _this.lang.font.name,
5505 template: function (item) {
5506 return '<span style="font-family: \'' + item + '\'">' + item + '</span>';
5507 },
5508 click: _this.context.createInvokeHandlerAndUpdateState('editor.fontName')
5509 })
5510 ]).render();
5511 });
5512 this.context.memo('button.fontsize', function () {
5513 return _this.ui.buttonGroup([
5514 _this.button({
5515 className: 'dropdown-toggle',
5516 contents: _this.ui.dropdownButtonContents('<span class="note-current-fontsize"/>', _this.options),
5517 tooltip: _this.lang.font.size,
5518 data: {
5519 toggle: 'dropdown'
5520 }
5521 }),
5522 _this.ui.dropdownCheck({
5523 className: 'dropdown-fontsize',
5524 checkClassName: _this.options.icons.menuCheck,
5525 items: _this.options.fontSizes,
5526 title: _this.lang.font.size,
5527 click: _this.context.createInvokeHandlerAndUpdateState('editor.fontSize')
5528 })
5529 ]).render();
5530 });
5531 this.context.memo('button.color', function () {
5532 return _this.colorPalette('note-color-all', _this.lang.color.recent, true, true);
5533 });
5534 this.context.memo('button.forecolor', function () {
5535 return _this.colorPalette('note-color-fore', _this.lang.color.foreground, false, true);
5536 });
5537 this.context.memo('button.backcolor', function () {
5538 return _this.colorPalette('note-color-back', _this.lang.color.background, true, false);
5539 });
5540 this.context.memo('button.ul', function () {
5541 return _this.button({
5542 contents: _this.ui.icon(_this.options.icons.unorderedlist),
5543 tooltip: _this.lang.lists.unordered + _this.representShortcut('insertUnorderedList'),
5544 click: _this.context.createInvokeHandler('editor.insertUnorderedList')
5545 }).render();
5546 });
5547 this.context.memo('button.ol', function () {
5548 return _this.button({
5549 contents: _this.ui.icon(_this.options.icons.orderedlist),
5550 tooltip: _this.lang.lists.ordered + _this.representShortcut('insertOrderedList'),
5551 click: _this.context.createInvokeHandler('editor.insertOrderedList')
5552 }).render();
5553 });
5554 var justifyLeft = this.button({
5555 contents: this.ui.icon(this.options.icons.alignLeft),
5556 tooltip: this.lang.paragraph.left + this.representShortcut('justifyLeft'),
5557 click: this.context.createInvokeHandler('editor.justifyLeft')
5558 });
5559 var justifyCenter = this.button({
5560 contents: this.ui.icon(this.options.icons.alignCenter),
5561 tooltip: this.lang.paragraph.center + this.representShortcut('justifyCenter'),
5562 click: this.context.createInvokeHandler('editor.justifyCenter')
5563 });
5564 var justifyRight = this.button({
5565 contents: this.ui.icon(this.options.icons.alignRight),
5566 tooltip: this.lang.paragraph.right + this.representShortcut('justifyRight'),
5567 click: this.context.createInvokeHandler('editor.justifyRight')
5568 });
5569 var justifyFull = this.button({
5570 contents: this.ui.icon(this.options.icons.alignJustify),
5571 tooltip: this.lang.paragraph.justify + this.representShortcut('justifyFull'),
5572 click: this.context.createInvokeHandler('editor.justifyFull')
5573 });
5574 var outdent = this.button({
5575 contents: this.ui.icon(this.options.icons.outdent),
5576 tooltip: this.lang.paragraph.outdent + this.representShortcut('outdent'),
5577 click: this.context.createInvokeHandler('editor.outdent')
5578 });
5579 var indent = this.button({
5580 contents: this.ui.icon(this.options.icons.indent),
5581 tooltip: this.lang.paragraph.indent + this.representShortcut('indent'),
5582 click: this.context.createInvokeHandler('editor.indent')
5583 });
5584 this.context.memo('button.justifyLeft', func.invoke(justifyLeft, 'render'));
5585 this.context.memo('button.justifyCenter', func.invoke(justifyCenter, 'render'));
5586 this.context.memo('button.justifyRight', func.invoke(justifyRight, 'render'));
5587 this.context.memo('button.justifyFull', func.invoke(justifyFull, 'render'));
5588 this.context.memo('button.outdent', func.invoke(outdent, 'render'));
5589 this.context.memo('button.indent', func.invoke(indent, 'render'));
5590 this.context.memo('button.paragraph', function () {
5591 return _this.ui.buttonGroup([
5592 _this.button({
5593 className: 'dropdown-toggle',
5594 contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.alignLeft), _this.options),
5595 tooltip: _this.lang.paragraph.paragraph,
5596 data: {
5597 toggle: 'dropdown'
5598 }
5599 }),
5600 _this.ui.dropdown([
5601 _this.ui.buttonGroup({
5602 className: 'note-align',
5603 children: [justifyLeft, justifyCenter, justifyRight, justifyFull]
5604 }),
5605 _this.ui.buttonGroup({
5606 className: 'note-list',
5607 children: [outdent, indent]
5608 })
5609 ])
5610 ]).render();
5611 });
5612 this.context.memo('button.height', function () {
5613 return _this.ui.buttonGroup([
5614 _this.button({
5615 className: 'dropdown-toggle',
5616 contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.textHeight), _this.options),
5617 tooltip: _this.lang.font.height,
5618 data: {
5619 toggle: 'dropdown'
5620 }
5621 }),
5622 _this.ui.dropdownCheck({
5623 items: _this.options.lineHeights,
5624 checkClassName: _this.options.icons.menuCheck,
5625 className: 'dropdown-line-height',
5626 title: _this.lang.font.height,
5627 click: _this.context.createInvokeHandler('editor.lineHeight')
5628 })
5629 ]).render();
5630 });
5631 this.context.memo('button.table', function () {
5632 return _this.ui.buttonGroup([
5633 _this.button({
5634 className: 'dropdown-toggle',
5635 contents: _this.ui.dropdownButtonContents(_this.ui.icon(_this.options.icons.table), _this.options),
5636 tooltip: _this.lang.table.table,
5637 data: {
5638 toggle: 'dropdown'
5639 }
5640 }),
5641 _this.ui.dropdown({
5642 title: _this.lang.table.table,
5643 className: 'note-table',
5644 items: [
5645 '<div class="note-dimension-picker">',
5646 ' <div class="note-dimension-picker-mousecatcher" data-event="insertTable" data-value="1x1"/>',
5647 ' <div class="note-dimension-picker-highlighted"/>',
5648 ' <div class="note-dimension-picker-unhighlighted"/>',
5649 '</div>',
5650 '<div class="note-dimension-display">1 x 1</div>'
5651 ].join('')
5652 })
5653 ], {
5654 callback: function ($node) {
5655 var $catcher = $node.find('.note-dimension-picker-mousecatcher');
5656 $catcher.css({
5657 width: _this.options.insertTableMaxSize.col + 'em',
5658 height: _this.options.insertTableMaxSize.row + 'em'
5659 }).mousedown(_this.context.createInvokeHandler('editor.insertTable'))
5660 .on('mousemove', _this.tableMoveHandler.bind(_this));
5661 }
5662 }).render();
5663 });
5664 this.context.memo('button.link', function () {
5665 return _this.button({
5666 contents: _this.ui.icon(_this.options.icons.link),
5667 tooltip: _this.lang.link.link + _this.representShortcut('linkDialog.show'),
5668 click: _this.context.createInvokeHandler('linkDialog.show')
5669 }).render();
5670 });
5671 this.context.memo('button.picture', function () {
5672 return _this.button({
5673 contents: _this.ui.icon(_this.options.icons.picture),
5674 tooltip: _this.lang.image.image,
5675 click: _this.context.createInvokeHandler('imageDialog.show')
5676 }).render();
5677 });
5678 this.context.memo('button.video', function () {
5679 return _this.button({
5680 contents: _this.ui.icon(_this.options.icons.video),
5681 tooltip: _this.lang.video.video,
5682 click: _this.context.createInvokeHandler('videoDialog.show')
5683 }).render();
5684 });
5685 this.context.memo('button.hr', function () {
5686 return _this.button({
5687 contents: _this.ui.icon(_this.options.icons.minus),
5688 tooltip: _this.lang.hr.insert + _this.representShortcut('insertHorizontalRule'),
5689 click: _this.context.createInvokeHandler('editor.insertHorizontalRule')
5690 }).render();
5691 });
5692 this.context.memo('button.fullscreen', function () {
5693 return _this.button({
5694 className: 'btn-fullscreen',
5695 contents: _this.ui.icon(_this.options.icons.arrowsAlt),
5696 tooltip: _this.lang.options.fullscreen,
5697 click: _this.context.createInvokeHandler('fullscreen.toggle')
5698 }).render();
5699 });
5700 this.context.memo('button.codeview', function () {
5701 return _this.button({
5702 className: 'btn-codeview',
5703 contents: _this.ui.icon(_this.options.icons.code),
5704 tooltip: _this.lang.options.codeview,
5705 click: _this.context.createInvokeHandler('codeview.toggle')
5706 }).render();
5707 });
5708 this.context.memo('button.redo', function () {
5709 return _this.button({
5710 contents: _this.ui.icon(_this.options.icons.redo),
5711 tooltip: _this.lang.history.redo + _this.representShortcut('redo'),
5712 click: _this.context.createInvokeHandler('editor.redo')
5713 }).render();
5714 });
5715 this.context.memo('button.undo', function () {
5716 return _this.button({
5717 contents: _this.ui.icon(_this.options.icons.undo),
5718 tooltip: _this.lang.history.undo + _this.representShortcut('undo'),
5719 click: _this.context.createInvokeHandler('editor.undo')
5720 }).render();
5721 });
5722 this.context.memo('button.help', function () {
5723 return _this.button({
5724 contents: _this.ui.icon(_this.options.icons.question),
5725 tooltip: _this.lang.options.help,
5726 click: _this.context.createInvokeHandler('helpDialog.show')
5727 }).render();
5728 });
5729 };
5730 /**
5731 * image : [
5732 * ['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
5733 * ['float', ['floatLeft', 'floatRight', 'floatNone' ]],
5734 * ['remove', ['removeMedia']]
5735 * ],
5736 */
5737 Buttons.prototype.addImagePopoverButtons = function () {
5738 var _this = this;
5739 // Image Size Buttons
5740 this.context.memo('button.imageSize100', function () {
5741 return _this.button({
5742 contents: '<span class="note-fontsize-10">100%</span>',
5743 tooltip: _this.lang.image.resizeFull,
5744 click: _this.context.createInvokeHandler('editor.resize', '1')
5745 }).render();
5746 });
5747 this.context.memo('button.imageSize50', function () {
5748 return _this.button({
5749 contents: '<span class="note-fontsize-10">50%</span>',
5750 tooltip: _this.lang.image.resizeHalf,
5751 click: _this.context.createInvokeHandler('editor.resize', '0.5')
5752 }).render();
5753 });
5754 this.context.memo('button.imageSize25', function () {
5755 return _this.button({
5756 contents: '<span class="note-fontsize-10">25%</span>',
5757 tooltip: _this.lang.image.resizeQuarter,
5758 click: _this.context.createInvokeHandler('editor.resize', '0.25')
5759 }).render();
5760 });
5761 // Float Buttons
5762 this.context.memo('button.floatLeft', function () {
5763 return _this.button({
5764 contents: _this.ui.icon(_this.options.icons.alignLeft),
5765 tooltip: _this.lang.image.floatLeft,
5766 click: _this.context.createInvokeHandler('editor.floatMe', 'left')
5767 }).render();
5768 });
5769 this.context.memo('button.floatRight', function () {
5770 return _this.button({
5771 contents: _this.ui.icon(_this.options.icons.alignRight),
5772 tooltip: _this.lang.image.floatRight,
5773 click: _this.context.createInvokeHandler('editor.floatMe', 'right')
5774 }).render();
5775 });
5776 this.context.memo('button.floatNone', function () {
5777 return _this.button({
5778 contents: _this.ui.icon(_this.options.icons.alignJustify),
5779 tooltip: _this.lang.image.floatNone,
5780 click: _this.context.createInvokeHandler('editor.floatMe', 'none')
5781 }).render();
5782 });
5783 // Remove Buttons
5784 this.context.memo('button.removeMedia', function () {
5785 return _this.button({
5786 contents: _this.ui.icon(_this.options.icons.trash),
5787 tooltip: _this.lang.image.remove,
5788 click: _this.context.createInvokeHandler('editor.removeMedia')
5789 }).render();
5790 });
5791 };
5792 Buttons.prototype.addLinkPopoverButtons = function () {
5793 var _this = this;
5794 this.context.memo('button.linkDialogShow', function () {
5795 return _this.button({
5796 contents: _this.ui.icon(_this.options.icons.link),
5797 tooltip: _this.lang.link.edit,
5798 click: _this.context.createInvokeHandler('linkDialog.show')
5799 }).render();
5800 });
5801 this.context.memo('button.unlink', function () {
5802 return _this.button({
5803 contents: _this.ui.icon(_this.options.icons.unlink),
5804 tooltip: _this.lang.link.unlink,
5805 click: _this.context.createInvokeHandler('editor.unlink')
5806 }).render();
5807 });
5808 };
5809 /**
5810 * table : [
5811 * ['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
5812 * ['delete', ['deleteRow', 'deleteCol', 'deleteTable']]
5813 * ],
5814 */
5815 Buttons.prototype.addTablePopoverButtons = function () {
5816 var _this = this;
5817 this.context.memo('button.addRowUp', function () {
5818 return _this.button({
5819 className: 'btn-md',
5820 contents: _this.ui.icon(_this.options.icons.rowAbove),
5821 tooltip: _this.lang.table.addRowAbove,
5822 click: _this.context.createInvokeHandler('editor.addRow', 'top')
5823 }).render();
5824 });
5825 this.context.memo('button.addRowDown', function () {
5826 return _this.button({
5827 className: 'btn-md',
5828 contents: _this.ui.icon(_this.options.icons.rowBelow),
5829 tooltip: _this.lang.table.addRowBelow,
5830 click: _this.context.createInvokeHandler('editor.addRow', 'bottom')
5831 }).render();
5832 });
5833 this.context.memo('button.addColLeft', function () {
5834 return _this.button({
5835 className: 'btn-md',
5836 contents: _this.ui.icon(_this.options.icons.colBefore),
5837 tooltip: _this.lang.table.addColLeft,
5838 click: _this.context.createInvokeHandler('editor.addCol', 'left')
5839 }).render();
5840 });
5841 this.context.memo('button.addColRight', function () {
5842 return _this.button({
5843 className: 'btn-md',
5844 contents: _this.ui.icon(_this.options.icons.colAfter),
5845 tooltip: _this.lang.table.addColRight,
5846 click: _this.context.createInvokeHandler('editor.addCol', 'right')
5847 }).render();
5848 });
5849 this.context.memo('button.deleteRow', function () {
5850 return _this.button({
5851 className: 'btn-md',
5852 contents: _this.ui.icon(_this.options.icons.rowRemove),
5853 tooltip: _this.lang.table.delRow,
5854 click: _this.context.createInvokeHandler('editor.deleteRow')
5855 }).render();
5856 });
5857 this.context.memo('button.deleteCol', function () {
5858 return _this.button({
5859 className: 'btn-md',
5860 contents: _this.ui.icon(_this.options.icons.colRemove),
5861 tooltip: _this.lang.table.delCol,
5862 click: _this.context.createInvokeHandler('editor.deleteCol')
5863 }).render();
5864 });
5865 this.context.memo('button.deleteTable', function () {
5866 return _this.button({
5867 className: 'btn-md',
5868 contents: _this.ui.icon(_this.options.icons.trash),
5869 tooltip: _this.lang.table.delTable,
5870 click: _this.context.createInvokeHandler('editor.deleteTable')
5871 }).render();
5872 });
5873 };
5874 Buttons.prototype.build = function ($container, groups) {
5875 for (var groupIdx = 0, groupLen = groups.length; groupIdx < groupLen; groupIdx++) {
5876 var group = groups[groupIdx];
5877 var groupName = $$1.isArray(group) ? group[0] : group;
5878 var buttons = $$1.isArray(group) ? ((group.length === 1) ? [group[0]] : group[1]) : [group];
5879 var $group = this.ui.buttonGroup({
5880 className: 'note-' + groupName
5881 }).render();
5882 for (var idx = 0, len = buttons.length; idx < len; idx++) {
5883 var btn = this.context.memo('button.' + buttons[idx]);
5884 if (btn) {
5885 $group.append(typeof btn === 'function' ? btn(this.context) : btn);
5886 }
5887 }
5888 $group.appendTo($container);
5889 }
5890 };
5891 /**
5892 * @param {jQuery} [$container]
5893 */
5894 Buttons.prototype.updateCurrentStyle = function ($container) {
5895 var _this = this;
5896 var $cont = $container || this.$toolbar;
5897 var styleInfo = this.context.invoke('editor.currentStyle');
5898 this.updateBtnStates($cont, {
5899 '.note-btn-bold': function () {
5900 return styleInfo['font-bold'] === 'bold';
5901 },
5902 '.note-btn-italic': function () {
5903 return styleInfo['font-italic'] === 'italic';
5904 },
5905 '.note-btn-underline': function () {
5906 return styleInfo['font-underline'] === 'underline';
5907 },
5908 '.note-btn-subscript': function () {
5909 return styleInfo['font-subscript'] === 'subscript';
5910 },
5911 '.note-btn-superscript': function () {
5912 return styleInfo['font-superscript'] === 'superscript';
5913 },
5914 '.note-btn-strikethrough': function () {
5915 return styleInfo['font-strikethrough'] === 'strikethrough';
5916 }
5917 });
5918 if (styleInfo['font-family']) {
5919 var fontNames = styleInfo['font-family'].split(',').map(function (name) {
5920 return name.replace(/[\'\"]/g, '')
5921 .replace(/\s+$/, '')
5922 .replace(/^\s+/, '');
5923 });
5924 var fontName_1 = lists.find(fontNames, this.isFontInstalled.bind(this));
5925 $cont.find('.dropdown-fontname a').each(function (idx, item) {
5926 var $item = $$1(item);
5927 // always compare string to avoid creating another func.
5928 var isChecked = ($item.data('value') + '') === (fontName_1 + '');
5929 $item.toggleClass('checked', isChecked);
5930 });
5931 $cont.find('.note-current-fontname').text(fontName_1).css('font-family', fontName_1);
5932 }
5933 if (styleInfo['font-size']) {
5934 var fontSize_1 = styleInfo['font-size'];
5935 $cont.find('.dropdown-fontsize a').each(function (idx, item) {
5936 var $item = $$1(item);
5937 // always compare with string to avoid creating another func.
5938 var isChecked = ($item.data('value') + '') === (fontSize_1 + '');
5939 $item.toggleClass('checked', isChecked);
5940 });
5941 $cont.find('.note-current-fontsize').text(fontSize_1);
5942 }
5943 if (styleInfo['line-height']) {
5944 var lineHeight_1 = styleInfo['line-height'];
5945 $cont.find('.dropdown-line-height li a').each(function (idx, item) {
5946 // always compare with string to avoid creating another func.
5947 var isChecked = ($$1(item).data('value') + '') === (lineHeight_1 + '');
5948 _this.className = isChecked ? 'checked' : '';
5949 });
5950 }
5951 };
5952 Buttons.prototype.updateBtnStates = function ($container, infos) {
5953 var _this = this;
5954 $$1.each(infos, function (selector, pred) {
5955 _this.ui.toggleBtnActive($container.find(selector), pred());
5956 });
5957 };
5958 Buttons.prototype.tableMoveHandler = function (event) {
5959 var PX_PER_EM = 18;
5960 var $picker = $$1(event.target.parentNode); // target is mousecatcher
5961 var $dimensionDisplay = $picker.next();
5962 var $catcher = $picker.find('.note-dimension-picker-mousecatcher');
5963 var $highlighted = $picker.find('.note-dimension-picker-highlighted');
5964 var $unhighlighted = $picker.find('.note-dimension-picker-unhighlighted');
5965 var posOffset;
5966 // HTML5 with jQuery - e.offsetX is undefined in Firefox
5967 if (event.offsetX === undefined) {
5968 var posCatcher = $$1(event.target).offset();
5969 posOffset = {
5970 x: event.pageX - posCatcher.left,
5971 y: event.pageY - posCatcher.top
5972 };
5973 }
5974 else {
5975 posOffset = {
5976 x: event.offsetX,
5977 y: event.offsetY
5978 };
5979 }
5980 var dim = {
5981 c: Math.ceil(posOffset.x / PX_PER_EM) || 1,
5982 r: Math.ceil(posOffset.y / PX_PER_EM) || 1
5983 };
5984 $highlighted.css({ width: dim.c + 'em', height: dim.r + 'em' });
5985 $catcher.data('value', dim.c + 'x' + dim.r);
5986 if (dim.c > 3 && dim.c < this.options.insertTableMaxSize.col) {
5987 $unhighlighted.css({ width: dim.c + 1 + 'em' });
5988 }
5989 if (dim.r > 3 && dim.r < this.options.insertTableMaxSize.row) {
5990 $unhighlighted.css({ height: dim.r + 1 + 'em' });
5991 }
5992 $dimensionDisplay.html(dim.c + ' x ' + dim.r);
5993 };
5994 return Buttons;
5995 }());
5996
5997 var Toolbar = /** @class */ (function () {
5998 function Toolbar(context) {
5999 this.context = context;
6000 this.$window = $$1(window);
6001 this.$document = $$1(document);
6002 this.ui = $$1.summernote.ui;
6003 this.$note = context.layoutInfo.note;
6004 this.$editor = context.layoutInfo.editor;
6005 this.$toolbar = context.layoutInfo.toolbar;
6006 this.options = context.options;
6007 this.followScroll = this.followScroll.bind(this);
6008 }
6009 Toolbar.prototype.shouldInitialize = function () {
6010 return !this.options.airMode;
6011 };
6012 Toolbar.prototype.initialize = function () {
6013 var _this = this;
6014 this.options.toolbar = this.options.toolbar || [];
6015 if (!this.options.toolbar.length) {
6016 this.$toolbar.hide();
6017 }
6018 else {
6019 this.context.invoke('buttons.build', this.$toolbar, this.options.toolbar);
6020 }
6021 if (this.options.toolbarContainer) {
6022 this.$toolbar.appendTo(this.options.toolbarContainer);
6023 }
6024 this.changeContainer(false);
6025 this.$note.on('summernote.keyup summernote.mouseup summernote.change', function () {
6026 _this.context.invoke('buttons.updateCurrentStyle');
6027 });
6028 this.context.invoke('buttons.updateCurrentStyle');
6029 if (this.options.followingToolbar) {
6030 this.$window.on('scroll resize', this.followScroll);
6031 }
6032 };
6033 Toolbar.prototype.destroy = function () {
6034 this.$toolbar.children().remove();
6035 if (this.options.followingToolbar) {
6036 this.$window.off('scroll resize', this.followScroll);
6037 }
6038 };
6039 Toolbar.prototype.followScroll = function () {
6040 if (this.$editor.hasClass('fullscreen')) {
6041 return false;
6042 }
6043 var $toolbarWrapper = this.$toolbar.parent('.note-toolbar-wrapper');
6044 var editorHeight = this.$editor.outerHeight();
6045 var editorWidth = this.$editor.width();
6046 var toolbarHeight = this.$toolbar.height();
6047 $toolbarWrapper.css({
6048 height: toolbarHeight
6049 });
6050 // check if the web app is currently using another static bar
6051 var otherBarHeight = 0;
6052 if (this.options.otherStaticBar) {
6053 otherBarHeight = $$1(this.options.otherStaticBar).outerHeight();
6054 }
6055 var currentOffset = this.$document.scrollTop();
6056 var editorOffsetTop = this.$editor.offset().top;
6057 var editorOffsetBottom = editorOffsetTop + editorHeight;
6058 var activateOffset = editorOffsetTop - otherBarHeight;
6059 var deactivateOffsetBottom = editorOffsetBottom - otherBarHeight - toolbarHeight;
6060 if ((currentOffset > activateOffset) && (currentOffset < deactivateOffsetBottom)) {
6061 this.$toolbar.css({
6062 position: 'fixed',
6063 top: otherBarHeight,
6064 width: editorWidth
6065 });
6066 }
6067 else {
6068 this.$toolbar.css({
6069 position: 'relative',
6070 top: 0,
6071 width: '100%'
6072 });
6073 }
6074 };
6075 Toolbar.prototype.changeContainer = function (isFullscreen) {
6076 if (isFullscreen) {
6077 this.$toolbar.prependTo(this.$editor);
6078 }
6079 else {
6080 if (this.options.toolbarContainer) {
6081 this.$toolbar.appendTo(this.options.toolbarContainer);
6082 }
6083 }
6084 };
6085 Toolbar.prototype.updateFullscreen = function (isFullscreen) {
6086 this.ui.toggleBtnActive(this.$toolbar.find('.btn-fullscreen'), isFullscreen);
6087 this.changeContainer(isFullscreen);
6088 };
6089 Toolbar.prototype.updateCodeview = function (isCodeview) {
6090 this.ui.toggleBtnActive(this.$toolbar.find('.btn-codeview'), isCodeview);
6091 if (isCodeview) {
6092 this.deactivate();
6093 }
6094 else {
6095 this.activate();
6096 }
6097 };
6098 Toolbar.prototype.activate = function (isIncludeCodeview) {
6099 var $btn = this.$toolbar.find('button');
6100 if (!isIncludeCodeview) {
6101 $btn = $btn.not('.btn-codeview');
6102 }
6103 this.ui.toggleBtn($btn, true);
6104 };
6105 Toolbar.prototype.deactivate = function (isIncludeCodeview) {
6106 var $btn = this.$toolbar.find('button');
6107 if (!isIncludeCodeview) {
6108 $btn = $btn.not('.btn-codeview');
6109 }
6110 this.ui.toggleBtn($btn, false);
6111 };
6112 return Toolbar;
6113 }());
6114
6115 var LinkDialog = /** @class */ (function () {
6116 function LinkDialog(context) {
6117 this.context = context;
6118 this.ui = $$1.summernote.ui;
6119 this.$body = $$1(document.body);
6120 this.$editor = context.layoutInfo.editor;
6121 this.options = context.options;
6122 this.lang = this.options.langInfo;
6123 context.memo('help.linkDialog.show', this.options.langInfo.help['linkDialog.show']);
6124 }
6125 LinkDialog.prototype.initialize = function () {
6126 var $container = this.options.dialogsInBody ? this.$body : this.$editor;
6127 var body = [
6128 '<div class="form-group note-form-group">',
6129 "<label class=\"note-form-label\">" + this.lang.link.textToDisplay + "</label>",
6130 '<input class="note-link-text form-control note-form-control note-input" type="text" />',
6131 '</div>',
6132 '<div class="form-group note-form-group">',
6133 "<label class=\"note-form-label\">" + this.lang.link.url + "</label>",
6134 '<input class="note-link-url form-control note-form-control note-input" type="text" value="http://" />',
6135 '</div>',
6136 !this.options.disableLinkTarget
6137 ? $$1('<div/>').append(this.ui.checkbox({
6138 className: 'sn-checkbox-open-in-new-window',
6139 text: this.lang.link.openInNewWindow,
6140 checked: true
6141 }).render()).html()
6142 : ''
6143 ].join('');
6144 var buttonClass = 'btn btn-primary note-btn note-btn-primary note-link-btn';
6145 var footer = "<input type=\"button\" href=\"#\" class=\"" + buttonClass + "\" value=\"" + this.lang.link.insert + "\" disabled>";
6146 this.$dialog = this.ui.dialog({
6147 className: 'link-dialog',
6148 title: this.lang.link.insert,
6149 fade: this.options.dialogsFade,
6150 body: body,
6151 footer: footer
6152 }).render().appendTo($container);
6153 };
6154 LinkDialog.prototype.destroy = function () {
6155 this.ui.hideDialog(this.$dialog);
6156 this.$dialog.remove();
6157 };
6158 LinkDialog.prototype.bindEnterKey = function ($input, $btn) {
6159 $input.on('keypress', function (event) {
6160 if (event.keyCode === key.code.ENTER) {
6161 event.preventDefault();
6162 $btn.trigger('click');
6163 }
6164 });
6165 };
6166 /**
6167 * toggle update button
6168 */
6169 LinkDialog.prototype.toggleLinkBtn = function ($linkBtn, $linkText, $linkUrl) {
6170 this.ui.toggleBtn($linkBtn, $linkText.val() && $linkUrl.val());
6171 };
6172 /**
6173 * Show link dialog and set event handlers on dialog controls.
6174 *
6175 * @param {Object} linkInfo
6176 * @return {Promise}
6177 */
6178 LinkDialog.prototype.showLinkDialog = function (linkInfo) {
6179 var _this = this;
6180 return $$1.Deferred(function (deferred) {
6181 var $linkText = _this.$dialog.find('.note-link-text');
6182 var $linkUrl = _this.$dialog.find('.note-link-url');
6183 var $linkBtn = _this.$dialog.find('.note-link-btn');
6184 var $openInNewWindow = _this.$dialog
6185 .find('.sn-checkbox-open-in-new-window input[type=checkbox]');
6186 _this.ui.onDialogShown(_this.$dialog, function () {
6187 _this.context.triggerEvent('dialog.shown');
6188 // if no url was given, copy text to url
6189 if (!linkInfo.url) {
6190 linkInfo.url = linkInfo.text;
6191 }
6192 $linkText.val(linkInfo.text);
6193 var handleLinkTextUpdate = function () {
6194 _this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
6195 // if linktext was modified by keyup,
6196 // stop cloning text from linkUrl
6197 linkInfo.text = $linkText.val();
6198 };
6199 $linkText.on('input', handleLinkTextUpdate).on('paste', function () {
6200 setTimeout(handleLinkTextUpdate, 0);
6201 });
6202 var handleLinkUrlUpdate = function () {
6203 _this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
6204 // display same link on `Text to display` input
6205 // when create a new link
6206 if (!linkInfo.text) {
6207 $linkText.val($linkUrl.val());
6208 }
6209 };
6210 $linkUrl.on('input', handleLinkUrlUpdate).on('paste', function () {
6211 setTimeout(handleLinkUrlUpdate, 0);
6212 }).val(linkInfo.url);
6213 if (!env.isSupportTouch) {
6214 $linkUrl.trigger('focus');
6215 }
6216 _this.toggleLinkBtn($linkBtn, $linkText, $linkUrl);
6217 _this.bindEnterKey($linkUrl, $linkBtn);
6218 _this.bindEnterKey($linkText, $linkBtn);
6219 var isNewWindowChecked = linkInfo.isNewWindow !== undefined
6220 ? linkInfo.isNewWindow : _this.context.options.linkTargetBlank;
6221 $openInNewWindow.prop('checked', isNewWindowChecked);
6222 $linkBtn.one('click', function (event) {
6223 event.preventDefault();
6224 deferred.resolve({
6225 range: linkInfo.range,
6226 url: $linkUrl.val(),
6227 text: $linkText.val(),
6228 isNewWindow: $openInNewWindow.is(':checked')
6229 });
6230 _this.ui.hideDialog(_this.$dialog);
6231 });
6232 });
6233 _this.ui.onDialogHidden(_this.$dialog, function () {
6234 // detach events
6235 $linkText.off('input paste keypress');
6236 $linkUrl.off('input paste keypress');
6237 $linkBtn.off('click');
6238 if (deferred.state() === 'pending') {
6239 deferred.reject();
6240 }
6241 });
6242 _this.ui.showDialog(_this.$dialog);
6243 }).promise();
6244 };
6245 /**
6246 * @param {Object} layoutInfo
6247 */
6248 LinkDialog.prototype.show = function () {
6249 var _this = this;
6250 var linkInfo = this.context.invoke('editor.getLinkInfo');
6251 this.context.invoke('editor.saveRange');
6252 this.showLinkDialog(linkInfo).then(function (linkInfo) {
6253 _this.context.invoke('editor.restoreRange');
6254 _this.context.invoke('editor.createLink', linkInfo);
6255 }).fail(function () {
6256 _this.context.invoke('editor.restoreRange');
6257 });
6258 };
6259 return LinkDialog;
6260 }());
6261
6262 var LinkPopover = /** @class */ (function () {
6263 function LinkPopover(context) {
6264 var _this = this;
6265 this.context = context;
6266 this.ui = $$1.summernote.ui;
6267 this.options = context.options;
6268 this.events = {
6269 'summernote.keyup summernote.mouseup summernote.change summernote.scroll': function () {
6270 _this.update();
6271 },
6272 'summernote.disable summernote.dialog.shown': function () {
6273 _this.hide();
6274 }
6275 };
6276 }
6277 LinkPopover.prototype.shouldInitialize = function () {
6278 return !lists.isEmpty(this.options.popover.link);
6279 };
6280 LinkPopover.prototype.initialize = function () {
6281 this.$popover = this.ui.popover({
6282 className: 'note-link-popover',
6283 callback: function ($node) {
6284 var $content = $node.find('.popover-content,.note-popover-content');
6285 $content.prepend('<span><a target="_blank"></a> </span>');
6286 }
6287 }).render().appendTo(this.options.container);
6288 var $content = this.$popover.find('.popover-content,.note-popover-content');
6289 this.context.invoke('buttons.build', $content, this.options.popover.link);
6290 };
6291 LinkPopover.prototype.destroy = function () {
6292 this.$popover.remove();
6293 };
6294 LinkPopover.prototype.update = function () {
6295 // Prevent focusing on editable when invoke('code') is executed
6296 if (!this.context.invoke('editor.hasFocus')) {
6297 this.hide();
6298 return;
6299 }
6300 var rng = this.context.invoke('editor.createRange');
6301 if (rng.isCollapsed() && rng.isOnAnchor()) {
6302 var anchor = dom.ancestor(rng.sc, dom.isAnchor);
6303 var href = $$1(anchor).attr('href');
6304 this.$popover.find('a').attr('href', href).html(href);
6305 var pos = dom.posFromPlaceholder(anchor);
6306 this.$popover.css({
6307 display: 'block',
6308 left: pos.left,
6309 top: pos.top
6310 });
6311 }
6312 else {
6313 this.hide();
6314 }
6315 };
6316 LinkPopover.prototype.hide = function () {
6317 this.$popover.hide();
6318 };
6319 return LinkPopover;
6320 }());
6321
6322 var ImageDialog = /** @class */ (function () {
6323 function ImageDialog(context) {
6324 this.context = context;
6325 this.ui = $$1.summernote.ui;
6326 this.$body = $$1(document.body);
6327 this.$editor = context.layoutInfo.editor;
6328 this.options = context.options;
6329 this.lang = this.options.langInfo;
6330 }
6331 ImageDialog.prototype.initialize = function () {
6332 var $container = this.options.dialogsInBody ? this.$body : this.$editor;
6333 var imageLimitation = '';
6334 if (this.options.maximumImageFileSize) {
6335 var unit = Math.floor(Math.log(this.options.maximumImageFileSize) / Math.log(1024));
6336 var readableSize = (this.options.maximumImageFileSize / Math.pow(1024, unit)).toFixed(2) * 1 +
6337 ' ' + ' KMGTP'[unit] + 'B';
6338 imageLimitation = "<small>" + (this.lang.image.maximumFileSize + ' : ' + readableSize) + "</small>";
6339 }
6340 var body = [
6341 '<div class="form-group note-form-group note-group-select-from-files">',
6342 '<label class="note-form-label">' + this.lang.image.selectFromFiles + '</label>',
6343 '<input class="note-image-input note-form-control note-input" ',
6344 ' type="file" name="files" accept="image/*" multiple="multiple" />',
6345 imageLimitation,
6346 '</div>',
6347 '<div class="form-group note-group-image-url" style="overflow:auto;">',
6348 '<label class="note-form-label">' + this.lang.image.url + '</label>',
6349 '<input class="note-image-url form-control note-form-control note-input ',
6350 ' col-md-12" type="text" />',
6351 '</div>'
6352 ].join('');
6353 var buttonClass = 'btn btn-primary note-btn note-btn-primary note-image-btn';
6354 var footer = "<input type=\"button\" href=\"#\" class=\"" + buttonClass + "\" value=\"" + this.lang.image.insert + "\" disabled>";
6355 this.$dialog = this.ui.dialog({
6356 title: this.lang.image.insert,
6357 fade: this.options.dialogsFade,
6358 body: body,
6359 footer: footer
6360 }).render().appendTo($container);
6361 };
6362 ImageDialog.prototype.destroy = function () {
6363 this.ui.hideDialog(this.$dialog);
6364 this.$dialog.remove();
6365 };
6366 ImageDialog.prototype.bindEnterKey = function ($input, $btn) {
6367 $input.on('keypress', function (event) {
6368 if (event.keyCode === key.code.ENTER) {
6369 event.preventDefault();
6370 $btn.trigger('click');
6371 }
6372 });
6373 };
6374 ImageDialog.prototype.show = function () {
6375 var _this = this;
6376 this.context.invoke('editor.saveRange');
6377 this.showImageDialog().then(function (data) {
6378 // [workaround] hide dialog before restore range for IE range focus
6379 _this.ui.hideDialog(_this.$dialog);
6380 _this.context.invoke('editor.restoreRange');
6381 if (typeof data === 'string') { // image url
6382 // If onImageLinkInsert set,
6383 if (_this.options.callbacks.onImageLinkInsert) {
6384 _this.context.triggerEvent('image.link.insert', data);
6385 }
6386 else {
6387 _this.context.invoke('editor.insertImage', data);
6388 }
6389 }
6390 else { // array of files
6391 // If onImageUpload set,
6392 if (_this.options.callbacks.onImageUpload) {
6393 _this.context.triggerEvent('image.upload', data);
6394 }
6395 else {
6396 // else insert Image as dataURL
6397 _this.context.invoke('editor.insertImagesAsDataURL', data);
6398 }
6399 }
6400 }).fail(function () {
6401 _this.context.invoke('editor.restoreRange');
6402 });
6403 };
6404 /**
6405 * show image dialog
6406 *
6407 * @param {jQuery} $dialog
6408 * @return {Promise}
6409 */
6410 ImageDialog.prototype.showImageDialog = function () {
6411 var _this = this;
6412 return $$1.Deferred(function (deferred) {
6413 var $imageInput = _this.$dialog.find('.note-image-input');
6414 var $imageUrl = _this.$dialog.find('.note-image-url');
6415 var $imageBtn = _this.$dialog.find('.note-image-btn');
6416 _this.ui.onDialogShown(_this.$dialog, function () {
6417 _this.context.triggerEvent('dialog.shown');
6418 // Cloning imageInput to clear element.
6419 $imageInput.replaceWith($imageInput.clone().on('change', function (event) {
6420 deferred.resolve(event.target.files || event.target.value);
6421 }).val(''));
6422 $imageBtn.click(function (event) {
6423 event.preventDefault();
6424 deferred.resolve($imageUrl.val());
6425 });
6426 $imageUrl.on('keyup paste', function () {
6427 var url = $imageUrl.val();
6428 _this.ui.toggleBtn($imageBtn, url);
6429 }).val('');
6430 if (!env.isSupportTouch) {
6431 $imageUrl.trigger('focus');
6432 }
6433 _this.bindEnterKey($imageUrl, $imageBtn);
6434 });
6435 _this.ui.onDialogHidden(_this.$dialog, function () {
6436 $imageInput.off('change');
6437 $imageUrl.off('keyup paste keypress');
6438 $imageBtn.off('click');
6439 if (deferred.state() === 'pending') {
6440 deferred.reject();
6441 }
6442 });
6443 _this.ui.showDialog(_this.$dialog);
6444 });
6445 };
6446 return ImageDialog;
6447 }());
6448
6449 /**
6450 * Image popover module
6451 * mouse events that show/hide popover will be handled by Handle.js.
6452 * Handle.js will receive the events and invoke 'imagePopover.update'.
6453 */
6454 var ImagePopover = /** @class */ (function () {
6455 function ImagePopover(context) {
6456 var _this = this;
6457 this.context = context;
6458 this.ui = $$1.summernote.ui;
6459 this.editable = context.layoutInfo.editable[0];
6460 this.options = context.options;
6461 this.events = {
6462 'summernote.disable': function () {
6463 _this.hide();
6464 }
6465 };
6466 }
6467 ImagePopover.prototype.shouldInitialize = function () {
6468 return !lists.isEmpty(this.options.popover.image);
6469 };
6470 ImagePopover.prototype.initialize = function () {
6471 this.$popover = this.ui.popover({
6472 className: 'note-image-popover'
6473 }).render().appendTo(this.options.container);
6474 var $content = this.$popover.find('.popover-content,.note-popover-content');
6475 this.context.invoke('buttons.build', $content, this.options.popover.image);
6476 };
6477 ImagePopover.prototype.destroy = function () {
6478 this.$popover.remove();
6479 };
6480 ImagePopover.prototype.update = function (target,event) {
6481 if (dom.isImg(target)) {
6482 var pos = dom.posFromPlaceholder(target);
6483 var posEditor = dom.posFromPlaceholder(this.editable);
6484 this.$popover.css({
6485 display: 'block',
6486 left: this.options.popatmouse ? event.pageX - 20 : pos.left,
6487 top: this.options.popatmouse ? event.pageY : Math.min(pos.top, posEditor.top)
6488 });
6489 }
6490 else {
6491 this.hide();
6492 }
6493 };
6494 ImagePopover.prototype.hide = function () {
6495 this.$popover.hide();
6496 };
6497 return ImagePopover;
6498 }());
6499
6500 var TablePopover = /** @class */ (function () {
6501 function TablePopover(context) {
6502 var _this = this;
6503 this.context = context;
6504 this.ui = $$1.summernote.ui;
6505 this.options = context.options;
6506 this.events = {
6507 'summernote.mousedown': function (we, e) {
6508 _this.update(e.target);
6509 },
6510 'summernote.keyup summernote.scroll summernote.change': function () {
6511 _this.update();
6512 },
6513 'summernote.disable': function () {
6514 _this.hide();
6515 }
6516 };
6517 }
6518 TablePopover.prototype.shouldInitialize = function () {
6519 return !lists.isEmpty(this.options.popover.table);
6520 };
6521 TablePopover.prototype.initialize = function () {
6522 this.$popover = this.ui.popover({
6523 className: 'note-table-popover'
6524 }).render().appendTo(this.options.container);
6525 var $content = this.$popover.find('.popover-content,.note-popover-content');
6526 this.context.invoke('buttons.build', $content, this.options.popover.table);
6527 // [workaround] Disable Firefox's default table editor
6528 if (env.isFF) {
6529 document.execCommand('enableInlineTableEditing', false, false);
6530 }
6531 };
6532 TablePopover.prototype.destroy = function () {
6533 this.$popover.remove();
6534 };
6535 TablePopover.prototype.update = function (target) {
6536 if (this.context.isDisabled()) {
6537 return false;
6538 }
6539 var isCell = dom.isCell(target);
6540 if (isCell) {
6541 var pos = dom.posFromPlaceholder(target);
6542 this.$popover.css({
6543 display: 'block',
6544 left: pos.left,
6545 top: pos.top
6546 });
6547 }
6548 else {
6549 this.hide();
6550 }
6551 return isCell;
6552 };
6553 TablePopover.prototype.hide = function () {
6554 this.$popover.hide();
6555 };
6556 return TablePopover;
6557 }());
6558
6559 var VideoDialog = /** @class */ (function () {
6560 function VideoDialog(context) {
6561 this.context = context;
6562 this.ui = $$1.summernote.ui;
6563 this.$body = $$1(document.body);
6564 this.$editor = context.layoutInfo.editor;
6565 this.options = context.options;
6566 this.lang = this.options.langInfo;
6567 }
6568 VideoDialog.prototype.initialize = function () {
6569 var $container = this.options.dialogsInBody ? this.$body : this.$editor;
6570 var body = [
6571 '<div class="form-group note-form-group row-fluid">',
6572 "<label class=\"note-form-label\">" + this.lang.video.url + " <small class=\"text-muted\">" + this.lang.video.providers + "</small></label>",
6573 '<input class="note-video-url form-control note-form-control note-input" type="text" />',
6574 '</div>'
6575 ].join('');
6576 var buttonClass = 'btn btn-primary note-btn note-btn-primary note-video-btn';
6577 var footer = "<input type=\"button\" href=\"#\" class=\"" + buttonClass + "\" value=\"" + this.lang.video.insert + "\" disabled>";
6578 this.$dialog = this.ui.dialog({
6579 title: this.lang.video.insert,
6580 fade: this.options.dialogsFade,
6581 body: body,
6582 footer: footer
6583 }).render().appendTo($container);
6584 };
6585 VideoDialog.prototype.destroy = function () {
6586 this.ui.hideDialog(this.$dialog);
6587 this.$dialog.remove();
6588 };
6589 VideoDialog.prototype.bindEnterKey = function ($input, $btn) {
6590 $input.on('keypress', function (event) {
6591 if (event.keyCode === key.code.ENTER) {
6592 event.preventDefault();
6593 $btn.trigger('click');
6594 }
6595 });
6596 };
6597 VideoDialog.prototype.createVideoNode = function (url) {
6598 // video url patterns(youtube, instagram, vimeo, dailymotion, youku, mp4, ogg, webm)
6599 var ytRegExp = /\/\/(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([\w|-]{11})(?:(?:[\?&]t=)(\S+))?$/;
6600 var ytRegExpForStart = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/;
6601 var ytMatch = url.match(ytRegExp);
6602 var igRegExp = /(?:www\.|\/\/)instagram\.com\/p\/(.[a-zA-Z0-9_-]*)/;
6603 var igMatch = url.match(igRegExp);
6604 var vRegExp = /\/\/vine\.co\/v\/([a-zA-Z0-9]+)/;
6605 var vMatch = url.match(vRegExp);
6606 var vimRegExp = /\/\/(player\.)?vimeo\.com\/([a-z]*\/)*(\d+)[?]?.*/;
6607 var vimMatch = url.match(vimRegExp);
6608 var dmRegExp = /.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/;
6609 var dmMatch = url.match(dmRegExp);
6610 var youkuRegExp = /\/\/v\.youku\.com\/v_show\/id_(\w+)=*\.html/;
6611 var youkuMatch = url.match(youkuRegExp);
6612 var qqRegExp = /\/\/v\.qq\.com.*?vid=(.+)/;
6613 var qqMatch = url.match(qqRegExp);
6614 var qqRegExp2 = /\/\/v\.qq\.com\/x?\/?(page|cover).*?\/([^\/]+)\.html\??.*/;
6615 var qqMatch2 = url.match(qqRegExp2);
6616 var mp4RegExp = /^.+.(mp4|m4v)$/;
6617 var mp4Match = url.match(mp4RegExp);
6618 var oggRegExp = /^.+.(ogg|ogv)$/;
6619 var oggMatch = url.match(oggRegExp);
6620 var webmRegExp = /^.+.(webm)$/;
6621 var webmMatch = url.match(webmRegExp);
6622 var $video;
6623 if (ytMatch && ytMatch[1].length === 11) {
6624 var youtubeId = ytMatch[1];
6625 var start = 0;
6626 if (typeof ytMatch[2] !== 'undefined') {
6627 var ytMatchForStart = ytMatch[2].match(ytRegExpForStart);
6628 if (ytMatchForStart) {
6629 for (var n = [3600, 60, 1], i = 0, r = n.length; i < r; i++) {
6630 start += (typeof ytMatchForStart[i + 1] !== 'undefined' ? n[i] * parseInt(ytMatchForStart[i + 1], 10) : 0);
6631 }
6632 }
6633 }
6634 $video = $$1('<iframe>')
6635 .attr('frameborder', 0)
6636 .attr('src', '//www.youtube.com/embed/' + youtubeId + (start > 0 ? '?start=' + start : ''))
6637 .attr('width', '640').attr('height', '360');
6638 }
6639 else if (igMatch && igMatch[0].length) {
6640 $video = $$1('<iframe>')
6641 .attr('frameborder', 0)
6642 .attr('src', 'https://instagram.com/p/' + igMatch[1] + '/embed/')
6643 .attr('width', '612').attr('height', '710')
6644 .attr('scrolling', 'no')
6645 .attr('allowtransparency', 'true');
6646 }
6647 else if (vMatch && vMatch[0].length) {
6648 $video = $$1('<iframe>')
6649 .attr('frameborder', 0)
6650 .attr('src', vMatch[0] + '/embed/simple')
6651 .attr('width', '600').attr('height', '600')
6652 .attr('class', 'vine-embed');
6653 }
6654 else if (vimMatch && vimMatch[3].length) {
6655 $video = $$1('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
6656 .attr('frameborder', 0)
6657 .attr('src', '//player.vimeo.com/video/' + vimMatch[3])
6658 .attr('width', '640').attr('height', '360');
6659 }
6660 else if (dmMatch && dmMatch[2].length) {
6661 $video = $$1('<iframe>')
6662 .attr('frameborder', 0)
6663 .attr('src', '//www.dailymotion.com/embed/video/' + dmMatch[2])
6664 .attr('width', '640').attr('height', '360');
6665 }
6666 else if (youkuMatch && youkuMatch[1].length) {
6667 $video = $$1('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
6668 .attr('frameborder', 0)
6669 .attr('height', '498')
6670 .attr('width', '510')
6671 .attr('src', '//player.youku.com/embed/' + youkuMatch[1]);
6672 }
6673 else if ((qqMatch && qqMatch[1].length) || (qqMatch2 && qqMatch2[2].length)) {
6674 var vid = ((qqMatch && qqMatch[1].length) ? qqMatch[1] : qqMatch2[2]);
6675 $video = $$1('<iframe webkitallowfullscreen mozallowfullscreen allowfullscreen>')
6676 .attr('frameborder', 0)
6677 .attr('height', '310')
6678 .attr('width', '500')
6679 .attr('src', 'http://v.qq.com/iframe/player.html?vid=' + vid + '&auto=0');
6680 }
6681 else if (mp4Match || oggMatch || webmMatch) {
6682 $video = $$1('<video controls>')
6683 .attr('src', url)
6684 .attr('width', '640').attr('height', '360');
6685 }
6686 else {
6687 // this is not a known video link. Now what, Cat? Now what?
6688 return false;
6689 }
6690 $video.addClass('note-video-clip');
6691 return $video[0];
6692 };
6693 VideoDialog.prototype.show = function () {
6694 var _this = this;
6695 var text = this.context.invoke('editor.getSelectedText');
6696 this.context.invoke('editor.saveRange');
6697 this.showVideoDialog(text).then(function (url) {
6698 // [workaround] hide dialog before restore range for IE range focus
6699 _this.ui.hideDialog(_this.$dialog);
6700 _this.context.invoke('editor.restoreRange');
6701 // build node
6702 var $node = _this.createVideoNode(url);
6703 if ($node) {
6704 // insert video node
6705 _this.context.invoke('editor.insertNode', $node);
6706 }
6707 }).fail(function () {
6708 _this.context.invoke('editor.restoreRange');
6709 });
6710 };
6711 /**
6712 * show image dialog
6713 *
6714 * @param {jQuery} $dialog
6715 * @return {Promise}
6716 */
6717 VideoDialog.prototype.showVideoDialog = function (text) {
6718 var _this = this;
6719 return $$1.Deferred(function (deferred) {
6720 var $videoUrl = _this.$dialog.find('.note-video-url');
6721 var $videoBtn = _this.$dialog.find('.note-video-btn');
6722 _this.ui.onDialogShown(_this.$dialog, function () {
6723 _this.context.triggerEvent('dialog.shown');
6724 $videoUrl.val(text).on('input', function () {
6725 _this.ui.toggleBtn($videoBtn, $videoUrl.val());
6726 });
6727 if (!env.isSupportTouch) {
6728 $videoUrl.trigger('focus');
6729 }
6730 $videoBtn.click(function (event) {
6731 event.preventDefault();
6732 deferred.resolve($videoUrl.val());
6733 });
6734 _this.bindEnterKey($videoUrl, $videoBtn);
6735 });
6736 _this.ui.onDialogHidden(_this.$dialog, function () {
6737 $videoUrl.off('input');
6738 $videoBtn.off('click');
6739 if (deferred.state() === 'pending') {
6740 deferred.reject();
6741 }
6742 });
6743 _this.ui.showDialog(_this.$dialog);
6744 });
6745 };
6746 return VideoDialog;
6747 }());
6748
6749 var HelpDialog = /** @class */ (function () {
6750 function HelpDialog(context) {
6751 this.context = context;
6752 this.ui = $$1.summernote.ui;
6753 this.$body = $$1(document.body);
6754 this.$editor = context.layoutInfo.editor;
6755 this.options = context.options;
6756 this.lang = this.options.langInfo;
6757 }
6758 HelpDialog.prototype.initialize = function () {
6759 var $container = this.options.dialogsInBody ? this.$body : this.$editor;
6760 var body = [
6761 '<p class="text-center">',
6762 '<a href="http://summernote.org/" target="_blank">Summernote 0.8.11</a> · ',
6763 '<a href="https://github.com/summernote/summernote" target="_blank">Project</a> · ',
6764 '<a href="https://github.com/summernote/summernote/issues" target="_blank">Issues</a>',
6765 '</p>'
6766 ].join('');
6767 this.$dialog = this.ui.dialog({
6768 title: this.lang.options.help,
6769 fade: this.options.dialogsFade,
6770 body: this.createShortcutList(),
6771 footer: body,
6772 callback: function ($node) {
6773 $node.find('.modal-body,.note-modal-body').css({
6774 'max-height': 300,
6775 'overflow': 'scroll'
6776 });
6777 }
6778 }).render().appendTo($container);
6779 };
6780 HelpDialog.prototype.destroy = function () {
6781 this.ui.hideDialog(this.$dialog);
6782 this.$dialog.remove();
6783 };
6784 HelpDialog.prototype.createShortcutList = function () {
6785 var _this = this;
6786 var keyMap = this.options.keyMap[env.isMac ? 'mac' : 'pc'];
6787 return Object.keys(keyMap).map(function (key) {
6788 var command = keyMap[key];
6789 var $row = $$1('<div><div class="help-list-item"/></div>');
6790 $row.append($$1('<label><kbd>' + key + '</kdb></label>').css({
6791 'width': 180,
6792 'margin-right': 10
6793 })).append($$1('<span/>').html(_this.context.memo('help.' + command) || command));
6794 return $row.html();
6795 }).join('');
6796 };
6797 /**
6798 * show help dialog
6799 *
6800 * @return {Promise}
6801 */
6802 HelpDialog.prototype.showHelpDialog = function () {
6803 var _this = this;
6804 return $$1.Deferred(function (deferred) {
6805 _this.ui.onDialogShown(_this.$dialog, function () {
6806 _this.context.triggerEvent('dialog.shown');
6807 deferred.resolve();
6808 });
6809 _this.ui.showDialog(_this.$dialog);
6810 }).promise();
6811 };
6812 HelpDialog.prototype.show = function () {
6813 var _this = this;
6814 this.context.invoke('editor.saveRange');
6815 this.showHelpDialog().then(function () {
6816 _this.context.invoke('editor.restoreRange');
6817 });
6818 };
6819 return HelpDialog;
6820 }());
6821
6822 var AIR_MODE_POPOVER_X_OFFSET = 20;
6823 var AirPopover = /** @class */ (function () {
6824 function AirPopover(context) {
6825 var _this = this;
6826 this.context = context;
6827 this.ui = $$1.summernote.ui;
6828 this.options = context.options;
6829 this.events = {
6830 'summernote.keyup summernote.mouseup summernote.scroll': function () {
6831 _this.update();
6832 },
6833 'summernote.disable summernote.change summernote.dialog.shown': function () {
6834 _this.hide();
6835 },
6836 'summernote.focusout': function (we, e) {
6837 // [workaround] Firefox doesn't support relatedTarget on focusout
6838 // - Ignore hide action on focus out in FF.
6839 if (env.isFF) {
6840 return;
6841 }
6842 if (!e.relatedTarget || !dom.ancestor(e.relatedTarget, func.eq(_this.$popover[0]))) {
6843 _this.hide();
6844 }
6845 }
6846 };
6847 }
6848 AirPopover.prototype.shouldInitialize = function () {
6849 return this.options.airMode && !lists.isEmpty(this.options.popover.air);
6850 };
6851 AirPopover.prototype.initialize = function () {
6852 this.$popover = this.ui.popover({
6853 className: 'note-air-popover'
6854 }).render().appendTo(this.options.container);
6855 var $content = this.$popover.find('.popover-content');
6856 this.context.invoke('buttons.build', $content, this.options.popover.air);
6857 };
6858 AirPopover.prototype.destroy = function () {
6859 this.$popover.remove();
6860 };
6861 AirPopover.prototype.update = function () {
6862 var styleInfo = this.context.invoke('editor.currentStyle');
6863 if (styleInfo.range && !styleInfo.range.isCollapsed()) {
6864 var rect = lists.last(styleInfo.range.getClientRects());
6865 if (rect) {
6866 var bnd = func.rect2bnd(rect);
6867 this.$popover.css({
6868 display: 'block',
6869 left: Math.max(bnd.left + bnd.width / 2, 0) - AIR_MODE_POPOVER_X_OFFSET,
6870 top: bnd.top + bnd.height
6871 });
6872 this.context.invoke('buttons.updateCurrentStyle', this.$popover);
6873 }
6874 }
6875 else {
6876 this.hide();
6877 }
6878 };
6879 AirPopover.prototype.hide = function () {
6880 this.$popover.hide();
6881 };
6882 return AirPopover;
6883 }());
6884
6885 var POPOVER_DIST = 5;
6886 var HintPopover = /** @class */ (function () {
6887 function HintPopover(context) {
6888 var _this = this;
6889 this.context = context;
6890 this.ui = $$1.summernote.ui;
6891 this.$editable = context.layoutInfo.editable;
6892 this.options = context.options;
6893 this.hint = this.options.hint || [];
6894 this.direction = this.options.hintDirection || 'bottom';
6895 this.hints = $$1.isArray(this.hint) ? this.hint : [this.hint];
6896 this.events = {
6897 'summernote.keyup': function (we, e) {
6898 if (!e.isDefaultPrevented()) {
6899 _this.handleKeyup(e);
6900 }
6901 },
6902 'summernote.keydown': function (we, e) {
6903 _this.handleKeydown(e);
6904 },
6905 'summernote.disable summernote.dialog.shown': function () {
6906 _this.hide();
6907 }
6908 };
6909 }
6910 HintPopover.prototype.shouldInitialize = function () {
6911 return this.hints.length > 0;
6912 };
6913 HintPopover.prototype.initialize = function () {
6914 var _this = this;
6915 this.lastWordRange = null;
6916 this.$popover = this.ui.popover({
6917 className: 'note-hint-popover',
6918 hideArrow: true,
6919 direction: ''
6920 }).render().appendTo(this.options.container);
6921 this.$popover.hide();
6922 this.$content = this.$popover.find('.popover-content,.note-popover-content');
6923 this.$content.on('click', '.note-hint-item', function (e) {
6924 _this.$content.find('.active').removeClass('active');
6925 $$1(e.currentTarget).addClass('active');
6926 _this.replace();
6927 });
6928 };
6929 HintPopover.prototype.destroy = function () {
6930 this.$popover.remove();
6931 };
6932 HintPopover.prototype.selectItem = function ($item) {
6933 this.$content.find('.active').removeClass('active');
6934 $item.addClass('active');
6935 this.$content[0].scrollTop = $item[0].offsetTop - (this.$content.innerHeight() / 2);
6936 };
6937 HintPopover.prototype.moveDown = function () {
6938 var $current = this.$content.find('.note-hint-item.active');
6939 var $next = $current.next();
6940 if ($next.length) {
6941 this.selectItem($next);
6942 }
6943 else {
6944 var $nextGroup = $current.parent().next();
6945 if (!$nextGroup.length) {
6946 $nextGroup = this.$content.find('.note-hint-group').first();
6947 }
6948 this.selectItem($nextGroup.find('.note-hint-item').first());
6949 }
6950 };
6951 HintPopover.prototype.moveUp = function () {
6952 var $current = this.$content.find('.note-hint-item.active');
6953 var $prev = $current.prev();
6954 if ($prev.length) {
6955 this.selectItem($prev);
6956 }
6957 else {
6958 var $prevGroup = $current.parent().prev();
6959 if (!$prevGroup.length) {
6960 $prevGroup = this.$content.find('.note-hint-group').last();
6961 }
6962 this.selectItem($prevGroup.find('.note-hint-item').last());
6963 }
6964 };
6965 HintPopover.prototype.replace = function () {
6966 var $item = this.$content.find('.note-hint-item.active');
6967 if ($item.length) {
6968 var node = this.nodeFromItem($item);
6969 // XXX: consider to move codes to editor for recording redo/undo.
6970 this.lastWordRange.insertNode(node);
6971 range.createFromNode(node).collapse().select();
6972 this.lastWordRange = null;
6973 this.hide();
6974 this.context.triggerEvent('change', this.$editable.html(), this.$editable[0]);
6975 this.context.invoke('editor.focus');
6976 }
6977 };
6978 HintPopover.prototype.nodeFromItem = function ($item) {
6979 var hint = this.hints[$item.data('index')];
6980 var item = $item.data('item');
6981 var node = hint.content ? hint.content(item) : item;
6982 if (typeof node === 'string') {
6983 node = dom.createText(node);
6984 }
6985 return node;
6986 };
6987 HintPopover.prototype.createItemTemplates = function (hintIdx, items) {
6988 var hint = this.hints[hintIdx];
6989 return items.map(function (item, idx) {
6990 var $item = $$1('<div class="note-hint-item"/>');
6991 $item.append(hint.template ? hint.template(item) : item + '');
6992 $item.data({
6993 'index': hintIdx,
6994 'item': item
6995 });
6996 return $item;
6997 });
6998 };
6999 HintPopover.prototype.handleKeydown = function (e) {
7000 if (!this.$popover.is(':visible')) {
7001 return;
7002 }
7003 if (e.keyCode === key.code.ENTER) {
7004 e.preventDefault();
7005 this.replace();
7006 }
7007 else if (e.keyCode === key.code.UP) {
7008 e.preventDefault();
7009 this.moveUp();
7010 }
7011 else if (e.keyCode === key.code.DOWN) {
7012 e.preventDefault();
7013 this.moveDown();
7014 }
7015 };
7016 HintPopover.prototype.searchKeyword = function (index, keyword, callback) {
7017 var hint = this.hints[index];
7018 if (hint && hint.match.test(keyword) && hint.search) {
7019 var matches = hint.match.exec(keyword);
7020 hint.search(matches[1], callback);
7021 }
7022 else {
7023 callback();
7024 }
7025 };
7026 HintPopover.prototype.createGroup = function (idx, keyword) {
7027 var _this = this;
7028 var $group = $$1('<div class="note-hint-group note-hint-group-' + idx + '"/>');
7029 this.searchKeyword(idx, keyword, function (items) {
7030 items = items || [];
7031 if (items.length) {
7032 $group.html(_this.createItemTemplates(idx, items));
7033 _this.show();
7034 }
7035 });
7036 return $group;
7037 };
7038 HintPopover.prototype.handleKeyup = function (e) {
7039 var _this = this;
7040 if (!lists.contains([key.code.ENTER, key.code.UP, key.code.DOWN], e.keyCode)) {
7041 var wordRange = this.context.invoke('editor.createRange').getWordRange();
7042 var keyword_1 = wordRange.toString();
7043 if (this.hints.length && keyword_1) {
7044 this.$content.empty();
7045 var bnd = func.rect2bnd(lists.last(wordRange.getClientRects()));
7046 if (bnd) {
7047 this.$popover.hide();
7048 this.lastWordRange = wordRange;
7049 this.hints.forEach(function (hint, idx) {
7050 if (hint.match.test(keyword_1)) {
7051 _this.createGroup(idx, keyword_1).appendTo(_this.$content);
7052 }
7053 });
7054 // select first .note-hint-item
7055 this.$content.find('.note-hint-item:first').addClass('active');
7056 // set position for popover after group is created
7057 if (this.direction === 'top') {
7058 this.$popover.css({
7059 left: bnd.left,
7060 top: bnd.top - this.$popover.outerHeight() - POPOVER_DIST
7061 });
7062 }
7063 else {
7064 this.$popover.css({
7065 left: bnd.left,
7066 top: bnd.top + bnd.height + POPOVER_DIST
7067 });
7068 }
7069 }
7070 }
7071 else {
7072 this.hide();
7073 }
7074 }
7075 };
7076 HintPopover.prototype.show = function () {
7077 this.$popover.show();
7078 };
7079 HintPopover.prototype.hide = function () {
7080 this.$popover.hide();
7081 };
7082 return HintPopover;
7083 }());
7084
7085 var Context = /** @class */ (function () {
7086 /**
7087 * @param {jQuery} $note
7088 * @param {Object} options
7089 */
7090 function Context($note, options) {
7091 this.ui = $$1.summernote.ui;
7092 this.$note = $note;
7093 this.memos = {};
7094 this.modules = {};
7095 this.layoutInfo = {};
7096 this.options = options;
7097 this.initialize();
7098 }
7099 /**
7100 * create layout and initialize modules and other resources
7101 */
7102 Context.prototype.initialize = function () {
7103 this.layoutInfo = this.ui.createLayout(this.$note, this.options);
7104 this._initialize();
7105 this.$note.hide();
7106 return this;
7107 };
7108 /**
7109 * destroy modules and other resources and remove layout
7110 */
7111 Context.prototype.destroy = function () {
7112 this._destroy();
7113 this.$note.removeData('summernote');
7114 this.ui.removeLayout(this.$note, this.layoutInfo);
7115 };
7116 /**
7117 * destory modules and other resources and initialize it again
7118 */
7119 Context.prototype.reset = function () {
7120 var disabled = this.isDisabled();
7121 this.code(dom.emptyPara);
7122 this._destroy();
7123 this._initialize();
7124 if (disabled) {
7125 this.disable();
7126 }
7127 };
7128 Context.prototype._initialize = function () {
7129 var _this = this;
7130 // add optional buttons
7131 var buttons = $$1.extend({}, this.options.buttons);
7132 Object.keys(buttons).forEach(function (key) {
7133 _this.memo('button.' + key, buttons[key]);
7134 });
7135 var modules = $$1.extend({}, this.options.modules, $$1.summernote.plugins || {});
7136 // add and initialize modules
7137 Object.keys(modules).forEach(function (key) {
7138 _this.module(key, modules[key], true);
7139 });
7140 Object.keys(this.modules).forEach(function (key) {
7141 _this.initializeModule(key);
7142 });
7143 };
7144 Context.prototype._destroy = function () {
7145 var _this = this;
7146 // destroy modules with reversed order
7147 Object.keys(this.modules).reverse().forEach(function (key) {
7148 _this.removeModule(key);
7149 });
7150 Object.keys(this.memos).forEach(function (key) {
7151 _this.removeMemo(key);
7152 });
7153 // trigger custom onDestroy callback
7154 this.triggerEvent('destroy', this);
7155 };
7156 Context.prototype.code = function (html) {
7157 var isActivated = this.invoke('codeview.isActivated');
7158 if (html === undefined) {
7159 this.invoke('codeview.sync');
7160 return isActivated ? this.layoutInfo.codable.val() : this.layoutInfo.editable.html();
7161 }
7162 else {
7163 if (isActivated) {
7164 this.layoutInfo.codable.val(html);
7165 }
7166 else {
7167 this.layoutInfo.editable.html(html);
7168 }
7169 this.$note.val(html);
7170 this.triggerEvent('change', html);
7171 }
7172 };
7173 Context.prototype.isDisabled = function () {
7174 return this.layoutInfo.editable.attr('contenteditable') === 'false';
7175 };
7176 Context.prototype.enable = function () {
7177 this.layoutInfo.editable.attr('contenteditable', true);
7178 this.invoke('toolbar.activate', true);
7179 this.triggerEvent('disable', false);
7180 };
7181 Context.prototype.disable = function () {
7182 // close codeview if codeview is opend
7183 if (this.invoke('codeview.isActivated')) {
7184 this.invoke('codeview.deactivate');
7185 }
7186 this.layoutInfo.editable.attr('contenteditable', false);
7187 this.invoke('toolbar.deactivate', true);
7188 this.triggerEvent('disable', true);
7189 };
7190 Context.prototype.triggerEvent = function () {
7191 var namespace = lists.head(arguments);
7192 var args = lists.tail(lists.from(arguments));
7193 var callback = this.options.callbacks[func.namespaceToCamel(namespace, 'on')];
7194 if (callback) {
7195 callback.apply(this.$note[0], args);
7196 }
7197 this.$note.trigger('summernote.' + namespace, args);
7198 };
7199 Context.prototype.initializeModule = function (key) {
7200 var module = this.modules[key];
7201 module.shouldInitialize = module.shouldInitialize || func.ok;
7202 if (!module.shouldInitialize()) {
7203 return;
7204 }
7205 // initialize module
7206 if (module.initialize) {
7207 module.initialize();
7208 }
7209 // attach events
7210 if (module.events) {
7211 dom.attachEvents(this.$note, module.events);
7212 }
7213 };
7214 Context.prototype.module = function (key, ModuleClass, withoutIntialize) {
7215 if (arguments.length === 1) {
7216 return this.modules[key];
7217 }
7218 this.modules[key] = new ModuleClass(this);
7219 if (!withoutIntialize) {
7220 this.initializeModule(key);
7221 }
7222 };
7223 Context.prototype.removeModule = function (key) {
7224 var module = this.modules[key];
7225 if (module.shouldInitialize()) {
7226 if (module.events) {
7227 dom.detachEvents(this.$note, module.events);
7228 }
7229 if (module.destroy) {
7230 module.destroy();
7231 }
7232 }
7233 delete this.modules[key];
7234 };
7235 Context.prototype.memo = function (key, obj) {
7236 if (arguments.length === 1) {
7237 return this.memos[key];
7238 }
7239 this.memos[key] = obj;
7240 };
7241 Context.prototype.removeMemo = function (key) {
7242 if (this.memos[key] && this.memos[key].destroy) {
7243 this.memos[key].destroy();
7244 }
7245 delete this.memos[key];
7246 };
7247 /**
7248 * Some buttons need to change their visual style immediately once they get pressed
7249 */
7250 Context.prototype.createInvokeHandlerAndUpdateState = function (namespace, value) {
7251 var _this = this;
7252 return function (event) {
7253 _this.createInvokeHandler(namespace, value)(event);
7254 _this.invoke('buttons.updateCurrentStyle');
7255 };
7256 };
7257 Context.prototype.createInvokeHandler = function (namespace, value) {
7258 var _this = this;
7259 return function (event) {
7260 event.preventDefault();
7261 var $target = $$1(event.target);
7262 _this.invoke(namespace, value || $target.closest('[data-value]').data('value'), $target);
7263 };
7264 };
7265 Context.prototype.invoke = function () {
7266 var namespace = lists.head(arguments);
7267 var args = lists.tail(lists.from(arguments));
7268 var splits = namespace.split('.');
7269 var hasSeparator = splits.length > 1;
7270 var moduleName = hasSeparator && lists.head(splits);
7271 var methodName = hasSeparator ? lists.last(splits) : lists.head(splits);
7272 var module = this.modules[moduleName || 'editor'];
7273 if (!moduleName && this[methodName]) {
7274 return this[methodName].apply(this, args);
7275 }
7276 else if (module && module[methodName] && module.shouldInitialize()) {
7277 return module[methodName].apply(module, args);
7278 }
7279 };
7280 return Context;
7281 }());
7282
7283 $$1.fn.extend({
7284 /**
7285 * Summernote API
7286 *
7287 * @param {Object|String}
7288 * @return {this}
7289 */
7290 summernote: function () {
7291 var type = $$1.type(lists.head(arguments));
7292 var isExternalAPICalled = type === 'string';
7293 var hasInitOptions = type === 'object';
7294 var options = $$1.extend({}, $$1.summernote.options, hasInitOptions ? lists.head(arguments) : {});
7295 // Update options
7296 options.langInfo = $$1.extend(true, {}, $$1.summernote.lang['en-US'], $$1.summernote.lang[options.lang]);
7297 options.icons = $$1.extend(true, {}, $$1.summernote.options.icons, options.icons);
7298 options.tooltip = options.tooltip === 'auto' ? !env.isSupportTouch : options.tooltip;
7299 this.each(function (idx, note) {
7300 var $note = $$1(note);
7301 if (!$note.data('summernote')) {
7302 var context = new Context($note, options);
7303 $note.data('summernote', context);
7304 $note.data('summernote').triggerEvent('init', context.layoutInfo);
7305 }
7306 });
7307 var $note = this.first();
7308 if ($note.length) {
7309 var context = $note.data('summernote');
7310 if (isExternalAPICalled) {
7311 return context.invoke.apply(context, lists.from(arguments));
7312 }
7313 else if (options.focus) {
7314 context.invoke('editor.focus');
7315 }
7316 }
7317 return this;
7318 }
7319 });
7320
7321 $$1.summernote = $$1.extend($$1.summernote, {
7322 version: '0.8.11',
7323 ui: ui,
7324 dom: dom,
7325 range: range,
7326 plugins: {},
7327 options: {
7328 modules: {
7329 'editor': Editor,
7330 'clipboard': Clipboard,
7331 'dropzone': Dropzone,
7332 'codeview': CodeView,
7333 'statusbar': Statusbar,
7334 'fullscreen': Fullscreen,
7335 'handle': Handle,
7336 // FIXME: HintPopover must be front of autolink
7337 // - Script error about range when Enter key is pressed on hint popover
7338 'hintPopover': HintPopover,
7339 'autoLink': AutoLink,
7340 'autoSync': AutoSync,
7341 'placeholder': Placeholder,
7342 'buttons': Buttons,
7343 'toolbar': Toolbar,
7344 'linkDialog': LinkDialog,
7345 'linkPopover': LinkPopover,
7346 'imageDialog': ImageDialog,
7347 'imagePopover': ImagePopover,
7348 'tablePopover': TablePopover,
7349 'videoDialog': VideoDialog,
7350 'helpDialog': HelpDialog,
7351 'airPopover': AirPopover
7352 },
7353 buttons: {},
7354 lang: 'en-US',
7355 followingToolbar: true,
7356 otherStaticBar: '',
7357 // toolbar
7358 toolbar: [
7359 ['style', ['style']],
7360 ['font', ['bold', 'underline', 'clear']],
7361 ['fontname', ['fontname']],
7362 ['color', ['color']],
7363 ['para', ['ul', 'ol', 'paragraph']],
7364 ['table', ['table']],
7365 ['insert', ['link', 'picture', 'video']],
7366 ['view', ['fullscreen', 'codeview', 'help']]
7367 ],
7368 // popover
7369 popatmouse: true,
7370 popover: {
7371 image: [
7372 ['imagesize', ['imageSize100', 'imageSize50', 'imageSize25']],
7373 ['float', ['floatLeft', 'floatRight', 'floatNone']],
7374 ['remove', ['removeMedia']]
7375 ],
7376 link: [
7377 ['link', ['linkDialogShow', 'unlink']]
7378 ],
7379 table: [
7380 ['add', ['addRowDown', 'addRowUp', 'addColLeft', 'addColRight']],
7381 ['delete', ['deleteRow', 'deleteCol', 'deleteTable']]
7382 ],
7383 air: [
7384 ['color', ['color']],
7385 ['font', ['bold', 'underline', 'clear']],
7386 ['para', ['ul', 'paragraph']],
7387 ['table', ['table']],
7388 ['insert', ['link', 'picture']]
7389 ]
7390 },
7391 // air mode: inline editor
7392 airMode: false,
7393 width: null,
7394 height: null,
7395 linkTargetBlank: true,
7396 focus: false,
7397 tabSize: 4,
7398 styleWithSpan: true,
7399 shortcuts: true,
7400 textareaAutoSync: true,
7401 hintDirection: 'bottom',
7402 tooltip: 'auto',
7403 container: 'body',
7404 maxTextLength: 0,
7405 styleTags: [
7406 'p',
7407 { title: 'Blockquote', tag: 'blockquote', className: 'blockquote', value: 'blockquote' },
7408 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
7409 ],
7410 fontNames: [
7411 'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New',
7412 'Helvetica Neue', 'Helvetica', 'Impact', 'Lucida Grande',
7413 'Tahoma', 'Times New Roman', 'Verdana'
7414 ],
7415 fontSizes: ['8', '9', '10', '11', '12', '14', '18', '24', '36'],
7416 // pallete colors(n x n)
7417 colors: [
7418 ['#000000', '#424242', '#636363', '#9C9C94', '#CEC6CE', '#EFEFEF', '#F7F7F7', '#FFFFFF'],
7419 ['#FF0000', '#FF9C00', '#FFFF00', '#00FF00', '#00FFFF', '#0000FF', '#9C00FF', '#FF00FF'],
7420 ['#F7C6CE', '#FFE7CE', '#FFEFC6', '#D6EFD6', '#CEDEE7', '#CEE7F7', '#D6D6E7', '#E7D6DE'],
7421 ['#E79C9C', '#FFC69C', '#FFE79C', '#B5D6A5', '#A5C6CE', '#9CC6EF', '#B5A5D6', '#D6A5BD'],
7422 ['#E76363', '#F7AD6B', '#FFD663', '#94BD7B', '#73A5AD', '#6BADDE', '#8C7BC6', '#C67BA5'],
7423 ['#CE0000', '#E79439', '#EFC631', '#6BA54A', '#4A7B8C', '#3984C6', '#634AA5', '#A54A7B'],
7424 ['#9C0000', '#B56308', '#BD9400', '#397B21', '#104A5A', '#085294', '#311873', '#731842'],
7425 ['#630000', '#7B3900', '#846300', '#295218', '#083139', '#003163', '#21104A', '#4A1031']
7426 ],
7427 // http://chir.ag/projects/name-that-color/
7428 colorsName: [
7429 ['Black', 'Tundora', 'Dove Gray', 'Star Dust', 'Pale Slate', 'Gallery', 'Alabaster', 'White'],
7430 ['Red', 'Orange Peel', 'Yellow', 'Green', 'Cyan', 'Blue', 'Electric Violet', 'Magenta'],
7431 ['Azalea', 'Karry', 'Egg White', 'Zanah', 'Botticelli', 'Tropical Blue', 'Mischka', 'Twilight'],
7432 ['Tonys Pink', 'Peach Orange', 'Cream Brulee', 'Sprout', 'Casper', 'Perano', 'Cold Purple', 'Careys Pink'],
7433 ['Mandy', 'Rajah', 'Dandelion', 'Olivine', 'Gulf Stream', 'Viking', 'Blue Marguerite', 'Puce'],
7434 ['Guardsman Red', 'Fire Bush', 'Golden Dream', 'Chelsea Cucumber', 'Smalt Blue', 'Boston Blue', 'Butterfly Bush', 'Cadillac'],
7435 ['Sangria', 'Mai Tai', 'Buddha Gold', 'Forest Green', 'Eden', 'Venice Blue', 'Meteorite', 'Claret'],
7436 ['Rosewood', 'Cinnamon', 'Olive', 'Parsley', 'Tiber', 'Midnight Blue', 'Valentino', 'Loulou']
7437 ],
7438 lineHeights: ['1.0', '1.2', '1.4', '1.5', '1.6', '1.8', '2.0', '3.0'],
7439 tableClassName: 'table table-bordered',
7440 insertTableMaxSize: {
7441 col: 10,
7442 row: 10
7443 },
7444 dialogsInBody: false,
7445 dialogsFade: false,
7446 maximumImageFileSize: null,
7447 callbacks: {
7448 onInit: null,
7449 onFocus: null,
7450 onBlur: null,
7451 onBlurCodeview: null,
7452 onEnter: null,
7453 onKeyup: null,
7454 onKeydown: null,
7455 onImageUpload: null,
7456 onImageUploadError: null,
7457 onImageLinkInsert: null
7458 },
7459 codemirror: {
7460 mode: 'text/html',
7461 htmlMode: true,
7462 lineNumbers: true
7463 },
7464 keyMap: {
7465 pc: {
7466 'ENTER': 'insertParagraph',
7467 'CTRL+Z': 'undo',
7468 'CTRL+Y': 'redo',
7469 'TAB': 'tab',
7470 'SHIFT+TAB': 'untab',
7471 'CTRL+B': 'bold',
7472 'CTRL+I': 'italic',
7473 'CTRL+U': 'underline',
7474 'CTRL+SHIFT+S': 'strikethrough',
7475 'CTRL+BACKSLASH': 'removeFormat',
7476 'CTRL+SHIFT+L': 'justifyLeft',
7477 'CTRL+SHIFT+E': 'justifyCenter',
7478 'CTRL+SHIFT+R': 'justifyRight',
7479 'CTRL+SHIFT+J': 'justifyFull',
7480 'CTRL+SHIFT+NUM7': 'insertUnorderedList',
7481 'CTRL+SHIFT+NUM8': 'insertOrderedList',
7482 'CTRL+LEFTBRACKET': 'outdent',
7483 'CTRL+RIGHTBRACKET': 'indent',
7484 'CTRL+NUM0': 'formatPara',
7485 'CTRL+NUM1': 'formatH1',
7486 'CTRL+NUM2': 'formatH2',
7487 'CTRL+NUM3': 'formatH3',
7488 'CTRL+NUM4': 'formatH4',
7489 'CTRL+NUM5': 'formatH5',
7490 'CTRL+NUM6': 'formatH6',
7491 'CTRL+ENTER': 'insertHorizontalRule',
7492 'CTRL+K': 'linkDialog.show'
7493 },
7494 mac: {
7495 'ENTER': 'insertParagraph',
7496 'CMD+Z': 'undo',
7497 'CMD+SHIFT+Z': 'redo',
7498 'TAB': 'tab',
7499 'SHIFT+TAB': 'untab',
7500 'CMD+B': 'bold',
7501 'CMD+I': 'italic',
7502 'CMD+U': 'underline',
7503 'CMD+SHIFT+S': 'strikethrough',
7504 'CMD+BACKSLASH': 'removeFormat',
7505 'CMD+SHIFT+L': 'justifyLeft',
7506 'CMD+SHIFT+E': 'justifyCenter',
7507 'CMD+SHIFT+R': 'justifyRight',
7508 'CMD+SHIFT+J': 'justifyFull',
7509 'CMD+SHIFT+NUM7': 'insertUnorderedList',
7510 'CMD+SHIFT+NUM8': 'insertOrderedList',
7511 'CMD+LEFTBRACKET': 'outdent',
7512 'CMD+RIGHTBRACKET': 'indent',
7513 'CMD+NUM0': 'formatPara',
7514 'CMD+NUM1': 'formatH1',
7515 'CMD+NUM2': 'formatH2',
7516 'CMD+NUM3': 'formatH3',
7517 'CMD+NUM4': 'formatH4',
7518 'CMD+NUM5': 'formatH5',
7519 'CMD+NUM6': 'formatH6',
7520 'CMD+ENTER': 'insertHorizontalRule',
7521 'CMD+K': 'linkDialog.show'
7522 }
7523 },
7524 icons: {
7525 'align': 'note-icon-align',
7526 'alignCenter': 'note-icon-align-center',
7527 'alignJustify': 'note-icon-align-justify',
7528 'alignLeft': 'note-icon-align-left',
7529 'alignRight': 'note-icon-align-right',
7530 'rowBelow': 'note-icon-row-below',
7531 'colBefore': 'note-icon-col-before',
7532 'colAfter': 'note-icon-col-after',
7533 'rowAbove': 'note-icon-row-above',
7534 'rowRemove': 'note-icon-row-remove',
7535 'colRemove': 'note-icon-col-remove',
7536 'indent': 'note-icon-align-indent',
7537 'outdent': 'note-icon-align-outdent',
7538 'arrowsAlt': 'note-icon-arrows-alt',
7539 'bold': 'note-icon-bold',
7540 'caret': 'note-icon-caret',
7541 'circle': 'note-icon-circle',
7542 'close': 'note-icon-close',
7543 'code': 'note-icon-code',
7544 'eraser': 'note-icon-eraser',
7545 'font': 'note-icon-font',
7546 'frame': 'note-icon-frame',
7547 'italic': 'note-icon-italic',
7548 'link': 'note-icon-link',
7549 'unlink': 'note-icon-chain-broken',
7550 'magic': 'note-icon-magic',
7551 'menuCheck': 'note-icon-menu-check',
7552 'minus': 'note-icon-minus',
7553 'orderedlist': 'note-icon-orderedlist',
7554 'pencil': 'note-icon-pencil',
7555 'picture': 'note-icon-picture',
7556 'question': 'note-icon-question',
7557 'redo': 'note-icon-redo',
7558 'square': 'note-icon-square',
7559 'strikethrough': 'note-icon-strikethrough',
7560 'subscript': 'note-icon-subscript',
7561 'superscript': 'note-icon-superscript',
7562 'table': 'note-icon-table',
7563 'textHeight': 'note-icon-text-height',
7564 'trash': 'note-icon-trash',
7565 'underline': 'note-icon-underline',
7566 'undo': 'note-icon-undo',
7567 'unorderedlist': 'note-icon-unorderedlist',
7568 'video': 'note-icon-video'
7569 }
7570 }
7571 });
7572
7573})));
7574//# sourceMappingURL=summernote-bs4.js.map