// original execCommand function _nativeCommand(doc, key, val) { try { doc.execCommand(key, false, val); } catch(e) {} } // original queryCommandValue function _nativeCommandValue(doc, key) { var val = ''; try { val = doc.queryCommandValue(key); } catch (e) {} if (typeof val !== 'string') { val = ''; } return val; } // get current selection of a document function _getSel(doc) { var win = _getWin(doc); return _IERANGE ? doc.selection : win.getSelection(); } // get range of current selection function _getRng(doc) { var sel = _getSel(doc), rng; try { if (sel.rangeCount > 0) { rng = sel.getRangeAt(0); } else { rng = sel.createRange(); } } catch(e) {} if (_IERANGE && (!rng || (!rng.item && rng.parentElement().ownerDocument !== doc))) { return null; } return rng; } //将map的复合key转换成单一key function _singleKeyMap(map) { var newMap = {}, arr, v; _each(map, function(key, val) { arr = key.split(','); for (var i = 0, len = arr.length; i < len; i++) { v = arr[i]; newMap[v] = val; } }); return newMap; } //判断一个node是否有指定属性或CSS function _hasAttrOrCss(knode, map) { return _hasAttrOrCssByKey(knode, map, '*') || _hasAttrOrCssByKey(knode, map); } function _hasAttrOrCssByKey(knode, map, mapKey) { mapKey = mapKey || knode.name; if (knode.type !== 1) { return false; } var newMap = _singleKeyMap(map); if (!newMap[mapKey]) { return false; } var arr = newMap[mapKey].split(','); for (var i = 0, len = arr.length; i < len; i++) { var key = arr[i]; if (key === '*') { return true; } var match = /^(\.?)([^=]+)(?:=([^=]*))?$/.exec(key); var method = match[1] ? 'css' : 'attr'; key = match[2]; var val = match[3] || ''; if (val === '' && knode[method](key) !== '') { return true; } if (val !== '' && knode[method](key) === val) { return true; } } return false; } //删除一个node的属性和CSS function _removeAttrOrCss(knode, map) { if (knode.type != 1) { return; } _removeAttrOrCssByKey(knode, map, '*'); _removeAttrOrCssByKey(knode, map); } function _removeAttrOrCssByKey(knode, map, mapKey) { mapKey = mapKey || knode.name; if (knode.type !== 1) { return; } var newMap = _singleKeyMap(map); if (!newMap[mapKey]) { return; } var arr = newMap[mapKey].split(','), allFlag = false; for (var i = 0, len = arr.length; i < len; i++) { var key = arr[i]; if (key === '*') { allFlag = true; break; } var match = /^(\.?)([^=]+)(?:=([^=]*))?$/.exec(key); key = match[2]; if (match[1]) { key = _toCamel(key); if (knode[0].style[key]) { knode[0].style[key] = ''; } } else { knode.removeAttr(key); } } if (allFlag) { knode.remove(true); } } //取得最里面的element function _getInnerNode(knode) { var inner = knode; while (inner.first()) { inner = inner.first(); } return inner; } //最里面的element为inline element时返回true function _isEmptyNode(knode) { if (knode.type != 1 || knode.isSingle()) { return false; } return knode.html().replace(/<[^>]+>/g, '') === ''; } //merge two wrapper //a : //b : //result : function _mergeWrapper(a, b) { a = a.clone(true); var lastA = _getInnerNode(a), childA = a, merged = false; while (b) { while (childA) { if (childA.name === b.name) { _mergeAttrs(childA, b.attr(), b.css()); merged = true; } childA = childA.first(); } if (!merged) { lastA.append(b.clone(false)); } merged = false; b = b.first(); } return a; } //wrap and merge a node function _wrapNode(knode, wrapper) { wrapper = wrapper.clone(true); //node为text node时 if (knode.type == 3) { _getInnerNode(wrapper).append(knode.clone(false)); knode.replaceWith(wrapper); return wrapper; } //node为element时 //取得node的wrapper var nodeWrapper = knode, child; while ((child = knode.first()) && child.children().length == 1) { knode = child; } //将node的子节点纳入在一个documentFragment里 child = knode.first(); var frag = knode.doc.createDocumentFragment(); while (child) { frag.appendChild(child[0]); child = child.next(); } wrapper = _mergeWrapper(nodeWrapper, wrapper); if (frag.firstChild) { _getInnerNode(wrapper).append(frag); } nodeWrapper.replaceWith(wrapper); return wrapper; } //merge attributes and styles function _mergeAttrs(knode, attrs, styles) { _each(attrs, function(key, val) { if (key !== 'style') { knode.attr(key, val); } }); _each(styles, function(key, val) { knode.css(key, val); }); } // 判断node是否在pre、style、script里 function _inPreElement(knode) { while (knode && knode.name != 'body') { if (_PRE_TAG_MAP[knode.name] || knode.name == 'div' && knode.hasClass('ke-script')) { return true; } knode = knode.parent(); } return false; } // create KCmd class function KCmd(range) { this.init(range); } _extend(KCmd, { init : function(range) { var self = this, doc = range.doc; self.doc = doc; self.win = _getWin(doc); self.sel = _getSel(doc); self.range = range; }, selection : function(forceReset) { var self = this, doc = self.doc, rng = _getRng(doc); self.sel = _getSel(doc); if (rng) { self.range = _range(rng); if (K(self.range.startContainer).name == 'html') { self.range.selectNodeContents(doc.body).collapse(false); } return self; } if (forceReset) { self.range.selectNodeContents(doc.body).collapse(false); } return self; }, select : function(hasDummy) { hasDummy = _undef(hasDummy, true); var self = this, sel = self.sel, range = self.range.cloneRange().shrink(), sc = range.startContainer, so = range.startOffset, ec = range.endContainer, eo = range.endOffset, doc = _getDoc(sc), win = self.win, rng, hasU200b = false; // tag内部无内容时选中tag内部,[] if (hasDummy && sc.nodeType == 1 && range.collapsed) { if (_IERANGE) { var dummy = K(' ', doc); range.insertNode(dummy[0]); rng = doc.body.createTextRange(); try { rng.moveToElementText(dummy[0]); } catch(ex) {} rng.collapse(false); rng.select(); dummy.remove(); win.focus(); return self; } if (_WEBKIT) { var children = sc.childNodes; if (K(sc).isInline() || so > 0 && K(children[so - 1]).isInline() || children[so] && K(children[so]).isInline()) { range.insertNode(doc.createTextNode('\u200B')); hasU200b = true; } } } //other case if (_IERANGE) { try { rng = range.get(true); rng.select(); } catch(e) {} } else { if (hasU200b) { range.collapse(false); } rng = range.get(true); // Bugfix: firefox browser multiple image upload if (sel != null) { sel.removeAllRanges(); sel.addRange(rng); } // Bugfix: https://github.com/kindsoft/kindeditor/issues/54 if (doc !== document) { var pos = K(rng.endContainer).pos(); win.scrollTo(pos.x, pos.y); } } win.focus(); return self; }, wrap : function(val) { var self = this, doc = self.doc, range = self.range, wrapper; wrapper = K(val, doc); // collapsed=true if (range.collapsed) { range.shrink(); range.insertNode(wrapper[0]).selectNodeContents(wrapper[0]); return self; } // block wrapper if (wrapper.isBlock()) { var copyWrapper = wrapper.clone(true), child = copyWrapper; // find inner element while (child.first()) { child = child.first(); } child.append(range.extractContents()); range.insertNode(copyWrapper[0]).selectNode(copyWrapper[0]); return self; } // collapsed=false range.enlarge(); var bookmark = range.createBookmark(), ancestor = range.commonAncestor(), isStart = false; K(ancestor).scan(function(node) { if (!isStart && node == bookmark.start) { isStart = true; return; } if (isStart) { if (node == bookmark.end) { return false; } var knode = K(node); if (_inPreElement(knode)) { return; } if (knode.type == 3 && _trim(node.nodeValue).length > 0) { // textNode为唯一的子节点时,重新设置node var parent; while ((parent = knode.parent()) && parent.isStyle() && parent.children().length == 1) { knode = parent; } _wrapNode(knode, wrapper); } } }); range.moveToBookmark(bookmark); return self; }, split : function(isStart, map) { var range = this.range, doc = range.doc; //get parent node var tempRange = range.cloneRange().collapse(isStart); var node = tempRange.startContainer, pos = tempRange.startOffset, parent = node.nodeType == 3 ? node.parentNode : node, needSplit = false, knode; while (parent && parent.parentNode) { knode = K(parent); if (map) { if (!knode.isStyle()) { break; } if (!_hasAttrOrCss(knode, map)) { break; } } else { if (_NOSPLIT_TAG_MAP[knode.name]) { break; } } needSplit = true; parent = parent.parentNode; } //split parent node if (needSplit) { var dummy = doc.createElement('span'); range.cloneRange().collapse(!isStart).insertNode(dummy); if (isStart) { tempRange.setStartBefore(parent.firstChild).setEnd(node, pos); } else { tempRange.setStart(node, pos).setEndAfter(parent.lastChild); } var frag = tempRange.extractContents(), first = frag.firstChild, last = frag.lastChild; if (isStart) { tempRange.insertNode(frag); range.setStartAfter(last).setEndBefore(dummy); } else { parent.appendChild(frag); range.setStartBefore(dummy).setEndBefore(first); } //调整endOffset var dummyParent = dummy.parentNode; if (dummyParent == range.endContainer) { var prev = K(dummy).prev(), next = K(dummy).next(); if (prev && next && prev.type == 3 && next.type == 3) { //dummy元素的左右都是textNode,fg range.setEnd(prev[0], prev[0].nodeValue.length); } else if (!isStart) { range.setEnd(range.endContainer, range.endOffset - 1); } } dummyParent.removeChild(dummy); } return this; }, remove : function(map) { var self = this, doc = self.doc, range = self.range; range.enlarge(); //

