[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
1 var MSXML = (typeof DOMParser === 'undefined' || typeof XSLTProcessor === 'undefined'); 2 var xslt = { 3 /** 4 * @param {string} xsl 5 */ 6 init: function(xsl) 7 { 8 var stylesheet = xslt.loadXML(xsl); 9 if (MSXML) 10 { 11 var generator = new ActiveXObject('MSXML2.XSLTemplate.6.0'); 12 generator['stylesheet'] = stylesheet; 13 xslt.proc = generator['createProcessor'](); 14 } 15 else 16 { 17 xslt.proc = new XSLTProcessor; 18 xslt.proc['importStylesheet'](stylesheet); 19 } 20 }, 21 22 /** 23 * @param {string} xml 24 * @return {!Document} 25 */ 26 loadXML: function(xml) 27 { 28 var dom; 29 if (MSXML) 30 { 31 dom = new ActiveXObject('MSXML2.FreeThreadedDOMDocument.6.0'); 32 dom['async'] = false; 33 dom['validateOnParse'] = false; 34 dom['loadXML'](xml); 35 } 36 else 37 { 38 dom = (new DOMParser).parseFromString(xml, 'text/xml'); 39 } 40 41 if (!dom) 42 { 43 throw 'Cannot parse ' + xml; 44 } 45 46 return dom; 47 }, 48 49 /** 50 * @param {string} paramName Parameter name 51 * @param {string} paramValue Parameter's value 52 */ 53 setParameter: function(paramName, paramValue) 54 { 55 if (MSXML) 56 { 57 xslt.proc['addParameter'](paramName, paramValue, ''); 58 } 59 else 60 { 61 xslt.proc['setParameter'](null, paramName, paramValue); 62 } 63 }, 64 65 /** 66 * @param {string} xml 67 * @param {!Document} targetDoc 68 * @return {!DocumentFragment} 69 */ 70 transformToFragment: function(xml, targetDoc) 71 { 72 if (MSXML) 73 { 74 var div = targetDoc.createElement('div'), 75 fragment = targetDoc.createDocumentFragment(); 76 77 xslt.proc['input'] = xslt.loadXML(xml); 78 xslt.proc['transform'](); 79 div.innerHTML = xslt.proc['output']; 80 while (div.firstChild) 81 { 82 fragment.appendChild(div.firstChild); 83 } 84 85 return fragment; 86 } 87 88 return xslt.proc['transformToFragment'](xslt.loadXML(xml), targetDoc); 89 } 90 }; 91 xslt.init(xsl); 92 93 /** 94 * Parse a given text and render it into given HTML element 95 * 96 * @param {string} text 97 * @param {!HTMLElement} target 98 * @return {!Node} 99 */ 100 function preview(text, target) 101 { 102 var targetDoc = target.ownerDocument; 103 if (!targetDoc) 104 { 105 throw 'Target does not have a ownerDocument'; 106 } 107 108 var resultFragment = xslt.transformToFragment(parse(text).replace(/<[eis]>[^<]*<\/[eis]>/g, ''), targetDoc), 109 lastUpdated = target; 110 111 // https://bugs.chromium.org/p/chromium/issues/detail?id=266305 112 if (typeof window !== 'undefined' && 'chrome' in window) 113 { 114 resultFragment.querySelectorAll('script').forEach( 115 function (oldScript) 116 { 117 let newScript = document.createElement('script'); 118 for (let attribute of oldScript['attributes']) 119 { 120 newScript['setAttribute'](attribute.name, attribute.value); 121 } 122 newScript.textContent = oldScript.textContent; 123 124 oldScript.parentNode.replaceChild(newScript, oldScript); 125 } 126 ); 127 } 128 129 // Compute and refresh hashes 130 if (HINT.hash) 131 { 132 computeHashes(resultFragment); 133 } 134 135 // Apply post-processing 136 if (HINT.onRender) 137 { 138 executeEvents(resultFragment, 'render'); 139 } 140 141 /** 142 * Compute and set all hashes in given document fragment 143 * 144 * @param {!DocumentFragment} fragment 145 */ 146 function computeHashes(fragment) 147 { 148 var nodes = fragment.querySelectorAll('[data-s9e-livepreview-hash]'), 149 i = nodes.length; 150 while (--i >= 0) 151 { 152 nodes[i]['setAttribute']('data-s9e-livepreview-hash', hash(nodes[i].outerHTML)); 153 } 154 } 155 156 /** 157 * Execute an event's code on a given node 158 * 159 * @param {!Element} node 160 * @param {string} eventName 161 */ 162 function executeEvent(node, eventName) 163 { 164 /** @type {string} */ 165 var code = node.getAttribute('data-s9e-livepreview-on' + eventName); 166 if (!functionCache[code]) 167 { 168 functionCache[code] = new Function(code); 169 } 170 171 functionCache[code]['call'](node); 172 } 173 174 /** 175 * Locate and execute an event on given document fragment or element 176 * 177 * @param {!DocumentFragment|!Element} root 178 * @param {string} eventName 179 */ 180 function executeEvents(root, eventName) 181 { 182 // Execute the event on the root node, as there is no self-or-descendant selector in CSS 183 if (root instanceof Element && root['hasAttribute']('data-s9e-livepreview-on' + eventName)) 184 { 185 executeEvent(root, eventName); 186 } 187 188 var nodes = root.querySelectorAll('[data-s9e-livepreview-on' + eventName + ']'), 189 i = nodes.length; 190 while (--i >= 0) 191 { 192 executeEvent(nodes[i], eventName); 193 } 194 } 195 196 /** 197 * Update the content of given node oldParent to match node newParent 198 * 199 * @param {!Node} oldParent 200 * @param {!Node} newParent 201 */ 202 function refreshElementContent(oldParent, newParent) 203 { 204 var oldNodes = oldParent.childNodes, 205 newNodes = newParent.childNodes, 206 oldCnt = oldNodes.length, 207 newCnt = newNodes.length, 208 oldNode, 209 newNode, 210 left = 0, 211 right = 0; 212 213 // Skip the leftmost matching nodes 214 while (left < oldCnt && left < newCnt) 215 { 216 oldNode = oldNodes[left]; 217 newNode = newNodes[left]; 218 if (!refreshNode(oldNode, newNode)) 219 { 220 break; 221 } 222 223 ++left; 224 } 225 226 // Skip the rightmost matching nodes 227 var maxRight = Math.min(oldCnt - left, newCnt - left); 228 while (right < maxRight) 229 { 230 oldNode = oldNodes[oldCnt - (right + 1)]; 231 newNode = newNodes[newCnt - (right + 1)]; 232 if (!refreshNode(oldNode, newNode)) 233 { 234 break; 235 } 236 237 ++right; 238 } 239 240 // Remove the old dirty nodes in the middle of the tree 241 var i = oldCnt - right; 242 while (--i >= left) 243 { 244 oldParent.removeChild(oldNodes[i]); 245 lastUpdated = oldParent; 246 } 247 248 // Test whether there are any nodes in the new tree between the matching nodes at the left 249 // and the matching nodes at the right 250 var rightBoundary = newCnt - right; 251 if (left >= rightBoundary) 252 { 253 return; 254 } 255 256 // Clone the new nodes 257 var newNodesFragment = targetDoc.createDocumentFragment(); 258 i = left; 259 do 260 { 261 newNode = newNodes[i]; 262 if (HINT.onUpdate && newNode instanceof Element) 263 { 264 executeEvents(newNode, 'update'); 265 } 266 lastUpdated = newNodesFragment.appendChild(newNode); 267 } 268 while (i < --rightBoundary); 269 270 // If we haven't skipped any nodes to the right, we can just append the fragment 271 if (!right) 272 { 273 oldParent.appendChild(newNodesFragment); 274 } 275 else 276 { 277 oldParent.insertBefore(newNodesFragment, oldParent.childNodes[left]); 278 } 279 } 280 281 /** 282 * Update given node oldNode to make it match newNode 283 * 284 * @param {!Node} oldNode 285 * @param {!Node} newNode 286 * @return {boolean} Whether the node can be skipped 287 */ 288 function refreshNode(oldNode, newNode) 289 { 290 if (oldNode.nodeName !== newNode.nodeName || oldNode.nodeType !== newNode.nodeType) 291 { 292 return false; 293 } 294 295 if (oldNode instanceof HTMLElement && newNode instanceof HTMLElement) 296 { 297 if (!oldNode.isEqualNode(newNode) && !elementHashesMatch(oldNode, newNode)) 298 { 299 if (HINT.onUpdate && newNode['hasAttribute']('data-s9e-livepreview-onupdate')) 300 { 301 executeEvent(newNode, 'update'); 302 } 303 syncElementAttributes(oldNode, newNode); 304 refreshElementContent(oldNode, newNode); 305 } 306 } 307 // Node.TEXT_NODE || Node.COMMENT_NODE 308 else if (oldNode.nodeType === 3 || oldNode.nodeType === 8) 309 { 310 if (oldNode.nodeValue !== newNode.nodeValue) 311 { 312 oldNode.nodeValue = newNode.nodeValue; 313 lastUpdated = oldNode; 314 } 315 } 316 317 return true; 318 } 319 320 /** 321 * Test whether both given elements have a hash value and both match 322 * 323 * @param {!HTMLElement} oldEl 324 * @param {!HTMLElement} newEl 325 * @return {boolean} 326 */ 327 function elementHashesMatch(oldEl, newEl) 328 { 329 if (!HINT.hash) 330 { 331 // Hashes can never match if there are no hashes in any template 332 return false; 333 } 334 const attrName = 'data-s9e-livepreview-hash'; 335 336 return oldEl['hasAttribute'](attrName) && newEl['hasAttribute'](attrName) && oldEl['getAttribute'](attrName) === newEl['getAttribute'](attrName); 337 } 338 339 /** 340 * Hash given string 341 * 342 * @param {string} text 343 * @return {number} 344 */ 345 function hash(text) 346 { 347 var pos = text.length, s1 = 0, s2 = 0; 348 while (--pos >= 0) 349 { 350 s1 = (s1 + text.charCodeAt(pos)) % 0xFFFF; 351 s2 = (s1 + s2) % 0xFFFF; 352 } 353 354 return (s2 << 16) | s1; 355 } 356 357 /** 358 * Make the set of attributes of given element oldEl match newEl's 359 * 360 * @param {!HTMLElement} oldEl 361 * @param {!HTMLElement} newEl 362 */ 363 function syncElementAttributes(oldEl, newEl) 364 { 365 var oldAttributes = oldEl['attributes'], 366 newAttributes = newEl['attributes'], 367 oldCnt = oldAttributes.length, 368 newCnt = newAttributes.length, 369 i = oldCnt, 370 ignoreAttrs = ' ' + oldEl.getAttribute('data-s9e-livepreview-ignore-attrs') + ' '; 371 372 while (--i >= 0) 373 { 374 var oldAttr = oldAttributes[i], 375 namespaceURI = oldAttr['namespaceURI'], 376 attrName = oldAttr['name']; 377 378 if (HINT.ignoreAttrs && ignoreAttrs.indexOf(' ' + attrName + ' ') > -1) 379 { 380 continue; 381 } 382 if (!newEl.hasAttributeNS(namespaceURI, attrName)) 383 { 384 oldEl.removeAttributeNS(namespaceURI, attrName); 385 lastUpdated = oldEl; 386 } 387 } 388 389 i = newCnt; 390 while (--i >= 0) 391 { 392 var newAttr = newAttributes[i], 393 namespaceURI = newAttr['namespaceURI'], 394 attrName = newAttr['name'], 395 attrValue = newAttr['value']; 396 397 if (HINT.ignoreAttrs && ignoreAttrs.indexOf(' ' + attrName + ' ') > -1) 398 { 399 continue; 400 } 401 if (attrValue !== oldEl.getAttributeNS(namespaceURI, attrName)) 402 { 403 oldEl.setAttributeNS(namespaceURI, attrName, attrValue); 404 lastUpdated = oldEl; 405 } 406 } 407 } 408 409 refreshElementContent(target, resultFragment); 410 411 return lastUpdated; 412 } 413 414 /** 415 * Set the value of a stylesheet parameter 416 * 417 * @param {string} paramName Parameter name 418 * @param {string} paramValue Parameter's value 419 */ 420 function setParameter(paramName, paramValue) 421 { 422 xslt.setParameter(paramName, paramValue); 423 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |