Skip to content

Instantly share code, notes, and snippets.

@AlexandreBonneau
Last active April 20, 2017 20:24
Show Gist options
  • Save AlexandreBonneau/24ee7fe74e8b02fa10e74e0c97ee149c to your computer and use it in GitHub Desktop.
Save AlexandreBonneau/24ee7fe74e8b02fa10e74e0c97ee149c to your computer and use it in GitHub Desktop.
This is the live TODO list for feature that could be part of autoNumeric v4. Any ideas? Contribute!

TODO list for autoNumeric v4

ES6 Conversion

  • Finish converting autoNumeric to an ES6 module :
/* 
 * The idea is to use the AutoNumeric :
 * - static methods to access default options and static functions,
 * - class to instantiate `AutoNumeric` elements.
 * 
 * Those `AutoNumeric` elements will hold all the configuration variables and provide an API to manipulate the element value.
 * 
 * In the former case, we can use the AutoNumeric class directly to format/unformat numbers and strings, without having to use a DOM element.
 * In the latter case, we can use an already instantiated `AutoNumeric` object in order to *clone* its configuration for initializing another DOM element.
 * 
 * To sum up :
 * - Use the `AutoNumeric` class as a static object to access default options and static methods,
 * - Instantiate an `AutoNumeric` when you want to *init* an element
 * - Those elements then provides the usual functions (ie. `anElement.set(42)`)
 * - You can use any `AutoNumeric` object to initialize other DOM elements with the same options configuration
 * - All those elements share a common list on which you can use global commands that will be replicated on each individual `AutoNumeric` element.
 */
// Import the autoNumeric module
import AutoNumeric from 'autoNumeric.min';

Initialization (Create an instance of the autoNumeric object)

anElement = new AutoNumeric(domElement); // With the default options
anElement = new AutoNumeric(domElement, { options }); // With one option object
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 those 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, null, { options }); // With a null initial value
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', 12345.789);
anElement = new AutoNumeric('.myCssClass > input', 12345.789, { options });
anElement = new AutoNumeric('.myCssClass > input', null, { options }); // With a null initial value
anElement = new AutoNumeric('.myCssClass > input', 12345.789).french({ options });

Note: AutoNumeric also accepts a limited tag list that it will format on page load, but without adding any event listeners

Initialization of multiple AutoNumeric elements at once

Note: The previous notation $('query selector string').autoNumeric('init') was not deterministic since depending on the selector result (none, one or more elements), autoNumeric initialized an unknown number of elements. With the new API featuring a class, using new AutoNumeric() to generate more than one AutoNumeric object is against all convention and should not be done. In order to initialize multiple AutoNumeric elements at once, you'll need to know in advance that you want to initialize multiple elements and therefore retrieve an array as a result. This can be done by using the new notation (using a static method) :

// ...or init multiple DOM elements in one call (and possibly pass multiple values that will be mapped to each DOM element)
[anElement1, anElement2, anElement3] = AutoNumeric.multiple([domElement1, domElement2, domElement3], { options });
[anElement1, anElement2, anElement3] = AutoNumeric.multiple([domElement1, domElement2, domElement3], 12345.789, { options });
[anElement1, anElement2, anElement3] = AutoNumeric.multiple.french([domElement1, domElement2, domElement3], [12345.789, 234.78, null], { options });

// Special case, if a <form> element is passed (or any other 'parent' (or 'root') DOM element), then autoNumeric will initialize each child <input> elements recursively, ignoring those referenced in the `exclude` attribute
[anElement1, anElement2] = AutoNumeric.multiple({ rootElement: formElement }, { options });
[anElement1, anElement2] = AutoNumeric.multiple({ rootElement: formElement, exclude : [hiddenElement, tokenElement] }, { options });
[anElement1, anElement2] = AutoNumeric.multiple({ rootElement: formElement, exclude : [hiddenElement, tokenElement] }, [12345.789, null], { options });

// If the user wants to select multiple elements via a css selector, then he must use the `multiple` function. Under the hood `QuerySelectorAll` is used.
[anElement1, anElement2] = AutoNumeric.multiple('.myCssClass > input', { options }); // This always return an Array, even if there is only one element selected
[anElement1, anElement2] = AutoNumeric.multiple('.myCssClass > input', [null, 12345.789], { options }); // Idem above, but with passing the initial values too

