editor-para-debug.js revision a5b442fa7022cb624533685ed17a7cfe6344890d
2667N/AYUI.add('editor-para', function(Y) {
3554N/A
2667N/A
3554N/A
2667N/A /**
2667N/A * Plugin for Editor to paragraph auto wrapping and correction.
2667N/A * @module editor
2667N/A * @submodule editor-para
2667N/A */
2667N/A /**
2667N/A * Plugin for Editor to paragraph auto wrapping and correction.
2667N/A * @class Plugin.EditorPara
2667N/A * @extends Base
2667N/A * @constructor
2667N/A */
2667N/A
2667N/A
2667N/A var EditorPara = function() {
2667N/A EditorPara.superclass.constructor.apply(this, arguments);
2667N/A }, HOST = 'host', BODY = 'body', NODE_CHANGE = 'nodeChange', PARENT_NODE = 'parentNode',
2667N/A FIRST_P = BODY + ' > p', P = 'p', BR = '<br>', FC = 'firstChild', LI = 'li';
2667N/A
2667N/A
2667N/A Y.extend(EditorPara, Y.Base, {
2667N/A /**
2667N/A * Utility method to create an empty paragraph when the document is empty.
2667N/A * @private
2667N/A * @method _fixFirstPara
2667N/A */
2667N/A _fixFirstPara: function() {
2667N/A var host = this.get(HOST), inst = host.getInstance(), sel;
2667N/A inst.one('body').set('innerHTML', '<' + P + '>' + inst.Selection.CURSOR + '</' + P + '>');
2667N/A var n = inst.one(FIRST_P);
2667N/A sel = new inst.Selection();
2667N/A sel.selectNode(n, true, false);
2667N/A
2667N/A },
2667N/A /**
2667N/A * nodeChange handler to handle fixing an empty document.
2667N/A * @private
2667N/A * @method _onNodeChange
2667N/A */
2667N/A _onNodeChange: function(e) {
2667N/A var host = this.get(HOST), inst = host.getInstance(),
2667N/A html, txt, par , d, sel, btag = inst.Selection.DEFAULT_BLOCK_TAG,
2667N/A inHTML, txt2, childs, aNode, index, node2, top, n, sib,
2667N/A ps, br, item, p, imgs, t, LAST_CHILD = ':last-child';
2667N/A
2667N/A switch (e.changedType) {
2667N/A case 'enter-up':
2667N/A var para = ((this._lastPara) ? this._lastPara : e.changedNode),
2667N/A b = para.one('br.yui-cursor');
2667N/A
2667N/A if (this._lastPara) {
2667N/A delete this._lastPara;
2667N/A }
2667N/A
2667N/A if (b) {
2667N/A if (b.previous() || b.next()) {
2667N/A b.remove();
2667N/A }
2667N/A }
2667N/A if (!para.test(btag)) {
2667N/A var para2 = para.ancestor(btag);
2667N/A if (para2) {
2667N/A para = para2;
2667N/A para2 = null;
2667N/A }
2667N/A }
2667N/A if (para.test(btag)) {
2667N/A var prev = para.previous(), lc, lc2, found = false;
2667N/A if (prev) {
2667N/A lc = prev.one(LAST_CHILD);
2667N/A while (!found) {
2667N/A if (lc) {
2667N/A lc2 = lc.one(LAST_CHILD);
2667N/A if (lc2) {
2667N/A lc = lc2;
2667N/A } else {
2667N/A found = true;
2667N/A }
2667N/A } else {
2667N/A found = true;
2667N/A }
2667N/A }
2667N/A if (lc) {
2667N/A host.copyStyles(lc, para);
2667N/A }
2667N/A }
2667N/A }
2667N/A break;
2667N/A case 'enter':
2667N/A if (Y.UA.ie) {
2667N/A if (e.changedNode.test('br')) {
2667N/A e.changedNode.remove();
2667N/A } else if (e.changedNode.test('p, span')) {
2667N/A var b = e.changedNode.one('br.yui-cursor');
2667N/A if (b) {
2667N/A b.remove();
2667N/A }
2667N/A }
2667N/A }
2667N/A if (Y.UA.webkit) {
2667N/A //Webkit doesn't support shift+enter as a BR, this fixes that.
2667N/A if (e.changedEvent.shiftKey) {
2667N/A host.execCommand('insertbr');
2667N/A e.changedEvent.preventDefault();
2667N/A }
2667N/A }
2667N/A //TODO Move this to a GECKO MODULE - Can't for the moment, requires no change to metadata (YMAIL)
2667N/A if (Y.UA.gecko && host.get('defaultblock') !== 'p') {
2667N/A par = e.changedNode;
2667N/A
2667N/A if (!par.test(LI) && !par.ancestor(LI)) {
2667N/A if (!par.test(btag)) {
2667N/A par = par.ancestor(btag);
2667N/A }
2667N/A d = inst.Node.create('<' + btag + '></' + btag + '>');
2667N/A par.insert(d, 'after');
2667N/A sel = new inst.Selection();
2667N/A if (sel.anchorOffset) {
2667N/A inHTML = sel.anchorNode.get('textContent');
2667N/A
2667N/A txt = inst.one(inst.config.doc.createTextNode(inHTML.substr(0, sel.anchorOffset)));
2667N/A txt2 = inst.one(inst.config.doc.createTextNode(inHTML.substr(sel.anchorOffset)));
2667N/A
2667N/A aNode = sel.anchorNode;
2667N/A aNode.setContent(''); //I
2667N/A node2 = aNode.cloneNode(); //I
2667N/A node2.append(txt2); //text
2667N/A top = false;
2667N/A sib = aNode; //I
2667N/A while (!top) {
2667N/A sib = sib.get(PARENT_NODE); //B
2667N/A if (sib && !sib.test(btag)) {
2667N/A n = sib.cloneNode();
2667N/A n.set('innerHTML', '');
2667N/A n.append(node2);
2667N/A
2667N/A //Get children..
2667N/A childs = sib.get('childNodes');
2667N/A var start = false;
2667N/A childs.each(function(c) {
2667N/A if (start) {
2667N/A n.append(c);
2667N/A }
2667N/A if (c === aNode) {
2667N/A start = true;
2667N/A }
2667N/A });
2667N/A
2667N/A aNode = sib; //Top sibling
2667N/A node2 = n;
2667N/A } else {
2667N/A top = true;
2667N/A }
2667N/A }
2667N/A txt2 = node2;
2667N/A sel.anchorNode.append(txt);
2667N/A
2667N/A if (txt2) {
2667N/A d.append(txt2);
2667N/A }
2667N/A }
2667N/A if (d.get(FC)) {
2667N/A d = d.get(FC);
2667N/A }
2667N/A d.prepend(inst.Selection.CURSOR);
2667N/A sel.focusCursor(true, true);
2667N/A html = inst.Selection.getText(d);
2667N/A if (html !== '') {
2667N/A inst.Selection.cleanCursor();
2667N/A }
2667N/A e.changedEvent.preventDefault();
2667N/A }
2667N/A }
2667N/A break;
2667N/A case 'keydown':
2667N/A if (inst.config.doc.childNodes.length < 2) {
2667N/A var cont = inst.config.doc.body.innerHTML;
2667N/A if (cont && cont.length < 5 && cont.toLowerCase() == BR) {
2667N/A this._fixFirstPara();
2667N/A }
2667N/A }
2667N/A break;
2667N/A case 'backspace-up':
2667N/A case 'backspace-down':
2667N/A case 'delete-up':
2667N/A if (!Y.UA.ie) {
2667N/A ps = inst.all(FIRST_P);
2667N/A item = inst.one(BODY);
2667N/A if (ps.item(0)) {
2667N/A item = ps.item(0);
2667N/A }
2667N/A br = item.one('br');
2667N/A if (br) {
2667N/A br.removeAttribute('id');
2667N/A br.removeAttribute('class');
2667N/A }
2667N/A
2667N/A txt = inst.Selection.getText(item);
2667N/A txt = txt.replace(/ /g, '').replace(/\n/g, '');
2667N/A imgs = item.all('img');
2667N/A
2667N/A if (txt.length === 0 && !imgs.size()) {
2667N/A //God this is horrible..
2667N/A if (!item.test(P)) {
2667N/A this._fixFirstPara();
2667N/A }
2667N/A p = null;
2667N/A if (e.changedNode && e.changedNode.test(P)) {
2667N/A p = e.changedNode;
2667N/A }
2667N/A if (!p && host._lastPara && host._lastPara.inDoc()) {
2667N/A p = host._lastPara;
2667N/A }
2667N/A if (p && !p.test(P)) {
2667N/A p = p.ancestor(P);
2667N/A }
2667N/A if (p) {
2667N/A if (!p.previous() && p.get(PARENT_NODE) && p.get(PARENT_NODE).test(BODY)) {
2667N/A Y.log('Stopping the backspace event', 'warn', 'editor-para');
2667N/A e.changedEvent.frameEvent.halt();
2667N/A }
2667N/A }
2667N/A }
2667N/A if (Y.UA.webkit) {
2667N/A if (e.changedNode) {
2667N/A item = e.changedNode;
2667N/A if (item.test('li') && (!item.previous() && !item.next())) {
2667N/A html = item.get('innerHTML').replace(BR, '');
2667N/A if (html === '') {
2667N/A if (item.get(PARENT_NODE)) {
2667N/A item.get(PARENT_NODE).replace(inst.Node.create(BR));
2667N/A e.changedEvent.frameEvent.halt();
2667N/A e.preventDefault();
2667N/A inst.Selection.filterBlocks();
2667N/A }
2667N/A }
2667N/A }
2667N/A }
2667N/A }
2667N/A }
2667N/A if (Y.UA.gecko) {
2667N/A /**
2667N/A * This forced FF to redraw the content on backspace.
2667N/A * On some occasions FF will leave a cursor residue after content has been deleted.
2667N/A * Dropping in the empty textnode and then removing it causes FF to redraw and
2667N/A * remove the "ghost cursors"
2667N/A */
2667N/A d = e.changedNode;
2667N/A t = inst.config.doc.createTextNode(' ');
2667N/A d.appendChild(t);
2667N/A d.removeChild(t);
2667N/A }
2667N/A break;
2667N/A }
2667N/A if (Y.UA.gecko) {
2667N/A if (e.changedNode && !e.changedNode.test(btag)) {
2667N/A p = e.changedNode.ancestor(btag);
2667N/A if (p) {
2667N/A this._lastPara = p;
2667N/A }
2667N/A }
2667N/A }
2667N/A
2667N/A },
2667N/A /**
2667N/A * Performs a block element filter when the Editor is first ready
2667N/A * @private
2667N/A * @method _afterEditorReady
2667N/A */
2667N/A _afterEditorReady: function() {
2667N/A var host = this.get(HOST), inst = host.getInstance(), btag;
2667N/A if (inst) {
2667N/A inst.Selection.filterBlocks();
2667N/A btag = inst.Selection.DEFAULT_BLOCK_TAG;
2667N/A FIRST_P = BODY + ' > ' + btag;
2667N/A P = btag;
2667N/A }
2667N/A },
2667N/A /**
2667N/A * Performs a block element filter when the Editor after an content change
2667N/A * @private
2667N/A * @method _afterContentChange
2667N/A */
2667N/A _afterContentChange: function() {
2667N/A var host = this.get(HOST), inst = host.getInstance();
2667N/A if (inst && inst.Selection) {
2667N/A inst.Selection.filterBlocks();
2667N/A }
2667N/A },
2667N/A /**
2667N/A * Performs block/paste filtering after paste.
2667N/A * @private
2667N/A * @method _afterPaste
2667N/A */
2667N/A _afterPaste: function() {
2667N/A var host = this.get(HOST), inst = host.getInstance(),
2667N/A sel = new inst.Selection();
2667N/A
2667N/A Y.later(50, host, function() {
2667N/A inst.Selection.filterBlocks();
2667N/A });
2667N/A
2667N/A },
2667N/A initializer: function() {
2667N/A var host = this.get(HOST);
2667N/A if (host.editorBR) {
2667N/A Y.error('Can not plug EditorPara and EditorBR at the same time.');
2667N/A return;
2667N/A }
2667N/A
2667N/A host.on(NODE_CHANGE, Y.bind(this._onNodeChange, this));
2667N/A host.after('ready', Y.bind(this._afterEditorReady, this));
2667N/A host.after('contentChange', Y.bind(this._afterContentChange, this));
2667N/A if (Y.Env.webkit) {
2667N/A host.after('dom:paste', Y.bind(this._afterPaste, this));
2667N/A }
2667N/A }
2667N/A }, {
2667N/A /**
2667N/A * editorPara
2667N/A * @static
2667N/A * @property NAME
2667N/A */
2667N/A NAME: 'editorPara',
2667N/A /**
2667N/A * editorPara
2667N/A * @static
2667N/A * @property NS
2667N/A */
2667N/A NS: 'editorPara',
2667N/A ATTRS: {
2667N/A host: {
2667N/A value: false
2667N/A }
2667N/A }
2667N/A });
2667N/A
2667N/A Y.namespace('Plugin');
2667N/A
2667N/A Y.Plugin.EditorPara = EditorPara;
2667N/A
2667N/A
2667N/A
2667N/A}, '@VERSION@' ,{requires:['node'], skinnable:false});
2667N/A