[ Index ] |
|
Code source de Kupu-1.3.5 |
1 /***************************************************************************** 2 * 3 * Copyright (c) 2003-2004 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 18097 2005-10-03 09:51:28Z duncan $ 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, 146 child.childNodes[j])); 147 }; 148 }; 149 }; 150 return newnode; 151 }; 152 }; 153 154 //----------------------------------------------------------------------------- 155 // 156 // XHTML validation support 157 // 158 // This class is the XHTML 1.0 transitional DTD expressed as Javascript 159 // data structures. 160 // 161 function XhtmlValidation(editor) { 162 // Support functions 163 this.Set = function(ary) { 164 if (typeof(ary)==typeof('')) ary = [ary]; 165 if (ary instanceof Array) { 166 for (var i = 0; i < ary.length; i++) { 167 this[ary[i]] = 1; 168 } 169 } 170 else { 171 for (var v in ary) { // already a set? 172 this[v] = 1; 173 } 174 } 175 } 176 177 this._exclude = function(array, exceptions) { 178 var ex; 179 if (exceptions.split) { 180 ex = exceptions.split("|"); 181 } else { 182 ex = exceptions; 183 } 184 var exclude = new this.Set(ex); 185 var res = []; 186 for (var k=0; k < array.length;k++) { 187 if (!exclude[array[k]]) res.push(array[k]); 188 } 189 return res; 190 } 191 this.setAttrFilter = function(attributes, filter) { 192 for (var j = 0; j < attributes.length; j++) { 193 var attr = attributes[j]; 194 this.attrFilters[attr] = filter || this._defaultCopyAttribute; 195 } 196 } 197 198 this.setTagAttributes = function(tags, attributes) { 199 for (var j = 0; j < tags.length; j++) { 200 this.tagAttributes[tags[j]] = attributes; 201 } 202 } 203 204 // define some new attributes for existing tags 205 this.includeTagAttributes = function(tags, attributes) { 206 for (var j = 0; j < tags.length; j++) { 207 var tag = tags[j]; 208 this.tagAttributes[tag] = this.tagAttributes[tag].concat(attributes); 209 } 210 } 211 212 this.excludeTagAttributes = function(tags, attributes) { 213 var bad = new this.Set(attributes); 214 var tagset = new this.Set(tags); 215 for (var tag in tagset) { 216 var val = this.tagAttributes[tag]; 217 for (var i = val.length; i >= 0; i--) { 218 if (bad[val[i]]) { 219 val = val.concat(); // Copy 220 val.splice(i,1); 221 } 222 } 223 this.tagAttributes[tag] = val; 224 // have to store this to allow filtering for nodes on which 225 // '*' is set as allowed, this allows using '*' for the attributes 226 // but also filtering some out 227 this.badTagAttributes[tag] = attributes; 228 } 229 } 230 231 this.excludeTags = function(badtags) { 232 if (typeof(badtags)==typeof('')) badtags = [badtags]; 233 for (var i = 0; i < badtags.length; i++) { 234 delete this.tagAttributes[badtags[i]]; 235 } 236 } 237 238 this.excludeAttributes = function(badattrs) { 239 this.excludeTagAttributes(this.tagAttributes, badattrs); 240 for (var i = 0; i < badattrs.length; i++) { 241 delete this.attrFilters[badattrs[i]]; 242 } 243 } 244 if (editor.getBrowserName()=="IE") { 245 this._getTagName = function(htmlnode) { 246 var nodename = htmlnode.nodeName.toLowerCase(); 247 if (htmlnode.scopeName && htmlnode.scopeName != "HTML") { 248 nodename = htmlnode.scopeName+':'+nodename; 249 } 250 return nodename; 251 } 252 } else { 253 this._getTagName = function(htmlnode) { 254 return htmlnode.nodeName.toLowerCase(); 255 } 256 }; 257 258 // Supporting declarations 259 this.elements = new function(validation) { 260 // A list of all attributes 261 this.attributes = [ 262 'abbr','accept','accept-charset','accesskey','action','align','alink', 263 'alt','archive','axis','background','bgcolor','border','cellpadding', 264 'cellspacing','char','charoff','charset','checked','cite','class', 265 'classid','clear','code','codebase','codetype','color','cols','colspan', 266 'compact','content','coords','data','datetime','declare','defer','dir', 267 'disabled','enctype','face','for','frame','frameborder','headers', 268 'height','href','hreflang','hspace','http-equiv','id','ismap','label', 269 'lang','language','link','longdesc','marginheight','marginwidth', 270 'maxlength','media','method','multiple','name','nohref','noshade','nowrap', 271 'object','onblur','onchange','onclick','ondblclick','onfocus','onkeydown', 272 'onkeypress','onkeyup','onload','onmousedown','onmousemove','onmouseout', 273 'onmouseover','onmouseup','onreset','onselect','onsubmit','onunload', 274 'profile','prompt','readonly','rel','rev','rows','rowspan','rules', 275 'scheme','scope','scrolling','selected','shape','size','span','src', 276 'standby','start','style','summary','tabindex','target','text','title', 277 'type','usemap','valign','value','valuetype','vlink','vspace','width', 278 'xml:lang','xml:space','xmlns']; 279 280 // Core attributes 281 this.coreattrs = ['id', 'title', 'style', 'class']; 282 this.i18n = ['lang', 'dir', 'xml:lang']; 283 // All event attributes are here but commented out so we don't 284 // have to remove them later. 285 this.events = []; // 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup'.split('|'); 286 this.focusevents = []; // ['onfocus','onblur'] 287 this.loadevents = []; // ['onload', 'onunload'] 288 this.formevents = []; // ['onsubmit','onreset'] 289 this.inputevents = [] ; // ['onselect', 'onchange'] 290 this.focus = ['accesskey', 'tabindex'].concat(this.focusevents); 291 this.attrs = [].concat(this.coreattrs, this.i18n, this.events); 292 293 // entities 294 this.special_extra = ['object','applet','img','map','iframe']; 295 this.special_basic=['br','span','bdo']; 296 this.special = [].concat(this.special_basic, this.special_extra); 297 this.fontstyle_extra = ['big','small','font','basefont']; 298 this.fontstyle_basic = ['tt','i','b','u','s','strike']; 299 this.fontstyle = [].concat(this.fontstyle_basic, this.fontstyle_extra); 300 this.phrase_extra = ['sub','sup']; 301 this.phrase_basic=[ 302 'em','strong','dfn','code','q', 303 'samp','kbd','var', 'cite','abbr','acronym']; 304 this.inline_forms = ['input','select','textarea','label','button']; 305 this.misc_inline = ['ins','del']; 306 this.misc = ['noscript'].concat(this.misc_inline); 307 this.inline = ['a'].concat(this.special, this.fontstyle, this.phrase, this.inline_forms); 308 309 this.Inline = ['#PCDATA'].concat(this.inline, this.misc_inline); 310 311 this.heading = ['h1','h2','h3','h4','h5','h6']; 312 this.lists = ['ul','ol','dl','menu','dir']; 313 this.blocktext = ['pre','hr','blockquote','address','center','noframes']; 314 this.block = ['p','div','isindex','fieldset','table'].concat( 315 this.heading, this.lists, this.blocktext); 316 317 this.Flow = ['#PCDATA','form'].concat(this.block, this.inline); 318 }(this); 319 320 this._commonsetting = function(self, names, value) { 321 for (var n = 0; n < names.length; n++) { 322 self[names[n]] = value; 323 } 324 } 325 326 // The tagAttributes class returns all valid attributes for a tag, 327 // e.g. a = this.tagAttributes.head 328 // a.head -> [ 'lang', 'xml:lang', 'dir', 'id', 'profile' ] 329 this.tagAttributes = new function(el, validation) { 330 this.title = el.i18n.concat('id'); 331 this.html = this.title.concat('xmlns'); 332 this.head = this.title.concat('profile'); 333 this.base = ['id', 'href', 'target']; 334 this.meta = this.title.concat('http-equiv','name','content', 'scheme'); 335 this.link = el.attrs.concat('charset','href','hreflang','type', 'rel','rev','media','target'); 336 this.style = this.title.concat('type','media','title', 'xml:space'); 337 this.script = ['id','charset','type','language','src','defer', 'xml:space']; 338 this.iframe = [ 339 'longdesc','name','src','frameborder','marginwidth', 340 'marginheight','scrolling','align','height','width'].concat(el.coreattrs); 341 this.body = ['background','bgcolor','text','link','vlink','alink'].concat(el.attrs, el.loadevents); 342 validation._commonsetting(this, 343 ['p','div'].concat(el.heading), 344 ['align'].concat(el.attrs)); 345 this.dl = this.dir = this.menu = el.attrs.concat('compact'); 346 this.ul = this.menu.concat('type'); 347 this.ol = this.ul.concat('start'); 348 this.li = el.attrs.concat('type','value'); 349 this.hr = el.attrs.concat('align','noshade','size','width'); 350 this.pre = el.attrs.concat('width','xml:space'); 351 this.blockquote = this.q = el.attrs.concat('cite'); 352 this.ins = this.del = this.blockquote.concat('datetime'); 353 this.a = el.attrs.concat(el.focus,'charset','type','name','href','hreflang','rel','rev','shape','coords','target'); 354 this.bdo = el.coreattrs.concat(el.events, 'lang','xml:lang','dir'); 355 this.br = el.coreattrs.concat('clear'); 356 validation._commonsetting(this, 357 ['noscript','noframes','dt', 'dd', 'address','center','span','em', 'strong', 'dfn','code', 358 'samp','kbd','var','cite','abbr','acronym','sub','sup','tt', 359 'i','b','big','small','u','s','strike', 'fieldset'], 360 el.attrs); 361 362 this.basefont = ['id','size','color','face']; 363 this.font = el.coreattrs.concat(el.i18n, 'size','color','face'); 364 this.object = el.attrs.concat('declare','classid','codebase','data','type','codetype','archive','standby','height','width','usemap','name','tabindex','align','border','hspace','vspace'); 365 this.param = ['id','name','value','valuetype','type']; 366 this.applet = el.coreattrs.concat('codebase','archive','code','object','alt','name','width','height','align','hspace','vspace'); 367 this.img = el.attrs.concat('src','alt','name','longdesc','height','width','usemap','ismap','align','border','hspace','vspace'); 368 this.map = this.title.concat('title','name', 'style', 'class', el.events); 369 this.area = el.attrs.concat('shape','coords','href','nohref','alt','target', el.focus); 370 this.form = el.attrs.concat('action','method','name','enctype',el.formevents,'accept','accept-charset','target'); 371 this.label = el.attrs.concat('for','accesskey', el.focusevents); 372 this.input = el.attrs.concat('type','name','value','checked','disabled','readonly','size','maxlength','src','alt','usemap',el.input,'accept','align', el.focus); 373 this.select = el.attrs.concat('name','size','multiple','disabled','tabindex', el.focusevents,el.input); 374 this.optgroup = el.attrs.concat('disabled','label'); 375 this.option = el.attrs.concat('selected','disabled','label','value'); 376 this.textarea = el.attrs.concat('name','rows','cols','disabled','readonly', el.inputevents, el.focus); 377 this.legend = el.attrs.concat('accesskey','align'); 378 this.button = el.attrs.concat('name','value','type','disabled',el.focus); 379 this.isindex = el.coreattrs.concat('prompt', el.i18n); 380 this.table = el.attrs.concat('summary','width','border','frame','rules','cellspacing','cellpadding','align','bgcolor'); 381 this.caption = el.attrs.concat('align'); 382 this.col = this.colgroup = el.attrs.concat('span','width','align','char','charoff','valign'); 383 this.thead = el.attrs.concat('align','char','charoff','valign'); 384 this.tfoot = this.tbody = this.thead; 385 this.tr = this.thead.concat('bgcolor'); 386 this.td = this.th = this.tr.concat('abbr','axis','headers','scope','rowspan','colspan','nowrap','width','height'); 387 }(this.elements, this); 388 389 this.badTagAttributes = new this.Set({}); 390 391 // State array. For each tag identifies what it can contain. 392 // I'm not attempting to check the order or number of contained 393 // tags (yet). 394 this.States = new function(el, validation) { 395 396 var here = this; 397 function setStates(tags, value) { 398 var valset = new validation.Set(value); 399 400 for (var i = 0; i < tags.length; i++) { 401 here[tags[i]] = valset; 402 } 403 } 404 405 setStates(['html'], ['head','body']); 406 setStates(['head'], ['title','base','script','style', 'meta','link','object','isindex']); 407 setStates([ 408 'base', 'meta', 'link', 'hr', 'param', 'img', 'area', 'input', 409 'br', 'basefont', 'isindex', 'col', 410 ], []); 411 412 setStates(['title','style','script','option','textarea'], ['#PCDATA']); 413 setStates([ 'noscript', 'iframe', 'noframes', 'body', 'div', 414 'li', 'dd', 'blockquote', 'center', 'ins', 'del', 'td', 'th', 415 ], el.Flow); 416 417 setStates(el.heading, el.Inline); 418 setStates([ 'p', 'dt', 'address', 'span', 'bdo', 'caption', 419 'em', 'strong', 'dfn','code','samp','kbd','var', 420 'cite','abbr','acronym','q','sub','sup','tt','i', 421 'b','big','small','u','s','strike','font','label', 422 'legend'], el.Inline); 423 424 setStates(['ul', 'ol', 'menu', 'dir', 'ul', ], ['li']); 425 setStates(['dl'], ['dt','dd']); 426 setStates(['pre'], validation._exclude(el.Inline, "img|object|applet|big|small|sub|sup|font|basefont")); 427 setStates(['a'], validation._exclude(el.Inline, "a")); 428 setStates(['applet', 'object'], ['#PCDATA', 'param','form'].concat(el.block, el.inline, el.misc)); 429 setStates(['map'], ['form', 'area'].concat(el.block, el.misc)); 430 setStates(['form'], validation._exclude(el.Flow, ['form'])); 431 setStates(['select'], ['optgroup','option']); 432 setStates(['optgroup'], ['option']); 433 setStates(['fieldset'], ['#PCDATA','legend','form'].concat(el.block,el.inline,el.misc)); 434 setStates(['button'], validation._exclude(el.Flow, ['a','form','iframe'].concat(el.inline_forms))); 435 setStates(['table'], ['caption','col','colgroup','thead','tfoot','tbody','tr']); 436 setStates(['thead', 'tfoot', 'tbody'], ['tr']); 437 setStates(['colgroup'], ['col']); 438 setStates(['tr'], ['th','td']); 439 }(this.elements, this); 440 441 // Permitted elements for style. 442 this.styleWhitelist = new this.Set(['text-align', 'list-style-type', 'float']); 443 this.classBlacklist = new this.Set(['MsoNormal', 'MsoTitle', 'MsoHeader', 'MsoFootnoteText', 444 'Bullet1', 'Bullet2']); 445 446 this.classFilter = function(value) { 447 var classes = value.split(' '); 448 var filtered = []; 449 for (var i = 0; i < classes.length; i++) { 450 var c = classes[i]; 451 if (c && !this.classBlacklist[c]) { 452 filtered.push(c); 453 } 454 } 455 return filtered.join(' '); 456 } 457 this._defaultCopyAttribute = function(name, htmlnode, xhtmlnode) { 458 var val = htmlnode.getAttribute(name); 459 if (val) xhtmlnode.setAttribute(name, val); 460 } 461 // Set up filters for attributes. 462 var filter = this; 463 this.attrFilters = new function(validation, editor) { 464 var attrs = validation.elements.attributes; 465 for (var i=0; i < attrs.length; i++) { 466 this[attrs[i]] = validation._defaultCopyAttribute; 467 } 468 this['class'] = function(name, htmlnode, xhtmlnode) { 469 var val = htmlnode.getAttribute('class'); 470 if (val) val = validation.classFilter(val); 471 if (val) xhtmlnode.setAttribute('class', val); 472 } 473 // allow a * wildcard to make all attributes valid in the filter 474 // note that this is pretty slow on IE 475 this['*'] = function(name, htmlnode, xhtmlnode) { 476 var nodeName = filter._getTagName(htmlnode); 477 var bad = filter.badTagAttributes[nodeName]; 478 for (var i=0; i < htmlnode.attributes.length; i++) { 479 var attr = htmlnode.attributes[i]; 480 if (bad && bad.contains(attr.name)) { 481 continue; 482 }; 483 if (attr.value !== null && attr.value !== undefined) { 484 xhtmlnode.setAttribute(attr.name, attr.value); 485 }; 486 }; 487 } 488 if (editor.getBrowserName()=="IE") { 489 this['class'] = function(name, htmlnode, xhtmlnode) { 490 var val = htmlnode.className; 491 if (val) val = validation.classFilter(val); 492 if (val) xhtmlnode.setAttribute('class', val); 493 } 494 this['http-equiv'] = function(name, htmlnode, xhtmlnode) { 495 var val = htmlnode.httpEquiv; 496 if (val) xhtmlnode.setAttribute('http-equiv', val); 497 } 498 this['xml:lang'] = this['xml:space'] = function(name, htmlnode, xhtmlnode) { 499 try { 500 var val = htmlnode.getAttribute(name); 501 if (val) xhtmlnode.setAttribute(name, val); 502 } catch(e) { 503 } 504 } 505 } 506 this.rowspan = this.colspan = function(name, htmlnode, xhtmlnode) { 507 var val = htmlnode.getAttribute(name); 508 if (val && val != '1') xhtmlnode.setAttribute(name, val); 509 } 510 this.style = function(name, htmlnode, xhtmlnode) { 511 var val = htmlnode.style.cssText; 512 if (val) { 513 var styles = val.split(/; */); 514 for (var i = styles.length; i >= 0; i--) if (styles[i]) { 515 var parts = /^([^:]+): *(.*)$/.exec(styles[i]); 516 var name = parts[1].toLowerCase(); 517 if (validation.styleWhitelist[name]) { 518 styles[i] = name+': '+parts[2]; 519 } else { 520 styles.splice(i,1); // delete 521 } 522 } 523 if (styles[styles.length-1]) styles.push(''); 524 val = styles.join('; ').strip(); 525 } 526 if (val) xhtmlnode.setAttribute('style', val); 527 } 528 }(this, editor); 529 530 // Exclude unwanted tags. 531 this.excludeTags(['center']); 532 533 if (editor.config && editor.config.htmlfilter) { 534 this.filterStructure = editor.config.htmlfilter.filterstructure; 535 536 var exclude = editor.config.htmlfilter; 537 if (exclude.a) 538 this.excludeAttributes(exclude.a); 539 if (exclude.t) 540 this.excludeTags(exclude.t); 541 if (exclude.c) { 542 var c = exclude.c; 543 if (!c.length) c = [c]; 544 for (var i = 0; i < c.length; i++) { 545 this.excludeTagAttributes(c[i].t, c[i].a); 546 } 547 } 548 if (exclude.xstyle) { 549 var s = exclude.xstyle; 550 for (var i = 0; i < s.length; i++) { 551 this.styleWhitelist[s[i]] = 1; 552 } 553 } 554 if (exclude['class']) { 555 var c = exclude['class']; 556 for (var i = 0; i < c.length; i++) { 557 this.classBlacklist[c[i]] = 1; 558 } 559 } 560 }; 561 562 // Copy all valid attributes from htmlnode to xhtmlnode. 563 this._copyAttributes = function(htmlnode, xhtmlnode, valid) { 564 if (valid.contains('*')) { 565 // allow all attributes on this tag 566 this.attrFilters['*'](name, htmlnode, xhtmlnode); 567 return; 568 }; 569 for (var i = 0; i < valid.length; i++) { 570 var name = valid[i]; 571 var filter = this.attrFilters[name]; 572 if (filter) filter(name, htmlnode, xhtmlnode); 573 } 574 } 575 576 this._convertToSarissaNode = function(ownerdoc, htmlnode, xhtmlparent) { 577 return this._convertNodes(ownerdoc, htmlnode, xhtmlparent, new this.Set(['html'])); 578 }; 579 580 this._convertNodes = function(ownerdoc, htmlnode, xhtmlparent, permitted) { 581 var name, parentnode = xhtmlparent; 582 var nodename = this._getTagName(htmlnode); 583 var nostructure = !this.filterstructure; 584 585 // TODO: This permits valid tags anywhere. it should use the state 586 // table in xhtmlvalid to only permit tags where the XHTML DTD 587 // says they are valid. 588 var validattrs = this.tagAttributes[nodename]; 589 if (validattrs && (nostructure || permitted[nodename])) { 590 try { 591 var xhtmlnode = ownerdoc.createElement(nodename); 592 parentnode = xhtmlnode; 593 } catch (e) { }; 594 595 if (validattrs && xhtmlnode) 596 this._copyAttributes(htmlnode, xhtmlnode, validattrs); 597 } 598 599 var kids = htmlnode.childNodes; 600 var permittedChildren = this.States[parentnode.tagName] || permitted; 601 602 if (kids.length == 0) { 603 if (htmlnode.text && htmlnode.text != "" && 604 (nostructure || permittedChildren['#PCDATA'])) { 605 var text = htmlnode.text; 606 var tnode = ownerdoc.createTextNode(text); 607 parentnode.appendChild(tnode); 608 } 609 } else { 610 for (var i = 0; i < kids.length; i++) { 611 var kid = kids[i]; 612 613 if (kid.parentNode !== htmlnode) { 614 if (kid.tagName == 'BODY') { 615 if (nodename != 'html') continue; 616 } else if (kid.parentNode.tagName === htmlnode.tagName) { 617 continue; // IE bug: nodes appear multiple places 618 } 619 } 620 621 if (kid.nodeType == 1) { 622 var newkid = this._convertNodes(ownerdoc, kid, parentnode, permittedChildren); 623 if (newkid != null) { 624 parentnode.appendChild(newkid); 625 }; 626 } else if (kid.nodeType == 3) { 627 if (nostructure || permittedChildren['#PCDATA']) 628 parentnode.appendChild(ownerdoc.createTextNode(kid.nodeValue)); 629 } else if (kid.nodeType == 4) { 630 if (nostructure || permittedChildren['#PCDATA']) 631 parentnode.appendChild(ownerdoc.createCDATASection(kid.nodeValue)); 632 } 633 } 634 } 635 return xhtmlnode; 636 }; 637 } 638 639
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 15:30:41 2007 | par Balluche grâce à PHPXref 0.7 |