-Roo.bootstrap = {};/**
+/**
* set the version of bootstrap based on the stylesheet...
*
*/
return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
}
-});
-Roo.htmleditor = {};
+});//<script type="text/javascript">
+
+/*
+ * Based Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ * LGPL
+ *
+ */
/**
- * @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
+ * @class Roo.HtmlEditorCore
+ * @extends Roo.Component
+ * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
+ *
+ * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
*/
+Roo.HtmlEditorCore = function(config){
+
+
+ Roo.HtmlEditorCore.superclass.constructor.call(this, config);
+
+
+ this.addEvents({
+ /**
+ * @event initialize
+ * Fires when the editor is fully initialized (including the iframe)
+ * @param {Roo.HtmlEditorCore} this
+ */
+ initialize: true,
+ /**
+ * @event activate
+ * Fires when the editor is first receives the focus. Any insertion must wait
+ * until after this event.
+ * @param {Roo.HtmlEditorCore} this
+ */
+ activate: true,
+ /**
+ * @event beforesync
+ * Fires before the textarea is updated with content from the editor iframe. Return false
+ * to cancel the sync.
+ * @param {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ beforesync: true,
+ /**
+ * @event beforepush
+ * Fires before the iframe editor is updated with content from the textarea. Return false
+ * to cancel the push.
+ * @param {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ beforepush: true,
+ /**
+ * @event sync
+ * Fires when the textarea is updated with content from the editor iframe.
+ * @param {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ sync: true,
+ /**
+ * @event push
+ * Fires when the iframe editor is updated with content from the textarea.
+ * @param {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ push: true,
+
+ /**
+ * @event editorevent
+ * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
+ * @param {Roo.HtmlEditorCore} this
+ */
+ editorevent: true
+
+
+ });
+
+ // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
+
+ // defaults : white / black...
+ this.applyBlacklists();
+
+
+
+};
-Roo.htmleditor.Filter = function(cfg) {
- Roo.apply(this.cfg);
- // this does not actually call walk as it's really just a abstract class
-}
+Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
-Roo.htmleditor.Filter.prototype = {
+ /**
+ * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
+ */
- node: false,
+ owner : false,
- tag: false,
-
- // overrride to do replace comments.
- replaceComment : false,
+ /**
+ * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
+ * Roo.resizable.
+ */
+ resizable : false,
+ /**
+ * @cfg {Number} height (in pixels)
+ */
+ height: 300,
+ /**
+ * @cfg {Number} width (in pixels)
+ */
+ width: 500,
+ /**
+ * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
+ * if you are doing an email editor, this probably needs disabling, it's designed
+ */
+ autoClean: true,
- // overrride to do replace or do stuff with tags..
- replaceTag : false,
+ /**
+ * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
+ */
+ enableBlocks : true,
+ /**
+ * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+ *
+ */
+ stylesheets: false,
+ /**
+ * @cfg {String} language default en - language of text (usefull for rtl languages)
+ *
+ */
+ language: 'en',
- 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
+ /**
+ * @cfg {boolean} allowComments - default false - allow comments in HTML source
+ * - by default they are stripped - if you are editing email you may need this.
+ */
+ allowComments: false,
+ // id of frame..
+ frameId: false,
- attrib_black : false, // array
- attrib_clean : false,
- attrib_white : false,
-
- style_white : false,
- style_black : false,
-
+ // private properties
+ validationEvent : false,
+ deferHeight: true,
+ initialized : false,
+ activated : false,
+ sourceEditMode : false,
+ onFocus : Roo.emptyFn,
+ iframePad:3,
+ hideMode:'offsets',
+
+ clearUp: true,
+
+ // blacklist + whitelisted elements..
+ black: false,
+ white: false,
- replaceTag : function(node)
- {
- if (!node.attributes || !node.attributes.length) {
- return true;
- }
+ bodyCls : '',
+
+
+ undoManager : false,
+ /**
+ * Protected method that will not generally be called directly. It
+ * is called when the editor initializes the iframe with HTML contents. Override this method if you
+ * want to change the initialization markup of the iframe (e.g. to add stylesheets).
+ */
+ getDocMarkup : function(){
+ // body styles..
+ var st = '';
- 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;
- }
-
+ // inherit styels from page...??
+ if (this.stylesheets === false) {
- 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..
+ Roo.get(document.head).select('style').each(function(node) {
+ st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
+ });
+ Roo.get(document.head).select('link').each(function(node) {
+ st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
+ });
- if (a.name == 'class') {
- if (a.value.match(/^Mso/)) {
- node.removeAttribute('class');
- }
-
- if (a.value.match(/^body$/)) {
- node.removeAttribute('class');
+ } else if (!this.stylesheets.length) {
+ // simple..
+ st = '<style type="text/css">' +
+ 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+ '</style>';
+ } else {
+ for (var i in this.stylesheets) {
+ if (typeof(this.stylesheets[i]) != 'string') {
+ continue;
}
- continue;
+ st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
}
-
- // style cleanup!?
- // class cleanup?
-
}
- return true; // clean children
- },
- cleanAttr: function(node, n,v)
- {
+ st += '<style type="text/css">' +
+ 'IMG { cursor: pointer } ' +
+ '</style>';
- 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;
+ st += '<meta name="google" content="notranslate">';
+
+ var cls = 'notranslate roo-htmleditor-body';
+
+ if(this.bodyCls.length){
+ cls += ' ' + this.bodyCls;
}
-// Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
- node.removeAttribute(n);
+ return '<html class="notranslate" translate="no"><head>' + st +
+ //<style type="text/css">' +
+ //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+ //'</style>' +
+ ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
},
- 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> </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);
- }
- }
-
-
-
-
-});
-/**
- * @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 = ' ';
- 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 : " " // 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">
-
-/*
- * Based Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- * LGPL
- *
- */
-
-/**
- * @class Roo.HtmlEditorCore
- * @extends Roo.Component
- * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
- *
- * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
- */
-
-Roo.HtmlEditorCore = function(config){
-
-
- Roo.HtmlEditorCore.superclass.constructor.call(this, config);
-
-
- this.addEvents({
- /**
- * @event initialize
- * Fires when the editor is fully initialized (including the iframe)
- * @param {Roo.HtmlEditorCore} this
- */
- initialize: true,
- /**
- * @event activate
- * Fires when the editor is first receives the focus. Any insertion must wait
- * until after this event.
- * @param {Roo.HtmlEditorCore} this
- */
- activate: true,
- /**
- * @event beforesync
- * Fires before the textarea is updated with content from the editor iframe. Return false
- * to cancel the sync.
- * @param {Roo.HtmlEditorCore} this
- * @param {String} html
- */
- beforesync: true,
- /**
- * @event beforepush
- * Fires before the iframe editor is updated with content from the textarea. Return false
- * to cancel the push.
- * @param {Roo.HtmlEditorCore} this
- * @param {String} html
- */
- beforepush: true,
- /**
- * @event sync
- * Fires when the textarea is updated with content from the editor iframe.
- * @param {Roo.HtmlEditorCore} this
- * @param {String} html
- */
- sync: true,
- /**
- * @event push
- * Fires when the iframe editor is updated with content from the textarea.
- * @param {Roo.HtmlEditorCore} this
- * @param {String} html
- */
- push: true,
-
- /**
- * @event editorevent
- * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
- * @param {Roo.HtmlEditorCore} this
- */
- editorevent: true
-
-
- });
-
- // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
-
- // defaults : white / black...
- this.applyBlacklists();
-
-
-
-};
-
-
-Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
-
-
- /**
- * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
- */
-
- owner : false,
-
- /**
- * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
- * Roo.resizable.
- */
- resizable : false,
- /**
- * @cfg {Number} height (in pixels)
- */
- height: 300,
- /**
- * @cfg {Number} width (in pixels)
- */
- width: 500,
- /**
- * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
- * if you are doing an email editor, this probably needs disabling, it's designed
- */
- autoClean: true,
-
- /**
- * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
- */
- enableBlocks : true,
- /**
- * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
- *
- */
- stylesheets: false,
- /**
- * @cfg {String} language default en - language of text (usefull for rtl languages)
- *
- */
- language: 'en',
-
- /**
- * @cfg {boolean} allowComments - default false - allow comments in HTML source
- * - by default they are stripped - if you are editing email you may need this.
- */
- allowComments: false,
- // id of frame..
- frameId: false,
-
- // private properties
- validationEvent : false,
- deferHeight: true,
- initialized : false,
- activated : false,
- sourceEditMode : false,
- onFocus : Roo.emptyFn,
- iframePad:3,
- hideMode:'offsets',
-
- clearUp: true,
-
- // blacklist + whitelisted elements..
- black: false,
- white: false,
-
- bodyCls : '',
-
-
- undoManager : false,
- /**
- * Protected method that will not generally be called directly. It
- * is called when the editor initializes the iframe with HTML contents. Override this method if you
- * want to change the initialization markup of the iframe (e.g. to add stylesheets).
- */
- getDocMarkup : function(){
- // body styles..
- var st = '';
-
- // inherit styels from page...??
- if (this.stylesheets === false) {
-
- Roo.get(document.head).select('style').each(function(node) {
- st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
- });
-
- Roo.get(document.head).select('link').each(function(node) {
- st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
- });
-
- } else if (!this.stylesheets.length) {
- // simple..
- st = '<style type="text/css">' +
- 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
- '</style>';
- } else {
- for (var i in this.stylesheets) {
- if (typeof(this.stylesheets[i]) != 'string') {
- continue;
- }
- st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
- }
-
- }
-
- st += '<style type="text/css">' +
- 'IMG { cursor: pointer } ' +
- '</style>';
-
- st += '<meta name="google" content="notranslate">';
-
- var cls = 'notranslate roo-htmleditor-body';
-
- if(this.bodyCls.length){
- cls += ' ' + this.bodyCls;
- }
-
- return '<html class="notranslate" translate="no"><head>' + st +
- //<style type="text/css">' +
- //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
- //'</style>' +
- ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
- },
-
- // private
- onRender : function(ct, position)
+
+ // private
+ onRender : function(ct, position)
{
var _t = this;
//Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
});
-Roo.bootstrap.dash = {};/*
+/*
* - LGPL
*
* numberBox