cmd.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  1. // original execCommand
  2. function _nativeCommand(doc, key, val) {
  3. try {
  4. doc.execCommand(key, false, val);
  5. } catch(e) {}
  6. }
  7. // original queryCommandValue
  8. function _nativeCommandValue(doc, key) {
  9. var val = '';
  10. try {
  11. val = doc.queryCommandValue(key);
  12. } catch (e) {}
  13. if (typeof val !== 'string') {
  14. val = '';
  15. }
  16. return val;
  17. }
  18. // get current selection of a document
  19. function _getSel(doc) {
  20. var win = _getWin(doc);
  21. return _IERANGE ? doc.selection : win.getSelection();
  22. }
  23. // get range of current selection
  24. function _getRng(doc) {
  25. var sel = _getSel(doc), rng;
  26. try {
  27. if (sel.rangeCount > 0) {
  28. rng = sel.getRangeAt(0);
  29. } else {
  30. rng = sel.createRange();
  31. }
  32. } catch(e) {}
  33. if (_IERANGE && (!rng || (!rng.item && rng.parentElement().ownerDocument !== doc))) {
  34. return null;
  35. }
  36. return rng;
  37. }
  38. //将map的复合key转换成单一key
  39. function _singleKeyMap(map) {
  40. var newMap = {}, arr, v;
  41. _each(map, function(key, val) {
  42. arr = key.split(',');
  43. for (var i = 0, len = arr.length; i < len; i++) {
  44. v = arr[i];
  45. newMap[v] = val;
  46. }
  47. });
  48. return newMap;
  49. }
  50. //判断一个node是否有指定属性或CSS
  51. function _hasAttrOrCss(knode, map) {
  52. return _hasAttrOrCssByKey(knode, map, '*') || _hasAttrOrCssByKey(knode, map);
  53. }
  54. function _hasAttrOrCssByKey(knode, map, mapKey) {
  55. mapKey = mapKey || knode.name;
  56. if (knode.type !== 1) {
  57. return false;
  58. }
  59. var newMap = _singleKeyMap(map);
  60. if (!newMap[mapKey]) {
  61. return false;
  62. }
  63. var arr = newMap[mapKey].split(',');
  64. for (var i = 0, len = arr.length; i < len; i++) {
  65. var key = arr[i];
  66. if (key === '*') {
  67. return true;
  68. }
  69. var match = /^(\.?)([^=]+)(?:=([^=]*))?$/.exec(key);
  70. var method = match[1] ? 'css' : 'attr';
  71. key = match[2];
  72. var val = match[3] || '';
  73. if (val === '' && knode[method](key) !== '') {
  74. return true;
  75. }
  76. if (val !== '' && knode[method](key) === val) {
  77. return true;
  78. }
  79. }
  80. return false;
  81. }
  82. //删除一个node的属性和CSS
  83. function _removeAttrOrCss(knode, map) {
  84. if (knode.type != 1) {
  85. return;
  86. }
  87. _removeAttrOrCssByKey(knode, map, '*');
  88. _removeAttrOrCssByKey(knode, map);
  89. }
  90. function _removeAttrOrCssByKey(knode, map, mapKey) {
  91. mapKey = mapKey || knode.name;
  92. if (knode.type !== 1) {
  93. return;
  94. }
  95. var newMap = _singleKeyMap(map);
  96. if (!newMap[mapKey]) {
  97. return;
  98. }
  99. var arr = newMap[mapKey].split(','), allFlag = false;
  100. for (var i = 0, len = arr.length; i < len; i++) {
  101. var key = arr[i];
  102. if (key === '*') {
  103. allFlag = true;
  104. break;
  105. }
  106. var match = /^(\.?)([^=]+)(?:=([^=]*))?$/.exec(key);
  107. key = match[2];
  108. if (match[1]) {
  109. key = _toCamel(key);
  110. if (knode[0].style[key]) {
  111. knode[0].style[key] = '';
  112. }
  113. } else {
  114. knode.removeAttr(key);
  115. }
  116. }
  117. if (allFlag) {
  118. knode.remove(true);
  119. }
  120. }
  121. //取得最里面的element
  122. function _getInnerNode(knode) {
  123. var inner = knode;
  124. while (inner.first()) {
  125. inner = inner.first();
  126. }
  127. return inner;
  128. }
  129. //最里面的element为inline element时返回true
  130. function _isEmptyNode(knode) {
  131. if (knode.type != 1 || knode.isSingle()) {
  132. return false;
  133. }
  134. return knode.html().replace(/<[^>]+>/g, '') === '';
  135. }
  136. //merge two wrapper
  137. //a : <span><strong></strong></span>
  138. //b : <strong><em></em></strong>
  139. //result : <span><strong><em></em></strong></span>
  140. function _mergeWrapper(a, b) {
  141. a = a.clone(true);
  142. var lastA = _getInnerNode(a), childA = a, merged = false;
  143. while (b) {
  144. while (childA) {
  145. if (childA.name === b.name) {
  146. _mergeAttrs(childA, b.attr(), b.css());
  147. merged = true;
  148. }
  149. childA = childA.first();
  150. }
  151. if (!merged) {
  152. lastA.append(b.clone(false));
  153. }
  154. merged = false;
  155. b = b.first();
  156. }
  157. return a;
  158. }
  159. //wrap and merge a node
  160. function _wrapNode(knode, wrapper) {
  161. wrapper = wrapper.clone(true);
  162. //node为text node时
  163. if (knode.type == 3) {
  164. _getInnerNode(wrapper).append(knode.clone(false));
  165. knode.replaceWith(wrapper);
  166. return wrapper;
  167. }
  168. //node为element时
  169. //取得node的wrapper
  170. var nodeWrapper = knode, child;
  171. while ((child = knode.first()) && child.children().length == 1) {
  172. knode = child;
  173. }
  174. //将node的子节点纳入在一个documentFragment里
  175. child = knode.first();
  176. var frag = knode.doc.createDocumentFragment();
  177. while (child) {
  178. frag.appendChild(child[0]);
  179. child = child.next();
  180. }
  181. wrapper = _mergeWrapper(nodeWrapper, wrapper);
  182. if (frag.firstChild) {
  183. _getInnerNode(wrapper).append(frag);
  184. }
  185. nodeWrapper.replaceWith(wrapper);
  186. return wrapper;
  187. }
  188. //merge attributes and styles
  189. function _mergeAttrs(knode, attrs, styles) {
  190. _each(attrs, function(key, val) {
  191. if (key !== 'style') {
  192. knode.attr(key, val);
  193. }
  194. });
  195. _each(styles, function(key, val) {
  196. knode.css(key, val);
  197. });
  198. }
  199. // 判断node是否在pre、style、script里
  200. function _inPreElement(knode) {
  201. while (knode && knode.name != 'body') {
  202. if (_PRE_TAG_MAP[knode.name] || knode.name == 'div' && knode.hasClass('ke-script')) {
  203. return true;
  204. }
  205. knode = knode.parent();
  206. }
  207. return false;
  208. }
  209. // create KCmd class
  210. function KCmd(range) {
  211. this.init(range);
  212. }
  213. _extend(KCmd, {
  214. init : function(range) {
  215. var self = this, doc = range.doc;
  216. self.doc = doc;
  217. self.win = _getWin(doc);
  218. self.sel = _getSel(doc);
  219. self.range = range;
  220. },
  221. selection : function(forceReset) {
  222. var self = this, doc = self.doc, rng = _getRng(doc);
  223. self.sel = _getSel(doc);
  224. if (rng) {
  225. self.range = _range(rng);
  226. if (K(self.range.startContainer).name == 'html') {
  227. self.range.selectNodeContents(doc.body).collapse(false);
  228. }
  229. return self;
  230. }
  231. if (forceReset) {
  232. self.range.selectNodeContents(doc.body).collapse(false);
  233. }
  234. return self;
  235. },
  236. select : function(hasDummy) {
  237. hasDummy = _undef(hasDummy, true);
  238. var self = this, sel = self.sel, range = self.range.cloneRange().shrink(),
  239. sc = range.startContainer, so = range.startOffset,
  240. ec = range.endContainer, eo = range.endOffset,
  241. doc = _getDoc(sc), win = self.win, rng, hasU200b = false;
  242. // tag内部无内容时选中tag内部,<tagName>[]</tagName>
  243. if (hasDummy && sc.nodeType == 1 && range.collapsed) {
  244. if (_IERANGE) {
  245. var dummy = K('<span>&nbsp;</span>', doc);
  246. range.insertNode(dummy[0]);
  247. rng = doc.body.createTextRange();
  248. try {
  249. rng.moveToElementText(dummy[0]);
  250. } catch(ex) {}
  251. rng.collapse(false);
  252. rng.select();
  253. dummy.remove();
  254. win.focus();
  255. return self;
  256. }
  257. if (_WEBKIT) {
  258. var children = sc.childNodes;
  259. if (K(sc).isInline() || so > 0 && K(children[so - 1]).isInline() || children[so] && K(children[so]).isInline()) {
  260. range.insertNode(doc.createTextNode('\u200B'));
  261. hasU200b = true;
  262. }
  263. }
  264. }
  265. //other case
  266. if (_IERANGE) {
  267. try {
  268. rng = range.get(true);
  269. rng.select();
  270. } catch(e) {}
  271. } else {
  272. if (hasU200b) {
  273. range.collapse(false);
  274. }
  275. rng = range.get(true);
  276. sel.removeAllRanges();
  277. sel.addRange(rng);
  278. // Bugfix: https://github.com/kindsoft/kindeditor/issues/54
  279. if (doc !== document) {
  280. var pos = K(rng.endContainer).pos();
  281. win.scrollTo(pos.x, pos.y);
  282. }
  283. }
  284. win.focus();
  285. return self;
  286. },
  287. wrap : function(val) {
  288. var self = this, doc = self.doc, range = self.range, wrapper;
  289. wrapper = K(val, doc);
  290. // collapsed=true
  291. if (range.collapsed) {
  292. range.shrink();
  293. range.insertNode(wrapper[0]).selectNodeContents(wrapper[0]);
  294. return self;
  295. }
  296. // block wrapper
  297. if (wrapper.isBlock()) {
  298. var copyWrapper = wrapper.clone(true), child = copyWrapper;
  299. // find inner element
  300. while (child.first()) {
  301. child = child.first();
  302. }
  303. child.append(range.extractContents());
  304. range.insertNode(copyWrapper[0]).selectNode(copyWrapper[0]);
  305. return self;
  306. }
  307. // collapsed=false
  308. range.enlarge();
  309. var bookmark = range.createBookmark(), ancestor = range.commonAncestor(), isStart = false;
  310. K(ancestor).scan(function(node) {
  311. if (!isStart && node == bookmark.start) {
  312. isStart = true;
  313. return;
  314. }
  315. if (isStart) {
  316. if (node == bookmark.end) {
  317. return false;
  318. }
  319. var knode = K(node);
  320. if (_inPreElement(knode)) {
  321. return;
  322. }
  323. if (knode.type == 3 && _trim(node.nodeValue).length > 0) {
  324. // textNode为唯一的子节点时,重新设置node
  325. var parent;
  326. while ((parent = knode.parent()) && parent.isStyle() && parent.children().length == 1) {
  327. knode = parent;
  328. }
  329. _wrapNode(knode, wrapper);
  330. }
  331. }
  332. });
  333. range.moveToBookmark(bookmark);
  334. return self;
  335. },
  336. split : function(isStart, map) {
  337. var range = this.range, doc = range.doc;
  338. //get parent node
  339. var tempRange = range.cloneRange().collapse(isStart);
  340. var node = tempRange.startContainer, pos = tempRange.startOffset,
  341. parent = node.nodeType == 3 ? node.parentNode : node,
  342. needSplit = false, knode;
  343. while (parent && parent.parentNode) {
  344. knode = K(parent);
  345. if (map) {
  346. if (!knode.isStyle()) {
  347. break;
  348. }
  349. if (!_hasAttrOrCss(knode, map)) {
  350. break;
  351. }
  352. } else {
  353. if (_NOSPLIT_TAG_MAP[knode.name]) {
  354. break;
  355. }
  356. }
  357. needSplit = true;
  358. parent = parent.parentNode;
  359. }
  360. //split parent node
  361. if (needSplit) {
  362. var dummy = doc.createElement('span');
  363. range.cloneRange().collapse(!isStart).insertNode(dummy);
  364. if (isStart) {
  365. tempRange.setStartBefore(parent.firstChild).setEnd(node, pos);
  366. } else {
  367. tempRange.setStart(node, pos).setEndAfter(parent.lastChild);
  368. }
  369. var frag = tempRange.extractContents(),
  370. first = frag.firstChild, last = frag.lastChild;
  371. if (isStart) {
  372. tempRange.insertNode(frag);
  373. range.setStartAfter(last).setEndBefore(dummy);
  374. } else {
  375. parent.appendChild(frag);
  376. range.setStartBefore(dummy).setEndBefore(first);
  377. }
  378. //调整endOffset
  379. var dummyParent = dummy.parentNode;
  380. if (dummyParent == range.endContainer) {
  381. var prev = K(dummy).prev(), next = K(dummy).next();
  382. if (prev && next && prev.type == 3 && next.type == 3) {
  383. //dummy元素的左右都是textNode,<strong>f<span></span>g</strong>
  384. range.setEnd(prev[0], prev[0].nodeValue.length);
  385. } else if (!isStart) {
  386. range.setEnd(range.endContainer, range.endOffset - 1);
  387. }
  388. }
  389. dummyParent.removeChild(dummy);
  390. }
  391. return this;
  392. },
  393. remove : function(map) {
  394. var self = this, doc = self.doc, range = self.range;
  395. range.enlarge();
  396. // <p><strong><em>[123456789]</em></strong></p>, remove strong
  397. if (range.startOffset === 0) {
  398. var ksc = K(range.startContainer), parent;
  399. while ((parent = ksc.parent()) && parent.isStyle() && parent.children().length == 1) {
  400. ksc = parent;
  401. }
  402. range.setStart(ksc[0], 0);
  403. // <p style="color:red;">[abcd</p>, remove style
  404. ksc = K(range.startContainer);
  405. if (ksc.isBlock()) {
  406. _removeAttrOrCss(ksc, map);
  407. }
  408. var kscp = ksc.parent();
  409. if (kscp && kscp.isBlock()) {
  410. _removeAttrOrCss(kscp, map);
  411. }
  412. }
  413. var sc, so;
  414. // collapsed == true
  415. if (range.collapsed) {
  416. self.split(true, map);
  417. // remove empty element
  418. sc = range.startContainer;
  419. so = range.startOffset;
  420. if (so > 0) {
  421. var sb = K(sc.childNodes[so - 1]);
  422. if (sb && _isEmptyNode(sb)) {
  423. sb.remove();
  424. range.setStart(sc, so - 1);
  425. }
  426. }
  427. var sa = K(sc.childNodes[so]);
  428. if (sa && _isEmptyNode(sa)) {
  429. sa.remove();
  430. }
  431. // <strong>|</strong>
  432. if (_isEmptyNode(sc)) {
  433. range.startBefore(sc);
  434. sc.remove();
  435. }
  436. range.collapse(true);
  437. return self;
  438. }
  439. // split range
  440. self.split(true, map);
  441. self.split(false, map);
  442. // insert dummy element
  443. var startDummy = doc.createElement('span'), endDummy = doc.createElement('span');
  444. range.cloneRange().collapse(false).insertNode(endDummy);
  445. range.cloneRange().collapse(true).insertNode(startDummy);
  446. // select element
  447. var nodeList = [], cmpStart = false;
  448. K(range.commonAncestor()).scan(function(node) {
  449. if (!cmpStart && node == startDummy) {
  450. cmpStart = true;
  451. return;
  452. }
  453. if (node == endDummy) {
  454. return false;
  455. }
  456. if (cmpStart) {
  457. nodeList.push(node);
  458. }
  459. });
  460. // remove dummy element
  461. K(startDummy).remove();
  462. K(endDummy).remove();
  463. // remove empty element
  464. sc = range.startContainer;
  465. so = range.startOffset;
  466. var ec = range.endContainer, eo = range.endOffset;
  467. if (so > 0) {
  468. var startBefore = K(sc.childNodes[so - 1]);
  469. if (startBefore && _isEmptyNode(startBefore)) {
  470. startBefore.remove();
  471. range.setStart(sc, so - 1);
  472. if (sc == ec) {
  473. range.setEnd(ec, eo - 1);
  474. }
  475. }
  476. // <b>abc[</b><b>def]</b><b>ghi</b>,分割后HTML变成
  477. // <b>abc</b>[<b></b><b>def</b>]<b>ghi</b>
  478. var startAfter = K(sc.childNodes[so]);
  479. if (startAfter && _isEmptyNode(startAfter)) {
  480. startAfter.remove();
  481. if (sc == ec) {
  482. range.setEnd(ec, eo - 1);
  483. }
  484. }
  485. }
  486. var endAfter = K(ec.childNodes[range.endOffset]);
  487. if (endAfter && _isEmptyNode(endAfter)) {
  488. endAfter.remove();
  489. }
  490. var bookmark = range.createBookmark(true);
  491. // remove attributes or styles
  492. _each(nodeList, function(i, node) {
  493. _removeAttrOrCss(K(node), map);
  494. });
  495. range.moveToBookmark(bookmark);
  496. return self;
  497. },
  498. commonNode : function(map) {
  499. var range = this.range;
  500. var ec = range.endContainer, eo = range.endOffset,
  501. node = (ec.nodeType == 3 || eo === 0) ? ec : ec.childNodes[eo - 1];
  502. function find(node) {
  503. var child = node, parent = node;
  504. while (parent) {
  505. if (_hasAttrOrCss(K(parent), map)) {
  506. return K(parent);
  507. }
  508. parent = parent.parentNode;
  509. }
  510. while (child && (child = child.lastChild)) {
  511. if (_hasAttrOrCss(K(child), map)) {
  512. return K(child);
  513. }
  514. }
  515. return null;
  516. }
  517. var cNode = find(node);
  518. if (cNode) {
  519. return cNode;
  520. }
  521. //<strong>123</strong>|4567
  522. //<strong>123</strong>|<br />
  523. if (node.nodeType == 1 || (ec.nodeType == 3 && eo === 0)) {
  524. var prev = K(node).prev();
  525. if (prev) {
  526. return find(prev);
  527. }
  528. }
  529. return null;
  530. },
  531. commonAncestor : function(tagName) {
  532. var range = this.range,
  533. sc = range.startContainer, so = range.startOffset,
  534. ec = range.endContainer, eo = range.endOffset,
  535. startNode = (sc.nodeType == 3 || so === 0) ? sc : sc.childNodes[so - 1],
  536. endNode = (ec.nodeType == 3 || eo === 0) ? ec : ec.childNodes[eo - 1];
  537. function find(node) {
  538. while (node) {
  539. if (node.nodeType == 1) {
  540. if (node.tagName.toLowerCase() === tagName) {
  541. return node;
  542. }
  543. }
  544. node = node.parentNode;
  545. }
  546. return null;
  547. }
  548. var start = find(startNode), end = find(endNode);
  549. if (start && end && start === end) {
  550. return K(start);
  551. }
  552. return null;
  553. },
  554. // Reference: document.queryCommandState
  555. // TODO
  556. state : function(key) {
  557. var self = this, doc = self.doc, bool = false;
  558. try {
  559. bool = doc.queryCommandState(key);
  560. } catch (e) {}
  561. return bool;
  562. },
  563. // Reference: document.queryCommandValue
  564. val : function(key) {
  565. var self = this, doc = self.doc, range = self.range;
  566. function lc(val) {
  567. return val.toLowerCase();
  568. }
  569. key = lc(key);
  570. var val = '', knode;
  571. if (key === 'fontfamily' || key === 'fontname') {
  572. val = _nativeCommandValue(doc, 'fontname');
  573. val = val.replace(/['"]/g, '');
  574. return lc(val);
  575. }
  576. if (key === 'formatblock') {
  577. val = _nativeCommandValue(doc, key);
  578. if (val === '') {
  579. knode = self.commonNode({'h1,h2,h3,h4,h5,h6,p,div,pre,address' : '*'});
  580. if (knode) {
  581. val = knode.name;
  582. }
  583. }
  584. if (val === 'Normal') {
  585. val = 'p';
  586. }
  587. return lc(val);
  588. }
  589. if (key === 'fontsize') {
  590. knode = self.commonNode({'*' : '.font-size'});
  591. if (knode) {
  592. val = knode.css('font-size');
  593. }
  594. return lc(val);
  595. }
  596. if (key === 'forecolor') {
  597. knode = self.commonNode({'*' : '.color'});
  598. if (knode) {
  599. val = knode.css('color');
  600. }
  601. val = _toHex(val);
  602. if (val === '') {
  603. val = 'default';
  604. }
  605. return lc(val);
  606. }
  607. if (key === 'hilitecolor') {
  608. knode = self.commonNode({'*' : '.background-color'});
  609. if (knode) {
  610. val = knode.css('background-color');
  611. }
  612. val = _toHex(val);
  613. if (val === '') {
  614. val = 'default';
  615. }
  616. return lc(val);
  617. }
  618. return val;
  619. },
  620. toggle : function(wrapper, map) {
  621. var self = this;
  622. if (self.commonNode(map)) {
  623. self.remove(map);
  624. } else {
  625. self.wrap(wrapper);
  626. }
  627. return self.select();
  628. },
  629. bold : function() {
  630. return this.toggle('<strong></strong>', {
  631. span : '.font-weight=bold',
  632. strong : '*',
  633. b : '*'
  634. });
  635. },
  636. italic : function() {
  637. return this.toggle('<em></em>', {
  638. span : '.font-style=italic',
  639. em : '*',
  640. i : '*'
  641. });
  642. },
  643. underline : function() {
  644. return this.toggle('<u></u>', {
  645. span : '.text-decoration=underline',
  646. u : '*'
  647. });
  648. },
  649. strikethrough : function() {
  650. return this.toggle('<s></s>', {
  651. span : '.text-decoration=line-through',
  652. s : '*'
  653. });
  654. },
  655. forecolor : function(val) {
  656. return this.wrap('<span style="color:' + val + ';"></span>').select();
  657. // return this.toggle('<span style="color:' + val + ';"></span>', {
  658. // span : '.color=' + val,
  659. // font : 'color'
  660. // });
  661. },
  662. hilitecolor : function(val) {
  663. return this.wrap('<span style="background-color:' + val + ';"></span>').select();
  664. // return this.toggle('<span style="background-color:' + val + ';"></span>', {
  665. // span : '.background-color=' + val
  666. // });
  667. },
  668. fontsize : function(val) {
  669. return this.wrap('<span style="font-size:' + val + ';"></span>').select();
  670. // return this.toggle('<span style="font-size:' + val + ';"></span>', {
  671. // span : '.font-size=' + val,
  672. // font : 'size'
  673. // });
  674. },
  675. fontname : function(val) {
  676. return this.fontfamily(val);
  677. },
  678. fontfamily : function(val) {
  679. return this.wrap('<span style="font-family:' + val + ';"></span>').select();
  680. // return this.toggle('<span style="font-family:' + val + ';"></span>', {
  681. // span : '.font-family=' + val,
  682. // font : 'face'
  683. // });
  684. },
  685. removeformat : function() {
  686. var map = {
  687. '*' : '.font-weight,.font-style,.text-decoration,.color,.background-color,.font-size,.font-family,.text-indent'
  688. },
  689. tags = _STYLE_TAG_MAP;
  690. _each(tags, function(key, val) {
  691. map[key] = '*';
  692. });
  693. this.remove(map);
  694. return this.select();
  695. },
  696. inserthtml : function(val, quickMode) {
  697. var self = this, range = self.range;
  698. if (val === '') {
  699. return self;
  700. }
  701. //if (_inPreElement(K(range.startContainer))) {
  702. // return self;
  703. //}
  704. // IE专用,优化性能
  705. function pasteHtml(range, val) {
  706. val = '<img id="__kindeditor_temp_tag__" width="0" height="0" style="display:none;" />' + val;
  707. var rng = range.get();
  708. if (rng.item) {
  709. rng.item(0).outerHTML = val;
  710. } else {
  711. rng.pasteHTML(val);
  712. }
  713. var temp = range.doc.getElementById('__kindeditor_temp_tag__');
  714. temp.parentNode.removeChild(temp);
  715. var newRange = _toRange(rng);
  716. range.setEnd(newRange.endContainer, newRange.endOffset);
  717. range.collapse(false);
  718. self.select(false);
  719. }
  720. // 全浏览器兼容,在IE上速度慢
  721. function insertHtml(range, val) {
  722. var doc = range.doc,
  723. frag = doc.createDocumentFragment();
  724. K('@' + val, doc).each(function() {
  725. frag.appendChild(this);
  726. });
  727. range.deleteContents();
  728. range.insertNode(frag);
  729. range.collapse(false);
  730. self.select(false);
  731. }
  732. if (_IERANGE && quickMode) {
  733. try {
  734. pasteHtml(range, val);
  735. } catch(e) {
  736. insertHtml(range, val);
  737. }
  738. return self;
  739. }
  740. insertHtml(range, val);
  741. return self;
  742. },
  743. hr : function() {
  744. return this.inserthtml('<hr />');
  745. },
  746. print : function() {
  747. this.win.print();
  748. return this;
  749. },
  750. insertimage : function(url, title, width, height, border, align) {
  751. title = _undef(title, '');
  752. border = _undef(border, 0);
  753. var html = '<img src="' + _escape(url) + '" data-ke-src="' + _escape(url) + '" ';
  754. if (width) {
  755. html += 'width="' + _escape(width) + '" ';
  756. }
  757. if (height) {
  758. html += 'height="' + _escape(height) + '" ';
  759. }
  760. if (title) {
  761. html += 'title="' + _escape(title) + '" ';
  762. }
  763. if (align) {
  764. html += 'align="' + _escape(align) + '" ';
  765. }
  766. html += 'alt="' + _escape(title) + '" ';
  767. html += '/>';
  768. return this.inserthtml(html);
  769. },
  770. createlink : function(url, type) {
  771. var self = this, doc = self.doc, range = self.range;
  772. self.select();
  773. var a = self.commonNode({ a : '*' });
  774. if (a && !range.isControl()) {
  775. range.selectNode(a.get());
  776. self.select();
  777. }
  778. var html = '<a href="' + _escape(url) + '" data-ke-src="' + _escape(url) + '" ';
  779. if (type) {
  780. html += ' target="' + _escape(type) + '"';
  781. }
  782. if (range.collapsed) {
  783. html += '>' + _escape(url) + '</a>';
  784. return self.inserthtml(html);
  785. }
  786. if (range.isControl()) {
  787. var node = K(range.startContainer.childNodes[range.startOffset]);
  788. html += '></a>';
  789. node.after(K(html, doc));
  790. node.next().append(node);
  791. range.selectNode(node[0]);
  792. return self.select();
  793. }
  794. function setAttr(node, url, type) {
  795. K(node).attr('href', url).attr('data-ke-src', url);
  796. if (type) {
  797. K(node).attr('target', type);
  798. } else {
  799. K(node).removeAttr('target');
  800. }
  801. }
  802. // Bugfix: https://github.com/kindsoft/kindeditor/issues/117
  803. // [IE] 当两个A标签并排在一起中间没有别的内容,修改后面的链接地址时,前面的链接地址也被改掉。
  804. var sc = range.startContainer, so = range.startOffset,
  805. ec = range.endContainer, eo = range.endOffset;
  806. if (sc.nodeType == 1 && sc === ec && so + 1 === eo) {
  807. var child = sc.childNodes[so];
  808. if (child.nodeName.toLowerCase() == 'a') {
  809. setAttr(child, url, type);
  810. return self;
  811. }
  812. }
  813. _nativeCommand(doc, 'createlink', '__kindeditor_temp_url__');
  814. K('a[href="__kindeditor_temp_url__"]', doc).each(function() {
  815. setAttr(this, url, type);
  816. });
  817. return self;
  818. },
  819. unlink : function() {
  820. var self = this, doc = self.doc, range = self.range;
  821. self.select();
  822. if (range.collapsed) {
  823. var a = self.commonNode({ a : '*' });
  824. if (a) {
  825. range.selectNode(a.get());
  826. self.select();
  827. }
  828. _nativeCommand(doc, 'unlink', null);
  829. if (_WEBKIT && K(range.startContainer).name === 'img') {
  830. var parent = K(range.startContainer).parent();
  831. if (parent.name === 'a') {
  832. parent.remove(true);
  833. }
  834. }
  835. } else {
  836. _nativeCommand(doc, 'unlink', null);
  837. }
  838. return self;
  839. }
  840. });
  841. _each(('formatblock,selectall,justifyleft,justifycenter,justifyright,justifyfull,insertorderedlist,' +
  842. 'insertunorderedlist,indent,outdent,subscript,superscript').split(','), function(i, name) {
  843. KCmd.prototype[name] = function(val) {
  844. var self = this;
  845. self.select();
  846. _nativeCommand(self.doc, name, val);
  847. // Bugfix: [IE] 先选中图片后居中,再左对齐,光标跳到顶部
  848. if (_IERANGE && _inArray(name, 'justifyleft,justifycenter,justifyright,justifyfull'.split(',')) >= 0) {
  849. self.selection();
  850. }
  851. // 在webkit和firefox上需要重新选取range,否则有时候会报错
  852. if (!_IERANGE || _inArray(name, 'formatblock,selectall,insertorderedlist,insertunorderedlist'.split(',')) >= 0) {
  853. self.selection();
  854. }
  855. return self;
  856. };
  857. });
  858. _each('cut,copy,paste'.split(','), function(i, name) {
  859. KCmd.prototype[name] = function() {
  860. var self = this;
  861. if (!self.doc.queryCommandSupported(name)) {
  862. throw 'not supported';
  863. }
  864. self.select();
  865. _nativeCommand(self.doc, name, null);
  866. return self;
  867. };
  868. });
  869. function _cmd(mixed) {
  870. // mixed is a node
  871. if (mixed.nodeName) {
  872. var doc = _getDoc(mixed);
  873. mixed = _range(doc).selectNodeContents(doc.body).collapse(false);
  874. }
  875. // mixed is a KRange
  876. return new KCmd(mixed);
  877. }
  878. K.CmdClass = KCmd;
  879. K.cmd = _cmd;