Annotation of kupu/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>