2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
8 YUI.add('editor-para', function (Y, NAME) {
12 * Plugin for Editor to paragraph auto wrapping and correction.
13 * @class Plugin.EditorPara
14 * @extends Plugin.EditorParaBase
17 * @submodule editor-para
21 var EditorPara = function() {
22 EditorPara.superclass.constructor.apply(this, arguments);
23 }, HOST = 'host', NODE_CHANGE = 'nodeChange', PARENT_NODE = 'parentNode',
24 FIRST_P = '> p', P = 'p', BR = '<br>', FC = 'firstChild', LI = 'li';
27 Y.extend(EditorPara, Y.Plugin.EditorParaBase, {
29 * Resolves the ROOT editor element.
33 _getRoot: function() {
34 return this.get(HOST).getInstance().EditorSelection.ROOT;
38 * nodeChange handler to handle fixing an empty document.
40 * @method _onNodeChange
42 _onNodeChange: function(e) {
43 var host = this.get(HOST), inst = host.getInstance(),
44 html, txt, par , d, sel, btag = inst.EditorSelection.DEFAULT_BLOCK_TAG,
45 inHTML, txt2, childs, aNode, node2, top, n, sib, para2, prev,
46 ps, br, item, p, imgs, t, LAST_CHILD = ':last-child', para, b, dir,
47 lc, lc2, found = false, root = this._getRoot(), start;
49 switch (e.changedType) {
51 para = ((this._lastPara) ? this._lastPara : e.changedNode);
52 b = para.one('br.yui-cursor');
55 delete this._lastPara;
59 if (b.previous() || b.next()) {
65 if (!para.test(btag)) {
66 para2 = para.ancestor(btag);
72 if (para.test(btag)) {
73 prev = para.previous();
75 lc = prev.one(LAST_CHILD);
78 lc2 = lc.one(LAST_CHILD);
89 host.copyStyles(lc, para);
96 //Webkit doesn't support shift+enter as a BR, this fixes that.
97 if (e.changedEvent.shiftKey) {
98 host.execCommand('insertbr');
99 e.changedEvent.preventDefault();
102 if (e.changedNode.test('li') && !Y.UA.ie) {
103 html = inst.EditorSelection.getText(e.changedNode);
105 par = e.changedNode.ancestor('ol,ul');
106 dir = par.getAttribute('dir');
108 dir = ' dir = "' + dir + '"';
110 par = e.changedNode.ancestor(inst.EditorSelection.BLOCKS);
111 d = inst.Node.create('<p' + dir + '>' + inst.EditorSelection.CURSOR + '</p>');
112 par.insert(d, 'after');
113 e.changedNode.remove();
114 e.changedEvent.halt();
116 sel = new inst.EditorSelection();
117 sel.selectNode(d, true, false);
120 //TODO Move this to a GECKO MODULE - Can't for the moment, requires no change to metadata (YMAIL)
121 if (Y.UA.gecko && host.get('defaultblock') !== 'p') {
124 if (!par.test(LI) && !par.ancestor(LI)) {
125 if (!par.test(btag)) {
126 par = par.ancestor(btag);
128 d = inst.Node.create('<' + btag + '></' + btag + '>');
129 par.insert(d, 'after');
130 sel = new inst.EditorSelection();
131 if (sel.anchorOffset) {
132 inHTML = sel.anchorNode.get('textContent');
134 txt = inst.one(inst.config.doc.createTextNode(inHTML.substr(0, sel.anchorOffset)));
135 txt2 = inst.one(inst.config.doc.createTextNode(inHTML.substr(sel.anchorOffset)));
137 aNode = sel.anchorNode;
138 aNode.setContent(''); //I
139 node2 = aNode.cloneNode(); //I
140 node2.append(txt2); //text
144 sib = sib.get(PARENT_NODE); //B
145 if (sib && !sib.test(btag)) {
147 n.set('innerHTML', '');
151 childs = sib.get('childNodes');
153 /*jshint loopfunc: true */
154 childs.each(function(c) {
163 aNode = sib; //Top sibling
170 sel.anchorNode.append(txt);
179 d.prepend(inst.EditorSelection.CURSOR);
180 sel.focusCursor(true, true);
181 html = inst.EditorSelection.getText(d);
183 inst.EditorSelection.cleanCursor();
185 e.changedEvent.preventDefault();
191 if (root && root.getHTML().length < 20) {
192 if (!root.one(FIRST_P)) {
193 this._fixFirstPara();
199 case 'backspace-down':
202 ps = root.all(FIRST_P);
209 br.removeAttribute('id');
210 br.removeAttribute('class');
213 txt = inst.EditorSelection.getText(item);
214 txt = txt.replace(/ /g, '').replace(/\n/g, '');
215 imgs = item.all('img');
217 if (txt.length === 0 && !imgs.size()) {
218 //God this is horrible..
220 this._fixFirstPara();
223 if (e.changedNode && e.changedNode.test(P)) {
226 if (!p && host._lastPara && host._lastPara.inDoc()) {
229 if (p && !p.test(P)) {
233 if (!p.previous() && p.get(PARENT_NODE) && p.get(PARENT_NODE).compareTo(root)) {
234 e.changedEvent.frameEvent.halt();
241 //All backspace calls in Webkit need a preventDefault to
242 //stop history navigation #2531299
244 item = e.changedNode;
245 if (item.test('li') && (!item.previous() && !item.next())) {
246 html = item.get('innerHTML').replace(BR, '');
248 if (item.get(PARENT_NODE)) {
249 item.get(PARENT_NODE).replace(inst.Node.create(BR));
250 e.changedEvent.frameEvent.halt();
251 inst.EditorSelection.filterBlocks();
260 * This forced FF to redraw the content on backspace.
261 * On some occasions FF will leave a cursor residue after content has been deleted.
262 * Dropping in the empty textnode and then removing it causes FF to redraw and
263 * remove the "ghost cursors"
266 t = inst.config.doc.createTextNode(' ');
273 if (e.changedNode && !e.changedNode.test(btag)) {
274 p = e.changedNode.ancestor(btag);
282 initializer: function() {
283 var host = this.get(HOST);
285 Y.error('Can not plug EditorPara and EditorBR at the same time.');
289 host.on(NODE_CHANGE, Y.bind(this._onNodeChange, this));
311 Y.namespace('Plugin');
313 Y.Plugin.EditorPara = EditorPara;
317 }, '3.13.0', {"requires": ["editor-para-base"]});