MediaWiki:Gadget-calculator-core.js
From WikiAnesthesia
Revision as of 07:07, 19 July 2021 by Chris Rishel (talk | contribs)
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/** * @author Chris Rishel */ ( function() { const INPUTSIZE_FULL = 'full'; const INPUTSIZE_COMPACT = 'compact'; const VALID_INPUTSIZES = [ INPUTSIZE_FULL, INPUTSIZE_COMPACT ]; const TYPE_NUMBER = 'number'; const TYPE_STRING = 'string'; const VALID_TYPES = [ TYPE_NUMBER, TYPE_STRING ]; /** * Class CalculatorObject * * @param {Object} varData * @param {Object} propertyData * @returns {Object} * @constructor */ function CalculatorObject( varData, propertyData ) { if( propertyData ) { if( propertyData.hasOwnProperty( 'required' ) ) { for( var iRequiredProperty in propertyData.required ) { var requiredProperty = propertyData.required[ iRequiredProperty ]; if( !varData.hasOwnProperty( requiredProperty ) ) { console.error( 'Missing required property "' + requiredProperty + '"' ); console.log( varData ); return null; } this[ requiredProperty ] = varData[ requiredProperty ]; } } if( propertyData.hasOwnProperty( 'optional' ) ) { for( var iOptionalProperty in propertyData.optional ) { var optionalProperty = propertyData.optional[ iOptionalProperty ]; if( varData.hasOwnProperty( optionalProperty ) ) { this[ optionalProperty ] = varData[ optionalProperty ]; } else { this[ optionalProperty ] = null; } } } } } /** * Class CalculatorVariable * @param {Object} varData * @returns {CalculatorVariable} * @constructor */ function CalculatorVariable( varData ) { var propertyData = { required: [ 'id', 'name', 'type' ], optional: [ 'abbreviation', 'defaultValue', 'maxLength', 'options', 'renderUnits', 'units' ] }; CalculatorObject.call( this, varData, propertyData ); if( VALID_TYPES.indexOf( this.type ) === -1 ) { throw new Error( 'Invalid type "' + this.type + '" for variable "' + this.id + '"' ); } var cookieValue = mw.calculators.getCookieValue( this.id ); if( cookieValue ) { this.setValue( cookieValue ); } else if( this.defaultValue ) { this.setValue( this.defaultValue ); } else { this.value = null; } } CalculatorVariable.prototype = Object.create( CalculatorObject.prototype ); CalculatorVariable.prototype.createInput = function( options ) { // Initialize options options = ( typeof options !== 'undefined' ) ? options : {}; if( !options.hasOwnProperty( 'size' ) ) { options.size = 'full'; } else if( VALID_INPUTSIZES.indexOf( options.size ) === -1 ) { throw new Error( 'Invalid input size "' + options.size + '" for variable "' + this.getId() + '"' ); } var value = this.getValue(); var inputContainerAttribs = { class: 'col-auto calculator-container-input' }; if( options.size === INPUTSIZE_COMPACT ) { inputContainerAttribs.class = inputContainerAttribs.class + ' calculator-container-input-compact'; } inputContainerAttribs.class = inputContainerAttribs.class + ' calculator-container-input-' + this.getName(); // Create the input container var $inputContainer = $( '<div>', inputContainerAttribs ); if( this.getType() === TYPE_NUMBER ) { // Set the input id var inputId = 'calculator-input-' + this.getId(); // Initialize label attributes var labelAttributes = { for: inputId, text: this.getName() }; // Initialize input options var inputAttributes = { id: inputId, class: 'form-control calculator-input-text', type: 'text', inputmode: 'decimal', placeholder: this.getName(), value: this.isValueMathObject() ? value.toNumber() : value }; // Configure additional options if( this.maxLength ) { inputAttributes.maxlength = this.maxLength; } if( options.size === INPUTSIZE_COMPACT ) { // Only use label for screen readers labelAttributes.class = 'sr-only'; inputAttributes.class = inputAttributes.class + ' calculator-input-text-compact'; // Switch the placeholder to the abbreviation if defined if( this.abbreviation ) { inputAttributes.placeholder = this.abbreviation; } // Set the size of the input to the maxlength if defined if( inputAttributes.hasOwnProperty( 'maxlength' ) ) { inputAttributes.size = inputAttributes.maxlength; } } // Add the input id to the list of classes inputAttributes.class = inputAttributes.class + ' ' + inputId; // Create the input and add handlers var $input = $( '<input>', inputAttributes ) .on( 'input', function() { console.log( 'woo' ); console.log( $( this ).val() ); } ); // Create the input label and append to the container $inputContainer.append( $( '<label>', labelAttributes ) ); // Create the input group var $inputGroup = $( '<div>', { class: 'input-group' } ).append( $input ); // If the variable has units, create the units input if( this.hasUnits() ) { var unitsId = inputId + '-units'; var unitsValue = this.isValueMathObject() ? value.formatUnits() : null; // Create the units container var $unitsContainer = $( '<div>', { class: 'input-group-append' } ); // Initialize the units input options var unitsInputAttributes = { id: unitsId, class: 'custom-select calculator-input-select' }; if( options.size === INPUTSIZE_COMPACT ) { unitsInputAttributes.class = unitsInputAttributes.class + ' calculator-input-select-compact'; } unitsInputAttributes.class = unitsInputAttributes.class + ' ' + unitsId; var $unitsInput = $( '<select>', unitsInputAttributes ); for( var iUnits in this.units ) { var units = this.units[ iUnits ]; var unitsOptionAttributes = { text: units, value: units }; // Apply custom rendering function for units if defined if( typeof this.renderUnits === 'function' ) { unitsOptionAttributes.text = this.renderUnits( units ); } if( units === unitsValue ) { unitsOptionAttributes.selected = true; } $unitsInput.append( $( '<option>', unitsOptionAttributes ) ); } $unitsContainer.append( $unitsInput ); $inputGroup.append( $unitsContainer ); } $inputContainer.append( $inputGroup ); } else if( this.getType === TYPE_STRING ) { } return $inputContainer; }; CalculatorVariable.prototype.getId = function() { return this.id; }; CalculatorVariable.prototype.getName = function() { return this.name; }; CalculatorVariable.prototype.getType = function() { return this.type; }; CalculatorVariable.prototype.getValue = function() { return this.value; }; CalculatorVariable.prototype.getValueString = function() { return String( this.value ); }; CalculatorVariable.prototype.hasUnits = function() { return this.units !== null; }; CalculatorVariable.prototype.isValueMathObject = function() { return this.value && this.value.hasOwnProperty( 'value' ); }; CalculatorVariable.prototype.setValue = function( value ) { if( this.type === 'number' ) { if( typeof value !== 'object' ) { value = math.unit( value ); } if( this.units ) { var valueUnits = value.formatUnits(); if( !valueUnits ) { throw new Error( 'Could not set value for "' + this.name + '": Value must define units' ); } else if( this.units.indexOf( valueUnits ) === -1 ) { throw new Error( 'Could not set value for "' + this.name + '": Units "' + valueUnits + '" are not valid for this variable' ); } } } this.value = value; return true; }; mw.calculators = { variables: {}, addVariables: function( variables ) { for( var varName in variables ) { if( mw.calculators.variables.hasOwnProperty( varName ) ) { console.warn( 'Calculation variable "' + varName + '" already defined.' ); continue; } var varData = variables[ varName ]; varData.id = varName; var calculationVariable = new CalculatorVariable( varData ); if( calculationVariable ) { mw.calculators.variables[ varName ] = calculationVariable; } } }, defineUnits: function() { // Create aliases for abbreviations of existing units math.createUnit( 'dy', { definition: '1 day' } ); math.createUnit( 'mo', { definition: '1 month' } ); math.createUnit( 'yr', { definition: '1 year' } ); // Body weight needs to be treated as a different fundamental unit type compared to mass for stoichiometry // Gram-weight, which uses short SI prefixes (e.g. 1 kgwt = 1000 gwt) math.createUnit( 'gwt', { prefixes: 'short' } ); // Pound-weight, which uses no prefixes math.createUnit( 'lbwt', { definition: '453.59237 gwt' } ); math.createUnit( 'puff' ); math.createUnit( 'unit', { aliases: [ 'units' ] } ); math.createUnit( 'vial', { aliases: [ 'vials' ] } ); }, getCookieKey: function( varName ) { return 'calculators-var-' + varName; }, getCookieValue: function( varName ) { if( mw.calculators.variables.hasOwnProperty( varName ) ) { var cookieValue = mw.cookie.get( mw.patientCalculators.getCookieKey( varName ) ); if( !cookieValue ) { return null; } return cookieValue; } else { return null; } }, getVariable: function( varName ) { if( mw.calculators.variables.hasOwnProperty( varName ) ) { return mw.calculators.variables[ varName ]; } else { return null; } }, getValue: function( varName ) { if( mw.calculators.variables.hasOwnProperty( varName ) ) { return mw.calculators.variables[ varName ].getValue(); } else { return null; } }, init: function() { mw.calculators.defineUnits(); }, setValue: function( varName, value ) { if( !mw.calculators.variables.hasOwnProperty( varName ) ) { return false; } return mw.calculators.variables[ varName ].setValue( value ); } }; mw.calculators.init(); }() );