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