Options update

// Options can be added/modified after the initialization
// Either by passing an options object that contains multiple options...
anElement.update({ moreOptions });
anElement.update(AutoNumeric.getLanguages().NorthAmerican); // Update the settings (and immediately reformat the element accordingly)
// ...or by changing the options one by one (or by calling a pre-defined option object)
anElement.options.minimumValue('12343567.89');
anElement.options.allowDecimalPadding(false);

// The option object can be accessed directly, thus allowing to query each options too
anElement.getSettings(); // Return the options object containing all the current autoNumeric settings in effect

Then use the usual functions on each autoNumeric-managed element

// AutoNumeric functions
anElement.set(42.76); // Set the value (that will be formatted immediately)
anElement.set(42.76, { options }); // Set the value and update the setting in one go
// anElement.set(42.76, { options }, saveSettings = false); // Set the value with the setting override, but do not save the settings //FIXME Add this later
anElement.setUnformatted(42.76); // Set the value (that will not be formatted immediately)
anElement.setUnformatted(42.76, { options }); // Set the value and update the setting in one go (the value will not be formatted immediately)
anElement.get(); // Alias for the `.getNumericString()` function
anElement.getNumericString(); // Return the unformatted number as a string
anElement.getFormatted(); // Return the formatted string
anElement.getNumber(); // Return the unformatted number as a number
anElement.getLocalized(); // Return the localized unformatted number as a string
anElement.getLocalized(forcedOutputFormat); // Return the localized unformatted number as a string, using the outputFormat option override passed as a parameter
anElement.reformat(); // Force the element to reformat its value again (in case the formatting has been lost)
anElement.unformat(); // Remove the formatting and keep only the raw unformatted value in the element (as a numeric string)
anElement.unformatLocalized(); // Remove the formatting and keep only the localized unformatted value in the element
anElement.unformatLocalized(forcedOutputFormat); // Idem above, but using the outputFormat option override passed as a parameter
anElement.isPristine(); // Return `true` if the current value is the same as when the element got initialized
anElement.select(); // Select the formatted element content, based on the `selectNumberOnly` option
anElement.selectNumber(); // Select only the numbers in the formatted element content, leaving out the currency symbol, whatever the value of the `selectNumberOnly` option
anElement.selectInteger(); // Select only the integer part in the formatted element content, whatever the value of `selectNumberOnly`
anElement.selectDecimal(); // Select only the decimal part in the formatted element content, whatever the value of `selectNumberOnly`
anElement.clear(); // Reset the element value to the empty string '' (or the currency sign, depending on the `emptyInputBehavior` option value)
anElement.clear(true); // Reset the element value to the empty string '' as above, no matter the `emptyInputBehavior` option value

// Un-initialize the AutoNumeric element
anElement.remove(); // Remove the autoNumeric listeners from the element (previous name : 'destroy'). Keep the element content intact.
anElement.wipe(); // Remove the autoNumeric listeners from the element, and reset its value to ''
anElement.nuke(); // Remove the autoNumeric listeners from the element, and delete the DOM element altogether

// Node manipulation
anElement.node(); // Return the DOM element reference of the autoNumeric-managed element
anElement.parent(); // Return the DOM element reference of the parent node of the autoNumeric-managed element
anElement.detach(); // Detach the current AutoNumeric element from the shared 'init' list (which means any changes made on that local shared list will not be transmitted to that element anymore)
anElement.detach(otherAnElement); // Idem above, but detach the given AutoNumeric element, not the current one
anElement.attach(otherAnElement, reFormat = true); // Attach the given AutoNumeric element to the shared local 'init' list. When doing that, by default the DOM content is left untouched. The user can force a reformat with the new shared list options by passing a second argument to `true`.

Use any AutoNumeric element to format/unformat other numbers/DOM elements without having to specify the options each time

// You can use any existing AutoNumeric element to access its methods without having to specify the options since they are already set, and passing another DOM element :
anElement.formatOther(12345, { options }); // Same function signature that when using the AutoNumeric object directly (cf. below: `AutoNumeric.format`), but without having to pass the options
anElement.formatOther(domElement5, { options }); // Idem above, but apply the formatting to the DOM element content directly
anElement.unformatOther('1.234,56 €', { options }); // Same function signature that when using the AutoNumeric object directly (cf. below: `AutoNumeric.unformat`), but without having to pass the options
anElement.unformatOther(domElement5, { options }); // Idem above, but apply the unformatting to the DOM element content directly

