Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
render.js
001 var MSXML = (typeof DOMParser === 'undefined' || typeof XSLTProcessor === 'undefined');
002 var xslt = {
003 /**
004 * @param {string} xsl
005 */
006 init: function(xsl)
007 {
008 var stylesheet = xslt.loadXML(xsl);
009 if (MSXML)
010 {
011 var generator = new ActiveXObject('MSXML2.XSLTemplate.6.0');
012 generator['stylesheet'] = stylesheet;
013 xslt.proc = generator['createProcessor']();
014 }
015 else
016 {
017 xslt.proc = new XSLTProcessor;
018 xslt.proc['importStylesheet'](stylesheet);
019 }
020 },
021
022 /**
023 * @param {string} xml
024 * @return {!Document}
025 */
026 loadXML: function(xml)
027 {
028 var dom;
029 if (MSXML)
030 {
031 dom = new ActiveXObject('MSXML2.FreeThreadedDOMDocument.6.0');
032 dom['async'] = false;
033 dom['validateOnParse'] = false;
034 dom['loadXML'](xml);
035 }
036 else
037 {
038 dom = (new DOMParser).parseFromString(xml, 'text/xml');
039 }
040
041 if (!dom)
042 {
043 throw 'Cannot parse ' + xml;
044 }
045
046 return dom;
047 },
048
049 /**
050 * @param {string} paramName Parameter name
051 * @param {string} paramValue Parameter's value
052 */
053 setParameter: function(paramName, paramValue)
054 {
055 if (MSXML)
056 {
057 xslt.proc['addParameter'](paramName, paramValue, '');
058 }
059 else
060 {
061 xslt.proc['setParameter'](null, paramName, paramValue);
062 }
063 },
064
065 /**
066 * @param {string} xml
067 * @param {!Document} targetDoc
068 * @return {!DocumentFragment}
069 */
070 transformToFragment: function(xml, targetDoc)
071 {
072 if (MSXML)
073 {
074 var div = targetDoc.createElement('div'),
075 fragment = targetDoc.createDocumentFragment();
076
077 xslt.proc['input'] = xslt.loadXML(xml);
078 xslt.proc['transform']();
079 div.innerHTML = xslt.proc['output'];
080 while (div.firstChild)
081 {
082 fragment.appendChild(div.firstChild);
083 }
084
085 return fragment;
086 }
087
088 return xslt.proc['transformToFragment'](xslt.loadXML(xml), targetDoc);
089 }
090 };
091 xslt.init(xsl);
092
093 /**
094 * Parse a given text and render it into given HTML element
095 *
096 * @param {string} text
097 * @param {!HTMLElement} target
098 * @return {!Node}
099 */
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 }