autonumeric
Version:
autoNumeric is a standalone Javascript library that provides live *as-you-type* formatting for international numbers and currencies. It supports most international numeric formats and currencies including those used in Europe, Asia, and North and South Am
1,048 lines (919 loc) • 478 kB
JavaScript
/**
* AutoNumeric.js
*
* @version 4.10.8
* @date 2025-03-03 UTC 08:10
*
* @authors 2016-2025 Alexandre Bonneau <alexandre.bonneau@linuxfr.eu>
* 2009-2016 Bob Knothe <bob.knothe@gmail.com>
* @contributors Sokolov Yura and others, cf. AUTHORS
* @copyright Alexandre Bonneau & Robert J. Knothe
* @since 2009-08-09
*
* @summary AutoNumeric is a standalone Javascript library
* that provides live *as-you-type* formatting for
* international numbers and currencies.
*
* @link http://autonumeric.org
* @docs https://docs.autonumeric.org
*
* Note : Some functions are borrowed from big.js
* @see https://github.com/MikeMcl/big.js/
*
* Please report any bugs to https://github.com/autoNumeric/autoNumeric
*
* @license Released under the MIT License
* @link http://www.opensource.org/licenses/mit-license.php
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sub license, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
//TODO Prevent having to enter relative path in the js files (i.e. using `./AutoNumericHelper` instead of just `AutoNumericHelper`) (cf. http://moduscreate.com/es6-es2015-import-no-relative-path-webpack/)
import AutoNumericHelper from './AutoNumericHelper';
import AutoNumericEnum from './AutoNumericEnum';
import Evaluator from './maths/Evaluator';
import Parser from './maths/Parser';
/**
* Class declaration for the AutoNumeric object.
*
* An AutoNumeric element is an object wrapper that keeps a reference to the DOM element it manages (usually an <input> one), and provides autoNumeric-specific variables and functions.
*/
export default class AutoNumeric {
static options; // Those static declarations are only used by the IDE, to prevent error messages not finding those fields declarations
static events;
static defaultSettings;
static predefinedOptions;
/**
* Return the autoNumeric version number (for debugging purpose)
*
* @returns {string}
*/
static version() {
return '4.10.8';
}
/**
* Initialize the AutoNumeric object onto the given DOM element, and attach the settings and related event listeners to it.
* The options passed as a parameter is an object that contains the settings (i.e. {digitGroupSeparator: ".", decimalCharacter: ",", currencySymbol: '€ '})
*
* @example
* anElement = new AutoNumeric(domElement); // With the default options
* anElement = new AutoNumeric(domElement, { options }); // With one option object
* anElement = new AutoNumeric(domElement, 'euroPos'); // With a named pre-defined string
* anElement = new AutoNumeric(domElement, [{ options1 }, 'euroPos', { options2 }]); // With multiple option objects (the latest option overwriting the previous ones)
* anElement = new AutoNumeric(domElement, null, { options }); // With one option object, and a failed initial value
* anElement = new AutoNumeric(domElement).french(); // With one pre-defined language object
* anElement = new AutoNumeric(domElement).french({ options });// With one pre-defined language object and additional options that will override the defaults
*
* // ...or init and set the value in one call :
* anElement = new AutoNumeric(domElement, 12345.789); // With the default options, and an initial value
* anElement = new AutoNumeric(domElement, 12345.789, { options });
* anElement = new AutoNumeric(domElement, '12345.789', { options });
* anElement = new AutoNumeric(domElement, 12345.789, 'euroPos');
* anElement = new AutoNumeric(domElement, 12345.789, [{ options1 }, 'euroPos', { options2 }]);
* anElement = new AutoNumeric(domElement, 12345.789).french({ options });
* anElement = new AutoNumeric(domElement, 12345.789, { options }).french({ options }); // Not really helpful, but possible
*
* // The AutoNumeric constructor class can also accept a string as a css selector. Under the hood this use `QuerySelector` and limit itself to only the first element it finds.
* anElement = new AutoNumeric('.myCssClass > input');
* anElement = new AutoNumeric('.myCssClass > input', { options });
* anElement = new AutoNumeric('.myCssClass > input', 'euroPos');
* anElement = new AutoNumeric('.myCssClass > input', [{ options1 }, 'euroPos', { options2 }]);
* anElement = new AutoNumeric('.myCssClass > input', 12345.789);
* anElement = new AutoNumeric('.myCssClass > input', 12345.789, { options });
* anElement = new AutoNumeric('.myCssClass > input', 12345.789, 'euroPos');
* anElement = new AutoNumeric('.myCssClass > input', 12345.789, [{ options1 }, 'euroPos', { options2 }]);
* anElement = new AutoNumeric('.myCssClass > input', null, { options }); // With a failed initial value
* anElement = new AutoNumeric('.myCssClass > input', 12345.789).french({ options });
*
* @param {object|Array|number|string} arg1
* @param {object|Array|number|string|null} arg2
* @param {object|Array|number|string|null} arg3
* @throws
*/
constructor(arg1 = null, arg2 = null, arg3 = null) {
// --------------------------------------------------------
// -------------- Initialization
// Initialize the arguments
const { domElement, initialValue, userOptions } = AutoNumeric._setArgumentsValues(arg1, arg2, arg3);
// Initialize the element
this.domElement = domElement;
// Generate the settings
this.defaultRawValue = ''; // The default raw value to set when initializing an AutoNumeric object
this._setSettings(userOptions, false);
//TODO If `styleRules` is not null, add by default a class 'autoNumeric' that adds transition to color, background-color, border-color properties
// Check if the DOM element is supported
this._checkElement();
// Store the additional attributes inside the AutoNumeric object
// Note: This variable is needed and not a duplicate of `initialValueOnFirstKeydown` nor `valueOnFocus` since it serves a different purpose and has a different lifecycle
this.savedCancellableValue = null;
// Initialize the undo/redo variables
this.historyTable = []; // Keep track of *all* valid states of the element value
this.historyTableIndex = -1; // Pointer to the current undo/redo state. This will be set to '0' during initialization since it first adds itself.
this.onGoingRedo = false; // Variable that keeps track if a 'redo' is ongoing (in order to prevent an 'undo' to be launch when releasing the shift key before the ctrl key after a 'redo' shortcut)
// Initialize the parent form element, if any
this.parentForm = this._getParentForm();
// Set the initial value if it exists and if the `formatOnPageLoad` option will allow it
if (!this.runOnce && this.settings.formatOnPageLoad) {
// Format the element value if needed
this._formatDefaultValueOnPageLoad(initialValue);
} else {
// Otherwise set the `rawValue` and the element value, but do not format the latter yet
let valueToSet;
if (AutoNumericHelper.isNull(initialValue)) {
switch (this.settings.emptyInputBehavior) {
case AutoNumeric.options.emptyInputBehavior.min:
valueToSet = this.settings.minimumValue;
break;
case AutoNumeric.options.emptyInputBehavior.max:
valueToSet = this.settings.maximumValue;
break;
case AutoNumeric.options.emptyInputBehavior.zero:
valueToSet = '0';
break;
case AutoNumeric.options.emptyInputBehavior.focus:
case AutoNumeric.options.emptyInputBehavior.press:
case AutoNumeric.options.emptyInputBehavior.always:
valueToSet = '';
break;
// It's possible to set the `null` value as the initial value
case AutoNumeric.options.emptyInputBehavior.null:
valueToSet = null;
break;
// When `emptyInputBehavior` is a number or a string representing a number
default :
valueToSet = this.settings.emptyInputBehavior;
}
} else {
valueToSet = initialValue;
}
this._setElementAndRawValue(valueToSet);
}
this.runOnce = true;
// Add the events listeners only on input or editable elements
this.hasEventListeners = false;
if (this.isInputElement || this.isContentEditable) {
if (!this.settings.noEventListeners) {
//XXX Here we make sure the global list is created after creating the event listeners, to only create the event listeners on `document` once
this._createEventListeners();
}
this._setWritePermissions(true);
}
// Save the initial values (html attribute + element.value) for the pristine test
this._saveInitialValues(initialValue);
// Set up the data for the persistent storage solution (i.e. sessionStorage)
this.storageNamePrefix = 'AUTO_'; // The prefix for the raw value storage name variable can be modified here
this._setPersistentStorageName();
// --------------------------------------------------------
// -------------- Tracking
this.validState = true; // Keep track if the element is in the valid state
this.isFocused = false; // Keep track if the element is currently focused
this.isWheelEvent = false; // Keep track if a mouse wheel event is currently ongoing
this.isDropEvent = false; // Keep track if a drop event is currently ongoing
this.isEditing = false; // Keep track if the user is currently editing the element
this.rawValueOnFocus = void(0); // Keep track of the rawValue (needed to define if a change event must be sent on blur or enter key)
// Watch any external changes to the element value/textContent/nodeValue and `set()` the new value so that it gets formatted/saved in the history
this.internalModification = false; // This is temporarily set to `true` only when the AutoNumeric object does update the element value
this.attributeToWatch = this._getAttributeToWatch();
this.getterSetter = Object.getOwnPropertyDescriptor(this.domElement.__proto__, this.attributeToWatch);
this._addWatcher();
if (this.settings.createLocalList) {
// Keep track of every AutoNumeric elements that this object initialized
this._createLocalList();
}
// Keep track of all AutoNumeric elements in the current web page
this.constructor._addToGlobalList(this);
// --------------------------------------------------------
// -------------- Methods
// Create the global functions
this.global = {
/**
* Set the same given element value for each element in the local AutoNumeric element list, and format those elements immediately
*
* @param {number|string} newValue The value must be a number or a numeric string
* @param {object} options A settings object that will override the current settings. Note: the update is done only if the `newValue` is defined.
*/
set: (newValue, options = null) => {
this.autoNumericLocalList.forEach(aNObject => {
aNObject.set(newValue, options);
});
},
/**
* Set the value given value directly as the DOM element value, without formatting it beforehand.
* This sets the same unformatted value for each element in the local AutoNumeric element list.
*
* @param {number|string} value
* @param {object} options
*/
setUnformatted: (value, options = null) => {
this.autoNumericLocalList.forEach(aNObject => {
aNObject.setUnformatted(value, options);
});
},
/**
* This is an alias of the `getNumericString()` function, and should not be used anymore.
*
* @param {function|null} callback If a callback is passed, then the result is passed to it as its first argument, and the AutoNumeric object has its second
* @returns {Array<string>}
* @deprecated
*/
get: (callback = null) => {
const result = [];
this.autoNumericLocalList.forEach(aNObject => {
result.push(aNObject.get());
});
this._executeCallback(result, callback);
return result;
},
/**
* Return an array of the unformatted values (as a string) of each AutoNumeric element of the local AutoNumeric element list
*
* @param {function|null} callback If a callback is passed, then the result is passed to it as its first argument, and the AutoNumeric object has its second
* @returns {Array<string>}
*/
getNumericString: (callback = null) => {
const result = [];
this.autoNumericLocalList.forEach(aNObject => {
result.push(aNObject.getNumericString());
});
this._executeCallback(result, callback);
return result;
},
/**
* Return an array of the current formatted values (as a string) of each AutoNumeric element of the local AutoNumeric element list
*
* @param {function|null} callback If a callback is passed, then the result is passed to it as its first argument, and the AutoNumeric object has its second
* @returns {Array<string>}
*/
getFormatted: (callback = null) => {
const result = [];
this.autoNumericLocalList.forEach(aNObject => {
result.push(aNObject.getFormatted());
});
this._executeCallback(result, callback);
return result;
},
/**
* Return an array of the element unformatted values (as a real Javascript number), for each element of the local AutoNumeric element list
*
* @param {function|null} callback If a callback is passed, then the result is passed to it as its first argument, and the AutoNumeric object has its second
* @returns {Array<number>}
*/
getNumber: (callback = null) => {
const result = [];
this.autoNumericLocalList.forEach(aNObject => {
result.push(aNObject.getNumber());
});
this._executeCallback(result, callback);
return result;
},
/**
* Returns the unformatted values (following the `outputFormat` setting) of each element of the local AutoNumeric element list into an array
*
* @param {function|null} callback If a callback is passed, then the result is passed to it as its first argument, and the AutoNumeric object has its second
* @returns {Array<string>}
*/
getLocalized: (callback = null) => {
const result = [];
this.autoNumericLocalList.forEach(aNObject => {
result.push(aNObject.getLocalized());
});
this._executeCallback(result, callback);
return result;
},
/**
* Force each element of the local AutoNumeric element list to reformat its value
*/
reformat: () => {
this.autoNumericLocalList.forEach(aNObject => {
aNObject.reformat();
});
},
/**
* Remove the formatting and keep only the raw unformatted value (as a numericString) in each element of the local AutoNumeric element list
*/
unformat: () => {
this.autoNumericLocalList.forEach(aNObject => {
aNObject.unformat();
});
},
/**
* Remove the formatting and keep only the localized unformatted value in the element, with the option to override the default outputFormat if needed
*
* @param {null|string} forcedOutputFormat If set to something different from `null`, then this is used as an overriding outputFormat option
*/
unformatLocalized: (forcedOutputFormat = null) => {
this.autoNumericLocalList.forEach(aNObject => {
aNObject.unformatLocalized(forcedOutputFormat);
});
},
/**
* Updates the AutoNumeric settings, and immediately format the elements accordingly, for each element of the local AutoNumeric element list
*
* @param {object} newOptions This can be either one or more option objects
*/
update: (...newOptions) => {
this.autoNumericLocalList.forEach(aNObject => {
aNObject.update(...newOptions);
});
},
/**
* Return `true` if *all* the autoNumeric-managed elements are pristine, if their raw value hasn't changed.
* By default, this returns `true` if the raw unformatted value is still the same even if the formatted one has changed (due to a configuration update for instance).
*
* @param {boolean} checkOnlyRawValue If set to `true`, the pristine value is done on the raw unformatted value, not the formatted one. If set to `false`, this also checks that the formatted value hasn't changed.
* @returns {boolean}
*/
isPristine: (checkOnlyRawValue = true) => {
let isPristine = true;
this.autoNumericLocalList.forEach(aNObject => {
if (isPristine && !aNObject.isPristine(checkOnlyRawValue)) {
isPristine = false;
}
});
return isPristine;
},
/**
* Execute the `clear()` method on each AutoNumeric object in the local AutoNumeric element list
*
* @param {boolean} forceClearAll
*/
clear: (forceClearAll = false) => {
this.autoNumericLocalList.forEach(aNObject => {
aNObject.clear(forceClearAll);
});
},
/**
* Execute the `remove()` method on each AutoNumeric object in the local AutoNumeric element list
*/
remove: () => {
this.autoNumericLocalList.forEach(aNObject => {
aNObject.remove();
});
},
/**
* Execute the `wipe()` method on each AutoNumeric object in the local AutoNumeric element list
*/
wipe: () => {
this.autoNumericLocalList.forEach(aNObject => {
aNObject.wipe();
});
},
/**
* Execute the `nuke()` method on each AutoNumeric object in the local AutoNumeric element list
*/
nuke: () => {
this.autoNumericLocalList.forEach(aNObject => {
aNObject.nuke();
});
},
/**
* Return `true` if the given AutoNumeric object (or DOM element) is in the local AutoNumeric element list
*
* @param {HTMLElement|HTMLInputElement|AutoNumeric} domElementOrAutoNumericObject
* @returns {*}
*/
has: domElementOrAutoNumericObject => {
let result;
if (domElementOrAutoNumericObject instanceof AutoNumeric) {
result = this.autoNumericLocalList.has(domElementOrAutoNumericObject.node());
} else {
result = this.autoNumericLocalList.has(domElementOrAutoNumericObject);
}
return result;
},
/**
* Add an existing AutoNumeric object (or DOM element) to the local AutoNumeric element list, using the DOM element as the key.
* This manages the case where `addObject` is used on an AutoNumeric object that already has multiple elements in its local list.
*
* @param {HTMLElement|HTMLInputElement|AutoNumeric} domElementOrAutoNumericObject
*/
addObject: domElementOrAutoNumericObject => {
// Start with the same data, whatever the user passed as arguments
let domElement;
let otherAutoNumericObject;
if (domElementOrAutoNumericObject instanceof AutoNumeric) {
domElement = domElementOrAutoNumericObject.node();
otherAutoNumericObject = domElementOrAutoNumericObject;
} else {
domElement = domElementOrAutoNumericObject;
otherAutoNumericObject = AutoNumeric.getAutoNumericElement(domElement);
}
// Check if the current autoNumeric object has a local list
if (!this._hasLocalList()) {
this._createLocalList();
}
// Check if the other autoNumeric object has a local list...
let otherANLocalList = otherAutoNumericObject._getLocalList();
if (otherANLocalList.size === 0) {
// Special case if the other AutoNumeric object has an empty local list, then populate itself to it
otherAutoNumericObject._createLocalList();
otherANLocalList = otherAutoNumericObject._getLocalList(); // Update the other local list
}
let mergedLocalLists;
if (otherANLocalList instanceof Map) {
// ...If it does, merge the local lists together
mergedLocalLists = AutoNumericHelper.mergeMaps(this._getLocalList(), otherANLocalList);
} else {
// ...If not, just set the current local list onto the other AutoNumeric object
// We need to specify the AutoNumeric object, otherwise the `_addToLocalList` function would not correctly add the AutoNumeric object since we would not have a reference to it, but a reference to the current AutoNumeric object on which is called this method.
this._addToLocalList(domElement, otherAutoNumericObject);
mergedLocalLists = this._getLocalList();
}
// Update the resulting list, on all the objects of that local list (so that we can indifferently use `init()` on any object belonging to that list)
mergedLocalLists.forEach(aNObject => {
aNObject._setLocalList(mergedLocalLists);
});
},
/**
* Remove the given AutoNumeric object (or DOM element) from the local AutoNumeric element list, using the DOM element as the key.
* If this function attempts to remove the current AutoNumeric object from the local list, a warning is shown, but the deletion is still done.
*
* Special cases :
* - If the current object removes itself, then it's removed from the shared local list, then a new empty local list is used/created
* - If another object remove this object, then a local list with only this object is used/created
*
* @param {HTMLElement|HTMLInputElement|AutoNumeric} domElementOrAutoNumericObject
* @param {boolean} keepCurrentANObject If set to `false`, then the function will also remove the current AutoNumeric object if asked, otherwise it will ignore it and print a warning message
*/
removeObject: (domElementOrAutoNumericObject, keepCurrentANObject = false) => {
// Start with the same data, whatever the user passed as arguments
let domElement;
let otherAutoNumericObject;
if (domElementOrAutoNumericObject instanceof AutoNumeric) {
domElement = domElementOrAutoNumericObject.node();
otherAutoNumericObject = domElementOrAutoNumericObject;
} else {
domElement = domElementOrAutoNumericObject;
otherAutoNumericObject = AutoNumeric.getAutoNumericElement(domElement);
}
// Remove the other object from the local list
const initialCompleteLocalList = this.autoNumericLocalList;
this.autoNumericLocalList.delete(domElement);
// Update the local list for all objects in it
initialCompleteLocalList.forEach(aNObject => {
aNObject._setLocalList(this.autoNumericLocalList);
});
if (!keepCurrentANObject && domElement === this.node()) {
// This object is removed by itself
// Empty the object local list
otherAutoNumericObject._setLocalList(new Map);
} else {
// This object is removed by another object
// Set the local list for the removed object, with only this object in it
otherAutoNumericObject._createLocalList();
}
},
/**
* Remove all elements from the shared list, effectively emptying it.
* This is the equivalent of calling `detach()` on each of its elements.
*
* @param {boolean} keepEachANObjectInItsOwnList If set to `true`, then instead of completely emptying the local list of each AutoNumeric objects, each one of those keeps itself in its own local list
*/
empty: (keepEachANObjectInItsOwnList = false) => {
const initialCompleteLocalList = this.autoNumericLocalList;
// Update the local list for all objects in it
initialCompleteLocalList.forEach(aNObject => {
if (keepEachANObjectInItsOwnList) {
aNObject._createLocalList();
} else {
aNObject._setLocalList(new Map);
}
});
},
/**
* Return an array containing all the AutoNumeric DOM elements that have been initialized by each other
*
* @returns {Array<HTMLElement>}
*/
elements: () => {
const result = [];
this.autoNumericLocalList.forEach(aNObject => {
result.push(aNObject.node());
});
return result;
},
/**
* Return the `Map` object directly
* @returns {Map}
*/
getList: () => this.autoNumericLocalList,
/**
* Return the number of element in the local AutoNumeric element list
* @returns {number}
*/
size: () => this.autoNumericLocalList.size,
};
// Create the functions that will allow to change each setting one by one
/**
* For each option, we define if we need to reformat the element content (does changing the options should change the way its value is displayed?).
* If yes, then we use the `update()` for force a reformat, otherwise, we just update the `settings` object.
*/
this.options = {
/**
* Reset any options set previously, by overwriting them with the default settings
*
* @returns {AutoNumeric}
*/
reset : () => {
//TODO Add a `settings` parameter so that the user can reset to a specific set of settings. This is different than update since it drops any non-default settings before using those new settings.
this.settings = { rawValue : this.defaultRawValue }; // Here we pass the default rawValue in order to prevent showing a warning that we try to set an `undefined` value
this.update(AutoNumeric.defaultSettings);
return this;
},
allowDecimalPadding : allowDecimalPadding => {
this.update({ allowDecimalPadding });
return this;
},
alwaysAllowDecimalCharacter : alwaysAllowDecimalCharacter => { //FIXME Test this
this.update({ alwaysAllowDecimalCharacter });
return this;
},
caretPositionOnFocus : caretPositionOnFocus => { //FIXME test this
this.settings.caretPositionOnFocus = caretPositionOnFocus;
return this;
},
createLocalList : createLocalList => {
this.settings.createLocalList = createLocalList;
// Delete the local list when this is set to `false`, create it if this is set to `true` and there is not pre-existing list
if (this.settings.createLocalList) {
if (!this._hasLocalList()) {
this._createLocalList();
}
} else {
this._deleteLocalList();
}
return this;
},
currencySymbol : currencySymbol => {
this.update({ currencySymbol });
return this;
},
currencySymbolPlacement : currencySymbolPlacement => {
this.update({ currencySymbolPlacement });
return this;
},
decimalCharacter : decimalCharacter => {
this.update({ decimalCharacter });
return this;
},
decimalCharacterAlternative : decimalCharacterAlternative => {
this.settings.decimalCharacterAlternative = decimalCharacterAlternative;
return this;
},
/**
* Update the decimal places globally, which means this override any previously set number of decimal shown on focus, on blur, or in the raw value.
*
* @param {int} decimalPlaces
* @returns {AutoNumeric}
*/
decimalPlaces : decimalPlaces => {
AutoNumericHelper.warning('Using `options.decimalPlaces()` instead of calling the specific `options.decimalPlacesRawValue()`, `options.decimalPlacesShownOnFocus()` and `options.decimalPlacesShownOnBlur()` methods will reset those options.\nPlease call the specific methods if you do not want to reset those.', this.settings.showWarnings);
this.update({ decimalPlaces });
return this;
},
decimalPlacesRawValue : decimalPlacesRawValue => { //FIXME test this
this.update({ decimalPlacesRawValue });
return this;
},
decimalPlacesShownOnBlur : decimalPlacesShownOnBlur => {
this.update({ decimalPlacesShownOnBlur });
return this;
},
decimalPlacesShownOnFocus : decimalPlacesShownOnFocus => {
this.update({ decimalPlacesShownOnFocus });
return this;
},
defaultValueOverride : defaultValueOverride => {
this.update({ defaultValueOverride });
return this;
},
digitalGroupSpacing : digitalGroupSpacing => {
this.update({ digitalGroupSpacing });
return this;
},
digitGroupSeparator : digitGroupSeparator => {
this.update({ digitGroupSeparator });
return this;
},
divisorWhenUnfocused : divisorWhenUnfocused => {
this.update({ divisorWhenUnfocused });
return this;
},
emptyInputBehavior : emptyInputBehavior => {
if (this.rawValue === null && emptyInputBehavior !== AutoNumeric.options.emptyInputBehavior.null) {
// Special case : if the current `rawValue` is `null` and the `emptyInputBehavior` is changed to something else than `'null'`, then it makes that `rawValue` invalid.
// Here we can either prevent the option update and throw an error, or still accept the option update and update the value from `null` to `''`.
// We cannot keep `rawValue` to `null` since if `emptyInputBehavior` is not set to `null`, lots of function assume `rawValue` is a string.
AutoNumericHelper.warning(`You are trying to modify the \`emptyInputBehavior\` option to something different than \`'null'\` (${emptyInputBehavior}), but the element raw value is currently set to \`null\`. This would result in an invalid \`rawValue\`. In order to fix that, the element value has been changed to the empty string \`''\`.`, this.settings.showWarnings);
this.rawValue = '';
}
this.update({ emptyInputBehavior });
return this;
},
eventBubbles : eventBubbles => {
this.settings.eventBubbles = eventBubbles;
return this;
},
eventIsCancelable : eventIsCancelable => {
this.settings.eventIsCancelable = eventIsCancelable;
return this;
},
failOnUnknownOption : failOnUnknownOption => {
this.settings.failOnUnknownOption = failOnUnknownOption; //TODO test this with unit tests
return this;
},
formatOnPageLoad : formatOnPageLoad => {
this.settings.formatOnPageLoad = formatOnPageLoad; //TODO test this with unit tests
return this;
},
formulaMode : formulaMode => {
this.settings.formulaMode = formulaMode; //TODO test this with unit tests
return this;
},
historySize : historySize => {
this.settings.historySize = historySize;
return this;
},
invalidClass : invalidClass => {
this.settings.invalidClass = invalidClass; //TODO test this with unit tests
return this;
},
isCancellable : isCancellable => {
this.settings.isCancellable = isCancellable; //TODO test this with unit tests
return this;
},
leadingZero : leadingZero => {
this.update({ leadingZero });
return this;
},
maximumValue : maximumValue => {
this.update({ maximumValue });
return this;
},
minimumValue : minimumValue => {
this.update({ minimumValue });
return this;
},
modifyValueOnUpDownArrow : modifyValueOnUpDownArrow => {
this.settings.modifyValueOnUpDownArrow = modifyValueOnUpDownArrow; //TODO test this with unit tests
return this;
},
modifyValueOnWheel : modifyValueOnWheel => {
this.settings.modifyValueOnWheel = modifyValueOnWheel; //TODO test this with unit tests
return this;
},
negativeBracketsTypeOnBlur : negativeBracketsTypeOnBlur => {
this.update({ negativeBracketsTypeOnBlur });
return this;
},
negativePositiveSignPlacement: negativePositiveSignPlacement => {
this.update({ negativePositiveSignPlacement });
return this;
},
negativeSignCharacter : negativeSignCharacter => {
this.update({ negativeSignCharacter });
return this;
},
negativePositiveSignBehavior : negativePositiveSignBehavior => {
this.settings.negativePositiveSignBehavior = negativePositiveSignBehavior; //TODO test this with unit tests
return this;
},
noEventListeners : noEventListeners => { //TODO test this with unit tests
if (noEventListeners === AutoNumeric.options.noEventListeners.noEvents && this.settings.noEventListeners === AutoNumeric.options.noEventListeners.addEvents) {
// Remove the events once
this._removeEventListeners();
}
this.update({ noEventListeners });
return this;
},
onInvalidPaste : onInvalidPaste => {
this.settings.onInvalidPaste = onInvalidPaste; //TODO test this with unit tests
return this;
},
outputFormat : outputFormat => {
this.settings.outputFormat = outputFormat;
return this;
},
overrideMinMaxLimits : overrideMinMaxLimits => {
this.update({ overrideMinMaxLimits });
return this;
},
positiveSignCharacter : positiveSignCharacter => {
this.update({ positiveSignCharacter });
return this;
},
rawValueDivisor : rawValueDivisor => {
this.update({ rawValueDivisor });
return this;
},
readOnly : readOnly => {
// When changing the readOnly attribute, the raw and formatted values do not change, so no need to call the costly 'update()` method
this.settings.readOnly = readOnly;
this._setWritePermissions();
return this;
},
roundingMethod : roundingMethod => {
this.update({ roundingMethod });
return this;
},
saveValueToSessionStorage : saveValueToSessionStorage => {
this.update({ saveValueToSessionStorage });
return this;
},
symbolWhenUnfocused : symbolWhenUnfocused => {
this.update({ symbolWhenUnfocused });
return this;
},
selectNumberOnly : selectNumberOnly => {
this.settings.selectNumberOnly = selectNumberOnly; //TODO test this with unit tests
return this;
},
selectOnFocus : selectOnFocus => {
this.settings.selectOnFocus = selectOnFocus; //TODO test this with unit tests
return this;
},
serializeSpaces : serializeSpaces => {
this.settings.serializeSpaces = serializeSpaces; //TODO test this with unit tests
return this;
},
showOnlyNumbersOnFocus : showOnlyNumbersOnFocus => {
this.update({ showOnlyNumbersOnFocus });
return this;
},
showPositiveSign : showPositiveSign => {
this.update({ showPositiveSign });
return this;
},
showWarnings : showWarnings => {
this.settings.showWarnings = showWarnings; //TODO test this with unit tests
return this;
},
styleRules : styleRules => {
this.update({ styleRules });
return this;
},
suffixText : suffixText => {
this.update({ suffixText });
return this;
},
unformatOnHover : unformatOnHover => {
this.settings.unformatOnHover = unformatOnHover; //TODO test this with unit tests
return this;
},
unformatOnSubmit : unformatOnSubmit => {
this.settings.unformatOnSubmit = unformatOnSubmit; //TODO test this with unit tests
return this;
},
upDownStep : upDownStep => {
this.settings.upDownStep = upDownStep; //TODO test this with unit tests
return this;
},
valuesToStrings : valuesToStrings => {
this.update({ valuesToStrings });
return this;
},
watchExternalChanges : watchExternalChanges => { //TODO test this with unit tests
this.update({ watchExternalChanges });
return this;
},
wheelOn : wheelOn => {
this.settings.wheelOn = wheelOn; //TODO test this with unit tests
return this;
},
wheelStep : wheelStep => {
this.settings.wheelStep = wheelStep; //TODO test this with unit tests
return this;
},
};
// Once the autoNumeric element has been initialized, broadcast that message with additional info.
// Note: When using `AutoNumeric.multiple()`, one event is sent *per* element initialized
this._triggerEvent(AutoNumeric.events.initialized, this.domElement, {
newValue : AutoNumericHelper.getElementValue(this.domElement),
newRawValue: this.rawValue,
error : null,
aNElement : this,
});
}
/**
* Take the parameters given to the AutoNumeric object, and output the three variables that are needed to finish initializing it :
* - domElement : The target DOM element
* - initialValue : The initial value, or `null` if none is given
* - userOptions : The option object
*
* @param {object|Array|number|string} arg1
* @param {object|Array|number|string|null} arg2
* @param {object|Array|number|string|null} arg3
* @returns {{domElement: *, initialValue: *, userOptions: *}}
* @throws
* @private
*/
static _setArgumentsValues(arg1, arg2, arg3) {
// Basic check on the argument count
if (AutoNumericHelper.isNull(arg1)) {
AutoNumericHelper.throwError('At least one valid parameter is needed in order to initialize an AutoNumeric object');
}
// Prepare the arguments in order to create the AutoNumeric object with the right values
// Test the argument types
const isArg1Element = AutoNumericHelper.isElement(arg1);
const isArg1String = AutoNumericHelper.isString(arg1);
const isArg2Object = AutoNumericHelper.isObject(arg2);
const isArg2Array = Array.isArray(arg2) && arg2.length > 0;
const isArg2Number = AutoNumericHelper.isNumberOrArabic(arg2) || arg2 === '';
const isArg2PreDefinedOptionName = this._isPreDefinedOptionValid(arg2);
const isArg2Null = AutoNumericHelper.isNull(arg2);
const isArg2EmptyString = AutoNumericHelper.isEmptyString(arg2);
const isArg3Object = AutoNumericHelper.isObject(arg3);
const isArg3Array = Array.isArray(arg3) && arg3.length > 0;
const isArg3Null = AutoNumericHelper.isNull(arg3);
const isArg3PreDefinedOptionName = this._isPreDefinedOptionValid(arg3);
// Given the parameters passed, sort the data and return a stable state before the initialization
let domElement;
let userOptions;
let initialValue;
//TODO Simplify those tests -->
if (isArg1Element && isArg2Null && isArg3Null) {
// new AutoNumeric(domElement); // With the default options
domElement = arg1;
initialValue = null;
userOptions = null;
} else if (isArg1Element && isArg2Number && isArg3Null) {
// new AutoNumeric(domElement, 12345.789); // With the default options, and an initial value
// new AutoNumeric(domElement, '12345.789');
domElement = arg1;
initialValue = arg2;
userOptions = null;
} else if (isArg1Element && isArg2Object && isArg3Null) {
// new AutoNumeric(domElement, { options }); // With one option object
domElement = arg1;
initialValue = null;
userOptions = arg2;
} else if (isArg1Element && isArg2PreDefinedOptionName && isArg3Null) {
// new AutoNumeric(domElement, 'euroPos'); // With one pre-defined option name
domElement = arg1;
initialValue = null;
userOptions = this._getOptionObject(arg2);
} else if (isArg1Element && isArg2Array && isArg3Null) {
// new AutoNumeric(domElement, [{ options1 }, { options2 }]); // With multiple option objects (the latest option overwriting the previous ones)
domElement = arg1;
initialValue = null;
userOptions = this.mergeOptions(arg2);
} else if (isArg1Element && (isArg2Null || isArg2EmptyString) && isArg3Object) {
// new AutoNumeric(domElement, null, { options }); // With one option object
domElement = arg1;
initialValue = null;
userOptions = arg3;
} else if (isArg1Element && (isArg2Null || isArg2EmptyString) && isArg3Array) {
// new AutoNumeric(domElement, null, [{ options1 }, { options2 }]); // With multiple option objects
domElement = arg1;
initialValue = null;
userOptions = this.mergeOptions(arg3);
} else if (isArg1String && isArg2Null && isArg3Null) {
// new AutoNumeric('.myCssClass > input');
domElement = document.querySelector(arg1);
initialValue = null;
userOptions = null;
} else if (isArg1String && isArg2Object && isArg3Null) {
// new AutoNumeric('.myCssClass > input', { options });
domElement = document.querySelector(arg1);
initialValue = null;
userOptions = arg2;
} else if (isArg1String && isArg2PreDefinedOptionName && isArg3Null) {
// new AutoNumeric('.myCssClass > input', 'euroPos');
domElement = document.querySelector(arg1);
initialValue = null;
userOptions = this._getOptionObject(arg2);
} else if (isArg1String && isArg2Array && isArg3Null) {
// new AutoNumeric('.myCssClass > input', [{ options1 }, { options2 }]); // With multiple option objects
domElement = document.querySelector(arg1);
initialValue = null;
userOptions = this.mergeOptions(arg2);
} else if (isArg1String && (isArg2Null || isArg2EmptyString) && isArg3Object) {
// new AutoNumeric('.myCssClass > input', null, { options });
domElement = document.querySelector(arg1);
initialValue = null;
userOptions = arg3;
} else if (isArg1String && (isArg2Null || isArg2EmptyString) && isArg3Array) {
// new AutoNumeric('.myCssClass > input', null, [{ options1 }, { options2 }]); // With multiple option objects
domElement = document.querySelector(arg1);
initialValue = null;
userOptions = this.mergeOptions(arg3);
} else if (isArg1String && isArg2Number && isArg3Null) {
// new AutoNumeric('.myCssClass > input', 12