Initialize other DOM Elements

// Once you have an AutoNumeric element already setup correctly with the right options, you can use it as many times you want to initialize as many other DOM elements as needed (this works only on elements that can be managed by autoNumeric) :
const anElement3 = anElement.init(domElement3); // Use an existing aN element to initialize another DOM element with the same options
// Whenever `init` is used to initialize other DOM element, a shared 'init' list of those elements is stored in the AutoNumeric objects.
anElement3 = anElement.init(domElement3, true); // If `true` is set as the second argument, then the newly generated AutoNumeric element will not share the same local element list as `anElement`

Perform actions globally on a shared list of AutoNumeric elements

// This list can then be used to perform global operations on all those AutoNumeric elements, with one function call :
anElement.global.set(2000); // Set the value 2000 in all the autoNumeric-managed elements that are shared on this element
anElement.global.setUnformatted(69);
[result1, result2, result3] = anElement.global.get(); // Return an array of results
[result1, result2, result3] = anElement.global.getNumericString(); // Return an array of results
[result1, result2, result3] = anElement.global.getFormatted(); // Return an array of results
[result1, result2, result3] = anElement.global.getNumber(); // Return an array of results
[result1, result2, result3] = anElement.global.getLocalized(); // Return an array of results
anElement.global.reformat();
anElement.global.unformat();
anElement.global.unformatLocalized();
anElement.global.unformatLocalized(forcedOutputFormat);
anElement.global.isPristine(); // Return `true` is *all* the autoNumeric-managed elements are pristine, if their raw value hasn't changed
anElement.global.isPristine(false); // Idem as above, but also checks that the formatted value hasn't changed
anElement.global.clear(); // Clear the value in all the autoNumeric-managed elements that are shared on this element
anElement.global.remove();
anElement.global.wipe();

// ...or use functions specifically designed for that global list
anElement.global.has(domElementOrAutoNumericObject); // Return `true` if the given AutoNumeric object (or DOM element) is in the local AutoNumeric element list
anElement.global.addObject(domElementOrAutoNumericObject); // Add an existing AutoNumeric object (or DOM element) to the local AutoNumeric element list, using the DOM element as the key
anElement.global.removeObject(domElementOrAutoNumericObject); // Remove the given AutoNumeric object (or DOM element) from the local AutoNumeric element list, using the DOM element as the key
anElement.global.empty(); // Remove all elements from the shared list, effectively emptying it
[anElement0, anElement1, anElement2, anElement3] = anElement.global.elements(); // Return an array containing all the AutoNumeric elements that have been initialized by each other
anElement.global.getList(); // Return the `Map` object directly
anElement.global.size(); // Return the number of elements in the local AutoNumeric element list
//FIXME Does updating the settings of one of those AutoNumeric object should update the settings of its siblings too?

Form functions

// Special functions that really work on the parent <form> element, instead of the <input> element itself :
anElement.form(); // Return a reference to the parent <form> element, `null` if it does not exist
anElement.formNumericString(); // Return a string in standard URL-encoded notation with the form input values being unformatted
anElement.formFormatted(); // Return a string in standard URL-encoded notation with the form input values being formatted
anElement.formLocalized(); // Return a string in standard URL-encoded notation with the form input values, with localized values
anElement.formLocalized(forcedOutputFormat); // Idem above, but with the possibility of overriding the `outputFormat` option
anElement.formArrayNumericString(); // Return an array containing an object for each form <input> element, with the values as numeric strings
anElement.formArrayFormatted(); // Return an array containing an object for each form <input> element, with the values as formatted strings
anElement.formArrayLocalized(); // Return an array containing an object for each form <input> element, with the values as localized numeric strings
anElement.formArrayLocalized(forcedOutputFormat); // Idem above, but with the possibility of overriding the `outputFormat` option
anElement.formJsonNumericString(); // Return a JSON string containing an object representing the form input values. This is based on the result of the `formArrayNumericString()` function.
anElement.formJsonFormatted(); // Return a JSON string containing an object representing the form input values. This is based on the result of the `formArrayFormatted()` function.
anElement.formJsonLocalized(); // Return a JSON string containing an object representing the form input values. This is based on the result of the `formArrayLocalized()` function.
anElement.formJsonLocalized(forcedOutputFormat); // Idem above, but with the possibility of overriding the `outputFormat` option
anElement.formUnformat(); // Unformat all the autoNumeric-managed elements that are a child to the parent <form> element of this `anElement` input, to numeric strings
anElement.formUnformatLocalized(); // Unformat all the autoNumeric-managed elements that are a child to the parent <form> element of this `anElement` input, to localized strings
anElement.formReformat(); // Reformat all the autoNumeric-managed elements that are a child to the parent <form> element of this `anElement` input