[123456789]

, remove strong if (range.startOffset === 0) { var ksc = K(range.startContainer), parent; while ((parent = ksc.parent()) && parent.isStyle() && parent.children().length == 1) { ksc = parent; } range.setStart(ksc[0], 0); //

[abcd

, remove style ksc = K(range.startContainer); if (ksc.isBlock()) { _removeAttrOrCss(ksc, map); } var kscp = ksc.parent(); if (kscp && kscp.isBlock()) { _removeAttrOrCss(kscp, map); } } var sc, so; // collapsed == true if (range.collapsed) { self.split(true, map); // remove empty element sc = range.startContainer; so = range.startOffset; if (so > 0) { var sb = K(sc.childNodes[so - 1]); if (sb && _isEmptyNode(sb)) { sb.remove(); range.setStart(sc, so - 1); } } var sa = K(sc.childNodes[so]); if (sa && _isEmptyNode(sa)) { sa.remove(); } // | if (_isEmptyNode(sc)) { range.startBefore(sc); sc.remove(); } range.collapse(true); return self; } // split range self.split(true, map); self.split(false, map); // insert dummy element var startDummy = doc.createElement('span'), endDummy = doc.createElement('span'); range.cloneRange().collapse(false).insertNode(endDummy); range.cloneRange().collapse(true).insertNode(startDummy); // select element var nodeList = [], cmpStart = false; K(range.commonAncestor()).scan(function(node) { if (!cmpStart && node == startDummy) { cmpStart = true; return; } if (node == endDummy) { return false; } if (cmpStart) { nodeList.push(node); } }); // remove dummy element K(startDummy).remove(); K(endDummy).remove(); // remove empty element sc = range.startContainer; so = range.startOffset; var ec = range.endContainer, eo = range.endOffset; if (so > 0) { var startBefore = K(sc.childNodes[so - 1]); if (startBefore && _isEmptyNode(startBefore)) { startBefore.remove(); range.setStart(sc, so - 1); if (sc == ec) { range.setEnd(ec, eo - 1); } } // abc[def]ghi,分割后HTML变成 // abc[def]ghi var startAfter = K(sc.childNodes[so]); if (startAfter && _isEmptyNode(startAfter)) { startAfter.remove(); if (sc == ec) { range.setEnd(ec, eo - 1); } } } var endAfter = K(ec.childNodes[range.endOffset]); if (endAfter && _isEmptyNode(endAfter)) { endAfter.remove(); } var bookmark = range.createBookmark(true); // remove attributes or styles _each(nodeList, function(i, node) { _removeAttrOrCss(K(node), map); }); range.moveToBookmark(bookmark); return self; }, commonNode : function(map) { var range = this.range; var ec = range.endContainer, eo = range.endOffset, node = (ec.nodeType == 3 || eo === 0) ? ec : ec.childNodes[eo - 1]; function find(node) { var child = node, parent = node; while (parent) { if (_hasAttrOrCss(K(parent), map)) { return K(parent); } parent = parent.parentNode; } while (child && (child = child.lastChild)) { if (_hasAttrOrCss(K(child), map)) { return K(child); } } return null; } var cNode = find(node); if (cNode) { return cNode; } //123|4567 //123|
if (node.nodeType == 1 || (ec.nodeType == 3 && eo === 0)) { var prev = K(node).prev(); if (prev) { return find(prev); } } return null; }, commonAncestor : function(tagName) { var range = this.range, sc = range.startContainer, so = range.startOffset, ec = range.endContainer, eo = range.endOffset, startNode = (sc.nodeType == 3 || so === 0) ? sc : sc.childNodes[so - 1], endNode = (ec.nodeType == 3 || eo === 0) ? ec : ec.childNodes[eo - 1]; function find(node) { while (node) { if (node.nodeType == 1) { if (node.tagName.toLowerCase() === tagName) { return node; } } node = node.parentNode; } return null; } var start = find(startNode), end = find(endNode); if (start && end && start === end) { return K(start); } return null; }, // Reference: document.queryCommandState // TODO state : function(key) { var self = this, doc = self.doc, bool = false; try { bool = doc.queryCommandState(key); } catch (e) {} return bool; }, // Reference: document.queryCommandValue val : function(key) { var self = this, doc = self.doc, range = self.range; function lc(val) { return val.toLowerCase(); } key = lc(key); var val = '', knode; if (key === 'fontfamily' || key === 'fontname') { val = _nativeCommandValue(doc, 'fontname'); val = val.replace(/['"]/g, ''); return lc(val); } if (key === 'formatblock') { val = _nativeCommandValue(doc, key); if (val === '') { knode = self.commonNode({'h1,h2,h3,h4,h5,h6,p,div,pre,address' : '*'}); if (knode) { val = knode.name; } } if (val === 'Normal') { val = 'p'; } return lc(val); } if (key === 'fontsize') { knode = self.commonNode({'*' : '.font-size'}); if (knode) { val = knode.css('font-size'); } return lc(val); } if (key === 'forecolor') { knode = self.commonNode({'*' : '.color'}); if (knode) { val = knode.css('color'); } val = _toHex(val); if (val === '') { val = 'default'; } return lc(val); } if (key === 'hilitecolor') { knode = self.commonNode({'*' : '.background-color'}); if (knode) { val = knode.css('background-color'); } val = _toHex(val); if (val === '') { val = 'default'; } return lc(val); } return val; }, toggle : function(wrapper, map) { var self = this; if (self.commonNode(map)) { self.remove(map); } else { self.wrap(wrapper); } return self.select(); }, bold : function() { return this.toggle('', { span : '.font-weight=bold', strong : '*', b : '*' }); }, italic : function() { return this.toggle('', { span : '.font-style=italic', em : '*', i : '*' }); }, underline : function() { return this.toggle('', { span : '.text-decoration=underline', u : '*' }); }, strikethrough : function() { return this.toggle('', { span : '.text-decoration=line-through', s : '*' }); }, forecolor : function(val) { return this.wrap('').select(); // return this.toggle('', { // span : '.color=' + val, // font : 'color' // }); }, hilitecolor : function(val) { return this.wrap('').select(); // return this.toggle('', { // span : '.background-color=' + val // }); }, fontsize : function(val) { return this.wrap('').select(); // return this.toggle('', { // span : '.font-size=' + val, // font : 'size' // }); }, fontname : function(val) { return this.fontfamily(val); }, fontfamily : function(val) { return this.wrap('').select(); // return this.toggle('', { // span : '.font-family=' + val, // font : 'face' // }); }, removeformat : function() { var map = { '*' : '.font-weight,.font-style,.text-decoration,.color,.background-color,.font-size,.font-family,.text-indent' }, tags = _STYLE_TAG_MAP; _each(tags, function(key, val) { map[key] = '*'; }); this.remove(map); return this.select(); }, inserthtml : function(val, quickMode) { var self = this, range = self.range; if (val === '') { return self; } //if (_inPreElement(K(range.startContainer))) { // return self; //} // IE专用,优化性能 function pasteHtml(range, val) { val = '' + val; var rng = range.get(); if (rng.item) { rng.item(0).outerHTML = val; } else { rng.pasteHTML(val); } var temp = range.doc.getElementById('__kindeditor_temp_tag__'); temp.parentNode.removeChild(temp); var newRange = _toRange(rng); range.setEnd(newRange.endContainer, newRange.endOffset); range.collapse(false); self.select(false); } // 全浏览器兼容,在IE上速度慢 function insertHtml(range, val) { var doc = range.doc, frag = doc.createDocumentFragment(); K('@' + val, doc).each(function() { frag.appendChild(this); }); range.deleteContents(); range.insertNode(frag); range.collapse(false); self.select(false); } if (_IERANGE && quickMode) { try { pasteHtml(range, val); } catch(e) { insertHtml(range, val); } return self; } insertHtml(range, val); return self; }, hr : function() { return this.inserthtml('
'); }, print : function() { this.win.print(); return this; }, insertimage : function(url, title, width, height, border, align) { title = _undef(title, ''); border = _undef(border, 0); var html = ''; return self.inserthtml(html); } if (range.isControl()) { var node = K(range.startContainer.childNodes[range.startOffset]); html += '>'; node.after(K(html, doc)); node.next().append(node); range.selectNode(node[0]); return self.select(); } function setAttr(node, url, type) { K(node).attr('href', url).attr('data-ke-src', url); if (type) { K(node).attr('target', type); } else { K(node).removeAttr('target'); } } // Bugfix: https://github.com/kindsoft/kindeditor/issues/117 // [IE] 当两个A标签并排在一起中间没有别的内容,修改后面的链接地址时,前面的链接地址也被改掉。 var sc = range.startContainer, so = range.startOffset, ec = range.endContainer, eo = range.endOffset; if (sc.nodeType == 1 && sc === ec && so + 1 === eo) { var child = sc.childNodes[so]; if (child.nodeName.toLowerCase() == 'a') { setAttr(child, url, type); return self; } } _nativeCommand(doc, 'createlink', '__kindeditor_temp_url__'); K('a[href="__kindeditor_temp_url__"]', doc).each(function() { setAttr(this, url, type); }); return self; }, unlink : function() { var self = this, doc = self.doc, range = self.range; self.select(); if (range.collapsed) { var a = self.commonNode({ a : '*' }); if (a) { range.selectNode(a.get()); self.select(); } _nativeCommand(doc, 'unlink', null); if (_WEBKIT && K(range.startContainer).name === 'img') { var parent = K(range.startContainer).parent(); if (parent.name === 'a') { parent.remove(true); } } } else { _nativeCommand(doc, 'unlink', null); } return self; } }); _each(('formatblock,selectall,justifyleft,justifycenter,justifyright,justifyfull,insertorderedlist,' + 'insertunorderedlist,indent,outdent,subscript,superscript').split(','), function(i, name) { KCmd.prototype[name] = function(val) { var self = this; self.select(); _nativeCommand(self.doc, name, val); // Bugfix: [IE] 先选中图片后居中,再左对齐,光标跳到顶部 if (_IERANGE && _inArray(name, 'justifyleft,justifycenter,justifyright,justifyfull'.split(',')) >= 0) { self.selection(); } // 在webkit和firefox上需要重新选取range,否则有时候会报错 if (!_IERANGE || _inArray(name, 'formatblock,selectall,insertorderedlist,insertunorderedlist'.split(',')) >= 0) { self.selection(); } return self; }; }); _each('cut,copy,paste'.split(','), function(i, name) { KCmd.prototype[name] = function() { var self = this; if (!self.doc.queryCommandSupported(name)) { throw 'not supported'; } self.select(); _nativeCommand(self.doc, name, null); return self; }; }); function _cmd(mixed) { // mixed is a node if (mixed.nodeName) { var doc = _getDoc(mixed); mixed = _range(doc).selectNodeContents(doc.body).collapse(false); } // mixed is a KRange return new KCmd(mixed); } K.CmdClass = KCmd; K.cmd = _cmd;