فهرست منبع

encapsulate UI elements in shadowRoot

Gildas 5 سال پیش
والد
کامیت
fd2eb1d9f0
1فایلهای تغییر یافته به همراه152 افزوده شده و 109 حذف شده
  1. 152 109
      extension/ui/content/content-ui-main.js

+ 152 - 109
extension/ui/content/content-ui-main.js

@@ -28,24 +28,25 @@ this.singlefile.extension.ui.content.main = this.singlefile.extension.ui.content
 	const SELECTED_CONTENT_ATTRIBUTE_NAME = this.singlefile.lib.helper.SELECTED_CONTENT_ATTRIBUTE_NAME;
 
 	const MASK_TAGNAME = "singlefile-mask";
-	const PROGRESS_BAR_TAGNAME = "singlefile-progress-bar";
-	const PROGRESS_CURSOR_TAGNAME = "singlefile-progress-cursor";
+	const MASK_CONTENT_CLASSNAME = "singlefile-mask-content";
+	const PROGRESSBAR_CLASSNAME = "singlefile-progress-bar";
+	const PROGRESSBAR_CONTENT_CLASSNAME = "singlefile-progress-bar-content";
 	const SELECTION_ZONE_TAGNAME = "single-file-selection-zone";
 	const LOGS_WINDOW_TAGNAME = "singlefile-logs-window";
-	const LOGS_LINE_TAGNAME = "singlefile-logs-line";
-	const LOGS_LINE_ELEMENT_TAGNAME = "singlefile-logs-element";
+	const LOGS_CLASSNAME = "singlefile-logs";
+	const LOGS_LINE_CLASSNAME = "singlefile-logs-line";
+	const LOGS_LINE_TEXT_ELEMENT_CLASSNAME = "singlefile-logs-line-text";
+	const LOGS_LINE_STATUS_ELEMENT_CLASSNAME = "singlefile-logs-line-icon";
 	const SINGLE_FILE_UI_ELEMENT_CLASS = "single-file-ui-element";
 	const SELECT_PX_THRESHOLD = 8;
 	const LOG_PANEL_DEFERRED_IMAGES_MESSAGE = browser.i18n.getMessage("logPanelDeferredImages");
 	const LOG_PANEL_FRAME_CONTENTS_MESSAGE = browser.i18n.getMessage("logPanelFrameContents");
 	const LOG_PANEL_STEP_MESSAGE = browser.i18n.getMessage("logPanelStep");
 	const LOG_PANEL_WIDTH = browser.i18n.getMessage("logPanelWidth");
+	const CSS_PROPERTIES = new Set(Array.from(getComputedStyle(document.body)));
 
-	let selectedAreaElement;
-
-	let logsWindowElement = createLogsWindowElement();
-	const allProperties = new Set();
-	Array.from(getComputedStyle(document.body)).forEach(property => allProperties.add(property));
+	let selectedAreaElement, logsWindowElement;
+	createLogsWindowElement();
 
 	return {
 		getSelectedLinks,
@@ -58,7 +59,6 @@ this.singlefile.extension.ui.content.main = this.singlefile.extension.ui.content
 			let maskElement = document.querySelector(MASK_TAGNAME);
 			if (!maskElement) {
 				if (options.logsEnabled) {
-					setLogsWindowStyle();
 					document.body.appendChild(logsWindowElement);
 				}
 				if (options.shadowEnabled) {
@@ -66,9 +66,6 @@ this.singlefile.extension.ui.content.main = this.singlefile.extension.ui.content
 					if (options.progressBarEnabled) {
 						createProgressBarElement(maskElement);
 					}
-					maskElement.offsetWidth;
-					maskElement.style.setProperty("background-color", "black", "important");
-					maskElement.style.setProperty("opacity", .3, "important");					
 				}
 			}
 		},
@@ -77,17 +74,20 @@ this.singlefile.extension.ui.content.main = this.singlefile.extension.ui.content
 			if (maskElement) {
 				maskElement.remove();
 			}
