vendor/assets/javascripts/simditor/simditor.js in simditor-2.1.15 vs vendor/assets/javascripts/simditor/simditor.js in simditor-2.2.0

- old
+ new

@@ -1,9 +1,9 @@ /*! * Simditor v2.1.15 * http://simditor.tower.im/ -* 2015-07-01 +* 2015-07-28 */ (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module unless amdModuleId is set define('simditor', ["jquery","simple-module","simple-hotkeys","simple-uploader"], function ($, SimpleModule, simpleHotkeys, simpleUploader) { @@ -32,82 +32,230 @@ return Selection.__super__.constructor.apply(this, arguments); } Selection.pluginName = 'Selection'; + Selection.prototype._range = null; + + Selection.prototype._startNodes = null; + + Selection.prototype._endNodes = null; + + Selection.prototype._containerNode = null; + + Selection.prototype._nodes = null; + + Selection.prototype._blockNodes = null; + + Selection.prototype._rootNodes = null; + Selection.prototype._init = function() { this.editor = this._module; - return this.sel = document.getSelection(); + this._selection = document.getSelection(); + this.editor.on('selectionchanged', (function(_this) { + return function(e) { + _this._reset(); + return _this._range = _this._selection.getRangeAt(0); + }; + })(this)); + return this.editor.on('blur', (function(_this) { + return function(e) { + return _this.clear(); + }; + })(this)); }; + Selection.prototype._reset = function() { + this._range = null; + this._startNodes = null; + this._endNodes = null; + this._containerNode = null; + this._nodes = null; + this._blockNodes = null; + return this._rootNodes = null; + }; + Selection.prototype.clear = function() { var e; try { - return this.sel.removeAllRanges(); + this._selection.removeAllRanges(); } catch (_error) { e = _error; } + return this._reset(); }; - Selection.prototype.getRange = function() { - if (!this.editor.inputManager.focused || !this.sel.rangeCount) { - return null; + Selection.prototype.range = function(range) { + var ffOrIE; + if (range) { + this.clear(); + this._selection.addRange(range); + this._range = range; + ffOrIE = this.editor.util.browser.firefox || this.editor.util.browser.msie; + if (!this.editor.inputManager.focused && ffOrIE) { + this.editor.body.focus(); + } + } else if (!this._range && this.editor.inputManager.focused && this._selection.rangeCount) { + this._range = this._selection.getRangeAt(0); } - return this.sel.getRangeAt(0); + return this._range; }; - Selection.prototype.selectRange = function(range) { - this.clear(); - this.sel.addRange(range); - if (!this.editor.inputManager.focused && (this.editor.util.browser.firefox || this.editor.util.browser.msie)) { - this.editor.body.focus(); + Selection.prototype.startNodes = function() { + if (this._range) { + this._startNodes || (this._startNodes = (function(_this) { + return function() { + var startNodes; + startNodes = $(_this._range.startContainer).parentsUntil(_this.editor.body).get(); + startNodes.unshift(_this._range.startContainer); + return $(startNodes); + }; + })(this)()); } - return range; + return this._startNodes; }; + Selection.prototype.endNodes = function() { + var endNodes; + if (this._range) { + this._endNodes || (this._endNodes = this._range.collapsed ? this.startNodes() : (endNodes = $(this._range.endContainer).parentsUntil(this.editor.body).get(), endNodes.unshift(this._range.endContainer), $(endNodes))); + } + return this._endNodes; + }; + + Selection.prototype.containerNode = function() { + if (this._range) { + this._containerNode || (this._containerNode = $(this._range.commonAncestorContainer)); + } + return this._containerNode; + }; + + Selection.prototype.nodes = function() { + if (this._range) { + this._nodes || (this._nodes = (function(_this) { + return function() { + var nodes; + nodes = []; + if (_this.startNodes().first().is(_this.endNodes().first())) { + nodes = _this.startNodes().get(); + } else { + _this.startNodes().each(function(i, node) { + var $endNode, $node, $nodes, endIndex, index, sharedIndex, startIndex; + $node = $(node); + if (_this.endNodes().index($node) > -1) { + return nodes.push(node); + } else if ($node.parent().is(_this.editor.body) || (sharedIndex = _this.endNodes().index($node.parent())) > -1) { + if (sharedIndex && sharedIndex > -1) { + $endNode = _this.endNodes().eq(sharedIndex - 1); + } else { + $endNode = _this.endNodes().last(); + } + $nodes = $node.parent().contents(); + startIndex = $nodes.index($node); + endIndex = $nodes.index($endNode); + return $.merge(nodes, $nodes.slice(startIndex, endIndex).get()); + } else { + $nodes = $node.parent().contents(); + index = $nodes.index($node); + return $.merge(nodes, $nodes.slice(index).get()); + } + }); + _this.endNodes().each(function(i, node) { + var $node, $nodes, index; + $node = $(node); + if ($node.parent().is(_this.editor.body) || _this.startNodes().index($node.parent()) > -1) { + nodes.push(node); + return false; + } else { + $nodes = $node.parent().contents(); + index = $nodes.index($node); + return $.merge(nodes, $nodes.slice(0, index + 1)); + } + }); + } + return $($.unique(nodes)); + }; + })(this)()); + } + return this._nodes; + }; + + Selection.prototype.blockNodes = function() { + if (!this._range) { + return; + } + this._blockNodes || (this._blockNodes = (function(_this) { + return function() { + return _this.nodes().filter(function(i, node) { + return _this.editor.util.isBlockNode(node); + }); + }; + })(this)()); + return this._blockNodes; + }; + + Selection.prototype.rootNodes = function() { + if (!this._range) { + return; + } + this._rootNodes || (this._rootNodes = (function(_this) { + return function() { + return _this.nodes().filter(function(i, node) { + var $parent; + $parent = $(node).parent(); + return $parent.is(_this.editor.body) || $parent.is('blockquote'); + }); + }; + })(this)()); + return this._rootNodes; + }; + Selection.prototype.rangeAtEndOf = function(node, range) { - var endNode, endNodeLength, result; + var afterLastNode, beforeLastNode, endNode, endNodeLength, lastNodeIsBr, result; if (range == null) { - range = this.getRange(); + range = this.range(); } - if (!((range != null) && range.collapsed)) { + if (!(range && range.collapsed)) { return; } node = $(node)[0]; endNode = range.endContainer; endNodeLength = this.editor.util.getNodeLength(endNode); - if (!(range.endOffset === endNodeLength - 1 && $(endNode).contents().last().is('br')) && range.endOffset !== endNodeLength) { + beforeLastNode = range.endOffset === endNodeLength - 1; + lastNodeIsBr = $(endNode).contents().last().is('br'); + afterLastNode = range.endOffset === endNodeLength; + if (!((beforeLastNode && lastNodeIsBr) || afterLastNode)) { return false; } if (node === endNode) { return true; } else if (!$.contains(node, endNode)) { return false; } result = true; - $(endNode).parentsUntil(node).addBack().each((function(_this) { - return function(i, n) { - var $lastChild, nodes; - nodes = $(n).parent().contents().filter(function() { - return !(this !== n && this.nodeType === 3 && !this.nodeValue); - }); - $lastChild = nodes.last(); - if (!($lastChild.get(0) === n || ($lastChild.is('br') && $lastChild.prev().get(0) === n))) { - result = false; - return false; - } - }; - })(this)); + $(endNode).parentsUntil(node).addBack().each(function(i, n) { + var $lastChild, beforeLastbr, isLastNode, nodes; + nodes = $(n).parent().contents().filter(function() { + return !(this !== n && this.nodeType === 3 && !this.nodeValue); + }); + $lastChild = nodes.last(); + isLastNode = $lastChild.get(0) === n; + beforeLastbr = $lastChild.is('br') && $lastChild.prev().get(0) === n; + if (!(isLastNode || beforeLastbr)) { + result = false; + return false; + } + }); return result; }; Selection.prototype.rangeAtStartOf = function(node, range) { var result, startNode; if (range == null) { - range = this.getRange(); + range = this.range(); } - if (!((range != null) && range.collapsed)) { + if (!(range && range.collapsed)) { return; } node = $(node)[0]; startNode = range.startContainer; if (range.startOffset !== 0) { @@ -117,88 +265,87 @@ return true; } else if (!$.contains(node, startNode)) { return false; } result = true; - $(startNode).parentsUntil(node).addBack().each((function(_this) { - return function(i, n) { - var nodes; - nodes = $(n).parent().contents().filter(function() { - return !(this !== n && this.nodeType === 3 && !this.nodeValue); - }); - if (nodes.first().get(0) !== n) { - return result = false; - } - }; - })(this)); + $(startNode).parentsUntil(node).addBack().each(function(i, n) { + var nodes; + nodes = $(n).parent().contents().filter(function() { + return !(this !== n && this.nodeType === 3 && !this.nodeValue); + }); + if (nodes.first().get(0) !== n) { + return result = false; + } + }); return result; }; Selection.prototype.insertNode = function(node, range) { if (range == null) { - range = this.getRange(); + range = this.range(); } - if (range == null) { + if (!range) { return; } node = $(node)[0]; range.insertNode(node); return this.setRangeAfter(node, range); }; Selection.prototype.setRangeAfter = function(node, range) { if (range == null) { - range = this.getRange(); + range = this.range(); } if (range == null) { return; } node = $(node)[0]; range.setEndAfter(node); range.collapse(false); - return this.selectRange(range); + return this.range(range); }; Selection.prototype.setRangeBefore = function(node, range) { if (range == null) { - range = this.getRange(); + range = this.range(); } if (range == null) { return; } node = $(node)[0]; range.setEndBefore(node); range.collapse(false); - return this.selectRange(range); + return this.range(range); }; Selection.prototype.setRangeAtStartOf = function(node, range) { if (range == null) { - range = this.getRange(); + range = this.range(); } node = $(node).get(0); range.setEnd(node, 0); range.collapse(false); - return this.selectRange(range); + return this.range(range); }; Selection.prototype.setRangeAtEndOf = function(node, range) { - var $lastNode, $node, contents, lastChild, lastText, nodeLength; + var $lastNode, $node, contents, lastChild, lastChildLength, lastText, nodeLength; if (range == null) { - range = this.getRange(); + range = this.range(); } $node = $(node); - node = $node.get(0); + node = $node[0]; if ($node.is('pre')) { contents = $node.contents(); if (contents.length > 0) { lastChild = contents.last(); lastText = lastChild.text(); + lastChildLength = this.editor.util.getNodeLength(lastChild[0]); if (lastText.charAt(lastText.length - 1) === '\n') { - range.setEnd(lastChild[0], this.editor.util.getNodeLength(lastChild[0]) - 1); + range.setEnd(lastChild[0], lastChildLength - 1); } else { - range.setEnd(lastChild[0], this.editor.util.getNodeLength(lastChild[0])); + range.setEnd(lastChild[0], lastChildLength); } } else { range.setEnd(node, 0); } } else { @@ -214,37 +361,39 @@ } } range.setEnd(node, nodeLength); } range.collapse(false); - return this.selectRange(range); + return this.range(range); }; Selection.prototype.deleteRangeContents = function(range) { - var endRange, startRange; + var atEndOfBody, atStartOfBody, endRange, startRange; if (range == null) { - range = this.getRange(); + range = this.range(); } startRange = range.cloneRange(); endRange = range.cloneRange(); startRange.collapse(true); endRange.collapse(false); - if (!range.collapsed && this.rangeAtStartOf(this.editor.body, startRange) && this.rangeAtEndOf(this.editor.body, endRange)) { + atStartOfBody = this.rangeAtStartOf(this.editor.body, startRange); + atEndOfBody = this.rangeAtEndOf(this.editor.body, endRange); + if (!range.collapsed && atStartOfBody && atEndOfBody) { this.editor.body.empty(); range.setStart(this.editor.body[0], 0); range.collapse(true); - this.selectRange(range); + this.range(range); } else { range.deleteContents(); } return range; }; Selection.prototype.breakBlockEl = function(el, range) { var $el; if (range == null) { - range = this.getRange(); + range = this.range(); } $el = $(el); if (!range.collapsed) { return $el; } @@ -256,11 +405,11 @@ }; Selection.prototype.save = function(range) { var endCaret, endRange, startCaret; if (range == null) { - range = this.getRange(); + range = this.range(); } if (this._selectionSaved) { return; } endRange = range.cloneRange(); @@ -291,11 +440,11 @@ range = document.createRange(); range.setStart(startContainer.get(0), startOffset); range.setEnd(endContainer.get(0), endOffset); startCaret.remove(); endCaret.remove(); - this.selectRange(range); + this.range(range); } else { startCaret.remove(); endCaret.remove(); } this._selectionSaved = false; @@ -314,52 +463,55 @@ } Formatter.pluginName = 'Formatter'; Formatter.prototype.opts = { - allowedTags: null, - allowedAttributes: null + allowedTags: [], + allowedAttributes: {}, + allowedStyles: {} }; Formatter.prototype._init = function() { this.editor = this._module; - this._allowedTags = this.opts.allowedTags || ['br', 'a', 'img', 'b', 'strong', 'i', 'u', 'font', 'p', 'ul', 'ol', 'li', 'blockquote', 'pre', 'code', 'h1', 'h2', 'h3', 'h4', 'hr']; - this._allowedAttributes = this.opts.allowedAttributes || { + this._allowedTags = $.merge(['br', 'span', 'a', 'img', 'b', 'strong', 'i', 'u', 'font', 'p', 'ul', 'ol', 'li', 'blockquote', 'pre', 'code', 'h1', 'h2', 'h3', 'h4', 'hr'], this.opts.allowedTags); + this._allowedAttributes = $.extend({ img: ['src', 'alt', 'width', 'height', 'data-non-image'], a: ['href', 'target'], font: ['color'], code: ['class'] - }; - this._allowedStyles = this.opts.allowedStyles || { + }, this.opts.allowedAttributes); + this._allowedStyles = $.extend({ + span: ['color'], p: ['margin-left', 'text-align'], - h1: ['margin-left'], - h2: ['margin-left'], - h3: ['margin-left'], - h4: ['margin-left'] - }; + h1: ['margin-left', 'text-align'], + h2: ['margin-left', 'text-align'], + h3: ['margin-left', 'text-align'], + h4: ['margin-left', 'text-align'] + }, this.opts.allowedStyles); return this.editor.body.on('click', 'a', function(e) { return false; }); }; Formatter.prototype.decorate = function($el) { if ($el == null) { $el = this.editor.body; } - return this.editor.trigger('decorate', [$el]); + this.editor.trigger('decorate', [$el]); + return $el; }; Formatter.prototype.undecorate = function($el) { if ($el == null) { $el = this.editor.body.clone(); } this.editor.trigger('undecorate', [$el]); - return $.trim($el.html()); + return $el; }; Formatter.prototype.autolink = function($el) { - var $link, $node, findLinkNode, j, lastIndex, len, linkNodes, match, re, replaceEls, subStr, text, uri; + var $link, $node, findLinkNode, k, lastIndex, len, linkNodes, match, re, replaceEls, subStr, text, uri; if ($el == null) { $el = this.editor.body; } linkNodes = []; findLinkNode = function($parentNode) { @@ -367,21 +519,21 @@ var $node, text; $node = $(node); if ($node.is('a') || $node.closest('a, pre', $el).length) { return; } - if ($node.contents().length) { + if (!$node.is('iframe') && $node.contents().length) { return findLinkNode($node); } else if ((text = $node.text()) && /https?:\/\/|www\./ig.test(text)) { return linkNodes.push($node); } }); }; findLinkNode($el); re = /(https?:\/\/|www\.)[\w\-\.\?&=\/#%:,@\!\+]+/ig; - for (j = 0, len = linkNodes.length; j < len; j++) { - $node = linkNodes[j]; + for (k = 0, len = linkNodes.length; k < len; k++) { + $node = linkNodes[k]; text = $node.text(); replaceEls = []; match = null; lastIndex = 0; while ((match = re.exec(text)) !== null) { @@ -397,26 +549,26 @@ } return $el; }; Formatter.prototype.format = function($el) { - var $node, blockNode, j, k, len, len1, n, node, ref, ref1; + var $node, blockNode, k, l, len, len1, n, node, ref, ref1; if ($el == null) { $el = this.editor.body; } if ($el.is(':empty')) { $el.append('<p>' + this.editor.util.phBr + '</p>'); return $el; } ref = $el.contents(); - for (j = 0, len = ref.length; j < len; j++) { - n = ref[j]; + for (k = 0, len = ref.length; k < len; k++) { + n = ref[k]; this.cleanNode(n, true); } ref1 = $el.contents(); - for (k = 0, len1 = ref1.length; k < len1; k++) { - node = ref1[k]; + for (l = 0, len1 = ref1.length; l < len1; l++) { + node = ref1[l]; $node = $(node); if ($node.is('br')) { if (typeof blockNode !== "undefined" && blockNode !== null) { blockNode = null; } @@ -441,11 +593,11 @@ } return $el; }; Formatter.prototype.cleanNode = function(node, recursive) { - var $childImg, $node, $p, $td, allowedAttributes, attr, contents, isDecoration, j, k, len, len1, n, ref, ref1, text, textNode; + var $childImg, $node, $p, $td, allowedAttributes, attr, contents, isDecoration, k, l, len, len1, n, ref, ref1, text, textNode; $node = $(node); if (!($node.length > 0)) { return; } if ($node[0].nodeType === 3) { @@ -456,12 +608,12 @@ } else { $node.remove(); } return; } - contents = $node.contents(); - isDecoration = $node.is('[class^="simditor-"]'); + contents = $node.is('iframe') ? null : $node.contents(); + isDecoration = this.editor.util.isDecoratedNode($node); if ($node.is(this._allowedTags.join(',')) || isDecoration) { if ($node.is('a') && ($childImg = $node.find('img')).length > 0) { $node.replaceWith($childImg); $node = $childImg; contents = null; @@ -470,20 +622,23 @@ $node.remove(); } if (!isDecoration) { allowedAttributes = this._allowedAttributes[$node[0].tagName.toLowerCase()]; ref = $.makeArray($node[0].attributes); - for (j = 0, len = ref.length; j < len; j++) { - attr = ref[j]; + for (k = 0, len = ref.length; k < len; k++) { + attr = ref[k]; if (attr.name === 'style') { continue; } if (!((allowedAttributes != null) && (ref1 = attr.name, indexOf.call(allowedAttributes, ref1) >= 0))) { $node.removeAttr(attr.name); } - this._cleanNodeStyles($node); } + this._cleanNodeStyles($node); + if ($node.is('span') && $node[0].attributes.length === 0) { + $node.contents().first().unwrap(); + } } } else if ($node[0].nodeType === 1 && !$node.is(':empty')) { if ($node.is('div, article, dl, header, footer, tr')) { $node.append('<br/>'); contents.first().unwrap(); @@ -506,20 +661,20 @@ } else { $node.remove(); contents = null; } if (recursive && (contents != null) && !$node.is('pre')) { - for (k = 0, len1 = contents.length; k < len1; k++) { - n = contents[k]; + for (l = 0, len1 = contents.length; l < len1; l++) { + n = contents[l]; this.cleanNode(n, true); } } return null; }; Formatter.prototype._cleanNodeStyles = function($node) { - var allowedStyles, j, len, pair, ref, ref1, style, styleStr, styles; + var allowedStyles, k, len, pair, ref, ref1, style, styleStr, styles; styleStr = $node.attr('style'); if (!styleStr) { return; } $node.removeAttr('style'); @@ -527,12 +682,12 @@ if (!(allowedStyles && allowedStyles.length > 0)) { return $node; } styles = {}; ref = styleStr.split(';'); - for (j = 0, len = ref.length; j < len; j++) { - style = ref[j]; + for (k = 0, len = ref.length; k < len; k++) { + style = ref[k]; style = $.trim(style); pair = style.split(':'); if (!(pair.length = 2)) { continue; } @@ -559,12 +714,12 @@ var $node, children; if (node.nodeType === 3) { return result += node.nodeValue; } else if (node.nodeType === 1) { $node = $(node); - children = $node.contents(); - if (children.length > 0) { + children = $node.is('iframe') ? null : $node.contents(); + if (children && children.length > 0) { result += _this.clearHtml(children); } if (lineBreak && i < contents.length - 1 && $node.is('br, p, div, li,tr, pre, address, artticle, aside, dl, figcaption, footer, h1, h2,h3, h4, header')) { return result += '\n'; } @@ -612,20 +767,22 @@ InputManager.prototype._arrowKeys = [37, 38, 39, 40]; InputManager.prototype._init = function() { var submitKey; this.editor = this._module; - this.throttledTrigger = this.editor.util.throttle((function(_this) { - return function() { - var args; - args = 1 <= arguments.length ? slice.call(arguments, 0) : []; + this.throttledValueChanged = this.editor.util.throttle((function(_this) { + return function(params) { return setTimeout(function() { - var ref; - return (ref = _this.editor).trigger.apply(ref, args); + return _this.editor.trigger('valuechanged', params); }, 10); }; })(this), 300); + this.throttledSelectionChanged = this.editor.util.throttle((function(_this) { + return function() { + return _this.editor.trigger('selectionchanged'); + }; + })(this), 50); if (this.opts.pasteImage && typeof this.opts.pasteImage !== 'string') { this.opts.pasteImage = 'inline'; } this._keystrokeHandlers = {}; this.hotkeys = simpleHotkeys({ @@ -645,22 +802,17 @@ $(document).on('selectionchange.simditor' + this.editor.id, (function(_this) { return function(e) { if (!_this.focused) { return; } - if (_this._selectionTimer) { - clearTimeout(_this._selectionTimer); - _this._selectionTimer = null; - } - return _this._selectionTimer = setTimeout(function() { - return _this.editor.trigger('selectionchanged'); - }, 20); + return _this.throttledSelectionChanged(); }; })(this)); this.editor.on('valuechanged', (function(_this) { return function() { - if (!_this.editor.util.closestBlockEl() && _this.focused) { + _this.lastCaretPosition = null; + if (_this.focused && !_this.editor.selection.blockNodes().length) { _this.editor.selection.save(); _this.editor.formatter.format(); _this.editor.selection.restore(); } _this.editor.body.find('hr, pre, .simditor-table').each(function(i, el) { @@ -675,40 +827,33 @@ if ($el.prev().length === 0) { $('<p/>').append(_this.editor.util.phBr).insertBefore($el); formatted = true; } if (formatted) { - return setTimeout(function() { - return _this.editor.trigger('valuechanged'); - }, 10); + return _this.throttledValueChanged(); } } }); _this.editor.body.find('pre:empty').append(_this.editor.util.phBr); if (!_this.editor.util.support.onselectionchange && _this.focused) { - return _this.editor.trigger('selectionchanged'); + return _this.throttledValueChanged(); } }; })(this)); - this.editor.on('selectionchanged', (function(_this) { - return function(e) { - return _this.editor.undoManager.update(); - }; - })(this)); this.editor.body.on('keydown', $.proxy(this._onKeyDown, this)).on('keypress', $.proxy(this._onKeyPress, this)).on('keyup', $.proxy(this._onKeyUp, this)).on('mouseup', $.proxy(this._onMouseUp, this)).on('focus', $.proxy(this._onFocus, this)).on('blur', $.proxy(this._onBlur, this)).on('paste', $.proxy(this._onPaste, this)).on('drop', $.proxy(this._onDrop, this)).on('input', $.proxy(this._onInput, this)); if (this.editor.util.browser.firefox) { this.addShortcut('cmd+left', (function(_this) { return function(e) { e.preventDefault(); - _this.editor.selection.sel.modify('move', 'backward', 'lineboundary'); + _this.editor.selection._selection.modify('move', 'backward', 'lineboundary'); return false; }; })(this)); this.addShortcut('cmd+right', (function(_this) { return function(e) { e.preventDefault(); - _this.editor.selection.sel.modify('move', 'forward', 'lineboundary'); + _this.editor.selection._selection.modify('move', 'forward', 'lineboundary'); return false; }; })(this)); this.addShortcut((this.editor.util.os.mac ? 'cmd+a' : 'ctrl+a'), (function(_this) { return function(e) { @@ -720,11 +865,11 @@ firstBlock = $children.first().get(0); lastBlock = $children.last().get(0); range = document.createRange(); range.setStart(firstBlock, 0); range.setEnd(lastBlock, _this.editor.util.getNodeLength(lastBlock)); - _this.editor.selection.selectRange(range); + _this.editor.selection.range(range); return false; }; })(this)); } submitKey = this.editor.util.os.mac ? 'cmd+enter' : 'ctrl+enter'; @@ -748,11 +893,13 @@ this.focused = true; this.lastCaretPosition = null; return setTimeout((function(_this) { return function() { _this.editor.triggerHandler('focus'); - return _this.editor.trigger('selectionchanged'); + if (!_this.editor.util.support.onselectionchange) { + return _this.throttledSelectionChanged(); + } }; })(this), 0); }; InputManager.prototype._onBlur = function(e) { @@ -764,15 +911,11 @@ return this.editor.triggerHandler('blur'); }; InputManager.prototype._onMouseUp = function(e) { if (!this.editor.util.support.onselectionchange) { - return setTimeout((function(_this) { - return function() { - return _this.editor.trigger('selectionchanged'); - }; - })(this), 0); + return this.throttledSelectionChanged(); } }; InputManager.prototype._onKeyDown = function(e) { var base, ref, ref1, result; @@ -783,39 +926,39 @@ return; } if (e.which in this._keystrokeHandlers) { result = typeof (base = this._keystrokeHandlers[e.which])['*'] === "function" ? base['*'](e) : void 0; if (result) { - this.editor.trigger('valuechanged'); + this.throttledValueChanged(); return false; } - this.editor.util.traverseUp((function(_this) { - return function(node) { + this.editor.selection.startNodes().each((function(_this) { + return function(i, node) { var handler, ref; - if (node.nodeType !== document.ELEMENT_NODE) { + if (node.nodeType !== Node.ELEMENT_NODE) { return; } handler = (ref = _this._keystrokeHandlers[e.which]) != null ? ref[node.tagName.toLowerCase()] : void 0; result = typeof handler === "function" ? handler(e, $(node)) : void 0; if (result === true || result === false) { return false; } }; })(this)); if (result) { - this.editor.trigger('valuechanged'); + this.throttledValueChanged(); return false; } } if ((ref = e.which, indexOf.call(this._modifierKeys, ref) >= 0) || (ref1 = e.which, indexOf.call(this._arrowKeys, ref1) >= 0)) { return; } if (this.editor.util.metaKey(e) && e.which === 86) { return; } if (!this.editor.util.support.oninput) { - this.throttledTrigger('valuechanged', ['typing']); + this.throttledValueChanged(['typing']); } return null; }; InputManager.prototype._onKeyPress = function(e) { @@ -828,11 +971,11 @@ var p, ref; if (this.editor.triggerHandler(e) === false) { return false; } if (!this.editor.util.support.onselectionchange && (ref = e.which, indexOf.call(this._arrowKeys, ref) >= 0)) { - this.editor.trigger('selectionchanged'); + this.throttledValueChanged(); return; } if ((e.which === 8 || e.which === 46) && this.editor.util.isEmptyNode(this.editor.body)) { this.editor.body.empty(); p = $('<p/>').append(this.editor.util.phBr).appendTo(this.editor.body); @@ -847,11 +990,12 @@ } range = this.editor.selection.deleteRangeContents(); if (!range.collapsed) { range.collapse(true); } - $blockEl = this.editor.util.closestBlockEl(); + this.editor.selection.range(range); + $blockEl = this.editor.selection.blockNodes().last(); cleanPaste = $blockEl.is('pre, table'); if (e.originalEvent.clipboardData && e.originalEvent.clipboardData.items && e.originalEvent.clipboardData.items.length > 0) { pasteItem = e.originalEvent.clipboardData.items[0]; if (/^image\//.test(pasteItem.type) && !cleanPaste) { imageFile = pasteItem.getAsFile(); @@ -869,37 +1013,37 @@ return false; } } processPasteContent = (function(_this) { return function(pasteContent) { - var $img, blob, children, insertPosition, j, k, l, lastLine, len, len1, len2, len3, len4, line, lines, m, node, o, ref1, ref2, ref3; + var $img, blob, children, insertPosition, k, l, lastLine, len, len1, len2, len3, len4, line, lines, m, node, o, q, ref1, ref2, ref3; if (_this.editor.triggerHandler('pasting', [pasteContent]) === false) { return; } if (!pasteContent) { return; } else if (cleanPaste) { if ($blockEl.is('table')) { lines = pasteContent.split('\n'); lastLine = lines.pop(); - for (j = 0, len = lines.length; j < len; j++) { - line = lines[j]; + for (k = 0, len = lines.length; k < len; k++) { + line = lines[k]; _this.editor.selection.insertNode(document.createTextNode(line)); _this.editor.selection.insertNode($('<br/>')); } _this.editor.selection.insertNode(document.createTextNode(lastLine)); } else { pasteContent = $('<div/>').text(pasteContent); ref1 = pasteContent.contents(); - for (k = 0, len1 = ref1.length; k < len1; k++) { - node = ref1[k]; + for (l = 0, len1 = ref1.length; l < len1; l++) { + node = ref1[l]; _this.editor.selection.insertNode($(node)[0], range); } } } else if ($blockEl.is(_this.editor.body)) { - for (l = 0, len2 = pasteContent.length; l < len2; l++) { - node = pasteContent[l]; + for (m = 0, len2 = pasteContent.length; m < len2; m++) { + node = pasteContent[m]; _this.editor.selection.insertNode(node, range); } } else if (pasteContent.length < 1) { return; } else if (pasteContent.length === 1) { @@ -921,23 +1065,23 @@ return; } else if ($img.is('img[src^="webkit-fake-url://"]')) { return; } } - for (m = 0, len3 = children.length; m < len3; m++) { - node = children[m]; + for (o = 0, len3 = children.length; o < len3; o++) { + node = children[o]; _this.editor.selection.insertNode(node, range); } } else if ($blockEl.is('p') && _this.editor.util.isEmptyNode($blockEl)) { $blockEl.replaceWith(pasteContent); _this.editor.selection.setRangeAtEndOf(pasteContent, range); } else if (pasteContent.is('ul, ol')) { if (pasteContent.find('li').length === 1) { pasteContent = $('<div/>').text(pasteContent.text()); ref3 = pasteContent.contents(); - for (o = 0, len4 = ref3.length; o < len4; o++) { - node = ref3[o]; + for (q = 0, len4 = ref3.length; q < len4; q++) { + node = ref3[q]; _this.editor.selection.insertNode($(node)[0], range); } } else if ($blockEl.is('li')) { $blockEl.parent().after(pasteContent); _this.editor.selection.setRangeAtEndOf(pasteContent, range); @@ -962,11 +1106,11 @@ insertPosition = 'before'; } $blockEl[insertPosition](pasteContent); _this.editor.selection.setRangeAtEndOf(pasteContent.last(), range); } - return _this.editor.trigger('valuechanged'); + return _this.throttledValueChanged(); }; })(this); if (cleanPaste) { e.preventDefault(); if (this.editor.util.browser.msie) { @@ -1004,19 +1148,15 @@ InputManager.prototype._onDrop = function(e) { if (this.editor.triggerHandler(e) === false) { return false; } - return setTimeout((function(_this) { - return function() { - return _this.editor.trigger('valuechanged'); - }; - })(this), 0); + return this.throttledValueChanged(); }; InputManager.prototype._onInput = function(e) { - return this.throttledTrigger('valuechanged', ['oninput']); + return this.throttledValueChanged(['oninput']); }; InputManager.prototype.addKeystrokeHandler = function(key, node, handler) { if (!this._keystrokeHandlers[key]) { this._keystrokeHandlers[key] = {}; @@ -1049,11 +1189,11 @@ return function(e) { var $blockEl, $br; if (!e.shiftKey) { return; } - $blockEl = _this.editor.util.closestBlockEl(); + $blockEl = _this.editor.selection.blockNodes().last(); if ($blockEl.is('pre')) { return; } $br = $('<br/>'); if (_this.editor.selection.rangeAtEndOf($blockEl)) { @@ -1086,21 +1226,22 @@ this.editor.inputManager.addKeystrokeHandler('13', 'h5', titleEnterHandler); this.editor.inputManager.addKeystrokeHandler('13', 'h6', titleEnterHandler); } this.editor.inputManager.addKeystrokeHandler('8', '*', (function(_this) { return function(e) { - var $blockEl, $prevBlockEl, $rootBlock; - $rootBlock = _this.editor.util.furthestBlockEl(); + var $blockEl, $prevBlockEl, $rootBlock, isWebkit; + $rootBlock = _this.editor.selection.rootNodes().first(); $prevBlockEl = $rootBlock.prev(); if ($prevBlockEl.is('hr') && _this.editor.selection.rangeAtStartOf($rootBlock)) { _this.editor.selection.save(); $prevBlockEl.remove(); _this.editor.selection.restore(); return true; } - $blockEl = _this.editor.util.closestBlockEl(); - if (_this.editor.util.browser.webkit && _this.editor.selection.rangeAtStartOf($blockEl)) { + $blockEl = _this.editor.selection.blockNodes().last(); + isWebkit = _this.editor.util.browser.webkit; + if (isWebkit && _this.editor.selection.rangeAtStartOf($blockEl)) { _this.editor.selection.save(); _this.editor.formatter.cleanNode($blockEl, true); _this.editor.selection.restore(); return null; } @@ -1109,11 +1250,11 @@ this.editor.inputManager.addKeystrokeHandler('13', 'li', (function(_this) { return function(e, $node) { var $cloneNode, listEl, newBlockEl, newListEl; $cloneNode = $node.clone(); $cloneNode.find('ul, ol').remove(); - if (!(_this.editor.util.isEmptyNode($cloneNode) && $node.is(_this.editor.util.closestBlockEl()))) { + if (!(_this.editor.util.isEmptyNode($cloneNode) && $node.is(_this.editor.selection.blockNodes().last()))) { return; } listEl = $node.parent(); if ($node.next('li').length > 0) { if (!_this.editor.util.isEmptyNode($node)) { @@ -1159,11 +1300,11 @@ if (e.shiftKey) { $p = $('<p/>').append(_this.editor.util.phBr).insertAfter($node); _this.editor.selection.setRangeAtStartOf($p); return true; } - range = _this.editor.selection.getRange(); + range = _this.editor.selection.range(); breakNode = null; range.deleteContents(); if (!_this.editor.util.browser.msie && _this.editor.selection.rangeAtEndOf($node)) { breakNode = document.createTextNode('\n\n'); range.insertNode(breakNode); @@ -1172,18 +1313,18 @@ breakNode = document.createTextNode('\n'); range.insertNode(breakNode); range.setStartAfter(breakNode); } range.collapse(false); - _this.editor.selection.selectRange(range); + _this.editor.selection.range(range); return true; }; })(this)); this.editor.inputManager.addKeystrokeHandler('13', 'blockquote', (function(_this) { return function(e, $node) { var $closestBlock, range; - $closestBlock = _this.editor.util.closestBlockEl(); + $closestBlock = _this.editor.selection.blockNodes().last(); if (!($closestBlock.is('p') && !$closestBlock.next().length && _this.editor.util.isEmptyNode($closestBlock))) { return; } $node.after($closestBlock); range = document.createRange(); @@ -1191,11 +1332,11 @@ return true; }; })(this)); this.editor.inputManager.addKeystrokeHandler('8', 'li', (function(_this) { return function(e, $node) { - var $br, $childList, $newLi, $prevChildList, $prevNode, $textNode, range, text; + var $br, $childList, $newLi, $prevChildList, $prevNode, $textNode, isFF, range, text; $childList = $node.children('ul, ol'); $prevNode = $node.prev('li'); if (!($childList.length > 0 && $prevNode.length > 0)) { return false; } @@ -1213,11 +1354,12 @@ } else if (n.nodeType === 1) { text += $(n).text(); } return $textNode = $(n); }); - if ($textNode && text.length === 1 && _this.editor.util.browser.firefox && !$textNode.next('br').length) { + isFF = _this.editor.util.browser.firefox && !$textNode.next('br').length; + if ($textNode && text.length === 1 && isFF) { $br = $(_this.editor.util.phBr).insertAfter($textNode); $textNode.remove(); _this.editor.selection.setRangeBefore($br); return true; } else if (text.length > 0) { @@ -1232,23 +1374,23 @@ _this.editor.selection.setRangeAtEndOf($newLi, range); } else { _this.editor.selection.setRangeAtEndOf($prevNode, range); $prevNode.append($childList); $node.remove(); - _this.editor.selection.selectRange(range); + _this.editor.selection.range(range); } return true; }; })(this)); this.editor.inputManager.addKeystrokeHandler('8', 'pre', (function(_this) { return function(e, $node) { var $newNode, codeStr, range; if (!_this.editor.selection.rangeAtStartOf($node)) { return; } - codeStr = $node.html().replace('\n', '<br/>'); - $newNode = $('<p/>').append(codeStr || _this.editor.util.phBr).insertAfter($node); + codeStr = $node.html().replace('\n', '<br/>') || _this.editor.util.phBr; + $newNode = $('<p/>').append(codeStr).insertAfter($node); $node.remove(); range = document.createRange(); _this.editor.selection.setRangeAtStartOf($newNode, range); return true; }; @@ -1280,16 +1422,18 @@ UndoManager.pluginName = 'UndoManager'; UndoManager.prototype._index = -1; - UndoManager.prototype._capacity = 50; + UndoManager.prototype._capacity = 20; - UndoManager.prototype._timer = null; + UndoManager.prototype._startPosition = null; + UndoManager.prototype._endPosition = null; + UndoManager.prototype._init = function() { - var redoShortcut, undoShortcut; + var redoShortcut, throttledPushState, undoShortcut; this.editor = this._module; this._stack = []; if (this.editor.util.os.mac) { undoShortcut = 'cmd+z'; redoShortcut = 'shift+cmd+z'; @@ -1312,27 +1456,59 @@ e.preventDefault(); _this.redo(); return false; }; })(this)); - return this.editor.on('valuechanged', (function(_this) { - return function(e, src) { - if (src === 'undo' || src === 'redo') { - return; - } - if (_this._timer) { - clearTimeout(_this._timer); - _this._timer = null; - } - return _this._timer = setTimeout(function() { - _this._pushUndoState(); - return _this._timer = null; - }, 200); + throttledPushState = this.editor.util.throttle((function(_this) { + return function() { + return _this._pushUndoState(); }; + })(this), 500); + this.editor.on('valuechanged', function(e, src) { + if (src === 'undo' || src === 'redo') { + return; + } + return throttledPushState(); + }); + this.editor.on('selectionchanged', (function(_this) { + return function(e) { + _this._startPosition = null; + _this._endPosition = null; + return _this.update(); + }; })(this)); + return this.editor.on('blur', (function(_this) { + return function(e) { + _this._startPosition = null; + return _this._endPosition = null; + }; + })(this)); }; + UndoManager.prototype.startPosition = function() { + if (this.editor.selection._range) { + this._startPosition || (this._startPosition = this._getPosition('start')); + } + return this._startPosition; + }; + + UndoManager.prototype.endPosition = function() { + if (this.editor.selection._range) { + this._endPosition || (this._endPosition = (function(_this) { + return function() { + var range; + range = _this.editor.selection.range(); + if (range.collapsed) { + return _this._startPosition; + } + return _this._getPosition('end'); + }; + })(this)()); + } + return this._endPosition; + }; + UndoManager.prototype._pushUndoState = function() { var currentState, html; if (this.editor.triggerHandler('pushundostate') === false) { return; } @@ -1412,57 +1588,61 @@ } else { $parent = $(node).parent(); } offset = 0; merging = false; - $parent.contents().each((function(_this) { - return function(i, child) { - if (index === i || node === child) { - return false; - } - if (child.nodeType === 3) { - if (!merging) { - offset += 1; - merging = true; - } - } else { + $parent.contents().each(function(i, child) { + if (index === i || node === child) { + return false; + } + if (child.nodeType === 3) { + if (!merging) { offset += 1; - merging = false; + merging = true; } - return null; - }; - })(this)); + } else { + offset += 1; + merging = false; + } + return null; + }); return offset; }; - UndoManager.prototype._getNodePosition = function(node, offset) { - var position, prevNode; - if (node.nodeType === 3) { + UndoManager.prototype._getPosition = function(type) { + var $nodes, node, nodes, offset, position, prevNode, range; + if (type == null) { + type = 'start'; + } + range = this.editor.selection.range(); + offset = range[type + "Offset"]; + $nodes = this.editor.selection[type + "Nodes"](); + if ((node = $nodes.first()[0]).nodeType === Node.TEXT_NODE) { prevNode = node.previousSibling; - while (prevNode && prevNode.nodeType === 3) { + while (prevNode && prevNode.nodeType === Node.TEXT_NODE) { node = prevNode; offset += this.editor.util.getNodeLength(prevNode); prevNode = prevNode.previousSibling; } - } else { - offset = this._getNodeOffset(node, offset); + nodes = $nodes.get(); + nodes[0] = node; + $nodes = $(nodes); } - position = []; - position.unshift(offset); - this.editor.util.traverseUp((function(_this) { - return function(n) { - return position.unshift(_this._getNodeOffset(n)); + position = [offset]; + $nodes.each((function(_this) { + return function(i, node) { + return position.unshift(_this._getNodeOffset(node)); }; - })(this), node); + })(this)); return position; }; UndoManager.prototype._getNodeByPosition = function(position) { - var child, childNodes, i, j, len, node, offset, ref; + var child, childNodes, i, k, len, node, offset, ref; node = this.editor.body[0]; ref = position.slice(0, position.length - 1); - for (i = j = 0, len = ref.length; j < len; i = ++j) { + for (i = k = 0, len = ref.length; k < len; i = ++k) { offset = ref[i]; childNodes = node.childNodes; if (offset > childNodes.length - 1) { if (i === position.length - 2 && $(node).is('pre')) { child = document.createTextNode(''); @@ -1479,24 +1659,16 @@ }; UndoManager.prototype.caretPosition = function(caret) { var endContainer, endOffset, range, startContainer, startOffset; if (!caret) { - range = this.editor.selection.getRange(); - if (!(this.editor.inputManager.focused && (range != null))) { - return {}; - } - caret = { - start: [], - end: null, - collapsed: true - }; - caret.start = this._getNodePosition(range.startContainer, range.startOffset); - if (!range.collapsed) { - caret.end = this._getNodePosition(range.endContainer, range.endOffset); - caret.collapsed = false; - } + range = this.editor.selection.range(); + caret = this.editor.inputManager.focused && (range != null) ? { + start: this.startPosition(), + end: this.endPosition(), + collapsed: range.collapsed + } : {}; return caret; } else { if (!this.editor.inputManager.focused) { this.editor.body.focus(); } @@ -1518,11 +1690,11 @@ return; } range = document.createRange(); range.setStart(startContainer, startOffset); range.setEnd(endContainer, endOffset); - return this.editor.selection.selectRange(range); + return this.editor.selection.range(range); } }; return UndoManager; @@ -1643,75 +1815,26 @@ var $node; $node = $(node); return $node.is(':empty') || (!$node.text() && !$node.find(':not(br, span, div)').length); }; + Util.prototype.isDecoratedNode = function(node) { + return $(node).is('[class^="simditor-"]'); + }; + Util.prototype.blockNodes = ["div", "p", "ul", "ol", "li", "blockquote", "hr", "pre", "h1", "h2", "h3", "h4", "table"]; Util.prototype.isBlockNode = function(node) { node = $(node)[0]; if (!node || node.nodeType === 3) { return false; } return new RegExp("^(" + (this.blockNodes.join('|')) + ")$").test(node.nodeName.toLowerCase()); }; - Util.prototype.closestBlockEl = function(node) { - var $node, blockEl, range; - if (node == null) { - range = this.editor.selection.getRange(); - node = range != null ? range.commonAncestorContainer : void 0; - } - $node = $(node); - if (!$node.length) { - return null; - } - blockEl = $node.parentsUntil(this.editor.body).addBack(); - blockEl = blockEl.filter((function(_this) { - return function(i) { - return _this.isBlockNode(blockEl.eq(i)); - }; - })(this)); - if (blockEl.length) { - return blockEl.last(); - } else { - return null; - } - }; - - Util.prototype.furthestNode = function(node, filter) { - var $node, blockEl, range; - if (node == null) { - range = this.editor.selection.getRange(); - node = range != null ? range.commonAncestorContainer : void 0; - } - $node = $(node); - if (!$node.length) { - return null; - } - blockEl = $node.parentsUntil(this.editor.body).addBack(); - blockEl = blockEl.filter(function(i) { - var $n; - $n = blockEl.eq(i); - if ($.isFunction(filter)) { - return filter($n); - } else { - return $n.is(filter); - } - }); - if (blockEl.length) { - return blockEl.first(); - } else { - return null; - } - }; - - Util.prototype.furthestBlockEl = function(node) { - return this.furthestNode(node, $.proxy(this.isBlockNode, this)); - }; - Util.prototype.getNodeLength = function(node) { + node = $(node)[0]; switch (node.nodeType) { case 7: case 10: return 0; case 3: @@ -1720,36 +1843,12 @@ default: return node.childNodes.length; } }; - Util.prototype.traverseUp = function(callback, node) { - var j, len, n, nodes, range, result, results1; - if (node == null) { - range = this.editor.selection.getRange(); - node = range != null ? range.commonAncestorContainer : void 0; - } - if ((node == null) || !$.contains(this.editor.body[0], node)) { - return false; - } - nodes = $(node).parentsUntil(this.editor.body).get(); - nodes.unshift(node); - results1 = []; - for (j = 0, len = nodes.length; j < len; j++) { - n = nodes[j]; - result = callback(n); - if (result === false) { - break; - } else { - results1.push(void 0); - } - } - return results1; - }; - Util.prototype.dataURLtoBlob = function(dataURL) { - var BlobBuilder, arrayBuffer, bb, blobArray, byteString, hasArrayBufferViewSupport, hasBlobConstructor, i, intArray, j, mimeString, ref, supportBlob; + var BlobBuilder, arrayBuffer, bb, blobArray, byteString, hasArrayBufferViewSupport, hasBlobConstructor, i, intArray, k, mimeString, ref, supportBlob; hasBlobConstructor = window.Blob && (function() { var e; try { return Boolean(new Blob()); } catch (_error) { @@ -1776,11 +1875,11 @@ } else { byteString = decodeURIComponent(dataURL.split(',')[1]); } arrayBuffer = new ArrayBuffer(byteString.length); intArray = new Uint8Array(arrayBuffer); - for (i = j = 0, ref = byteString.length; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) { + for (i = k = 0, ref = byteString.length; 0 <= ref ? k <= ref : k >= ref; i = 0 <= ref ? ++k : --k) { intArray[i] = byteString.charCodeAt(i); } mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0]; if (hasBlobConstructor) { blobArray = hasArrayBufferViewSupport ? intArray : arrayBuffer; @@ -1792,42 +1891,34 @@ bb.append(arrayBuffer); return bb.getBlob(mimeString); }; Util.prototype.throttle = function(func, wait) { - var delayedCallTimeout, previousCallTime, stopDelayedCall; - delayedCallTimeout = null; - previousCallTime = 0; - stopDelayedCall = function() { - if (delayedCallTimeout) { - clearTimeout(delayedCallTimeout); - return delayedCallTimeout = null; - } + var args, call, ctx, last, rtn, throttled, timeoutID; + last = 0; + timeoutID = 0; + ctx = args = rtn = null; + call = function() { + timeoutID = 0; + last = +new Date(); + rtn = func.apply(ctx, args); + ctx = null; + return args = null; }; - return function() { - var args, now, remaining, result; - now = Date.now(); - previousCallTime || (previousCallTime = now); - remaining = wait - (now - previousCallTime); - result = null; - if ((0 < remaining && remaining < wait)) { - previousCallTime = now; - stopDelayedCall(); - args = arguments; - delayedCallTimeout = setTimeout(function() { - previousCallTime = 0; - delayedCallTimeout = null; - return result = func.apply(null, args); - }, wait); - } else { - stopDelayedCall(); - if (previousCallTime !== now) { - previousCallTime = 0; + return throttled = function() { + var delta; + ctx = this; + args = arguments; + delta = new Date() - last; + if (!timeoutID) { + if (delta >= wait) { + call(); + } else { + timeoutID = setTimeout(call, wait - delta); } - result = func.apply(null, arguments); } - return result; + return rtn; }; }; Util.prototype.formatHTML = function(html) { var cursor, indentString, lastMatch, level, match, re, repeatString, result, str; @@ -1901,15 +1992,13 @@ } if (!$.isArray(this.opts.toolbar)) { this.opts.toolbar = ['bold', 'italic', 'underline', 'strikethrough', '|', 'ol', 'ul', 'blockquote', 'code', '|', 'link', 'image', '|', 'indent', 'outdent']; } this._render(); - this.list.on('click', (function(_this) { - return function(e) { - return false; - }; - })(this)); + this.list.on('click', function(e) { + return false; + }); this.wrapper.on('mousedown', (function(_this) { return function(e) { return _this.list.find('.menu-on').removeClass('.menu-on'); }; })(this)); @@ -1951,41 +2040,36 @@ } } }; })(this)); } - this.editor.on('selectionchanged', (function(_this) { - return function() { - return _this.toolbarStatus(); - }; - })(this)); this.editor.on('destroy', (function(_this) { return function() { return _this.buttons.length = 0; }; })(this)); - return $(document).on('mousedown.simditor-' + this.editor.id, (function(_this) { + return $(document).on("mousedown.simditor-" + this.editor.id, (function(_this) { return function(e) { return _this.list.find('li.menu-on').removeClass('menu-on'); }; })(this)); }; Toolbar.prototype._render = function() { - var j, len, name, ref; + var k, len, name, ref; this.buttons = []; this.wrapper = $(this._tpl.wrapper).prependTo(this.editor.wrapper); this.list = this.wrapper.find('ul'); ref = this.opts.toolbar; - for (j = 0, len = ref.length; j < len; j++) { - name = ref[j]; + for (k = 0, len = ref.length; k < len; k++) { + name = ref[k]; if (name === '|') { $(this._tpl.separator).appendTo(this.list); continue; } if (!this.constructor.buttons[name]) { - throw new Error('simditor: invalid toolbar button "' + name + '"'); + throw new Error("simditor: invalid toolbar button " + name); continue; } this.buttons.push(new this.constructor.buttons[name]({ editor: this.editor })); @@ -1993,41 +2077,10 @@ if (this.opts.toolbarHidden) { return this.wrapper.hide(); } }; - Toolbar.prototype.toolbarStatus = function(name) { - var buttons; - if (!this.editor.inputManager.focused) { - return; - } - buttons = this.buttons.slice(0); - return this.editor.util.traverseUp((function(_this) { - return function(node) { - var button, i, j, k, len, len1, removeButtons; - removeButtons = []; - for (i = j = 0, len = buttons.length; j < len; i = ++j) { - button = buttons[i]; - if ((name != null) && button.name !== name) { - continue; - } - if (!button.status || button.status($(node)) === true) { - removeButtons.push(button); - } - } - for (k = 0, len1 = removeButtons.length; k < len1; k++) { - button = removeButtons[k]; - i = $.inArray(button, buttons); - buttons.splice(i, 1); - } - if (buttons.length === 0) { - return false; - } - }; - })(this)); - }; - Toolbar.prototype.findButton = function(name) { var button; button = this.list.find('.toolbar-item-' + name).data('button'); return button != null ? button : null; }; @@ -2068,44 +2121,55 @@ }; })(this)); }; Indentation.prototype.indent = function(isBackward) { - var $blockEls, $endBlock, $startBlock, range, result; - range = this.editor.selection.getRange(); - if (!range) { - return; - } - $startBlock = this.editor.util.closestBlockEl(range.startContainer); - $endBlock = this.editor.util.closestBlockEl(range.endContainer); - if (!($startBlock.is('li') && $endBlock.is('li') && $startBlock.parent().is($endBlock.parent()))) { - $startBlock = this.editor.util.furthestBlockEl($startBlock); - $endBlock = this.editor.util.furthestBlockEl($endBlock); - } - if ($startBlock.is($endBlock)) { - $blockEls = $startBlock; - } else { - $blockEls = $startBlock.nextUntil($endBlock).add($startBlock).add($endBlock); - } + var $blockNodes, $endNodes, $startNodes, nodes, result; + $startNodes = this.editor.selection.startNodes(); + $endNodes = this.editor.selection.endNodes(); + $blockNodes = this.editor.selection.blockNodes(); + nodes = []; + $blockNodes = $blockNodes.each(function(i, node) { + var include, j, k, len, n; + include = true; + for (j = k = 0, len = nodes.length; k < len; j = ++k) { + n = nodes[j]; + if ($.contains(node, n)) { + include = false; + break; + } else if ($.contains(n, node)) { + nodes.splice(j, 1, node); + include = false; + break; + } + } + if (include) { + return nodes.push(node); + } + }); + $blockNodes = $(nodes); result = false; - $blockEls.each((function(_this) { + $blockNodes.each((function(_this) { return function(i, blockEl) { - return result = isBackward ? _this.outdentBlock(blockEl) : _this.indentBlock(blockEl); + var r; + r = isBackward ? _this.outdentBlock(blockEl) : _this.indentBlock(blockEl); + if (r) { + return result = r; + } }; })(this)); return result; }; Indentation.prototype.indentBlock = function(blockEl) { - var $blockEl, $childList, $nextTd, $nextTr, $parentLi, $pre, $td, $tr, marginLeft, range, tagName; + var $blockEl, $childList, $nextTd, $nextTr, $parentLi, $pre, $td, $tr, marginLeft, tagName; $blockEl = $(blockEl); if (!$blockEl.length) { return; } if ($blockEl.is('pre')) { - range = this.editor.selection.getRange(); - $pre = $(range.commonAncestorContainer); + $pre = this.editor.selection.containerNode; if (!($pre.is($blockEl) || $pre.closest('pre').is($blockEl))) { return; } this.indentText(range); } else if ($blockEl.is('li')) { @@ -2125,25 +2189,26 @@ } else if ($blockEl.is('p, h1, h2, h3, h4')) { marginLeft = parseInt($blockEl.css('margin-left')) || 0; marginLeft = (Math.round(marginLeft / this.opts.indentWidth) + 1) * this.opts.indentWidth; $blockEl.css('margin-left', marginLeft); } else if ($blockEl.is('table') || $blockEl.is('.simditor-table')) { - range = this.editor.selection.getRange(); - $td = $(range.commonAncestorContainer).closest('td, th'); + $td = this.editor.selection.containerNode.closest('td, th'); $nextTd = $td.next('td, th'); if (!($nextTd.length > 0)) { $tr = $td.parent('tr'); $nextTr = $tr.next('tr'); if ($nextTr.length < 1 && $tr.parent().is('thead')) { $nextTr = $tr.parent('thead').next('tbody').find('tr:first'); } $nextTd = $nextTr.find('td:first, th:first'); } if (!($td.length > 0 && $nextTd.length > 0)) { - return false; + return; } this.editor.selection.setRangeAtEndOf($nextTd); + } else { + return false; } return true; }; Indentation.prototype.indentText = function(range) { @@ -2152,55 +2217,55 @@ textNode = document.createTextNode(text || '\u00A0\u00A0'); range.deleteContents(); range.insertNode(textNode); if (text) { range.selectNode(textNode); - return this.editor.selection.selectRange(range); + return this.editor.selection.range(range); } else { return this.editor.selection.setRangeAfter(textNode); } }; Indentation.prototype.outdentBlock = function(blockEl) { - var $blockEl, $parent, $parentLi, $pre, $prevTd, $prevTr, $td, $tr, button, marginLeft, range; + var $blockEl, $parent, $parentLi, $pre, $prevTd, $prevTr, $td, $tr, marginLeft, range; $blockEl = $(blockEl); if (!($blockEl && $blockEl.length > 0)) { return; } if ($blockEl.is('pre')) { - range = this.editor.selection.getRange(); - $pre = $(range.commonAncestorContainer); + $pre = this.editor.selection.containerNode; if (!($pre.is($blockEl) || $pre.closest('pre').is($blockEl))) { return; } this.outdentText(range); } else if ($blockEl.is('li')) { $parent = $blockEl.parent(); $parentLi = $parent.parent('li'); + this.editor.selection.save(); if ($parentLi.length < 1) { - button = this.editor.toolbar.findButton($parent[0].tagName.toLowerCase()); - if (button != null) { - button.command(); + range = document.createRange(); + range.setStartBefore($parent[0]); + range.setEndBefore($blockEl[0]); + $parent.before(range.extractContents()); + $('<p/>').insertBefore($parent).after($blockEl.children('ul, ol')).append($blockEl.contents()); + $blockEl.remove(); + } else { + if ($blockEl.next('li').length > 0) { + $('<' + $parent[0].tagName + '/>').append($blockEl.nextAll('li')).appendTo($blockEl); } - return; + $blockEl.insertAfter($parentLi); + if ($parent.children('li').length < 1) { + $parent.remove(); + } } - this.editor.selection.save(); - if ($blockEl.next('li').length > 0) { - $('<' + $parent[0].tagName + '/>').append($blockEl.nextAll('li')).appendTo($blockEl); - } - $blockEl.insertAfter($parentLi); - if ($parent.children('li').length < 1) { - $parent.remove(); - } this.editor.selection.restore(); } else if ($blockEl.is('p, h1, h2, h3, h4')) { marginLeft = parseInt($blockEl.css('margin-left')) || 0; marginLeft = Math.max(Math.round(marginLeft / this.opts.indentWidth) - 1, 0) * this.opts.indentWidth; $blockEl.css('margin-left', marginLeft === 0 ? '' : marginLeft); } else if ($blockEl.is('table') || $blockEl.is('.simditor-table')) { - range = this.editor.selection.getRange(); - $td = $(range.commonAncestorContainer).closest('td, th'); + $td = this.editor.selection.containerNode.closest('td, th'); $prevTd = $td.prev('td, th'); if (!($prevTd.length > 0)) { $tr = $td.parent('tr'); $prevTr = $tr.prev('tr'); if ($prevTr.length < 1 && $tr.parent().is('tbody')) { @@ -2210,10 +2275,12 @@ } if (!($td.length > 0 && $prevTd.length > 0)) { return; } this.editor.selection.setRangeAtEndOf($prevTd); + } else { + return false; } return true; }; Indentation.prototype.outdentText = function(range) {}; @@ -2231,18 +2298,18 @@ Simditor.connect(Util); Simditor.connect(InputManager); + Simditor.connect(Selection); + Simditor.connect(UndoManager); Simditor.connect(Keystroke); Simditor.connect(Formatter); - Simditor.connect(Selection); - Simditor.connect(Toolbar); Simditor.connect(Indentation); Simditor.count = 0; @@ -2298,22 +2365,22 @@ }; })(this)); if (this.util.browser.mozilla) { this.util.reflow(); try { - document.execCommand("enableObjectResizing", false, false); - return document.execCommand("enableInlineTableEditing", false, false); + document.execCommand('enableObjectResizing', false, false); + return document.execCommand('enableInlineTableEditing', false, false); } catch (_error) { e = _error; } } }; Simditor.prototype._tpl = "<div class=\"simditor\">\n <div class=\"simditor-wrapper\">\n <div class=\"simditor-placeholder\"></div>\n <div class=\"simditor-body\" contenteditable=\"true\">\n </div>\n </div>\n</div>"; Simditor.prototype._render = function() { - var key, ref, results1, val; + var key, ref, results, val; this.el = $(this._tpl).insertBefore(this.textarea); this.wrapper = this.el.find('.simditor-wrapper'); this.body = this.wrapper.find('.simditor-body'); this.placeholderEl = this.wrapper.find('.simditor-placeholder').append(this.opts.placeholder); this.el.data('simditor', this); @@ -2328,20 +2395,20 @@ if (this.util.os.mobile) { this.el.addClass('simditor-mobile'); } if (this.opts.params) { ref = this.opts.params; - results1 = []; + results = []; for (key in ref) { val = ref[key]; - results1.push($('<input/>', { + results.push($('<input/>', { type: 'hidden', name: key, value: val }).insertAfter(this.textarea)); } - return results1; + return results; } }; Simditor.prototype._placeholder = function() { var children; @@ -2487,10 +2554,54 @@ 'alignment': '水平对齐', 'alignCenter': '居中', 'alignLeft': '居左', 'alignRight': '居右', 'selectLanguage': '选择程序语言' + }, + 'en-US': { + 'blockquote': 'Block Quote', + 'bold': 'Bold', + 'code': 'Code', + 'color': 'Text Color', + 'hr': 'Horizontal Line', + 'image': 'Insert Image', + 'externalImage': 'External Image', + 'uploadImage': 'Upload Image', + 'uploadFailed': 'Upload failed', + 'uploadError': 'Error occurs during upload', + 'imageUrl': 'Url', + 'imageSize': 'Size', + 'imageAlt': 'Alt', + 'restoreImageSize': 'Restore Origin Size', + 'uploading': 'Uploading', + 'indent': 'Indent', + 'outdent': 'Outdent', + 'italic': 'Italic', + 'link': 'Insert Link', + 'text': 'Text', + 'linkText': 'Link Text', + 'linkUrl': 'Link Url', + 'removeLink': 'Remove Link', + 'ol': 'Ordered List', + 'ul': 'Unordered List', + 'strikethrough': 'Strikethrough', + 'table': 'Table', + 'deleteRow': 'Delete Row', + 'insertRowAbove': 'Insert Row Above', + 'insertRowBelow': 'Insert Row Below', + 'deleteColumn': 'Delete Column', + 'insertColumnLeft': 'Insert Column Left', + 'insertColumnRight': 'Insert Column Right', + 'deleteTable': 'Delete Table', + 'title': 'Title', + 'normalText': 'Text', + 'underline': 'Underline', + 'alignment': 'Alignment', + 'alignCenter': 'Align Center', + 'alignLeft': 'Align Left', + 'alignRight': 'Align Right', + 'selectLanguage': 'Select Language' } }; Button = (function(superClass) { extend(Button, superClass); @@ -2529,17 +2640,18 @@ this.title = this._t(this.name); Button.__super__.constructor.call(this, opts); } Button.prototype._init = function() { - var j, len, ref, results1, tag; + var k, len, ref, tag; this.render(); this.el.on('mousedown', (function(_this) { return function(e) { - var exceed, param; + var exceed, noFocus, param; e.preventDefault(); - if (_this.el.hasClass('disabled') || (_this.needFocus && !_this.editor.inputManager.focused)) { + noFocus = _this.needFocus && !_this.editor.inputManager.focused; + if (_this.el.hasClass('disabled') || noFocus) { return false; } if (_this.menu) { _this.wrapper.toggleClass('menu-on').siblings('li').removeClass('menu-on'); if (_this.wrapper.is('.menu-on')) { @@ -2559,31 +2671,32 @@ return false; }; })(this)); this.wrapper.on('click', 'a.menu-item', (function(_this) { return function(e) { - var btn, param; + var btn, noFocus, param; e.preventDefault(); btn = $(e.currentTarget); _this.wrapper.removeClass('menu-on'); - if (btn.hasClass('disabled') || (_this.needFocus && !_this.editor.inputManager.focused)) { + noFocus = _this.needFocus && !_this.editor.inputManager.focused; + if (btn.hasClass('disabled') || noFocus) { return false; } _this.editor.toolbar.wrapper.removeClass('menu-on'); param = btn.data('param'); _this.command(param); return false; }; })(this)); - this.wrapper.on('mousedown', 'a.menu-item', (function(_this) { - return function(e) { - return false; - }; - })(this)); + this.wrapper.on('mousedown', 'a.menu-item', function(e) { + return false; + }); this.editor.on('blur', (function(_this) { return function() { - if (!(_this.editor.body.is(':visible') && _this.editor.body.is('[contenteditable]'))) { + var editorActive; + editorActive = _this.editor.body.is(':visible') && _this.editor.body.is('[contenteditable]'); + if (!editorActive) { return; } _this.setActive(false); return _this.setDisabled(false); }; @@ -2595,21 +2708,24 @@ return false; }; })(this)); } ref = this.htmlTag.split(','); - results1 = []; - for (j = 0, len = ref.length; j < len; j++) { - tag = ref[j]; + for (k = 0, len = ref.length; k < len; k++) { + tag = ref[k]; tag = $.trim(tag); if (tag && $.inArray(tag, this.editor.formatter._allowedTags) < 0) { - results1.push(this.editor.formatter._allowedTags.push(tag)); - } else { - results1.push(void 0); + this.editor.formatter._allowedTags.push(tag); } } - return results1; + return this.editor.on('selectionchanged', (function(_this) { + return function(e) { + if (_this.editor.inputManager.focused) { + return _this._status(); + } + }; + })(this)); }; Button.prototype.iconClassOf = function(icon) { if (icon) { return "simditor-icon simditor-icon-" + icon; @@ -2634,68 +2750,85 @@ this.menuWrapper.addClass("toolbar-menu-" + this.name); return this.renderMenu(); }; Button.prototype.renderMenu = function() { - var $menuBtnEl, $menuItemEl, j, len, menuItem, ref, ref1, results1; + var $menuBtnEl, $menuItemEl, k, len, menuItem, ref, ref1, results; if (!$.isArray(this.menu)) { return; } this.menuEl = $('<ul/>').appendTo(this.menuWrapper); ref = this.menu; - results1 = []; - for (j = 0, len = ref.length; j < len; j++) { - menuItem = ref[j]; + results = []; + for (k = 0, len = ref.length; k < len; k++) { + menuItem = ref[k]; if (menuItem === '|') { $(this._tpl.separator).appendTo(this.menuEl); continue; } $menuItemEl = $(this._tpl.menuItem).appendTo(this.menuEl); $menuBtnEl = $menuItemEl.find('a.menu-item').attr({ 'title': (ref1 = menuItem.title) != null ? ref1 : menuItem.text, 'data-param': menuItem.param }).addClass('menu-item-' + menuItem.name); if (menuItem.icon) { - results1.push($menuBtnEl.find('span').addClass(this.iconClassOf(menuItem.icon))); + results.push($menuBtnEl.find('span').addClass(this.iconClassOf(menuItem.icon))); } else { - results1.push($menuBtnEl.find('span').text(menuItem.text)); + results.push($menuBtnEl.find('span').text(menuItem.text)); } } - return results1; + return results; }; Button.prototype.setActive = function(active) { if (active === this.active) { return; } this.active = active; - this.el.toggleClass('active', this.active); - return this.editor.toolbar.trigger('buttonstatus', [this]); + return this.el.toggleClass('active', this.active); }; Button.prototype.setDisabled = function(disabled) { if (disabled === this.disabled) { return; } this.disabled = disabled; - this.el.toggleClass('disabled', this.disabled); - return this.editor.toolbar.trigger('buttonstatus', [this]); + return this.el.toggleClass('disabled', this.disabled); }; - Button.prototype.status = function($node) { - if ($node != null) { - this.setDisabled($node.is(this.disableTag)); - } + Button.prototype._disableStatus = function() { + var disabled, endNodes, startNodes; + startNodes = this.editor.selection.startNodes(); + endNodes = this.editor.selection.endNodes(); + disabled = startNodes.filter(this.disableTag).length > 0 || endNodes.filter(this.disableTag).length > 0; + this.setDisabled(disabled); if (this.disabled) { - return true; + this.setActive(false); } - if ($node != null) { - this.setActive($node.is(this.htmlTag)); - } + return this.disabled; + }; + + Button.prototype._activeStatus = function() { + var active, endNode, endNodes, startNode, startNodes; + startNodes = this.editor.selection.startNodes(); + endNodes = this.editor.selection.endNodes(); + startNode = startNodes.filter(this.htmlTag); + endNode = endNodes.filter(this.htmlTag); + active = startNode.length > 0 && endNode.length > 0 && startNode.is(endNode); + this.node = active ? startNode : null; + this.setActive(active); return this.active; }; + Button.prototype._status = function() { + this._disableStatus(); + if (this.disabled) { + return; + } + return this._activeStatus(); + }; + Button.prototype.command = function(param) {}; Button.prototype._t = function() { var args, ref, result; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; @@ -2752,18 +2885,16 @@ position = 'bottom'; } if ($target == null) { return; } - this.el.siblings('.simditor-popover').each((function(_this) { - return function(i, popover) { - popover = $(popover).data('popover'); - if (popover.active) { - return popover.hide(); - } - }; - })(this)); + this.el.siblings('.simditor-popover').each(function(i, popover) { + popover = $(popover).data('popover'); + if (popover.active) { + return popover.hide(); + } + }); if (this.active && this.target) { this.target.removeClass('selected'); } this.target = $target.addClass('selected'); if (this.active) { @@ -2772,16 +2903,13 @@ } else { this.active = true; this.el.css({ left: -9999 }).show(); - return setTimeout((function(_this) { - return function() { - _this.refresh(position); - return _this.trigger('popovershow'); - }; - })(this), 0); + this.editor.util.reflow(); + this.refresh(position); + return this.trigger('popovershow'); } }; Popover.prototype.hide = function() { if (!this.active) { @@ -2795,11 +2923,11 @@ this.el.hide(); return this.trigger('popoverhide'); }; Popover.prototype.refresh = function(position) { - var editorOffset, left, targetH, targetOffset, top; + var editorOffset, left, maxLeft, targetH, targetOffset, top; if (position == null) { position = 'bottom'; } if (!this.active) { return; @@ -2810,11 +2938,12 @@ if (position === 'bottom') { top = targetOffset.top - editorOffset.top + targetH; } else if (position === 'top') { top = targetOffset.top - editorOffset.top - this.el.height(); } - left = Math.min(targetOffset.left - editorOffset.left, this.editor.wrapper.width() - this.el.outerWidth() - 10); + maxLeft = this.editor.wrapper.width() - this.el.outerWidth() - 10; + left = Math.min(targetOffset.left - editorOffset.left, maxLeft); return this.el.css({ top: top + this.offset.top, left: left + this.offset.left }); }; @@ -2886,77 +3015,37 @@ return TitleButton.__super__._init.call(this); }; TitleButton.prototype.setActive = function(active, param) { TitleButton.__super__.setActive.call(this, active); + if (active) { + param || (param = this.node[0].tagName.toLowerCase()); + } this.el.removeClass('active-p active-h1 active-h2 active-h3'); if (active) { return this.el.addClass('active active-' + param); } }; - TitleButton.prototype.status = function($node) { - var param, ref; - if ($node != null) { - this.setDisabled($node.is(this.disableTag)); - } - if (this.disabled) { - return true; - } - if ($node != null) { - param = (ref = $node[0].tagName) != null ? ref.toLowerCase() : void 0; - this.setActive($node.is(this.htmlTag), param); - } - return this.active; - }; - TitleButton.prototype.command = function(param) { - var $contents, $endBlock, $startBlock, endNode, j, len, node, range, ref, results, startNode; - range = this.editor.selection.getRange(); - startNode = range.startContainer; - endNode = range.endContainer; - $startBlock = this.editor.util.closestBlockEl(startNode); - $endBlock = this.editor.util.closestBlockEl(endNode); + var $rootNodes; + $rootNodes = this.editor.selection.rootNodes(); this.editor.selection.save(); - range.setStartBefore($startBlock[0]); - range.setEndAfter($endBlock[0]); - $contents = $(range.extractContents()); - results = []; - $contents.children().each((function(_this) { - return function(i, el) { - var c, converted, j, len, results1; - converted = _this._convertEl(el, param); - results1 = []; - for (j = 0, len = converted.length; j < len; j++) { - c = converted[j]; - results1.push(results.push(c)); + $rootNodes.each((function(_this) { + return function(i, node) { + var $node; + $node = $(node); + if ($node.is('blockquote') || $node.is(param) || $node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node)) { + return; } - return results1; + return $('<' + param + '/>').append($node.contents()).replaceAll($node); }; })(this)); - ref = results.reverse(); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - range.insertNode(node[0]); - } this.editor.selection.restore(); return this.editor.trigger('valuechanged'); }; - TitleButton.prototype._convertEl = function(el, param) { - var $block, $el, results; - $el = $(el); - results = []; - if ($el.is(param)) { - results.push($el); - } else { - $block = $('<' + param + '/>').append($el.contents()); - results.push($block); - } - return results; - }; - return TitleButton; })(Button); Simditor.Toolbar.addButton(TitleButton); @@ -2986,21 +3075,15 @@ this.shortcut = 'ctrl+b'; } return BoldButton.__super__._init.call(this); }; - BoldButton.prototype.status = function($node) { + BoldButton.prototype._activeStatus = function() { var active; - if ($node != null) { - this.setDisabled($node.is(this.disableTag)); - } - if (this.disabled) { - return true; - } active = document.queryCommandState('bold') === true; this.setActive(active); - return active; + return this.active; }; BoldButton.prototype.command = function() { document.execCommand('bold'); if (!this.editor.util.support.oninput) { @@ -3032,29 +3115,23 @@ ItalicButton.prototype.shortcut = 'cmd+i'; ItalicButton.prototype._init = function() { if (this.editor.util.os.mac) { - this.title = this.title + ' ( Cmd + i )'; + this.title = this.title + " ( Cmd + i )"; } else { - this.title = this.title + ' ( Ctrl + i )'; + this.title = this.title + " ( Ctrl + i )"; this.shortcut = 'ctrl+i'; } return ItalicButton.__super__._init.call(this); }; - ItalicButton.prototype.status = function($node) { + ItalicButton.prototype._activeStatus = function() { var active; - if ($node != null) { - this.setDisabled($node.is(this.disableTag)); - } - if (this.disabled) { - return this.disabled; - } active = document.queryCommandState('italic') === true; this.setActive(active); - return active; + return this.active; }; ItalicButton.prototype.command = function() { document.execCommand('italic'); if (!this.editor.util.support.oninput) { @@ -3094,21 +3171,15 @@ this.shortcut = 'ctrl+u'; } return UnderlineButton.__super__.render.call(this); }; - UnderlineButton.prototype.status = function($node) { + UnderlineButton.prototype._activeStatus = function() { var active; - if ($node != null) { - this.setDisabled($node.is(this.disableTag)); - } - if (this.disabled) { - return this.disabled; - } active = document.queryCommandState('underline') === true; this.setActive(active); - return active; + return this.active; }; UnderlineButton.prototype.command = function() { document.execCommand('underline'); if (!this.editor.util.support.oninput) { @@ -3143,11 +3214,11 @@ args = 1 <= arguments.length ? slice.call(arguments, 0) : []; return ColorButton.__super__.render.apply(this, args); }; ColorButton.prototype.renderMenu = function() { - $('<ul class="color-list">\n <li><a href="javascript:;" class="font-color font-color-1" data-color=""></a></li>\n <li><a href="javascript:;" class="font-color font-color-2" data-color=""></a></li>\n <li><a href="javascript:;" class="font-color font-color-3" data-color=""></a></li>\n <li><a href="javascript:;" class="font-color font-color-4" data-color=""></a></li>\n <li><a href="javascript:;" class="font-color font-color-5" data-color=""></a></li>\n <li><a href="javascript:;" class="font-color font-color-6" data-color=""></a></li>\n <li><a href="javascript:;" class="font-color font-color-7" data-color=""></a></li>\n <li><a href="javascript:;" class="font-color font-color-default" data-color=""></a></li>\n</ul>').appendTo(this.menuWrapper); + $('<ul class="color-list">\n <li><a href="javascript:;" class="font-color font-color-1"></a></li>\n <li><a href="javascript:;" class="font-color font-color-2"></a></li>\n <li><a href="javascript:;" class="font-color font-color-3"></a></li>\n <li><a href="javascript:;" class="font-color font-color-4"></a></li>\n <li><a href="javascript:;" class="font-color font-color-5"></a></li>\n <li><a href="javascript:;" class="font-color font-color-6"></a></li>\n <li><a href="javascript:;" class="font-color font-color-7"></a></li>\n <li><a href="javascript:;" class="font-color font-color-default"></a></li>\n</ul>').appendTo(this.menuWrapper); this.menuWrapper.on('mousedown', '.color-list', function(e) { return false; }); return this.menuWrapper.on('click', '.font-color', (function(_this) { return function(e) { @@ -3166,11 +3237,13 @@ hex = _this._convertRgbToHex(rgb); } if (!hex) { return; } + document.execCommand('styleWithCSS', false, true); document.execCommand('foreColor', false, hex); + document.execCommand('styleWithCSS', false, false); if (!_this.editor.util.support.oninput) { return _this.editor.trigger('valuechanged'); } }; })(this)); @@ -3214,133 +3287,47 @@ ListButton.prototype.type = ''; ListButton.prototype.disableTag = 'pre, table'; - ListButton.prototype.status = function($node) { - var anotherType; - if ($node != null) { - this.setDisabled($node.is(this.disableTag)); - } - if (this.disabled) { - return true; - } - if ($node == null) { - return this.active; - } - anotherType = this.type === 'ul' ? 'ol' : 'ul'; - if ($node.is(anotherType)) { - this.setActive(false); - return true; - } else { - this.setActive($node.is(this.htmlTag)); - return this.active; - } - }; - ListButton.prototype.command = function(param) { - var $contents, $endBlock, $furthestEnd, $furthestStart, $parent, $startBlock, endLevel, endNode, getListLevel, j, len, node, range, ref, results, startLevel, startNode; - range = this.editor.selection.getRange(); - startNode = range.startContainer; - endNode = range.endContainer; - $startBlock = this.editor.util.closestBlockEl(startNode); - $endBlock = this.editor.util.closestBlockEl(endNode); + var $list, $rootNodes, anotherType; + $rootNodes = this.editor.selection.blockNodes(); + anotherType = this.type === 'ul' ? 'ol' : 'ul'; this.editor.selection.save(); - range.setStartBefore($startBlock[0]); - range.setEndAfter($endBlock[0]); - if ($startBlock.is('li') && $endBlock.is('li')) { - $furthestStart = this.editor.util.furthestNode($startBlock, 'ul, ol'); - $furthestEnd = this.editor.util.furthestNode($endBlock, 'ul, ol'); - if ($furthestStart.is($furthestEnd)) { - getListLevel = function($li) { - var lvl; - lvl = 1; - while (!$li.parent().is($furthestStart)) { - lvl += 1; - $li = $li.parent(); - } - return lvl; - }; - startLevel = getListLevel($startBlock); - endLevel = getListLevel($endBlock); - if (startLevel > endLevel) { - $parent = $endBlock.parent(); + $list = null; + $rootNodes.each((function(_this) { + return function(i, node) { + var $node; + $node = $(node); + if ($node.is('blockquote, li') || $node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node) || !$.contains(document, node)) { + return; + } + if ($node.is(_this.type)) { + $node.children('li').each(function(i, li) { + var $childList, $li; + $li = $(li); + $childList = $li.children('ul, ol').insertAfter($node); + return $('<p/>').append($(li).html() || _this.editor.util.phBr).insertBefore($node); + }); + return $node.remove(); + } else if ($node.is(anotherType)) { + return $('<' + _this.type + '/>').append($node.contents()).replaceAll($node); + } else if ($list && $node.prev().is($list)) { + $('<li/>').append($node.html() || _this.editor.util.phBr).appendTo($list); + return $node.remove(); } else { - $parent = $startBlock.parent(); + $list = $("<" + _this.type + "><li></li></" + _this.type + ">"); + $list.find('li').append($node.html() || _this.editor.util.phBr); + return $list.replaceAll($node); } - range.setStartBefore($parent[0]); - range.setEndAfter($parent[0]); - } else { - range.setStartBefore($furthestStart[0]); - range.setEndAfter($furthestEnd[0]); - } - } - $contents = $(range.extractContents()); - results = []; - $contents.children().each((function(_this) { - return function(i, el) { - var c, converted, j, len, results1; - converted = _this._convertEl(el); - results1 = []; - for (j = 0, len = converted.length; j < len; j++) { - c = converted[j]; - if (results.length && results[results.length - 1].is(_this.type) && c.is(_this.type)) { - results1.push(results[results.length - 1].append(c.children())); - } else { - results1.push(results.push(c)); - } - } - return results1; }; })(this)); - ref = results.reverse(); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - range.insertNode(node[0]); - } this.editor.selection.restore(); return this.editor.trigger('valuechanged'); }; - ListButton.prototype._convertEl = function(el) { - var $el, anotherType, block, child, children, j, len, ref, results; - $el = $(el); - results = []; - anotherType = this.type === 'ul' ? 'ol' : 'ul'; - if ($el.is(this.type)) { - $el.children('li').each((function(_this) { - return function(i, li) { - var $childList, $li, block; - $li = $(li); - $childList = $li.children('ul, ol').remove(); - block = $('<p/>').append($(li).html() || _this.editor.util.phBr); - results.push(block); - if ($childList.length > 0) { - return results.push($childList); - } - }; - })(this)); - } else if ($el.is(anotherType)) { - block = $('<' + this.type + '/>').append($el.html()); - results.push(block); - } else if ($el.is('blockquote')) { - ref = $el.children().get(); - for (j = 0, len = ref.length; j < len; j++) { - child = ref[j]; - children = this._convertEl(child); - } - $.merge(results, children); - } else if ($el.is('table')) { - - } else { - block = $('<' + this.type + '><li></li></' + this.type + '>'); - block.find('li').append($el.html() || this.editor.util.phBr); - results.push(block); - } - return results; - }; - return ListButton; })(Button); OrderListButton = (function(superClass) { @@ -3423,63 +3410,44 @@ BlockquoteButton.prototype.htmlTag = 'blockquote'; BlockquoteButton.prototype.disableTag = 'pre, table'; BlockquoteButton.prototype.command = function() { - var $contents, $endBlock, $startBlock, endNode, j, len, node, range, ref, results, startNode; - range = this.editor.selection.getRange(); - startNode = range.startContainer; - endNode = range.endContainer; - $startBlock = this.editor.util.furthestBlockEl(startNode); - $endBlock = this.editor.util.furthestBlockEl(endNode); + var $rootNodes, clearCache, nodeCache; + $rootNodes = this.editor.selection.rootNodes(); this.editor.selection.save(); - range.setStartBefore($startBlock[0]); - range.setEndAfter($endBlock[0]); - $contents = $(range.extractContents()); - results = []; - $contents.children().each((function(_this) { - return function(i, el) { - var c, converted, j, len, results1; - converted = _this._convertEl(el); - results1 = []; - for (j = 0, len = converted.length; j < len; j++) { - c = converted[j]; - if (results.length && results[results.length - 1].is(_this.htmlTag) && c.is(_this.htmlTag)) { - results1.push(results[results.length - 1].append(c.children())); - } else { - results1.push(results.push(c)); - } + nodeCache = []; + clearCache = (function(_this) { + return function() { + if (nodeCache.length > 0) { + $("<" + _this.htmlTag + "/>").insertBefore(nodeCache[0]).append(nodeCache); + return nodeCache.length = 0; } - return results1; }; + })(this); + $rootNodes.each((function(_this) { + return function(i, node) { + var $node; + $node = $(node); + if (!$node.parent().is(_this.editor.body)) { + return; + } + if ($node.is(_this.htmlTag)) { + clearCache(); + return $node.children().unwrap(); + } else if ($node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node)) { + return clearCache(); + } else { + return nodeCache.push(node); + } + }; })(this)); - ref = results.reverse(); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - range.insertNode(node[0]); - } + clearCache(); this.editor.selection.restore(); return this.editor.trigger('valuechanged'); }; - BlockquoteButton.prototype._convertEl = function(el) { - var $el, block, results; - $el = $(el); - results = []; - if ($el.is(this.htmlTag)) { - $el.children().each((function(_this) { - return function(i, node) { - return results.push($(node)); - }; - })(this)); - } else { - block = $('<' + this.htmlTag + '/>').append($el); - results.push(block); - } - return results; - }; - return BlockquoteButton; })(Button); Simditor.Toolbar.addButton(BlockquoteButton); @@ -3495,11 +3463,11 @@ CodeButton.prototype.icon = 'code'; CodeButton.prototype.htmlTag = 'pre'; - CodeButton.prototype.disableTag = 'li, table'; + CodeButton.prototype.disableTag = 'ul, ol, table'; CodeButton.prototype._init = function() { CodeButton.__super__._init.call(this); this.editor.on('decorate', (function(_this) { return function(e, $el) { @@ -3524,19 +3492,17 @@ return this.popover = new CodePopover({ button: this }); }; - CodeButton.prototype.status = function($node) { - var result; - result = CodeButton.__super__.status.call(this, $node); + CodeButton.prototype._status = function() { + CodeButton.__super__._status.call(this); if (this.active) { - this.popover.show($node); - } else if (this.editor.util.isBlockNode($node)) { - this.popover.hide(); + return this.popover.show(this.node); + } else { + return this.popover.hide(); } - return result; }; CodeButton.prototype.decorate = function($pre) { var $code, lang, ref, ref1; $code = $pre.find('> code'); @@ -3558,64 +3524,44 @@ } return $pre.wrapInner($code).removeAttr('data-lang'); }; CodeButton.prototype.command = function() { - var $contents, $endBlock, $startBlock, endNode, j, len, node, range, ref, results, startNode; - range = this.editor.selection.getRange(); - startNode = range.startContainer; - endNode = range.endContainer; - $startBlock = this.editor.util.closestBlockEl(startNode); - $endBlock = this.editor.util.closestBlockEl(endNode); - range.setStartBefore($startBlock[0]); - range.setEndAfter($endBlock[0]); - $contents = $(range.extractContents()); - results = []; - $contents.children().each((function(_this) { - return function(i, el) { - var c, converted, j, len, results1; - converted = _this._convertEl(el); - results1 = []; - for (j = 0, len = converted.length; j < len; j++) { - c = converted[j]; - if (results.length && results[results.length - 1].is(_this.htmlTag) && c.is(_this.htmlTag)) { - results1.push(results[results.length - 1].append(c.contents())); - } else { - results1.push(results.push(c)); - } + var $rootNodes, clearCache, nodeCache, pres; + $rootNodes = this.editor.selection.rootNodes(); + nodeCache = []; + pres = []; + clearCache = (function(_this) { + return function() { + var $pre; + if (!(nodeCache.length > 0)) { + return; } - return results1; + $pre = $("<" + _this.htmlTag + "/>").insertBefore(nodeCache[0]).text(_this.editor.formatter.clearHtml(nodeCache)); + pres.push($pre[0]); + return nodeCache.length = 0; }; + })(this); + $rootNodes.each((function(_this) { + return function(i, node) { + var $node; + $node = $(node); + if ($node.is(_this.htmlTag)) { + clearCache(); + return $('<p/>').append($node.html().replace('\n', '<br/>')).replaceAll($node); + } else if ($node.is(_this.disableTag) || _this.editor.util.isDecoratedNode($node) || $node.is('blockquote')) { + return clearCache(); + } else { + return nodeCache.push(node); + } + }; })(this)); - ref = results.reverse(); - for (j = 0, len = ref.length; j < len; j++) { - node = ref[j]; - range.insertNode(node[0]); - } - this.editor.selection.setRangeAtEndOf(results[0]); + clearCache(); + this.editor.selection.setRangeAtEndOf($(pres).last()); return this.editor.trigger('valuechanged'); }; - CodeButton.prototype._convertEl = function(el) { - var $el, block, codeStr, results; - $el = $(el); - results = []; - if ($el.is(this.htmlTag)) { - block = $('<p/>').append($el.html().replace('\n', '<br/>')); - results.push(block); - } else { - if (!$el.text() && $el.children().length === 1 && $el.children().is('br')) { - codeStr = '\n'; - } else { - codeStr = this.editor.formatter.clearHtml($el); - } - block = $('<' + this.htmlTag + '/>').text(codeStr); - results.push(block); - } - return results; - }; - return CodeButton; })(Button); CodePopover = (function(superClass) { @@ -3624,11 +3570,11 @@ function CodePopover() { return CodePopover.__super__.constructor.apply(this, arguments); } CodePopover.prototype.render = function() { - var $option, j, lang, len, ref; + var $option, k, lang, len, ref; this._tpl = "<div class=\"code-settings\">\n <div class=\"settings-field\">\n <select class=\"select-lang\">\n <option value=\"-1\">" + (this._t('selectLanguage')) + "</option>\n </select>\n </div>\n</div>"; this.langs = this.editor.opts.codeLanguages || [ { name: 'Bash', value: 'bash' @@ -3692,12 +3638,12 @@ } ]; this.el.addClass('code-popover').append(this._tpl); this.selectEl = this.el.find('.select-lang'); ref = this.langs; - for (j = 0, len = ref.length; j < len; j++) { - lang = ref[j]; + for (k = 0, len = ref.length; k < len; k++) { + lang = ref[k]; $option = $('<option/>', { text: lang.name, value: lang.value }).appendTo(this.selectEl); } @@ -3765,60 +3711,35 @@ return this.popover = new LinkPopover({ button: this }); }; - LinkButton.prototype.status = function($node) { - var showPopover; - if ($node != null) { - this.setDisabled($node.is(this.disableTag)); - } - if (this.disabled) { - return true; - } - if ($node == null) { - return this.active; - } - showPopover = true; - if (!$node.is(this.htmlTag) || $node.is('[class^="simditor-"]')) { - this.setActive(false); - showPopover = false; - } else if (this.editor.selection.rangeAtEndOf($node)) { - this.setActive(true); - showPopover = false; + LinkButton.prototype._status = function() { + LinkButton.__super__._status.call(this); + if (this.active && !this.editor.selection.rangeAtEndOf(this.node)) { + return this.popover.show(this.node); } else { - this.setActive(true); + return this.popover.hide(); } - if (showPopover) { - this.popover.show($node); - } else if (this.editor.util.isBlockNode($node)) { - this.popover.hide(); - } - return this.active; }; LinkButton.prototype.command = function() { - var $contents, $endBlock, $link, $newBlock, $startBlock, endNode, linkText, range, startNode, txtNode; - range = this.editor.selection.getRange(); + var $contents, $link, $newBlock, linkText, range, txtNode; + range = this.editor.selection.range(); if (this.active) { - $link = $(range.commonAncestorContainer).closest('a'); - txtNode = document.createTextNode($link.text()); - $link.replaceWith(txtNode); + txtNode = document.createTextNode(this.node.text()); + this.node.replaceWith(txtNode); range.selectNode(txtNode); } else { - startNode = range.startContainer; - endNode = range.endContainer; - $startBlock = this.editor.util.closestBlockEl(startNode); - $endBlock = this.editor.util.closestBlockEl(endNode); $contents = $(range.extractContents()); linkText = this.editor.formatter.clearHtml($contents.contents(), false); $link = $('<a/>', { href: 'http://www.example.com', target: '_blank', text: linkText || this._t('linkText') }); - if ($startBlock[0] === $endBlock[0]) { + if (this.editor.selection.blockNodes().length === 1) { range.insertNode($link[0]); } else { $newBlock = $('<p/>').append($link); range.insertNode($newBlock[0]); } @@ -3833,11 +3754,11 @@ return _this.popover.textEl[0].select(); } }; })(this)); } - this.editor.selection.selectRange(range); + this.editor.selection.range(range); return this.editor.trigger('valuechanged'); }; return LinkButton; @@ -3850,11 +3771,11 @@ return LinkPopover.__super__.constructor.apply(this, arguments); } LinkPopover.prototype.render = function() { var tpl; - tpl = "<div class=\"link-settings\">\n <div class=\"settings-field\">\n <label>" + (this._t('text')) + "</label>\n <input class=\"link-text\" type=\"text\"/>\n <a class=\"btn-unlink\" href=\"javascript:;\" title=\"" + (this._t('removeLink')) + "\" tabindex=\"-1\"><span class=\"simditor-icon simditor-icon-unlink\"></span></a>\n </div>\n <div class=\"settings-field\">\n <label>" + (this._t('linkUrl')) + "</label>\n <input class=\"link-url\" type=\"text\"/>\n </div>\n</div>"; + tpl = "<div class=\"link-settings\">\n <div class=\"settings-field\">\n <label>" + (this._t('text')) + "</label>\n <input class=\"link-text\" type=\"text\"/>\n <a class=\"btn-unlink\" href=\"javascript:;\" title=\"" + (this._t('removeLink')) + "\"\n tabindex=\"-1\">\n <span class=\"simditor-icon simditor-icon-unlink\"></span>\n </a>\n </div>\n <div class=\"settings-field\">\n <label>" + (this._t('linkUrl')) + "</label>\n <input class=\"link-url\" type=\"text\"/>\n </div>\n</div>"; this.el.addClass('link-popover').append(tpl); this.textEl = this.el.find('.link-text'); this.urlEl = this.el.find('.link-url'); this.unlinkEl = this.el.find('.btn-unlink'); this.textEl.on('keyup', (function(_this) { @@ -3878,19 +3799,17 @@ return _this.target.attr('href', val); }; })(this)); $([this.urlEl[0], this.textEl[0]]).on('keydown', (function(_this) { return function(e) { + var range; if (e.which === 13 || e.which === 27 || (!e.shiftKey && e.which === 9 && $(e.target).hasClass('link-url'))) { e.preventDefault(); - return setTimeout(function() { - var range; - range = document.createRange(); - _this.editor.selection.setRangeAfter(_this.target, range); - _this.hide(); - return _this.editor.trigger('valuechanged'); - }, 0); + range = document.createRange(); + _this.editor.selection.setRangeAfter(_this.target, range); + _this.hide(); + return _this.editor.trigger('valuechanged'); } }; })(this)); return this.unlinkEl.on('click', (function(_this) { return function(e) { @@ -3937,17 +3856,17 @@ ImageButton.prototype.defaultImage = ''; ImageButton.prototype.needFocus = false; ImageButton.prototype._init = function() { - var item, j, len, ref; + var item, k, len, ref; if (this.editor.opts.imageButton) { if (Array.isArray(this.editor.opts.imageButton)) { this.menu = []; ref = this.editor.opts.imageButton; - for (j = 0, len = ref.length; j < len; j++) { - item = ref[j]; + for (k = 0, len = ref.length; k < len; k++) { + item = ref[k]; this.menu.push({ name: item + '-image', text: this._t(item + 'Image') }); } @@ -3974,26 +3893,24 @@ return function(e) { var $img, range; $img = $(e.currentTarget); range = document.createRange(); range.selectNode($img[0]); - _this.editor.selection.selectRange(range); + _this.editor.selection.range(range); if (!_this.editor.util.support.onselectionchange) { _this.editor.trigger('selectionchanged'); } return false; }; })(this)); - this.editor.body.on('mouseup', 'img:not([data-non-image])', (function(_this) { - return function(e) { - return false; - }; - })(this)); + this.editor.body.on('mouseup', 'img:not([data-non-image])', function(e) { + return false; + }); this.editor.on('selectionchanged.image', (function(_this) { return function() { var $contents, $img, range; - range = _this.editor.selection.getRange(); + range = _this.editor.selection.range(); if (range == null) { return; } $contents = $(range.cloneContents()).contents(); if ($contents.length === 1 && $contents.is('img:not([data-non-image])')) { @@ -4049,11 +3966,11 @@ ImageButton.__super__.renderMenu.call(this); return this._initUploader(); }; ImageButton.prototype._initUploader = function($uploadItem) { - var $input, createInput; + var $input, createInput, uploadProgress; if ($uploadItem == null) { $uploadItem = this.menuEl.find('.menu-item-upload-image'); } if (this.editor.uploader == null) { this.el.find('.btn-upload').remove(); @@ -4063,19 +3980,21 @@ createInput = (function(_this) { return function() { if ($input) { $input.remove(); } - return $input = $('<input type="file" title="' + _this._t('uploadImage') + '" accept="image/*">').appendTo($uploadItem); + return $input = $('<input/>', { + type: 'file', + title: _this._t('uploadImage'), + accept: 'image/*' + }).appendTo($uploadItem); }; })(this); createInput(); - $uploadItem.on('click mousedown', 'input[type=file]', (function(_this) { - return function(e) { - return e.stopPropagation(); - }; - })(this)); + $uploadItem.on('click mousedown', 'input[type=file]', function(e) { + return e.stopPropagation(); + }); $uploadItem.on('change', 'input[type=file]', (function(_this) { return function(e) { if (_this.editor.inputManager.focused) { _this.editor.uploader.upload($input, { inline: true @@ -4120,11 +4039,11 @@ } }); }); }; })(this)); - this.editor.uploader.on('uploadprogress', $.proxy(this.editor.util.throttle(function(e, file, loaded, total) { + uploadProgress = $.proxy(this.editor.util.throttle(function(e, file, loaded, total) { var $img, $mask, percent; if (!file.inline) { return; } $mask = file.img.data('mask'); @@ -4140,11 +4059,12 @@ percent = (percent * 100).toFixed(0); if (percent > 99) { percent = 99; } return $mask.find('.progress').height((100 - percent) + "%"); - }, 500), this)); + }, 500), this); + this.editor.uploader.on('uploadprogress', uploadProgress); this.editor.uploader.on('uploadsuccess', (function(_this) { return function(e, file, result) { var $img, $mask, msg; if (!file.inline) { return; @@ -4228,17 +4148,12 @@ } }; })(this)); }; - ImageButton.prototype.status = function($node) { - if ($node != null) { - this.setDisabled($node.is(this.disableTag)); - } - if (this.disabled) { - return true; - } + ImageButton.prototype._status = function() { + return this._disableStatus(); }; ImageButton.prototype.loadImage = function($img, src, callback) { var $mask, img, positionMask; positionMask = (function(_this) { @@ -4255,11 +4170,11 @@ }; })(this); $img.addClass('loading'); $mask = $img.data('mask'); if (!$mask) { - $mask = $('<div class="simditor-image-loading"><div class="progress"></div></div>').hide().appendTo(this.editor.wrapper); + $mask = $('<div class="simditor-image-loading">\n <div class="progress"></div>\n</div>').hide().appendTo(this.editor.wrapper); positionMask(); $img.data('mask', $mask); $mask.data('img', $img); } img = new Image(); @@ -4271,10 +4186,12 @@ } width = img.width; height = img.height; $img.attr({ src: src, + width: width, + height: height, 'data-image-size': width + ',' + height }).removeClass('loading'); if ($img.hasClass('uploading')) { _this.editor.util.reflow(_this.editor.body); positionMask(); @@ -4283,42 +4200,33 @@ $img.removeData('mask'); } return callback(img); }; })(this); - img.onerror = (function(_this) { - return function() { - callback(false); - $mask.remove(); - return $img.removeData('mask').removeClass('loading'); - }; - })(this); + img.onerror = function() { + callback(false); + $mask.remove(); + return $img.removeData('mask').removeClass('loading'); + }; return img.src = src; }; ImageButton.prototype.createImage = function(name) { - var $block, $img, $nextBlock, range; + var $img, range; if (name == null) { name = 'Image'; } if (!this.editor.inputManager.focused) { this.editor.focus(); } - range = this.editor.selection.getRange(); + range = this.editor.selection.range(); range.deleteContents(); - $block = this.editor.util.closestBlockEl(); - if ($block.is('p') && !this.editor.util.isEmptyNode($block)) { - $block = $('<p/>').append(this.editor.util.phBr).insertAfter($block); - this.editor.selection.setRangeAtStartOf($block, range); - } + this.editor.selection.range(range); $img = $('<img/>').attr('alt', name); range.insertNode($img[0]); - $nextBlock = $block.next('p'); - if (!($nextBlock.length > 0)) { - $nextBlock = $('<p/>').append(this.editor.util.phBr).insertAfter($block); - } - this.editor.selection.setRangeAtStartOf($nextBlock); + this.editor.selection.setRangeAfter($img, range); + this.editor.trigger('valuechanged'); return $img; }; ImageButton.prototype.command = function(src) { var $img; @@ -4352,11 +4260,11 @@ left: -4 }; ImagePopover.prototype.render = function() { var tpl; - tpl = "<div class=\"link-settings\">\n <div class=\"settings-field\">\n <label>" + (this._t('imageUrl')) + "</label>\n <input class=\"image-src\" type=\"text\" tabindex=\"1\" />\n <a class=\"btn-upload\" href=\"javascript:;\" title=\"" + (this._t('uploadImage')) + "\" tabindex=\"-1\">\n <span class=\"simditor-icon simditor-icon-upload\"></span>\n </a>\n </div>\n <div class='settings-field'>\n <label>" + (this._t('imageAlt')) + "</label>\n <input class=\"image-alt\" id=\"image-alt\" type=\"text\" tabindex=\"1\" />\n </div>\n <div class=\"settings-field\">\n <label>" + (this._t('imageSize')) + "</label>\n <input class=\"image-size\" id=\"image-width\" type=\"text\" tabindex=\"2\" />\n <span class=\"times\">×</span>\n <input class=\"image-size\" id=\"image-height\" type=\"text\" tabindex=\"3\" />\n <a class=\"btn-restore\" href=\"javascript:;\" title=\"" + (this._t('restoreImageSize')) + "\" tabindex=\"-1\">\n <span class=\"simditor-icon simditor-icon-undo\"></span>\n </a>\n </div>\n</div>"; + tpl = "<div class=\"link-settings\">\n <div class=\"settings-field\">\n <label>" + (this._t('imageUrl')) + "</label>\n <input class=\"image-src\" type=\"text\" tabindex=\"1\" />\n <a class=\"btn-upload\" href=\"javascript:;\"\n title=\"" + (this._t('uploadImage')) + "\" tabindex=\"-1\">\n <span class=\"simditor-icon simditor-icon-upload\"></span>\n </a>\n </div>\n <div class='settings-field'>\n <label>" + (this._t('imageAlt')) + "</label>\n <input class=\"image-alt\" id=\"image-alt\" type=\"text\" tabindex=\"1\" />\n </div>\n <div class=\"settings-field\">\n <label>" + (this._t('imageSize')) + "</label>\n <input class=\"image-size\" id=\"image-width\" type=\"text\" tabindex=\"2\" />\n <span class=\"times\">×</span>\n <input class=\"image-size\" id=\"image-height\" type=\"text\" tabindex=\"3\" />\n <a class=\"btn-restore\" href=\"javascript:;\"\n title=\"" + (this._t('restoreImageSize')) + "\" tabindex=\"-1\">\n <span class=\"simditor-icon simditor-icon-undo\"></span>\n </a>\n </div>\n</div>"; this.el.addClass('image-popover').append(tpl); this.srcEl = this.el.find('.image-src'); this.widthEl = this.el.find('#image-width'); this.heightEl = this.el.find('#image-height'); this.altEl = this.el.find('#image-alt'); @@ -4455,19 +4363,21 @@ createInput = (function(_this) { return function() { if (_this.input) { _this.input.remove(); } - return _this.input = $('<input type="file" title="' + _this._t('uploadImage') + '" accept="image/*">').appendTo($uploadBtn); + return _this.input = $('<input/>', { + type: 'file', + title: _this._t('uploadImage'), + accept: 'image/*' + }).appendTo($uploadBtn); }; })(this); createInput(); - this.el.on('click mousedown', 'input[type=file]', (function(_this) { - return function(e) { - return e.stopPropagation(); - }; - })(this)); + this.el.on('click mousedown', 'input[type=file]', function(e) { + return e.stopPropagation(); + }); return this.el.on('change', 'input[type=file]', (function(_this) { return function(e) { _this.editor.uploader.upload(_this.input, { inline: true, img: _this.target @@ -4485,20 +4395,22 @@ value = inputEl.val() * 1; if (!($.isNumeric(value) || value < 0)) { return; } if (inputEl.is(this.widthEl)) { + width = value; height = this.height * value / this.width; this.heightEl.val(height); } else { + height = value; width = this.width * value / this.height; this.widthEl.val(width); } if (!onlySetVal) { this.target.attr({ - width: width || value, - height: height || value + width: width, + height: height }); } return this.editor.trigger('valuechanged'); }; @@ -4519,10 +4431,13 @@ if (callback) { callback(false); } return; } + if (this.target.attr('src') === src) { + return; + } return this.button.loadImage(this.target, src, (function(_this) { return function(img) { var blob; if (!img) { return; @@ -4530,11 +4445,10 @@ if (_this.active) { _this.width = img.width; _this.height = img.height; _this.widthEl.val(_this.width); _this.heightEl.val(_this.height); - _this.target.removeAttr('width').removeAttr('height'); } if (/^data:image/.test(src)) { blob = _this.editor.util.dataURLtoBlob(src); blob.name = "Base64 Image.png"; _this.editor.uploader.upload(blob, { @@ -4589,13 +4503,11 @@ IndentButton.prototype._init = function() { this.title = this._t(this.name) + ' (Tab)'; return IndentButton.__super__._init.call(this); }; - IndentButton.prototype.status = function($node) { - return true; - }; + IndentButton.prototype._status = function() {}; IndentButton.prototype.command = function() { return this.editor.indentation.indent(); }; @@ -4619,13 +4531,11 @@ OutdentButton.prototype._init = function() { this.title = this._t(this.name) + ' (Shift + Tab)'; return OutdentButton.__super__._init.call(this); }; - OutdentButton.prototype.status = function($node) { - return true; - }; + OutdentButton.prototype._status = function() {}; OutdentButton.prototype.command = function() { return this.editor.indentation.indent(true); }; @@ -4646,17 +4556,15 @@ HrButton.prototype.icon = 'minus'; HrButton.prototype.htmlTag = 'hr'; - HrButton.prototype.status = function($node) { - return true; - }; + HrButton.prototype._status = function() {}; HrButton.prototype.command = function() { var $hr, $newBlock, $nextBlock, $rootBlock; - $rootBlock = this.editor.util.furthestBlockEl(); + $rootBlock = this.editor.selection.rootNodes().first(); $nextBlock = $rootBlock.next(); if ($nextBlock.length > 0) { this.editor.selection.save(); } else { $newBlock = $('<p/>').append(this.editor.util.phBr); @@ -4699,10 +4607,14 @@ $.merge(this.editor.formatter._allowedTags, ['thead', 'th', 'tbody', 'tr', 'td', 'colgroup', 'col']); $.extend(this.editor.formatter._allowedAttributes, { td: ['rowspan', 'colspan'], col: ['width'] }); + $.extend(this.editor.formatter._allowedStyles, { + td: ['text-align'], + th: ['text-align'] + }); this._initShortcuts(); this.editor.on('decorate', (function(_this) { return function(e, $el) { return $el.find('table').each(function(i, table) { return _this.decorate($(table)); @@ -4718,15 +4630,15 @@ })(this)); this.editor.on('selectionchanged.table', (function(_this) { return function(e) { var $container, range; _this.editor.body.find('.simditor-table td, .simditor-table th').removeClass('active'); - range = _this.editor.selection.getRange(); - if (range == null) { + range = _this.editor.selection.range(); + if (!range) { return; } - $container = $(range.commonAncestorContainer); + $container = _this.editor.selection.containerNode(); if (range.collapsed && $container.is('.simditor-table')) { if (_this.editor.selection.rangeAtStartOf($container)) { $container = $container.find('th:first'); } else { $container = $container.find('td:last'); @@ -4805,92 +4717,87 @@ var $colgroup, $resizeHandle, $wrapper; $wrapper = $table.parent('.simditor-table'); $colgroup = $table.find('colgroup'); if ($colgroup.length < 1) { $colgroup = $('<colgroup/>').prependTo($table); - $table.find('thead tr th').each((function(_this) { - return function(i, td) { - var $col; - return $col = $('<col/>').appendTo($colgroup); - }; - })(this)); + $table.find('thead tr th').each(function(i, td) { + var $col; + return $col = $('<col/>').appendTo($colgroup); + }); this.refreshTableWidth($table); } - $resizeHandle = $('<div class="simditor-resize-handle" contenteditable="false"></div>').appendTo($wrapper); - $wrapper.on('mousemove', 'td, th', (function(_this) { - return function(e) { - var $col, $td, index, ref, ref1, x; - if ($wrapper.hasClass('resizing')) { - return; - } - $td = $(e.currentTarget); - x = e.pageX - $(e.currentTarget).offset().left; - if (x < 5 && $td.prev().length > 0) { - $td = $td.prev(); - } - if ($td.next('td, th').length < 1) { - $resizeHandle.hide(); - return; - } - if ((ref = $resizeHandle.data('td')) != null ? ref.is($td) : void 0) { - $resizeHandle.show(); - return; - } - index = $td.parent().find('td, th').index($td); - $col = $colgroup.find('col').eq(index); - if ((ref1 = $resizeHandle.data('col')) != null ? ref1.is($col) : void 0) { - $resizeHandle.show(); - return; - } - return $resizeHandle.css('left', $td.position().left + $td.outerWidth() - 5).data('td', $td).data('col', $col).show(); - }; - })(this)); - $wrapper.on('mouseleave', (function(_this) { - return function(e) { - return $resizeHandle.hide(); - }; - })(this)); - return $wrapper.on('mousedown', '.simditor-resize-handle', (function(_this) { - return function(e) { - var $handle, $leftCol, $leftTd, $rightCol, $rightTd, minWidth, startHandleLeft, startLeftWidth, startRightWidth, startX, tableWidth; - $handle = $(e.currentTarget); - $leftTd = $handle.data('td'); - $leftCol = $handle.data('col'); - $rightTd = $leftTd.next('td, th'); - $rightCol = $leftCol.next('col'); - startX = e.pageX; - startLeftWidth = $leftTd.outerWidth() * 1; - startRightWidth = $rightTd.outerWidth() * 1; - startHandleLeft = parseFloat($handle.css('left')); - tableWidth = $leftTd.closest('table').width(); - minWidth = 50; - $(document).on('mousemove.simditor-resize-table', function(e) { - var deltaX, leftWidth, rightWidth; - deltaX = e.pageX - startX; - leftWidth = startLeftWidth + deltaX; + $resizeHandle = $('<div />', { + "class": 'simditor-resize-handle', + contenteditable: 'false' + }).appendTo($wrapper); + $wrapper.on('mousemove', 'td, th', function(e) { + var $col, $td, index, ref, ref1, x; + if ($wrapper.hasClass('resizing')) { + return; + } + $td = $(e.currentTarget); + x = e.pageX - $(e.currentTarget).offset().left; + if (x < 5 && $td.prev().length > 0) { + $td = $td.prev(); + } + if ($td.next('td, th').length < 1) { + $resizeHandle.hide(); + return; + } + if ((ref = $resizeHandle.data('td')) != null ? ref.is($td) : void 0) { + $resizeHandle.show(); + return; + } + index = $td.parent().find('td, th').index($td); + $col = $colgroup.find('col').eq(index); + if ((ref1 = $resizeHandle.data('col')) != null ? ref1.is($col) : void 0) { + $resizeHandle.show(); + return; + } + return $resizeHandle.css('left', $td.position().left + $td.outerWidth() - 5).data('td', $td).data('col', $col).show(); + }); + $wrapper.on('mouseleave', function(e) { + return $resizeHandle.hide(); + }); + return $wrapper.on('mousedown', '.simditor-resize-handle', function(e) { + var $handle, $leftCol, $leftTd, $rightCol, $rightTd, minWidth, startHandleLeft, startLeftWidth, startRightWidth, startX, tableWidth; + $handle = $(e.currentTarget); + $leftTd = $handle.data('td'); + $leftCol = $handle.data('col'); + $rightTd = $leftTd.next('td, th'); + $rightCol = $leftCol.next('col'); + startX = e.pageX; + startLeftWidth = $leftTd.outerWidth() * 1; + startRightWidth = $rightTd.outerWidth() * 1; + startHandleLeft = parseFloat($handle.css('left')); + tableWidth = $leftTd.closest('table').width(); + minWidth = 50; + $(document).on('mousemove.simditor-resize-table', function(e) { + var deltaX, leftWidth, rightWidth; + deltaX = e.pageX - startX; + leftWidth = startLeftWidth + deltaX; + rightWidth = startRightWidth - deltaX; + if (leftWidth < minWidth) { + leftWidth = minWidth; + deltaX = minWidth - startLeftWidth; rightWidth = startRightWidth - deltaX; - if (leftWidth < minWidth) { - leftWidth = minWidth; - deltaX = minWidth - startLeftWidth; - rightWidth = startRightWidth - deltaX; - } else if (rightWidth < minWidth) { - rightWidth = minWidth; - deltaX = startRightWidth - minWidth; - leftWidth = startLeftWidth + deltaX; - } - $leftCol.attr('width', (leftWidth / tableWidth * 100) + '%'); - $rightCol.attr('width', (rightWidth / tableWidth * 100) + '%'); - return $handle.css('left', startHandleLeft + deltaX); - }); - $(document).one('mouseup.simditor-resize-table', function(e) { - $(document).off('.simditor-resize-table'); - return $wrapper.removeClass('resizing'); - }); - $wrapper.addClass('resizing'); - return false; - }; - })(this)); + } else if (rightWidth < minWidth) { + rightWidth = minWidth; + deltaX = startRightWidth - minWidth; + leftWidth = startLeftWidth + deltaX; + } + $leftCol.attr('width', (leftWidth / tableWidth * 100) + '%'); + $rightCol.attr('width', (rightWidth / tableWidth * 100) + '%'); + return $handle.css('left', startHandleLeft + deltaX); + }); + $(document).one('mouseup.simditor-resize-table', function(e) { + $(document).off('.simditor-resize-table'); + return $wrapper.removeClass('resizing'); + }); + $wrapper.addClass('resizing'); + return false; + }); }; TableButton.prototype._initShortcuts = function() { this.editor.inputManager.addShortcut('ctrl+alt+up', (function(_this) { return function(e) { @@ -4934,11 +4841,11 @@ return $table.parent().replaceWith($table); }; TableButton.prototype.renderMenu = function() { var $table; - $("<div class=\"menu-create-table\">\n</div>\n<div class=\"menu-edit-table\">\n <ul>\n <li><a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\" href=\"javascript:;\" data-param=\"deleteRow\"><span>" + (this._t('deleteRow')) + "</span></a></li>\n <li><a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\" href=\"javascript:;\" data-param=\"insertRowAbove\"><span>" + (this._t('insertRowAbove')) + " ( Ctrl + Alt + ↑ )</span></a></li>\n <li><a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\" href=\"javascript:;\" data-param=\"insertRowBelow\"><span>" + (this._t('insertRowBelow')) + " ( Ctrl + Alt + ↓ )</span></a></li>\n <li><span class=\"separator\"></span></li>\n <li><a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\" href=\"javascript:;\" data-param=\"deleteCol\"><span>" + (this._t('deleteColumn')) + "</span></a></li>\n <li><a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\" href=\"javascript:;\" data-param=\"insertColLeft\"><span>" + (this._t('insertColumnLeft')) + " ( Ctrl + Alt + ← )</span></a></li>\n <li><a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\" href=\"javascript:;\" data-param=\"insertColRight\"><span>" + (this._t('insertColumnRight')) + " ( Ctrl + Alt + → )</span></a></li>\n <li><span class=\"separator\"></span></li>\n <li><a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\" href=\"javascript:;\" data-param=\"deleteTable\"><span>" + (this._t('deleteTable')) + "</span></a></li>\n </ul>\n</div>").appendTo(this.menuWrapper); + $("<div class=\"menu-create-table\">\n</div>\n<div class=\"menu-edit-table\">\n <ul>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"deleteRow\">\n <span>" + (this._t('deleteRow')) + "</span>\n </a>\n </li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"insertRowAbove\">\n <span>" + (this._t('insertRowAbove')) + " ( Ctrl + Alt + ↑ )</span>\n </a>\n </li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"insertRowBelow\">\n <span>" + (this._t('insertRowBelow')) + " ( Ctrl + Alt + ↓ )</span>\n </a>\n </li>\n <li><span class=\"separator\"></span></li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"deleteCol\">\n <span>" + (this._t('deleteColumn')) + "</span>\n </a>\n </li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"insertColLeft\">\n <span>" + (this._t('insertColumnLeft')) + " ( Ctrl + Alt + ← )</span>\n </a>\n </li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"insertColRight\">\n <span>" + (this._t('insertColumnRight')) + " ( Ctrl + Alt + → )</span>\n </a>\n </li>\n <li><span class=\"separator\"></span></li>\n <li>\n <a tabindex=\"-1\" unselectable=\"on\" class=\"menu-item\"\n href=\"javascript:;\" data-param=\"deleteTable\">\n <span>" + (this._t('deleteTable')) + "</span>\n </a>\n </li>\n </ul>\n</div>").appendTo(this.menuWrapper); this.createMenu = this.menuWrapper.find('.menu-create-table'); this.editMenu = this.menuWrapper.find('.menu-edit-table'); $table = this.createTable(6, 6).appendTo(this.createMenu); this.createMenu.on('mouseenter', 'td, th', (function(_this) { return function(e) { @@ -4952,15 +4859,13 @@ $trs = $trs.add($table.find('thead tr')); } return $trs.find("td:lt(" + num + "), th:lt(" + num + ")").addClass('selected'); }; })(this)); - this.createMenu.on('mouseleave', (function(_this) { - return function(e) { - return $(e.currentTarget).find('td, th').removeClass('selected'); - }; - })(this)); + this.createMenu.on('mouseleave', function(e) { + return $(e.currentTarget).find('td, th').removeClass('selected'); + }); return this.createMenu.on('mousedown', 'td, th', (function(_this) { return function(e) { var $closestBlock, $td, $tr, colNum, rowNum; _this.wrapper.removeClass('menu-on'); if (!_this.editor.inputManager.focused) { @@ -4972,11 +4877,11 @@ rowNum = $tr.prevAll('tr').length + 1; if ($tr.parent().is('tbody')) { rowNum += 1; } $table = _this.createTable(rowNum, colNum, true); - $closestBlock = _this.editor.util.closestBlockEl(); + $closestBlock = _this.editor.selection.blockNodes().last(); if (_this.editor.util.isEmptyNode($closestBlock)) { $closestBlock.replaceWith($table); } else { $closestBlock.after($table); } @@ -4987,18 +4892,18 @@ }; })(this)); }; TableButton.prototype.createTable = function(row, col, phBr) { - var $table, $tbody, $td, $thead, $tr, c, j, k, r, ref, ref1; + var $table, $tbody, $td, $thead, $tr, c, k, l, r, ref, ref1; $table = $('<table/>'); $thead = $('<thead/>').appendTo($table); $tbody = $('<tbody/>').appendTo($table); - for (r = j = 0, ref = row; 0 <= ref ? j < ref : j > ref; r = 0 <= ref ? ++j : --j) { + for (r = k = 0, ref = row; 0 <= ref ? k < ref : k > ref; r = 0 <= ref ? ++k : --k) { $tr = $('<tr/>'); $tr.appendTo(r === 0 ? $thead : $tbody); - for (c = k = 0, ref1 = col; 0 <= ref1 ? k < ref1 : k > ref1; c = 0 <= ref1 ? ++k : --k) { + for (c = l = 0, ref1 = col; 0 <= ref1 ? l < ref1 : l > ref1; c = 0 <= ref1 ? ++l : --l) { $td = $(r === 0 ? '<th/>' : '<td/>').appendTo($tr); if (phBr) { $td.append(this.editor.util.phBr); } } @@ -5008,17 +4913,15 @@ TableButton.prototype.refreshTableWidth = function($table) { var cols, tableWidth; tableWidth = $table.width(); cols = $table.find('col'); - return $table.find('thead tr th').each((function(_this) { - return function(i, td) { - var $col; - $col = cols.eq(i); - return $col.attr('width', ($(td).outerWidth() / tableWidth * 100) + '%'); - }; - })(this)); + return $table.find('thead tr th').each(function(i, td) { + var $col; + $col = cols.eq(i); + return $col.attr('width', ($(td).outerWidth() / tableWidth * 100) + '%'); + }); }; TableButton.prototype.setActive = function(active) { TableButton.__super__.setActive.call(this, active); if (active) { @@ -5029,17 +4932,15 @@ return this.editMenu.hide(); } }; TableButton.prototype._changeCellTag = function($tr, tagName) { - return $tr.find('td, th').each((function(_this) { - return function(i, cell) { - var $cell; - $cell = $(cell); - return $cell.replaceWith("<" + tagName + ">" + ($cell.html()) + "</" + tagName + ">"); - }; - })(this)); + return $tr.find('td, th').each(function(i, cell) { + var $cell; + $cell = $(cell); + return $cell.replaceWith("<" + tagName + ">" + ($cell.html()) + "</" + tagName + ">"); + }); }; TableButton.prototype.deleteRow = function($td) { var $newTr, $tr, index; $tr = $td.parent('tr'); @@ -5059,22 +4960,20 @@ return this.editor.selection.setRangeAtEndOf($newTr.find('td, th').eq(index)); } }; TableButton.prototype.insertRow = function($td, direction) { - var $newTr, $table, $tr, cellTag, colNum, i, index, j, ref; + var $newTr, $table, $tr, cellTag, colNum, i, index, k, ref; if (direction == null) { direction = 'after'; } $tr = $td.parent('tr'); $table = $tr.closest('table'); colNum = 0; - $table.find('tr').each((function(_this) { - return function(i, tr) { - return colNum = Math.max(colNum, $(tr).find('td').length); - }; - })(this)); + $table.find('tr').each(function(i, tr) { + return colNum = Math.max(colNum, $(tr).find('td').length); + }); index = $tr.find('td, th').index($td); $newTr = $('<tr/>'); cellTag = 'td'; if (direction === 'after' && $tr.parent().is('thead')) { $tr.parent().next('tbody').prepend($newTr); @@ -5084,34 +4983,34 @@ this._changeCellTag($tr, 'td'); cellTag = 'th'; } else { $tr[direction]($newTr); } - for (i = j = 1, ref = colNum; 1 <= ref ? j <= ref : j >= ref; i = 1 <= ref ? ++j : --j) { + for (i = k = 1, ref = colNum; 1 <= ref ? k <= ref : k >= ref; i = 1 <= ref ? ++k : --k) { $("<" + cellTag + "/>").append(this.editor.util.phBr).appendTo($newTr); } return this.editor.selection.setRangeAtStartOf($newTr.find('td, th').eq(index)); }; TableButton.prototype.deleteCol = function($td) { - var $newTd, $table, $tr, index; + var $newTd, $table, $tr, index, noOtherCol, noOtherRow; $tr = $td.parent('tr'); - if ($tr.closest('table').find('tr').length < 1 && $td.siblings('td, th').length < 1) { + noOtherRow = $tr.closest('table').find('tr').length < 2; + noOtherCol = $td.siblings('td, th').length < 1; + if (noOtherRow && noOtherCol) { return this.deleteTable($td); } else { index = $tr.find('td, th').index($td); $newTd = $td.next('td, th'); if (!($newTd.length > 0)) { $newTd = $tr.prev('td, th'); } $table = $tr.closest('table'); $table.find('col').eq(index).remove(); - $table.find('tr').each((function(_this) { - return function(i, tr) { - return $(tr).find('td, th').eq(index).remove(); - }; - })(this)); + $table.find('tr').each(function(i, tr) { + return $(tr).find('td, th').eq(index).remove(); + }); this.refreshTableWidth($table); return this.editor.selection.setRangeAtEndOf($newTd); } }; @@ -5152,13 +5051,12 @@ return this.editor.selection.setRangeAtStartOf($block); } }; TableButton.prototype.command = function(param) { - var $td, range; - range = this.editor.selection.getRange(); - $td = $(range.commonAncestorContainer).closest('td, th'); + var $td; + $td = this.editor.selection.containerNode.closest('td, th'); if (!($td.length > 0)) { return; } if (param === 'deleteRow') { this.deleteRow($td); @@ -5199,21 +5097,15 @@ StrikethroughButton.prototype.htmlTag = 'strike'; StrikethroughButton.prototype.disableTag = 'pre'; - StrikethroughButton.prototype.status = function($node) { + StrikethroughButton.prototype._activeStatus = function() { var active; - if ($node != null) { - this.setDisabled($node.is(this.disableTag)); - } - if (this.disabled) { - return true; - } active = document.queryCommandState('strikethrough') === true; this.setActive(active); - return active; + return this.active; }; StrikethroughButton.prototype.command = function() { document.execCommand('strikethrough'); if (!this.editor.util.support.oninput) { @@ -5237,11 +5129,11 @@ AlignmentButton.prototype.name = "alignment"; AlignmentButton.prototype.icon = 'align-left'; - AlignmentButton.prototype.htmlTag = 'p, h1, h2, h3, h4'; + AlignmentButton.prototype.htmlTag = 'p, h1, h2, h3, h4, td, th'; AlignmentButton.prototype._init = function() { this.menu = [ { name: 'left', @@ -5281,43 +5173,27 @@ } this.setIcon('align-' + align); return this.menuEl.find('.menu-item').show().end().find('.menu-item-' + align).hide(); }; - AlignmentButton.prototype.status = function($node) { - if ($node == null) { - return true; + AlignmentButton.prototype._status = function() { + this.nodes = this.editor.selection.nodes().filter(this.htmlTag); + if (this.nodes.length < 1) { + this.setDisabled(true); + return this.setActive(false); + } else { + this.setDisabled(false); + return this.setActive(true, this.nodes.first().css('text-align')); } - if (!this.editor.util.isBlockNode($node)) { - return; - } - this.setDisabled(!$node.is(this.htmlTag)); - if (this.disabled) { - this.setActive(false); - return true; - } - this.setActive(true, $node.css('text-align')); - return this.active; }; AlignmentButton.prototype.command = function(align) { - var $blockEls, $endBlock, $startBlock, block, endNode, j, len, range, ref, startNode; - if (['left', 'center', 'right'].indexOf(align) < 0) { - throw new Error("invalid " + align); + if (align !== 'left' && align !== 'center' && align !== 'right') { + throw new Error("simditor alignment button: invalid align " + align); } - range = this.editor.selection.getRange(); - startNode = range.startContainer; - endNode = range.endContainer; - $startBlock = this.editor.util.closestBlockEl(startNode); - $endBlock = this.editor.util.closestBlockEl(endNode); - this.editor.selection.save(); - $blockEls = $startBlock.is($endBlock) ? $startBlock : $startBlock.nextUntil($endBlock).addBack().add($endBlock); - ref = $blockEls.filter(this.htmlTag); - for (j = 0, len = ref.length; j < len; j++) { - block = ref[j]; - $(block).css('text-align', align === 'left' ? '' : align); - } - this.editor.selection.restore(); + this.nodes.css({ + 'text-align': align === 'left' ? '' : align + }); return this.editor.trigger('valuechanged'); }; return AlignmentButton;