vendor/assets/javascripts/simditor/simditor.js in simditor-2.3.4 vs vendor/assets/javascripts/simditor/simditor.js in simditor-2.3.5

- old
+ new

@@ -1,9 +1,9 @@ /*! -* Simditor v2.3.3 +* Simditor v2.3.5 * http://simditor.tower.im/ -* 2015-10-20 +* 2015-11-19 */ (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) { @@ -17,11 +17,11 @@ } else { root['Simditor'] = factory(jQuery,SimpleModule,simple.hotkeys,simple.uploader); } }(this, function ($, SimpleModule, simpleHotkeys, simpleUploader) { -var AlignmentButton, BlockquoteButton, BoldButton, Button, Clipboard, CodeButton, CodePopover, ColorButton, Formatter, HrButton, ImageButton, ImagePopover, IndentButton, Indentation, InputManager, ItalicButton, Keystroke, LinkButton, LinkPopover, ListButton, OrderListButton, OutdentButton, Popover, Selection, Simditor, StrikethroughButton, TableButton, TitleButton, Toolbar, UnderlineButton, UndoManager, UnorderListButton, Util, +var AlignmentButton, BlockquoteButton, BoldButton, Button, Clipboard, CodeButton, CodePopover, ColorButton, FontScaleButton, Formatter, HrButton, ImageButton, ImagePopover, IndentButton, Indentation, InputManager, ItalicButton, Keystroke, LinkButton, LinkPopover, ListButton, OrderListButton, OutdentButton, Popover, Selection, Simditor, StrikethroughButton, TableButton, TitleButton, Toolbar, UnderlineButton, UndoManager, UnorderListButton, Util, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, slice = [].slice; @@ -57,11 +57,11 @@ return _this._range = _this._selection.getRangeAt(0); }; })(this)); return this.editor.on('blur', (function(_this) { return function(e) { - return _this.clear(); + return _this.reset(); }; })(this)); }; Selection.prototype.reset = function() { @@ -478,11 +478,11 @@ a: ['href', 'target'], font: ['color'], code: ['class'] }, this.opts.allowedAttributes); this._allowedStyles = $.extend({ - span: ['color'], + span: ['color', 'font-size'], b: ['color'], i: ['color'], strong: ['color'], strike: ['color'], u: ['color'], @@ -601,11 +601,11 @@ } return $el; }; Formatter.prototype.cleanNode = function(node, recursive) { - var $childImg, $node, $p, $td, allowedAttributes, attr, contents, isDecoration, k, l, len, len1, n, ref, ref1, text, textNode; + var $blockEls, $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) { @@ -624,10 +624,18 @@ if ($node.is('a') && ($childImg = $node.find('img')).length > 0) { $node.replaceWith($childImg); $node = $childImg; contents = null; } + if ($node.is('td') && ($blockEls = $node.find(this.editor.util.blockNodes.join(','))).length > 0) { + $blockEls.each((function(_this) { + return function(i, blockEl) { + return $(blockEl).contents().unwrap(); + }; + })(this)); + contents = $node.contents(); + } if ($node.is('img') && $node.hasClass('uploading')) { $node.remove(); } if (!isDecoration) { allowedAttributes = this._allowedAttributes[$node[0].tagName.toLowerCase()]; @@ -901,13 +909,13 @@ range = _this.editor.selection._selection.getRangeAt(0); if (range.startContainer === _this.editor.body[0]) { if (_this.lastCaretPosition) { _this.editor.undoManager.caretPosition(_this.lastCaretPosition); } else { - $blockEl = _this.body.children.first(); + $blockEl = _this.editor.body.children().first(); range = document.createRange(); - _this.selection.setRangeAtStartOf($blockEl, range); + _this.editor.selection.setRangeAtStartOf($blockEl, range); } } _this.lastCaretPosition = null; _this.editor.triggerHandler('focus'); if (!_this.editor.util.support.onselectionchange) { @@ -1327,25 +1335,31 @@ })(this)); this.throttledPushState = this.editor.util.throttle((function(_this) { return function() { return _this._pushUndoState(); }; - })(this), 500); + })(this), 2000); this.editor.on('valuechanged', (function(_this) { return function(e, src) { if (src === 'undo' || src === 'redo') { return; } return _this.throttledPushState(); }; })(this)); this.editor.on('selectionchanged', (function(_this) { return function(e) { - _this.resetCaretPosition(); - return _this.update(); + return _this.resetCaretPosition(); }; })(this)); + this.editor.on('focus', (function(_this) { + return function(e) { + if (_this._stack.length === 0) { + return _this._pushUndoState(); + } + }; + })(this)); return this.editor.on('blur', (function(_this) { return function(e) { return _this.resetCaretPosition(); }; })(this)); @@ -1378,23 +1392,22 @@ } return this._endPosition; }; UndoManager.prototype._pushUndoState = function() { - var currentState, html; + var caret; if (this.editor.triggerHandler('pushundostate') === false) { return; } - currentState = this.currentState(); - html = this.editor.body.html(); - if (currentState && currentState.html === html) { + caret = this.caretPosition(); + if (!caret.start) { return; } this._index += 1; this._stack.length = this._index; this._stack.push({ - html: html, + html: this.editor.body.html(), caret: this.caretPosition() }); if (this._stack.length > this._capacity) { this._stack.shift(); return this._index -= 1; @@ -1462,11 +1475,11 @@ $parent.contents().each(function(i, child) { if (node === child || (index === i && i === 0)) { return false; } if (child.nodeType === Node.TEXT_NODE) { - if (!merging) { + if (!merging && child.nodeValue.length > 0) { offset += 1; merging = true; } } else { offset += 1; @@ -1911,15 +1924,14 @@ toolbarHeight = _this.wrapper.outerHeight(); _this.editor.placeholderEl.css('top', toolbarHeight); return true; }; })(this); + floatInitialized = null; $(window).on('resize.simditor-' + this.editor.id, function(e) { - var floatInitialized; - return floatInitialized = null; + return floatInitialized = initToolbarFloat(); }); - floatInitialized = null; $(window).on('scroll.simditor-' + this.editor.id, (function(_this) { return function(e) { var bottomEdge, scrollTop, topEdge; if (!_this.wrapper.is(':visible')) { return; @@ -2666,13 +2678,15 @@ 'uploading': '正在上传', 'indent': '向右缩进', 'outdent': '向左缩进', 'italic': '斜体文字', 'link': '插入链接', - 'text': '文本', 'linkText': '链接文字', - 'linkUrl': '地址', + 'linkUrl': '链接地址', + 'linkTarget': '打开方式', + 'openLinkInCurrentWindow': '在新窗口中打开', + 'openLinkInNewWindow': '在当前窗口中打开', 'removeLink': '移除链接', 'ol': '有序列表', 'ul': '无序列表', 'strikethrough': '删除线文字', 'table': '表格', @@ -2688,11 +2702,17 @@ 'underline': '下划线文字', 'alignment': '水平对齐', 'alignCenter': '居中', 'alignLeft': '居左', 'alignRight': '居右', - 'selectLanguage': '选择程序语言' + 'selectLanguage': '选择程序语言', + 'fontScale': '字体大小', + 'fontScaleXLarge': '超大字体', + 'fontScaleLarge': '大号字体', + 'fontScaleNormal': '正常大小', + 'fontScaleSmall': '小号字体', + 'fontScaleXSmall': '超小字体' }, 'en-US': { 'blockquote': 'Block Quote', 'bold': 'Bold', 'code': 'Code', @@ -2711,13 +2731,15 @@ 'uploading': 'Uploading', 'indent': 'Indent', 'outdent': 'Outdent', 'italic': 'Italic', 'link': 'Insert Link', - 'text': 'Text', - 'linkText': 'Link Text', - 'linkUrl': 'Link Url', + 'linkText': 'Text', + 'linkUrl': 'Url', + 'linkTarget': 'Target', + 'openLinkInCurrentWindow': 'Open link in current window', + 'openLinkInNewWindow': 'Open link in new window', 'removeLink': 'Remove Link', 'ol': 'Ordered List', 'ul': 'Unordered List', 'strikethrough': 'Strikethrough', 'table': 'Table', @@ -2733,11 +2755,17 @@ 'underline': 'Underline', 'alignment': 'Alignment', 'alignCenter': 'Align Center', 'alignLeft': 'Align Left', 'alignRight': 'Align Right', - 'selectLanguage': 'Select Language' + 'selectLanguage': 'Select Language', + 'fontScale': 'Font Size', + 'fontScaleXLarge': 'X Large Size', + 'fontScaleLarge': 'Large Size', + 'fontScaleNormal': 'Normal Size', + 'fontScaleSmall': 'Small Size', + 'fontScaleXSmall': 'X Small Size' } }; Button = (function(superClass) { extend(Button, superClass); @@ -3014,10 +3042,31 @@ })(this)); }; Popover.prototype.render = function() {}; + Popover.prototype._initLabelWidth = function() { + var $fields; + $fields = this.el.find('.settings-field'); + if (!($fields.length > 0)) { + return; + } + this._labelWidth = 0; + $fields.each((function(_this) { + return function(i, field) { + var $field, $label; + $field = $(field); + $label = $field.find('label'); + if (!($label.length > 0)) { + return; + } + return _this._labelWidth = Math.max(_this._labelWidth, $label.width()); + }; + })(this)); + return $fields.find('label').width(this._labelWidth); + }; + Popover.prototype.show = function($target, position) { if (position == null) { position = 'bottom'; } if ($target == null) { @@ -3039,10 +3088,13 @@ } else { this.active = true; this.el.css({ left: -9999 }).show(); + if (!this._labelWidth) { + this._initLabelWidth(); + } this.editor.util.reflow(); this.refresh(position); return this.trigger('popovershow'); } }; @@ -3184,10 +3236,109 @@ })(Button); Simditor.Toolbar.addButton(TitleButton); +FontScaleButton = (function(superClass) { + extend(FontScaleButton, superClass); + + function FontScaleButton() { + return FontScaleButton.__super__.constructor.apply(this, arguments); + } + + FontScaleButton.prototype.name = 'fontScale'; + + FontScaleButton.prototype.icon = 'font'; + + FontScaleButton.prototype.disableTag = 'pre'; + + FontScaleButton.prototype.htmlTag = 'span'; + + FontScaleButton.prototype.sizeMap = { + 'x-large': '1.5em', + 'large': '1.25em', + 'small': '.75em', + 'x-small': '.5em' + }; + + FontScaleButton.prototype._init = function() { + this.menu = [ + { + name: '150%', + text: this._t('fontScaleXLarge'), + param: '5' + }, { + name: '125%', + text: this._t('fontScaleLarge'), + param: '4' + }, { + name: '100%', + text: this._t('fontScaleNormal'), + param: '3' + }, { + name: '75%', + text: this._t('fontScaleSmall'), + param: '2' + }, { + name: '50%', + text: this._t('fontScaleXSmall'), + param: '1' + } + ]; + return FontScaleButton.__super__._init.call(this); + }; + + FontScaleButton.prototype._activeStatus = function() { + var active, endNode, endNodes, range, startNode, startNodes; + range = this.editor.selection.range(); + startNodes = this.editor.selection.startNodes(); + endNodes = this.editor.selection.endNodes(); + startNode = startNodes.filter('span[style*="font-size"]'); + endNode = endNodes.filter('span[style*="font-size"]'); + active = startNodes.length > 0 && endNodes.length > 0 && startNode.is(endNode); + this.setActive(active); + return this.active; + }; + + FontScaleButton.prototype.command = function(param) { + var $scales, containerNode, range; + range = this.editor.selection.range(); + if (range.collapsed) { + return; + } + document.execCommand('styleWithCSS', false, true); + document.execCommand('fontSize', false, param); + document.execCommand('styleWithCSS', false, false); + this.editor.selection.reset(); + this.editor.selection.range(); + containerNode = this.editor.selection.containerNode(); + if (containerNode[0].nodeType === Node.TEXT_NODE) { + $scales = containerNode.closest('span[style*="font-size"]'); + } else { + $scales = containerNode.find('span[style*="font-size"]'); + } + $scales.each((function(_this) { + return function(i, n) { + var $span, size; + $span = $(n); + size = n.style.fontSize; + if (/large|x-large|small|x-small/.test(size)) { + return $span.css('fontSize', _this.sizeMap[size]); + } else if (size === 'medium') { + return $span.replaceWith($span.contents()); + } + }; + })(this)); + return this.editor.trigger('valuechanged'); + }; + + return FontScaleButton; + +})(Button); + +Simditor.Toolbar.addButton(FontScaleButton); + BoldButton = (function(superClass) { extend(BoldButton, superClass); function BoldButton() { return BoldButton.__super__.constructor.apply(this, arguments); @@ -3638,12 +3789,28 @@ return this.popover = new CodePopover({ button: this }); }; + CodeButton.prototype._checkMode = function() { + var $blockNodes, range; + range = this.editor.selection.range(); + if (($blockNodes = $(range.cloneContents()).find(this.editor.util.blockNodes.join(','))) > 0 || (range.collapsed && this.editor.selection.startNodes().filter('code').length === 0)) { + this.inlineMode = false; + return this.htmlTag = 'pre'; + } else { + this.inlineMode = true; + return this.htmlTag = 'code'; + } + }; + CodeButton.prototype._status = function() { + this._checkMode(); CodeButton.__super__._status.call(this); + if (this.inlineMode) { + return; + } if (this.active) { return this.popover.show(this.node); } else { return this.popover.hide(); } @@ -3670,10 +3837,18 @@ } return $pre.wrapInner($code).removeAttr('data-lang'); }; CodeButton.prototype.command = function() { + if (this.inlineMode) { + return this._inlineCommand(); + } else { + return this._blockCommand(); + } + }; + + CodeButton.prototype._blockCommand = function() { var $rootNodes, clearCache, nodeCache, resultNodes; $rootNodes = this.editor.selection.rootNodes(); nodeCache = []; resultNodes = []; clearCache = (function(_this) { @@ -3705,10 +3880,28 @@ clearCache(); this.editor.selection.setRangeAtEndOf($(resultNodes).last()); return this.editor.trigger('valuechanged'); }; + CodeButton.prototype._inlineCommand = function() { + var $code, $contents, range; + range = this.editor.selection.range(); + if (this.active) { + range.selectNodeContents(this.node[0]); + this.editor.selection.save(range); + this.node.contents().unwrap(); + this.editor.selection.restore(); + } else { + $contents = $(range.extractContents()); + $code = $("<" + this.htmlTag + "/>").append($contents.contents()); + range.insertNode($code[0]); + range.selectNodeContents($code[0]); + this.editor.selection.range(range); + } + return this.editor.trigger('valuechanged'); + }; + return CodeButton; })(Button); CodePopover = (function(superClass) { @@ -3918,21 +4111,23 @@ 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')) + "\"\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>"; + tpl = "<div class=\"link-settings\">\n <div class=\"settings-field\">\n <label>" + (this._t('linkText')) + "</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 class=\"settings-field\">\n <label>" + (this._t('linkTarget')) + "</label>\n <select class=\"link-target\">\n <option value=\"_blank\">" + (this._t('openLinkInNewWindow')) + " (_blank)</option>\n <option value=\"_self\">" + (this._t('openLinkInCurrentWindow')) + " (_self)</option>\n </select>\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.selectTarget = this.el.find('.link-target'); this.textEl.on('keyup', (function(_this) { return function(e) { if (e.which === 13) { return; } - return _this.target.text(_this.textEl.val()); + _this.target.text(_this.textEl.val()); + return _this.editor.inputManager.throttledValueChanged(); }; })(this)); this.urlEl.on('keyup', (function(_this) { return function(e) { var val; @@ -3941,36 +4136,43 @@ } val = _this.urlEl.val(); if (!(/https?:\/\/|^\//ig.test(val) || !val)) { val = 'http://' + val; } - return _this.target.attr('href', val); + _this.target.attr('href', val); + return _this.editor.inputManager.throttledValueChanged(); }; })(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(); range = document.createRange(); _this.editor.selection.setRangeAfter(_this.target, range); _this.hide(); - return _this.editor.trigger('valuechanged'); + return _this.editor.inputManager.throttledValueChanged(); } }; })(this)); - return this.unlinkEl.on('click', (function(_this) { + this.unlinkEl.on('click', (function(_this) { return function(e) { var range, txtNode; txtNode = document.createTextNode(_this.target.text()); _this.target.replaceWith(txtNode); _this.hide(); range = document.createRange(); _this.editor.selection.setRangeAfter(txtNode, range); - return _this.editor.trigger('valuechanged'); + return _this.editor.inputManager.throttledValueChanged(); }; })(this)); + return this.selectTarget.on('change', (function(_this) { + return function(e) { + _this.target.attr('target', _this.selectTarget.val()); + return _this.editor.inputManager.throttledValueChanged(); + }; + })(this)); }; LinkPopover.prototype.show = function() { var args; args = 1 <= arguments.length ? slice.call(arguments, 0) : []; @@ -4985,13 +5187,26 @@ }; })(this)); }; TableButton.prototype.decorate = function($table) { + var $headRow, $tbody, $thead; if ($table.parent('.simditor-table').length > 0) { this.undecorate($table); } $table.wrap('<div class="simditor-table"></div>'); + if ($table.find('thead').length < 1) { + $thead = $('<thead />'); + $headRow = $table.find('tr').first(); + $thead.append($headRow); + this._changeCellTag($headRow, 'th'); + $tbody = $table.find('tbody'); + if ($tbody.length > 0) { + $tbody.before($thead); + } else { + $table.prepend($thead); + } + } this.initResize($table); return $table.parent(); }; TableButton.prototype.undecorate = function($table) {