-});Roo.rtf = {}; // namespace
-Roo.rtf.Hex = function(hex)
-{
- this.hexstr = hex;
-};
-Roo.rtf.Paragraph = function(opts)
-{
- this.content = []; ///??? is that used?
-};Roo.rtf.Span = function(opts)
-{
- this.value = opts.value;
-};
-
-Roo.rtf.Group = function(parent)
-{
- // we dont want to acutally store parent - it will make debug a nightmare..
- this.content = [];
- this.cn = [];
-
-
-
-};
-
-Roo.rtf.Group.prototype = {
- ignorable : false,
- content: false,
- cn: false,
- addContent : function(node) {
- // could set styles...
- this.content.push(node);
- },
- addChild : function(cn)
- {
- this.cn.push(cn);
- },
- // only for images really...
- toDataURL : function()
- {
- var mimetype = false;
- switch(true) {
- case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
- mimetype = "image/png";
- break;
- case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
- mimetype = "image/jpeg";
- break;
- default :
- return 'about:blank'; // ?? error?
- }
-
-
- var hexstring = this.content[this.content.length-1].value;
-
- return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
- return String.fromCharCode(parseInt(a, 16));
- }).join(""));
- }
-
-};
-// this looks like it's normally the {rtf{ .... }}
-Roo.rtf.Document = function()
-{
- // we dont want to acutally store parent - it will make debug a nightmare..
- this.rtlch = [];
- this.content = [];
- this.cn = [];
-
-};
-Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
- addChild : function(cn)
- {
- this.cn.push(cn);
- switch(cn.type) {
- case 'rtlch': // most content seems to be inside this??
- case 'listtext':
- case 'shpinst':
- this.rtlch.push(cn);
- return;
- default:
- this[cn.type] = cn;
- }
-
- },
-
- getElementsByType : function(type)
- {
- var ret = [];
- this._getElementsByType(type, ret, this.cn, 'rtf');
- return ret;
- },
- _getElementsByType : function (type, ret, search_array, path)
- {
- search_array.forEach(function(n,i) {
- if (n.type == type) {
- n.path = path + '/' + n.type + ':' + i;
- ret.push(n);
- }
- if (n.cn.length > 0) {
- this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
- }
- },this);
- }
-
-});
-
-Roo.rtf.Ctrl = function(opts)
-{
- this.value = opts.value;
- this.param = opts.param;
-};
-/**
- *
- *
- * based on this https://github.com/iarna/rtf-parser
- * it's really only designed to extract pict from pasted RTF
- *
- * usage:
- *
- * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
- *
- *
- */
-
-
-
-
-
-Roo.rtf.Parser = function(text) {
- //super({objectMode: true})
- this.text = '';
- this.parserState = this.parseText;
-
- // these are for interpeter...
- this.doc = {};
- ///this.parserState = this.parseTop
- this.groupStack = [];
- this.hexStore = [];
- this.doc = false;
-
- this.groups = []; // where we put the return.
-
- for (var ii = 0; ii < text.length; ++ii) {
- ++this.cpos;
-
- if (text[ii] === '\n') {
- ++this.row;
- this.col = 1;
- } else {
- ++this.col;
- }
- this.parserState(text[ii]);
- }
-
-
-
-};
-Roo.rtf.Parser.prototype = {
- text : '', // string being parsed..
- controlWord : '',
- controlWordParam : '',
- hexChar : '',
- doc : false,
- group: false,
- groupStack : false,
- hexStore : false,
-
-
- cpos : 0,
- row : 1, // reportin?
- col : 1, //
-
-
- push : function (el)
- {
- var m = 'cmd'+ el.type;
- if (typeof(this[m]) == 'undefined') {
- Roo.log('invalid cmd:' + el.type);
- return;
- }
- this[m](el);
- //Roo.log(el);
- },
- flushHexStore : function()
- {
- if (this.hexStore.length < 1) {
- return;
- }
- var hexstr = this.hexStore.map(
- function(cmd) {
- return cmd.value;
- }).join('');
-
- this.group.addContent( new Roo.rtf.Hex( hexstr ));
-
-
- this.hexStore.splice(0)
-
- },
-
- cmdgroupstart : function()
- {
- this.flushHexStore();
- if (this.group) {
- this.groupStack.push(this.group);
- }
- // parent..
- if (this.doc === false) {
- this.group = this.doc = new Roo.rtf.Document();
- return;
-
- }
- this.group = new Roo.rtf.Group(this.group);
- },
- cmdignorable : function()
- {
- this.flushHexStore();
- this.group.ignorable = true;
- },
- cmdendparagraph : function()
- {
- this.flushHexStore();
- this.group.addContent(new Roo.rtf.Paragraph());
- },
- cmdgroupend : function ()
- {
- this.flushHexStore();
- var endingGroup = this.group;
-
-
- this.group = this.groupStack.pop();
- if (this.group) {
- this.group.addChild(endingGroup);
- }
-
-
-
- var doc = this.group || this.doc;
- //if (endingGroup instanceof FontTable) {
- // doc.fonts = endingGroup.table
- //} else if (endingGroup instanceof ColorTable) {
- // doc.colors = endingGroup.table
- //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
- if (endingGroup.ignorable === false) {
- //code
- this.groups.push(endingGroup);
- // Roo.log( endingGroup );
- }
- //Roo.each(endingGroup.content, function(item)) {
- // doc.addContent(item);
- //}
- //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
- //}
- },
- cmdtext : function (cmd)
- {
- this.flushHexStore();
- if (!this.group) { // an RTF fragment, missing the {\rtf1 header
- //this.group = this.doc
- }
- this.group.addContent(new Roo.rtf.Span(cmd));
- },
- cmdcontrolword : function (cmd)
- {
- this.flushHexStore();
- if (!this.group.type) {
- this.group.type = cmd.value;
- return;
- }
- this.group.addContent(new Roo.rtf.Ctrl(cmd));
- // we actually don't care about ctrl words...
- return ;
- /*
- var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
- if (this[method]) {
- this[method](cmd.param)
- } else {
- if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
- }
- */
- },
- cmdhexchar : function(cmd) {
- this.hexStore.push(cmd);
- },
- cmderror : function(cmd) {
- throw new Exception (cmd.value);
- },
-
- /*
- _flush (done) {
- if (this.text !== '\u0000') this.emitText()
- done()
- }
- */
-
-
- parseText : function(c)
- {
- if (c === '\\') {
- this.parserState = this.parseEscapes;
- } else if (c === '{') {
- this.emitStartGroup();
- } else if (c === '}') {
- this.emitEndGroup();
- } else if (c === '\x0A' || c === '\x0D') {
- // cr/lf are noise chars
- } else {
- this.text += c;
- }
- },
-
- parseEscapes: function (c)
- {
- if (c === '\\' || c === '{' || c === '}') {
- this.text += c;
- this.parserState = this.parseText;
- } else {
- this.parserState = this.parseControlSymbol;
- this.parseControlSymbol(c);
- }
- },
- parseControlSymbol: function(c)
- {
- if (c === '~') {
- this.text += '\u00a0'; // nbsp
- this.parserState = this.parseText
- } else if (c === '-') {
- this.text += '\u00ad'; // soft hyphen
- } else if (c === '_') {
- this.text += '\u2011'; // non-breaking hyphen
- } else if (c === '*') {
- this.emitIgnorable();
- this.parserState = this.parseText;
- } else if (c === "'") {
- this.parserState = this.parseHexChar;
- } else if (c === '|') { // formula cacter
- this.emitFormula();
- this.parserState = this.parseText;
- } else if (c === ':') { // subentry in an index entry
- this.emitIndexSubEntry();
- this.parserState = this.parseText;
- } else if (c === '\x0a') {
- this.emitEndParagraph();
- this.parserState = this.parseText;
- } else if (c === '\x0d') {
- this.emitEndParagraph();
- this.parserState = this.parseText;
- } else {
- this.parserState = this.parseControlWord;
- this.parseControlWord(c);
- }
- },
- parseHexChar: function (c)
- {
- if (/^[A-Fa-f0-9]$/.test(c)) {
- this.hexChar += c;
- if (this.hexChar.length >= 2) {
- this.emitHexChar();
- this.parserState = this.parseText;
- }
- return;
- }
- this.emitError("Invalid character \"" + c + "\" in hex literal.");
- this.parserState = this.parseText;
-
- },
- parseControlWord : function(c)
- {
- if (c === ' ') {
- this.emitControlWord();
- this.parserState = this.parseText;
- } else if (/^[-\d]$/.test(c)) {
- this.parserState = this.parseControlWordParam;
- this.controlWordParam += c;
- } else if (/^[A-Za-z]$/.test(c)) {
- this.controlWord += c;
- } else {
- this.emitControlWord();
- this.parserState = this.parseText;
- this.parseText(c);
- }
- },
- parseControlWordParam : function (c) {
- if (/^\d$/.test(c)) {
- this.controlWordParam += c;
- } else if (c === ' ') {
- this.emitControlWord();
- this.parserState = this.parseText;
- } else {
- this.emitControlWord();
- this.parserState = this.parseText;
- this.parseText(c);
- }
- },
-
-
-
-
- emitText : function () {
- if (this.text === '') {
- return;
- }
- this.push({
- type: 'text',
- value: this.text,
- pos: this.cpos,
- row: this.row,
- col: this.col
- });
- this.text = ''
- },
- emitControlWord : function ()
- {
- this.emitText();
- if (this.controlWord === '') {
- this.emitError('empty control word');
- } else {
- this.push({
- type: 'controlword',
- value: this.controlWord,
- param: this.controlWordParam !== '' && Number(this.controlWordParam),
- pos: this.cpos,
- row: this.row,
- col: this.col
- });
- }
- this.controlWord = '';
- this.controlWordParam = '';
- },
- emitStartGroup : function ()
- {
- this.emitText();
- this.push({
- type: 'groupstart',
- pos: this.cpos,
- row: this.row,
- col: this.col
- });
- },
- emitEndGroup : function ()
- {
- this.emitText();
- this.push({
- type: 'groupend',
- pos: this.cpos,
- row: this.row,
- col: this.col
- });
- },
- emitIgnorable : function ()
- {
- this.emitText();
- this.push({
- type: 'ignorable',
- pos: this.cpos,
- row: this.row,
- col: this.col
- });
- },
- emitHexChar : function ()
- {
- this.emitText();
- this.push({
- type: 'hexchar',
- value: this.hexChar,
- pos: this.cpos,
- row: this.row,
- col: this.col
- });
- this.hexChar = ''
- },
- emitError : function (message)
- {
- this.emitText();
- this.push({
- type: 'error',
- value: message,
- row: this.row,
- col: this.col,
- char: this.cpos //,
- //stack: new Error().stack
- });
- },
- emitEndParagraph : function () {
- this.emitText();
- this.push({
- type: 'endparagraph',
- pos: this.cpos,
- row: this.row,
- col: this.col
- });
- }
-
-} ;
-Roo.htmleditor = {};
-
-/**
- * @class Roo.htmleditor.Filter
- * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
- * @cfg {DomElement} node The node to iterate and filter
- * @cfg {boolean|String|Array} tag Tags to replace
- * @constructor
- * Create a new Filter.
- * @param {Object} config Configuration options
- */
-
-
-
-Roo.htmleditor.Filter = function(cfg) {
- Roo.apply(this.cfg);
- // this does not actually call walk as it's really just a abstract class
-}
-
-
-Roo.htmleditor.Filter.prototype = {
-
- node: false,
-
- tag: false,
-
- // overrride to do replace comments.
- replaceComment : false,
-
- // overrride to do replace or do stuff with tags..
- replaceTag : false,
-
- walk : function(dom)
- {
- Roo.each( Array.from(dom.childNodes), function( e ) {
- switch(true) {
-
- case e.nodeType == 8 && this.replaceComment !== false: // comment
- this.replaceComment(e);
- return;
-
- case e.nodeType != 1: //not a node.
- return;
-
- case this.tag === true: // everything
- case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
- case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
- if (this.replaceTag && false === this.replaceTag(e)) {
- return;
- }
- if (e.hasChildNodes()) {
- this.walk(e);
- }
- return;
-
- default: // tags .. that do not match.
- if (e.hasChildNodes()) {
- this.walk(e);
- }
- }
-
- }, this);
-
- }
-};
-
-/**
- * @class Roo.htmleditor.FilterAttributes
- * clean attributes and styles including http:// etc.. in attribute
- * @constructor
-* Run a new Attribute Filter
-* @param {Object} config Configuration options
- */
-Roo.htmleditor.FilterAttributes = function(cfg)
-{
- Roo.apply(this, cfg);
- this.attrib_black = this.attrib_black || [];
- this.attrib_white = this.attrib_white || [];
-
- this.attrib_clean = this.attrib_clean || [];
- this.style_white = this.style_white || [];
- this.style_black = this.style_black || [];
- this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
-{
- tag: true, // all tags
-
- attrib_black : false, // array
- attrib_clean : false,
- attrib_white : false,
-
- style_white : false,
- style_black : false,
-
-
- replaceTag : function(node)
- {
- if (!node.attributes || !node.attributes.length) {
- return true;
- }
-
- for (var i = node.attributes.length-1; i > -1 ; i--) {
- var a = node.attributes[i];
- //console.log(a);
- if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
- node.removeAttribute(a.name);
- continue;
- }
-
-
-
- if (a.name.toLowerCase().substr(0,2)=='on') {
- node.removeAttribute(a.name);
- continue;
- }
-
-
- if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
- node.removeAttribute(a.name);
- continue;
- }
- if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
- this.cleanAttr(node,a.name,a.value); // fixme..
- continue;
- }
- if (a.name == 'style') {
- this.cleanStyle(node,a.name,a.value);
- continue;
- }
- /// clean up MS crap..
- // tecnically this should be a list of valid class'es..
-
-
- if (a.name == 'class') {
- if (a.value.match(/^Mso/)) {
- node.removeAttribute('class');
- }
-
- if (a.value.match(/^body$/)) {
- node.removeAttribute('class');
- }
- continue;
- }
-
-
- // style cleanup!?
- // class cleanup?
-
- }
- return true; // clean children
- },
-
- cleanAttr: function(node, n,v)
- {
-
- if (v.match(/^\./) || v.match(/^\//)) {
- return;
- }
- if (v.match(/^(http|https):\/\//)
- || v.match(/^mailto:/)
- || v.match(/^ftp:/)
- || v.match(/^data:/)
- ) {
- return;
- }
- if (v.match(/^#/)) {
- return;
- }
- if (v.match(/^\{/)) { // allow template editing.
- return;
- }
-// Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
- node.removeAttribute(n);
-
- },
- cleanStyle : function(node, n,v)
- {
- if (v.match(/expression/)) { //XSS?? should we even bother..
- node.removeAttribute(n);
- return;
- }
-
- var parts = v.split(/;/);
- var clean = [];
-
- Roo.each(parts, function(p) {
- p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
- if (!p.length) {
- return true;
- }
- var l = p.split(':').shift().replace(/\s+/g,'');
- l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
-
- if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
- return true;
- }
- //Roo.log()
- // only allow 'c whitelisted system attributes'
- if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
- return true;
- }
-
-
- clean.push(p);
- return true;
- },this);
- if (clean.length) {
- node.setAttribute(n, clean.join(';'));
- } else {
- node.removeAttribute(n);
- }
-
- }
-
-
-
-
-});/**
- * @class Roo.htmleditor.FilterBlack
- * remove blacklisted elements.
- * @constructor
- * Run a new Blacklisted Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterBlack = function(cfg)
-{
- Roo.apply(this, cfg);
- this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
-{
- tag : true, // all elements.
-
- replaceTag : function(n)
- {
- n.parentNode.removeChild(n);
- }
-});
-/**
- * @class Roo.htmleditor.FilterComment
- * remove comments.
- * @constructor
-* Run a new Comments Filter
-* @param {Object} config Configuration options
- */
-Roo.htmleditor.FilterComment = function(cfg)
-{
- this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
-{
-
- replaceComment : function(n)
- {
- n.parentNode.removeChild(n);
- }
-});/**
- * @class Roo.htmleditor.FilterKeepChildren
- * remove tags but keep children
- * @constructor
- * Run a new Keep Children Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterKeepChildren = function(cfg)
-{
- Roo.apply(this, cfg);
- if (this.tag === false) {
- return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
- }
- this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
-{
-
-
- replaceTag : function(node)
- {
- // walk children...
- //Roo.log(node);
- var ar = Array.from(node.childNodes);
- //remove first..
- for (var i = 0; i < ar.length; i++) {
- if (ar[i].nodeType == 1) {
- if (
- (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
- || // array and it matches
- (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
- ) {
- this.replaceTag(ar[i]); // child is blacklisted as well...
- continue;
- }
- }
- }
- ar = Array.from(node.childNodes);
- for (var i = 0; i < ar.length; i++) {
-
- node.removeChild(ar[i]);
- // what if we need to walk these???
- node.parentNode.insertBefore(ar[i], node);
- if (this.tag !== false) {
- this.walk(ar[i]);
-
- }
- }
- node.parentNode.removeChild(node);
- return false; // don't walk children
-
-
- }
-});/**
- * @class Roo.htmleditor.FilterParagraph
- * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
- * like on 'push' to remove the <p> tags and replace them with line breaks.
- * @constructor
- * Run a new Paragraph Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterParagraph = function(cfg)
-{
- // no need to apply config.
- this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
-{
-
-
- tag : 'P',
-
-
- replaceTag : function(node)
- {
-
- if (node.childNodes.length == 1 &&
- node.childNodes[0].nodeType == 3 &&
- node.childNodes[0].textContent.trim().length < 1
- ) {
- // remove and replace with '<BR>';
- node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
- return false; // no need to walk..
- }
- var ar = Array.from(node.childNodes);
- for (var i = 0; i < ar.length; i++) {
- node.removeChild(ar[i]);
- // what if we need to walk these???
- node.parentNode.insertBefore(ar[i], node);
- }
- // now what about this?
- // <p> </p>
-
- // double BR.
- node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
- node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
- node.parentNode.removeChild(node);
-
- return false;
-
- }
-
-});/**
- * @class Roo.htmleditor.FilterSpan
- * filter span's with no attributes out..
- * @constructor
- * Run a new Span Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterSpan = function(cfg)
-{
- // no need to apply config.
- this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
-{
-
- tag : 'SPAN',
-
-
- replaceTag : function(node)
- {
- if (node.attributes && node.attributes.length > 0) {
- return true; // walk if there are any.
- }
- Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
- return false;
-
- }
-
-});/**
- * @class Roo.htmleditor.FilterTableWidth
- try and remove table width data - as that frequently messes up other stuff.
- *
- * was cleanTableWidths.
- *
- * Quite often pasting from word etc.. results in tables with column and widths.
- * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
- *
- * @constructor
- * Run a new Table Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterTableWidth = function(cfg)
-{
- // no need to apply config.
- this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
- this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
-{
-
-
-
- replaceTag: function(node) {
-
-
-
- if (node.hasAttribute('width')) {
- node.removeAttribute('width');
- }
-
-
- if (node.hasAttribute("style")) {
- // pretty basic...
-
- var styles = node.getAttribute("style").split(";");
- var nstyle = [];
- Roo.each(styles, function(s) {
- if (!s.match(/:/)) {
- return;
- }
- var kv = s.split(":");
- if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
- return;
- }
- // what ever is left... we allow.
- nstyle.push(s);
- });
- node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
- if (!nstyle.length) {
- node.removeAttribute('style');
- }
- }
-
- return true; // continue doing children..
- }
-});/**
- * @class Roo.htmleditor.FilterWord
- * try and clean up all the mess that Word generates.
- *
- * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
-
- * @constructor
- * Run a new Span Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterWord = function(cfg)
-{
- // no need to apply config.
- this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
-{
- tag: true,
-
-
- /**
- * Clean up MS wordisms...
- */
- replaceTag : function(node)
- {
-
- // no idea what this does - span with text, replaceds with just text.
- if(
- node.nodeName == 'SPAN' &&
- !node.hasAttributes() &&
- node.childNodes.length == 1 &&
- node.firstChild.nodeName == "#text"
- ) {
- var textNode = node.firstChild;
- node.removeChild(textNode);
- if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
- node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
- }
- node.parentNode.insertBefore(textNode, node);
- if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
- node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
- }
-
- node.parentNode.removeChild(node);
- return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
- }
-
-
-
- if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
- node.parentNode.removeChild(node);
- return false; // dont do chidlren
- }
- //Roo.log(node.tagName);
- // remove - but keep children..
- if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
- //Roo.log('-- removed');
- while (node.childNodes.length) {
- var cn = node.childNodes[0];
- node.removeChild(cn);
- node.parentNode.insertBefore(cn, node);
- // move node to parent - and clean it..
- this.replaceTag(cn);
- }
- node.parentNode.removeChild(node);
- /// no need to iterate chidlren = it's got none..
- //this.iterateChildren(node, this.cleanWord);
- return false; // no need to iterate children.
- }
- // clean styles
- if (node.className.length) {
-
- var cn = node.className.split(/\W+/);
- var cna = [];
- Roo.each(cn, function(cls) {
- if (cls.match(/Mso[a-zA-Z]+/)) {
- return;
- }
- cna.push(cls);
- });
- node.className = cna.length ? cna.join(' ') : '';
- if (!cna.length) {
- node.removeAttribute("class");
- }
- }
-
- if (node.hasAttribute("lang")) {
- node.removeAttribute("lang");
- }
-
- if (node.hasAttribute("style")) {
-
- var styles = node.getAttribute("style").split(";");
- var nstyle = [];
- Roo.each(styles, function(s) {
- if (!s.match(/:/)) {
- return;
- }
- var kv = s.split(":");
- if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
- return;
- }
- // what ever is left... we allow.
- nstyle.push(s);
- });
- node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
- if (!nstyle.length) {
- node.removeAttribute('style');
- }
- }
- return true; // do children
-
-
-
- }
-});
-/**
- * @class Roo.htmleditor.FilterStyleToTag
- * part of the word stuff... - certain 'styles' should be converted to tags.
- * eg.
- * font-weight: bold -> bold
- * ?? super / subscrit etc..
- *
- * @constructor
-* Run a new style to tag filter.
-* @param {Object} config Configuration options
- */
-Roo.htmleditor.FilterStyleToTag = function(cfg)
-{
-
- this.tags = {
- B : [ 'fontWeight' , 'bold'],
- I : [ 'fontStyle' , 'italic'],
- //pre : [ 'font-style' , 'italic'],
- // h1.. h6 ?? font-size?
- SUP : [ 'verticalAlign' , 'super' ],
- SUB : [ 'verticalAlign' , 'sub' ]
-
-
- };
-
- Roo.apply(this, cfg);
-
-
- this.walk(cfg.node);
-
-
-
-}
-
-
-Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
-{
- tag: true, // all tags
-
- tags : false,
-
-
- replaceTag : function(node)
- {
-
-
- if (node.getAttribute("style") === null) {
- return true;
- }
- var inject = [];
- for (var k in this.tags) {
- if (node.style[this.tags[k][0]] == this.tags[k][1]) {
- inject.push(k);
- node.style.removeProperty(this.tags[k][0]);
- }
- }
- if (!inject.length) {
- return true;
- }
- var cn = Array.from(node.childNodes);
- var nn = node;
- Roo.each(inject, function(t) {
- var nc = node.ownerDocument.createElement(t);
- nn.appendChild(nc);
- nn = nc;
- });
- for(var i = 0;i < cn.length;cn++) {
- node.removeChild(cn[i]);
- nn.appendChild(cn[i]);
- }
- return true /// iterate thru
- }
-
-})/**
- * @class Roo.htmleditor.FilterLongBr
- * BR/BR/BR - keep a maximum of 2...
- * @constructor
- * Run a new Long BR Filter
- * @param {Object} config Configuration options
- */
-
-Roo.htmleditor.FilterLongBr = function(cfg)
-{
- // no need to apply config.
- this.walk(cfg.node);
-}
-
-Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
-{
-
-
- tag : 'BR',
-
-
- replaceTag : function(node)
- {
-
- var ps = node.nextSibling;
- while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
- ps = ps.nextSibling;
- }
-
- if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
- node.parentNode.removeChild(node); // remove last BR inside one fo these tags
- return false;
- }
-
- if (!ps || ps.nodeType != 1) {
- return false;
- }
-
- if (!ps || ps.tagName != 'BR') {
-
- return false;
- }
-
-
-
-
-
- if (!node.previousSibling) {
- return false;
- }
- var ps = node.previousSibling;
-
- while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
- ps = ps.previousSibling;
- }
- if (!ps || ps.nodeType != 1) {
- return false;
- }
- // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
- if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
- return false;
- }
-
- node.parentNode.removeChild(node); // remove me...
-
- return false; // no need to do children
-
- }
-
-});
-
-/**
- * @class Roo.htmleditor.FilterBlock
- * removes id / data-block and contenteditable that are associated with blocks
- * usage should be done on a cloned copy of the dom
- * @constructor
-* Run a new Attribute Filter { node : xxxx }}
-* @param {Object} config Configuration options
- */
-Roo.htmleditor.FilterBlock = function(cfg)
-{
- Roo.apply(this, cfg);
- var qa = cfg.node.querySelectorAll;
- this.removeAttributes('data-block');
- this.removeAttributes('contenteditable');
- this.removeAttributes('id');
-
-}
-
-Roo.apply(Roo.htmleditor.FilterBlock.prototype,
-{
- node: true, // all tags
-
-
- removeAttributes : function(attr)
- {
- var ar = this.node.querySelectorAll('*[' + attr + ']');
- for (var i =0;i<ar.length;i++) {
- ar[i].removeAttribute(attr);
- }
- }
-
-
-
-
-});
-/***
- * This is based loosely on tinymce
- * @class Roo.htmleditor.TidySerializer
- * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
- * @constructor
- * @method Serializer
- * @param {Object} settings Name/value settings object.
- */
-
-
-Roo.htmleditor.TidySerializer = function(settings)
-{
- Roo.apply(this, settings);
-
- this.writer = new Roo.htmleditor.TidyWriter(settings);
-
-
-
-};
-Roo.htmleditor.TidySerializer.prototype = {
-
- /**
- * @param {boolean} inner do the inner of the node.
- */
- inner : false,
-
- writer : false,
-
- /**
- * Serializes the specified node into a string.
- *
- * @example
- * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
- * @method serialize
- * @param {DomElement} node Node instance to serialize.
- * @return {String} String with HTML based on DOM tree.
- */
- serialize : function(node) {
-
- // = settings.validate;
- var writer = this.writer;
- var self = this;
- this.handlers = {
- // #text
- 3: function(node) {
-
- writer.text(node.nodeValue, node);
- },
- // #comment
- 8: function(node) {
- writer.comment(node.nodeValue);
- },
- // Processing instruction
- 7: function(node) {
- writer.pi(node.name, node.nodeValue);
- },
- // Doctype
- 10: function(node) {
- writer.doctype(node.nodeValue);
- },
- // CDATA
- 4: function(node) {
- writer.cdata(node.nodeValue);
- },
- // Document fragment
- 11: function(node) {
- node = node.firstChild;
- if (!node) {
- return;
- }
- while(node) {
- self.walk(node);
- node = node.nextSibling
- }
- }
- };
- writer.reset();
- 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
- return writer.getContent();
- },
-
- walk: function(node)
- {
- var attrName, attrValue, sortedAttrs, i, l, elementRule,
- handler = this.handlers[node.nodeType];
-
- if (handler) {
- handler(node);
- return;
- }
-
- var name = node.nodeName;
- var isEmpty = node.childNodes.length < 1;
-
- var writer = this.writer;
- var attrs = node.attributes;
- // Sort attributes
-
- writer.start(node.nodeName, attrs, isEmpty, node);
- if (isEmpty) {
- return;
- }
- node = node.firstChild;
- if (!node) {
- writer.end(name);
- return;
- }
- while (node) {
- this.walk(node);
- node = node.nextSibling;
- }
- writer.end(name);
-
-
- }
- // Serialize element and treat all non elements as fragments
-
-};
-
-/***
- * This is based loosely on tinymce
- * @class Roo.htmleditor.TidyWriter
- * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
- *
- * Known issues?
- * - not tested much with 'PRE' formated elements.
- *
- *
- *
- */
-
-Roo.htmleditor.TidyWriter = function(settings)
-{
-
- // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
- Roo.apply(this, settings);
- this.html = [];
- this.state = [];
-
- this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
-
-}
-Roo.htmleditor.TidyWriter.prototype = {
-
-
- state : false,
-
- indent : ' ',
-
- // part of state...
- indentstr : '',
- in_pre: false,
- in_inline : false,
- last_inline : false,
- encode : false,
-
-
- /**
- * Writes the a start element such as <p id="a">.
- *
- * @method start
- * @param {String} name Name of the element.
- * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
- * @param {Boolean} empty Optional empty state if the tag should end like <br />.
- */
- start: function(name, attrs, empty, node)
- {
- var i, l, attr, value;
-
- // there are some situations where adding line break && indentation will not work. will not work.
- // <span / b / i ... formating?
-
- var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
- var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
-
- var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
-
- var add_lb = name == 'BR' ? false : in_inline;
-
- if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
- i_inline = false;
- }
-
- var indentstr = this.indentstr;
-
- // e_inline = elements that can be inline, but still allow \n before and after?
- // only 'BR' ??? any others?
-
- // ADD LINE BEFORE tage
- if (!this.in_pre) {
- if (in_inline) {
- //code
- if (name == 'BR') {
- this.addLine();
- } else if (this.lastElementEndsWS()) {
- this.addLine();
- } else{
- // otherwise - no new line. (and dont indent.)
- indentstr = '';
- }
-
- } else {
- this.addLine();
- }
- } else {
- indentstr = '';
- }
-
- this.html.push(indentstr + '<', name.toLowerCase());
-
- if (attrs) {
- for (i = 0, l = attrs.length; i < l; i++) {
- attr = attrs[i];
- this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
- }
- }
-
- if (empty) {
- if (is_short) {
- this.html[this.html.length] = '/>';
- } else {
- this.html[this.html.length] = '></' + name.toLowerCase() + '>';
- }
- var e_inline = name == 'BR' ? false : this.in_inline;
-
- if (!e_inline && !this.in_pre) {
- this.addLine();
- }
- return;
-
- }
- // not empty..
- this.html[this.html.length] = '>';
-
- // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
- /*
- if (!in_inline && !in_pre) {
- var cn = node.firstChild;
- while(cn) {
- if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
- in_inline = true
- break;
- }
- cn = cn.nextSibling;
- }
-
- }
- */
-
-
- this.pushState({
- indentstr : in_pre ? '' : (this.indentstr + this.indent),
- in_pre : in_pre,
- in_inline : in_inline
- });
- // add a line after if we are not in a
-
- if (!in_inline && !in_pre) {
- this.addLine();
- }
-
-
-
-
- },
-
- lastElementEndsWS : function()
- {
- var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
- if (value === false) {
- return true;
- }
- return value.match(/\s+$/);
-
- },
-
- /**
- * Writes the a end element such as </p>.
- *
- * @method end
- * @param {String} name Name of the element.
- */
- end: function(name) {
- var value;
- this.popState();
- var indentstr = '';
- var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
-
- if (!this.in_pre && !in_inline) {
- this.addLine();
- indentstr = this.indentstr;
- }
- this.html.push(indentstr + '</', name.toLowerCase(), '>');
- this.last_inline = in_inline;
-
- // pop the indent state..
- },
- /**
- * Writes a text node.
- *
- * In pre - we should not mess with the contents.
- *
- *
- * @method text
- * @param {String} text String to write out.
- * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
- */
- text: function(text, node)
- {
- // if not in whitespace critical
- if (text.length < 1) {
- return;
- }
- if (this.in_pre) {
- this.html[this.html.length] = text;
- return;
- }
-
- if (this.in_inline) {
- text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
- if (text != ' ') {
- text = text.replace(/\s+/,' '); // all white space to single white space
-
-
- // if next tag is '<BR>', then we can trim right..
- if (node.nextSibling &&
- node.nextSibling.nodeType == 1 &&
- node.nextSibling.nodeName == 'BR' )
- {
- text = text.replace(/\s+$/g,'');
- }
- // if previous tag was a BR, we can also trim..
- if (node.previousSibling &&
- node.previousSibling.nodeType == 1 &&
- node.previousSibling.nodeName == 'BR' )
- {
- text = this.indentstr + text.replace(/^\s+/g,'');
- }
- if (text.match(/\n/)) {
- text = text.replace(
- /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
- );
- // remoeve the last whitespace / line break.
- text = text.replace(/\n\s+$/,'');
- }
- // repace long lines
-
- }
-
- this.html[this.html.length] = text;
- return;
- }
- // see if previous element was a inline element.
- var indentstr = this.indentstr;
-
- text = text.replace(/\s+/g," "); // all whitespace into single white space.
-
- // should trim left?
- if (node.previousSibling &&
- node.previousSibling.nodeType == 1 &&
- Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
- {
- indentstr = '';
-
- } else {
- this.addLine();
- text = text.replace(/^\s+/,''); // trim left
-
- }
- // should trim right?
- if (node.nextSibling &&
- node.nextSibling.nodeType == 1 &&
- Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
- {
- // noop
-
- } else {
- text = text.replace(/\s+$/,''); // trim right
- }
-
-
-
-
-
- if (text.length < 1) {
- return;
- }
- if (!text.match(/\n/)) {
- this.html.push(indentstr + text);
- return;
- }
-
- text = this.indentstr + text.replace(
- /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
- );
- // remoeve the last whitespace / line break.
- text = text.replace(/\s+$/,'');
-
- this.html.push(text);
-
- // split and indent..
-
-
- },
- /**
- * Writes a cdata node such as <![CDATA[data]]>.
- *
- * @method cdata
- * @param {String} text String to write out inside the cdata.
- */
- cdata: function(text) {
- this.html.push('<![CDATA[', text, ']]>');
- },
- /**
- * Writes a comment node such as <!-- Comment -->.
- *
- * @method cdata
- * @param {String} text String to write out inside the comment.
- */
- comment: function(text) {
- this.html.push('<!--', text, '-->');
- },
- /**
- * Writes a PI node such as <?xml attr="value" ?>.
- *
- * @method pi
- * @param {String} name Name of the pi.
- * @param {String} text String to write out inside the pi.
- */
- pi: function(name, text) {
- text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
- this.indent != '' && this.html.push('\n');
- },
- /**
- * Writes a doctype node such as <!DOCTYPE data>.
- *
- * @method doctype
- * @param {String} text String to write out inside the doctype.
- */
- doctype: function(text) {
- this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
- },
- /**
- * Resets the internal buffer if one wants to reuse the writer.
- *
- * @method reset
- */
- reset: function() {
- this.html.length = 0;
- this.state = [];
- this.pushState({
- indentstr : '',
- in_pre : false,
- in_inline : false
- })
- },
- /**
- * Returns the contents that got serialized.
- *
- * @method getContent
- * @return {String} HTML contents that got written down.
- */
- getContent: function() {
- return this.html.join('').replace(/\n$/, '');
- },
-
- pushState : function(cfg)
- {
- this.state.push(cfg);
- Roo.apply(this, cfg);
- },
-
- popState : function()
- {
- if (this.state.length < 1) {
- return; // nothing to push
- }
- var cfg = {
- in_pre: false,
- indentstr : ''
- };
- this.state.pop();
- if (this.state.length > 0) {
- cfg = this.state[this.state.length-1];
- }
- Roo.apply(this, cfg);
- },
-
- addLine: function()
- {
- if (this.html.length < 1) {
- return;
- }
-
-
- var value = this.html[this.html.length - 1];
- if (value.length > 0 && '\n' !== value) {
- this.html.push('\n');
- }
- }
-
-
-//'pre script noscript style textarea video audio iframe object code'
-// shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
-// inline
-};
-
-Roo.htmleditor.TidyWriter.inline_elements = [
- 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
- 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
-];
-Roo.htmleditor.TidyWriter.shortend_elements = [
- 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
- 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
-];
-
-Roo.htmleditor.TidyWriter.whitespace_elements = [
- 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
-];/***
- * This is based loosely on tinymce
- * @class Roo.htmleditor.TidyEntities
- * @static
- * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
- *
- * Not 100% sure this is actually used or needed.
- */
-
-Roo.htmleditor.TidyEntities = {
-
- /**
- * initialize data..
- */
- init : function (){
-
- this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
-
- },
-
-
- buildEntitiesLookup: function(items, radix) {
- var i, chr, entity, lookup = {};
- if (!items) {
- return {};
- }
- items = typeof(items) == 'string' ? items.split(',') : items;
- radix = radix || 10;
- // Build entities lookup table
- for (i = 0; i < items.length; i += 2) {
- chr = String.fromCharCode(parseInt(items[i], radix));
- // Only add non base entities
- if (!this.baseEntities[chr]) {
- entity = '&' + items[i + 1] + ';';
- lookup[chr] = entity;
- lookup[entity] = chr;
- }
- }
- return lookup;
-
- },
-
- asciiMap : {
- 128: '€',
- 130: '‚',
- 131: 'ƒ',
- 132: '„',
- 133: '…',
- 134: '†',
- 135: '‡',
- 136: 'ˆ',
- 137: '‰',
- 138: 'Š',
- 139: '‹',
- 140: 'Œ',
- 142: 'Ž',
- 145: '‘',
- 146: '’',
- 147: '“',
- 148: '”',
- 149: '•',
- 150: '–',
- 151: '—',
- 152: '˜',
- 153: '™',
- 154: 'š',
- 155: '›',
- 156: 'œ',
- 158: 'ž',
- 159: 'Ÿ'
- },
- // Raw entities
- baseEntities : {
- '"': '"',
- // Needs to be escaped since the YUI compressor would otherwise break the code
- '\'': ''',
- '<': '<',
- '>': '>',
- '&': '&',
- '`': '`'
- },
- // Reverse lookup table for raw entities
- reverseEntities : {
- '<': '<',
- '>': '>',
- '&': '&',
- '"': '"',
- ''': '\''
- },
-
- attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
- textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
- rawCharsRegExp : /[<>&\"\']/g,
- entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
- namedEntities : false,
- namedEntitiesData : [
- '50',
- 'nbsp',
- '51',
- 'iexcl',
- '52',
- 'cent',
- '53',
- 'pound',
- '54',
- 'curren',
- '55',
- 'yen',
- '56',
- 'brvbar',
- '57',
- 'sect',
- '58',
- 'uml',
- '59',
- 'copy',
- '5a',
- 'ordf',
- '5b',
- 'laquo',
- '5c',
- 'not',
- '5d',
- 'shy',
- '5e',
- 'reg',
- '5f',
- 'macr',
- '5g',
- 'deg',
- '5h',
- 'plusmn',
- '5i',
- 'sup2',
- '5j',
- 'sup3',
- '5k',
- 'acute',
- '5l',
- 'micro',
- '5m',
- 'para',
- '5n',
- 'middot',
- '5o',
- 'cedil',
- '5p',
- 'sup1',
- '5q',
- 'ordm',
- '5r',
- 'raquo',
- '5s',
- 'frac14',
- '5t',
- 'frac12',
- '5u',
- 'frac34',
- '5v',
- 'iquest',
- '60',
- 'Agrave',
- '61',
- 'Aacute',
- '62',
- 'Acirc',
- '63',
- 'Atilde',
- '64',
- 'Auml',
- '65',
- 'Aring',
- '66',
- 'AElig',
- '67',
- 'Ccedil',
- '68',
- 'Egrave',
- '69',
- 'Eacute',
- '6a',
- 'Ecirc',
- '6b',
- 'Euml',
- '6c',
- 'Igrave',
- '6d',
- 'Iacute',
- '6e',
- 'Icirc',
- '6f',
- 'Iuml',
- '6g',
- 'ETH',
- '6h',
- 'Ntilde',
- '6i',
- 'Ograve',
- '6j',
- 'Oacute',
- '6k',
- 'Ocirc',
- '6l',
- 'Otilde',
- '6m',
- 'Ouml',
- '6n',
- 'times',
- '6o',
- 'Oslash',
- '6p',
- 'Ugrave',
- '6q',
- 'Uacute',
- '6r',
- 'Ucirc',
- '6s',
- 'Uuml',
- '6t',
- 'Yacute',
- '6u',
- 'THORN',
- '6v',
- 'szlig',
- '70',
- 'agrave',
- '71',
- 'aacute',
- '72',
- 'acirc',
- '73',
- 'atilde',
- '74',
- 'auml',
- '75',
- 'aring',
- '76',
- 'aelig',
- '77',
- 'ccedil',
- '78',
- 'egrave',
- '79',
- 'eacute',
- '7a',
- 'ecirc',
- '7b',
- 'euml',
- '7c',
- 'igrave',
- '7d',
- 'iacute',
- '7e',
- 'icirc',
- '7f',
- 'iuml',
- '7g',
- 'eth',
- '7h',
- 'ntilde',
- '7i',
- 'ograve',
- '7j',
- 'oacute',
- '7k',
- 'ocirc',
- '7l',
- 'otilde',
- '7m',
- 'ouml',
- '7n',
- 'divide',
- '7o',
- 'oslash',
- '7p',
- 'ugrave',
- '7q',
- 'uacute',
- '7r',
- 'ucirc',
- '7s',
- 'uuml',
- '7t',
- 'yacute',
- '7u',
- 'thorn',
- '7v',
- 'yuml',
- 'ci',
- 'fnof',
- 'sh',
- 'Alpha',
- 'si',
- 'Beta',
- 'sj',
- 'Gamma',
- 'sk',
- 'Delta',
- 'sl',
- 'Epsilon',
- 'sm',
- 'Zeta',
- 'sn',
- 'Eta',
- 'so',
- 'Theta',
- 'sp',
- 'Iota',
- 'sq',
- 'Kappa',
- 'sr',
- 'Lambda',
- 'ss',
- 'Mu',
- 'st',
- 'Nu',
- 'su',
- 'Xi',
- 'sv',
- 'Omicron',
- 't0',
- 'Pi',
- 't1',
- 'Rho',
- 't3',
- 'Sigma',
- 't4',
- 'Tau',
- 't5',
- 'Upsilon',
- 't6',
- 'Phi',
- 't7',
- 'Chi',
- 't8',
- 'Psi',
- 't9',
- 'Omega',
- 'th',
- 'alpha',
- 'ti',
- 'beta',
- 'tj',
- 'gamma',
- 'tk',
- 'delta',
- 'tl',
- 'epsilon',
- 'tm',
- 'zeta',
- 'tn',
- 'eta',
- 'to',
- 'theta',
- 'tp',
- 'iota',
- 'tq',
- 'kappa',
- 'tr',
- 'lambda',
- 'ts',
- 'mu',
- 'tt',
- 'nu',
- 'tu',
- 'xi',
- 'tv',
- 'omicron',
- 'u0',
- 'pi',
- 'u1',
- 'rho',
- 'u2',
- 'sigmaf',
- 'u3',
- 'sigma',
- 'u4',
- 'tau',
- 'u5',
- 'upsilon',
- 'u6',
- 'phi',
- 'u7',
- 'chi',
- 'u8',
- 'psi',
- 'u9',
- 'omega',
- 'uh',
- 'thetasym',
- 'ui',
- 'upsih',
- 'um',
- 'piv',
- '812',
- 'bull',
- '816',
- 'hellip',
- '81i',
- 'prime',
- '81j',
- 'Prime',
- '81u',
- 'oline',
- '824',
- 'frasl',
- '88o',
- 'weierp',
- '88h',
- 'image',
- '88s',
- 'real',
- '892',
- 'trade',
- '89l',
- 'alefsym',
- '8cg',
- 'larr',
- '8ch',
- 'uarr',
- '8ci',
- 'rarr',
- '8cj',
- 'darr',
- '8ck',
- 'harr',
- '8dl',
- 'crarr',
- '8eg',
- 'lArr',
- '8eh',
- 'uArr',
- '8ei',
- 'rArr',
- '8ej',
- 'dArr',
- '8ek',
- 'hArr',
- '8g0',
- 'forall',
- '8g2',
- 'part',
- '8g3',
- 'exist',
- '8g5',
- 'empty',
- '8g7',
- 'nabla',
- '8g8',
- 'isin',
- '8g9',
- 'notin',
- '8gb',
- 'ni',
- '8gf',
- 'prod',
- '8gh',
- 'sum',
- '8gi',
- 'minus',
- '8gn',
- 'lowast',
- '8gq',
- 'radic',
- '8gt',
- 'prop',
- '8gu',
- 'infin',
- '8h0',
- 'ang',
- '8h7',
- 'and',
- '8h8',
- 'or',
- '8h9',
- 'cap',
- '8ha',
- 'cup',
- '8hb',
- 'int',
- '8hk',
- 'there4',
- '8hs',
- 'sim',
- '8i5',
- 'cong',
- '8i8',
- 'asymp',
- '8j0',
- 'ne',
- '8j1',
- 'equiv',
- '8j4',
- 'le',
- '8j5',
- 'ge',
- '8k2',
- 'sub',
- '8k3',
- 'sup',
- '8k4',
- 'nsub',
- '8k6',
- 'sube',
- '8k7',
- 'supe',
- '8kl',
- 'oplus',
- '8kn',
- 'otimes',
- '8l5',
- 'perp',
- '8m5',
- 'sdot',
- '8o8',
- 'lceil',
- '8o9',
- 'rceil',
- '8oa',
- 'lfloor',
- '8ob',
- 'rfloor',
- '8p9',
- 'lang',
- '8pa',
- 'rang',
- '9ea',
- 'loz',
- '9j0',
- 'spades',
- '9j3',
- 'clubs',
- '9j5',
- 'hearts',
- '9j6',
- 'diams',
- 'ai',
- 'OElig',
- 'aj',
- 'oelig',
- 'b0',
- 'Scaron',
- 'b1',
- 'scaron',
- 'bo',
- 'Yuml',
- 'm6',
- 'circ',
- 'ms',
- 'tilde',
- '802',
- 'ensp',
- '803',
- 'emsp',
- '809',
- 'thinsp',
- '80c',
- 'zwnj',
- '80d',
- 'zwj',
- '80e',
- 'lrm',
- '80f',
- 'rlm',
- '80j',
- 'ndash',
- '80k',
- 'mdash',
- '80o',
- 'lsquo',
- '80p',
- 'rsquo',
- '80q',
- 'sbquo',
- '80s',
- 'ldquo',
- '80t',
- 'rdquo',
- '80u',
- 'bdquo',
- '810',
- 'dagger',
- '811',
- 'Dagger',
- '81g',
- 'permil',
- '81p',
- 'lsaquo',
- '81q',
- 'rsaquo',
- '85c',
- 'euro'
- ],
-
-
- /**
- * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
- *
- * @method encodeRaw
- * @param {String} text Text to encode.
- * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
- * @return {String} Entity encoded text.
- */
- encodeRaw: function(text, attr)
- {
- var t = this;
- return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
- return t.baseEntities[chr] || chr;
- });
- },
- /**
- * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
- * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
- * and is exposed as the DOMUtils.encode function.
- *
- * @method encodeAllRaw
- * @param {String} text Text to encode.
- * @return {String} Entity encoded text.
- */
- encodeAllRaw: function(text) {
- var t = this;
- return ('' + text).replace(this.rawCharsRegExp, function(chr) {
- return t.baseEntities[chr] || chr;
- });
- },
- /**
- * Encodes the specified string using numeric entities. The core entities will be
- * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
- *
- * @method encodeNumeric
- * @param {String} text Text to encode.
- * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
- * @return {String} Entity encoded text.
- */
- encodeNumeric: function(text, attr) {
- var t = this;
- return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
- // Multi byte sequence convert it to a single entity
- if (chr.length > 1) {
- return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
- }
- return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
- });
- },
- /**
- * Encodes the specified string using named entities. The core entities will be encoded
- * as named ones but all non lower ascii characters will be encoded into named entities.
- *
- * @method encodeNamed
- * @param {String} text Text to encode.
- * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
- * @param {Object} entities Optional parameter with entities to use.
- * @return {String} Entity encoded text.
- */
- encodeNamed: function(text, attr, entities) {
- var t = this;
- entities = entities || this.namedEntities;
- return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
- return t.baseEntities[chr] || entities[chr] || chr;
- });
- },
- /**
- * Returns an encode function based on the name(s) and it's optional entities.
- *
- * @method getEncodeFunc
- * @param {String} name Comma separated list of encoders for example named,numeric.
- * @param {String} entities Optional parameter with entities to use instead of the built in set.
- * @return {function} Encode function to be used.
- */
- getEncodeFunc: function(name, entities) {
- entities = this.buildEntitiesLookup(entities) || this.namedEntities;
- var t = this;
- function encodeNamedAndNumeric(text, attr) {
- return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
- return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
- });
- }
-
- function encodeCustomNamed(text, attr) {
- return t.encodeNamed(text, attr, entities);
- }
- // Replace + with , to be compatible with previous TinyMCE versions
- name = this.makeMap(name.replace(/\+/g, ','));
- // Named and numeric encoder
- if (name.named && name.numeric) {
- return this.encodeNamedAndNumeric;
- }
- // Named encoder
- if (name.named) {
- // Custom names
- if (entities) {
- return encodeCustomNamed;
- }
- return this.encodeNamed;
- }
- // Numeric
- if (name.numeric) {
- return this.encodeNumeric;
- }
- // Raw encoder
- return this.encodeRaw;
- },
- /**
- * Decodes the specified string, this will replace entities with raw UTF characters.
- *
- * @method decode
- * @param {String} text Text to entity decode.
- * @return {String} Entity decoded string.
- */
- decode: function(text)
- {
- var t = this;
- return text.replace(this.entityRegExp, function(all, numeric) {
- if (numeric) {
- numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
- // Support upper UTF
- if (numeric > 65535) {
- numeric -= 65536;
- return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
- }
- return t.asciiMap[numeric] || String.fromCharCode(numeric);
- }
- return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
- });
- },
- nativeDecode : function (text) {
- return text;
- },
- makeMap : function (items, delim, map) {
- var i;
- items = items || [];
- delim = delim || ',';
- if (typeof items == "string") {
- items = items.split(delim);
- }
- map = map || {};
- i = items.length;
- while (i--) {
- map[items[i]] = {};
- }
- return map;
- }
-};
-
-
-
-Roo.htmleditor.TidyEntities.init();
-/**
- * @class Roo.htmleditor.KeyEnter
- * Handle Enter press..
- * @cfg {Roo.HtmlEditorCore} core the editor.
- * @constructor
- * Create a new Filter.
- * @param {Object} config Configuration options
- */
-
-
-
-
-
-Roo.htmleditor.KeyEnter = function(cfg) {
- Roo.apply(this, cfg);
- // this does not actually call walk as it's really just a abstract class
-
- Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
-}
-
-//Roo.htmleditor.KeyEnter.i = 0;
-
-
-Roo.htmleditor.KeyEnter.prototype = {
-
- core : false,
-
- keypress : function(e)
- {
- if (e.charCode != 13 && e.charCode != 10) {
- Roo.log([e.charCode,e]);
- return true;
- }
- e.preventDefault();
- // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
- var doc = this.core.doc;
- //add a new line
-
-
- var sel = this.core.getSelection();
- var range = sel.getRangeAt(0);
- var n = range.commonAncestorContainer;
- var pc = range.closest([ 'ol', 'ul']);
- var pli = range.closest('li');
- if (!pc || e.ctrlKey) {
- sel.insertNode('br', 'after');
-
- this.core.undoManager.addEvent();
- this.core.fireEditorEvent(e);
- return false;
- }
-
- // deal with <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">