X-Git-Url: http://git.roojs.org/?a=blobdiff_plain;f=roojs-ui-debug.js;h=f1c5c749ef8f557486044df98e7699b742d9b486;hb=e28295d9bf2622b5292557e0935b71b03f537647;hp=5dfd60bbcc4c7d0963a90002cd79e00d8c8d0670;hpb=f98dd316be2f4854ebc067fc8134f751eb74ebf0;p=roojs1 diff --git a/roojs-ui-debug.js b/roojs-ui-debug.js index 5dfd60bbcc..f1c5c749ef 100644 --- a/roojs-ui-debug.js +++ b/roojs-ui-debug.js @@ -10302,6 +10302,7 @@ Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, { /** * @class Roo.MessageBox + * @static * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used. * Example usage: *

@@ -10411,6 +10412,7 @@ Roo.MessageBox = function(){
                         }
                     }
                 });
+              
                 dlg.on("hide", handleHide);
                 mask = dlg.mask;
                 dlg.addKeyListener(27, handleEsc);
@@ -10654,6 +10656,7 @@ Roo.Msg.show({
                 d.animateTarget = null;
                 d.show(options.animEl);
             }
+            dlg.toFront();
             return this;
         },
 
@@ -15520,7 +15523,7 @@ Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
      */
     text: '',
      /**
-     * @cfg {String} HTML to render in menu
+     * @cfg {String} html to render in menu
      * The text to show on the menu item (HTML version).
      */
     html: '',
