|
| 1 | +/** |
| 2 | +* @license Apache-2.0 |
| 3 | +* |
| 4 | +* Copyright (c) 2026 The Stdlib Authors. |
| 5 | +* |
| 6 | +* Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | +* you may not use this file except in compliance with the License. |
| 8 | +* You may obtain a copy of the License at |
| 9 | +* |
| 10 | +* http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +* |
| 12 | +* Unless required by applicable law or agreed to in writing, software |
| 13 | +* distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | +* See the License for the specific language governing permissions and |
| 16 | +* limitations under the License. |
| 17 | +*/ |
| 18 | + |
| 19 | +/* eslint-disable no-restricted-syntax, no-invalid-this */ |
| 20 | + |
| 21 | +'use strict'; |
| 22 | + |
| 23 | +// MODULES // |
| 24 | + |
| 25 | +var logger = require( 'debug' ); |
| 26 | +var isObject = require( '@stdlib/assert/is-object' ); |
| 27 | +var setReadWriteAccessor = require( '@stdlib/utils/define-read-write-accessor' ); |
| 28 | +var setNonEnumerableReadOnly = require( '@stdlib/utils/define-nonenumerable-read-only-property' ); |
| 29 | +var setNonEnumerableReadOnlyAccessor = require( '@stdlib/utils/define-nonenumerable-read-only-accessor' ); // eslint-disable-line id-length |
| 30 | +var hasProp = require( '@stdlib/assert/has-property' ); |
| 31 | +var inherit = require( '@stdlib/utils/inherit' ); |
| 32 | +var objectKeys = require( '@stdlib/utils/keys' ); |
| 33 | +var transformErrorMessage = require( '@stdlib/plot/vega/base/transform-validation-message' ); |
| 34 | +var instance2json = require( '@stdlib/plot/vega/base/to-json' ); |
| 35 | +var Scale = require( '@stdlib/plot/vega/scale/base/ctor' ); |
| 36 | +var format = require( '@stdlib/string/format' ); |
| 37 | +var properties = require( './properties.json' ); |
| 38 | +var defaults = require( './defaults.js' ); |
| 39 | + |
| 40 | +// Note: keep the following in alphabetical order according to the `require` path... |
| 41 | +var getProperties = require( './properties/get.js' ); |
| 42 | + |
| 43 | +var getType = require( './type/get.js' ); |
| 44 | +var setType = require( './type/set.js' ); |
| 45 | + |
| 46 | + |
| 47 | +// VARIABLES // |
| 48 | + |
| 49 | +var debug = logger( 'vega:discrete-scale:main' ); |
| 50 | + |
| 51 | + |
| 52 | +// MAIN // |
| 53 | + |
| 54 | +/** |
| 55 | +* Discrete scale constructor. |
| 56 | +* |
| 57 | +* @constructor |
| 58 | +* @param {Options} options - constructor options |
| 59 | +* @param {string} options.name - scale name |
| 60 | +* @param {(Collection|Object|Signal)} [options.domain] - domain of associated data values |
| 61 | +* @param {number} [options.domainMax] - maximum value in the scale domain (overrides the `domain` option) |
| 62 | +* @param {number} [options.domainMin] - minimum value in the scale domain (overrides the `domain` option) |
| 63 | +* @param {number} [options.domainMid] - single mid-point value inserted into a two-element domain |
| 64 | +* @param {Collection} [options.domainRaw] - array of raw domain values which overrides the `domain` property |
| 65 | +* @param {(string|Object)} [options.interpolate] - scale range interpolation method |
| 66 | +* @param {(Collection|Object|Signal|string)} [options.range] - scale range |
| 67 | +* @param {boolean} [options.reverse=false] - boolean indicating whether to reverse the order of the scale range |
| 68 | +* @param {boolean} [options.round=false] - boolean indicating whether to round numeric output values to integers |
| 69 | +* @param {string} [options.type='ordinal'] - scale type |
| 70 | +* @throws {TypeError} options argument must be an object |
| 71 | +* @throws {Error} must provide valid options |
| 72 | +* @returns {DiscreteScale} scale instance |
| 73 | +* |
| 74 | +* @example |
| 75 | +* var scale = new DiscreteScale({ |
| 76 | +* 'name': 'xScale' |
| 77 | +* }); |
| 78 | +* // returns <DiscreteScale> |
| 79 | +*/ |
| 80 | +function DiscreteScale( options ) { |
| 81 | + var opts; |
| 82 | + var keys; |
| 83 | + var v; |
| 84 | + var k; |
| 85 | + var i; |
| 86 | + if ( !( this instanceof DiscreteScale ) ) { |
| 87 | + return new DiscreteScale( options ); |
| 88 | + } |
| 89 | + if ( !isObject( options ) ) { |
| 90 | + throw new TypeError( format( 'invalid argument. Options argument must be an object. Value: `%s`.', options ) ); |
| 91 | + } |
| 92 | + // Check for required properties... |
| 93 | + if ( !hasProp( options, 'name' ) ) { |
| 94 | + throw new TypeError( 'invalid argument. Options argument must specify the scale name.' ); |
| 95 | + } |
| 96 | + Scale.call( this, { |
| 97 | + 'name': options.name |
| 98 | + }); |
| 99 | + |
| 100 | + // Resolve the default configuration: |
| 101 | + opts = defaults(); |
| 102 | + |
| 103 | + // Set internal properties according to the default configuration... |
| 104 | + keys = objectKeys( opts ); |
| 105 | + for ( i = 0; i < keys.length; i++ ) { |
| 106 | + k = keys[ i ]; |
| 107 | + this[ '_'+k ] = opts[ k ]; |
| 108 | + } |
| 109 | + // Validate provided options by attempting to assign option values to corresponding fields... |
| 110 | + for ( i = 0; i < properties.length; i++ ) { |
| 111 | + k = properties[ i ]; |
| 112 | + if ( !hasProp( options, k ) ) { |
| 113 | + continue; |
| 114 | + } |
| 115 | + v = options[ k ]; |
| 116 | + try { |
| 117 | + this[ k ] = v; |
| 118 | + } catch ( err ) { |
| 119 | + debug( 'Encountered an error. Error: %s', err.message ); |
| 120 | + |
| 121 | + // FIXME: retain thrown error type |
| 122 | + throw new Error( transformErrorMessage( err.message ) ); |
| 123 | + } |
| 124 | + } |
| 125 | + return this; |
| 126 | +} |
| 127 | + |
| 128 | +/* |
| 129 | +* Inherit from a parent prototype. |
| 130 | +*/ |
| 131 | +inherit( DiscreteScale, Scale ); |
| 132 | + |
| 133 | +/** |
| 134 | +* Constructor name. |
| 135 | +* |
| 136 | +* @private |
| 137 | +* @name name |
| 138 | +* @memberof DiscreteScale |
| 139 | +* @readonly |
| 140 | +* @type {string} |
| 141 | +*/ |
| 142 | +setNonEnumerableReadOnly( DiscreteScale, 'name', 'DiscreteScale' ); |
| 143 | + |
| 144 | +/** |
| 145 | +* Scale properties. |
| 146 | +* |
| 147 | +* @name properties |
| 148 | +* @memberof DiscreteScale.prototype |
| 149 | +* @type {Array<string>} |
| 150 | +* |
| 151 | +* @example |
| 152 | +* var scale = new DiscreteScale({ |
| 153 | +* 'name': 'xScale' |
| 154 | +* }); |
| 155 | +* |
| 156 | +* var v = scale.properties; |
| 157 | +* // returns [...] |
| 158 | +*/ |
| 159 | +setNonEnumerableReadOnlyAccessor( DiscreteScale.prototype, 'properties', getProperties ); |
| 160 | + |
| 161 | +/** |
| 162 | +* Scale type. |
| 163 | +* |
| 164 | +* @name type |
| 165 | +* @memberof DiscreteScale.prototype |
| 166 | +* @type {string} |
| 167 | +* @default 'ordinal' |
| 168 | +* |
| 169 | +* @example |
| 170 | +* var scale = new DiscreteScale({ |
| 171 | +* 'name': 'xScale' |
| 172 | +* }); |
| 173 | +* |
| 174 | +* var v = scale.type; |
| 175 | +* // returns 'ordinal' |
| 176 | +*/ |
| 177 | +setReadWriteAccessor( DiscreteScale.prototype, 'type', getType, setType ); |
| 178 | + |
| 179 | +/** |
| 180 | +* Serializes an instance to a JSON object. |
| 181 | +* |
| 182 | +* ## Notes |
| 183 | +* |
| 184 | +* - This method is implicitly invoked by `JSON.stringify`. |
| 185 | +* |
| 186 | +* @name toJSON |
| 187 | +* @memberof DiscreteScale.prototype |
| 188 | +* @type {Function} |
| 189 | +* @returns {Object} JSON object |
| 190 | +* |
| 191 | +* @example |
| 192 | +* var scale = new DiscreteScale({ |
| 193 | +* 'name': 'xScale' |
| 194 | +* }); |
| 195 | +* |
| 196 | +* var v = scale.toJSON(); |
| 197 | +* // returns {...} |
| 198 | +*/ |
| 199 | +setNonEnumerableReadOnly( DiscreteScale.prototype, 'toJSON', function toJSON() { |
| 200 | + return instance2json( this, properties ); |
| 201 | +}); |
| 202 | + |
| 203 | + |
| 204 | +// EXPORTS // |
| 205 | + |
| 206 | +module.exports = DiscreteScale; |
0 commit comments