File Editor
Directories:
.. (Back)
Files:
attribute-binder.js
field-conditions.js
frame-helpers.js
frames.js
property-resolver.js
utils.js
Create New File
Create
Edit File: property-resolver.js
// External Dependencies import map from 'lodash/map'; import has from 'lodash/has'; import get from 'lodash/get'; import isUndefined from 'lodash/isUndefined'; import size from 'lodash/size'; import includes from 'lodash/includes'; import isEmpty from 'lodash/isEmpty'; // Internal Dependencies import { top_window } from './frame-helpers'; class PropertyResolver { /** * Prepended to all paths before performing property value lookups in source object. * For example, if the source object was 'module', the prefix was 'props.attrs.', and the * path being looked up was 'provider', it would be looked up using path: 'module.props.attrs.provider'. * * @since 3.10 * * @type {string} */ path_prefix = ''; /** * Regex Patterns * * @since 3.10 * * @type {{scope: RegExp, variable: RegExp}} */ patterns = { scope: /^(\w+):(.+)/, // Matches scope:variable variable: /(\w*)\${(\w*?):?(\w+)}(\w*)/, // Matches ${variable} or ${scope:variable} function: /^(function)\.(.+)/, // Matches function.functionName }; /** * Used to lookup default values for properties. * * @since 3.10 * * @type {object} */ property_definitions = {}; /** * The object used to lookup property values. * * @since 3.10 * * @type {object} */ source_object = {}; /** * PropertyResolver constructor. * * @since 3.10 * * @param {object} source_object * @param {object} property_definitions * @param {string} path_prefix */ constructor(source_object, property_definitions, path_prefix = '') { this.property_definitions = property_definitions || {}; this.source_object = source_object || {}; this.path_prefix = path_prefix; } /** * Resolves variables in a path string to values of properties in the source object. * * @since 3.10 * @private * * @param {string} path * * @return {string} */ _resolveVariables(path) { return map(path.split('.'), property => { if (this.matchesPattern(property, 'variable')) { // This property includes a variable that resolves to the value of another property. // For example, let's say the property is '${provider}_list'. The matches would be... let [_, before, scope, prop, after] = property.match(this.patterns.variable); // '${provider}_list', '', 'provider', '_list' let source = this.source_object; if (scope && has(source, scope)) { // Resolve the variable from an object within the source object. For example, // if the property was '${parentModule:provider}_list', the source object would be // 'this.source_object.parentModule' source = get(source, scope); } // Continuing from above example, if the value of 'provider' property on the source object was 'mailchimp' the // resolved property would be 'mailchimp_list'. prop = get(source, `${this.path_prefix}${prop}`); if (isUndefined(prop)) { prop = get(this.property_definitions, [prop, 'default']); } property = before + prop + after; } return property; }).join('.'); } /** * Whether or not a string matches a regex pattern located in {@see this.patterns}. * * @since 3.10 * * @param {string} string The string to test. * @param {string} pattern The name of the pattern to test against. * * @return {boolean} */ matchesPattern = (string, pattern) => this.patterns[pattern].test(string); /** * Whether or not a Provider/List has predefined Custom Fields * * @since 3.10 * * @return {string} */ hasPredefinedFields = () => { const attrs = get(this, 'source_object.props.attrs'); const provider = get(attrs, 'provider', get(this.property_definitions, 'provider.default')); const allow_dynamic = get(this, 'property_definitions.use_custom_fields.allow_dynamic'); if (provider) { let [account, list] = get(attrs, `${provider}_list`, '').split('|'); if (account) { account = account.toLowerCase().replace(/ /g, ''); let key = `predefined_field_${provider}_${account}`; let predefined_fields = get(this, ['child_property_definitions', key, 'options']); if (list && ! predefined_fields) { key = `${key}_${list}`; predefined_fields = get(this, ['child_property_definitions', key, 'options'], []); } return ( // Provider allows custom fields even if not predefined includes(allow_dynamic, provider) || // At least one custom field is predefined other than the default 'none' size(predefined_fields) > 1 ) ? 'on' : 'off'; } } return 'off'; }; /** * Return current page protocol. * * @since 3.19 * * @return {string} */ protocol = () => get(top_window, 'location.protocol', 'http:').split(':')[0]; /** * Return whether the current post type is a TB layout. * * @since 4.0 * * @return {string} */ isTBLayout = () => ETBuilderBackend.themeBuilder.isLayout ? 'on' : 'off'; /** * Return whether the current post type is post. * * @since 4.3.4 * * @return {string} */ isPost = () => 'post' === ETBuilderBackend.postType ? 'on' : 'off'; /** * Return whether the current post type is post or a TB layout. * * @since 4.3.4 * * @return {string} */ isPostOrTBLayout = () => 'on' === this.isPost() || 'on' === this.isTBLayout() ? 'on' : 'off'; /** * Whether the "Image Overlay" toggle of the Image module should appear. * It should be visible if either the "Open in Lightbox" option is "on" * OR "Image Link URL" is not empty and the conditional logic only * works with actual values and also not with OR relation. * * @since 4.0.7 * * @return {string} */ showImageUseOverlayField = () => 'on' === this.resolve('show_in_lightbox') || !isEmpty(this.resolve('url')) ? 'on' : 'off'; /** * Resolves a property value using the provided path. * * @since 3.10 * * @param {string} path * * @return {*} */ resolve(path) { let source = this.source_object; let scope; let _; if (this.matchesPattern(path, 'function')) { [_, scope, path] = path.match(this.patterns.function); return has(this, path) ? get(this, path)() : false; } else if (this.matchesPattern(path, 'scope')) { [_, scope, path] = path.match(this.patterns.scope); } if (scope && has(source, scope)) { // Resolve the path using an object within the source object. For example, // if the path was 'parentModule:mailchimp_list', the source object would be // 'this.source_object.parentModule' source = get(source, scope); } // Resolve any variables in the path path = this._resolveVariables(path); // Use path to get the value from the source let value = get(source, `${this.path_prefix}${path}`); if (isUndefined(value)) { value = !isUndefined(scope) ? get(this.property_definitions, [scope, path, 'default']) : get(this.property_definitions, [path, 'default']); } return value; } } export default PropertyResolver;
Save Changes
Rename File
Rename