X-Git-Url: http://git.roojs.org/?p=roojs1;a=blobdiff_plain;f=roojs-bootstrap-debug.js;h=4d87cb67fc62e0b7ca2116c447d1803fbe191113;hp=4c3502b749744659b09911aab59919149f5ea10c;hb=248c5cdd0fe1c3012360d530f5fd34ef516a79fb;hpb=89b03a0009ad29be68bd417551948042c2b146b3 diff --git a/roojs-bootstrap-debug.js b/roojs-bootstrap-debug.js index 4c3502b749..4d87cb67fc 100644 --- a/roojs-bootstrap-debug.js +++ b/roojs-bootstrap-debug.js @@ -17,7 +17,10 @@ Roo.bootstrap.version = ( function() { })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {}; Roo.bootstrap.nav = {}; -Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/* +Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {}; +Roo.htmleditor = {}; +Roo.namespace('Roo.bootstrap.form.HtmlEditorToolbar'); +/* * Based on: * Ext JS Library 1.1.1 * Copyright(c) 2006-2007, Ext JS, LLC. @@ -4545,10 +4548,16 @@ Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, { : this.el.select('.modal-footer div',true).first(); }, + + closeClick : function() + { + this.hide(); + }, + initEvents : function() { if (this.allow_close) { - this.closeEl.on('click', this.hide, this); + this.closeEl.on('click', this.closeClick, this); } Roo.EventManager.onWindowResize(this.resize, this, true); if (this.editableTitle) { @@ -9157,6 +9166,7 @@ Currently the Table uses multiple headers to try and handle XL / Medium etc... * also adds table-responsive (see bootstrap docs for details) * @cfg {Boolean} loadMask (true|false) default false * @cfg {Boolean} footerShow (true|false) generate tfoot, default true + * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false * @cfg {Boolean} headerShow (true|false) generate thead, default true * @cfg {Boolean} rowSelection (true|false) default false * @cfg {Boolean} cellSelection (true|false) default false @@ -9165,6 +9175,7 @@ Currently the Table uses multiple headers to try and handle XL / Medium etc... * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false) * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false) * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop) + * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS() * * * @cfg {Number} minColumnWidth default 50 pixels minimum column width @@ -9340,8 +9351,10 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { store : false, loadMask : false, footerShow : true, + footerRow : false, headerShow : true, enableColumnResize: true, + disableAutoSize: false, rowSelection : false, cellSelection : false, @@ -9414,9 +9427,10 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { cfg.cn.push(this.renderBody()); - if(this.footerShow){ + if(this.footerShow || this.footerRow){ cfg.cn.push(this.renderFooter()); } + // where does this come from? //cfg.cls+= ' TableGrid'; } @@ -9503,7 +9517,9 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { initCSS : function() { - + if(this.disableAutoSize) { + return; + } var cm = this.cm, styles = []; this.CSS.removeStyleSheet(this.id + '-cssrules'); @@ -9975,8 +9991,6 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { return footer; }, - - onLoad : function() { // Roo.log('ds onload'); @@ -10023,7 +10037,7 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { var tfoot = this.el.select('tfoot', true).first(); - if(this.footerShow && this.auto_hide_footer && this.mainFoot){ + if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){ this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide(); @@ -10033,6 +10047,30 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { this.mainFoot.show(); } } + + if(!this.footerShow && this.footerRow) { + + var tr = { + tag : 'tr', + cn : [] + }; + + for(var i = 0, len = cm.getColumnCount(); i < len; i++){ + var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer; + var td = { + tag: 'td', + cls : ' x-fcol-' + i, + html: footer + }; + + tr.cn.push(td); + + } + + tfoot.dom.innerHTML = ''; + + tfoot.createChild(tr); + } Roo.each(this.el.select('tbody td', true).elements, function(e){ e.on('mouseover', _this.onMouseover, _this); @@ -10300,7 +10338,7 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { var id = false; if(typeof(renderer) !== 'undefined'){ - value = renderer(d.data[cm.getDataIndex(i)], false, d); + value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d); } // if object are returned, then they are expected to be Roo.bootstrap.Component instances // and are rendered into the cells after the row is rendered - using the id for the element. @@ -10507,6 +10545,9 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, { */ autoSize : function() { + if(this.disableAutoSize) { + return; + } //var ctr = Roo.get(this.container.dom.parentElement); var ctr = Roo.get(this.el.dom); @@ -11277,7 +11318,13 @@ Roo.extend(Roo.form.Action.Submit, Roo.form.Action, { } var ret = false; try { - ret = Roo.decode(response.responseText); + var rt = response.responseText; + if (rt.match(/^\. + * + * @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', 'A' +]; +Roo.htmleditor.TidyWriter.shortend_elements = [ + 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT', + 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK' +]; + +Roo.htmleditor.TidyWriter.whitespace_elements = [ + 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE' +];/*** + * This is based loosely on tinymce + * @class Roo.htmleditor.TidyEntities + * @static + * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js + * + * Not 100% sure this is actually used or needed. + */ + +Roo.htmleditor.TidyEntities = { + + /** + * initialize data.. + */ + init : function (){ + + this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32); + + }, + + + buildEntitiesLookup: function(items, radix) { + var i, chr, entity, lookup = {}; + if (!items) { + return {}; + } + items = typeof(items) == 'string' ? items.split(',') : items; + radix = radix || 10; + // Build entities lookup table + for (i = 0; i < items.length; i += 2) { + chr = String.fromCharCode(parseInt(items[i], radix)); + // Only add non base entities + if (!this.baseEntities[chr]) { + entity = '&' + items[i + 1] + ';'; + lookup[chr] = entity; + lookup[entity] = chr; + } + } + return lookup; + + }, + + asciiMap : { + 128: '€', + 130: '‚', + 131: 'ƒ', + 132: '„', + 133: '…', + 134: '†', + 135: '‡', + 136: 'ˆ', + 137: '‰', + 138: 'Å ', + 139: '‹', + 140: 'Œ', + 142: 'Ž', + 145: '‘', + 146: '’', + 147: '“', + 148: '”', + 149: '•', + 150: '–', + 151: '—', + 152: '˜', + 153: '™', + 154: 'Å¡', + 155: '›', + 156: 'œ', + 158: 'ž', + 159: 'Ÿ' + }, + // Raw entities + baseEntities : { + '"': '"', + // 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.. @@ -27492,7 +29330,7 @@ Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, { } }, - + { xtype : 'Button', text: 'Hide Caption', @@ -29031,10 +30869,9 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component, { owner : false, /** - * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a - * Roo.resizable. + * @cfg {String} css styling for resizing. (used on bootstrap only) */ - resizable : false, + resize : false, /** * @cfg {Number} height (in pixels) */ @@ -29167,17 +31004,19 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component, { this.frameId = Roo.id(); - - - var iframe = this.owner.wrap.createChild({ + var ifcfg = { tag: 'iframe', cls: 'form-control', // bootstrap.. id: this.frameId, name: this.frameId, frameBorder : 'no', 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false" - }, this.el - ); + }; + if (this.resize) { + ifcfg.style = { resize : this.resize }; + } + + var iframe = this.owner.wrap.createChild(ifcfg, this.el); this.iframe = iframe.dom; @@ -29312,11 +31151,42 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component, { if (this.enableBlocks) { new Roo.htmleditor.FilterBlock({ node : div }); } + + var html = div.innerHTML; + //?? tidy? - var tidy = new Roo.htmleditor.TidySerializer({ - inner: true - }); - var html = tidy.serialize(div); + if (this.autoClean) { + + new Roo.htmleditor.FilterAttributes({ + node : div, + attrib_white : [ + 'href', + 'src', + 'name', + 'align', + 'colspan', + 'rowspan', + 'data-display', + 'data-width', + 'start' , + 'style', + // youtube embed. + 'class', + 'allowfullscreen', + 'frameborder', + 'width', + 'height', + 'alt' + ], + attrib_clean : ['href', 'src' ] + }); + + var tidy = new Roo.htmleditor.TidySerializer({ + inner: true + }); + html = tidy.serialize(div); + + } if(Roo.isSafari){ @@ -29509,9 +31379,34 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component, { var urlAPI = (window.createObjectURL && window) || (window.URL && URL.revokeObjectURL && URL) || (window.webkitURL && webkitURL); - - var url = urlAPI.createObjectURL( cd.files[0]); - this.insertAtCursor(''); + + var r = new FileReader(); + var t = this; + r.addEventListener('load',function() + { + + var d = (new DOMParser().parseFromString('', 'text/html')).body; + // is insert asycn? + if (t.enableBlocks) { + + Array.from(d.getElementsByTagName('img')).forEach(function(img) { + if (img.closest('figure')) { // assume!! that it's aready + return; + } + var fig = new Roo.htmleditor.BlockFigure({ + image_src : img.src + }); + fig.updateElement(img); // replace it.. + + }); + } + t.insertAtCursor(d.innerHTML.replace(/ /g,' ')); + t.owner.fireEvent('paste', this); + }); + r.readAsDataURL(cd.files[0]); + + e.preventDefault(); + return false; } if (cd.types.indexOf('text/html') < 0 ) { @@ -29604,6 +31499,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component, { e.preventDefault(); + this.owner.fireEvent('paste', this); return false; // default behaveiour should be our local cleanup paste? (optional?) // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable.. @@ -29680,6 +31576,8 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component, { if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) { return; // we do not handle this.. (undo manager does..) } + // clicking a 'block'? + // in theory this detects if the last element is not a br, then we try and do that. // its so clicking in space at bottom triggers adding a br and moving the cursor. if (e && @@ -29782,6 +31680,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component, { break; case 'bold': case 'italic': + case 'underline': // if there is no selection, then we insert, and set the curson inside it.. this.execCmd('styleWithCSS', false); break; @@ -30655,9 +32554,9 @@ Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, { /** - * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one + * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none. */ - toolbars : false, + toolbars : true, /** * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty @@ -30665,10 +32564,9 @@ Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, { btns : [], /** - * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a - * Roo.resizable. + * @cfg {String} resize (none|both|horizontal|vertical) - css resize of element */ - resizable : false, + resize : false, /** * @cfg {Number} height (in pixels) */ @@ -30711,26 +32609,35 @@ Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, { * add custom toolbar buttons. * @param {HtmlEditor} editor */ - createToolbar : function(){ - Roo.log('renewing'); - Roo.log("create toolbars"); + createToolbar : function() + { + //Roo.log('renewing'); + //Roo.log("create toolbars"); + if (this.toolbars === false) { + return; + } + if (this.toolbars === true) { + this.toolbars = [ 'Standard' ]; + } - this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ]; - this.toolbars[0].render(this.toolbarContainer()); + var ar = Array.from(this.toolbars); + this.toolbars = []; + ar.forEach(function(t,i) { + if (typeof(t) == 'string') { + t = { + xtype : t + }; + } + if (typeof(t) == 'object' && typeof(t.xtype) == 'string') { + t.editor = this; + t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar; + t = Roo.factory(t); + } + this.toolbars[i] = t; + this.toolbars[i].render(this.toolbarContainer()); + }, this); - return; -// if (!editor.toolbars || !editor.toolbars.length) { -// editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty? -// } -// -// for (var i =0 ; i < editor.toolbars.length;i++) { -// editor.toolbars[i] = Roo.factory( -// typeof(editor.toolbars[i]) == 'string' ? -// { xtype: editor.toolbars[i]} : editor.toolbars[i], -// Roo.bootstrap.form.HtmlEditor); -// editor.toolbars[i].init(editor); -// } }, @@ -30747,33 +32654,11 @@ Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, { this.editorcore.onRender(ct, position); - if (this.resizable) { - this.resizeEl = new Roo.Resizable(this.wrap, { - pinned : true, - wrap: true, - dynamic : true, - minHeight : this.height, - height: this.height, - handles : this.resizable, - width: this.width, - listeners : { - resize : function(r, w, h) { - _t.onResize(w,h); // -something - } - } - }); - - } + this.createToolbar(this); - if(!this.width && this.resizable){ - this.setSize(this.wrap.getSize()); - } - if (this.resizeEl) { - this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] ); - // should trigger onReize.. - } + }, @@ -30844,9 +32729,9 @@ Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, { //this.deferFocus(); } - if(this.resizable){ - this.setSize(this.wrap.getSize()); - } + //if(this.resizable){ + // this.setSize(this.wrap.getSize()); + //} this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode); }, @@ -30983,9 +32868,8 @@ Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, { -Roo.namespace('Roo.bootstrap.form.HtmlEditor'); /** - * @class Roo.bootstrap.form.HtmlEditorToolbarStandard + * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard * @parent Roo.bootstrap.form.HtmlEditor * @extends Roo.bootstrap.nav.Simplebar * Basic Toolbar @@ -30996,7 +32880,7 @@ Roo.namespace('Roo.bootstrap.form.HtmlEditor'); new Roo.bootstrap.form.HtmlEditor({ .... toolbars : [ - new Roo.bootstrap.form.HtmlEditorToolbarStandard({ + new Roo.bootstrap.form.HtmlEditorToolbar.Standard({ disable : { fonts: 1 , format: 1, ..., ... , ...], btns : [ .... ] }) @@ -31011,7 +32895,7 @@ Roo.namespace('Roo.bootstrap.form.HtmlEditor'); * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; } */ -Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config) +Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config) { Roo.apply(this, config); @@ -31023,17 +32907,17 @@ Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config) colors : true, specialElements : true }); - Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config); + Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config); this.editor = config.editor; this.editorcore = config.editor.editorcore; - this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; }); + this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.btnid; }); //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config); // dont call parent... till later. } -Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, { +Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar, { bar : true, @@ -31049,11 +32933,14 @@ Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simpl 'div','span' ], + + deleteBtn: false, + onRender : function(ct, position) { // Roo.log("Call onRender: " + this.xtype); - Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position); + Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position); Roo.log(this.el); this.el.dom.style.marginBottom = '0'; var _this = this; @@ -31061,7 +32948,7 @@ Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simpl var editor= this.editor; var children = []; - var btn = function(id,cmd , toggle, handler, html){ + var btn = function(id, cmd , toggle, handler, html){ var event = toggle ? 'toggle' : 'click'; @@ -31070,9 +32957,11 @@ Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simpl xtype: 'Button', xns: Roo.bootstrap, //glyphicon : id, + btnid : id, fa: id, - cmd : id || cmd, - enableToggle:toggle !== false, + cls : 'roo-html-editor-btn-' + id, + cmd : cmd, // why id || cmd + enableToggle: toggle !== false, html : html || '', pressed : toggle ? false : null, listeners : {} @@ -31091,6 +32980,7 @@ Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simpl size : 'sm', xns: Roo.bootstrap, fa : 'font', + cls : 'roo-html-editor-font-chooser', //html : 'submit' menu : { xtype: 'Menu', @@ -31116,19 +33006,19 @@ Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simpl }); children.push(style); - btn('bold',false,true); - btn('italic',false,true); - btn('align-left', 'justifyleft',true); + btn('bold', 'bold',true); + btn('italic', 'italic',true); + btn('underline', 'underline',true); + btn('align-left', 'justifyleft',true); btn('align-center', 'justifycenter',true); btn('align-right' , 'justifyright',true); - btn('link', false, false, function(btn) { - //Roo.log("create link?"); - var url = prompt(this.createLinkText, this.defaultLinkValue); - if(url && url != 'http:/'+'/'){ - this.editorcore.relayCmd('createlink', url); - } - }), + btn('link', false, true, this.onLinkClick); + + + btn('image', false, true, this.onImageClick); btn('list','insertunorderedlist',true); + btn('list-ol','insertorderedlist',true); + btn('pencil', false,true, function(btn){ Roo.log(this); this.toggleSourceEdit(btn.pressed); @@ -31140,58 +33030,166 @@ Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simpl } } - /* - var cog = { - xtype: 'Button', - size : 'sm', - xns: Roo.bootstrap, - glyphicon : 'cog', - //html : 'submit' - menu : { - xtype: 'Menu', - xns: Roo.bootstrap, - items: [] - } - }; - - cog.menu.items.push({ - xtype :'MenuItem', - xns: Roo.bootstrap, - html : Clean styles, - tagname : f, - listeners : { - click : function() - { - editorcore.insertTag(this.tagname); - editor.focus(); - } - } - - }); - */ - this.xtype = 'NavSimplebar'; + this.xtype = 'NavSimplebar'; // why? for(var i=0;i< children.length;i++) { this.buttons.add(this.addxtypeChild(children[i])); } - + this.buildToolbarDelete(); + editor.on('editorevent', this.updateToolbar, this); }, + + buildToolbarDelete : function() + { + + /* this.addxtypeChild({ + xtype : 'Element', + xns : Roo.bootstrap, + cls : 'roo-htmleditor-fill' + }); + */ + this.deleteBtn = this.addxtypeChild({ + size : 'sm', + xtype: 'Button', + xns: Roo.bootstrap, + fa: 'eraser', + tooltip : "Clear Formating / Delete", + listeners : { + click : this.onDelete.createDelegate(this) + } + }); + this.deleteBtn.hide(); + + }, + + onImageClick : function() + { + if (this.input) { + this.input.un('change', this.onFileSelected, this); + } + this.input = Roo.get(document.body).createChild({ + tag: 'input', + type : 'file', + style : 'display:none', + multiple: 'multiple' + }); + this.input.on('change', this.onFileSelected, this); + this.input.dom.click(); + }, + + onFileSelected : function(e) + { + e.preventDefault(); + + if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){ + return; + } + + + this.addFiles(Array.prototype.slice.call(this.input.dom.files)); + }, + + addFiles : function(far) { + + if (!far.length) { + return; + } + + var f = far.pop(); + + if (!f.type.match(/^image/)) { + this.addFiles(far); + return; + } + + var sn = this.selectedNode; + + var bl = sn && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false; + + var editor = this.editorcore; + + var reader = new FileReader(); + reader.addEventListener('load', (function() { + if (bl) { + bl.image_src = reader.result; + //bl.caption = f.name; + bl.updateElement(sn); + editor.owner.fireEvent('editorevent', editor, false); + // we only do the first file!! and replace. + return; + } + if (this.editorcore.enableBlocks) { + var fig = new Roo.htmleditor.BlockFigure({ + image_src : reader.result, + caption : '', + caption_display : 'none' //default to hide captions.. + }); + editor.insertAtCursor(fig.toHTML()); + editor.owner.fireEvent('editorevent', editor, false); + return; + } + // just a standard img.. + if (sn && sn.tagName.toUpperCase() == 'IMG') { + sn.src = reader.result; + editor.owner.fireEvent('editorevent', editor, false); + return; + } + editor.insertAtCursor(''); + editor.owner.fireEvent('editorevent', editor, false); + + }).createDelegate(this)); + reader.readAsDataURL(f); + + + }, + + onBtnClick : function(id) { this.editorcore.relayCmd(id); this.editorcore.focus(); }, + onLinkClick : function(btn) { + var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ? + this.selectedNode.getAttribute('href') : ''; + + Roo.bootstrap.MessageBox.show({ + title : "Add / Edit Link URL", + msg : "Enter the URL for the link", + buttons: Roo.bootstrap.MessageBox.OKCANCEL, + minWidth: 250, + scope : this, + prompt:true, + multiline: false, + modal : true, + value : url, + fn: function(pressed, newurl) { + if (pressed != 'ok') { + this.editorcore.focus(); + return; + } + if (url != '') { + this.selectedNode.setAttribute('href', newurl); + return; + } + if(newurl && newurl .match(/http(s):\/\/.+/)) { + this.editorcore.relayCmd('createlink', newurl); + } + this.editorcore.focus(); + } + }); + }, /** * Protected method that will not generally be called directly. It triggers * a toolbar update by reading the markup state of the current selection in the editor. */ - updateToolbar: function(){ + updateToolbar: function(editor ,ev, sel){ if(!this.editorcore.activated){ this.editor.onFirstFocus(); // is this neeed? @@ -31200,39 +33198,89 @@ Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simpl var btns = this.buttons; var doc = this.editorcore.doc; - btns.get('bold').setActive(doc.queryCommandState('bold')); - btns.get('italic').setActive(doc.queryCommandState('italic')); - //btns.get('underline').setActive(doc.queryCommandState('underline')); + var hasToggle = false; + btns.each(function(e) { + if (e.enableToggle && e.cmd) { + hasToggle = hasToggle || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd)); + e.setActive(doc.queryCommandState(e.cmd)); + } + }, this); - btns.get('align-left').setActive(doc.queryCommandState('justifyleft')); - btns.get('align-center').setActive(doc.queryCommandState('justifycenter')); - btns.get('align-right').setActive(doc.queryCommandState('justifyright')); - //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist')); - btns.get('list').setActive(doc.queryCommandState('insertunorderedlist')); - /* + if (ev && + (ev.type == 'mouseup' || ev.type == 'click' ) && + ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') { + // they have click on an image... + // let's see if we can change the selection... + sel = ev.target; + + } var ans = this.editorcore.getAllAncestors(); - if (this.formatCombo) { + if (!sel) { + sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body; + sel = sel ? sel : this.editorcore.doc.body; + sel = sel.tagName.length ? sel : this.editorcore.doc.body; + } + + var lastSel = this.selectedNode; + this.selectedNode = sel; + + // ok see if we are editing a block? + + var db = false; + // you are not actually selecting the block. + if (sel && sel.hasAttribute('data-block')) { + db = sel; + } else if (sel && sel.closest('[data-block]')) { + db = sel.closest('[data-block]'); + } + + Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) { + e.classList.remove('roo-ed-selection'); + }); + + var block = false; + if (db && this.editorcore.enableBlocks) { + block = Roo.htmleditor.Block.factory(db); - var store = this.formatCombo.store; - this.formatCombo.setValue(""); - for (var i =0; i < ans.length;i++) { - if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) { - // select it.. - this.formatCombo.setValue(ans[i].tagName.toLowerCase()); - break; - } + if (block) { + db.className = (db.classList.length > 0 ? db.className + ' ' : '') + + ' roo-ed-selection'; + sel = this.selectedNode = db; } } + // highlight the 'a'.. + var tn = sel && sel.tagName.toUpperCase() || ''; + if (!block && sel && tn != 'A') { + var asel = sel.closest('A'); + if (asel) { + sel = asel; + } + } + + btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href')); + btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE'); + btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false); - - // hides menus... - so this cant be on a menu... - Roo.bootstrap.MenuMgr.hideAll(); - */ Roo.bootstrap.menu.Manager.hideAll(); + + + + + + // handle delete button.. + if (hasToggle || (tn.length && tn == 'BODY')) { + this.deleteBtn.hide(); + return; + + } + this.deleteBtn.show(); + + + //this.editorsyncValue(); }, onFirstFocus: function() { @@ -31240,6 +33288,49 @@ Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simpl item.enable(); }); }, + + onDelete : function() + { + var range = this.editorcore.createRange(); + var selection = this.editorcore.getSelection(); + var sn = this.selectedNode; + range.setStart(sn,0); + range.setEnd(sn,0); + + + if (sn.hasAttribute('data-block')) { + var block = Roo.htmleditor.Block.factory(this.selectedNode); + if (block) { + sn = block.removeNode(); + sn.parentNode.removeChild(sn); + selection.removeAllRanges(); + selection.addRange(range); + this.updateToolbar(null, null, null); + this.editorcore.fireEditorEvent(false); + return; + } + + } + if (!sn) { + return; // should not really happen.. + } + if (sn && sn.tagName == 'BODY') { + return; + } + var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode; + + // remove and keep parents. + a = new Roo.htmleditor.FilterKeepChildren({tag : false}); + a.replaceTag(sn); + + selection.removeAllRanges(); + selection.addRange(range); + this.editorcore.fireEditorEvent(false); + + + }, + + toggleSourceEdit : function(sourceEditMode){ @@ -32738,10 +34829,18 @@ Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, { this.el.removeClass(['fade','top','bottom', 'left', 'right','in', 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']); + + if(this.bindEl.attr('tooltip-class')) { + this.el.addClass(this.bindEl.attr('tooltip-class')); + } var placement = typeof this.placement == 'function' ? this.placement.call(this, this.el, on_el) : this.placement; + + if(this.bindEl.attr('tooltip-placement')) { + placement = this.bindEl.attr('tooltip-placement'); + } var autoToken = /\s?auto?\s?/i; var autoPlace = autoToken.test(placement); @@ -32831,6 +34930,9 @@ Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, { return; } //this.el.setXY([0,0]); + if(this.bindEl.attr('tooltip-class')) { + this.el.removeClass(this.bindEl.attr('tooltip-class')); + } this.el.removeClass(['show', 'in']); //this.el.hide();