Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
223 lines
6.3 KiB
JavaScript
223 lines
6.3 KiB
JavaScript
import PluginBase from '../abstracts/PluginBase';
|
|
|
|
/**
|
|
* Data configuration provider.
|
|
*
|
|
* Provides a mechanism for passing configuration data through an element's data attributes. This
|
|
* is generally used for widgets or UI interactions to configure them.
|
|
*
|
|
* @copyright 2022 Winter.
|
|
* @author Ben Thomson <git@alfreido.com>
|
|
*/
|
|
export default class DataConfig extends PluginBase {
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param {PluginBase} instance
|
|
* @param {HTMLElement} element
|
|
* @param {Object} localConfig
|
|
*/
|
|
construct(instance, element, localConfig) {
|
|
if (instance instanceof PluginBase === false) {
|
|
throw new Error('You must provide a Snowboard plugin to enable data configuration');
|
|
}
|
|
if (element instanceof HTMLElement === false) {
|
|
throw new Error('Data configuration can only be extracted from HTML elements');
|
|
}
|
|
|
|
this.instance = instance;
|
|
this.element = element;
|
|
this.localConfig = localConfig || {};
|
|
this.instanceConfig = {};
|
|
this.acceptedConfigs = {};
|
|
this.refresh();
|
|
}
|
|
|
|
/**
|
|
* Gets the config for this instance.
|
|
*
|
|
* If the `config` parameter is unspecified, returns the entire configuration.
|
|
*
|
|
* @param {string} config
|
|
*/
|
|
get(config) {
|
|
if (config === undefined) {
|
|
return this.instanceConfig;
|
|
}
|
|
|
|
if (this.instanceConfig[config] !== undefined) {
|
|
return this.instanceConfig[config];
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Sets the config for this instance.
|
|
*
|
|
* This allows you to override, at runtime, any configuration value as necessary.
|
|
*
|
|
* @param {string} config
|
|
* @param {any} value
|
|
* @param {boolean} persist
|
|
*/
|
|
set(config, value, persist) {
|
|
if (config === undefined) {
|
|
throw new Error('You must provide a configuration key to set');
|
|
}
|
|
|
|
this.instanceConfig[config] = value;
|
|
|
|
if (persist === true) {
|
|
this.element.dataset[config] = value;
|
|
this.localConfig[config] = value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Refreshes the configuration from the element.
|
|
*
|
|
* This will allow you to make changes to the data config on a DOM level and re-apply them
|
|
* to the config on the JavaScript side.
|
|
*/
|
|
refresh() {
|
|
this.acceptedConfigs = this.getAcceptedConfigs();
|
|
this.instanceConfig = this.processConfig();
|
|
}
|
|
|
|
/**
|
|
* Determines the available configurations that can be set through the data config.
|
|
*
|
|
* If an instance has an `acceptAllDataConfigs` property, set to `true`, then all data
|
|
* attributes will be available as configuration values. This can be a security concern, so
|
|
* tread carefully.
|
|
*
|
|
* Otherwise, available configurations will be determined by the keys available in an object
|
|
* returned by a `defaults()` method in the instance.
|
|
*
|
|
* @returns {string[]|boolean}
|
|
*/
|
|
getAcceptedConfigs() {
|
|
if (
|
|
this.instance.acceptAllDataConfigs !== undefined
|
|
&& this.instance.acceptAllDataConfigs === true
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
if (
|
|
this.instance.defaults !== undefined
|
|
&& typeof this.instance.defaults === 'function'
|
|
&& typeof this.instance.defaults() === 'object'
|
|
) {
|
|
return Object.keys(this.instance.defaults());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the default values for the instance.
|
|
*
|
|
* This will be an empty object if the instance either does not have a `defaults()` method, or
|
|
* the method itself does not return an object.
|
|
*
|
|
* @returns {object}
|
|
*/
|
|
getDefaults() {
|
|
if (
|
|
this.instance.defaults !== undefined
|
|
&& typeof this.instance.defaults === 'function'
|
|
&& typeof this.instance.defaults() === 'object'
|
|
) {
|
|
return this.instance.defaults();
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* Processes the configuration.
|
|
*
|
|
* Loads up the defaults, then populates it with any configuration values provided by the data
|
|
* attributes, based on the rules of the accepted configurations.
|
|
*
|
|
* This configuration object is then cached and available through `config.get()` calls.
|
|
*
|
|
* @returns {object}
|
|
*/
|
|
processConfig() {
|
|
const config = this.getDefaults();
|
|
|
|
if (this.acceptedConfigs === false) {
|
|
return config;
|
|
}
|
|
|
|
/* eslint-disable */
|
|
for (const key in this.element.dataset) {
|
|
if (this.acceptedConfigs === true || this.acceptedConfigs.includes(key)) {
|
|
config[key] = this.coerceValue(this.element.dataset[key]);
|
|
}
|
|
}
|
|
|
|
for (const key in this.localConfig) {
|
|
if (this.acceptedConfigs === true || this.acceptedConfigs.includes(key)) {
|
|
config[key] = this.localConfig[key];
|
|
}
|
|
}
|
|
/* eslint-enable */
|
|
|
|
return config;
|
|
}
|
|
|
|
/**
|
|
* Coerces configuration values for JavaScript.
|
|
*
|
|
* Takes the string value returned from the data attribute and coerces it into a more suitable
|
|
* type for JavaScript processing.
|
|
*
|
|
* @param {*} value
|
|
* @returns {*}
|
|
*/
|
|
coerceValue(value) {
|
|
const stringValue = String(value);
|
|
|
|
// Null value
|
|
if (stringValue === 'null') {
|
|
return null;
|
|
}
|
|
|
|
// Undefined value
|
|
if (stringValue === 'undefined') {
|
|
return undefined;
|
|
}
|
|
|
|
// Base64 value
|
|
if (stringValue.startsWith('base64:')) {
|
|
const base64str = stringValue.replace(/^base64:/, '');
|
|
const decoded = atob(base64str);
|
|
return this.coerceValue(decoded);
|
|
}
|
|
|
|
// Boolean value
|
|
if (['true', 'yes'].includes(stringValue.toLowerCase())) {
|
|
return true;
|
|
}
|
|
if (['false', 'no'].includes(stringValue.toLowerCase())) {
|
|
return false;
|
|
}
|
|
|
|
// Numeric value
|
|
if (/^[-+]?[0-9]+(\.[0-9]+)?$/.test(stringValue)) {
|
|
return Number(stringValue);
|
|
}
|
|
|
|
// JSON value
|
|
try {
|
|
return this.snowboard.jsonParser().parse(stringValue);
|
|
} catch (e) {
|
|
return (stringValue === '') ? true : stringValue;
|
|
}
|
|
}
|
|
}
|