var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
var FUNC_ERROR_TEXT = "Expected a function";
var NAN = 0 / 0;
var symbolTag = "[object Symbol]";
var reTrim = /^\s+|\s+$/g;
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
var reIsBinary = /^0b[01]+$/i;
var reIsOctal = /^0o[0-7]+$/i;
var freeParseInt = parseInt;
var freeGlobal = typeof commonjsGlobal == "object" && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
var freeSelf = typeof self == "object" && self && self.Object === Object && self;
var root = freeGlobal || freeSelf || Function("return this")();
var objectProto = Object.prototype;
var objectToString = objectProto.toString;
var nativeMax = Math.max, nativeMin = Math.min;
var now = function() {
return root.Date.now();
};
function debounce(func, wait, options) {
var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true;
if (typeof func != "function") {
throw new TypeError(FUNC_ERROR_TEXT);
}
wait = toNumber(wait) || 0;
if (isObject(options)) {
leading = !!options.leading;
maxing = "maxWait" in options;
maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
trailing = "trailing" in options ? !!options.trailing : trailing;
}
function invokeFunc(time) {
var args = lastArgs, thisArg = lastThis;
lastArgs = lastThis = void 0;
lastInvokeTime = time;
result = func.apply(thisArg, args);
return result;
}
function leadingEdge(time) {
lastInvokeTime = time;
timerId = setTimeout(timerExpired, wait);
return leading ? invokeFunc(time) : result;
}
function remainingWait(time) {
var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, result2 = wait - timeSinceLastCall;
return maxing ? nativeMin(result2, maxWait - timeSinceLastInvoke) : result2;
}
function shouldInvoke(time) {
var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime;
return lastCallTime === void 0 || timeSinceLastCall >= wait || timeSinceLastCall < 0 || maxing && timeSinceLastInvoke >= maxWait;
}
function timerExpired() {
var time = now();
if (shouldInvoke(time)) {
return trailingEdge(time);
}
timerId = setTimeout(timerExpired, remainingWait(time));
}
function trailingEdge(time) {
timerId = void 0;
if (trailing && lastArgs) {
return invokeFunc(time);
}
lastArgs = lastThis = void 0;
return result;
}
function cancel() {
if (timerId !== void 0) {
clearTimeout(timerId);
}
lastInvokeTime = 0;
lastArgs = lastCallTime = lastThis = timerId = void 0;
}
function flush() {
return timerId === void 0 ? result : trailingEdge(now());
}
function debounced() {
var time = now(), isInvoking = shouldInvoke(time);
lastArgs = arguments;
lastThis = this;
lastCallTime = time;
if (isInvoking) {
if (timerId === void 0) {
return leadingEdge(lastCallTime);
}
if (maxing) {
timerId = setTimeout(timerExpired, wait);
return invokeFunc(lastCallTime);
}
}
if (timerId === void 0) {
timerId = setTimeout(timerExpired, wait);
}
return result;
}
debounced.cancel = cancel;
debounced.flush = flush;
return debounced;
}
function isObject(value) {
var type = typeof value;
return !!value && (type == "object" || type == "function");
}
function isObjectLike(value) {
return !!value && typeof value == "object";
}
function isSymbol(value) {
return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag;
}
function toNumber(value) {
if (typeof value == "number") {
return value;
}
if (isSymbol(value)) {
return NAN;
}
if (isObject(value)) {
var other = typeof value.valueOf == "function" ? value.valueOf() : value;
value = isObject(other) ? other + "" : other;
}
if (typeof value != "string") {
return value === 0 ? value : +value;
}
value = value.replace(reTrim, "");
var isBinary = reIsBinary.test(value);
return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value;
}
var lodash_debounce = debounce;
const HIDDEN_CLASS = "copy-tuner-hidden";
class CopytunerBar {
constructor(element, data, callback) {
this.element = element;
this.data = data;
this.callback = callback;
this.searchBoxElement = element.querySelector(".js-copy-tuner-bar-search");
this.logMenuElement = this.makeLogMenu();
this.element.append(this.logMenuElement);
this.addHandler();
}
addHandler() {
const openLogButton = this.element.querySelector(".js-copy-tuner-bar-open-log");
openLogButton.addEventListener("click", (event) => {
event.preventDefault();
this.toggleLogMenu();
});
this.searchBoxElement.addEventListener("input", lodash_debounce(this.onKeyup.bind(this), 250));
}
show() {
this.element.classList.remove(HIDDEN_CLASS);
this.searchBoxElement.focus();
}
hide() {
this.element.classList.add(HIDDEN_CLASS);
}
showLogMenu() {
this.logMenuElement.classList.remove(HIDDEN_CLASS);
}
toggleLogMenu() {
this.logMenuElement.classList.toggle(HIDDEN_CLASS);
}
makeLogMenu() {
const div = document.createElement("div");
div.setAttribute("id", "copy-tuner-bar-log-menu");
div.classList.add(HIDDEN_CLASS);
const table = document.createElement("table");
const tbody = document.createElement("tbody");
tbody.classList.remove("is-not-initialized");
for (const key of Object.keys(this.data).sort()) {
const value = this.data[key];
if (value === "") {
continue;
}
const td1 = document.createElement("td");
td1.textContent = key;
const td2 = document.createElement("td");
td2.textContent = value;
const tr = document.createElement("tr");
tr.classList.add("copy-tuner-bar-log-menu__row");
tr.dataset.key = key;
tr.addEventListener("click", ({ currentTarget }) => {
this.callback(currentTarget.dataset.key);
});
tr.append(td1);
tr.append(td2);
tbody.append(tr);
}
table.append(tbody);
div.append(table);
return div;
}
onKeyup({ target }) {
const keyword = target.value.trim();
this.showLogMenu();
const rows = [...this.logMenuElement.querySelectorAll("tr")];
for (const row of rows) {
const isShow = keyword === "" || [...row.querySelectorAll("td")].some((td) => td.textContent.includes(keyword));
row.classList.toggle(HIDDEN_CLASS, !isShow);
}
}
}
const isMac = navigator.platform.toUpperCase().includes("MAC");
const isVisible = (element) => !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length > 0);
const getOffset = (elment) => {
const box = elment.getBoundingClientRect();
return {
top: box.top + (window.pageYOffset - document.documentElement.clientTop),
left: box.left + (window.pageXOffset - document.documentElement.clientLeft)
};
};
const computeBoundingBox = (element) => {
if (!isVisible(element)) {
return null;
}
const boxFrame = getOffset(element);
boxFrame.right = boxFrame.left + element.offsetWidth;
boxFrame.bottom = boxFrame.top + element.offsetHeight;
return {
left: boxFrame.left,
top: boxFrame.top,
width: boxFrame.right - boxFrame.left,
height: boxFrame.bottom - boxFrame.top
};
};
const ZINDEX = 2e9;
class Specimen {
constructor(element, key, callback) {
this.element = element;
this.key = key;
this.callback = callback;
}
show() {
this.box = this.makeBox();
if (this.box === null)
return;
this.box.addEventListener("click", () => {
this.callback(this.key);
});
document.body.append(this.box);
}
remove() {
if (!this.box) {
return;
}
this.box.remove();
this.box = null;
}
makeBox() {
const box = document.createElement("div");
box.classList.add("copyray-specimen");
box.classList.add("Specimen");
const bounds = computeBoundingBox(this.element);
if (bounds === null)
return null;
for (const key of Object.keys(bounds)) {
const value = bounds[key];
box.style[key] = `${value}px`;
}
box.style.zIndex = ZINDEX;
const { position, top, left } = getComputedStyle(this.element);
if (position === "fixed") {
this.box.style.position = "fixed";
this.box.style.top = `${top}px`;
this.box.style.left = `${left}px`;
}
box.append(this.makeLabel());
return box;
}
makeLabel() {
const div = document.createElement("div");
div.classList.add("copyray-specimen-handle");
div.classList.add("Specimen");
div.textContent = this.key;
return div;
}
}
const findBlurbs = () => {
const filterNone = () => NodeFilter.FILTER_ACCEPT;
const iterator = document.createNodeIterator(document.body, NodeFilter.SHOW_COMMENT, filterNone, false);
const comments = [];
let curNode;
while (curNode = iterator.nextNode()) {
comments.push(curNode);
}
return comments.filter((comment) => comment.nodeValue.startsWith("COPYRAY")).map((comment) => {
const [, key] = comment.nodeValue.match(/^COPYRAY (\S*)$/);
const element = comment.parentNode;
return { key, element };
});
};
class Copyray {
constructor(baseUrl, data) {
this.baseUrl = baseUrl;
this.data = data;
this.isShowing = false;
this.specimens = [];
this.overlay = this.makeOverlay();
this.toggleButton = this.makeToggleButton();
this.boundOpen = this.open.bind(this);
this.copyTunerBar = new CopytunerBar(document.querySelector("#copy-tuner-bar"), this.data, this.boundOpen);
}
show() {
this.reset();
document.body.append(this.overlay);
this.makeSpecimens();
for (const specimen of this.specimens) {
specimen.show();
}
this.copyTunerBar.show();
this.isShowing = true;
}
hide() {
this.overlay.remove();
this.reset();
this.copyTunerBar.hide();
this.isShowing = false;
}
toggle() {
if (this.isShowing) {
this.hide();
} else {
this.show();
}
}
open(key) {
window.open(`${this.baseUrl}/blurbs/${key}/edit`);
}
makeSpecimens() {
for (const { element, key } of findBlurbs()) {
this.specimens.push(new Specimen(element, key, this.boundOpen));
}
}
makeToggleButton() {
const element = document.createElement("a");
element.addEventListener("click", () => {
this.show();
});
element.classList.add("copyray-toggle-button");
element.classList.add("hidden-on-mobile");
element.textContent = "Open CopyTuner";
document.body.append(element);
return element;
}
makeOverlay() {
const div = document.createElement("div");
div.setAttribute("id", "copyray-overlay");
div.addEventListener("click", () => this.hide());
return div;
}
reset() {
for (const specimen of this.specimens) {
specimen.remove();
}
}
}
var copyray = "";
const appendCopyTunerBar = (url) => {
const bar = document.createElement("div");
bar.id = "copy-tuner-bar";
bar.classList.add("copy-tuner-hidden");
bar.innerHTML = `
CopyTuner
Sync
Translations in this page
`;
document.body.append(bar);
};
const start = () => {
const { url, data } = window.CopyTuner;
appendCopyTunerBar(url);
const copyray2 = new Copyray(url, data);
document.addEventListener("keydown", (event) => {
if (copyray2.isShowing && ["Escape", "Esc"].includes(event.key)) {
copyray2.hide();
return;
}
if ((isMac && event.metaKey || !isMac && event.ctrlKey) && event.shiftKey && event.key.toLowerCase() === "k") {
copyray2.toggle();
}
});
if (console) {
console.log(`Ready to Copyray. Press ${isMac ? "cmd+shift+k" : "ctrl+shift+k"} to scan your UI.`);
}
};
if (document.readyState === "complete" || document.readyState !== "loading") {
start();
} else {
document.addEventListener("DOMContentLoaded", () => start());
}