Roo.HtmlEditorCore.superclass.constructor.call(this, config);
+
+
this.addEvents({
/**
* @event initialize
* @param {Roo.HtmlEditorCore} this
*/
editorevent: true
+
});
-
+
+ // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
+
+ // defaults : white / black...
+ this.applyBlacklists();
+
+
+
};
clearUp: true,
+ // blacklist + whitelisted elements..
+ black: false,
+ white: false,
-
+ bodyCls : '',
/**
* Protected method that will not generally be called directly. It
getDocMarkup : function(){
// body styles..
var st = '';
- Roo.log(this.stylesheets);
// inherit styels from page...??
if (this.stylesheets === false) {
st = '<style type="text/css">' +
'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
'</style>';
- } else {
- Roo.each(this.stylesheets, function(s) {
- st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
- });
-
+ } else {
+ st = '<style type="text/css">' +
+ this.stylesheets +
+ '</style>';
}
st += '<style type="text/css">' +
'IMG { cursor: pointer } ' +
'</style>';
+ var cls = 'roo-htmleditor-body';
+
+ if(this.bodyCls.length){
+ cls += ' ' + this.bodyCls;
+ }
return '<html><head>' + st +
//<style type="text/css">' +
//'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
//'</style>' +
- ' </head><body class="roo-htmleditor-body"></body></html>';
+ ' </head><body class="' + cls + '"></body></html>';
},
// private
};
Roo.TaskMgr.start(task);
-
-
},
// private
this.execCmd('FontSize', v );
},
- onEditorEvent : function(e){
+ onEditorEvent : function(e)
+ {
this.owner.fireEvent('editorevent', this, e);
// this.updateToolbar();
this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
insertAtCursor : function(text)
{
-
-
if(!this.activated){
return;
}
var nodeIsBefore = ss == 1;
var nodeIsAfter = ee == -1;
- if (nodeIsBefore && nodeIsAfter)
+ if (nodeIsBefore && nodeIsAfter) {
return 0; // outer
- if (!nodeIsBefore && nodeIsAfter)
+ }
+ if (!nodeIsBefore && nodeIsAfter) {
return 1; //right trailed.
+ }
- if (nodeIsBefore && !nodeIsAfter)
+ if (nodeIsBefore && !nodeIsAfter) {
return 2; // left trailed.
+ }
// fully contined.
return 3;
},
// clean up silly Windows -- stuff?
return;
}
+ var lcname = node.tagName.toLowerCase();
+ // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
+ // whitelist of tags..
- if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
+ if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
// remove node.
node.parentNode.removeChild(node);
return;
if (v.match(/^\./) || v.match(/^\//)) {
return;
}
- if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
+ if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
return;
}
if (v.match(/^#/)) {
}
+ var cwhite = this.cwhite;
+ var cblack = this.cblack;
+
function cleanStyle(n,v)
{
if (v.match(/expression/)) { //XSS?? should we even bother..
node.removeAttribute(n);
return;
}
- var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
- var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
-
var parts = v.split(/;/);
var clean = [];
var l = p.split(':').shift().replace(/\s+/g,'');
l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
- if ( cblack.indexOf(l) > -1) {
+ if ( cwhite.length && cblack.indexOf(l) > -1) {
// Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
//node.removeAttribute(n);
return true;
node.className = '';
}
- if (a.value.match(/body/)) {
+ if (a.value.match(/^body$/)) {
node.className = '';
}
continue;
},
+
/**
* Clean up MS wordisms...
*/
cleanWord : function(node)
{
- var _t = this;
- var cleanWordChildren = function()
- {
- if (!node.childNodes.length) {
- return;
- }
- for (var i = node.childNodes.length-1; i > -1 ; i--) {
- _t.cleanWord(node.childNodes[i]);
- }
- }
if (!node) {
node.parentNode.insertBefore(cn, node);
}
node.parentNode.removeChild(node);
- cleanWordChildren();
+ this.iterateChildren(node, this.cleanWord);
return;
}
// clean styles
node.removeAttribute('style');
}
}
+ this.iterateChildren(node, this.cleanWord);
- cleanWordChildren();
},
- domToHTML : function(currentElement, depth, nopadtext) {
+ /**
+ * iterateChildren of a Node, calling fn each time, using this as the scole..
+ * @param {DomNode} node node to iterate children of.
+ * @param {Function} fn method of this class to call on each item.
+ */
+ iterateChildren : function(node, fn)
+ {
+ if (!node.childNodes.length) {
+ return;
+ }
+ for (var i = node.childNodes.length-1; i > -1 ; i--) {
+ fn.call(this, node.childNodes[i])
+ }
+ },
+
+
+ /**
+ * 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..
+ *
+ */
+ cleanTableWidths : function(node)
+ {
+
+
+ if (!node) {
+ this.cleanTableWidths(this.doc.body);
+ return;
+ }
- depth = depth || 0;
- nopadtext = nopadtext || false;
+ // ignore list...
+ if (node.nodeName == "#text" || node.nodeName == "#comment") {
+ return;
+ }
+ Roo.log(node.tagName);
+ if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
+ this.iterateChildren(node, this.cleanTableWidths);
+ return;
+ }
+ if (node.hasAttribute('width')) {
+ node.removeAttribute('width');
+ }
- if (!currentElement) {
- return this.domToHTML(this.doc.body);
- }
-
- //Roo.log(currentElement);
- var j;
- var allText = false;
- var nodeName = currentElement.nodeName;
- var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
+
+ if (node.hasAttribute("style")) {
+ // pretty basic...
- if (nodeName == '#text') {
- return currentElement.nodeValue;
+ 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');
}
+ }
+
+ this.iterateChildren(node, this.cleanTableWidths);
+
+
+ },
+
+
+
+
+ domToHTML : function(currentElement, depth, nopadtext) {
+
+ depth = depth || 0;
+ nopadtext = nopadtext || false;
+
+ if (!currentElement) {
+ return this.domToHTML(this.doc.body);
+ }
+
+ //Roo.log(currentElement);
+ var j;
+ var allText = false;
+ var nodeName = currentElement.nodeName;
+ var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
+
+ if (nodeName == '#text') {
-
- var ret = '';
- if (nodeName != 'BODY') {
-
- var i = 0;
- // Prints the node tagName, such as <A>, <IMG>, etc
- if (tagName) {
- var attr = [];
- for(i = 0; i < currentElement.attributes.length;i++) {
- // quoting?
- var aname = currentElement.attributes.item(i).name;
- if (!currentElement.attributes.item(i).value.length) {
- continue;
- }
- attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
+ return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
+ }
+
+
+ var ret = '';
+ if (nodeName != 'BODY') {
+
+ var i = 0;
+ // Prints the node tagName, such as <A>, <IMG>, etc
+ if (tagName) {
+ var attr = [];
+ for(i = 0; i < currentElement.attributes.length;i++) {
+ // quoting?
+ var aname = currentElement.attributes.item(i).name;
+ if (!currentElement.attributes.item(i).value.length) {
+ continue;
}
-
- ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
- }
- else {
-
- // eack
+ attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
}
- } else {
- tagName = false;
- }
- if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
- return ret;
+
+ ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
+ }
+ else {
+
+ // eack
}
- if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
- nopadtext = true;
+ } else {
+ tagName = false;
+ }
+ if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
+ return ret;
+ }
+ if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
+ nopadtext = true;
+ }
+
+
+ // Traverse the tree
+ i = 0;
+ var currentElementChild = currentElement.childNodes.item(i);
+ var allText = true;
+ var innerHTML = '';
+ lastnode = '';
+ while (currentElementChild) {
+ // Formatting code (indent the tree so it looks nice on the screen)
+ var nopad = nopadtext;
+ if (lastnode == 'SPAN') {
+ nopad = true;
}
-
-
- // Traverse the tree
- i = 0;
- var currentElementChild = currentElement.childNodes.item(i);
- var allText = true;
- var innerHTML = '';
- lastnode = '';
- while (currentElementChild) {
- // Formatting code (indent the tree so it looks nice on the screen)
- var nopad = nopadtext;
- if (lastnode == 'SPAN') {
- nopad = true;
- }
- // text
- if (currentElementChild.nodeName == '#text') {
- var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
- if (!nopad && toadd.length > 80) {
- innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
- }
- innerHTML += toadd;
-
- i++;
- currentElementChild = currentElement.childNodes.item(i);
- lastNode = '';
- continue;
+ // text
+ if (currentElementChild.nodeName == '#text') {
+ var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
+ toadd = nopadtext ? toadd : toadd.trim();
+ if (!nopad && toadd.length > 80) {
+ innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
}
- allText = false;
+ innerHTML += toadd;
- innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
-
- // Recursively traverse the tree structure of the child node
- innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
- lastnode = currentElementChild.nodeName;
i++;
- currentElementChild=currentElement.childNodes.item(i);
+ currentElementChild = currentElement.childNodes.item(i);
+ lastNode = '';
+ continue;
}
+ allText = false;
- ret += innerHTML;
+ innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
+
+ // Recursively traverse the tree structure of the child node
+ innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
+ lastnode = currentElementChild.nodeName;
+ i++;
+ currentElementChild=currentElement.childNodes.item(i);
+ }
+
+ ret += innerHTML;
+
+ if (!allText) {
+ // The remaining code is mostly for formatting the tree
+ ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
+ }
+
+
+ if (tagName) {
+ ret+= "</"+tagName+">";
+ }
+ return ret;
+
+ },
+
+ applyBlacklists : function()
+ {
+ var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
+ var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
+
+ this.white = [];
+ this.black = [];
+ Roo.each(Roo.HtmlEditorCore.white, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
+ }
+ this.white.push(tag);
- if (!allText) {
- // The remaining code is mostly for formatting the tree
- ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
+ }, this);
+
+ Roo.each(w, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
+ }
+ if (this.white.indexOf(tag) > -1) {
+ return;
}
+ this.white.push(tag);
+ }, this);
+
+
+ Roo.each(Roo.HtmlEditorCore.black, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
+ }
+ this.black.push(tag);
- if (tagName) {
- ret+= "</"+tagName+">";
+ }, this);
+
+ Roo.each(b, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
}
- return ret;
+ if (this.black.indexOf(tag) > -1) {
+ return;
+ }
+ this.black.push(tag);
+
+ }, this);
+
+
+ w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
+ b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
+
+ this.cwhite = [];
+ this.cblack = [];
+ Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
+ }
+ this.cwhite.push(tag);
+
+ }, this);
+
+ Roo.each(w, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
+ }
+ if (this.cwhite.indexOf(tag) > -1) {
+ return;
+ }
+ this.cwhite.push(tag);
+
+ }, this);
+
+
+ Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
+ }
+ this.cblack.push(tag);
+
+ }, this);
+
+ Roo.each(b, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
+ }
+ if (this.cblack.indexOf(tag) > -1) {
+ return;
+ }
+ this.cblack.push(tag);
+ }, this);
+ },
+
+ setStylesheets : function(stylesheets)
+ {
+ if(typeof(stylesheets) == 'string'){
+ Roo.get(this.iframe.contentDocument.head).createChild({
+ tag : 'link',
+ rel : 'stylesheet',
+ type : 'text/css',
+ href : stylesheets
+ });
+
+ return;
}
+ var _this = this;
+
+ Roo.each(stylesheets, function(s) {
+ if(!s.length){
+ return;
+ }
+
+ Roo.get(_this.iframe.contentDocument.head).createChild({
+ tag : 'link',
+ rel : 'stylesheet',
+ type : 'text/css',
+ href : s
+ });
+ });
+
+
+ },
+
+ removeStylesheets : function()
+ {
+ var _this = this;
+
+ Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
+ s.remove();
+ });
+ },
+
+ setStyle : function(style)
+ {
+ Roo.get(this.iframe.contentDocument.head).createChild({
+ tag : 'style',
+ type : 'text/css',
+ html : style
+ });
+
+ return;
+ }
// hide stuff that is not compatible
/**