/**
* Provides the style editor.
*
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module WoltLabSuite/Core/Acp/Ui/Style/Editor
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Acp/Ui/Style/Editor
*/
-define(['Ajax', 'Core', 'Dictionary', 'Dom/Util', 'EventHandler', 'Ui/Screen'], function (Ajax, Core, Dictionary, DomUtil, EventHandler, UiScreen) {
+define(["require", "exports", "tslib", "../../../Ajax", "../../../Core", "../../../Dom/Util", "../../../Event/Handler", "../../../Ui/Screen"], function (require, exports, tslib_1, Ajax, Core, Util_1, EventHandler, UiScreen) {
"use strict";
- var _stylePreviewRegions = new Dictionary();
- var _stylePreviewRegionMarker = null;
- var _stylePreviewWindow = elById('spWindow');
- var _isVisible = true;
- var _isSmartphone = false;
- var _updateRegionMarker = null;
+ Object.defineProperty(exports, "__esModule", { value: true });
+ exports.showVisualEditor = exports.hideVisualEditor = exports.setup = void 0;
+ Ajax = tslib_1.__importStar(Ajax);
+ Core = tslib_1.__importStar(Core);
+ Util_1 = tslib_1.__importDefault(Util_1);
+ EventHandler = tslib_1.__importStar(EventHandler);
+ UiScreen = tslib_1.__importStar(UiScreen);
+ const _stylePreviewRegions = new Map();
+ let _stylePreviewRegionMarker;
+ const _stylePreviewWindow = document.getElementById("spWindow");
+ let _isVisible = true;
+ let _isSmartphone = false;
+ let _updateRegionMarker;
/**
- * @module WoltLabSuite/Core/Acp/Ui/Style/Editor
+ * Handles the switch between static and fluid layout.
*/
- return {
- /**
- * Sets up dynamic style options.
- */
- setup: function (options) {
- this._handleLayoutWidth();
- this._handleScss(options.isTainted);
- if (!options.isTainted) {
- this._handleProtection(options.styleId);
+ function handleLayoutWidth() {
+ const useFluidLayout = document.getElementById("useFluidLayout");
+ const fluidLayoutMinWidth = document.getElementById("fluidLayoutMinWidth");
+ const fluidLayoutMaxWidth = document.getElementById("fluidLayoutMaxWidth");
+ const fixedLayoutVariables = document.getElementById("fixedLayoutVariables");
+ function change() {
+ if (useFluidLayout.checked) {
+ Util_1.default.show(fluidLayoutMinWidth);
+ Util_1.default.show(fluidLayoutMaxWidth);
+ Util_1.default.hide(fixedLayoutVariables);
}
- this._initVisualEditor(options.styleRuleMap);
- UiScreen.on('screen-sm-down', {
- match: this.hideVisualEditor.bind(this),
- unmatch: this.showVisualEditor.bind(this),
- setup: this.hideVisualEditor.bind(this)
- });
- var callbackRegionMarker = function () {
- if (_isVisible)
- _updateRegionMarker();
- };
- window.addEventListener('resize', callbackRegionMarker);
- EventHandler.add('com.woltlab.wcf.AcpMenu', 'resize', callbackRegionMarker);
- EventHandler.add('com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer', 'select', function (data) {
- _isVisible = (data.activeName === 'colors');
- callbackRegionMarker();
- });
- },
- /**
- * Handles the switch between static and fluid layout.
- */
- _handleLayoutWidth: function () {
- var useFluidLayout = elById('useFluidLayout');
- var fluidLayoutMinWidth = elById('fluidLayoutMinWidth');
- var fluidLayoutMaxWidth = elById('fluidLayoutMaxWidth');
- var fixedLayoutVariables = elById('fixedLayoutVariables');
- function change() {
- var checked = useFluidLayout.checked;
- fluidLayoutMinWidth.style[(checked ? 'remove' : 'set') + 'Property']('display', 'none');
- fluidLayoutMaxWidth.style[(checked ? 'remove' : 'set') + 'Property']('display', 'none');
- fixedLayoutVariables.style[(checked ? 'set' : 'remove') + 'Property']('display', 'none');
+ else {
+ Util_1.default.hide(fluidLayoutMinWidth);
+ Util_1.default.hide(fluidLayoutMaxWidth);
+ Util_1.default.show(fixedLayoutVariables);
}
- useFluidLayout.addEventListener('change', change);
- change();
- },
- /**
- * Handles SCSS input fields.
- *
- * @param {boolean} isTainted false if style is in protected mode
- */
- _handleScss: function (isTainted) {
- var individualScss = elById('individualScss');
- var overrideScss = elById('overrideScss');
- if (isTainted) {
- EventHandler.add('com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer', 'select', function (data) {
+ }
+ useFluidLayout.addEventListener("change", change);
+ change();
+ }
+ /**
+ * Handles SCSS input fields.
+ */
+ function handleScss(isTainted) {
+ const individualScss = document.getElementById("individualScss");
+ const overrideScss = document.getElementById("overrideScss");
+ if (isTainted) {
+ EventHandler.add("com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer", "select", () => {
+ individualScss.codemirror.refresh();
+ overrideScss.codemirror.refresh();
+ });
+ }
+ else {
+ EventHandler.add("com.woltlab.wcf.simpleTabMenu_advanced", "select", (data) => {
+ if (data.activeName === "advanced-custom") {
+ document.getElementById("individualScssCustom").codemirror.refresh();
+ document.getElementById("overrideScssCustom").codemirror.refresh();
+ }
+ else if (data.activeName === "advanced-original") {
individualScss.codemirror.refresh();
overrideScss.codemirror.refresh();
- });
- }
- else {
- EventHandler.add('com.woltlab.wcf.simpleTabMenu_advanced', 'select', function (data) {
- if (data.activeName === 'advanced-custom') {
- elById('individualScssCustom').codemirror.refresh();
- elById('overrideScssCustom').codemirror.refresh();
- }
- else if (data.activeName === 'advanced-original') {
- individualScss.codemirror.refresh();
- overrideScss.codemirror.refresh();
- }
- });
- }
- },
- _handleProtection: function (styleId) {
- var button = elById('styleDisableProtectionSubmit');
- var checkbox = elById('styleDisableProtectionConfirm');
- checkbox.addEventListener('change', function () {
- button.disabled = !checkbox.checked;
+ }
});
- button.addEventListener('click', function () {
- Ajax.apiOnce({
- data: {
- actionName: 'markAsTainted',
- className: 'wcf\\data\\style\\StyleAction',
- objectIDs: [styleId]
- },
- success: function () {
- window.location.reload();
- }
- });
+ }
+ }
+ function handleProtection(styleId) {
+ const button = document.getElementById("styleDisableProtectionSubmit");
+ const checkbox = document.getElementById("styleDisableProtectionConfirm");
+ checkbox.addEventListener("change", () => {
+ button.disabled = !checkbox.checked;
+ });
+ button.addEventListener("click", () => {
+ Ajax.apiOnce({
+ data: {
+ actionName: "markAsTainted",
+ className: "wcf\\data\\style\\StyleAction",
+ objectIDs: [styleId],
+ },
+ success: () => {
+ window.location.reload();
+ },
});
- },
- _initVisualEditor: function (styleRuleMap) {
- elBySelAll('[data-region]', _stylePreviewWindow, function (region) {
- _stylePreviewRegions.set(elData(region, 'region'), region);
+ });
+ }
+ function initVisualEditor(styleRuleMap) {
+ _stylePreviewWindow.querySelectorAll("[data-region]").forEach((region) => {
+ _stylePreviewRegions.set(region.dataset.region, region);
+ });
+ _stylePreviewRegionMarker = document.createElement("div");
+ _stylePreviewRegionMarker.id = "stylePreviewRegionMarker";
+ _stylePreviewRegionMarker.innerHTML = '<div id="stylePreviewRegionMarkerBottom"></div>';
+ Util_1.default.hide(_stylePreviewRegionMarker);
+ document.getElementById("colors").appendChild(_stylePreviewRegionMarker);
+ const container = document.getElementById("spSidebar");
+ const select = document.getElementById("spCategories");
+ let lastValue = select.value;
+ _updateRegionMarker = () => {
+ if (_isSmartphone) {
+ return;
+ }
+ if (lastValue === "none") {
+ Util_1.default.hide(_stylePreviewRegionMarker);
+ return;
+ }
+ const region = _stylePreviewRegions.get(lastValue);
+ const rect = region.getBoundingClientRect();
+ let top = rect.top + (window.scrollY || window.pageYOffset);
+ Util_1.default.setStyles(_stylePreviewRegionMarker, {
+ height: `${region.clientHeight + 20}px`,
+ left: `${rect.left + document.body.scrollLeft - 10}px`,
+ top: `${top - 10}px`,
+ width: `${region.clientWidth + 20}px`,
});
- _stylePreviewRegionMarker = elCreate('div');
- _stylePreviewRegionMarker.id = 'stylePreviewRegionMarker';
- _stylePreviewRegionMarker.innerHTML = '<div id="stylePreviewRegionMarkerBottom"></div>';
- elHide(_stylePreviewRegionMarker);
- elById('colors').appendChild(_stylePreviewRegionMarker);
- var container = elById('spSidebar');
- var select = elById('spCategories');
- var lastValue = select.value;
- _updateRegionMarker = function () {
- if (_isSmartphone) {
- return;
- }
- if (lastValue === 'none') {
- elHide(_stylePreviewRegionMarker);
- return;
- }
- var region = _stylePreviewRegions.get(lastValue);
- var rect = region.getBoundingClientRect();
- var top = rect.top + (window.scrollY || window.pageYOffset);
- DomUtil.setStyles(_stylePreviewRegionMarker, {
- height: (region.clientHeight + 20) + 'px',
- left: (rect.left + document.body.scrollLeft - 10) + 'px',
- top: (top - 10) + 'px',
- width: (region.clientWidth + 20) + 'px'
- });
- elShow(_stylePreviewRegionMarker);
- top = DomUtil.offset(region).top;
- // `+ 80` = account for sticky header + selection markers (20px)
- var firstVisiblePixel = (window.pageYOffset || window.scrollY) + 80;
- if (firstVisiblePixel > top) {
- window.scrollTo(0, Math.max(top - 80, 0));
+ Util_1.default.show(_stylePreviewRegionMarker);
+ top = Util_1.default.offset(region).top;
+ // `+ 80` = account for sticky header + selection markers (20px)
+ const firstVisiblePixel = (window.pageYOffset || window.scrollY) + 80;
+ if (firstVisiblePixel > top) {
+ window.scrollTo(0, Math.max(top - 80, 0));
+ }
+ else {
+ const lastVisiblePixel = window.innerHeight + (window.pageYOffset || window.scrollY);
+ if (lastVisiblePixel < top) {
+ window.scrollTo(0, top);
}
else {
- var lastVisiblePixel = window.innerHeight + (window.pageYOffset || window.scrollY);
- if (lastVisiblePixel < top) {
- window.scrollTo(0, top);
- }
- else {
- var bottom = top + region.offsetHeight + 20;
- if (lastVisiblePixel < bottom) {
- window.scrollBy(0, bottom - top);
- }
+ const bottom = top + region.offsetHeight + 20;
+ if (lastVisiblePixel < bottom) {
+ window.scrollBy(0, bottom - top);
}
}
- };
- var apiVersions = elBySel('.spSidebarBox[data-category="apiVersion"]', container);
- var element;
- var callbackChange = function () {
- element = elBySel('.spSidebarBox[data-category="' + lastValue + '"]', container);
- elHide(element);
- lastValue = select.value;
- element = elBySel('.spSidebarBox[data-category="' + lastValue + '"]', container);
- elShow(element);
- var showCompatibilityNotice = elBySel('.spApiVersion', element) !== null;
- window[showCompatibilityNotice ? 'elShow' : 'elHide'](apiVersions);
- // set region marker
- _updateRegionMarker();
- };
- select.addEventListener('change', callbackChange);
- // apply CSS rules
- var style = elCreate('style');
- style.appendChild(document.createTextNode(''));
- elData(style, 'created-by', 'WoltLab/Acp/Ui/Style/Editor');
- document.head.appendChild(style);
- function updateCSSRule(identifier, value) {
- if (styleRuleMap[identifier] === undefined) {
- return;
- }
- var rule = styleRuleMap[identifier].replace(/VALUE/g, value + ' !important');
- if (!rule) {
- return;
- }
- var rules = [];
- if (rule.indexOf('__COMBO_RULE__')) {
- rules = rule.split('__COMBO_RULE__');
- }
- else {
- rules = [rule];
+ }
+ };
+ const apiVersions = container.querySelector('.spSidebarBox[data-category="apiVersion"]');
+ const callbackChange = () => {
+ let element = container.querySelector(`.spSidebarBox[data-category="${lastValue}"]`);
+ Util_1.default.hide(element);
+ lastValue = select.value;
+ element = container.querySelector(`.spSidebarBox[data-category="${lastValue}"]`);
+ Util_1.default.show(element);
+ const showCompatibilityNotice = element.querySelector(".spApiVersion") !== null;
+ if (showCompatibilityNotice) {
+ Util_1.default.show(apiVersions);
+ }
+ else {
+ Util_1.default.hide(apiVersions);
+ }
+ // set region marker
+ _updateRegionMarker();
+ };
+ select.addEventListener("change", callbackChange);
+ // apply CSS rules
+ const style = document.createElement("style");
+ style.appendChild(document.createTextNode(""));
+ style.dataset.createdBy = "WoltLab/Acp/Ui/Style/Editor";
+ document.head.appendChild(style);
+ function updateCSSRule(identifier, value) {
+ if (styleRuleMap[identifier] === undefined) {
+ return;
+ }
+ const rule = styleRuleMap[identifier].replace(/VALUE/g, value + " !important");
+ if (!rule) {
+ return;
+ }
+ let rules;
+ if (rule.indexOf("__COMBO_RULE__")) {
+ rules = rule.split("__COMBO_RULE__");
+ }
+ else {
+ rules = [rule];
+ }
+ rules.forEach((rule) => {
+ try {
+ style.sheet.insertRule(rule, style.sheet.cssRules.length);
}
- for (var i = 0, length = rules.length; i < length; i++) {
- try {
- style.sheet.insertRule(rules[i], style.sheet.cssRules.length);
- }
- catch (e) {
- // ignore errors for unknown placeholder selectors
- if (!/[a-z]+\-placeholder/.test(rules[i])) {
- console.debug(e.message);
- }
+ catch (e) {
+ // ignore errors for unknown placeholder selectors
+ if (!/[a-z]+-placeholder/.test(rule)) {
+ console.debug(e.message);
}
}
- }
- var elements = elByClass('styleVariableColor', elById('spVariablesWrapper'));
- [].forEach.call(elements, function (colorField) {
- var variableName = elData(colorField, 'store').replace(/_value$/, '');
- var observer = new MutationObserver(function (mutations) {
- mutations.forEach(function (mutation) {
- if (mutation.attributeName === 'style') {
- updateCSSRule(variableName, colorField.style.getPropertyValue('background-color'));
- }
- });
- });
- observer.observe(colorField, {
- attributes: true
- });
- updateCSSRule(variableName, colorField.style.getPropertyValue('background-color'));
- });
- // category selection by clicking on the area
- var buttonToggleColorPalette = elBySel('.jsButtonToggleColorPalette');
- var buttonSelectCategoryByClick = elBySel('.jsButtonSelectCategoryByClick');
- var toggleSelectionMode = function () {
- buttonSelectCategoryByClick.classList.toggle('active');
- buttonToggleColorPalette.classList.toggle('disabled');
- _stylePreviewWindow.classList.toggle('spShowRegions');
- _stylePreviewRegionMarker.classList.toggle('forceHide');
- select.disabled = !select.disabled;
- };
- buttonSelectCategoryByClick.addEventListener('click', function (event) {
- event.preventDefault();
- toggleSelectionMode();
});
- elBySelAll('[data-region]', _stylePreviewWindow, function (region) {
- region.addEventListener('click', function (event) {
- if (!_stylePreviewWindow.classList.contains('spShowRegions')) {
- return;
+ }
+ const wrapper = document.getElementById("spVariablesWrapper");
+ wrapper.querySelectorAll(".styleVariableColor").forEach((colorField) => {
+ const variableName = colorField.dataset.store.replace(/_value$/, "");
+ const observer = new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ if (mutation.attributeName === "style") {
+ updateCSSRule(variableName, colorField.style.getPropertyValue("background-color"));
}
- event.preventDefault();
- event.stopPropagation();
- toggleSelectionMode();
- select.value = elData(region, 'region');
- // Programmatically trigger the change event handler, rather than dispatching an event,
- // because Firefox fails to execute the event if it has previously been disabled.
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=1426856
- callbackChange();
});
});
- // toggle view
- var spSelectCategory = elById('spSelectCategory');
- buttonToggleColorPalette.addEventListener('click', function (event) {
+ observer.observe(colorField, {
+ attributes: true,
+ });
+ updateCSSRule(variableName, colorField.style.getPropertyValue("background-color"));
+ });
+ // category selection by clicking on the area
+ const buttonToggleColorPalette = document.querySelector(".jsButtonToggleColorPalette");
+ const buttonSelectCategoryByClick = document.querySelector(".jsButtonSelectCategoryByClick");
+ function toggleSelectionMode() {
+ buttonSelectCategoryByClick.classList.toggle("active");
+ buttonToggleColorPalette.classList.toggle("disabled");
+ _stylePreviewWindow.classList.toggle("spShowRegions");
+ _stylePreviewRegionMarker.classList.toggle("forceHide");
+ select.disabled = !select.disabled;
+ }
+ buttonSelectCategoryByClick.addEventListener("click", (event) => {
+ event.preventDefault();
+ toggleSelectionMode();
+ });
+ _stylePreviewWindow.querySelectorAll("[data-region]").forEach((region) => {
+ region.addEventListener("click", (event) => {
+ if (!_stylePreviewWindow.classList.contains("spShowRegions")) {
+ return;
+ }
event.preventDefault();
- buttonSelectCategoryByClick.classList.toggle('disabled');
- elToggle(spSelectCategory);
- buttonToggleColorPalette.classList.toggle('active');
- _stylePreviewWindow.classList.toggle('spColorPalette');
- _stylePreviewRegionMarker.classList.toggle('forceHide');
- select.disabled = !select.disabled;
+ event.stopPropagation();
+ toggleSelectionMode();
+ select.value = region.dataset.region;
+ // Programmatically trigger the change event handler, rather than dispatching an event,
+ // because Firefox fails to execute the event if it has previously been disabled.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1426856
+ callbackChange();
});
- },
- hideVisualEditor: function () {
- elHide(_stylePreviewWindow);
- elById('spVariablesWrapper').style.removeProperty('transform');
- elHide(elById('stylePreviewRegionMarker'));
- _isSmartphone = true;
- },
- showVisualEditor: function () {
- elShow(_stylePreviewWindow);
- window.setTimeout(function () {
- Core.triggerEvent(elById('spCategories'), 'change');
- }, 100);
- _isSmartphone = false;
+ });
+ // toggle view
+ const spSelectCategory = document.getElementById("spSelectCategory");
+ buttonToggleColorPalette.addEventListener("click", (event) => {
+ event.preventDefault();
+ buttonSelectCategoryByClick.classList.toggle("disabled");
+ Util_1.default.toggle(spSelectCategory);
+ buttonToggleColorPalette.classList.toggle("active");
+ _stylePreviewWindow.classList.toggle("spColorPalette");
+ _stylePreviewRegionMarker.classList.toggle("forceHide");
+ select.disabled = !select.disabled;
+ });
+ }
+ /**
+ * Sets up dynamic style options.
+ */
+ function setup(options) {
+ handleLayoutWidth();
+ handleScss(options.isTainted);
+ if (!options.isTainted) {
+ handleProtection(options.styleId);
+ }
+ initVisualEditor(options.styleRuleMap);
+ UiScreen.on("screen-sm-down", {
+ match() {
+ hideVisualEditor();
+ },
+ unmatch() {
+ showVisualEditor();
+ },
+ setup() {
+ hideVisualEditor();
+ },
+ });
+ function callbackRegionMarker() {
+ if (_isVisible) {
+ _updateRegionMarker();
+ }
}
- };
+ window.addEventListener("resize", callbackRegionMarker);
+ EventHandler.add("com.woltlab.wcf.AcpMenu", "resize", callbackRegionMarker);
+ EventHandler.add("com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer", "select", function (data) {
+ _isVisible = data.activeName === "colors";
+ callbackRegionMarker();
+ });
+ }
+ exports.setup = setup;
+ function hideVisualEditor() {
+ Util_1.default.hide(_stylePreviewWindow);
+ document.getElementById("spVariablesWrapper").style.removeProperty("transform");
+ Util_1.default.hide(document.getElementById("stylePreviewRegionMarker"));
+ _isSmartphone = true;
+ }
+ exports.hideVisualEditor = hideVisualEditor;
+ function showVisualEditor() {
+ Util_1.default.show(_stylePreviewWindow);
+ window.setTimeout(() => {
+ Core.triggerEvent(document.getElementById("spCategories"), "change");
+ }, 100);
+ _isSmartphone = false;
+ }
+ exports.showVisualEditor = showVisualEditor;
});
isHidden(element) {
return element.style.getPropertyValue("display") === "none";
},
+ /**
+ * Shorthand function to toggle the element visibility using either `hide()` or `show()`.
+ */
+ toggle(element) {
+ if (this.isHidden(element)) {
+ this.show(element);
+ }
+ else {
+ this.hide(element);
+ }
+ },
/**
* Displays or removes an error message below the provided element.
*/
+++ /dev/null
-/**
- * Provides the style editor.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module WoltLabSuite/Core/Acp/Ui/Style/Editor
- */
-define(['Ajax', 'Core', 'Dictionary', 'Dom/Util', 'EventHandler', 'Ui/Screen'], function(Ajax, Core, Dictionary, DomUtil, EventHandler, UiScreen) {
- "use strict";
-
- var _stylePreviewRegions = new Dictionary();
- var _stylePreviewRegionMarker = null;
- var _stylePreviewWindow = elById('spWindow');
-
- var _isVisible = true;
- var _isSmartphone = false;
- var _updateRegionMarker = null;
-
- /**
- * @module WoltLabSuite/Core/Acp/Ui/Style/Editor
- */
- return {
- /**
- * Sets up dynamic style options.
- */
- setup: function(options) {
- this._handleLayoutWidth();
- this._handleScss(options.isTainted);
-
- if (!options.isTainted) {
- this._handleProtection(options.styleId);
- }
-
- this._initVisualEditor(options.styleRuleMap);
-
- UiScreen.on('screen-sm-down', {
- match: this.hideVisualEditor.bind(this),
- unmatch: this.showVisualEditor.bind(this),
- setup: this.hideVisualEditor.bind(this)
- });
-
- var callbackRegionMarker = function () {
- if (_isVisible) _updateRegionMarker();
- };
- window.addEventListener('resize', callbackRegionMarker);
- EventHandler.add('com.woltlab.wcf.AcpMenu', 'resize', callbackRegionMarker);
- EventHandler.add('com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer', 'select', function (data) {
- _isVisible = (data.activeName === 'colors');
- callbackRegionMarker();
- });
- },
-
- /**
- * Handles the switch between static and fluid layout.
- */
- _handleLayoutWidth: function() {
- var useFluidLayout = elById('useFluidLayout');
- var fluidLayoutMinWidth = elById('fluidLayoutMinWidth');
- var fluidLayoutMaxWidth = elById('fluidLayoutMaxWidth');
- var fixedLayoutVariables = elById('fixedLayoutVariables');
-
- function change() {
- var checked = useFluidLayout.checked;
-
- fluidLayoutMinWidth.style[(checked ? 'remove' : 'set') + 'Property']('display', 'none');
- fluidLayoutMaxWidth.style[(checked ? 'remove' : 'set') + 'Property']('display', 'none');
- fixedLayoutVariables.style[(checked ? 'set' : 'remove') + 'Property']('display', 'none');
- }
-
- useFluidLayout.addEventListener('change', change);
-
- change();
- },
-
- /**
- * Handles SCSS input fields.
- *
- * @param {boolean} isTainted false if style is in protected mode
- */
- _handleScss: function(isTainted) {
- var individualScss = elById('individualScss');
- var overrideScss = elById('overrideScss');
-
- if (isTainted) {
- EventHandler.add('com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer', 'select', function(data) {
- individualScss.codemirror.refresh();
- overrideScss.codemirror.refresh();
- });
- }
- else {
- EventHandler.add('com.woltlab.wcf.simpleTabMenu_advanced', 'select', function(data) {
- if (data.activeName === 'advanced-custom') {
- elById('individualScssCustom').codemirror.refresh();
- elById('overrideScssCustom').codemirror.refresh();
- }
- else if (data.activeName === 'advanced-original') {
- individualScss.codemirror.refresh();
- overrideScss.codemirror.refresh();
- }
- });
- }
- },
-
- _handleProtection: function(styleId) {
- var button = elById('styleDisableProtectionSubmit');
- var checkbox = elById('styleDisableProtectionConfirm');
-
- checkbox.addEventListener('change', function() {
- button.disabled = !checkbox.checked;
- });
-
- button.addEventListener('click', function() {
- Ajax.apiOnce({
- data: {
- actionName: 'markAsTainted',
- className: 'wcf\\data\\style\\StyleAction',
- objectIDs: [styleId]
- },
- success: function() {
- window.location.reload();
- }
- });
- });
- },
-
- _initVisualEditor: function(styleRuleMap) {
- elBySelAll('[data-region]', _stylePreviewWindow, function(region) {
- _stylePreviewRegions.set(elData(region, 'region'), region);
- });
-
- _stylePreviewRegionMarker = elCreate('div');
- _stylePreviewRegionMarker.id = 'stylePreviewRegionMarker';
- _stylePreviewRegionMarker.innerHTML = '<div id="stylePreviewRegionMarkerBottom"></div>';
- elHide(_stylePreviewRegionMarker);
- elById('colors').appendChild(_stylePreviewRegionMarker);
-
- var container = elById('spSidebar');
- var select = elById('spCategories');
- var lastValue = select.value;
-
- _updateRegionMarker = function() {
- if (_isSmartphone) {
- return;
- }
-
- if (lastValue === 'none') {
- elHide(_stylePreviewRegionMarker);
- return;
- }
-
- var region = _stylePreviewRegions.get(lastValue);
- var rect = region.getBoundingClientRect();
-
- var top = rect.top + (window.scrollY || window.pageYOffset);
-
- DomUtil.setStyles(_stylePreviewRegionMarker, {
- height: (region.clientHeight + 20) + 'px',
- left: (rect.left + document.body.scrollLeft - 10) + 'px',
- top: (top - 10) + 'px',
- width: (region.clientWidth + 20) + 'px'
- });
-
- elShow(_stylePreviewRegionMarker);
-
- top = DomUtil.offset(region).top;
- // `+ 80` = account for sticky header + selection markers (20px)
- var firstVisiblePixel = (window.pageYOffset || window.scrollY) + 80;
- if (firstVisiblePixel > top) {
- window.scrollTo(0, Math.max(top - 80, 0));
- }
- else {
- var lastVisiblePixel = window.innerHeight + (window.pageYOffset || window.scrollY);
- if (lastVisiblePixel < top) {
- window.scrollTo(0, top);
- }
- else {
- var bottom = top + region.offsetHeight + 20;
- if (lastVisiblePixel < bottom) {
- window.scrollBy(0, bottom - top);
- }
- }
- }
- };
-
- var apiVersions = elBySel('.spSidebarBox[data-category="apiVersion"]', container);
- var element;
- var callbackChange = function() {
- element = elBySel('.spSidebarBox[data-category="' + lastValue + '"]', container);
- elHide(element);
-
- lastValue = select.value;
- element = elBySel('.spSidebarBox[data-category="' + lastValue + '"]', container);
- elShow(element);
-
- var showCompatibilityNotice = elBySel('.spApiVersion', element) !== null;
- window[showCompatibilityNotice ? 'elShow' : 'elHide'](apiVersions);
-
- // set region marker
- _updateRegionMarker();
- };
- select.addEventListener('change', callbackChange);
-
- // apply CSS rules
- var style = elCreate('style');
- style.appendChild(document.createTextNode(''));
- elData(style, 'created-by', 'WoltLab/Acp/Ui/Style/Editor');
- document.head.appendChild(style);
-
- function updateCSSRule(identifier, value) {
- if (styleRuleMap[identifier] === undefined) {
- return;
- }
-
- var rule = styleRuleMap[identifier].replace(/VALUE/g, value + ' !important');
- if (!rule) {
- return;
- }
-
- var rules = [];
- if (rule.indexOf('__COMBO_RULE__')) {
- rules = rule.split('__COMBO_RULE__');
- }
- else {
- rules = [rule];
- }
-
- for (var i = 0, length = rules.length; i < length; i++) {
- try {
- style.sheet.insertRule(rules[i], style.sheet.cssRules.length);
- }
- catch (e) {
- // ignore errors for unknown placeholder selectors
- if (!/[a-z]+\-placeholder/.test(rules[i])) {
- console.debug(e.message);
- }
- }
- }
- }
-
- var elements = elByClass('styleVariableColor', elById('spVariablesWrapper'));
- [].forEach.call(elements, function(colorField) {
- var variableName = elData(colorField, 'store').replace(/_value$/, '');
-
- var observer = new MutationObserver(function(mutations) {
- mutations.forEach(function(mutation) {
- if (mutation.attributeName === 'style') {
- updateCSSRule(variableName, colorField.style.getPropertyValue('background-color'));
- }
- });
- });
-
- observer.observe(colorField, {
- attributes: true
- });
-
- updateCSSRule(variableName, colorField.style.getPropertyValue('background-color'));
- });
-
- // category selection by clicking on the area
- var buttonToggleColorPalette = elBySel('.jsButtonToggleColorPalette');
- var buttonSelectCategoryByClick = elBySel('.jsButtonSelectCategoryByClick');
- var toggleSelectionMode = function() {
- buttonSelectCategoryByClick.classList.toggle('active');
- buttonToggleColorPalette.classList.toggle('disabled');
- _stylePreviewWindow.classList.toggle('spShowRegions');
- _stylePreviewRegionMarker.classList.toggle('forceHide');
- select.disabled = !select.disabled;
- };
-
- buttonSelectCategoryByClick.addEventListener('click', function (event) {
- event.preventDefault();
-
- toggleSelectionMode();
- });
-
- elBySelAll('[data-region]', _stylePreviewWindow, function (region) {
- region.addEventListener('click', function (event) {
- if (!_stylePreviewWindow.classList.contains('spShowRegions')) {
- return;
- }
-
- event.preventDefault();
- event.stopPropagation();
-
- toggleSelectionMode();
-
- select.value = elData(region, 'region');
-
- // Programmatically trigger the change event handler, rather than dispatching an event,
- // because Firefox fails to execute the event if it has previously been disabled.
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=1426856
- callbackChange();
- });
- });
-
- // toggle view
- var spSelectCategory = elById('spSelectCategory');
- buttonToggleColorPalette.addEventListener('click', function (event) {
- event.preventDefault();
-
- buttonSelectCategoryByClick.classList.toggle('disabled');
- elToggle(spSelectCategory);
- buttonToggleColorPalette.classList.toggle('active');
- _stylePreviewWindow.classList.toggle('spColorPalette');
- _stylePreviewRegionMarker.classList.toggle('forceHide');
- select.disabled = !select.disabled;
- });
- },
-
- hideVisualEditor: function() {
- elHide(_stylePreviewWindow);
- elById('spVariablesWrapper').style.removeProperty('transform');
- elHide(elById('stylePreviewRegionMarker'));
-
- _isSmartphone = true;
- },
-
- showVisualEditor: function() {
- elShow(_stylePreviewWindow);
-
- window.setTimeout(function() {
- Core.triggerEvent(elById('spCategories'), 'change');
- }, 100);
-
- _isSmartphone = false;
- }
- };
-});
--- /dev/null
+/**
+ * Provides the style editor.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLabSuite/Core/Acp/Ui/Style/Editor
+ */
+
+import * as Ajax from "../../../Ajax";
+import * as Core from "../../../Core";
+import DomUtil from "../../../Dom/Util";
+import * as EventHandler from "../../../Event/Handler";
+import * as UiScreen from "../../../Ui/Screen";
+
+const _stylePreviewRegions = new Map<string, HTMLElement>();
+let _stylePreviewRegionMarker: HTMLElement;
+const _stylePreviewWindow = document.getElementById("spWindow")!;
+
+let _isVisible = true;
+let _isSmartphone = false;
+let _updateRegionMarker: () => void;
+
+interface StyleRuleMap {
+ [key: string]: string;
+}
+
+interface StyleEditorOptions {
+ isTainted: boolean;
+ styleId: number;
+ styleRuleMap: StyleRuleMap;
+}
+
+/**
+ * Handles the switch between static and fluid layout.
+ */
+function handleLayoutWidth(): void {
+ const useFluidLayout = document.getElementById("useFluidLayout") as HTMLInputElement;
+ const fluidLayoutMinWidth = document.getElementById("fluidLayoutMinWidth") as HTMLInputElement;
+ const fluidLayoutMaxWidth = document.getElementById("fluidLayoutMaxWidth") as HTMLInputElement;
+ const fixedLayoutVariables = document.getElementById("fixedLayoutVariables") as HTMLDListElement;
+
+ function change(): void {
+ if (useFluidLayout.checked) {
+ DomUtil.show(fluidLayoutMinWidth);
+ DomUtil.show(fluidLayoutMaxWidth);
+ DomUtil.hide(fixedLayoutVariables);
+ } else {
+ DomUtil.hide(fluidLayoutMinWidth);
+ DomUtil.hide(fluidLayoutMaxWidth);
+ DomUtil.show(fixedLayoutVariables);
+ }
+ }
+
+ useFluidLayout.addEventListener("change", change);
+
+ change();
+}
+
+/**
+ * Handles SCSS input fields.
+ */
+function handleScss(isTainted: boolean): void {
+ const individualScss = document.getElementById("individualScss")!;
+ const overrideScss = document.getElementById("overrideScss")!;
+
+ if (isTainted) {
+ EventHandler.add("com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer", "select", () => {
+ (individualScss as any).codemirror.refresh();
+ (overrideScss as any).codemirror.refresh();
+ });
+ } else {
+ EventHandler.add("com.woltlab.wcf.simpleTabMenu_advanced", "select", (data: { activeName: string }) => {
+ if (data.activeName === "advanced-custom") {
+ (document.getElementById("individualScssCustom") as any).codemirror.refresh();
+ (document.getElementById("overrideScssCustom") as any).codemirror.refresh();
+ } else if (data.activeName === "advanced-original") {
+ (individualScss as any).codemirror.refresh();
+ (overrideScss as any).codemirror.refresh();
+ }
+ });
+ }
+}
+
+function handleProtection(styleId: number): void {
+ const button = document.getElementById("styleDisableProtectionSubmit") as HTMLButtonElement;
+ const checkbox = document.getElementById("styleDisableProtectionConfirm") as HTMLInputElement;
+
+ checkbox.addEventListener("change", () => {
+ button.disabled = !checkbox.checked;
+ });
+
+ button.addEventListener("click", () => {
+ Ajax.apiOnce({
+ data: {
+ actionName: "markAsTainted",
+ className: "wcf\\data\\style\\StyleAction",
+ objectIDs: [styleId],
+ },
+ success: () => {
+ window.location.reload();
+ },
+ });
+ });
+}
+
+function initVisualEditor(styleRuleMap: StyleRuleMap): void {
+ _stylePreviewWindow.querySelectorAll("[data-region]").forEach((region: HTMLElement) => {
+ _stylePreviewRegions.set(region.dataset.region!, region);
+ });
+
+ _stylePreviewRegionMarker = document.createElement("div");
+ _stylePreviewRegionMarker.id = "stylePreviewRegionMarker";
+ _stylePreviewRegionMarker.innerHTML = '<div id="stylePreviewRegionMarkerBottom"></div>';
+ DomUtil.hide(_stylePreviewRegionMarker);
+ document.getElementById("colors")!.appendChild(_stylePreviewRegionMarker);
+
+ const container = document.getElementById("spSidebar")!;
+ const select = document.getElementById("spCategories") as HTMLSelectElement;
+ let lastValue = select.value;
+
+ _updateRegionMarker = (): void => {
+ if (_isSmartphone) {
+ return;
+ }
+
+ if (lastValue === "none") {
+ DomUtil.hide(_stylePreviewRegionMarker);
+ return;
+ }
+
+ const region = _stylePreviewRegions.get(lastValue)!;
+ const rect = region.getBoundingClientRect();
+
+ let top = rect.top + (window.scrollY || window.pageYOffset);
+
+ DomUtil.setStyles(_stylePreviewRegionMarker, {
+ height: `${region.clientHeight + 20}px`,
+ left: `${rect.left + document.body.scrollLeft - 10}px`,
+ top: `${top - 10}px`,
+ width: `${region.clientWidth + 20}px`,
+ });
+
+ DomUtil.show(_stylePreviewRegionMarker);
+
+ top = DomUtil.offset(region).top;
+ // `+ 80` = account for sticky header + selection markers (20px)
+ const firstVisiblePixel = (window.pageYOffset || window.scrollY) + 80;
+ if (firstVisiblePixel > top) {
+ window.scrollTo(0, Math.max(top - 80, 0));
+ } else {
+ const lastVisiblePixel = window.innerHeight + (window.pageYOffset || window.scrollY);
+ if (lastVisiblePixel < top) {
+ window.scrollTo(0, top);
+ } else {
+ const bottom = top + region.offsetHeight + 20;
+ if (lastVisiblePixel < bottom) {
+ window.scrollBy(0, bottom - top);
+ }
+ }
+ }
+ };
+
+ const apiVersions = container.querySelector('.spSidebarBox[data-category="apiVersion"]') as HTMLElement;
+ const callbackChange = () => {
+ let element = container.querySelector(`.spSidebarBox[data-category="${lastValue}"]`) as HTMLElement;
+ DomUtil.hide(element);
+
+ lastValue = select.value;
+ element = container.querySelector(`.spSidebarBox[data-category="${lastValue}"]`) as HTMLElement;
+ DomUtil.show(element);
+
+ const showCompatibilityNotice = element.querySelector(".spApiVersion") !== null;
+ if (showCompatibilityNotice) {
+ DomUtil.show(apiVersions);
+ } else {
+ DomUtil.hide(apiVersions);
+ }
+
+ // set region marker
+ _updateRegionMarker();
+ };
+ select.addEventListener("change", callbackChange);
+
+ // apply CSS rules
+ const style = document.createElement("style");
+ style.appendChild(document.createTextNode(""));
+ style.dataset.createdBy = "WoltLab/Acp/Ui/Style/Editor";
+ document.head.appendChild(style);
+
+ function updateCSSRule(identifier: string, value: string): void {
+ if (styleRuleMap[identifier] === undefined) {
+ return;
+ }
+
+ const rule = styleRuleMap[identifier].replace(/VALUE/g, value + " !important");
+ if (!rule) {
+ return;
+ }
+
+ let rules: string[];
+ if (rule.indexOf("__COMBO_RULE__")) {
+ rules = rule.split("__COMBO_RULE__");
+ } else {
+ rules = [rule];
+ }
+
+ rules.forEach((rule) => {
+ try {
+ style.sheet!.insertRule(rule, style.sheet!.cssRules.length);
+ } catch (e) {
+ // ignore errors for unknown placeholder selectors
+ if (!/[a-z]+-placeholder/.test(rule)) {
+ console.debug(e.message);
+ }
+ }
+ });
+ }
+
+ const wrapper = document.getElementById("spVariablesWrapper")!;
+ wrapper.querySelectorAll(".styleVariableColor").forEach((colorField: HTMLElement) => {
+ const variableName = colorField.dataset.store!.replace(/_value$/, "");
+
+ const observer = new MutationObserver((mutations) => {
+ mutations.forEach((mutation) => {
+ if (mutation.attributeName === "style") {
+ updateCSSRule(variableName, colorField.style.getPropertyValue("background-color"));
+ }
+ });
+ });
+
+ observer.observe(colorField, {
+ attributes: true,
+ });
+
+ updateCSSRule(variableName, colorField.style.getPropertyValue("background-color"));
+ });
+
+ // category selection by clicking on the area
+ const buttonToggleColorPalette = document.querySelector(".jsButtonToggleColorPalette") as HTMLAnchorElement;
+ const buttonSelectCategoryByClick = document.querySelector(".jsButtonSelectCategoryByClick") as HTMLAnchorElement;
+
+ function toggleSelectionMode(): void {
+ buttonSelectCategoryByClick.classList.toggle("active");
+ buttonToggleColorPalette.classList.toggle("disabled");
+ _stylePreviewWindow.classList.toggle("spShowRegions");
+ _stylePreviewRegionMarker.classList.toggle("forceHide");
+ select.disabled = !select.disabled;
+ }
+
+ buttonSelectCategoryByClick.addEventListener("click", (event) => {
+ event.preventDefault();
+
+ toggleSelectionMode();
+ });
+
+ _stylePreviewWindow.querySelectorAll("[data-region]").forEach((region: HTMLElement) => {
+ region.addEventListener("click", (event) => {
+ if (!_stylePreviewWindow.classList.contains("spShowRegions")) {
+ return;
+ }
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ toggleSelectionMode();
+
+ select.value = region.dataset.region!;
+
+ // Programmatically trigger the change event handler, rather than dispatching an event,
+ // because Firefox fails to execute the event if it has previously been disabled.
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1426856
+ callbackChange();
+ });
+ });
+
+ // toggle view
+ const spSelectCategory = document.getElementById("spSelectCategory") as HTMLSelectElement;
+ buttonToggleColorPalette.addEventListener("click", (event) => {
+ event.preventDefault();
+
+ buttonSelectCategoryByClick.classList.toggle("disabled");
+ DomUtil.toggle(spSelectCategory);
+ buttonToggleColorPalette.classList.toggle("active");
+ _stylePreviewWindow.classList.toggle("spColorPalette");
+ _stylePreviewRegionMarker.classList.toggle("forceHide");
+ select.disabled = !select.disabled;
+ });
+}
+
+/**
+ * Sets up dynamic style options.
+ */
+export function setup(options: StyleEditorOptions): void {
+ handleLayoutWidth();
+ handleScss(options.isTainted);
+
+ if (!options.isTainted) {
+ handleProtection(options.styleId);
+ }
+
+ initVisualEditor(options.styleRuleMap);
+
+ UiScreen.on("screen-sm-down", {
+ match() {
+ hideVisualEditor();
+ },
+ unmatch() {
+ showVisualEditor();
+ },
+ setup() {
+ hideVisualEditor();
+ },
+ });
+
+ function callbackRegionMarker(): void {
+ if (_isVisible) {
+ _updateRegionMarker();
+ }
+ }
+
+ window.addEventListener("resize", callbackRegionMarker);
+ EventHandler.add("com.woltlab.wcf.AcpMenu", "resize", callbackRegionMarker);
+ EventHandler.add("com.woltlab.wcf.simpleTabMenu_styleTabMenuContainer", "select", function (data) {
+ _isVisible = data.activeName === "colors";
+ callbackRegionMarker();
+ });
+}
+
+export function hideVisualEditor(): void {
+ DomUtil.hide(_stylePreviewWindow);
+ document.getElementById("spVariablesWrapper")!.style.removeProperty("transform");
+ DomUtil.hide(document.getElementById("stylePreviewRegionMarker")!);
+
+ _isSmartphone = true;
+}
+
+export function showVisualEditor(): void {
+ DomUtil.show(_stylePreviewWindow);
+
+ window.setTimeout(() => {
+ Core.triggerEvent(document.getElementById("spCategories")!, "change");
+ }, 100);
+
+ _isSmartphone = false;
+}
return element.style.getPropertyValue("display") === "none";
},
+ /**
+ * Shorthand function to toggle the element visibility using either `hide()` or `show()`.
+ */
+ toggle(element: HTMLElement): void {
+ if (this.isHidden(element)) {
+ this.show(element);
+ } else {
+ this.hide(element);
+ }
+ },
+
/**
* Displays or removes an error message below the provided element.
*/