@@ -21120,7 +21123,7 @@ Roo.htmleditor.Filter.prototype = {
         Roo.each( Array.from(dom.childNodes), function( e ) {
             switch(true) {
                 
-                case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
+                case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
                     this.replaceComment(e);
                     return;
                 
@@ -21320,7 +21323,7 @@ Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
 {
     tag : true, // all elements.
    
-    replace : function(n)
+    replaceTag : function(n)
     {
         n.parentNode.removeChild(n);
     }
@@ -21719,7 +21722,7 @@ Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
         var cn = Array.from(node.childNodes);
         var nn = node;
         Roo.each(inject, function(t) {
-            var nc = node.ownerDocument.createelement(t);
+            var nc = node.ownerDocument.createElement(t);
             nn.appendChild(nc);
             nn = nc;
         });
@@ -21836,265 +21839,1357 @@ Roo.apply(Roo.htmleditor.FilterBlock.prototype,
         
     
 });
-/**
- * @class Roo.htmleditor.Tidy
- * Tidy HTML 
- * @cfg {Roo.HtmlEditorCore} core the editor.
+/***
+ * This is based loosely on tinymce 
+ * @class Roo.htmleditor.TidySerializer
+ * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
  * @constructor
- * Create a new Filter.
- * @param {Object} config Configuration options
+ * @method Serializer
+ * @param {Object} settings Name/value settings object.
  */
 
 
-Roo.htmleditor.Tidy = function(cfg) {
-    Roo.apply(this, cfg);
-    
-    this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
-     
-}
-
-Roo.htmleditor.Tidy.toString = function(node)
+Roo.htmleditor.TidySerializer = function(settings)
 {
-    return Roo.htmleditor.Tidy.prototype.tidy(node, '');
-}
-
-Roo.htmleditor.Tidy.prototype = {
+    Roo.apply(this, settings);
+    
+    this.writer = new Roo.htmleditor.TidyWriter(settings);
     
     
-    wrap : function(s) {
-        return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
-    },
 
+};
+Roo.htmleditor.TidySerializer.prototype = {
     
-    tidy : function(node, indent) {
-     
-        if  (node.nodeType == 3) {
-            // text.
-            
-            
-            return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
+    /**
+     * @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('

text

')); + * @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 - if (node.nodeType != 1) { - return ''; + 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

. + * + * @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
. + */ + 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. + // , , etc - var ret = "<" + node.tagName + this.attr(node) ; + 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; - // elements with no children.. - if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) { - return ret + '/>'; - } - ret += '>'; + var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false; + var add_lb = name == 'BR' ? false : in_inline; - var cindent = indent === false ? '' : (indent + ' '); - // tags where we will not pad the children.. (inline text tags etc..) - if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code? - cindent = false; - - + if (!add_lb && !this.in_pre && this.lastElementEndsWS()) { + i_inline = false; } + + var indentstr = this.indentstr; - var cn = this.cn(node, cindent ); - - return ret + cn + ''; - - }, - cn: function(node, indent) - { - var ret = []; + // e_inline = elements that can be inline, but still allow \n before and after? + // only 'BR' ??? any others? - var ar = Array.from(node.childNodes); - for (var i = 0 ; i < ar.length ; i++) { - - - - if (indent !== false // indent==false preservies everything - && i > 0 - && ar[i].nodeType == 3 - && ar[i].nodeValue.length > 0 - && ar[i].nodeValue.match(/^\s+/) - ) { - if (ret.length && ret[ret.length-1] == "\n" + indent) { - ret.pop(); // remove line break from last? + // 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 = ''; } - ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces. + } else { + this.addLine(); } - if (indent !== false - && ar[i].nodeType == 1 // element - and indent is not set... - ) { - ret.push("\n" + indent); + } 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), '"'); } - - ret.push(this.tidy(ar[i], indent)); - // text + trailing indent - if (indent !== false - && ar[i].nodeType == 3 - && ar[i].nodeValue.length > 0 - && ar[i].nodeValue.match(/\s+$/) - ){ - ret.push("\n" + indent); + } + + if (empty) { + if (is_short) { + this.html[this.html.length] = '/>'; + } else { + this.html[this.html.length] = '>'; } + 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; + } + } - // what if all text? + */ - return ret.join(''); - }, - - + 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 - attr : function(node) - { - var attr = []; - for(i = 0; i < node.attributes.length;i++) { - - // skip empty values? - if (!node.attributes.item(i).value.length) { - continue; - } - attr.push( node.attributes.item(i).name + '="' + - Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"' - ); + if (!in_inline && !in_pre) { + this.addLine(); } - return attr.length ? (' ' + attr.join(' ') ) : ''; - } - - - -} -/** - * @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) + lastElementEndsWS : function() { - if (e.charCode != 13) { + var value = this.html.length > 0 ? this.html[this.html.length-1] : false; + if (value === false) { return true; } - e.preventDefault(); - // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome - var doc = this.core.doc; + return value.match(/\s+$/); - var docFragment = doc.createDocumentFragment(); - - //add a new line - - + }, - var range = this.core.win.getSelection().getRangeAt(0); - var n = range.commonAncestorContainer ; - while (n && n.nodeType != 1) { - n = n.parentNode; + /** + * Writes the a end element such as

. + * + * @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; } - var li = false; - if (n && n.tagName == 'UL') { - li = doc.createElement('LI'); - n.appendChild(li); - + this.html.push(indentstr + ''); + 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 (n && n.tagName == 'LI') { - li = doc.createElement('LI'); - if (n.nextSibling) { - n.parentNode.insertBefore(li, n.firstSibling); + 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 '
', 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 - } else { - n.parentNode.appendChild(li); } + + this.html[this.html.length] = text; + return; } - if (li) { - range = doc.createRange(); - range.setStartAfter(li); - range.collapse(true); + // see if previous element was a inline element. + var indentstr = this.indentstr; + + text = text.replace(/\s+/g," "); // all whitespace into single white space. - //make the cursor there - var sel = this.core.win.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - this.core.undoManager.addEvent(); - return false; + // 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 } - var newEle = doc.createTextNode('\n'); - docFragment.appendChild(newEle); + + - //add the br, or p, or something else - newEle = doc.createElement('br'); - //newEle.setAttribute('data-id', Roo.htmleditor.KeyEnter.i++); - docFragment.appendChild(newEle); - doc.createTextNode('\n'); - docFragment.appendChild(newEle); - range.deleteContents(); - range.insertNode(docFragment); //<< inseting here... - - var ns = newEle.nextSibling; - while (ns && ns.nodeType == 3) { - ns = ns.nextSibling; - } - if (!ns) { - //Roo.log('add extra'); - ns = doc.createElement('br'); - //ns.setAttribute('data-id', 'x' + Roo.htmleditor.KeyEnter.i++); - newEle.parentNode.appendChild(ns); + 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); - range = doc.createRange(); - range.setStartAfter(newEle); - range.collapse(true); + // split and indent.. - var sel = this.core.win.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - //this.core.undoManager.addEvent(); - return false; - + + }, + /** + * Writes a cdata node such as . + * + * @method cdata + * @param {String} text String to write out inside the cdata. + */ + cdata: function(text) { + this.html.push(''); + }, + /** + * Writes a comment node such as . + * + * @method cdata + * @param {String} text String to write out inside the comment. + */ + comment: function(text) { + this.html.push(''); + }, + /** + * Writes a PI node such as . + * + * @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('') : this.html.push(''); + this.indent != '' && this.html.push('\n'); + }, + /** + * Writes a doctype node such as . + * + * @method doctype + * @param {String} text String to write out inside the doctype. + */ + doctype: function(text) { + this.html.push('', 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' +]; +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 : { + '"': '"', + // Needs to be escaped since the YUI compressor would otherwise break the code + '\'': ''', + '<': '<', + '>': '>', + '&': '&', + '`': '`' + }, + // Reverse lookup table for raw entities + reverseEntities : { + '<': '<', + '>': '>', + '&': '&', + '"': '"', + ''': '\'' + }, + + 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
  • 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 = ' '; + 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. @@ -22120,7 +23215,7 @@ 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(); + Roo.htmleditor.Block.cache[id].readElement(node); return Roo.htmleditor.Block.cache[id]; } var db = node.getAttribute('data-block'); @@ -22202,14 +23297,17 @@ Roo.htmleditor.Block.prototype = { // 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.style[style]; } - return Roo.get(n).attr(attr); + return n.hasAttribute(attr) ? n.getAttribute(attr) : ''; }, /** @@ -22240,8 +23338,8 @@ Roo.htmleditor.Block.prototype = { * 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} text_align (left|right) alignment for the text caption 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 %? * @@ -22263,46 +23361,229 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, { // setable values. image_src: '', - - align: 'left', + align: 'center', caption : '', - text_align: 'left', + caption_display : 'block', + width : '100%', + cls : '', + href: '', + video_url : '', - width : '46%', - margin: '2%', + // 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", - context : { // ?? static really - width : { - title: "Width", - width: 40 - // ?? number - }, - margin : { - title: "Margin", - width: 40 - // ?? number - }, - align: { - title: "Align", - opts : [[ "left"],[ "right"]], - width : 80 + 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: ' + block().video_url + ''); + } + }, + xns : rooui.Toolbar + }, - }, - text_align: { - title: "Caption Align", - opts : [ [ "left"],[ "right"],[ "center"]], - width : 80 - }, + + { + 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 + } + ]; - - image_src : { - title: "Src", - width: 220 - } }, /** * create a DomHelper friendly object - for use with @@ -22313,48 +23594,125 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, { var d = document.createElement('div'); d.innerHTML = this.caption; - return { + 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' + + + } + }; + /* + '
    ' + + '' + + '' + + '' + + '
    ', + */ + + 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 + ] + }; + } + + return { tag: 'figure', 'data-block' : 'Figure', contenteditable : 'false', style : { - display: 'table', + display: 'block', float : this.align , - width : this.width, - margin: this.margin + 'max-width': this.width, + width : 'auto', + margin: m, + padding: '10px' + }, + + + align : this.align, cn : [ - { - tag : 'img', - src : this.image_src, - alt : d.innerText.replace(/\n/g, " "), // removeHTML.. - style: { - width: '100%' - } - }, + img, + { tag: 'figcaption', - contenteditable : true, + style : { - 'text-align': this.text_align + 'text-align': 'left', + 'margin-top' : '16px', + 'font-size' : '16px', + 'line-height' : '24px', + display : this.caption_display }, - html : this.caption + cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '', + cn : [ + { + // we can not rely on yahoo syndication to use CSS elements - so have to use '' to encase stuff. + tag : 'i', + contenteditable : true, + html : this.caption + } + ] } ] }; + }, 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', 'style', 'float'); + + this.align = this.getVal(node, 'figure', 'align'); this.caption = this.getVal(node, 'figcaption', 'html'); - this.text_align = this.getVal(node, 'figcaption', 'style','text-align'); - this.width = this.getVal(node, 'figure', 'style', 'width'); - this.margin = this.getVal(node, 'figure', 'style', 'margin'); + // remove ' + if (this.caption.trim().match(/^]*>/i)) { + this.caption = this.caption.trim().replace(/^]*>/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; + } @@ -22424,6 +23782,11 @@ Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, { var fields = {}; return [ + { + xtype : 'TextItem', + text : "Width: ", + xns : rooui.Toolbar //Boostrap? + }, { xtype : 'ComboBox', allowBlank : false, @@ -22438,11 +23801,12 @@ Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, { 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, @@ -22470,8 +23834,10 @@ Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, { listeners : { click : function (_self, e) { + toolbar.editorcore.selectNode(toolbar.tb.selectedNode); block().removeColumn(); syncValue(); + toolbar.editorcore.onEditorEvent(); } }, xns : rooui.Toolbar @@ -22482,8 +23848,10 @@ Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, { listeners : { click : function (_self, e) { + toolbar.editorcore.selectNode(toolbar.tb.selectedNode); block().addColumn(); syncValue(); + toolbar.editorcore.onEditorEvent(); } }, xns : rooui.Toolbar @@ -22501,8 +23869,10 @@ Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, { listeners : { click : function (_self, e) { + toolbar.editorcore.selectNode(toolbar.tb.selectedNode); block().removeRow(); syncValue(); + toolbar.editorcore.onEditorEvent(); } }, xns : rooui.Toolbar @@ -22515,6 +23885,7 @@ Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, { { block().addRow(); syncValue(); + toolbar.editorcore.onEditorEvent(); } }, xns : rooui.Toolbar @@ -22529,6 +23900,7 @@ Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, { { block().resetWidths(); syncValue(); + toolbar.editorcore.onEditorEvent(); } }, xns : rooui.Toolbar @@ -22624,22 +23996,18 @@ Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, { this.rows = []; this.no_row = 0; - var trs = Array.from(node.getElementsByTagName('tr')); + var trs = Array.from(node.rows); trs.forEach(function(tr) { var row = []; this.rows.push(row); - if (Roo.get(tr).hasClass('roo-html-editor-el')) { // ??? this is for our 'row' selection' - return; - } + this.no_row++; var no_column = 0; - Array.from(tr.getElementsByTagName('td')).forEach(function(td) { - if (Roo.get(td).hasClass('roo-html-editor-el')) { // ??? this is for our 'row' selection' - return; - } + Array.from(tr.cells).forEach(function(td) { + var add = { - colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan') : 1, - rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan') : 1, + 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 }; @@ -22805,7 +24173,7 @@ Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, { addRow : function() { - row = []; + var row = []; for (var i = 0; i < this.no_col; i++ ) { row.push(this.emptyCell()); @@ -22889,6 +24257,7 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { width: '', textAlign : 'left', + valign : 'top', colspan : 1, rowspan : 1, @@ -22896,7 +24265,7 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { // used by context menu friendly_name : 'Table Cell', - deleteTitle : 'Delete Table', + deleteTitle : false, // use our customer delete // context menu is drawn once.. @@ -22965,10 +24334,10 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { listeners : { click : function (_self, e) { - saveSel(); + toolbar.editorcore.selectNode(toolbar.tb.selectedNode); cell().shrinkColumn(); syncValue(); - restoreSel(); + toolbar.editorcore.onEditorEvent(); } }, xns : rooui.Toolbar @@ -22979,14 +24348,55 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { listeners : { click : function (_self, e) { - saveSel(); + toolbar.editorcore.selectNode(toolbar.tb.selectedNode); cell().growColumn(); syncValue(); - restoreSel(); + 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: ", @@ -23001,11 +24411,11 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { listeners : { click : function (_self, e) { - saveSel(); + toolbar.editorcore.selectNode(toolbar.tb.selectedNode); cell().mergeRight(); //block().growColumn(); syncValue(); - restoreSel(); + toolbar.editorcore.onEditorEvent(); } }, xns : rooui.Toolbar @@ -23017,11 +24427,11 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { listeners : { click : function (_self, e) { - saveSel(); + toolbar.editorcore.selectNode(toolbar.tb.selectedNode); cell().mergeBelow(); //block().growColumn(); syncValue(); - restoreSel(); + toolbar.editorcore.onEditorEvent(); } }, xns : rooui.Toolbar @@ -23039,14 +24449,91 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { listeners : { click : function (_self, e) { - saveSel(); + //toolbar.editorcore.selectNode(toolbar.tb.selectedNode); cell().split(); syncValue(); - restoreSel(); + 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 ]; @@ -23071,21 +24558,27 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { tag : 'td', contenteditable : 'true', // this stops cell selection from picking the table. 'data-block' : 'Td', - width: this.width, + valign : this.valign, style : { - width: this.width, 'text-align' : this.textAlign, border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded? - 'border-collapse' : 'collapse' + '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 = cell.colspan ; + ret.colspan = this.colspan ; } - if (ret.rowspan > 1) { - this.rowspan = cell.rowspan ; + if (this.rowspan > 1) { + ret.rowspan = this.rowspan ; } @@ -23098,7 +24591,8 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { { 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; @@ -23118,8 +24612,7 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { removeNode : function() { return this.node.closest('table'); - - + }, cellData : false, @@ -23292,13 +24785,19 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { 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; @@ -23414,9 +24913,52 @@ Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, { }, 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); } + + }) //