Annotation of kupuMPIWG/common/kupucontentfilters.js, revision 1.1
1.1 ! dwinter 1: /*****************************************************************************
! 2: *
! 3: * Copyright (c) 2003-2005 Kupu Contributors. All rights reserved.
! 4: *
! 5: * This software is distributed under the terms of the Kupu
! 6: * License. See LICENSE.txt for license text. For a list of Kupu
! 7: * Contributors see CREDITS.txt.
! 8: *
! 9: *****************************************************************************/
! 10:
! 11: // $Id: kupucontentfilters.js 12162 2005-05-10 15:53:10Z guido $
! 12:
! 13:
! 14: //----------------------------------------------------------------------------
! 15: //
! 16: // ContentFilters
! 17: //
! 18: // These are (or currently 'this is') filters for HTML cleanup and
! 19: // conversion. Kupu filters should be classes that should get registered to
! 20: // the editor using the registerFilter method with 2 methods: 'initialize'
! 21: // and 'filter'. The first will be called with the editor as its only
! 22: // argument and the latter with a reference to the ownerdoc (always use
! 23: // that to create new nodes and such) and the root node of the HTML DOM as
! 24: // its arguments.
! 25: //
! 26: //----------------------------------------------------------------------------
! 27:
! 28: function NonXHTMLTagFilter() {
! 29: /* filter out non-XHTML tags*/
! 30:
! 31: // A mapping from element name to whether it should be left out of the
! 32: // document entirely. If you want an element to reappear in the resulting
! 33: // document *including* it's contents, add it to the mapping with a 1 value.
! 34: // If you want an element not to appear but want to leave it's contents in
! 35: // tact, add it to the mapping with a 0 value. If you want an element and
! 36: // it's contents to be removed from the document, don't add it.
! 37: if (arguments.length) {
! 38: // allow an optional filterdata argument
! 39: this.filterdata = arguments[0];
! 40: } else {
! 41: // provide a default filterdata dict
! 42: this.filterdata = {'html': 1,
! 43: 'body': 1,
! 44: 'head': 1,
! 45: 'title': 1,
! 46:
! 47: 'a': 1,
! 48: 'abbr': 1,
! 49: 'acronym': 1,
! 50: 'address': 1,
! 51: 'b': 1,
! 52: 'base': 1,
! 53: 'blockquote': 1,
! 54: 'br': 1,
! 55: 'caption': 1,
! 56: 'cite': 1,
! 57: 'code': 1,
! 58: 'col': 1,
! 59: 'colgroup': 1,
! 60: 'dd': 1,
! 61: 'dfn': 1,
! 62: 'div': 1,
! 63: 'dl': 1,
! 64: 'dt': 1,
! 65: 'em': 1,
! 66: 'h1': 1,
! 67: 'h2': 1,
! 68: 'h3': 1,
! 69: 'h4': 1,
! 70: 'h5': 1,
! 71: 'h6': 1,
! 72: 'h7': 1,
! 73: 'i': 1,
! 74: 'img': 1,
! 75: 'kbd': 1,
! 76: 'li': 1,
! 77: 'link': 1,
! 78: 'meta': 1,
! 79: 'ol': 1,
! 80: 'p': 1,
! 81: 'pre': 1,
! 82: 'q': 1,
! 83: 'samp': 1,
! 84: 'script': 1,
! 85: 'span': 1,
! 86: 'strong': 1,
! 87: 'style': 1,
! 88: 'sub': 1,
! 89: 'sup': 1,
! 90: 'table': 1,
! 91: 'tbody': 1,
! 92: 'td': 1,
! 93: 'tfoot': 1,
! 94: 'th': 1,
! 95: 'thead': 1,
! 96: 'tr': 1,
! 97: 'ul': 1,
! 98: 'u': 1,
! 99: 'var': 1,
! 100:
! 101: // even though they're deprecated we should leave
! 102: // font tags as they are, since Kupu sometimes
! 103: // produces them itself.
! 104: 'font': 1,
! 105: 'center': 0
! 106: };
! 107: };
! 108:
! 109: this.initialize = function(editor) {
! 110: /* init */
! 111: this.editor = editor;
! 112: };
! 113:
! 114: this.filter = function(ownerdoc, htmlnode) {
! 115: return this._filterHelper(ownerdoc, htmlnode);
! 116: };
! 117:
! 118: this._filterHelper = function(ownerdoc, node) {
! 119: /* filter unwanted elements */
! 120: if (node.nodeType == 3) {
! 121: return ownerdoc.createTextNode(node.nodeValue);
! 122: } else if (node.nodeType == 4) {
! 123: return ownerdoc.createCDATASection(node.nodeValue);
! 124: };
! 125: // create a new node to place the result into
! 126: // XXX this can be severely optimized by doing stuff inline rather
! 127: // than on creating new elements all the time!
! 128: var newnode = ownerdoc.createElement(node.nodeName);
! 129: // copy the attributes
! 130: for (var i=0; i < node.attributes.length; i++) {
! 131: var attr = node.attributes[i];
! 132: newnode.setAttribute(attr.nodeName, attr.nodeValue);
! 133: };
! 134: for (var i=0; i < node.childNodes.length; i++) {
! 135: var child = node.childNodes[i];
! 136: var nodeType = child.nodeType;
! 137: var nodeName = child.nodeName.toLowerCase();
! 138: if (nodeType == 3 || nodeType == 4) {
! 139: newnode.appendChild(this._filterHelper(ownerdoc, child));
! 140: };
! 141: if (nodeName in this.filterdata && this.filterdata[nodeName]) {
! 142: newnode.appendChild(this._filterHelper(ownerdoc, child));
! 143: } else if (nodeName in this.filterdata) {
! 144: for (var j=0; j < child.childNodes.length; j++) {
! 145: newnode.appendChild(this._filterHelper(ownerdoc, child.childNodes[j]));
! 146: };
! 147: };
! 148: };
! 149: return newnode;
! 150: };
! 151: };
! 152:
! 153: //-----------------------------------------------------------------------------
! 154: //
! 155: // XHTML validation support
! 156: //
! 157: // This class is the XHTML 1.0 transitional DTD expressed as Javascript
! 158: // data structures.
! 159: //
! 160: function XhtmlValidation(editor) {
! 161: // Support functions
! 162: this.Set = function(ary) {
! 163: if (typeof(ary)==typeof('')) ary = [ary];
! 164: if (ary instanceof Array) {
! 165: for (var i = 0; i < ary.length; i++) {
! 166: this[ary[i]] = 1;
! 167: }
! 168: }
! 169: else {
! 170: for (var v in ary) { // already a set?
! 171: this[v] = 1;
! 172: }
! 173: }
! 174: }
! 175:
! 176: this._exclude = function(array, exceptions) {
! 177: var ex;
! 178: if (exceptions.split) {
! 179: ex = exceptions.split("|");
! 180: } else {
! 181: ex = exceptions;
! 182: }
! 183: var exclude = new this.Set(ex);
! 184: var res = [];
! 185: for (var k=0; k < array.length;k++) {
! 186: if (!exclude[array[k]]) res.push(array[k]);
! 187: }
! 188: return res;
! 189: }
! 190: this.setAttrFilter = function(attributes, filter) {
! 191: for (var j = 0; j < attributes.length; j++) {
! 192: var attr = attributes[j];
! 193: this.attrFilters[attr] = filter || this._defaultCopyAttribute;
! 194: }
! 195: }
! 196:
! 197: this.setTagAttributes = function(tags, attributes) {
! 198: for (var j = 0; j < tags.length; j++) {
! 199: this.tagAttributes[tags[j]] = attributes;
! 200: }
! 201: }
! 202:
! 203: // define some new attributes for existing tags
! 204: this.includeTagAttributes = function(tags, attributes) {
! 205: for (var j = 0; j < tags.length; j++) {
! 206: var tag = tags[j];
! 207: this.tagAttributes[tag] = this.tagAttributes[tag].concat(attributes);
! 208: }
! 209: }
! 210:
! 211: this.excludeTagAttributes = function(tags, attributes) {
! 212: var bad = new this.Set(attributes);
! 213: var tagset = new this.Set(tags);
! 214: for (var tag in tagset) {
! 215: var val = this.tagAttributes[tag];
! 216: for (var i = val.length; i >= 0; i--) {
! 217: if (bad[val[i]]) {
! 218: val = val.concat(); // Copy
! 219: val.splice(i,1);
! 220: }
! 221: }
! 222: this.tagAttributes[tag] = val;
! 223: }
! 224: }
! 225:
! 226: this.excludeTags = function(badtags) {
! 227: if (typeof(badtags)==typeof('')) badtags = [badtags];
! 228: for (var i = 0; i < badtags.length; i++) {
! 229: delete this.tagAttributes[badtags[i]];
! 230: }
! 231: }
! 232:
! 233: this.excludeAttributes = function(badattrs) {
! 234: this.excludeTagAttributes(this.tagAttributes, badattrs);
! 235: for (var i = 0; i < badattrs.length; i++) {
! 236: delete this.attrFilters[badattrs[i]];
! 237: }
! 238: }
! 239: if (editor.getBrowserName()=="IE") {
! 240: this._getTagName = function(htmlnode) {
! 241: var nodename = htmlnode.nodeName.toLowerCase();
! 242: if (htmlnode.scopeName && htmlnode.scopeName != "HTML") {
! 243: nodename = htmlnode.scopeName+':'+nodename;
! 244: }
! 245: return nodename;
! 246: }
! 247: } else {
! 248: this._getTagName = function(htmlnode) {
! 249: return htmlnode.nodeName.toLowerCase();
! 250: }
! 251: };
! 252:
! 253: // Supporting declarations
! 254: this.elements = new function(validation) {
! 255: // A list of all attributes
! 256: this.attributes = [
! 257: 'abbr','accept','accept-charset','accesskey','action','align','alink',
! 258: 'alt','archive','axis','background','bgcolor','border','cellpadding',
! 259: 'cellspacing','char','charoff','charset','checked','cite','class',
! 260: 'classid','clear','code','codebase','codetype','color','cols','colspan',
! 261: 'compact','content','coords','data','datetime','declare','defer','dir',
! 262: 'disabled','enctype','face','for','frame','frameborder','headers',
! 263: 'height','href','hreflang','hspace','http-equiv','id','ismap','label',
! 264: 'lang','language','link','longdesc','marginheight','marginwidth',
! 265: 'maxlength','media','method','multiple','name','nohref','noshade','nowrap',
! 266: 'object','onblur','onchange','onclick','ondblclick','onfocus','onkeydown',
! 267: 'onkeypress','onkeyup','onload','onmousedown','onmousemove','onmouseout',
! 268: 'onmouseover','onmouseup','onreset','onselect','onsubmit','onunload',
! 269: 'profile','prompt','readonly','rel','rev','rows','rowspan','rules',
! 270: 'scheme','scope','scrolling','selected','shape','size','span','src',
! 271: 'standby','start','style','summary','tabindex','target','text','title',
! 272: 'type','usemap','valign','value','valuetype','vlink','vspace','width',
! 273: 'xml:lang','xml:space','xmlns'];
! 274:
! 275: // Core attributes
! 276: this.coreattrs = ['id', 'title', 'style', 'class'];
! 277: this.i18n = ['lang', 'dir', 'xml:lang'];
! 278: // All event attributes are here but commented out so we don't
! 279: // have to remove them later.
! 280: this.events = []; // 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup'.split('|');
! 281: this.focusevents = []; // ['onfocus','onblur']
! 282: this.loadevents = []; // ['onload', 'onunload']
! 283: this.formevents = []; // ['onsubmit','onreset']
! 284: this.inputevents = [] ; // ['onselect', 'onchange']
! 285: this.focus = ['accesskey', 'tabindex'].concat(this.focusevents);
! 286: this.attrs = [].concat(this.coreattrs, this.i18n, this.events);
! 287:
! 288: // entities
! 289: this.special_extra = ['object','applet','img','map','iframe'];
! 290: this.special_basic=['br','span','bdo'];
! 291: this.special = [].concat(this.special_basic, this.special_extra);
! 292: this.fontstyle_extra = ['big','small','font','basefont'];
! 293: this.fontstyle_basic = ['tt','i','b','u','s','strike'];
! 294: this.fontstyle = [].concat(this.fontstyle_basic, this.fontstyle_extra);
! 295: this.phrase_extra = ['sub','sup'];
! 296: this.phrase_basic=[
! 297: 'em','strong','dfn','code','q',
! 298: 'samp','kbd','var', 'cite','abbr','acronym'];
! 299: this.inline_forms = ['input','select','textarea','label','button'];
! 300: this.misc_inline = ['ins','del'];
! 301: this.misc = ['noscript'].concat(this.misc_inline);
! 302: this.inline = ['a'].concat(this.special, this.fontstyle, this.phrase, this.inline_forms);
! 303:
! 304: this.Inline = ['#PCDATA'].concat(this.inline, this.misc_inline);
! 305:
! 306: this.heading = ['h1','h2','h3','h4','h5','h6'];
! 307: this.lists = ['ul','ol','dl','menu','dir'];
! 308: this.blocktext = ['pre','hr','blockquote','address','center','noframes'];
! 309: this.block = ['p','div','isindex','fieldset','table'].concat(
! 310: this.heading, this.lists, this.blocktext);
! 311:
! 312: this.Flow = ['#PCDATA','form'].concat(this.block, this.inline);
! 313: }(this);
! 314:
! 315: this._commonsetting = function(self, names, value) {
! 316: for (var n = 0; n < names.length; n++) {
! 317: self[names[n]] = value;
! 318: }
! 319: }
! 320:
! 321: // The tagAttributes class returns all valid attributes for a tag,
! 322: // e.g. a = this.tagAttributes.head
! 323: // a.head -> [ 'lang', 'xml:lang', 'dir', 'id', 'profile' ]
! 324: this.tagAttributes = new function(el, validation) {
! 325: this.title = el.i18n.concat('id');
! 326: this.html = this.title.concat('xmlns');
! 327: this.head = this.title.concat('profile');
! 328: this.base = ['id', 'href', 'target'];
! 329: this.meta = this.title.concat('http-equiv','name','content', 'scheme');
! 330: this.link = el.attrs.concat('charset','href','hreflang','type', 'rel','rev','media','target');
! 331: this.style = this.title.concat('type','media','title', 'xml:space');
! 332: this.script = ['id','charset','type','language','src','defer', 'xml:space'];
! 333: this.iframe = [
! 334: 'longdesc','name','src','frameborder','marginwidth',
! 335: 'marginheight','scrolling','align','height','width'].concat(el.coreattrs);
! 336: this.body = ['background','bgcolor','text','link','vlink','alink'].concat(el.attrs, el.loadevents);
! 337: validation._commonsetting(this,
! 338: ['p','div'].concat(el.heading),
! 339: ['align'].concat(el.attrs));
! 340: this.dl = this.dir = this.menu = el.attrs.concat('compact');
! 341: this.ul = this.menu.concat('type');
! 342: this.ol = this.ul.concat('start');
! 343: this.li = el.attrs.concat('type','value');
! 344: this.hr = el.attrs.concat('align','noshade','size','width');
! 345: this.pre = el.attrs.concat('width','xml:space');
! 346: this.blockquote = this.q = el.attrs.concat('cite');
! 347: this.ins = this.del = this.blockquote.concat('datetime');
! 348: this.a = el.attrs.concat(el.focus,'charset','type','name','href','hreflang','rel','rev','shape','coords','target');
! 349: this.bdo = el.coreattrs.concat(el.events, 'lang','xml:lang','dir');
! 350: this.br = el.coreattrs.concat('clear');
! 351: validation._commonsetting(this,
! 352: ['noscript','noframes','dt', 'dd', 'address','center','span','em', 'strong', 'dfn','code',
! 353: 'samp','kbd','var','cite','abbr','acronym','sub','sup','tt',
! 354: 'i','b','big','small','u','s','strike', 'fieldset'],
! 355: el.attrs);
! 356:
! 357: this.basefont = ['id','size','color','face'];
! 358: this.font = el.coreattrs.concat(el.i18n, 'size','color','face');
! 359: this.object = el.attrs.concat('declare','classid','codebase','data','type','codetype','archive','standby','height','width','usemap','name','tabindex','align','border','hspace','vspace');
! 360: this.param = ['id','name','value','valuetype','type'];
! 361: this.applet = el.coreattrs.concat('codebase','archive','code','object','alt','name','width','height','align','hspace','vspace');
! 362: this.img = el.attrs.concat('src','alt','name','longdesc','height','width','usemap','ismap','align','border','hspace','vspace');
! 363: this.map = this.title.concat('title','name', 'style', 'class', el.events);
! 364: this.area = el.attrs.concat('shape','coords','href','nohref','alt','target', el.focus);
! 365: this.form = el.attrs.concat('action','method','name','enctype',el.formevents,'accept','accept-charset','target');
! 366: this.label = el.attrs.concat('for','accesskey', el.focusevents);
! 367: this.input = el.attrs.concat('type','name','value','checked','disabled','readonly','size','maxlength','src','alt','usemap',el.input,'accept','align', el.focus);
! 368: this.select = el.attrs.concat('name','size','multiple','disabled','tabindex', el.focusevents,el.input);
! 369: this.optgroup = el.attrs.concat('disabled','label');
! 370: this.option = el.attrs.concat('selected','disabled','label','value');
! 371: this.textarea = el.attrs.concat('name','rows','cols','disabled','readonly', el.inputevents, el.focus);
! 372: this.legend = el.attrs.concat('accesskey','align');
! 373: this.button = el.attrs.concat('name','value','type','disabled',el.focus);
! 374: this.isindex = el.coreattrs.concat('prompt', el.i18n);
! 375: this.table = el.attrs.concat('summary','width','border','frame','rules','cellspacing','cellpadding','align','bgcolor');
! 376: this.caption = el.attrs.concat('align');
! 377: this.col = this.colgroup = el.attrs.concat('span','width','align','char','charoff','valign');
! 378: this.thead = el.attrs.concat('align','char','charoff','valign');
! 379: this.tfoot = this.tbody = this.thead;
! 380: this.tr = this.thead.concat('bgcolor');
! 381: this.td = this.th = this.tr.concat('abbr','axis','headers','scope','rowspan','colspan','nowrap','width','height');
! 382: }(this.elements, this);
! 383:
! 384: // State array. For each tag identifies what it can contain.
! 385: // I'm not attempting to check the order or number of contained
! 386: // tags (yet).
! 387: this.States = new function(el, validation) {
! 388:
! 389: var here = this;
! 390: function setStates(tags, value) {
! 391: var valset = new validation.Set(value);
! 392:
! 393: for (var i = 0; i < tags.length; i++) {
! 394: here[tags[i]] = valset;
! 395: }
! 396: }
! 397:
! 398: setStates(['html'], ['head','body']);
! 399: setStates(['head'], ['title','base','script','style', 'meta','link','object','isindex']);
! 400: setStates([
! 401: 'base', 'meta', 'link', 'hr', 'param', 'img', 'area', 'input',
! 402: 'br', 'basefont', 'isindex', 'col',
! 403: ], []);
! 404:
! 405: setStates(['title','style','script','option','textarea'], ['#PCDATA']);
! 406: setStates([ 'noscript', 'iframe', 'noframes', 'body', 'div',
! 407: 'li', 'dd', 'blockquote', 'center', 'ins', 'del', 'td', 'th',
! 408: ], el.Flow);
! 409:
! 410: setStates(el.heading, el.Inline);
! 411: setStates([ 'p', 'dt', 'address', 'span', 'bdo', 'caption',
! 412: 'em', 'strong', 'dfn','code','samp','kbd','var',
! 413: 'cite','abbr','acronym','q','sub','sup','tt','i',
! 414: 'b','big','small','u','s','strike','font','label',
! 415: 'legend'], el.Inline);
! 416:
! 417: setStates(['ul', 'ol', 'menu', 'dir', 'ul', ], ['li']);
! 418: setStates(['dl'], ['dt','dd']);
! 419: setStates(['pre'], validation._exclude(el.Inline, "img|object|applet|big|small|sub|sup|font|basefont"));
! 420: setStates(['a'], validation._exclude(el.Inline, "a"));
! 421: setStates(['applet', 'object'], ['#PCDATA', 'param','form'].concat(el.block, el.inline, el.misc));
! 422: setStates(['map'], ['form', 'area'].concat(el.block, el.misc));
! 423: setStates(['form'], validation._exclude(el.Flow, ['form']));
! 424: setStates(['select'], ['optgroup','option']);
! 425: setStates(['optgroup'], ['option']);
! 426: setStates(['fieldset'], ['#PCDATA','legend','form'].concat(el.block,el.inline,el.misc));
! 427: setStates(['button'], validation._exclude(el.Flow, ['a','form','iframe'].concat(el.inline_forms)));
! 428: setStates(['table'], ['caption','col','colgroup','thead','tfoot','tbody','tr']);
! 429: setStates(['thead', 'tfoot', 'tbody'], ['tr']);
! 430: setStates(['colgroup'], ['col']);
! 431: setStates(['tr'], ['th','td']);
! 432: }(this.elements, this);
! 433:
! 434: // Permitted elements for style.
! 435: this.styleWhitelist = new this.Set(['text-align', 'list-style-type', 'float']);
! 436: this.classBlacklist = new this.Set(['MsoNormal', 'MsoTitle', 'MsoHeader', 'MsoFootnoteText',
! 437: 'Bullet1', 'Bullet2']);
! 438:
! 439: this.classFilter = function(value) {
! 440: var classes = value.split(' ');
! 441: var filtered = [];
! 442: for (var i = 0; i < classes.length; i++) {
! 443: var c = classes[i];
! 444: if (c && !this.classBlacklist[c]) {
! 445: filtered.push(c);
! 446: }
! 447: }
! 448: return filtered.join(' ');
! 449: }
! 450: this._defaultCopyAttribute = function(name, htmlnode, xhtmlnode) {
! 451: var val = htmlnode.getAttribute(name);
! 452: if (val) xhtmlnode.setAttribute(name, val);
! 453: }
! 454: // Set up filters for attributes.
! 455: this.attrFilters = new function(validation, editor) {
! 456: var attrs = validation.elements.attributes;
! 457: for (var i=0; i < attrs.length; i++) {
! 458: this[attrs[i]] = validation._defaultCopyAttribute;
! 459: }
! 460: this['class'] = function(name, htmlnode, xhtmlnode) {
! 461: var val = htmlnode.getAttribute('class');
! 462: if (val) val = validation.classFilter(val);
! 463: if (val) xhtmlnode.setAttribute('class', val);
! 464: }
! 465: // allow a * wildcard to make all attributes valid in the filter
! 466: // note that this is pretty slow on IE
! 467: this['*'] = function(name, htmlnode, xhtmlnode) {
! 468: for (var i=0; i < htmlnode.attributes.length; i++) {
! 469: var attr = htmlnode.attributes[i];
! 470: if (attr.value !== null && attr.value !== undefined) {
! 471: xhtmlnode.setAttribute(attr.name, attr.value);
! 472: };
! 473: };
! 474: };
! 475: if (editor.getBrowserName()=="IE") {
! 476: this['class'] = function(name, htmlnode, xhtmlnode) {
! 477: var val = htmlnode.className;
! 478: if (val) val = validation.classFilter(val);
! 479: if (val) xhtmlnode.setAttribute('class', val);
! 480: }
! 481: this['http-equiv'] = function(name, htmlnode, xhtmlnode) {
! 482: var val = htmlnode.httpEquiv;
! 483: if (val) xhtmlnode.setAttribute('http-equiv', val);
! 484: }
! 485: this['xml:lang'] = this['xml:space'] = function(name, htmlnode, xhtmlnode) {
! 486: try {
! 487: var val = htmlnode.getAttribute(name);
! 488: if (val) xhtmlnode.setAttribute(name, val);
! 489: } catch(e) {
! 490: }
! 491: }
! 492: }
! 493: this.rowspan = this.colspan = function(name, htmlnode, xhtmlnode) {
! 494: var val = htmlnode.getAttribute(name);
! 495: if (val && val != '1') xhtmlnode.setAttribute(name, val);
! 496: }
! 497: this.style = function(name, htmlnode, xhtmlnode) {
! 498: var val = htmlnode.style.cssText;
! 499: if (val) {
! 500: var styles = val.split(/; */);
! 501: for (var i = styles.length; i >= 0; i--) if (styles[i]) {
! 502: var parts = /^([^:]+): *(.*)$/.exec(styles[i]);
! 503: var name = parts[1].toLowerCase();
! 504: if (validation.styleWhitelist[name]) {
! 505: styles[i] = name+': '+parts[2];
! 506: } else {
! 507: styles.splice(i,1); // delete
! 508: }
! 509: }
! 510: if (styles[styles.length-1]) styles.push('');
! 511: val = styles.join('; ').strip();
! 512: }
! 513: if (val) xhtmlnode.setAttribute('style', val);
! 514: }
! 515: }(this, editor);
! 516:
! 517: // Exclude unwanted tags.
! 518: this.excludeTags(['center']);
! 519:
! 520: if (editor.config && editor.config.htmlfilter) {
! 521: this.filterStructure = editor.config.htmlfilter.filterstructure;
! 522:
! 523: var exclude = editor.config.htmlfilter;
! 524: if (exclude.a)
! 525: this.excludeAttributes(exclude.a);
! 526: if (exclude.t)
! 527: this.excludeTags(exclude.t);
! 528: if (exclude.c) {
! 529: var c = exclude.c;
! 530: if (!c.length) c = [c];
! 531: for (var i = 0; i < c.length; i++) {
! 532: this.excludeTagAttributes(c[i].t, c[i].a);
! 533: }
! 534: }
! 535: if (exclude.style) {
! 536: var s = exclude.style;
! 537: for (var i = 0; i < s.length; i++) {
! 538: this.styleWhitelist[s[i]] = 1;
! 539: }
! 540: }
! 541: if (exclude['class']) {
! 542: var c = exclude['class'];
! 543: for (var i = 0; i < c.length; i++) {
! 544: this.classBlacklist[c[i]] = 1;
! 545: }
! 546: }
! 547: };
! 548:
! 549: // Copy all valid attributes from htmlnode to xhtmlnode.
! 550: this._copyAttributes = function(htmlnode, xhtmlnode, valid) {
! 551: if (valid.contains('*')) {
! 552: // allow all attributes on this tag
! 553: this.attrFilters['*'](name, htmlnode, xhtmlnode);
! 554: return;
! 555: };
! 556: for (var i = 0; i < valid.length; i++) {
! 557: var name = valid[i];
! 558: var filter = this.attrFilters[name];
! 559: if (filter) filter(name, htmlnode, xhtmlnode);
! 560: }
! 561: }
! 562:
! 563: this._convertToSarissaNode = function(ownerdoc, htmlnode, xhtmlparent) {
! 564: return this._convertNodes(ownerdoc, htmlnode, xhtmlparent, new this.Set(['html']));
! 565: };
! 566:
! 567: this._convertNodes = function(ownerdoc, htmlnode, xhtmlparent, permitted) {
! 568: var name, parentnode = xhtmlparent;
! 569: var nodename = this._getTagName(htmlnode);
! 570: var nostructure = !this.filterstructure;
! 571:
! 572: // TODO: This permits valid tags anywhere. it should use the state
! 573: // table in xhtmlvalid to only permit tags where the XHTML DTD
! 574: // says they are valid.
! 575: var validattrs = this.tagAttributes[nodename];
! 576: if (validattrs && (nostructure || permitted[nodename])) {
! 577: try {
! 578: var xhtmlnode = ownerdoc.createElement(nodename);
! 579: parentnode = xhtmlnode;
! 580: } catch (e) { };
! 581:
! 582: if (validattrs && xhtmlnode)
! 583: this._copyAttributes(htmlnode, xhtmlnode, validattrs);
! 584: }
! 585:
! 586: var kids = htmlnode.childNodes;
! 587: var permittedChildren = this.States[parentnode.tagName] || permitted;
! 588:
! 589: if (kids.length == 0) {
! 590: if (htmlnode.text && htmlnode.text != "" &&
! 591: (nostructure || permittedChildren['#PCDATA'])) {
! 592: var text = htmlnode.text;
! 593: var tnode = ownerdoc.createTextNode(text);
! 594: parentnode.appendChild(tnode);
! 595: }
! 596: } else {
! 597: for (var i = 0; i < kids.length; i++) {
! 598: var kid = kids[i];
! 599:
! 600: if (kid.parentNode !== htmlnode) {
! 601: if (kid.tagName == 'BODY') {
! 602: if (nodename != 'html') continue;
! 603: } else if (kid.parentNode.tagName === htmlnode.tagName) {
! 604: continue; // IE bug: nodes appear multiple places
! 605: }
! 606: }
! 607:
! 608: if (kid.nodeType == 1) {
! 609: var newkid = this._convertNodes(ownerdoc, kid, parentnode, permittedChildren);
! 610: if (newkid != null) {
! 611: parentnode.appendChild(newkid);
! 612: };
! 613: } else if (kid.nodeType == 3) {
! 614: if (nostructure || permittedChildren['#PCDATA'])
! 615: parentnode.appendChild(ownerdoc.createTextNode(kid.nodeValue));
! 616: } else if (kid.nodeType == 4) {
! 617: if (nostructure || permittedChildren['#PCDATA'])
! 618: parentnode.appendChild(ownerdoc.createCDATASection(kid.nodeValue));
! 619: }
! 620: }
! 621: }
! 622: return xhtmlnode;
! 623: };
! 624: }
! 625:
! 626:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>