| 1 |
/* ──────────────────────────────────────────────────────────────────────────── |
| 2 |
Utility helpers |
| 3 |
────────────────────────────────────────────────────────────────────────── */ |
| 4 |
|
| 5 |
(function () { |
| 6 |
'use strict'; |
| 7 |
|
| 8 |
/* ---------- insertAfter ------------------------------------------------ */ |
| 9 |
/* Fast path for “append” avoids creating a reference to nextSibling. */ |
| 10 |
function insertAfter(newNode, referenceNode) { |
| 11 |
const parent = referenceNode.parentNode; |
| 12 |
if (parent.lastChild === referenceNode) { |
| 13 |
parent.appendChild(newNode); |
| 14 |
} else { |
| 15 |
parent.insertBefore(newNode, referenceNode.nextSibling); |
| 16 |
} |
| 17 |
} |
| 18 |
|
| 19 |
/* ---------- getElementByXPath ----------------------------------------- */ |
| 20 |
/* 1) Use native evaluate where available (all evergreen browsers). */ |
| 21 |
/* 2) Small, allocation-free fallback for very old IE engines. */ |
| 22 |
function getElementByXPath(xPath, doc = document) { |
| 23 |
if (doc.evaluate) { |
| 24 |
// XPathResult.FIRST_ORDERED_NODE_TYPE = 9 |
| 25 |
return doc.evaluate( |
| 26 |
xPath, |
| 27 |
doc, |
| 28 |
null, |
| 29 |
9, |
| 30 |
null |
| 31 |
).singleNodeValue; |
| 32 |
} |
| 33 |
|
| 34 |
// Fallback (legacy IE) – minimal parsing, no .filter / regex loops |
| 35 |
xPath = xPath.replace(/^\/+/, ''); // strip leading “/” |
| 36 |
const steps = xPath.split('/'); |
| 37 |
let current = doc; |
| 38 |
|
| 39 |
for (let i = 0, l = steps.length; i < l && current; i += 1) { |
| 40 |
const match = /([^\[\]]+)(?:\[(\d+)\])?/.exec(steps[i]); |
| 41 |
if (!match) return null; |
| 42 |
const [, tag, idx] = match; |
| 43 |
const pos = idx ? (idx - 1) : 0; // XPath indices are 1-based |
| 44 |
current = current.getElementsByTagName(tag)[pos] || null; |
| 45 |
} |
| 46 |
return current; |
| 47 |
} |
| 48 |
|
| 49 |
/* ---------- Array.prototype.filter polyfill (unchanged API) ------------ */ |
| 50 |
/* MIT – kept for <= IE8; tightened slightly for speed/readability. */ |
| 51 |
/* eslint-disable-next-line no-extend-native */ |
| 52 |
if (!Array.prototype.filter) { |
| 53 |
Array.prototype.filter = function (callback, thisArg) { |
| 54 |
if (typeof callback !== 'function') throw new TypeError(); |
| 55 |
const out = []; |
| 56 |
for (let i = 0, l = this.length >>> 0; i < l; i += 1) { |
| 57 |
if (i in this) { |
| 58 |
const val = this[i]; |
| 59 |
if (callback.call(thisArg, val, i, this)) out.push(val); |
| 60 |
} |
| 61 |
} |
| 62 |
return out; |
| 63 |
}; |
| 64 |
} |
| 65 |
|
| 66 |
/* ---------- Injection helpers ----------------------------------------- */ |
| 67 |
|
| 68 |
function injectWidgetByXpath(xpath) { |
| 69 |
const anchor = |
| 70 |
getElementByXPath(xpath) || document.getElementById('tbdefault'); |
| 71 |
if (anchor) innerInject(anchor); |
| 72 |
} |
| 73 |
|
| 74 |
function injectWidgetByMarker(markerId) { |
| 75 |
const markerNode = document.getElementById(markerId); |
| 76 |
if (markerNode && markerNode.parentNode) innerInject(markerNode.parentNode); |
| 77 |
} |
| 78 |
|
| 79 |
/* One reflow: build everything in a fragment, then insert once. */ |
| 80 |
function innerInject(node) { |
| 81 |
if (!node) return; |
| 82 |
|
| 83 |
const fragment = document.createDocumentFragment(); |
| 84 |
const container = document.createElement('span'); |
| 85 |
const script = document.createElement('script'); |
| 86 |
|
| 87 |
/* Keep single quotes – some minifiers mangle this otherwise. */ |
| 88 |
container.insertAdjacentHTML('beforeend', '{{HTML}}'); |
| 89 |
script.text = "{{SCRIPT}}"; |
| 90 |
|
| 91 |
fragment.appendChild(container); |
| 92 |
fragment.appendChild(script); |
| 93 |
insertAfter(fragment, node); |
| 94 |
} |
| 95 |
|
| 96 |
/* ---------- Export to global scope (names unchanged) ------------------- */ |
| 97 |
window.insertAfter = insertAfter; |
| 98 |
window.getElementByXPath = getElementByXPath; |
| 99 |
window.injectWidgetByXpath = injectWidgetByXpath; |
| 100 |
window.injectWidgetByMarker = injectWidgetByMarker; |
| 101 |
window.innerInject = innerInject; |
| 102 |
}()); |
| 103 |
|