// The following functions can either take a callback, or not. If they don't, the default `form.submit()` function will be called.
anElement.formSubmitNumericString(callback); // Run the `callback(value)` with `value` being equal to the result of `formNumericString()`
anElement.formSubmitFormatted(callback); // Run the `callback(value)` with `value` being equal to the result of `formFormatted()`
anElement.formSubmitLocalized(callback); // Run the `callback(value)` with `value` being equal to the result of `formLocalized()`
anElement.formSubmitLocalized(forcedOutputFormat, callback); // Idem above, but with the possibility of overriding the `outputFormat` option

// For the following methods, the callback is mandatory
anElement.formSubmitArrayNumericString(callback); // Run the `callback(value)` with `value` being equal to the result of `formArrayNumericString()`
anElement.formSubmitArrayFormatted(callback); // Run the `callback(value)` with `value` being equal to the result of `formArrayFormatted()`
anElement.formSubmitArrayLocalized(callback, forcedOutputFormat); // Idem above, but with the possibility of overriding the `outputFormat` option
anElement.formSubmitJsonNumericString(callback); // Run the `callback(value)` with `value` being equal to the result of `formJsonNumericString()`
anElement.formSubmitJsonFormatted(callback); // Run the `callback(value)` with `value` being equal to the result of `formJsonFormatted()`
anElement.formSubmitJsonLocalized(callback, forcedOutputFormat); // Idem above, but with the possibility of overriding the `outputFormat` option

Static methods

// the AutoNumeric object also provide static methods :
AutoNumeric.validate({ options }); // Check if the given option object is valid, and that each option is valid as well. This throws an error if it's not.
AutoNumeric.areSettingsValid({ options }); // Return `true` if the `options` are valid settings
AutoNumeric.getDefaultConfig(); // Return the default autoNumeric settings 
AutoNumeric.getLanguages(); // Return all the predefined language options in one object
AutoNumeric.getLanguages().French; // Return a specific pre-defined language options object
AutoNumeric.format(12345.21, { options }); // Format the given number (or numeric string) with the given options. This returns the formatted value as a string.
AutoNumeric.format('12345.21', { options }); // Idem as above
AutoNumeric.format(domElement, { options }); // ..or format the `domElement` value with the given options and returns the formatted value as a string. This does *not* update that element value.
AutoNumeric.formatAndSet(domElement, { options }); // Format the `domElement` value with the given options and returns the formatted value as a string. This function does update that element value with the newly formatted value in the process.
AutoNumeric.unformat('1.234,56 €', { options }); // Unformat the given formatted string with the given options. This returns a numeric string.
AutoNumeric.unformat(domElement, { options }); // ..or unformat the `domElement` value with the given options and returns the unformatted numeric string. This does *not* update that element value.
AutoNumeric.unformatAndSet(domElement, { options }); // Unformat the `domElement` value with the given options and returns the unformatted value as a numeric string. This function does update that element value with the newly unformatted value in the process.
AutoNumeric.unformatAndSet(referenceToTheDomElement); // Recursively unformat all the autoNumeric-managed elements that are a child to the `referenceToTheDomElement` element given as a parameter (this is usually the parent `<form>` element)
AutoNumeric.reformatAndSet(referenceToTheDomElement); // Recursively format all the autoNumeric-managed elements that are a child to the `referenceToTheDomElement` element given as a parameter (this is usually the parent `<form>` element), with the settings of each AutoNumeric elements.
AutoNumeric.localize('1.234,56 €', { options }); // Unformat and localize the given formatted string with the given options. This returns a string.
AutoNumeric.localize(domElement, { options }); // Idem as above, but return the localized DOM element value. This does *not* update that element value.
AutoNumeric.localizeAndSet(domElement, { options }); // Unformat and localize the `domElement1` value with the given options and returns the localized value as a string. This function does update that element value with the newly localized value in the process.
AutoNumeric.test(domElement); // Test if the given domElement is already managed by AutoNumeric (if it is initialized)
AutoNumeric.version(); // Return the autoNumeric version number (for debugging purpose)

