fix #6962 - new editor
[roojs1] / roojs-debug.js
index d2b15cc..f903c90 100644 (file)
@@ -4907,468 +4907,7 @@ Roo.lib.Easing = {
             }
         };
     })();
-/**
- * Originally based of this code... - refactored for Roo...
- * https://github.com/aaalsaleh/undo-manager
- * undo-manager.js
- * @author  Abdulrahman Alsaleh 
- * @copyright 2015 Abdulrahman Alsaleh 
- * @license  MIT License (c) 
- *
- * Hackily modifyed by alan@roojs.com
- *
- *
- *  
- *
- *  TOTALLY UNTESTED...
- *
- *  Documentation to be done....
- */
-
-/**
-* @class Roo.lib.UndoManager
-* An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
-* Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
-
- * Usage:
- * <pre><code>
-
-
-editor.undoManager = new Roo.lib.UndoManager(1000, editor);
-</code></pre>
-
-* For more information see this blog post with examples:
-*  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
-     - Create Elements using DOM, HTML fragments and Templates</a>. 
-* @constructor
-* @param {Number} limit how far back to go ... use 1000?
-* @param {Object} scope usually use document..
-*/
-
-Roo.lib.UndoManager = function (limit, undoScopeHost)
-{
-    this.stack = [];
-    this.limit = limit;
-    this.scope = undoScopeHost;
-    this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
-    if (this.fireEvent) {
-        this.bindEvents();
-    }
-    this.reset();
-    
-};
-        
-Roo.lib.UndoManager.prototype = {
-    
-    limit : false,
-    stack : false,
-    scope :  false,
-    fireEvent : false,
-    position : 0,
-    length : 0,
-    
-    
-     /**
-     * To push and execute a transaction, the method undoManager.transact
-     * must be called by passing a transaction object as the first argument, and a merge
-     * flag as the second argument. A transaction object has the following properties:
-     *
-     * Usage:
-<pre><code>
-undoManager.transact({
-    label: 'Typing',
-    execute: function() { ... },
-    undo: function() { ... },
-    // redo same as execute
-    redo: function() { this.execute(); }
-}, false);
-
-// merge transaction
-undoManager.transact({
-    label: 'Typing',
-    execute: function() { ... },  // this will be run...
-    undo: function() { ... }, // what to do when undo is run.
-    // redo same as execute
-    redo: function() { this.execute(); }
-}, true); 
-</code></pre> 
-     *
-     * 
-     * @param {Object} transaction The transaction to add to the stack.
-     * @return {String} The HTML fragment
-     */
-    
-    
-    transact : function (transaction, merge)
-    {
-        if (arguments.length < 2) {
-            throw new TypeError('Not enough arguments to UndoManager.transact.');
-        }
-
-        transaction.execute();
-
-        this.stack.splice(0, this.position);
-        if (merge && this.length) {
-            this.stack[0].push(transaction);
-        } else {
-            this.stack.unshift([transaction]);
-        }
-    
-        this.position = 0;
-
-        if (this.limit && this.stack.length > this.limit) {
-            this.length = this.stack.length = this.limit;
-        } else {
-            this.length = this.stack.length;
-        }
-
-        if (this.fireEvent) {
-            this.scope.dispatchEvent(
-                new CustomEvent('DOMTransaction', {
-                    detail: {
-                        transactions: this.stack[0].slice()
-                    },
-                    bubbles: true,
-                    cancelable: false
-                })
-            );
-        }
-        
-        //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
-      
-        
-    },
-
-    undo : function ()
-    {
-        //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
-        
-        if (this.position < this.length) {
-            for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
-                this.stack[this.position][i].undo();
-            }
-            this.position++;
-
-            if (this.fireEvent) {
-                this.scope.dispatchEvent(
-                    new CustomEvent('undo', {
-                        detail: {
-                            transactions: this.stack[this.position - 1].slice()
-                        },
-                        bubbles: true,
-                        cancelable: false
-                    })
-                );
-            }
-        }
-    },
-
-    redo : function ()
-    {
-        if (this.position > 0) {
-            for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
-                this.stack[this.position - 1][i].redo();
-            }
-            this.position--;
-
-            if (this.fireEvent) {
-                this.scope.dispatchEvent(
-                    new CustomEvent('redo', {
-                        detail: {
-                            transactions: this.stack[this.position].slice()
-                        },
-                        bubbles: true,
-                        cancelable: false
-                    })
-                );
-            }
-        }
-    },
-
-    item : function (index)
-    {
-        if (index >= 0 && index < this.length) {
-            return this.stack[index].slice();
-        }
-        return null;
-    },
-
-    clearUndo : function () {
-        this.stack.length = this.length = this.position;
-    },
-
-    clearRedo : function () {
-        this.stack.splice(0, this.position);
-        this.position = 0;
-        this.length = this.stack.length;
-    },
-    /**
-     * Reset the undo - probaly done on load to clear all history.
-     */
-    reset : function()
-    {
-        this.stack = [];
-        this.position = 0;
-        this.length = 0;
-        this.current_html = this.scope.innerHTML;
-        if (this.timer !== false) {
-            clearTimeout(this.timer);
-        }
-        this.timer = false;
-        this.merge = false;
-        this.addEvent();
-        
-    },
-    current_html : '',
-    timer : false,
-    merge : false,
-    
-    
-    // this will handle the undo/redo on the element.?
-    bindEvents : function()
-    {
-        var el  = this.scope;
-        el.undoManager = this;
-        
-        
-        this.scope.addEventListener('keydown', function(e) {
-            if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
-                if (e.shiftKey) {
-                    el.undoManager.redo(); // Ctrl/Command + Shift + Z
-                } else {
-                    el.undoManager.undo(); // Ctrl/Command + Z
-                }
-        
-                e.preventDefault();
-            }
-        });
-        /// ignore keyup..
-        this.scope.addEventListener('keyup', function(e) {
-            if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
-                e.preventDefault();
-            }
-        });
-        
-        
-        
-        var t = this;
-        
-        el.addEventListener('input', function(e) {
-            if(el.innerHTML == t.current_html) {
-                return;
-            }
-            // only record events every second.
-            if (t.timer !== false) {
-               clearTimeout(t.timer);
-               t.timer = false;
-            }
-            t.timer = setTimeout(function() { t.merge = false; }, 1000);
-            
-            t.addEvent(t.merge);
-            t.merge = true; // ignore changes happening every second..
-        });
-       },
-    /**
-     * Manually add an event.
-     * Normall called without arguements - and it will just get added to the stack.
-     * 
-     */
-    
-    addEvent : function(merge)
-    {
-        //Roo.log("undomanager +" + (merge ? 'Y':'n'));
-        // not sure if this should clear the timer 
-        merge = typeof(merge) == 'undefined' ? false : merge; 
-        
-        this.scope.undoManager.transact({
-            scope : this.scope,
-            oldHTML: this.current_html,
-            newHTML: this.scope.innerHTML,
-            // nothing to execute (content already changed when input is fired)
-            execute: function() { },
-            undo: function() {
-                this.scope.innerHTML = this.current_html = this.oldHTML;
-            },
-            redo: function() {
-                this.scope.innerHTML = this.current_html = this.newHTML;
-            }
-        }, false); //merge);
-        
-        this.merge = merge;
-        
-        this.current_html = this.scope.innerHTML;
-    }
-    
-    
-     
-    
-    
-    
-};
-/**
- * @class Roo.lib.Range
- * @constructor
- * This is a toolkit, normally used to copy features into a Dom Range element
- * Roo.lib.Range.wrap(x);
- *
- *
- *
- */
-Roo.lib.Range = function() { };
-
-/**
- * Wrap a Dom Range object, to give it new features...
- * @static
- * @param {Range} the range to wrap
- */
-Roo.lib.Range.wrap = function(r) {
-    return Roo.apply(r, Roo.lib.Range.prototype);
-};
-/**
- * find a parent node eg. LI / OL
- * @param {string|Array} node name or array of nodenames
- * @return {DomElement|false}
- */
-Roo.apply(Roo.lib.Range.prototype,
-{
-    
-    closest : function(str)
-    {
-        if (typeof(str) != 'string') {
-            // assume it's a array.
-            for(var i = 0;i < str.length;i++) {
-                var r = this.closest(str[i]);
-                if (r !== false) {
-                    return r;
-                }
-                
-            }
-            return false;
-        }
-        str = str.toLowerCase();
-        var n = this.commonAncestorContainer; // might not be a node
-        while (n.nodeType != 1) {
-            n = n.parentNode;
-        }
-        
-        if (n.nodeName.toLowerCase() == str ) {
-            return n;
-        }
-        if (n.nodeName.toLowerCase() == 'body') {
-            return false;
-        }
-            
-        return n.closest(str) || false;
-        
-    },
-    cloneRange : function()
-    {
-        return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
-    }
-});/**
- * @class Roo.lib.Selection
- * @constructor
- * This is a toolkit, normally used to copy features into a Dom Selection element
- * Roo.lib.Selection.wrap(x);
- *
- *
- *
- */
-Roo.lib.Selection = function() { };
-
-/**
- * Wrap a Dom Range object, to give it new features...
- * @static
- * @param {Range} the range to wrap
- */
-Roo.lib.Selection.wrap = function(r, doc) {
-    Roo.apply(r, Roo.lib.Selection.prototype);
-    r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
-    return r;
-};
-/**
- * find a parent node eg. LI / OL
- * @param {string|Array} node name or array of nodenames
- * @return {DomElement|false}
- */
-Roo.apply(Roo.lib.Selection.prototype,
-{
-    /**
-     * the owner document
-     */
-    ownerDocument : false,
-    
-    getRangeAt : function(n)
-    {
-        return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
-    },
-    
-    /**
-     * insert node at selection 
-     * @param {DomElement|string} node
-     * @param {string} cursor (after|in|none) where to place the cursor after inserting.
-     */
-    insertNode: function(node, cursor)
-    {
-        if (typeof(node) == 'string') {
-            node = this.ownerDocument.createElement(node);
-            if (cursor == 'in') {
-                node.innerHTML = '&nbsp;';
-            }
-        }
-        
-        var range = this.getRangeAt(0);
-        
-        if (this.type != 'Caret') {
-            range.deleteContents();
-        }
-        var sn = node.childNodes[0]; // select the contents.
-
-        
-        
-        range.insertNode(node);
-        if (cursor == 'after') {
-            node.insertAdjacentHTML('afterend', '&nbsp;');
-            sn = node.nextSibling;
-        }
-        
-        if (cursor == 'none') {
-            return;
-        }
-        
-        this.cursorText(sn);
-    },
-    
-    cursorText : function(n)
-    {
-       
-        //var range = this.getRangeAt(0);
-        range = Roo.lib.Range.wrap(new Range());
-        //range.selectNode(n);
-        
-        var ix = Array.from(n.parentNode.childNodes).indexOf(n);
-        range.setStart(n.parentNode,ix);
-        range.setEnd(n.parentNode,ix+1);
-        //range.collapse(false);
-         
-        this.removeAllRanges();
-        this.addRange(range);
-        
-        Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
-    },
-    cursorAfter : function(n)
-    {
-        if (!n.nextSibling || n.nextSibling.nodeValue != '&nbsp;') {
-            n.insertAdjacentHTML('afterend', '&nbsp;');
-        }
-        this.cursorText (n.nextSibling);
-    }
-        
-    
-});/*
+/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -45066,4377 +44605,7 @@ Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
     } 
     
     
-});Roo.rtf = {}; // namespace
-Roo.rtf.Hex = function(hex)
-{
-    this.hexstr = hex;
-};
-Roo.rtf.Paragraph = function(opts)
-{
-    this.content = []; ///??? is that used?
-};Roo.rtf.Span = function(opts)
-{
-    this.value = opts.value;
-};
-
-Roo.rtf.Group = function(parent)
-{
-    // we dont want to acutally store parent - it will make debug a nightmare..
-    this.content = [];
-    this.cn  = [];
-     
-       
-    
-};
-
-Roo.rtf.Group.prototype = {
-    ignorable : false,
-    content: false,
-    cn: false,
-    addContent : function(node) {
-        // could set styles...
-        this.content.push(node);
-    },
-    addChild : function(cn)
-    {
-        this.cn.push(cn);
-    },
-    // only for images really...
-    toDataURL : function()
-    {
-        var mimetype = false;
-        switch(true) {
-            case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
-                mimetype = "image/png";
-                break;
-             case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
-                mimetype = "image/jpeg";
-                break;
-            default :
-                return 'about:blank'; // ?? error?
-        }
-        
-        
-        var hexstring = this.content[this.content.length-1].value;
-        
-        return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
-            return String.fromCharCode(parseInt(a, 16));
-        }).join(""));
-    }
-    
-};
-// this looks like it's normally the {rtf{ .... }}
-Roo.rtf.Document = function()
-{
-    // we dont want to acutally store parent - it will make debug a nightmare..
-    this.rtlch  = [];
-    this.content = [];
-    this.cn = [];
-    
-};
-Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
-    addChild : function(cn)
-    {
-        this.cn.push(cn);
-        switch(cn.type) {
-            case 'rtlch': // most content seems to be inside this??
-            case 'listtext':
-            case 'shpinst':
-                this.rtlch.push(cn);
-                return;
-            default:
-                this[cn.type] = cn;
-        }
-        
-    },
-    
-    getElementsByType : function(type)
-    {
-        var ret =  [];
-        this._getElementsByType(type, ret, this.cn, 'rtf');
-        return ret;
-    },
-    _getElementsByType : function (type, ret, search_array, path)
-    {
-        search_array.forEach(function(n,i) {
-            if (n.type == type) {
-                n.path = path + '/' + n.type + ':' + i;
-                ret.push(n);
-            }
-            if (n.cn.length > 0) {
-                this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
-            }
-        },this);
-    }
-    
-});
-Roo.rtf.Ctrl = function(opts)
-{
-    this.value = opts.value;
-    this.param = opts.param;
-};
-/**
- *
- *
- * based on this https://github.com/iarna/rtf-parser
- * it's really only designed to extract pict from pasted RTF 
- *
- * usage:
- *
- *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
- *  
- *
- */
-
-
-
-
-Roo.rtf.Parser = function(text) {
-    //super({objectMode: true})
-    this.text = '';
-    this.parserState = this.parseText;
-    
-    // these are for interpeter...
-    this.doc = {};
-    ///this.parserState = this.parseTop
-    this.groupStack = [];
-    this.hexStore = [];
-    this.doc = false;
-    
-    this.groups = []; // where we put the return.
-    
-    for (var ii = 0; ii < text.length; ++ii) {
-        ++this.cpos;
-        
-        if (text[ii] === '\n') {
-            ++this.row;
-            this.col = 1;
-        } else {
-            ++this.col;
-        }
-        this.parserState(text[ii]);
-    }
-    
-    
-    
-};
-Roo.rtf.Parser.prototype = {
-    text : '', // string being parsed..
-    controlWord : '',
-    controlWordParam :  '',
-    hexChar : '',
-    doc : false,
-    group: false,
-    groupStack : false,
-    hexStore : false,
-    
-    
-    cpos : 0, 
-    row : 1, // reportin?
-    col : 1, //
-
-     
-    push : function (el)
-    {
-        var m = 'cmd'+ el.type;
-        if (typeof(this[m]) == 'undefined') {
-            Roo.log('invalid cmd:' + el.type);
-            return;
-        }
-        this[m](el);
-        //Roo.log(el);
-    },
-    flushHexStore : function()
-    {
-        if (this.hexStore.length < 1) {
-            return;
-        }
-        var hexstr = this.hexStore.map(
-            function(cmd) {
-                return cmd.value;
-        }).join('');
-        
-        this.group.addContent( new Roo.rtf.Hex( hexstr ));
-              
-            
-        this.hexStore.splice(0)
-        
-    },
-    
-    cmdgroupstart : function()
-    {
-        this.flushHexStore();
-        if (this.group) {
-            this.groupStack.push(this.group);
-        }
-         // parent..
-        if (this.doc === false) {
-            this.group = this.doc = new Roo.rtf.Document();
-            return;
-            
-        }
-        this.group = new Roo.rtf.Group(this.group);
-    },
-    cmdignorable : function()
-    {
-        this.flushHexStore();
-        this.group.ignorable = true;
-    },
-    cmdendparagraph : function()
-    {
-        this.flushHexStore();
-        this.group.addContent(new Roo.rtf.Paragraph());
-    },
-    cmdgroupend : function ()
-    {
-        this.flushHexStore();
-        var endingGroup = this.group;
-        
-        
-        this.group = this.groupStack.pop();
-        if (this.group) {
-            this.group.addChild(endingGroup);
-        }
-        
-        
-        
-        var doc = this.group || this.doc;
-        //if (endingGroup instanceof FontTable) {
-        //  doc.fonts = endingGroup.table
-        //} else if (endingGroup instanceof ColorTable) {
-        //  doc.colors = endingGroup.table
-        //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
-        if (endingGroup.ignorable === false) {
-            //code
-            this.groups.push(endingGroup);
-           // Roo.log( endingGroup );
-        }
-            //Roo.each(endingGroup.content, function(item)) {
-            //    doc.addContent(item);
-            //}
-            //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
-        //}
-    },
-    cmdtext : function (cmd)
-    {
-        this.flushHexStore();
-        if (!this.group) { // an RTF fragment, missing the {\rtf1 header
-            //this.group = this.doc
-        }
-        this.group.addContent(new Roo.rtf.Span(cmd));
-    },
-    cmdcontrolword : function (cmd)
-    {
-        this.flushHexStore();
-        if (!this.group.type) {
-            this.group.type = cmd.value;
-            return;
-        }
-        this.group.addContent(new Roo.rtf.Ctrl(cmd));
-        // we actually don't care about ctrl words...
-        return ;
-        /*
-        var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
-        if (this[method]) {
-            this[method](cmd.param)
-        } else {
-            if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
-        }
-        */
-    },
-    cmdhexchar : function(cmd) {
-        this.hexStore.push(cmd);
-    },
-    cmderror : function(cmd) {
-        throw new Exception (cmd.value);
-    },
-    
-    /*
-      _flush (done) {
-        if (this.text !== '\u0000') this.emitText()
-        done()
-      }
-      */
-      
-      
-    parseText : function(c)
-    {
-        if (c === '\\') {
-            this.parserState = this.parseEscapes;
-        } else if (c === '{') {
-            this.emitStartGroup();
-        } else if (c === '}') {
-            this.emitEndGroup();
-        } else if (c === '\x0A' || c === '\x0D') {
-            // cr/lf are noise chars
-        } else {
-            this.text += c;
-        }
-    },
-    
-    parseEscapes: function (c)
-    {
-        if (c === '\\' || c === '{' || c === '}') {
-            this.text += c;
-            this.parserState = this.parseText;
-        } else {
-            this.parserState = this.parseControlSymbol;
-            this.parseControlSymbol(c);
-        }
-    },
-    parseControlSymbol: function(c)
-    {
-        if (c === '~') {
-            this.text += '\u00a0'; // nbsp
-            this.parserState = this.parseText
-        } else if (c === '-') {
-             this.text += '\u00ad'; // soft hyphen
-        } else if (c === '_') {
-            this.text += '\u2011'; // non-breaking hyphen
-        } else if (c === '*') {
-            this.emitIgnorable();
-            this.parserState = this.parseText;
-        } else if (c === "'") {
-            this.parserState = this.parseHexChar;
-        } else if (c === '|') { // formula cacter
-            this.emitFormula();
-            this.parserState = this.parseText;
-        } else if (c === ':') { // subentry in an index entry
-            this.emitIndexSubEntry();
-            this.parserState = this.parseText;
-        } else if (c === '\x0a') {
-            this.emitEndParagraph();
-            this.parserState = this.parseText;
-        } else if (c === '\x0d') {
-            this.emitEndParagraph();
-            this.parserState = this.parseText;
-        } else {
-            this.parserState = this.parseControlWord;
-            this.parseControlWord(c);
-        }
-    },
-    parseHexChar: function (c)
-    {
-        if (/^[A-Fa-f0-9]$/.test(c)) {
-            this.hexChar += c;
-            if (this.hexChar.length >= 2) {
-              this.emitHexChar();
-              this.parserState = this.parseText;
-            }
-            return;
-        }
-        this.emitError("Invalid character \"" + c + "\" in hex literal.");
-        this.parserState = this.parseText;
-        
-    },
-    parseControlWord : function(c)
-    {
-        if (c === ' ') {
-            this.emitControlWord();
-            this.parserState = this.parseText;
-        } else if (/^[-\d]$/.test(c)) {
-            this.parserState = this.parseControlWordParam;
-            this.controlWordParam += c;
-        } else if (/^[A-Za-z]$/.test(c)) {
-          this.controlWord += c;
-        } else {
-          this.emitControlWord();
-          this.parserState = this.parseText;
-          this.parseText(c);
-        }
-    },
-    parseControlWordParam : function (c) {
-        if (/^\d$/.test(c)) {
-          this.controlWordParam += c;
-        } else if (c === ' ') {
-          this.emitControlWord();
-          this.parserState = this.parseText;
-        } else {
-          this.emitControlWord();
-          this.parserState = this.parseText;
-          this.parseText(c);
-        }
-    },
-    
-    
-    
-    
-    emitText : function () {
-        if (this.text === '') {
-            return;
-        }
-        this.push({
-            type: 'text',
-            value: this.text,
-            pos: this.cpos,
-            row: this.row,
-            col: this.col
-        });
-        this.text = ''
-    },
-    emitControlWord : function ()
-    {
-        this.emitText();
-        if (this.controlWord === '') {
-            this.emitError('empty control word');
-        } else {
-            this.push({
-                  type: 'controlword',
-                  value: this.controlWord,
-                  param: this.controlWordParam !== '' && Number(this.controlWordParam),
-                  pos: this.cpos,
-                  row: this.row,
-                  col: this.col
-            });
-        }
-        this.controlWord = '';
-        this.controlWordParam = '';
-    },
-    emitStartGroup : function ()
-    {
-        this.emitText();
-        this.push({
-            type: 'groupstart',
-            pos: this.cpos,
-            row: this.row,
-            col: this.col
-        });
-    },
-    emitEndGroup : function ()
-    {
-        this.emitText();
-        this.push({
-            type: 'groupend',
-            pos: this.cpos,
-            row: this.row,
-            col: this.col
-        });
-    },
-    emitIgnorable : function ()
-    {
-        this.emitText();
-        this.push({
-            type: 'ignorable',
-            pos: this.cpos,
-            row: this.row,
-            col: this.col
-        });
-    },
-    emitHexChar : function ()
-    {
-        this.emitText();
-        this.push({
-            type: 'hexchar',
-            value: this.hexChar,
-            pos: this.cpos,
-            row: this.row,
-            col: this.col
-        });
-        this.hexChar = ''
-    },
-    emitError : function (message)
-    {
-      this.emitText();
-      this.push({
-            type: 'error',
-            value: message,
-            row: this.row,
-            col: this.col,
-            char: this.cpos //,
-            //stack: new Error().stack
-        });
-    },
-    emitEndParagraph : function () {
-        this.emitText();
-        this.push({
-            type: 'endparagraph',
-            pos: this.cpos,
-            row: this.row,
-            col: this.col
-        });
-    }
-     
-} ;
-Roo.htmleditor = {};
-/**
- * @class Roo.htmleditor.Filter
- * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
- * @cfg {DomElement} node The node to iterate and filter
- * @cfg {boolean|String|Array} tag Tags to replace 
- * @constructor
- * Create a new Filter.
- * @param {Object} config Configuration options
- */
-
-
-
-Roo.htmleditor.Filter = function(cfg) {
-    Roo.apply(this.cfg);
-    // this does not actually call walk as it's really just a abstract class
-}
-
-
-Roo.htmleditor.Filter.prototype = {
-    
-    node: false,
-    
-    tag: false,
-
-    // overrride to do replace comments.
-    replaceComment : false,
-    
-    // overrride to do replace or do stuff with tags..
-    replaceTag : false,
-    
-    walk : function(dom)
-    {
-        Roo.each( Array.from(dom.childNodes), function( e ) {
-            switch(true) {
-                
-                case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
-                    this.replaceComment(e);
-                    return;
-                
-                case e.nodeType != 1: //not a node.
-                    return;
-                
-                case this.tag === true: // everything
-                case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
-                case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
-                    if (this.replaceTag && false === this.replaceTag(e)) {
-                        return;
-                    }
-                    if (e.hasChildNodes()) {
-                        this.walk(e);
-                    }
-                    return;
-                
-                default:    // tags .. that do not match.
-                    if (e.hasChildNodes()) {
-                        this.walk(e);
-                    }
-            }
-            
-        }, this);
-        
-    }
-}; 
-
-/**
- * @class Roo.htmleditor.FilterAttributes
- * clean attributes and  styles including http:// etc.. in attribute
- * @constructor
-* Run a new Attribute Filter
-* @param {Object} config Configuration options
- */
-Roo.htmleditor.FilterAttributes = function(cfg)
-{
-    Roo.apply(this, cfg);
-    this.attrib_black = this.attrib_black || [];
-    this.attrib_white = this.attrib_white || [];
-
-    this.attrib_clean = this.attrib_clean || [];
-    this.style_white = this.style_white || [];
-    this.style_black = this.style_black || [];
-    this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
-{
-    tag: true, // all tags
-    
-    attrib_black : false, // array
-    attrib_clean : false,
-    attrib_white : false,
-
-    style_white : false,
-    style_black : false,
-     
-     
-    replaceTag : function(node)
-    {
-        if (!node.attributes || !node.attributes.length) {
-            return true;
-        }
-        
-        for (var i = node.attributes.length-1; i > -1 ; i--) {
-            var a = node.attributes[i];
-            //console.log(a);
-            if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
-                node.removeAttribute(a.name);
-                continue;
-            }
-            
-            
-            
-            if (a.name.toLowerCase().substr(0,2)=='on')  {
-                node.removeAttribute(a.name);
-                continue;
-            }
-            
-            
-            if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
-                node.removeAttribute(a.name);
-                continue;
-            }
-            if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
-                this.cleanAttr(node,a.name,a.value); // fixme..
-                continue;
-            }
-            if (a.name == 'style') {
-                this.cleanStyle(node,a.name,a.value);
-                continue;
-            }
-            /// clean up MS crap..
-            // tecnically this should be a list of valid class'es..
-            
-            
-            if (a.name == 'class') {
-                if (a.value.match(/^Mso/)) {
-                    node.removeAttribute('class');
-                }
-                
-                if (a.value.match(/^body$/)) {
-                    node.removeAttribute('class');
-                }
-                continue;
-            }
-            
-            
-            // style cleanup!?
-            // class cleanup?
-            
-        }
-        return true; // clean children
-    },
-        
-    cleanAttr: function(node, n,v)
-    {
-        
-        if (v.match(/^\./) || v.match(/^\//)) {
-            return;
-        }
-        if (v.match(/^(http|https):\/\//)
-            || v.match(/^mailto:/) 
-            || v.match(/^ftp:/)
-            || v.match(/^data:/)
-            ) {
-            return;
-        }
-        if (v.match(/^#/)) {
-            return;
-        }
-        if (v.match(/^\{/)) { // allow template editing.
-            return;
-        }
-//            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
-        node.removeAttribute(n);
-        
-    },
-    cleanStyle : function(node,  n,v)
-    {
-        if (v.match(/expression/)) { //XSS?? should we even bother..
-            node.removeAttribute(n);
-            return;
-        }
-        
-        var parts = v.split(/;/);
-        var clean = [];
-        
-        Roo.each(parts, function(p) {
-            p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
-            if (!p.length) {
-                return true;
-            }
-            var l = p.split(':').shift().replace(/\s+/g,'');
-            l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
-            
-            if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
-                return true;
-            }
-            //Roo.log()
-            // only allow 'c whitelisted system attributes'
-            if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
-                return true;
-            }
-            
-            
-            clean.push(p);
-            return true;
-        },this);
-        if (clean.length) { 
-            node.setAttribute(n, clean.join(';'));
-        } else {
-            node.removeAttribute(n);
-        }
-        
-    }
-        
-        
-        
-    
-});/**
- * @class Roo.htmleditor.FilterBlack
- * remove blacklisted elements.
- * @constructor
- * Run a new Blacklisted Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterBlack = function(cfg)
-{
-    Roo.apply(this, cfg);
-    this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
-{
-    tag : true, // all elements.
-   
-    replaceTag : function(n)
-    {
-        n.parentNode.removeChild(n);
-    }
-});
-/**
- * @class Roo.htmleditor.FilterComment
- * remove comments.
- * @constructor
-* Run a new Comments Filter
-* @param {Object} config Configuration options
- */
-Roo.htmleditor.FilterComment = function(cfg)
-{
-    this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
-{
-  
-    replaceComment : function(n)
-    {
-        n.parentNode.removeChild(n);
-    }
-});/**
- * @class Roo.htmleditor.FilterKeepChildren
- * remove tags but keep children
- * @constructor
- * Run a new Keep Children Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterKeepChildren = function(cfg)
-{
-    Roo.apply(this, cfg);
-    if (this.tag === false) {
-        return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
-    }
-    this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
-{
-    
-  
-    replaceTag : function(node)
-    {
-        // walk children...
-        //Roo.log(node);
-        var ar = Array.from(node.childNodes);
-        //remove first..
-        for (var i = 0; i < ar.length; i++) {
-            if (ar[i].nodeType == 1) {
-                if (
-                    (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
-                    || // array and it matches
-                    (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
-                ) {
-                    this.replaceTag(ar[i]); // child is blacklisted as well...
-                    continue;
-                }
-            }
-        }  
-        ar = Array.from(node.childNodes);
-        for (var i = 0; i < ar.length; i++) {
-         
-            node.removeChild(ar[i]);
-            // what if we need to walk these???
-            node.parentNode.insertBefore(ar[i], node);
-            if (this.tag !== false) {
-                this.walk(ar[i]);
-                
-            }
-        }
-        node.parentNode.removeChild(node);
-        return false; // don't walk children
-        
-        
-    }
-});/**
- * @class Roo.htmleditor.FilterParagraph
- * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
- * like on 'push' to remove the <p> tags and replace them with line breaks.
- * @constructor
- * Run a new Paragraph Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterParagraph = function(cfg)
-{
-    // no need to apply config.
-    this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
-{
-    
-     
-    tag : 'P',
-    
-     
-    replaceTag : function(node)
-    {
-        
-        if (node.childNodes.length == 1 &&
-            node.childNodes[0].nodeType == 3 &&
-            node.childNodes[0].textContent.trim().length < 1
-            ) {
-            // remove and replace with '<BR>';
-            node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
-            return false; // no need to walk..
-        }
-        var ar = Array.from(node.childNodes);
-        for (var i = 0; i < ar.length; i++) {
-            node.removeChild(ar[i]);
-            // what if we need to walk these???
-            node.parentNode.insertBefore(ar[i], node);
-        }
-        // now what about this?
-        // <p> &nbsp; </p>
-        
-        // double BR.
-        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
-        node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
-        node.parentNode.removeChild(node);
-        
-        return false;
-
-    }
-    
-});/**
- * @class Roo.htmleditor.FilterSpan
- * filter span's with no attributes out..
- * @constructor
- * Run a new Span Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterSpan = function(cfg)
-{
-    // no need to apply config.
-    this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
-{
-     
-    tag : 'SPAN',
-     
-    replaceTag : function(node)
-    {
-        if (node.attributes && node.attributes.length > 0) {
-            return true; // walk if there are any.
-        }
-        Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
-        return false;
-     
-    }
-    
-});/**
- * @class Roo.htmleditor.FilterTableWidth
-  try and remove table width data - as that frequently messes up other stuff.
- * 
- *      was cleanTableWidths.
- *
- * Quite often pasting from word etc.. results in tables with column and widths.
- * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
- *
- * @constructor
- * Run a new Table Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterTableWidth = function(cfg)
-{
-    // no need to apply config.
-    this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
-    this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
-{
-     
-     
-    
-    replaceTag: function(node) {
-        
-        
-      
-        if (node.hasAttribute('width')) {
-            node.removeAttribute('width');
-        }
-        
-         
-        if (node.hasAttribute("style")) {
-            // pretty basic...
-            
-            var styles = node.getAttribute("style").split(";");
-            var nstyle = [];
-            Roo.each(styles, function(s) {
-                if (!s.match(/:/)) {
-                    return;
-                }
-                var kv = s.split(":");
-                if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
-                    return;
-                }
-                // what ever is left... we allow.
-                nstyle.push(s);
-            });
-            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
-            if (!nstyle.length) {
-                node.removeAttribute('style');
-            }
-        }
-        
-        return true; // continue doing children..
-    }
-});/**
- * @class Roo.htmleditor.FilterWord
- * try and clean up all the mess that Word generates.
- * 
- * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
- * @constructor
- * Run a new Span Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterWord = function(cfg)
-{
-    // no need to apply config.
-    this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
-{
-    tag: true,
-     
-    
-    /**
-     * Clean up MS wordisms...
-     */
-    replaceTag : function(node)
-    {
-         
-        // no idea what this does - span with text, replaceds with just text.
-        if(
-                node.nodeName == 'SPAN' &&
-                !node.hasAttributes() &&
-                node.childNodes.length == 1 &&
-                node.firstChild.nodeName == "#text"  
-        ) {
-            var textNode = node.firstChild;
-            node.removeChild(textNode);
-            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
-                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
-            }
-            node.parentNode.insertBefore(textNode, node);
-            if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
-                node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
-            }
-            
-            node.parentNode.removeChild(node);
-            return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
-        }
-        
-   
-        
-        if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
-            node.parentNode.removeChild(node);
-            return false; // dont do chidlren
-        }
-        //Roo.log(node.tagName);
-        // remove - but keep children..
-        if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
-            //Roo.log('-- removed');
-            while (node.childNodes.length) {
-                var cn = node.childNodes[0];
-                node.removeChild(cn);
-                node.parentNode.insertBefore(cn, node);
-                // move node to parent - and clean it..
-                this.replaceTag(cn);
-            }
-            node.parentNode.removeChild(node);
-            /// no need to iterate chidlren = it's got none..
-            //this.iterateChildren(node, this.cleanWord);
-            return false; // no need to iterate children.
-        }
-        // clean styles
-        if (node.className.length) {
-            
-            var cn = node.className.split(/\W+/);
-            var cna = [];
-            Roo.each(cn, function(cls) {
-                if (cls.match(/Mso[a-zA-Z]+/)) {
-                    return;
-                }
-                cna.push(cls);
-            });
-            node.className = cna.length ? cna.join(' ') : '';
-            if (!cna.length) {
-                node.removeAttribute("class");
-            }
-        }
-        
-        if (node.hasAttribute("lang")) {
-            node.removeAttribute("lang");
-        }
-        
-        if (node.hasAttribute("style")) {
-            
-            var styles = node.getAttribute("style").split(";");
-            var nstyle = [];
-            Roo.each(styles, function(s) {
-                if (!s.match(/:/)) {
-                    return;
-                }
-                var kv = s.split(":");
-                if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
-                    return;
-                }
-                // what ever is left... we allow.
-                nstyle.push(s);
-            });
-            node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
-            if (!nstyle.length) {
-                node.removeAttribute('style');
-            }
-        }
-        return true; // do children
-        
-        
-        
-    }
-});
-/**
- * @class Roo.htmleditor.FilterStyleToTag
- * part of the word stuff... - certain 'styles' should be converted to tags.
- * eg.
- *   font-weight: bold -> bold
- *   ?? super / subscrit etc..
- * 
- * @constructor
-* Run a new style to tag filter.
-* @param {Object} config Configuration options
- */
-Roo.htmleditor.FilterStyleToTag = function(cfg)
-{
-    
-    this.tags = {
-        B  : [ 'fontWeight' , 'bold'],
-        I :  [ 'fontStyle' , 'italic'],
-        //pre :  [ 'font-style' , 'italic'],
-        // h1.. h6 ?? font-size?
-        SUP : [ 'verticalAlign' , 'super' ],
-        SUB : [ 'verticalAlign' , 'sub' ]
-        
-        
-    };
-    
-    Roo.apply(this, cfg);
-     
-    
-    this.walk(cfg.node);
-    
-    
-    
-}
-
-
-Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
-{
-    tag: true, // all tags
-    
-    tags : false,
-    
-    
-    replaceTag : function(node)
-    {
-        
-        
-        if (node.getAttribute("style") === null) {
-            return true;
-        }
-        var inject = [];
-        for (var k in this.tags) {
-            if (node.style[this.tags[k][0]] == this.tags[k][1]) {
-                inject.push(k);
-                node.style.removeProperty(this.tags[k][0]);
-            }
-        }
-        if (!inject.length) {
-            return true; 
-        }
-        var cn = Array.from(node.childNodes);
-        var nn = node;
-        Roo.each(inject, function(t) {
-            var nc = node.ownerDocument.createElement(t);
-            nn.appendChild(nc);
-            nn = nc;
-        });
-        for(var i = 0;i < cn.length;cn++) {
-            node.removeChild(cn[i]);
-            nn.appendChild(cn[i]);
-        }
-        return true /// iterate thru
-    }
-    
-})/**
- * @class Roo.htmleditor.FilterLongBr
- * BR/BR/BR - keep a maximum of 2...
- * @constructor
- * Run a new Long BR Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterLongBr = function(cfg)
-{
-    // no need to apply config.
-    this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
-{
-    
-     
-    tag : 'BR',
-    
-     
-    replaceTag : function(node)
-    {
-        
-        var ps = node.nextSibling;
-        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
-            ps = ps.nextSibling;
-        }
-        
-        if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
-            node.parentNode.removeChild(node); // remove last BR inside one fo these tags
-            return false;
-        }
-        
-        if (!ps || ps.nodeType != 1) {
-            return false;
-        }
-        
-        if (!ps || ps.tagName != 'BR') {
-           
-            return false;
-        }
-        
-        
-        
-        
-        
-        if (!node.previousSibling) {
-            return false;
-        }
-        var ps = node.previousSibling;
-        
-        while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
-            ps = ps.previousSibling;
-        }
-        if (!ps || ps.nodeType != 1) {
-            return false;
-        }
-        // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
-        if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
-            return false;
-        }
-        
-        node.parentNode.removeChild(node); // remove me...
-        
-        return false; // no need to do children
-
-    }
-    
-}); 
-
-/**
- * @class Roo.htmleditor.FilterBlock
- * removes id / data-block and contenteditable that are associated with blocks
- * usage should be done on a cloned copy of the dom
- * @constructor
-* Run a new Attribute Filter { node : xxxx }}
-* @param {Object} config Configuration options
- */
-Roo.htmleditor.FilterBlock = function(cfg)
-{
-    Roo.apply(this, cfg);
-    var qa = cfg.node.querySelectorAll;
-    this.removeAttributes('data-block');
-    this.removeAttributes('contenteditable');
-    this.removeAttributes('id');
-    
-}
-
-Roo.apply(Roo.htmleditor.FilterBlock.prototype,
-{
-    node: true, // all tags
-     
-     
-    removeAttributes : function(attr)
-    {
-        var ar = this.node.querySelectorAll('*[' + attr + ']');
-        for (var i =0;i<ar.length;i++) {
-            ar[i].removeAttribute(attr);
-        }
-    }
-        
-        
-        
-    
-});
-/***
- * This is based loosely on tinymce 
- * @class Roo.htmleditor.TidySerializer
- * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
- * @constructor
- * @method Serializer
- * @param {Object} settings Name/value settings object.
- */
-
-
-Roo.htmleditor.TidySerializer = function(settings)
-{
-    Roo.apply(this, settings);
-    
-    this.writer = new Roo.htmleditor.TidyWriter(settings);
-    
-    
-
-};
-Roo.htmleditor.TidySerializer.prototype = {
-    
-    /**
-     * @param {boolean} inner do the inner of the node.
-     */
-    inner : false,
-    
-    writer : false,
-    
-    /**
-    * Serializes the specified node into a string.
-    *
-    * @example
-    * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
-    * @method serialize
-    * @param {DomElement} node Node instance to serialize.
-    * @return {String} String with HTML based on DOM tree.
-    */
-    serialize : function(node) {
-        
-        // = settings.validate;
-        var writer = this.writer;
-        var self  = this;
-        this.handlers = {
-            // #text
-            3: function(node) {
-                
-                writer.text(node.nodeValue, node);
-            },
-            // #comment
-            8: function(node) {
-                writer.comment(node.nodeValue);
-            },
-            // Processing instruction
-            7: function(node) {
-                writer.pi(node.name, node.nodeValue);
-            },
-            // Doctype
-            10: function(node) {
-                writer.doctype(node.nodeValue);
-            },
-            // CDATA
-            4: function(node) {
-                writer.cdata(node.nodeValue);
-            },
-            // Document fragment
-            11: function(node) {
-                node = node.firstChild;
-                if (!node) {
-                    return;
-                }
-                while(node) {
-                    self.walk(node);
-                    node = node.nextSibling
-                }
-            }
-        };
-        writer.reset();
-        1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
-        return writer.getContent();
-    },
-
-    walk: function(node)
-    {
-        var attrName, attrValue, sortedAttrs, i, l, elementRule,
-            handler = this.handlers[node.nodeType];
-            
-        if (handler) {
-            handler(node);
-            return;
-        }
-    
-        var name = node.nodeName;
-        var isEmpty = node.childNodes.length < 1;
-      
-        var writer = this.writer;
-        var attrs = node.attributes;
-        // Sort attributes
-        
-        writer.start(node.nodeName, attrs, isEmpty, node);
-        if (isEmpty) {
-            return;
-        }
-        node = node.firstChild;
-        if (!node) {
-            writer.end(name);
-            return;
-        }
-        while (node) {
-            this.walk(node);
-            node = node.nextSibling;
-        }
-        writer.end(name);
-        
-    
-    }
-    // Serialize element and treat all non elements as fragments
-   
-}; 
-
-/***
- * This is based loosely on tinymce 
- * @class Roo.htmleditor.TidyWriter
- * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
- *
- * Known issues?
- * - not tested much with 'PRE' formated elements.
- * 
- *
- *
- */
-
-Roo.htmleditor.TidyWriter = function(settings)
-{
-    
-    // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
-    Roo.apply(this, settings);
-    this.html = [];
-    this.state = [];
-     
-    this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
-  
-}
-Roo.htmleditor.TidyWriter.prototype = {
-
-    state : false,
-    
-    indent :  '  ',
-    
-    // part of state...
-    indentstr : '',
-    in_pre: false,
-    in_inline : false,
-    last_inline : false,
-    encode : false,
-     
-    
-            /**
-    * Writes the a start element such as <p id="a">.
-    *
-    * @method start
-    * @param {String} name Name of the element.
-    * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
-    * @param {Boolean} empty Optional empty state if the tag should end like <br />.
-    */
-    start: function(name, attrs, empty, node)
-    {
-        var i, l, attr, value;
-        
-        // there are some situations where adding line break && indentation will not work. will not work.
-        // <span / b / i ... formating?
-        
-        var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
-        var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
-        
-        var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
-        
-        var add_lb = name == 'BR' ? false : in_inline;
-        
-        if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
-            i_inline = false;
-        }
-
-        var indentstr =  this.indentstr;
-        
-        // e_inline = elements that can be inline, but still allow \n before and after?
-        // only 'BR' ??? any others?
-        
-        // ADD LINE BEFORE tage
-        if (!this.in_pre) {
-            if (in_inline) {
-                //code
-                if (name == 'BR') {
-                    this.addLine();
-                } else if (this.lastElementEndsWS()) {
-                    this.addLine();
-                } else{
-                    // otherwise - no new line. (and dont indent.)
-                    indentstr = '';
-                }
-                
-            } else {
-                this.addLine();
-            }
-        } else {
-            indentstr = '';
-        }
-        
-        this.html.push(indentstr + '<', name.toLowerCase());
-        
-        if (attrs) {
-            for (i = 0, l = attrs.length; i < l; i++) {
-                attr = attrs[i];
-                this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
-            }
-        }
-     
-        if (empty) {
-            if (is_short) {
-                this.html[this.html.length] = '/>';
-            } else {
-                this.html[this.html.length] = '></' + name.toLowerCase() + '>';
-            }
-            var e_inline = name == 'BR' ? false : this.in_inline;
-            
-            if (!e_inline && !this.in_pre) {
-                this.addLine();
-            }
-            return;
-        
-        }
-        // not empty..
-        this.html[this.html.length] = '>';
-        
-        // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
-        /*
-        if (!in_inline && !in_pre) {
-            var cn = node.firstChild;
-            while(cn) {
-                if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
-                    in_inline = true
-                    break;
-                }
-                cn = cn.nextSibling;
-            }
-             
-        }
-        */
-        
-        
-        this.pushState({
-            indentstr : in_pre   ? '' : (this.indentstr + this.indent),
-            in_pre : in_pre,
-            in_inline :  in_inline
-        });
-        // add a line after if we are not in a
-        
-        if (!in_inline && !in_pre) {
-            this.addLine();
-        }
-        
-            
-         
-        
-    },
-    
-    lastElementEndsWS : function()
-    {
-        var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
-        if (value === false) {
-            return true;
-        }
-        return value.match(/\s+$/);
-        
-    },
-    
-    /**
-     * Writes the a end element such as </p>.
-     *
-     * @method end
-     * @param {String} name Name of the element.
-     */
-    end: function(name) {
-        var value;
-        this.popState();
-        var indentstr = '';
-        var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
-        
-        if (!this.in_pre && !in_inline) {
-            this.addLine();
-            indentstr  = this.indentstr;
-        }
-        this.html.push(indentstr + '</', name.toLowerCase(), '>');
-        this.last_inline = in_inline;
-        
-        // pop the indent state..
-    },
-    /**
-     * Writes a text node.
-     *
-     * In pre - we should not mess with the contents.
-     * 
-     *
-     * @method text
-     * @param {String} text String to write out.
-     * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
-     */
-    text: function(text, node)
-    {
-        // if not in whitespace critical
-        if (text.length < 1) {
-            return;
-        }
-        if (this.in_pre) {
-            this.html[this.html.length] =  text;
-            return;   
-        }
-        
-        if (this.in_inline) {
-            text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
-            if (text != ' ') {
-                text = text.replace(/\s+/,' ');  // all white space to single white space
-                
-                    
-                // if next tag is '<BR>', then we can trim right..
-                if (node.nextSibling &&
-                    node.nextSibling.nodeType == 1 &&
-                    node.nextSibling.nodeName == 'BR' )
-                {
-                    text = text.replace(/\s+$/g,'');
-                }
-                // if previous tag was a BR, we can also trim..
-                if (node.previousSibling &&
-                    node.previousSibling.nodeType == 1 &&
-                    node.previousSibling.nodeName == 'BR' )
-                {
-                    text = this.indentstr +  text.replace(/^\s+/g,'');
-                }
-                if (text.match(/\n/)) {
-                    text = text.replace(
-                        /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
-                    );
-                    // remoeve the last whitespace / line break.
-                    text = text.replace(/\n\s+$/,'');
-                }
-                // repace long lines
-                
-            }
-             
-            this.html[this.html.length] =  text;
-            return;   
-        }
-        // see if previous element was a inline element.
-        var indentstr = this.indentstr;
-   
-        text = text.replace(/\s+/g," "); // all whitespace into single white space.
-        
-        // should trim left?
-        if (node.previousSibling &&
-            node.previousSibling.nodeType == 1 &&
-            Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
-        {
-            indentstr = '';
-            
-        } else {
-            this.addLine();
-            text = text.replace(/^\s+/,''); // trim left
-          
-        }
-        // should trim right?
-        if (node.nextSibling &&
-            node.nextSibling.nodeType == 1 &&
-            Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
-        {
-          // noop
-            
-        }  else {
-            text = text.replace(/\s+$/,''); // trim right
-        }
-         
-              
-        
-        
-        
-        if (text.length < 1) {
-            return;
-        }
-        if (!text.match(/\n/)) {
-            this.html.push(indentstr + text);
-            return;
-        }
-        
-        text = this.indentstr + text.replace(
-            /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
-        );
-        // remoeve the last whitespace / line break.
-        text = text.replace(/\s+$/,''); 
-        
-        this.html.push(text);
-        
-        // split and indent..
-        
-        
-    },
-    /**
-     * Writes a cdata node such as <![CDATA[data]]>.
-     *
-     * @method cdata
-     * @param {String} text String to write out inside the cdata.
-     */
-    cdata: function(text) {
-        this.html.push('<![CDATA[', text, ']]>');
-    },
-    /**
-    * Writes a comment node such as <!-- Comment -->.
-    *
-    * @method cdata
-    * @param {String} text String to write out inside the comment.
-    */
-   comment: function(text) {
-       this.html.push('<!--', text, '-->');
-   },
-    /**
-     * Writes a PI node such as <?xml attr="value" ?>.
-     *
-     * @method pi
-     * @param {String} name Name of the pi.
-     * @param {String} text String to write out inside the pi.
-     */
-    pi: function(name, text) {
-        text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
-        this.indent != '' && this.html.push('\n');
-    },
-    /**
-     * Writes a doctype node such as <!DOCTYPE data>.
-     *
-     * @method doctype
-     * @param {String} text String to write out inside the doctype.
-     */
-    doctype: function(text) {
-        this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
-    },
-    /**
-     * Resets the internal buffer if one wants to reuse the writer.
-     *
-     * @method reset
-     */
-    reset: function() {
-        this.html.length = 0;
-        this.state = [];
-        this.pushState({
-            indentstr : '',
-            in_pre : false, 
-            in_inline : false
-        })
-    },
-    /**
-     * Returns the contents that got serialized.
-     *
-     * @method getContent
-     * @return {String} HTML contents that got written down.
-     */
-    getContent: function() {
-        return this.html.join('').replace(/\n$/, '');
-    },
-    
-    pushState : function(cfg)
-    {
-        this.state.push(cfg);
-        Roo.apply(this, cfg);
-    },
-    
-    popState : function()
-    {
-        if (this.state.length < 1) {
-            return; // nothing to push
-        }
-        var cfg = {
-            in_pre: false,
-            indentstr : ''
-        };
-        this.state.pop();
-        if (this.state.length > 0) {
-            cfg = this.state[this.state.length-1]; 
-        }
-        Roo.apply(this, cfg);
-    },
-    
-    addLine: function()
-    {
-        if (this.html.length < 1) {
-            return;
-        }
-        
-        
-        var value = this.html[this.html.length - 1];
-        if (value.length > 0 && '\n' !== value) {
-            this.html.push('\n');
-        }
-    }
-    
-    
-//'pre script noscript style textarea video audio iframe object code'
-// shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
-// inline 
-};
-
-Roo.htmleditor.TidyWriter.inline_elements = [
-        'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
-        'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
-];
-Roo.htmleditor.TidyWriter.shortend_elements = [
-    'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
-    'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
-];
-
-Roo.htmleditor.TidyWriter.whitespace_elements = [
-    'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
-];/***
- * This is based loosely on tinymce 
- * @class Roo.htmleditor.TidyEntities
- * @static
- * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
- *
- * Not 100% sure this is actually used or needed.
- */
-
-Roo.htmleditor.TidyEntities = {
-    
-    /**
-     * initialize data..
-     */
-    init : function (){
-     
-        this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
-       
-    },
-
-
-    buildEntitiesLookup: function(items, radix) {
-        var i, chr, entity, lookup = {};
-        if (!items) {
-            return {};
-        }
-        items = typeof(items) == 'string' ? items.split(',') : items;
-        radix = radix || 10;
-        // Build entities lookup table
-        for (i = 0; i < items.length; i += 2) {
-            chr = String.fromCharCode(parseInt(items[i], radix));
-            // Only add non base entities
-            if (!this.baseEntities[chr]) {
-                entity = '&' + items[i + 1] + ';';
-                lookup[chr] = entity;
-                lookup[entity] = chr;
-            }
-        }
-        return lookup;
-        
-    },
-    
-    asciiMap : {
-            128: '€',
-            130: '‚',
-            131: 'ƒ',
-            132: '„',
-            133: '…',
-            134: '†',
-            135: '‡',
-            136: 'ˆ',
-            137: '‰',
-            138: 'Š',
-            139: '‹',
-            140: 'Œ',
-            142: 'Ž',
-            145: '‘',
-            146: '’',
-            147: '“',
-            148: '”',
-            149: '•',
-            150: '–',
-            151: '—',
-            152: '˜',
-            153: '™',
-            154: 'š',
-            155: '›',
-            156: 'œ',
-            158: 'ž',
-            159: 'Ÿ'
-    },
-    // Raw entities
-    baseEntities : {
-        '"': '&quot;',
-        // Needs to be escaped since the YUI compressor would otherwise break the code
-        '\'': '&#39;',
-        '<': '&lt;',
-        '>': '&gt;',
-        '&': '&amp;',
-        '`': '&#96;'
-    },
-    // Reverse lookup table for raw entities
-    reverseEntities : {
-        '&lt;': '<',
-        '&gt;': '>',
-        '&amp;': '&',
-        '&quot;': '"',
-        '&apos;': '\''
-    },
-    
-    attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
-    textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
-    rawCharsRegExp : /[<>&\"\']/g,
-    entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
-    namedEntities  : false,
-    namedEntitiesData : [ 
-        '50',
-        'nbsp',
-        '51',
-        'iexcl',
-        '52',
-        'cent',
-        '53',
-        'pound',
-        '54',
-        'curren',
-        '55',
-        'yen',
-        '56',
-        'brvbar',
-        '57',
-        'sect',
-        '58',
-        'uml',
-        '59',
-        'copy',
-        '5a',
-        'ordf',
-        '5b',
-        'laquo',
-        '5c',
-        'not',
-        '5d',
-        'shy',
-        '5e',
-        'reg',
-        '5f',
-        'macr',
-        '5g',
-        'deg',
-        '5h',
-        'plusmn',
-        '5i',
-        'sup2',
-        '5j',
-        'sup3',
-        '5k',
-        'acute',
-        '5l',
-        'micro',
-        '5m',
-        'para',
-        '5n',
-        'middot',
-        '5o',
-        'cedil',
-        '5p',
-        'sup1',
-        '5q',
-        'ordm',
-        '5r',
-        'raquo',
-        '5s',
-        'frac14',
-        '5t',
-        'frac12',
-        '5u',
-        'frac34',
-        '5v',
-        'iquest',
-        '60',
-        'Agrave',
-        '61',
-        'Aacute',
-        '62',
-        'Acirc',
-        '63',
-        'Atilde',
-        '64',
-        'Auml',
-        '65',
-        'Aring',
-        '66',
-        'AElig',
-        '67',
-        'Ccedil',
-        '68',
-        'Egrave',
-        '69',
-        'Eacute',
-        '6a',
-        'Ecirc',
-        '6b',
-        'Euml',
-        '6c',
-        'Igrave',
-        '6d',
-        'Iacute',
-        '6e',
-        'Icirc',
-        '6f',
-        'Iuml',
-        '6g',
-        'ETH',
-        '6h',
-        'Ntilde',
-        '6i',
-        'Ograve',
-        '6j',
-        'Oacute',
-        '6k',
-        'Ocirc',
-        '6l',
-        'Otilde',
-        '6m',
-        'Ouml',
-        '6n',
-        'times',
-        '6o',
-        'Oslash',
-        '6p',
-        'Ugrave',
-        '6q',
-        'Uacute',
-        '6r',
-        'Ucirc',
-        '6s',
-        'Uuml',
-        '6t',
-        'Yacute',
-        '6u',
-        'THORN',
-        '6v',
-        'szlig',
-        '70',
-        'agrave',
-        '71',
-        'aacute',
-        '72',
-        'acirc',
-        '73',
-        'atilde',
-        '74',
-        'auml',
-        '75',
-        'aring',
-        '76',
-        'aelig',
-        '77',
-        'ccedil',
-        '78',
-        'egrave',
-        '79',
-        'eacute',
-        '7a',
-        'ecirc',
-        '7b',
-        'euml',
-        '7c',
-        'igrave',
-        '7d',
-        'iacute',
-        '7e',
-        'icirc',
-        '7f',
-        'iuml',
-        '7g',
-        'eth',
-        '7h',
-        'ntilde',
-        '7i',
-        'ograve',
-        '7j',
-        'oacute',
-        '7k',
-        'ocirc',
-        '7l',
-        'otilde',
-        '7m',
-        'ouml',
-        '7n',
-        'divide',
-        '7o',
-        'oslash',
-        '7p',
-        'ugrave',
-        '7q',
-        'uacute',
-        '7r',
-        'ucirc',
-        '7s',
-        'uuml',
-        '7t',
-        'yacute',
-        '7u',
-        'thorn',
-        '7v',
-        'yuml',
-        'ci',
-        'fnof',
-        'sh',
-        'Alpha',
-        'si',
-        'Beta',
-        'sj',
-        'Gamma',
-        'sk',
-        'Delta',
-        'sl',
-        'Epsilon',
-        'sm',
-        'Zeta',
-        'sn',
-        'Eta',
-        'so',
-        'Theta',
-        'sp',
-        'Iota',
-        'sq',
-        'Kappa',
-        'sr',
-        'Lambda',
-        'ss',
-        'Mu',
-        'st',
-        'Nu',
-        'su',
-        'Xi',
-        'sv',
-        'Omicron',
-        't0',
-        'Pi',
-        't1',
-        'Rho',
-        't3',
-        'Sigma',
-        't4',
-        'Tau',
-        't5',
-        'Upsilon',
-        't6',
-        'Phi',
-        't7',
-        'Chi',
-        't8',
-        'Psi',
-        't9',
-        'Omega',
-        'th',
-        'alpha',
-        'ti',
-        'beta',
-        'tj',
-        'gamma',
-        'tk',
-        'delta',
-        'tl',
-        'epsilon',
-        'tm',
-        'zeta',
-        'tn',
-        'eta',
-        'to',
-        'theta',
-        'tp',
-        'iota',
-        'tq',
-        'kappa',
-        'tr',
-        'lambda',
-        'ts',
-        'mu',
-        'tt',
-        'nu',
-        'tu',
-        'xi',
-        'tv',
-        'omicron',
-        'u0',
-        'pi',
-        'u1',
-        'rho',
-        'u2',
-        'sigmaf',
-        'u3',
-        'sigma',
-        'u4',
-        'tau',
-        'u5',
-        'upsilon',
-        'u6',
-        'phi',
-        'u7',
-        'chi',
-        'u8',
-        'psi',
-        'u9',
-        'omega',
-        'uh',
-        'thetasym',
-        'ui',
-        'upsih',
-        'um',
-        'piv',
-        '812',
-        'bull',
-        '816',
-        'hellip',
-        '81i',
-        'prime',
-        '81j',
-        'Prime',
-        '81u',
-        'oline',
-        '824',
-        'frasl',
-        '88o',
-        'weierp',
-        '88h',
-        'image',
-        '88s',
-        'real',
-        '892',
-        'trade',
-        '89l',
-        'alefsym',
-        '8cg',
-        'larr',
-        '8ch',
-        'uarr',
-        '8ci',
-        'rarr',
-        '8cj',
-        'darr',
-        '8ck',
-        'harr',
-        '8dl',
-        'crarr',
-        '8eg',
-        'lArr',
-        '8eh',
-        'uArr',
-        '8ei',
-        'rArr',
-        '8ej',
-        'dArr',
-        '8ek',
-        'hArr',
-        '8g0',
-        'forall',
-        '8g2',
-        'part',
-        '8g3',
-        'exist',
-        '8g5',
-        'empty',
-        '8g7',
-        'nabla',
-        '8g8',
-        'isin',
-        '8g9',
-        'notin',
-        '8gb',
-        'ni',
-        '8gf',
-        'prod',
-        '8gh',
-        'sum',
-        '8gi',
-        'minus',
-        '8gn',
-        'lowast',
-        '8gq',
-        'radic',
-        '8gt',
-        'prop',
-        '8gu',
-        'infin',
-        '8h0',
-        'ang',
-        '8h7',
-        'and',
-        '8h8',
-        'or',
-        '8h9',
-        'cap',
-        '8ha',
-        'cup',
-        '8hb',
-        'int',
-        '8hk',
-        'there4',
-        '8hs',
-        'sim',
-        '8i5',
-        'cong',
-        '8i8',
-        'asymp',
-        '8j0',
-        'ne',
-        '8j1',
-        'equiv',
-        '8j4',
-        'le',
-        '8j5',
-        'ge',
-        '8k2',
-        'sub',
-        '8k3',
-        'sup',
-        '8k4',
-        'nsub',
-        '8k6',
-        'sube',
-        '8k7',
-        'supe',
-        '8kl',
-        'oplus',
-        '8kn',
-        'otimes',
-        '8l5',
-        'perp',
-        '8m5',
-        'sdot',
-        '8o8',
-        'lceil',
-        '8o9',
-        'rceil',
-        '8oa',
-        'lfloor',
-        '8ob',
-        'rfloor',
-        '8p9',
-        'lang',
-        '8pa',
-        'rang',
-        '9ea',
-        'loz',
-        '9j0',
-        'spades',
-        '9j3',
-        'clubs',
-        '9j5',
-        'hearts',
-        '9j6',
-        'diams',
-        'ai',
-        'OElig',
-        'aj',
-        'oelig',
-        'b0',
-        'Scaron',
-        'b1',
-        'scaron',
-        'bo',
-        'Yuml',
-        'm6',
-        'circ',
-        'ms',
-        'tilde',
-        '802',
-        'ensp',
-        '803',
-        'emsp',
-        '809',
-        'thinsp',
-        '80c',
-        'zwnj',
-        '80d',
-        'zwj',
-        '80e',
-        'lrm',
-        '80f',
-        'rlm',
-        '80j',
-        'ndash',
-        '80k',
-        'mdash',
-        '80o',
-        'lsquo',
-        '80p',
-        'rsquo',
-        '80q',
-        'sbquo',
-        '80s',
-        'ldquo',
-        '80t',
-        'rdquo',
-        '80u',
-        'bdquo',
-        '810',
-        'dagger',
-        '811',
-        'Dagger',
-        '81g',
-        'permil',
-        '81p',
-        'lsaquo',
-        '81q',
-        'rsaquo',
-        '85c',
-        'euro'
-    ],
-
-         
-    /**
-     * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
-     *
-     * @method encodeRaw
-     * @param {String} text Text to encode.
-     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
-     * @return {String} Entity encoded text.
-     */
-    encodeRaw: function(text, attr)
-    {
-        var t = this;
-        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
-            return t.baseEntities[chr] || chr;
-        });
-    },
-    /**
-     * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
-     * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
-     * and is exposed as the DOMUtils.encode function.
-     *
-     * @method encodeAllRaw
-     * @param {String} text Text to encode.
-     * @return {String} Entity encoded text.
-     */
-    encodeAllRaw: function(text) {
-        var t = this;
-        return ('' + text).replace(this.rawCharsRegExp, function(chr) {
-            return t.baseEntities[chr] || chr;
-        });
-    },
-    /**
-     * Encodes the specified string using numeric entities. The core entities will be
-     * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
-     *
-     * @method encodeNumeric
-     * @param {String} text Text to encode.
-     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
-     * @return {String} Entity encoded text.
-     */
-    encodeNumeric: function(text, attr) {
-        var t = this;
-        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
-            // Multi byte sequence convert it to a single entity
-            if (chr.length > 1) {
-                return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
-            }
-            return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
-        });
-    },
-    /**
-     * Encodes the specified string using named entities. The core entities will be encoded
-     * as named ones but all non lower ascii characters will be encoded into named entities.
-     *
-     * @method encodeNamed
-     * @param {String} text Text to encode.
-     * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
-     * @param {Object} entities Optional parameter with entities to use.
-     * @return {String} Entity encoded text.
-     */
-    encodeNamed: function(text, attr, entities) {
-        var t = this;
-        entities = entities || this.namedEntities;
-        return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
-            return t.baseEntities[chr] || entities[chr] || chr;
-        });
-    },
-    /**
-     * Returns an encode function based on the name(s) and it's optional entities.
-     *
-     * @method getEncodeFunc
-     * @param {String} name Comma separated list of encoders for example named,numeric.
-     * @param {String} entities Optional parameter with entities to use instead of the built in set.
-     * @return {function} Encode function to be used.
-     */
-    getEncodeFunc: function(name, entities) {
-        entities = this.buildEntitiesLookup(entities) || this.namedEntities;
-        var t = this;
-        function encodeNamedAndNumeric(text, attr) {
-            return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
-                return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
-            });
-        }
-
-        function encodeCustomNamed(text, attr) {
-            return t.encodeNamed(text, attr, entities);
-        }
-        // Replace + with , to be compatible with previous TinyMCE versions
-        name = this.makeMap(name.replace(/\+/g, ','));
-        // Named and numeric encoder
-        if (name.named && name.numeric) {
-            return this.encodeNamedAndNumeric;
-        }
-        // Named encoder
-        if (name.named) {
-            // Custom names
-            if (entities) {
-                return encodeCustomNamed;
-            }
-            return this.encodeNamed;
-        }
-        // Numeric
-        if (name.numeric) {
-            return this.encodeNumeric;
-        }
-        // Raw encoder
-        return this.encodeRaw;
-    },
-    /**
-     * Decodes the specified string, this will replace entities with raw UTF characters.
-     *
-     * @method decode
-     * @param {String} text Text to entity decode.
-     * @return {String} Entity decoded string.
-     */
-    decode: function(text)
-    {
-        var  t = this;
-        return text.replace(this.entityRegExp, function(all, numeric) {
-            if (numeric) {
-                numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
-                // Support upper UTF
-                if (numeric > 65535) {
-                    numeric -= 65536;
-                    return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
-                }
-                return t.asciiMap[numeric] || String.fromCharCode(numeric);
-            }
-            return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
-        });
-    },
-    nativeDecode : function (text) {
-        return text;
-    },
-    makeMap : function (items, delim, map) {
-               var i;
-               items = items || [];
-               delim = delim || ',';
-               if (typeof items == "string") {
-                       items = items.split(delim);
-               }
-               map = map || {};
-               i = items.length;
-               while (i--) {
-                       map[items[i]] = {};
-               }
-               return map;
-       }
-};
-    
-    
-    
-Roo.htmleditor.TidyEntities.init();
-/**
- * @class Roo.htmleditor.KeyEnter
- * Handle Enter press..
- * @cfg {Roo.HtmlEditorCore} core the editor.
- * @constructor
- * Create a new Filter.
- * @param {Object} config Configuration options
- */
-
-
-
-
-
-Roo.htmleditor.KeyEnter = function(cfg) {
-    Roo.apply(this, cfg);
-    // this does not actually call walk as it's really just a abstract class
-    Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
-}
-
-//Roo.htmleditor.KeyEnter.i = 0;
-
-
-Roo.htmleditor.KeyEnter.prototype = {
-    
-    core : false,
-    
-    keypress : function(e)
-    {
-        if (e.charCode != 13 && e.charCode != 10) {
-            Roo.log([e.charCode,e]);
-            return true;
-        }
-        e.preventDefault();
-        // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
-        var doc = this.core.doc;
-          //add a new line
-       
-    
-        var sel = this.core.getSelection();
-        var range = sel.getRangeAt(0);
-        var n = range.commonAncestorContainer;
-        var pc = range.closest([ 'ol', 'ul']);
-        var pli = range.closest('li');
-        if (!pc || e.ctrlKey) {
-            sel.insertNode('br', 'after'); 
-         
-            this.core.undoManager.addEvent();
-            this.core.fireEditorEvent(e);
-            return false;
-        }
-        
-        // deal with <li> insetion
-        if (pli.innerText.trim() == '' &&
-            pli.previousSibling &&
-            pli.previousSibling.nodeName == 'LI' &&
-            pli.previousSibling.innerText.trim() ==  '') {
-            pli.parentNode.removeChild(pli.previousSibling);
-            sel.cursorAfter(pc);
-            this.core.undoManager.addEvent();
-            this.core.fireEditorEvent(e);
-            return false;
-        }
-    
-        var li = doc.createElement('LI');
-        li.innerHTML = '&nbsp;';
-        if (!pli || !pli.firstSibling) {
-            pc.appendChild(li);
-        } else {
-            pli.parentNode.insertBefore(li, pli.firstSibling);
-        }
-        sel.cursorText (li.firstChild);
-      
-        this.core.undoManager.addEvent();
-        this.core.fireEditorEvent(e);
-
-        return false;
-        
-    
-        
-        
-         
-    }
-};
-     
-/**
- * @class Roo.htmleditor.Block
- * Base class for html editor blocks - do not use it directly .. extend it..
- * @cfg {DomElement} node The node to apply stuff to.
- * @cfg {String} friendly_name the name that appears in the context bar about this block
- * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
- * @constructor
- * Create a new Filter.
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.Block  = function(cfg)
-{
-    // do nothing .. should not be called really.
-}
-/**
- * factory method to get the block from an element (using cache if necessary)
- * @static
- * @param {HtmlElement} the dom element
- */
-Roo.htmleditor.Block.factory = function(node)
-{
-    var cc = Roo.htmleditor.Block.cache;
-    var id = Roo.get(node).id;
-    if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
-        Roo.htmleditor.Block.cache[id].readElement(node);
-        return Roo.htmleditor.Block.cache[id];
-    }
-    var db  = node.getAttribute('data-block');
-    if (!db) {
-        db = node.nodeName.toLowerCase().toUpperCaseFirst();
-    }
-    var cls = Roo.htmleditor['Block' + db];
-    if (typeof(cls) == 'undefined') {
-        //Roo.log(node.getAttribute('data-block'));
-        Roo.log("OOps missing block : " + 'Block' + db);
-        return false;
-    }
-    Roo.htmleditor.Block.cache[id] = new cls({ node: node });
-    return Roo.htmleditor.Block.cache[id];  /// should trigger update element
-};
-
-/**
- * initalize all Elements from content that are 'blockable'
- * @static
- * @param the body element
- */
-Roo.htmleditor.Block.initAll = function(body, type)
-{
-    if (typeof(type) == 'undefined') {
-        var ia = Roo.htmleditor.Block.initAll;
-        ia(body,'table');
-        ia(body,'td');
-        ia(body,'figure');
-        return;
-    }
-    Roo.each(Roo.get(body).query(type), function(e) {
-        Roo.htmleditor.Block.factory(e);    
-    },this);
-};
-// question goes here... do we need to clear out this cache sometimes?
-// or show we make it relivant to the htmleditor.
-Roo.htmleditor.Block.cache = {};
-
-Roo.htmleditor.Block.prototype = {
-    
-    node : false,
-    
-     // used by context menu
-    friendly_name : 'Based Block',
-    
-    // text for button to delete this element
-    deleteTitle : false,
-    
-    context : false,
-    /**
-     * Update a node with values from this object
-     * @param {DomElement} node
-     */
-    updateElement : function(node)
-    {
-        Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
-    },
-     /**
-     * convert to plain HTML for calling insertAtCursor..
-     */
-    toHTML : function()
-    {
-        return Roo.DomHelper.markup(this.toObject());
-    },
-    /**
-     * used by readEleemnt to extract data from a node
-     * may need improving as it's pretty basic
-     
-     * @param {DomElement} node
-     * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
-     * @param {String} attribute (use html - for contents, or style for using next param as style)
-     * @param {String} style the style property - eg. text-align
-     */
-    getVal : function(node, tag, attr, style)
-    {
-        var n = node;
-        if (tag !== true && n.tagName != tag.toUpperCase()) {
-            // in theory we could do figure[3] << 3rd figure? or some more complex search..?
-            // but kiss for now.
-            n = node.getElementsByTagName(tag).item(0);
-        }
-        if (!n) {
-            return '';
-        }
-        if (attr == 'html') {
-            return n.innerHTML;
-        }
-        if (attr == 'style') {
-            return n.style[style]; 
-        }
-        
-        return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
-            
-    },
-    /**
-     * create a DomHelper friendly object - for use with 
-     * Roo.DomHelper.markup / overwrite / etc..
-     * (override this)
-     */
-    toObject : function()
-    {
-        return {};
-    },
-      /**
-     * Read a node that has a 'data-block' property - and extract the values from it.
-     * @param {DomElement} node - the node
-     */
-    readElement : function(node)
-    {
-        
-    } 
-    
-    
-};
-
-
-/**
- * @class Roo.htmleditor.BlockFigure
- * Block that has an image and a figcaption
- * @cfg {String} image_src the url for the image
- * @cfg {String} align (left|right) alignment for the block default left
- * @cfg {String} caption the text to appear below  (and in the alt tag)
- * @cfg {String} caption_display (block|none) display or not the caption
- * @cfg {String|number} image_width the width of the image number or %?
- * @cfg {String|number} image_height the height of the image number or %?
- * 
- * @constructor
- * Create a new Filter.
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.BlockFigure = function(cfg)
-{
-    if (cfg.node) {
-        this.readElement(cfg.node);
-        this.updateElement(cfg.node);
-    }
-    Roo.apply(this, cfg);
-}
-Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
-    
-    // setable values.
-    image_src: '',
-    align: 'center',
-    caption : '',
-    caption_display : 'block',
-    width : '100%',
-    cls : '',
-    href: '',
-    video_url : '',
-    
-    // margin: '2%', not used
-    
-    text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
-
-    
-    // used by context menu
-    friendly_name : 'Image with caption',
-    deleteTitle : "Delete Image and Caption",
-    
-    contextMenu : function(toolbar)
-    {
-        
-        var block = function() {
-            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
-        };
-        
-        
-        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
-        
-        var syncValue = toolbar.editorcore.syncValue;
-        
-        var fields = {};
-        
-        return [
-             {
-                xtype : 'TextItem',
-                text : "Source: ",
-                xns : rooui.Toolbar  //Boostrap?
-            },
-            {
-                xtype : 'Button',
-                text: 'Change Image URL',
-                 
-                listeners : {
-                    click: function (btn, state)
-                    {
-                        var b = block();
-                        
-                        Roo.MessageBox.show({
-                            title : "Image Source URL",
-                            msg : "Enter the url for the image",
-                            buttons: Roo.MessageBox.OKCANCEL,
-                            fn: function(btn, val){
-                                if (btn != 'ok') {
-                                    return;
-                                }
-                                b.image_src = val;
-                                b.updateElement();
-                                syncValue();
-                                toolbar.editorcore.onEditorEvent();
-                            },
-                            minWidth:250,
-                            prompt:true,
-                            //multiline: multiline,
-                            modal : true,
-                            value : b.image_src
-                        });
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-         
-            {
-                xtype : 'Button',
-                text: 'Change Link URL',
-                 
-                listeners : {
-                    click: function (btn, state)
-                    {
-                        var b = block();
-                        
-                        Roo.MessageBox.show({
-                            title : "Link URL",
-                            msg : "Enter the url for the link - leave blank to have no link",
-                            buttons: Roo.MessageBox.OKCANCEL,
-                            fn: function(btn, val){
-                                if (btn != 'ok') {
-                                    return;
-                                }
-                                b.href = val;
-                                b.updateElement();
-                                syncValue();
-                                toolbar.editorcore.onEditorEvent();
-                            },
-                            minWidth:250,
-                            prompt:true,
-                            //multiline: multiline,
-                            modal : true,
-                            value : b.href
-                        });
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-            {
-                xtype : 'Button',
-                text: 'Show Video URL',
-                 
-                listeners : {
-                    click: function (btn, state)
-                    {
-                        Roo.MessageBox.alert("Video URL",
-                            block().video_url == '' ? 'This image is not linked ot a video' :
-                                'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-            
-            
-            {
-                xtype : 'TextItem',
-                text : "Width: ",
-                xns : rooui.Toolbar  //Boostrap?
-            },
-            {
-                xtype : 'ComboBox',
-                allowBlank : false,
-                displayField : 'val',
-                editable : true,
-                listWidth : 100,
-                triggerAction : 'all',
-                typeAhead : true,
-                valueField : 'val',
-                width : 70,
-                name : 'width',
-                listeners : {
-                    select : function (combo, r, index)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        var b = block();
-                        b.width = r.get('val');
-                        b.updateElement();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.form,
-                store : {
-                    xtype : 'SimpleStore',
-                    data : [
-                        ['auto'],
-                        ['50%'],
-                        ['100%']
-                    ],
-                    fields : [ 'val'],
-                    xns : Roo.data
-                }
-            },
-            {
-                xtype : 'TextItem',
-                text : "Align: ",
-                xns : rooui.Toolbar  //Boostrap?
-            },
-            {
-                xtype : 'ComboBox',
-                allowBlank : false,
-                displayField : 'val',
-                editable : true,
-                listWidth : 100,
-                triggerAction : 'all',
-                typeAhead : true,
-                valueField : 'val',
-                width : 70,
-                name : 'align',
-                listeners : {
-                    select : function (combo, r, index)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        var b = block();
-                        b.align = r.get('val');
-                        b.updateElement();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.form,
-                store : {
-                    xtype : 'SimpleStore',
-                    data : [
-                        ['left'],
-                        ['right'],
-                        ['center']
-                    ],
-                    fields : [ 'val'],
-                    xns : Roo.data
-                }
-            },
-            
-            
-            {
-                xtype : 'Button',
-                text: 'Hide Caption',
-                name : 'caption_display',
-                pressed : false,
-                enableToggle : true,
-                setValue : function(v) {
-                    this.toggle(v == 'block' ? false : true);
-                },
-                listeners : {
-                    toggle: function (btn, state)
-                    {
-                        var b  = block();
-                        b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
-                        this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
-                        b.updateElement();
-                        syncValue();
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.Toolbar
-            }
-        ];
-        
-    },
-    /**
-     * create a DomHelper friendly object - for use with
-     * Roo.DomHelper.markup / overwrite / etc..
-     */
-    toObject : function()
-    {
-        var d = document.createElement('div');
-        d.innerHTML = this.caption;
-        
-        var m = this.width == '50%' && this.align == 'center' ? '0 auto' : 0; 
-        
-        var img =   {
-            tag : 'img',
-            contenteditable : 'false',
-            src : this.image_src,
-            alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
-            style: {
-                width : 'auto',
-                'max-width': '100%',
-                margin : '0px' 
-                
-                
-            }
-        };
-        /*
-        '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
-                    '<a href="{2}">' + 
-                        '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
-                    '</a>' + 
-                '</div>',
-        */
-                
-        if (this.href.length > 0) {
-            img = {
-                tag : 'a',
-                href: this.href,
-                contenteditable : 'true',
-                cn : [
-                    img
-                ]
-            };
-        }
-        
-        
-        if (this.video_url.length > 0) {
-            img = {
-                tag : 'div',
-                cls : this.cls,
-                frameborder : 0,
-                allowfullscreen : true,
-                width : 420,  // these are for video tricks - that we replace the outer
-                height : 315,
-                src : this.video_url,
-                cn : [
-                    img
-                ]
-            };
-        }
-        
-        var captionhtml = this.caption_display == 'hidden' ? this.caption : (this.caption.length ? this.caption : "Caption");
-        
-        return  {
-            tag: 'figure',
-            'data-block' : 'Figure',
-            contenteditable : 'false',
-            style : {
-                display: 'block',
-                float :  this.align ,
-                'max-width':  this.width,
-                width : 'auto',
-                margin:  m,
-                padding: '10px'
-                
-            },
-           
-            
-            align : this.align,
-            cn : [
-                img,
-              
-                {
-                    tag: 'figcaption',
-                    
-                    style : {
-                        'text-align': 'left',
-                        'margin-top' : '16px',
-                        'font-size' : '16px',
-                        'line-height' : '24px',
-                         display : this.caption_display
-                    },
-                    cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
-                    cn : [
-                        {
-                            // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
-                            tag : 'i',
-                            contenteditable : true,
-                            html : captionhtml
-                        }
-                    ]
-                    
-                }
-            ]
-        };
-         
-    },
-    
-    readElement : function(node)
-    {
-        // this should not really come from the link...
-        this.video_url = this.getVal(node, 'div', 'src');
-        this.cls = this.getVal(node, 'div', 'class');
-        this.href = this.getVal(node, 'a', 'href');
-        
-        this.image_src = this.getVal(node, 'img', 'src');
-         
-        this.align = this.getVal(node, 'figure', 'align');
-        this.caption = this.getVal(node, 'figcaption', 'html');
-        // remove '<i>
-        if (this.caption.trim().match(/^<i[^>]*>/i)) {
-            this.caption = this.caption.trim().replace(/^<i[^>]*>/i, '').replace(/^<\/i>$/i, '');
-        }
-        //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
-        this.width = this.getVal(node, 'figure', 'style', 'max-width');
-        //this.margin = this.getVal(node, 'figure', 'style', 'margin');
-        
-    },
-    removeNode : function()
-    {
-        return this.node;
-    }
-    
-  
-   
-     
-    
-    
-    
-    
-})
-
-
-/**
- * @class Roo.htmleditor.BlockTable
- * Block that manages a table
- * 
- * @constructor
- * Create a new Filter.
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.BlockTable = function(cfg)
-{
-    if (cfg.node) {
-        this.readElement(cfg.node);
-        this.updateElement(cfg.node);
-    }
-    Roo.apply(this, cfg);
-    if (!cfg.node) {
-        this.rows = [];
-        for(var r = 0; r < this.no_row; r++) {
-            this.rows[r] = [];
-            for(var c = 0; c < this.no_col; c++) {
-                this.rows[r][c] = this.emptyCell();
-            }
-        }
-    }
-    
-    
-}
-Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
-    rows : false,
-    no_col : 1,
-    no_row : 1,
-    
-    
-    width: '100%',
-    
-    // used by context menu
-    friendly_name : 'Table',
-    deleteTitle : 'Delete Table',
-    // context menu is drawn once..
-    
-    contextMenu : function(toolbar)
-    {
-        
-        var block = function() {
-            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
-        };
-        
-        
-        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
-        
-        var syncValue = toolbar.editorcore.syncValue;
-        
-        var fields = {};
-        
-        return [
-            {
-                xtype : 'TextItem',
-                text : "Width: ",
-                xns : rooui.Toolbar  //Boostrap?
-            },
-            {
-                xtype : 'ComboBox',
-                allowBlank : false,
-                displayField : 'val',
-                editable : true,
-                listWidth : 100,
-                triggerAction : 'all',
-                typeAhead : true,
-                valueField : 'val',
-                width : 100,
-                name : 'width',
-                listeners : {
-                    select : function (combo, r, index)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        var b = block();
-                        b.width = r.get('val');
-                        b.updateElement();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.form,
-                store : {
-                    xtype : 'SimpleStore',
-                    data : [
-                        ['100%'],
-                        ['auto']
-                    ],
-                    fields : [ 'val'],
-                    xns : Roo.data
-                }
-            },
-            // -------- Cols
-            
-            {
-                xtype : 'TextItem',
-                text : "Columns: ",
-                xns : rooui.Toolbar  //Boostrap?
-            },
-         
-            {
-                xtype : 'Button',
-                text: '-',
-                listeners : {
-                    click : function (_self, e)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        block().removeColumn();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-            {
-                xtype : 'Button',
-                text: '+',
-                listeners : {
-                    click : function (_self, e)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        block().addColumn();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-            // -------- ROWS
-            {
-                xtype : 'TextItem',
-                text : "Rows: ",
-                xns : rooui.Toolbar  //Boostrap?
-            },
-         
-            {
-                xtype : 'Button',
-                text: '-',
-                listeners : {
-                    click : function (_self, e)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        block().removeRow();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-            {
-                xtype : 'Button',
-                text: '+',
-                listeners : {
-                    click : function (_self, e)
-                    {
-                        block().addRow();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-            // -------- ROWS
-            {
-                xtype : 'Button',
-                text: 'Reset Column Widths',
-                listeners : {
-                    
-                    click : function (_self, e)
-                    {
-                        block().resetWidths();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.Toolbar
-            } 
-            
-            
-            
-        ];
-        
-    },
-    
-    
-  /**
-     * create a DomHelper friendly object - for use with
-     * Roo.DomHelper.markup / overwrite / etc..
-     * ?? should it be called with option to hide all editing features?
-     */
-    toObject : function()
-    {
-        
-        var ret = {
-            tag : 'table',
-            contenteditable : 'false', // this stops cell selection from picking the table.
-            'data-block' : 'Table',
-            style : {
-                width:  this.width,
-                border : 'solid 1px #000', // ??? hard coded?
-                'border-collapse' : 'collapse' 
-            },
-            cn : [
-                { tag : 'tbody' , cn : [] }
-            ]
-        };
-        
-        // do we have a head = not really 
-        var ncols = 0;
-        Roo.each(this.rows, function( row ) {
-            var tr = {
-                tag: 'tr',
-                style : {
-                    margin: '6px',
-                    border : 'solid 1px #000',
-                    textAlign : 'left' 
-                },
-                cn : [ ]
-            };
-            
-            ret.cn[0].cn.push(tr);
-            // does the row have any properties? ?? height?
-            var nc = 0;
-            Roo.each(row, function( cell ) {
-                
-                var td = {
-                    tag : 'td',
-                    contenteditable :  'true',
-                    'data-block' : 'Td',
-                    html : cell.html,
-                    style : cell.style
-                };
-                if (cell.colspan > 1) {
-                    td.colspan = cell.colspan ;
-                    nc += cell.colspan;
-                } else {
-                    nc++;
-                }
-                if (cell.rowspan > 1) {
-                    td.rowspan = cell.rowspan ;
-                }
-                
-                
-                // widths ?
-                tr.cn.push(td);
-                    
-                
-            }, this);
-            ncols = Math.max(nc, ncols);
-            
-            
-        }, this);
-        // add the header row..
-        
-        ncols++;
-         
-        
-        return ret;
-         
-    },
-    
-    readElement : function(node)
-    {
-        node  = node ? node : this.node ;
-        this.width = this.getVal(node, true, 'style', 'width') || '100%';
-        
-        this.rows = [];
-        this.no_row = 0;
-        var trs = Array.from(node.rows);
-        trs.forEach(function(tr) {
-            var row =  [];
-            this.rows.push(row);
-            
-            this.no_row++;
-            var no_column = 0;
-            Array.from(tr.cells).forEach(function(td) {
-                
-                var add = {
-                    colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
-                    rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
-                    style : td.hasAttribute('style') ? td.getAttribute('style') : '',
-                    html : td.innerHTML
-                };
-                no_column += add.colspan;
-                     
-                
-                row.push(add);
-                
-                
-            },this);
-            this.no_col = Math.max(this.no_col, no_column);
-            
-            
-        },this);
-        
-        
-    },
-    normalizeRows: function()
-    {
-        var ret= [];
-        var rid = -1;
-        this.rows.forEach(function(row) {
-            rid++;
-            ret[rid] = [];
-            row = this.normalizeRow(row);
-            var cid = 0;
-            row.forEach(function(c) {
-                while (typeof(ret[rid][cid]) != 'undefined') {
-                    cid++;
-                }
-                if (typeof(ret[rid]) == 'undefined') {
-                    ret[rid] = [];
-                }
-                ret[rid][cid] = c;
-                c.row = rid;
-                c.col = cid;
-                if (c.rowspan < 2) {
-                    return;
-                }
-                
-                for(var i = 1 ;i < c.rowspan; i++) {
-                    if (typeof(ret[rid+i]) == 'undefined') {
-                        ret[rid+i] = [];
-                    }
-                    ret[rid+i][cid] = c;
-                }
-            });
-        }, this);
-        return ret;
-    
-    },
-    
-    normalizeRow: function(row)
-    {
-        var ret= [];
-        row.forEach(function(c) {
-            if (c.colspan < 2) {
-                ret.push(c);
-                return;
-            }
-            for(var i =0 ;i < c.colspan; i++) {
-                ret.push(c);
-            }
-        });
-        return ret;
-    
-    },
-    
-    deleteColumn : function(sel)
-    {
-        if (!sel || sel.type != 'col') {
-            return;
-        }
-        if (this.no_col < 2) {
-            return;
-        }
-        
-        this.rows.forEach(function(row) {
-            var cols = this.normalizeRow(row);
-            var col = cols[sel.col];
-            if (col.colspan > 1) {
-                col.colspan --;
-            } else {
-                row.remove(col);
-            }
-            
-        }, this);
-        this.no_col--;
-        
-    },
-    removeColumn : function()
-    {
-        this.deleteColumn({
-            type: 'col',
-            col : this.no_col-1
-        });
-        this.updateElement();
-    },
-    
-     
-    addColumn : function()
-    {
-        
-        this.rows.forEach(function(row) {
-            row.push(this.emptyCell());
-           
-        }, this);
-        this.updateElement();
-    },
-    
-    deleteRow : function(sel)
-    {
-        if (!sel || sel.type != 'row') {
-            return;
-        }
-        
-        if (this.no_row < 2) {
-            return;
-        }
-        
-        var rows = this.normalizeRows();
-        
-        
-        rows[sel.row].forEach(function(col) {
-            if (col.rowspan > 1) {
-                col.rowspan--;
-            } else {
-                col.remove = 1; // flage it as removed.
-            }
-            
-        }, this);
-        var newrows = [];
-        this.rows.forEach(function(row) {
-            newrow = [];
-            row.forEach(function(c) {
-                if (typeof(c.remove) == 'undefined') {
-                    newrow.push(c);
-                }
-                
-            });
-            if (newrow.length > 0) {
-                newrows.push(row);
-            }
-        });
-        this.rows =  newrows;
-        
-        
-        
-        this.no_row--;
-        this.updateElement();
-        
-    },
-    removeRow : function()
-    {
-        this.deleteRow({
-            type: 'row',
-            row : this.no_row-1
-        });
-        
-    },
-    
-     
-    addRow : function()
-    {
-        
-        var row = [];
-        for (var i = 0; i < this.no_col; i++ ) {
-            
-            row.push(this.emptyCell());
-           
-        }
-        this.rows.push(row);
-        this.updateElement();
-        
-    },
-     
-    // the default cell object... at present...
-    emptyCell : function() {
-        return (new Roo.htmleditor.BlockTd({})).toObject();
-        
-     
-    },
-    
-    removeNode : function()
-    {
-        return this.node;
-    },
-    
-    
-    
-    resetWidths : function()
-    {
-        Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
-            var nn = Roo.htmleditor.Block.factory(n);
-            nn.width = '';
-            nn.updateElement(n);
-        });
-    }
-    
-    
-    
-    
-})
-
-/**
- *
- * editing a TD?
- *
- * since selections really work on the table cell, then editing really should work from there
- *
- * The original plan was to support merging etc... - but that may not be needed yet..
- *
- * So this simple version will support:
- *   add/remove cols
- *   adjust the width +/-
- *   reset the width...
- *   
- *
- */
-
-
-
-/**
- * @class Roo.htmleditor.BlockTable
- * Block that manages a table
- * 
- * @constructor
- * Create a new Filter.
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.BlockTd = function(cfg)
-{
-    if (cfg.node) {
-        this.readElement(cfg.node);
-        this.updateElement(cfg.node);
-    }
-    Roo.apply(this, cfg);
-     
-    
-    
-}
-Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
-    node : false,
-    
-    width: '',
-    textAlign : 'left',
-    valign : 'top',
-    
-    colspan : 1,
-    rowspan : 1,
-    
-    
-    // used by context menu
-    friendly_name : 'Table Cell',
-    deleteTitle : false, // use our customer delete
-    
-    // context menu is drawn once..
-    
-    contextMenu : function(toolbar)
-    {
-        
-        var cell = function() {
-            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
-        };
-        
-        var table = function() {
-            return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
-        };
-        
-        var lr = false;
-        var saveSel = function()
-        {
-            lr = toolbar.editorcore.getSelection().getRangeAt(0);
-        }
-        var restoreSel = function()
-        {
-            if (lr) {
-                (function() {
-                    toolbar.editorcore.focus();
-                    var cr = toolbar.editorcore.getSelection();
-                    cr.removeAllRanges();
-                    cr.addRange(lr);
-                    toolbar.editorcore.onEditorEvent();
-                }).defer(10, this);
-                
-                
-            }
-        }
-        
-        var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
-        
-        var syncValue = toolbar.editorcore.syncValue;
-        
-        var fields = {};
-        
-        return [
-            {
-                xtype : 'Button',
-                text : 'Edit Table',
-                listeners : {
-                    click : function() {
-                        var t = toolbar.tb.selectedNode.closest('table');
-                        toolbar.editorcore.selectNode(t);
-                        toolbar.editorcore.onEditorEvent();                        
-                    }
-                }
-                
-            },
-              
-           
-             
-            {
-                xtype : 'TextItem',
-                text : "Column Width: ",
-                 xns : rooui.Toolbar 
-               
-            },
-            {
-                xtype : 'Button',
-                text: '-',
-                listeners : {
-                    click : function (_self, e)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        cell().shrinkColumn();
-                        syncValue();
-                         toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-            {
-                xtype : 'Button',
-                text: '+',
-                listeners : {
-                    click : function (_self, e)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        cell().growColumn();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-            
-            {
-                xtype : 'TextItem',
-                text : "Vertical Align: ",
-                xns : rooui.Toolbar  //Boostrap?
-            },
-            {
-                xtype : 'ComboBox',
-                allowBlank : false,
-                displayField : 'val',
-                editable : true,
-                listWidth : 100,
-                triggerAction : 'all',
-                typeAhead : true,
-                valueField : 'val',
-                width : 100,
-                name : 'valign',
-                listeners : {
-                    select : function (combo, r, index)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        var b = cell();
-                        b.valign = r.get('val');
-                        b.updateElement();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.form,
-                store : {
-                    xtype : 'SimpleStore',
-                    data : [
-                        ['top'],
-                        ['middle'],
-                        ['bottom'] // there are afew more... 
-                    ],
-                    fields : [ 'val'],
-                    xns : Roo.data
-                }
-            },
-            
-            {
-                xtype : 'TextItem',
-                text : "Merge Cells: ",
-                 xns : rooui.Toolbar 
-               
-            },
-            
-            
-            {
-                xtype : 'Button',
-                text: 'Right',
-                listeners : {
-                    click : function (_self, e)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        cell().mergeRight();
-                        //block().growColumn();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-             
-            {
-                xtype : 'Button',
-                text: 'Below',
-                listeners : {
-                    click : function (_self, e)
-                    {
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        cell().mergeBelow();
-                        //block().growColumn();
-                        syncValue();
-                        toolbar.editorcore.onEditorEvent();
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-            {
-                xtype : 'TextItem',
-                text : "| ",
-                 xns : rooui.Toolbar 
-               
-            },
-            
-            {
-                xtype : 'Button',
-                text: 'Split',
-                listeners : {
-                    click : function (_self, e)
-                    {
-                        //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        cell().split();
-                        syncValue();
-                        toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
-                        toolbar.editorcore.onEditorEvent();
-                                             
-                    }
-                },
-                xns : rooui.Toolbar
-            },
-            {
-                xtype : 'Fill',
-                xns : rooui.Toolbar 
-               
-            },
-        
-          
-            {
-                xtype : 'Button',
-                text: 'Delete',
-                 
-                xns : rooui.Toolbar,
-                menu : {
-                    xtype : 'Menu',
-                    xns : rooui.menu,
-                    items : [
-                        {
-                            xtype : 'Item',
-                            html: 'Column',
-                            listeners : {
-                                click : function (_self, e)
-                                {
-                                    var t = table();
-                                    
-                                    cell().deleteColumn();
-                                    syncValue();
-                                    toolbar.editorcore.selectNode(t.node);
-                                    toolbar.editorcore.onEditorEvent();   
-                                }
-                            },
-                            xns : rooui.menu
-                        },
-                        {
-                            xtype : 'Item',
-                            html: 'Row',
-                            listeners : {
-                                click : function (_self, e)
-                                {
-                                    var t = table();
-                                    cell().deleteRow();
-                                    syncValue();
-                                    
-                                    toolbar.editorcore.selectNode(t.node);
-                                    toolbar.editorcore.onEditorEvent();   
-                                                         
-                                }
-                            },
-                            xns : rooui.menu
-                        },
-                       {
-                            xtype : 'Separator',
-                            xns : rooui.menu
-                        },
-                        {
-                            xtype : 'Item',
-                            html: 'Table',
-                            listeners : {
-                                click : function (_self, e)
-                                {
-                                    var t = table();
-                                    var nn = t.node.nextSibling || t.node.previousSibling;
-                                    t.node.parentNode.removeChild(t.node);
-                                    if (nn) { 
-                                        toolbar.editorcore.selectNode(nn, true);
-                                    }
-                                    toolbar.editorcore.onEditorEvent();   
-                                                         
-                                }
-                            },
-                            xns : rooui.menu
-                        }
-                    ]
-                }
-            }
-            
-            // align... << fixme
-            
-        ];
-        
-    },
-    
-    
-  /**
-     * create a DomHelper friendly object - for use with
-     * Roo.DomHelper.markup / overwrite / etc..
-     * ?? should it be called with option to hide all editing features?
-     */
- /**
-     * create a DomHelper friendly object - for use with
-     * Roo.DomHelper.markup / overwrite / etc..
-     * ?? should it be called with option to hide all editing features?
-     */
-    toObject : function()
-    {
-        
-        var ret = {
-            tag : 'td',
-            contenteditable : 'true', // this stops cell selection from picking the table.
-            'data-block' : 'Td',
-            valign : this.valign,
-            style : {  
-                'text-align' :  this.textAlign,
-                border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
-                'border-collapse' : 'collapse',
-                padding : '6px', // 8 for desktop / 4 for mobile
-                'vertical-align': this.valign
-            },
-            html : this.html
-        };
-        if (this.width != '') {
-            ret.width = this.width;
-            ret.style.width = this.width;
-        }
-        
-        
-        if (this.colspan > 1) {
-            ret.colspan = this.colspan ;
-        } 
-        if (this.rowspan > 1) {
-            ret.rowspan = this.rowspan ;
-        }
-        
-           
-        
-        return ret;
-         
-    },
-    
-    readElement : function(node)
-    {
-        node  = node ? node : this.node ;
-        this.width = node.style.width;
-        this.colspan = Math.max(1,1*node.getAttribute('colspan'));
-        this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
-        this.html = node.innerHTML;
-        
-        
-    },
-     
-    // the default cell object... at present...
-    emptyCell : function() {
-        return {
-            colspan :  1,
-            rowspan :  1,
-            textAlign : 'left',
-            html : "&nbsp;" // is this going to be editable now?
-        };
-     
-    },
-    
-    removeNode : function()
-    {
-        return this.node.closest('table');
-         
-    },
-    
-    cellData : false,
-    
-    colWidths : false,
-    
-    toTableArray  : function()
-    {
-        var ret = [];
-        var tab = this.node.closest('tr').closest('table');
-        Array.from(tab.rows).forEach(function(r, ri){
-            ret[ri] = [];
-        });
-        var rn = 0;
-        this.colWidths = [];
-        var all_auto = true;
-        Array.from(tab.rows).forEach(function(r, ri){
-            
-            var cn = 0;
-            Array.from(r.cells).forEach(function(ce, ci){
-                var c =  {
-                    cell : ce,
-                    row : rn,
-                    col: cn,
-                    colspan : ce.colSpan,
-                    rowspan : ce.rowSpan
-                };
-                if (ce.isEqualNode(this.node)) {
-                    this.cellData = c;
-                }
-                // if we have been filled up by a row?
-                if (typeof(ret[rn][cn]) != 'undefined') {
-                    while(typeof(ret[rn][cn]) != 'undefined') {
-                        cn++;
-                    }
-                    c.col = cn;
-                }
-                
-                if (typeof(this.colWidths[cn]) == 'undefined') {
-                    this.colWidths[cn] =   ce.style.width;
-                    if (this.colWidths[cn] != '') {
-                        all_auto = false;
-                    }
-                }
-                
-                
-                if (c.colspan < 2 && c.rowspan < 2 ) {
-                    ret[rn][cn] = c;
-                    cn++;
-                    return;
-                }
-                for(var j = 0; j < c.rowspan; j++) {
-                    if (typeof(ret[rn+j]) == 'undefined') {
-                        continue; // we have a problem..
-                    }
-                    ret[rn+j][cn] = c;
-                    for(var i = 0; i < c.colspan; i++) {
-                        ret[rn+j][cn+i] = c;
-                    }
-                }
-                
-                cn += c.colspan;
-            }, this);
-            rn++;
-        }, this);
-        
-        // initalize widths.?
-        // either all widths or no widths..
-        if (all_auto) {
-            this.colWidths[0] = false; // no widths flag.
-        }
-        
-        
-        return ret;
-        
-    },
-    
-    
-    
-    
-    mergeRight: function()
-    {
-         
-        // get the contents of the next cell along..
-        var tr = this.node.closest('tr');
-        var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
-        if (i >= tr.childNodes.length - 1) {
-            return; // no cells on right to merge with.
-        }
-        var table = this.toTableArray();
-        
-        if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
-            return; // nothing right?
-        }
-        var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
-        // right cell - must be same rowspan and on the same row.
-        if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
-            return; // right hand side is not same rowspan.
-        }
-        
-        
-        
-        this.node.innerHTML += ' ' + rc.cell.innerHTML;
-        tr.removeChild(rc.cell);
-        this.colspan += rc.colspan;
-        this.node.setAttribute('colspan', this.colspan);
-
-    },
-    
-    
-    mergeBelow : function()
-    {
-        var table = this.toTableArray();
-        if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
-            return; // no row below
-        }
-        if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
-            return; // nothing right?
-        }
-        var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
-        
-        if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
-            return; // right hand side is not same rowspan.
-        }
-        this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
-        rc.cell.parentNode.removeChild(rc.cell);
-        this.rowspan += rc.rowspan;
-        this.node.setAttribute('rowspan', this.rowspan);
-    },
-    
-    split: function()
-    {
-        if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
-            return;
-        }
-        var table = this.toTableArray();
-        var cd = this.cellData;
-        this.rowspan = 1;
-        this.colspan = 1;
-        
-        for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
-            
-            
-            
-            for(var c = cd.col; c < cd.col + cd.colspan; c++) {
-                if (r == cd.row && c == cd.col) {
-                    this.node.removeAttribute('rowspan');
-                    this.node.removeAttribute('colspan');
-                    continue;
-                }
-                 
-                var ntd = this.node.cloneNode(); // which col/row should be 0..
-                ntd.removeAttribute('id'); //
-                //ntd.style.width  = '';
-                ntd.innerHTML = '';
-                table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
-            }
-            
-        }
-        this.redrawAllCells(table);
-        
-         
-        
-    },
-    
-    
-    
-    redrawAllCells: function(table)
-    {
-        
-         
-        var tab = this.node.closest('tr').closest('table');
-        var ctr = tab.rows[0].parentNode;
-        Array.from(tab.rows).forEach(function(r, ri){
-            
-            Array.from(r.cells).forEach(function(ce, ci){
-                ce.parentNode.removeChild(ce);
-            });
-            r.parentNode.removeChild(r);
-        });
-        for(var r = 0 ; r < table.length; r++) {
-            var re = tab.rows[r];
-            
-            var re = tab.ownerDocument.createElement('tr');
-            ctr.appendChild(re);
-            for(var c = 0 ; c < table[r].length; c++) {
-                if (table[r][c].cell === false) {
-                    continue;
-                }
-                
-                re.appendChild(table[r][c].cell);
-                 
-                table[r][c].cell = false;
-            }
-        }
-        
-    },
-    updateWidths : function(table)
-    {
-        for(var r = 0 ; r < table.length; r++) {
-           
-            for(var c = 0 ; c < table[r].length; c++) {
-                if (table[r][c].cell === false) {
-                    continue;
-                }
-                
-                if (this.colWidths[0] != false && table[r][c].colspan < 2) {
-                    var el = Roo.htmleditor.Block.factory(table[r][c].cell);
-                    el.width = Math.floor(this.colWidths[c])  +'%';
-                    el.updateElement(el.node);
-                }
-                table[r][c].cell = false; // done
-            }
-        }
-    },
-    normalizeWidths : function(table)
-    {
-    
-        if (this.colWidths[0] === false) {
-            var nw = 100.0 / this.colWidths.length;
-            this.colWidths.forEach(function(w,i) {
-                this.colWidths[i] = nw;
-            },this);
-            return;
-        }
-    
-        var t = 0, missing = [];
-        
-        this.colWidths.forEach(function(w,i) {
-            //if you mix % and
-            this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
-            var add =  this.colWidths[i];
-            if (add > 0) {
-                t+=add;
-                return;
-            }
-            missing.push(i);
-            
-            
-        },this);
-        var nc = this.colWidths.length;
-        if (missing.length) {
-            var mult = (nc - missing.length) / (1.0 * nc);
-            var t = mult * t;
-            var ew = (100 -t) / (1.0 * missing.length);
-            this.colWidths.forEach(function(w,i) {
-                if (w > 0) {
-                    this.colWidths[i] = w * mult;
-                    return;
-                }
-                
-                this.colWidths[i] = ew;
-            }, this);
-            // have to make up numbers..
-             
-        }
-        // now we should have all the widths..
-        
-    
-    },
-    
-    shrinkColumn : function()
-    {
-        var table = this.toTableArray();
-        this.normalizeWidths(table);
-        var col = this.cellData.col;
-        var nw = this.colWidths[col] * 0.8;
-        if (nw < 5) {
-            return;
-        }
-        var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
-        this.colWidths.forEach(function(w,i) {
-            if (i == col) {
-                 this.colWidths[i] = nw;
-                return;
-            }
-            this.colWidths[i] += otherAdd
-        }, this);
-        this.updateWidths(table);
-         
-    },
-    growColumn : function()
-    {
-        var table = this.toTableArray();
-        this.normalizeWidths(table);
-        var col = this.cellData.col;
-        var nw = this.colWidths[col] * 1.2;
-        if (nw > 90) {
-            return;
-        }
-        var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
-        this.colWidths.forEach(function(w,i) {
-            if (i == col) {
-                this.colWidths[i] = nw;
-                return;
-            }
-            this.colWidths[i] -= otherSub
-        }, this);
-        this.updateWidths(table);
-         
-    },
-    deleteRow : function()
-    {
-        // delete this rows 'tr'
-        // if any of the cells in this row have a rowspan > 1 && row!= this row..
-        // then reduce the rowspan.
-        var table = this.toTableArray();
-        // this.cellData.row;
-        for (var i =0;i< table[this.cellData.row].length ; i++) {
-            var c = table[this.cellData.row][i];
-            if (c.row != this.cellData.row) {
-                
-                c.rowspan--;
-                c.cell.setAttribute('rowspan', c.rowspan);
-                continue;
-            }
-            if (c.rowspan > 1) {
-                c.rowspan--;
-                c.cell.setAttribute('rowspan', c.rowspan);
-            }
-        }
-        table.splice(this.cellData.row,1);
-        this.redrawAllCells(table);
-        
-    },
-    deleteColumn : function()
-    {
-        var table = this.toTableArray();
-        
-        for (var i =0;i< table.length ; i++) {
-            var c = table[i][this.cellData.col];
-            if (c.col != this.cellData.col) {
-                table[i][this.cellData.col].colspan--;
-            } else if (c.colspan > 1) {
-                c.colspan--;
-                c.cell.setAttribute('colspan', c.colspan);
-            }
-            table[i].splice(this.cellData.col,1);
-        }
-        
-        this.redrawAllCells(table);
-    }
-    
-    
-    
-    
-})
-
-//<script type="text/javascript">
+});//<script type="text/javascript">
 
 /*
  * Based  Ext JS Library 1.1.1