VulpesOS/gaia/distros/spark/apps/customizer/js/elements/fxos-customizer.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

192 lines
No EOL
7 KiB
JavaScript
Executable file

define(["exports"], function (exports) {
"use strict";
(function (window) {
"use strict";
var proto = Object.create(HTMLElement.prototype);
var template = "<style scoped>\n[data-icon]:before {\n font-family: \"gaia-icons\";\n content: attr(data-icon);\n display: inline-block;\n font-weight: 500;\n font-style: normal;\n text-decoration: inherit;\n text-transform: none;\n text-rendering: optimizeLegibility;\n font-size: 30px;\n}\n[data-customizer-icon]:before {\n font-family: \"customizer-icons\";\n content: attr(data-customizer-icon);\n display: inline-block;\n font-weight: 500;\n font-style: normal;\n text-decoration: inherit;\n text-transform: none;\n text-rendering: optimizeLegibility;\n font-size: 30px;\n}\ngaia-dom-tree {\n width: 100%;\n height: calc(100% - 46px);\n}\n.pin {\n position: absolute;\n top: 0;\n right: 0;\n margin: 1rem !important;\n opacity: 1;\n transition: opacity 0.5s ease-in-out;\n}\n.pin.scrolling {\n pointer-events: none;\n opacity: 0;\n}\n</style>\n<gaia-button circular class=\"pin\" data-action=\"settings\">\n <i data-icon=\"settings\"></i>\n</gaia-button>\n<gaia-dom-tree></gaia-dom-tree>\n<gaia-toolbar>\n <button data-customizer-icon=\"edit\" data-action=\"edit\" disabled></button>\n <button data-customizer-icon=\"copy\" data-action=\"copyOrMove\" disabled></button>\n <button data-icon=\"add\" data-action=\"append\" disabled></button>\n <button data-customizer-icon=\"source\" data-action=\"viewSource\" disabled></button>\n <button data-icon=\"delete\" data-action=\"remove\" disabled></button>\n</gaia-toolbar>";
proto.createdCallback = function () {
this.shadow = this.createShadowRoot();
this.shadow.innerHTML = template;
this.settingsButton = this.shadow.querySelector("[data-action=\"settings\"]");
this.gaiaDomTree = this.shadow.querySelector("gaia-dom-tree");
this.gaiaToolbar = this.shadow.querySelector("gaia-toolbar");
this.settingsButton.addEventListener("click", this._handleMenuAction.bind(this));
this.gaiaDomTree.addEventListener("click", this._handleSelected.bind(this));
this.gaiaToolbar.addEventListener("click", this._handleAction.bind(this));
this._watchScrolling();
this.gaiaDomTree.addEventListener("contextmenu", function (evt) {
evt.stopPropagation();
});
this._rootNodeClickHandler = this._handleClick.bind(this);
};
proto.setRootNode = function (rootNode) {
// If we already have a root node defined, disconnect from it first
if (this._root) {
this.unwatchChanges();
this.gaiaDomTree.setRoot(null);
this.gaiaDomTree.render();
this._root.removeEventListener("click", this._rootNodeClickHandler);
this._root = null;
}
if (!rootNode) {
return;
}
// Set up the new root node
this._root = rootNode;
rootNode.addEventListener("click", this._rootNodeClickHandler);
this.gaiaDomTree.setRoot(rootNode);
this.gaiaDomTree.render();
this.watchChanges();
// Pre-expand the root node
var rootTreeNode = this.gaiaDomTree.treeMap.get(rootNode);
if (rootTreeNode) {
this.gaiaDomTree.expandNode(rootTreeNode);
}
var head = rootNode.querySelector("head");
var body = rootNode.querySelector("body");
if (!head || !body) {
return;
}
// Pre-expand the <head> node
var headTreeNode = this.gaiaDomTree.treeMap.get(head);
if (headTreeNode) {
this.gaiaDomTree.expandNode(headTreeNode);
}
// Pre-expand the <body> node
var bodyTreeNode = this.gaiaDomTree.treeMap.get(body);
if (bodyTreeNode) {
this.gaiaDomTree.expandNode(bodyTreeNode);
}
};
proto._watchScrolling = function () {
var _this = this;
this.gaiaDomTree.shadowRoot.addEventListener("scroll", function (evt) {
if (_this._scrollTimeout) {
clearTimeout(_this._scrollTimeout);
}
_this._scrollTimeout = setTimeout(function () {
_this.settingsButton.classList.remove("scrolling");
}, 500);
_this.settingsButton.classList.add("scrolling");
}, true);
};
proto._shadowContains = function (el) {
var customizerRootView = document.body.querySelector(".fxos-customizer-main-view");
if (!el || el == document.documentElement) {
return false;
} else if (el == customizerRootView) {
return true;
}
return this._shadowContains(el.parentNode || el.host);
};
proto.watchChanges = function () {
var _this2 = this;
var OBSERVER_CONFIG = {
childList: true,
attributes: true,
characterData: true,
subtree: true
};
this._observer = new MutationObserver(function (mutations) {
// Only re-render if a mutation occurred in the app itself, and is
// outside of the customizer addon. This depends on the customizer
// root element having the class "fxos-customizer-main-view"
for (var i = mutations.length - 1; i >= 0; i--) {
if (!_this2._shadowContains(mutations[i].target)) {
var selectedNode = _this2.gaiaDomTree.selectedNode;
_this2.gaiaDomTree.render();
_this2.select(selectedNode);
return;
}
}
});
this._observer.observe(this._root, OBSERVER_CONFIG);
};
proto.unwatchChanges = function () {
this._observer.disconnect();
this._observer = null;
};
proto.select = function (node) {
this.gaiaDomTree.select(node);
};
proto._handleMenuAction = function (e) {
var action = e.target.dataset.action;
if (action) {
this.dispatchEvent(new CustomEvent("menu", {
detail: action
}));
}
};
proto._handleSelected = function (e) {
e.stopPropagation();
var selectedNode = this.gaiaDomTree.selectedNode;
if (!selectedNode) {
return;
}
var selected = this._selected = (selectedNode.nodeType === Node.TEXT_NODE) ? selectedNode.parentNode : selectedNode;
[].forEach.call(this.gaiaToolbar.querySelectorAll("button"), function (button) {
button.disabled = !selected;
});
this.gaiaToolbar.querySelector("[data-action=\"viewSource\"]").disabled = (selected.tagName !== "SCRIPT" || !selected.hasAttribute("src")) && (selected.tagName !== "LINK" || !selected.hasAttribute("href"));
this.dispatchEvent(new CustomEvent("selected", {
detail: this._selected
}));
};
proto._handleAction = function (e) {
this.dispatchEvent(new CustomEvent("action:" + e.target.dataset.action, {
detail: this._selected
}));
};
proto._handleClick = function (e) {
if (e.target === this.gaiaDomTree) {
return;
}
this.select(e.target);
};
try {
document.registerElement("fxos-customizer", { prototype: proto });
} catch (e) {
if (e.name !== "NotSupportedError") {
throw e;
}
}
})(window);
});