-
 			logsWindowElement.remove();
 			clearLogs();
 		},
 		onLoadResource(index, maxIndex, options) {
 			if (options.shadowEnabled && options.progressBarEnabled) {
-				const progressBarElement = document.querySelector(PROGRESS_BAR_TAGNAME);
-				if (progressBarElement && maxIndex) {
-					const width = Math.floor((index / maxIndex) * 100) + "%";
-					if (progressBarElement.style.getPropertyValue("width") != width) {
-						requestAnimationFrame(() => progressBarElement.style.setProperty("width", Math.floor((index / maxIndex) * 100) + "%", "important"));
+				const maskElement = document.querySelector(MASK_TAGNAME);
+				if (maskElement) {
+					const progressBarElement = maskElement.shadowRoot.querySelector("." + PROGRESSBAR_CLASSNAME);
+					if (progressBarElement && maxIndex) {
+						const width = Math.floor((index / maxIndex) * 100) + "%";
+						if (progressBarElement.style.getPropertyValue("width") != width) {
+							progressBarElement.style.setProperty("width", width);
+							progressBarElement.offsetWidth;
+						}
 					}
 				}
 			}
@@ -377,104 +377,151 @@ this.singlefile.extension.ui.content.main = this.singlefile.extension.ui.content
 	function createMaskElement() {
 		let maskElement = document.querySelector(MASK_TAGNAME);
 		if (!maskElement) {
-			maskElement = createElement(MASK_TAGNAME);
-			maskElement.style.setProperty("opacity", 0, "important");
-			maskElement.style.setProperty("background-color", "transparent", "important");
-			maskElement.style.setProperty("position", "fixed", "important");
-			maskElement.style.setProperty("top", "0", "important");
-			maskElement.style.setProperty("left", "0", "important");
-			maskElement.style.setProperty("width", "100%", "important");
-			maskElement.style.setProperty("height", "100%", "important");
-			maskElement.style.setProperty("z-index", 2147483646, "important");
-			maskElement.style.setProperty("transition", "opacity 250ms", "important");
-			document.body.appendChild(maskElement);
+			maskElement = createElement(MASK_TAGNAME, document.body);
+			const shadowRoot = maskElement.attachShadow({ mode: "open" });
+			const styleElement = document.createElement("style");
+			styleElement.textContent = `
+				@keyframes single-file-progress { 
+					0% { 
+						left: -50px;
+					} 
+					100% { 
+						left: 0;
+					}
+				}
+				.${PROGRESSBAR_CLASSNAME} {
+					position: fixed;
+					top: 0;
+					left: 0;
+					width: 0;
+					height: 8px;
+					z-index: 2147483646;
+					opacity: .5;
+					overflow: hidden;					
+					transition: width 200ms ease-in-out;
+				}
+				.${PROGRESSBAR_CONTENT_CLASSNAME} {
+					position: absolute;
+					left: 0;
+					animation: single-file-progress 3s linear infinite reverse;
+					background: 
+						white 
+						linear-gradient(-45deg, rgba(0, 0, 0, 0.075) 25%, 
+							transparent 25%, 
+							transparent 50%, 
+							rgba(0, 0, 0, 0.075) 50%, 
+							rgba(0, 0, 0, 0.075) 75%, 
+							transparent 75%, transparent)
+						repeat scroll 0% 0% / 50px 50px padding-box border-box;
+					width: calc(100% + 50px);
+					height: 100%;					
+				}
+				.${MASK_CONTENT_CLASSNAME} {
+					position: fixed;
+					top: 0;
+					left: 0;
+					width: 100%;
+					height: 100%;
+					z-index: 2147483646;
+					opacity: 0;
+					background-color: black;
+					transition: opacity 250ms;
+				}
+			`;
+			shadowRoot.appendChild(styleElement);
+			let maskElementContent = document.createElement("div");
+			maskElementContent.classList.add(MASK_CONTENT_CLASSNAME);
+			shadowRoot.appendChild(maskElementContent);
+			maskElement.offsetWidth;
+			maskElementContent.style.setProperty("opacity", .3);
 			maskElement.offsetWidth;
 		}
 		return maskElement;
 	}
 
 	function createProgressBarElement(maskElement) {
-		let progressBarElementContainer = document.querySelector(PROGRESS_BAR_TAGNAME);
-		if (!progressBarElementContainer) {
-			progressBarElementContainer = createElement(PROGRESS_BAR_TAGNAME, maskElement);
-			const styleElement = document.createElement("style");
-			styleElement.textContent = "@keyframes single-file-progress { 0% { left: -50px } 100% { left: 0 }";
-			maskElement.appendChild(styleElement);
-			progressBarElementContainer.style.setProperty("position", "fixed", "important");
-			progressBarElementContainer.style.setProperty("top", "0", "important");
-			progressBarElementContainer.style.setProperty("left", "0", "important");
-			progressBarElementContainer.style.setProperty("width", "0", "important");
-			progressBarElementContainer.style.setProperty("height", "8px", "important");
-			progressBarElementContainer.style.setProperty("overflow", "hidden", "important");
-			progressBarElementContainer.style.setProperty("transition", "width 200ms", "important");
-			progressBarElementContainer.style.setProperty("will-change", "width", "important");
-			const progressBarElement = createElement(PROGRESS_CURSOR_TAGNAME, progressBarElementContainer);
-			progressBarElement.style.setProperty("position", "absolute", "important");
-			progressBarElement.style.setProperty("left", "0");
-			progressBarElement.style.setProperty("animation", "single-file-progress 5s linear infinite reverse", "important");
-			progressBarElement.style.setProperty("background", "white linear-gradient(-45deg, rgba(0, 0, 0, 0.1) 25%, transparent 25%, transparent 50%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0.1) 75%, transparent 75%, transparent) repeat scroll 0% 0% / 50px 50px padding-box border-box", "important");
-			progressBarElement.style.setProperty("width", "calc(100% + 50px)", "important");
-			progressBarElement.style.setProperty("height", "100%", "important");
-			progressBarElement.style.setProperty("inset-inline-start", "auto");
+		let progressBarElement = maskElement.shadowRoot.querySelector("." + PROGRESSBAR_CLASSNAME);
+		if (!progressBarElement) {
+			let progressBarContent = document.createElement("div");
+			progressBarContent.classList.add(PROGRESSBAR_CLASSNAME);
+			maskElement.shadowRoot.appendChild(progressBarContent);
+			const progressBarContentElement = document.createElement("div");
+			progressBarContentElement.classList.add(PROGRESSBAR_CONTENT_CLASSNAME);
+			progressBarContent.appendChild(progressBarContentElement);
 		}
-		return progressBarElementContainer;
 	}
 
 	function createLogsWindowElement() {
-		let logsWindowElement = document.querySelector(LOGS_WINDOW_TAGNAME);
+		logsWindowElement = document.querySelector(LOGS_WINDOW_TAGNAME);
 		if (!logsWindowElement) {
-			logsWindowElement = document.createElement(LOGS_WINDOW_TAGNAME);
-			logsWindowElement.className = SINGLE_FILE_UI_ELEMENT_CLASS;
+			logsWindowElement = createElement(LOGS_WINDOW_TAGNAME);
+			const shadowRoot = logsWindowElement.attachShadow({ mode: "open" });
+			const styleElement = document.createElement("style");
+			styleElement.textContent = `
+				@keyframes single-file-pulse { 
+					0% { 
+						opacity: .25;
+					} 
+					100% { 
+						opacity: 1;
+					} 
+				}
+				.${LOGS_CLASSNAME} {
+					position: fixed;
+					bottom: 24px;
+					left: 8px;
+					z-index: 2147483647;
+					opacity: 0.9;
+					padding: 4px;
+					background-color: white;
+					min-width: ${LOG_PANEL_WIDTH}px;
+					min-height: 16px;
+					transition: height 100ms;
+				}
+				.${LOGS_LINE_CLASSNAME} {
+					display: flex;
+					justify-content: space-between;
+					padding: 2px;
+					font-family: arial, sans-serif;
+					color: black;
+					background-color: white;
+				}
+				.${LOGS_LINE_TEXT_ELEMENT_CLASSNAME} {
+					font-size: 13px;
+					opacity: 1;
+					transition: opacity 200ms;
+				}
+				.${LOGS_LINE_STATUS_ELEMENT_CLASSNAME} {
+					font-size: 11px;
+					min-width: 15px;
+					text-align: center;
+					position: relative;
+					top: 1px;
+				}
+			`;
+			shadowRoot.appendChild(styleElement);
+			const logsContentElement = document.createElement("div");
+			logsContentElement.classList.add(LOGS_CLASSNAME);
+			shadowRoot.appendChild(logsContentElement);
 		}
-		const styleElement = document.createElement("style");
-		logsWindowElement.appendChild(styleElement);
-		styleElement.textContent = "@keyframes single-file-pulse { 0% { opacity: .5 } 100% { opacity: 1 }";
-		return logsWindowElement;
-	}
-
-	function setLogsWindowStyle() {
-		initStyle(logsWindowElement);
-		logsWindowElement.style.setProperty("opacity", "0.9", "important");
-		logsWindowElement.style.setProperty("padding", "4px", "important");
-		logsWindowElement.style.setProperty("position", "fixed", "important");
-		logsWindowElement.style.setProperty("bottom", "24px", "important");
-		logsWindowElement.style.setProperty("left", "8px", "important");
-		logsWindowElement.style.setProperty("z-index", 2147483647, "important");
-		logsWindowElement.style.setProperty("background-color", "white", "important");
-		logsWindowElement.style.setProperty("min-width", LOG_PANEL_WIDTH + "px", "important");
-		logsWindowElement.style.setProperty("min-height", "16px", "important");
-		logsWindowElement.style.setProperty("transition", "height 100ms", "important");
-		logsWindowElement.style.setProperty("will-change", "height", "important");
-		logsWindowElement.style.setProperty("inset-block", "auto");
 	}
 
 	function updateLog(id, textContent, textStatus, options) {
 		if (options.logsEnabled) {
-			let lineElement = logsWindowElement.querySelector("[data-id='" + id + "']");
+			const logsContentElement = logsWindowElement.shadowRoot.querySelector("." + LOGS_CLASSNAME);
+			let lineElement = logsContentElement.querySelector("[data-id='" + id + "']");
 			if (!lineElement) {
-				lineElement = createElement(LOGS_LINE_TAGNAME, logsWindowElement);
+				lineElement = document.createElement("div");
+				lineElement.classList.add(LOGS_LINE_CLASSNAME);
+				logsContentElement.appendChild(lineElement);
 				lineElement.setAttribute("data-id", id);
-				lineElement.style.setProperty("display", "flex", "important");
-				lineElement.style.setProperty("justify-content", "space-between", "important");
-				lineElement.style.setProperty("padding", "2px", "important");
-				const textElement = createElement(LOGS_LINE_ELEMENT_TAGNAME, lineElement);
-				textElement.style.setProperty("font-size", "13px", "important");
-				textElement.style.setProperty("font-family", "arial, sans-serif", "important");
-				textElement.style.setProperty("color", "black", "important");
-				textElement.style.setProperty("background-color", "white", "important");
-				textElement.style.setProperty("opacity", "1", "important");
-				textElement.style.setProperty("transition", "opacity 200ms", "important");
+				const textElement = document.createElement("div");
+				textElement.classList.add(LOGS_LINE_TEXT_ELEMENT_CLASSNAME);
+				lineElement.appendChild(textElement);
 				textElement.textContent = textContent;
-				const statusElement = createElement(LOGS_LINE_ELEMENT_TAGNAME, lineElement);
-				statusElement.style.setProperty("font-size", "11px", "important");
-				statusElement.style.setProperty("font-family", "arial, sans-serif", "important");
-				statusElement.style.setProperty("color", "black", "important");
-				statusElement.style.setProperty("background-color", "white", "important");
-				statusElement.style.setProperty("min-width", "15px", "important");
-				statusElement.style.setProperty("text-align", "center", "important");
-				statusElement.style.setProperty("position", "relative", "important");
-				statusElement.style.setProperty("top", "1px", "important");
-				statusElement.style.setProperty("will-change", "opacity", "important");
+				const statusElement = document.createElement("div");
+				statusElement.classList.add(LOGS_LINE_STATUS_ELEMENT_CLASSNAME);
+				lineElement.appendChild(statusElement);
 			}
 			updateLogLine(lineElement, textContent, textStatus);
 		}
@@ -484,19 +531,19 @@ this.singlefile.extension.ui.content.main = this.singlefile.extension.ui.content
 		const textElement = lineElement.childNodes[0];
 		const statusElement = lineElement.childNodes[1];
 		textElement.textContent = textContent;
-		statusElement.style.setProperty("color", textStatus == "✓" ? "#055000" : "black", "important");
+		statusElement.style.setProperty("color", textStatus == "✓" ? "#055000" : "black");
 		if (textStatus == "✓") {
-			textElement.style.setProperty("opacity", ".5", "important");
-			statusElement.style.setProperty("animation", "none", "important");
+			textElement.style.setProperty("opacity", ".5");
+			statusElement.style.setProperty("opacity", ".5");
+			statusElement.style.setProperty("animation", "none");
 		} else {
-			statusElement.style.setProperty("opacity", ".5", "important");
-			statusElement.style.setProperty("animation", "single-file-pulse 1s linear infinite alternate", "important");
+			statusElement.style.setProperty("animation", "1s ease-in-out 0s infinite alternate none running single-file-pulse");
 		}
 		statusElement.textContent = textStatus;
 	}
 
 	function clearLogs() {
-		logsWindowElement = createLogsWindowElement();
+		createLogsWindowElement();
 	}
 
 	function getMatchedParents(target, property) {
@@ -533,12 +580,8 @@ this.singlefile.extension.ui.content.main = this.singlefile.extension.ui.content
 		if (parentElement) {
 			parentElement.appendChild(element);
 		}
-		initStyle(element);
+		CSS_PROPERTIES.forEach(property => element.style.setProperty(property, "initial", "important"));
 		return element;
 	}
 
-	function initStyle(element) {
-		allProperties.forEach(property => element.style.setProperty(property, "initial", "important"));
-	}
-
 })();