Function chaining

// All those functions can be chained
anElement.set(42)
         .getNumber(callback1)
         .update({ options })
         .formSubmitJsonNumericString(callback2)
         .clear();
  • Allow autoNumeric to only format without adding any event listeners to an input, or any other DOM elements (that the function would accept as a parameter) (This would be useful for read-only values for instance)
// Initialize without setting up any event listeners
anElement = new AutoNumeric(domElement, 12345.789, { options }).remove(); // This is the default existing way of doing that...
// ...but you can also directly pass a special option `noEventListeners` to prevent the initial creation of those event listeners
anElement = new AutoNumeric(domElement, 12345.789, { noEventListeners: true });
// In that case, it initialize the AutoNumeric element, except it does not add any event listeners. Which means it format the value only once and then let the user modify it freely. The value can be formatted via another call to `set`.
//TODO In case of using the `noEventListeners` option, since we cannot make sure the content of the input is a valid one (because the user could modify its value afterwards), we need to decide if the other methods should be allowed (like `get` or `number`)
  • Allow autoNumeric to initialize an element with the readonly property
// It's possible for the user to want to declare an <input> element as read-only. This is done by passing the `readOnly` option:
anElement = new AutoNumeric(domElement, 12345.789, { readOnly: true });

Wheel events

  • Add an option modifyValueOnWheel (true, false) for using the mousewheel up/down to increment/decrement the current input value
    • a second option wheelStep would allow to define the step, either :
      • fixed '100', or
      • 'progressive' : calculate the number of numbers in the integer part (ie. 9.999.999 -> 7, and add/substract 7-1 = 6th places : 9.999.999 - 100.000)
    • Make sure the user cannot use that wheel event to go past the min and max values. If those are attained, then drop the wheel event to not scroll the page.
    • Allow the user to hold Shift while scrolling to ignore the input and just scroll the page (useful on small screen where some badly configured inputs could use all the available space)

Cancellable

  • Add a cancellable feature :
    • on focus, you save the input value
    • if the user change the input value, and hit 'Escape', then the initial value saved on focus is set back
    • on the other hand if the user either have used 'Enter' to validate ('Enter' throw a change event) his entries, or if the input value has been changed by another script in the mean time, then we save the new input value
    • bonus; if the value has not changed, hitting 'Esc' just select all the input value

Other features and tasks

  • Make sure ctrl+z (undo) is always usable (after an input, but also after a paste)
  • Create enumerations for every options that allows only a set of values :
import AutoNumeric from 'autoNumeric.min';
new AutoNumeric(domElement, { onInvalidPaste : AutoNumeric.options.onInvalidPaste.error });
  • Fix as many //FIXME in the source code as possible
  • Evaluate and fix all the //TODO in the source code
  • Finish removing all jQuery dependencies
  • Rewrite all the event handling (by dropping the keypress event)
  • Remove any direct reference to a key or char code, and use the real character (given by character(event))
  • Write more tests in order to get better coverage
  • If the user hover the autoNumeric-managed element then press 'Alt', display the unformatted value inside the hovered element
  • Add support for changing the input style (background and text color), based on rules like 'positive', 'negative', 'within range', etc. We could either use default colors, or allow the user to pass its own css class names
    • Color if the value is positive
    • Color if the value is negative
    • Color if the value is within the given range (and multiple ranges can be defined)
const styleRules = {
    positive: 'positiveAn',
    negative: 'negativeAn',
    range : [
        { min: -15, max: 0, class: 'red' },
        { min: 0, max: 20, class: 'orange' },
        { min: 20, max: 40, class: 'yellow' },
        { min: 40, max: Number.MAX_SAFE_INTEGER, class: 'green' },
    ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment