API Docs for: 3.10.3
Show:

File: editor/js/editor-para.js

  1.  
  2. /**
  3. * Plugin for Editor to paragraph auto wrapping and correction.
  4. * @class Plugin.EditorPara
  5. * @extends Plugin.EditorParaBase
  6. * @constructor
  7. * @module editor
  8. * @submodule editor-para
  9. */
  10.  
  11.  
  12. var EditorPara = function() {
  13. EditorPara.superclass.constructor.apply(this, arguments);
  14. }, HOST = 'host', BODY = 'body', NODE_CHANGE = 'nodeChange', PARENT_NODE = 'parentNode',
  15. FIRST_P = BODY + ' > p', P = 'p', BR = '<br>', FC = 'firstChild', LI = 'li';
  16.  
  17.  
  18. Y.extend(EditorPara, Y.Plugin.EditorParaBase, {
  19. /**
  20. * nodeChange handler to handle fixing an empty document.
  21. * @private
  22. * @method _onNodeChange
  23. */
  24. _onNodeChange: function(e) {
  25. var host = this.get(HOST), inst = host.getInstance(),
  26. html, txt, par , d, sel, btag = inst.EditorSelection.DEFAULT_BLOCK_TAG,
  27. inHTML, txt2, childs, aNode, node2, top, n, sib, para2, prev,
  28. ps, br, item, p, imgs, t, LAST_CHILD = ':last-child', para, b, dir,
  29. lc, lc2, found = false, start;
  30.  
  31. switch (e.changedType) {
  32. case 'enter-up':
  33. para = ((this._lastPara) ? this._lastPara : e.changedNode);
  34. b = para.one('br.yui-cursor');
  35.  
  36. if (this._lastPara) {
  37. delete this._lastPara;
  38. }
  39.  
  40. if (b) {
  41. if (b.previous() || b.next()) {
  42. if (b.ancestor(P)) {
  43. b.remove();
  44. }
  45. }
  46. }
  47. if (!para.test(btag)) {
  48. para2 = para.ancestor(btag);
  49. if (para2) {
  50. para = para2;
  51. para2 = null;
  52. }
  53. }
  54. if (para.test(btag)) {
  55. prev = para.previous();
  56. if (prev) {
  57. lc = prev.one(LAST_CHILD);
  58. while (!found) {
  59. if (lc) {
  60. lc2 = lc.one(LAST_CHILD);
  61. if (lc2) {
  62. lc = lc2;
  63. } else {
  64. found = true;
  65. }
  66. } else {
  67. found = true;
  68. }
  69. }
  70. if (lc) {
  71. host.copyStyles(lc, para);
  72. }
  73. }
  74. }
  75. break;
  76. case 'enter':
  77. if (Y.UA.webkit) {
  78. //Webkit doesn't support shift+enter as a BR, this fixes that.
  79. if (e.changedEvent.shiftKey) {
  80. host.execCommand('insertbr');
  81. e.changedEvent.preventDefault();
  82. }
  83. }
  84. if (e.changedNode.test('li') && !Y.UA.ie) {
  85. html = inst.EditorSelection.getText(e.changedNode);
  86. if (html === '') {
  87. par = e.changedNode.ancestor('ol,ul');
  88. dir = par.getAttribute('dir');
  89. if (dir !== '') {
  90. dir = ' dir = "' + dir + '"';
  91. }
  92. par = e.changedNode.ancestor(inst.EditorSelection.BLOCKS);
  93. d = inst.Node.create('<p' + dir + '>' + inst.EditorSelection.CURSOR + '</p>');
  94. par.insert(d, 'after');
  95. e.changedNode.remove();
  96. e.changedEvent.halt();
  97.  
  98. sel = new inst.EditorSelection();
  99. sel.selectNode(d, true, false);
  100. }
  101. }
  102. //TODO Move this to a GECKO MODULE - Can't for the moment, requires no change to metadata (YMAIL)
  103. if (Y.UA.gecko && host.get('defaultblock') !== 'p') {
  104. par = e.changedNode;
  105.  
  106. if (!par.test(LI) && !par.ancestor(LI)) {
  107. if (!par.test(btag)) {
  108. par = par.ancestor(btag);
  109. }
  110. d = inst.Node.create('<' + btag + '></' + btag + '>');
  111. par.insert(d, 'after');
  112. sel = new inst.EditorSelection();
  113. if (sel.anchorOffset) {
  114. inHTML = sel.anchorNode.get('textContent');
  115.  
  116. txt = inst.one(inst.config.doc.createTextNode(inHTML.substr(0, sel.anchorOffset)));
  117. txt2 = inst.one(inst.config.doc.createTextNode(inHTML.substr(sel.anchorOffset)));
  118.  
  119. aNode = sel.anchorNode;
  120. aNode.setContent(''); //I
  121. node2 = aNode.cloneNode(); //I
  122. node2.append(txt2); //text
  123. top = false;
  124. sib = aNode; //I
  125. while (!top) {
  126. sib = sib.get(PARENT_NODE); //B
  127. if (sib && !sib.test(btag)) {
  128. n = sib.cloneNode();
  129. n.set('innerHTML', '');
  130. n.append(node2);
  131.  
  132. //Get children..
  133. childs = sib.get('childNodes');
  134. start = false;
  135. /*jshint loopfunc: true */
  136. childs.each(function(c) {
  137. if (start) {
  138. n.append(c);
  139. }
  140. if (c === aNode) {
  141. start = true;
  142. }
  143. });
  144.  
  145. aNode = sib; //Top sibling
  146. node2 = n;
  147. } else {
  148. top = true;
  149. }
  150. }
  151. txt2 = node2;
  152. sel.anchorNode.append(txt);
  153.  
  154. if (txt2) {
  155. d.append(txt2);
  156. }
  157. }
  158. if (d.get(FC)) {
  159. d = d.get(FC);
  160. }
  161. d.prepend(inst.EditorSelection.CURSOR);
  162. sel.focusCursor(true, true);
  163. html = inst.EditorSelection.getText(d);
  164. if (html !== '') {
  165. inst.EditorSelection.cleanCursor();
  166. }
  167. e.changedEvent.preventDefault();
  168. }
  169. }
  170. break;
  171. case 'keyup':
  172. if (Y.UA.gecko) {
  173. if (inst.config.doc && inst.config.doc.body && inst.config.doc.body.innerHTML.length < 20) {
  174. if (!inst.one(FIRST_P)) {
  175. this._fixFirstPara();
  176. }
  177. }
  178. }
  179. break;
  180. case 'backspace-up':
  181. case 'backspace-down':
  182. case 'delete-up':
  183. if (!Y.UA.ie) {
  184. ps = inst.all(FIRST_P);
  185. item = inst.one(BODY);
  186. if (ps.item(0)) {
  187. item = ps.item(0);
  188. }
  189. br = item.one('br');
  190. if (br) {
  191. br.removeAttribute('id');
  192. br.removeAttribute('class');
  193. }
  194.  
  195. txt = inst.EditorSelection.getText(item);
  196. txt = txt.replace(/ /g, '').replace(/\n/g, '');
  197. imgs = item.all('img');
  198.  
  199. if (txt.length === 0 && !imgs.size()) {
  200. //God this is horrible..
  201. if (!item.test(P)) {
  202. this._fixFirstPara();
  203. }
  204. p = null;
  205. if (e.changedNode && e.changedNode.test(P)) {
  206. p = e.changedNode;
  207. }
  208. if (!p && host._lastPara && host._lastPara.inDoc()) {
  209. p = host._lastPara;
  210. }
  211. if (p && !p.test(P)) {
  212. p = p.ancestor(P);
  213. }
  214. if (p) {
  215. if (!p.previous() && p.get(PARENT_NODE) && p.get(PARENT_NODE).test(BODY)) {
  216. Y.log('Stopping the backspace event', 'warn', 'editor-para');
  217. e.changedEvent.frameEvent.halt();
  218. e.preventDefault();
  219. }
  220. }
  221. }
  222. if (Y.UA.webkit) {
  223. if (e.changedNode) {
  224. //All backspace calls in Webkit need a preventDefault to
  225. //stop history navigation #2531299
  226. e.preventDefault();
  227. item = e.changedNode;
  228. if (item.test('li') && (!item.previous() && !item.next())) {
  229. html = item.get('innerHTML').replace(BR, '');
  230. if (html === '') {
  231. if (item.get(PARENT_NODE)) {
  232. item.get(PARENT_NODE).replace(inst.Node.create(BR));
  233. e.changedEvent.frameEvent.halt();
  234. inst.EditorSelection.filterBlocks();
  235. }
  236. }
  237. }
  238. }
  239. }
  240. }
  241. if (Y.UA.gecko) {
  242. /*
  243. * This forced FF to redraw the content on backspace.
  244. * On some occasions FF will leave a cursor residue after content has been deleted.
  245. * Dropping in the empty textnode and then removing it causes FF to redraw and
  246. * remove the "ghost cursors"
  247. */
  248. d = e.changedNode;
  249. t = inst.config.doc.createTextNode(' ');
  250. d.appendChild(t);
  251. d.removeChild(t);
  252. }
  253. break;
  254. }
  255. if (Y.UA.gecko) {
  256. if (e.changedNode && !e.changedNode.test(btag)) {
  257. p = e.changedNode.ancestor(btag);
  258. if (p) {
  259. this._lastPara = p;
  260. }
  261. }
  262. }
  263.  
  264. },
  265. initializer: function() {
  266. var host = this.get(HOST);
  267. if (host.editorBR) {
  268. Y.error('Can not plug EditorPara and EditorBR at the same time.');
  269. return;
  270. }
  271.  
  272. host.on(NODE_CHANGE, Y.bind(this._onNodeChange, this));
  273. }
  274. }, {
  275. /**
  276. * editorPara
  277. * @static
  278. * @property NAME
  279. */
  280. NAME: 'editorPara',
  281. /**
  282. * editorPara
  283. * @static
  284. * @property NS
  285. */
  286. NS: 'editorPara',
  287. ATTRS: {
  288. host: {
  289. value: false
  290. }
  291. }
  292. });
  293.  
  294. Y.namespace('Plugin');
  295.  
  296. Y.Plugin.EditorPara = EditorPara;
  297.  
  298.