cleaned up method
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
export function getAssetVariables() {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
// ChildRegistrationUtils.js
|
|
||||||
class ChildRegistrationUtils {
|
|
||||||
constructor(mainClass) {
|
|
||||||
this.mainClass = mainClass; // Reference to the main class
|
|
||||||
this.logger = mainClass.logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
async registerChild(child, positionVsParent) {
|
|
||||||
const { softwareType } = child.config.functionality;
|
|
||||||
const { name, id, unit } = child.config.general;
|
|
||||||
const { type = "", subType = "" } = child.config.asset || {};
|
|
||||||
const emitter = child.emitter;
|
|
||||||
|
|
||||||
//define position vs parent in child
|
|
||||||
child.positionVsParent = positionVsParent;
|
|
||||||
child.parent = this.mainClass;
|
|
||||||
|
|
||||||
if (!this.mainClass.child) this.mainClass.child = {};
|
|
||||||
if (!this.mainClass.child[softwareType])
|
|
||||||
this.mainClass.child[softwareType] = {};
|
|
||||||
if (!this.mainClass.child[softwareType][type])
|
|
||||||
this.mainClass.child[softwareType][type] = {};
|
|
||||||
if (!this.mainClass.child[softwareType][type][subType])
|
|
||||||
this.mainClass.child[softwareType][type][subType] = {};
|
|
||||||
|
|
||||||
// Use an array to handle multiple subtypes
|
|
||||||
if (!Array.isArray(this.mainClass.child[softwareType][type][subType])) {
|
|
||||||
this.mainClass.child[softwareType][type][subType] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the child in the cloud when available and supply the new child on base of tagcode OLIFANT WE NEED TO FIX THIS SO ITS DYNAMIC!
|
|
||||||
/*
|
|
||||||
try{
|
|
||||||
const url = "https://pimmoerman.nl/rdlab/tagcode.app/v2.1/api/asset/create_asset.php?";
|
|
||||||
const TagCode = child.config.asset.tagCode;
|
|
||||||
//console.log(`Register child => ${TagCode}`);
|
|
||||||
const completeURL = url + `asset_product_model_id=1&asset_product_model_uuid=123456789&asset_name=AssetNaam&asset_description=Beschrijving&asset_status=actief&asset_profile_id=1&asset_location_id=1&asset_process_id=11&asset_tag_number=${TagCode}&child_assets=[L6616]`;
|
|
||||||
|
|
||||||
await fetch(completeURL, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}catch(e){
|
|
||||||
console.log("Error saving assetID and tagnumber", e);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Push the new child to the array of the mainclass so we can track the childs
|
|
||||||
this.mainClass.child[softwareType][type][subType].push({
|
|
||||||
name,
|
|
||||||
id,
|
|
||||||
unit,
|
|
||||||
emitter,
|
|
||||||
});
|
|
||||||
|
|
||||||
//then connect the child depending on the type subtype etc..
|
|
||||||
this.connectChild(
|
|
||||||
id,
|
|
||||||
softwareType,
|
|
||||||
emitter,
|
|
||||||
type,
|
|
||||||
child,
|
|
||||||
subType,
|
|
||||||
positionVsParent
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
connectChild(
|
|
||||||
id,
|
|
||||||
softwareType,
|
|
||||||
emitter,
|
|
||||||
type,
|
|
||||||
child,
|
|
||||||
subType,
|
|
||||||
positionVsParent
|
|
||||||
) {
|
|
||||||
this.logger.debug(
|
|
||||||
`Connecting child id=${id}: desc=${softwareType}, type=${type},subType=${subType}, position=${positionVsParent}`
|
|
||||||
);
|
|
||||||
|
|
||||||
switch (softwareType) {
|
|
||||||
case "measurement":
|
|
||||||
this.logger.debug(
|
|
||||||
`Registering measurement child: ${id} with type=${type}`
|
|
||||||
);
|
|
||||||
this.connectMeasurement(child, subType, positionVsParent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "machine":
|
|
||||||
this.logger.debug(`Registering complete machine child: ${id}`);
|
|
||||||
this.connectMachine(child);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "valve":
|
|
||||||
this.logger.debug(`Registering complete valve child: ${id}`);
|
|
||||||
this.connectValve(child);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "actuator":
|
|
||||||
this.logger.debug(`Registering linear actuator child: ${id}`);
|
|
||||||
this.connectActuator(child,positionVsParent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
this.logger.error(`Child registration unrecognized desc: ${desc}`);
|
|
||||||
this.logger.error(`Unrecognized softwareType: ${softwareType}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connectMeasurement(child, subType, position) {
|
|
||||||
this.logger.debug(
|
|
||||||
`Connecting measurement child: ${subType} with position=${position}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if subType is valid
|
|
||||||
if (!subType) {
|
|
||||||
this.logger.error(`Invalid subType for measurement: ${subType}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize the measurement to a number - logging each step for debugging
|
|
||||||
try {
|
|
||||||
this.logger.debug(
|
|
||||||
`Initializing measurement: ${subType}, position: ${position} value: 0`
|
|
||||||
);
|
|
||||||
const typeResult = this.mainClass.measurements.type(subType);
|
|
||||||
const variantResult = typeResult.variant("measured");
|
|
||||||
const positionResult = variantResult.position(position);
|
|
||||||
positionResult.value(0);
|
|
||||||
|
|
||||||
this.logger.debug(
|
|
||||||
`Subscribing on mAbs event for measurement: ${subType}, position: ${position}`
|
|
||||||
);
|
|
||||||
// Listen for the mAbs event and update the measurement
|
|
||||||
|
|
||||||
this.logger.debug(
|
|
||||||
`Successfully initialized measurement: ${subType}, position: ${position}`
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Failed to initialize measurement: ${error.message}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
child.emitter.on("mAbs", (value) => {
|
|
||||||
// Use the same method chaining approach that worked during initialization
|
|
||||||
this.mainClass.measurements
|
|
||||||
.type(subType)
|
|
||||||
.variant("measured")
|
|
||||||
.position(position)
|
|
||||||
.value(value);
|
|
||||||
this.mainClass.updateMeasurement("measured", subType, value, position);
|
|
||||||
//this.logger.debug(`--------->>>>>>>>>Updated measurement: ${subType}, value: ${value}, position: ${position}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
connectMachine(machine) {
|
|
||||||
if (!machine) {
|
|
||||||
this.logger.error("Invalid machine provided.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const machineId = Object.keys(this.mainClass.machines).length + 1;
|
|
||||||
this.mainClass.machines[machineId] = machine;
|
|
||||||
|
|
||||||
this.logger.info(
|
|
||||||
`Setting up pressureChange listener for machine ${machineId}`
|
|
||||||
);
|
|
||||||
|
|
||||||
machine.emitter.on("pressureChange", () =>
|
|
||||||
this.mainClass.handlePressureChange(machine)
|
|
||||||
);
|
|
||||||
|
|
||||||
//update of child triggers the handler
|
|
||||||
this.mainClass.handleChildChange();
|
|
||||||
|
|
||||||
this.logger.info(`Machine ${machineId} registered successfully.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
connectValve(valve) {
|
|
||||||
if (!valve) {
|
|
||||||
this.logger.warn("Invalid valve provided.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const valveId = Object.keys(this.mainClass.valves).length + 1;
|
|
||||||
this.mainClass.valves[valveId] = valve; // Gooit valve object in de valves attribute met valve objects
|
|
||||||
|
|
||||||
valve.state.emitter.on("positionChange", (data) => {
|
|
||||||
//ValveGroupController abboneren op klepstand verandering
|
|
||||||
this.mainClass.logger.debug(`Position change of valve detected: ${data}`);
|
|
||||||
this.mainClass.calcValveFlows();
|
|
||||||
}); //bepaal nieuwe flow per valve
|
|
||||||
valve.emitter.on("deltaPChange", () => {
|
|
||||||
this.mainClass.logger.debug("DeltaP change of valve detected");
|
|
||||||
this.mainClass.calcMaxDeltaP();
|
|
||||||
}); //bepaal nieuwe max deltaP
|
|
||||||
|
|
||||||
this.logger.info(`Valve ${valveId} registered successfully.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
connectActuator(actuator, positionVsParent) {
|
|
||||||
if (!actuator) {
|
|
||||||
this.logger.warn("Invalid actuator provided.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Special case gateGroupControl
|
|
||||||
if (
|
|
||||||
this.mainClass.config.functionality.softwareType == "gateGroupControl"
|
|
||||||
) {
|
|
||||||
if (Object.keys(this.mainClass.actuators).length < 2) {
|
|
||||||
if (positionVsParent == "downstream") {
|
|
||||||
this.mainClass.actuators[0] = actuator;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (positionVsParent == "upstream") {
|
|
||||||
this.mainClass.actuators[1] = actuator;
|
|
||||||
}
|
|
||||||
//define emitters
|
|
||||||
actuator.state.emitter.on("positionChange", (data) => {
|
|
||||||
this.mainClass.logger.debug(`Position change of actuator detected: ${data}`);
|
|
||||||
this.mainClass.eventUpdate();
|
|
||||||
});
|
|
||||||
|
|
||||||
//define emitters
|
|
||||||
actuator.state.emitter.on("stateChange", (data) => {
|
|
||||||
this.mainClass.logger.debug(`State change of actuator detected: ${data}`);
|
|
||||||
this.mainClass.eventUpdate();
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.logger.error(
|
|
||||||
"Too many actuators registered. Only two are allowed."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//wanneer hij deze ontvangt is deltaP van een van de valves veranderd (kan ook zijn niet child zijn, maar dat maakt niet uit)
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ChildRegistrationUtils;
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file configUtils.js
|
|
||||||
*
|
|
||||||
* Permission is hereby granted to any person obtaining a copy of this software
|
|
||||||
* and associated documentation files (the "Software"), to use it for personal
|
|
||||||
* or non-commercial purposes, with the following restrictions:
|
|
||||||
*
|
|
||||||
* 1. **No Copying or Redistribution**: The Software or any of its parts may not
|
|
||||||
* be copied, merged, distributed, sublicensed, or sold without explicit
|
|
||||||
* prior written permission from the author.
|
|
||||||
*
|
|
||||||
* 2. **Commercial Use**: Any use of the Software for commercial purposes requires
|
|
||||||
* a valid license, obtainable only with the explicit consent of the author.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* Ownership of this code remains solely with the original author. Unauthorized
|
|
||||||
* use of this Software is strictly prohibited.
|
|
||||||
*
|
|
||||||
* @summary Utility for managing and validating configuration values.
|
|
||||||
* @description Utility for managing and validating configuration values.
|
|
||||||
* @module ConfigUtils
|
|
||||||
* @requires ValidationUtils
|
|
||||||
* @requires Logger
|
|
||||||
* @exports ConfigUtils
|
|
||||||
* @version 0.1.0
|
|
||||||
* @since 0.1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
const ValidationUtils = require("./validationUtils");
|
|
||||||
const Logger = require("./logger");
|
|
||||||
|
|
||||||
class ConfigUtils {
|
|
||||||
constructor(defaultConfig, IloggerEnabled , IloggerLevel) {
|
|
||||||
const loggerEnabled = IloggerEnabled || true;
|
|
||||||
const loggerLevel = IloggerLevel || "warn";
|
|
||||||
this.logger = new Logger(loggerEnabled, loggerLevel, 'ConfigUtils');
|
|
||||||
this.defaultConfig = defaultConfig;
|
|
||||||
this.validationUtils = new ValidationUtils(loggerEnabled, loggerLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize configuration
|
|
||||||
initConfig(config) {
|
|
||||||
this.logger.info("Initializing configuration...");
|
|
||||||
|
|
||||||
// Validate the provided configuration
|
|
||||||
const validatedConfig = this.validationUtils.validateSchema(config, this.defaultConfig, this.defaultConfig.functionality.softwareType.default);
|
|
||||||
|
|
||||||
this.logger.info("Configuration initialized successfully.");
|
|
||||||
|
|
||||||
return validatedConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Update configuration
|
|
||||||
updateConfig(currentConfig, newConfig) {
|
|
||||||
this.logger.info("Updating configuration...");
|
|
||||||
|
|
||||||
// Merge 2 configs and validate the result
|
|
||||||
const mergedConfig = this.mergeObjects(currentConfig, newConfig);
|
|
||||||
|
|
||||||
// Merge current configuration with new values
|
|
||||||
const updatedConfig = this.validationUtils.validateSchema(mergedConfig, this.defaultConfig, this.defaultConfig.functionality.softwareType.default);
|
|
||||||
|
|
||||||
this.logger.info("Configuration updated successfully.");
|
|
||||||
return updatedConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
// loop through objects and merge them obj1 will be updated with obj2 values
|
|
||||||
mergeObjects(obj1, obj2) {
|
|
||||||
for (let key in obj2) {
|
|
||||||
if (obj2.hasOwnProperty(key)) {
|
|
||||||
if (typeof obj2[key] === 'object') {
|
|
||||||
if (!obj1[key]) {
|
|
||||||
obj1[key] = {};
|
|
||||||
}
|
|
||||||
this.mergeObjects(obj1[key], obj2[key]);
|
|
||||||
} else {
|
|
||||||
obj1[key] = obj2[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ConfigUtils;
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
class Logger {
|
|
||||||
constructor(logging = true, logLevel = 'debug', nameModule = 'N/A') {
|
|
||||||
this.logging = logging; // Boolean flag to enable/disable logging
|
|
||||||
this.logLevel = logLevel; // Default log level: 'debug', 'info', 'warn', 'error'
|
|
||||||
this.levels = ['debug', 'info', 'warn', 'error']; // Log levels in order of severity
|
|
||||||
this.nameModule = nameModule; // Name of the module that uses the logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to check if a log message should be displayed based on current log level
|
|
||||||
shouldLog(level) {
|
|
||||||
return this.levels.indexOf(level) >= this.levels.indexOf(this.logLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log a debug message
|
|
||||||
debug(message) {
|
|
||||||
if (this.logging && this.shouldLog('debug')) {
|
|
||||||
console.debug(`[DEBUG] -> ${this.nameModule}: ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log an info message
|
|
||||||
info(message) {
|
|
||||||
if (this.logging && this.shouldLog('info')) {
|
|
||||||
console.info(`[INFO] -> ${this.nameModule}: ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log a warning message
|
|
||||||
warn(message) {
|
|
||||||
if (this.logging && this.shouldLog('warn')) {
|
|
||||||
console.warn(`[WARN] -> ${this.nameModule}: ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log an error message
|
|
||||||
error(message) {
|
|
||||||
if (this.logging && this.shouldLog('error')) {
|
|
||||||
console.error(`[ERROR] -> ${this.nameModule}: ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the log level dynamically
|
|
||||||
setLogLevel(level) {
|
|
||||||
if (this.levels.includes(level)) {
|
|
||||||
this.logLevel = level;
|
|
||||||
} else {
|
|
||||||
console.error(`[ERROR ${nameModule}]: Invalid log level: ${level}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle the logging on or off
|
|
||||||
toggleLogging() {
|
|
||||||
this.logging = !this.logging;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Logger;
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
// Add unit conversion support
|
|
||||||
const convertModule = require('../../../convert/dependencies/index');
|
|
||||||
|
|
||||||
class Measurement {
|
|
||||||
constructor(type, variant, position, windowSize) {
|
|
||||||
this.type = type; // e.g. 'pressure', 'flow', etc.
|
|
||||||
this.variant = variant; // e.g. 'predicted' or 'measured', etc..
|
|
||||||
this.position = position; // Downstream or upstream of parent object
|
|
||||||
this.windowSize = windowSize; // Rolling window size
|
|
||||||
|
|
||||||
// Place all data inside an array
|
|
||||||
this.values = []; // Array to store all values
|
|
||||||
this.timestamps = []; // Array to store all timestamps
|
|
||||||
|
|
||||||
// Unit tracking
|
|
||||||
this.unit = null; // Current unit of measurement
|
|
||||||
|
|
||||||
// For tracking differences if this is a calculated difference measurement
|
|
||||||
this.isDifference = false;
|
|
||||||
this.sourcePositions = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- Updater methods --
|
|
||||||
setType(type) {
|
|
||||||
this.type = type;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setVariant(variant) {
|
|
||||||
this.variant = variant;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setPosition(position) {
|
|
||||||
this.position = position;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue(value, timestamp = Date.now()) {
|
|
||||||
/*
|
|
||||||
if (value === undefined || value === null) {
|
|
||||||
value = null ;
|
|
||||||
//throw new Error('Value cannot be null or undefined');
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//shift the oldest value
|
|
||||||
if(this.values.length >= this.windowSize){
|
|
||||||
this.values.shift();
|
|
||||||
this.timestamps.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
//push the new value
|
|
||||||
this.values.push(value);
|
|
||||||
this.timestamps.push(timestamp);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setUnit(unit) {
|
|
||||||
this.unit = unit;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- Getter methods --
|
|
||||||
getCurrentValue() {
|
|
||||||
if (this.values.length === 0) return null;
|
|
||||||
return this.values[this.values.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
getAverage() {
|
|
||||||
if (this.values.length === 0) return null;
|
|
||||||
const sum = this.values.reduce((acc, val) => acc + val, 0);
|
|
||||||
return sum / this.values.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMin() {
|
|
||||||
if (this.values.length === 0) return null;
|
|
||||||
return Math.min(...this.values);
|
|
||||||
}
|
|
||||||
|
|
||||||
getMax() {
|
|
||||||
if (this.values.length === 0) return null;
|
|
||||||
return Math.max(...this.values);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAllValues() {
|
|
||||||
return {
|
|
||||||
values: [...this.values],
|
|
||||||
timestamps: [...this.timestamps],
|
|
||||||
unit: this.unit
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- Position-based methods --
|
|
||||||
|
|
||||||
// Create a new measurement that is the difference between two positions
|
|
||||||
static createDifference(upstreamMeasurement, downstreamMeasurement) {
|
|
||||||
console.log('hello:');
|
|
||||||
if (upstreamMeasurement.type !== downstreamMeasurement.type ||
|
|
||||||
upstreamMeasurement.variant !== downstreamMeasurement.variant) {
|
|
||||||
throw new Error('Cannot calculate difference between different measurement types or variants');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure units match
|
|
||||||
let downstream = downstreamMeasurement;
|
|
||||||
if (upstreamMeasurement.unit && downstream.unit &&
|
|
||||||
upstreamMeasurement.unit !== downstream.unit) {
|
|
||||||
downstream = downstream.convertTo(upstreamMeasurement.unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new difference measurement
|
|
||||||
const diffMeasurement = new Measurement(
|
|
||||||
upstreamMeasurement.type,
|
|
||||||
upstreamMeasurement.variant,
|
|
||||||
'difference',
|
|
||||||
Math.min(upstreamMeasurement.windowSize, downstream.windowSize)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Mark as a difference measurement and keep track of sources
|
|
||||||
diffMeasurement.isDifference = true;
|
|
||||||
diffMeasurement.sourcePositions = ['upstream', 'downstream'];
|
|
||||||
|
|
||||||
// Calculate all differences where timestamps match
|
|
||||||
const upValues = upstreamMeasurement.getAllValues();
|
|
||||||
const downValues = downstream.getAllValues();
|
|
||||||
|
|
||||||
// Match timestamps and calculate differences
|
|
||||||
for (let i = 0; i < upValues.timestamps.length; i++) {
|
|
||||||
const upTimestamp = upValues.timestamps[i];
|
|
||||||
const downIndex = downValues.timestamps.indexOf(upTimestamp);
|
|
||||||
|
|
||||||
if (downIndex !== -1) {
|
|
||||||
|
|
||||||
const diff = upValues.values[i] - downValues.values[downIndex];
|
|
||||||
diffMeasurement.setValue(diff, upTimestamp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diffMeasurement.setUnit(upstreamMeasurement.unit);
|
|
||||||
|
|
||||||
return diffMeasurement;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- Additional getter methods --
|
|
||||||
getLatestTimestamp() {
|
|
||||||
if (this.timestamps.length === 0) return null;
|
|
||||||
return this.timestamps[this.timestamps.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
getValueAtTimestamp(timestamp) {
|
|
||||||
const index = this.timestamps.indexOf(timestamp);
|
|
||||||
if (index === -1) return null;
|
|
||||||
return this.values[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- Unit conversion methods --
|
|
||||||
convertTo(targetUnit) {
|
|
||||||
if (!this.unit) {
|
|
||||||
throw new Error('Current unit not set, cannot convert');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const convertedValues = this.values.map(value =>
|
|
||||||
convertModule.convert(value).from(this.unit).to(targetUnit)
|
|
||||||
);
|
|
||||||
|
|
||||||
const newMeasurement = new Measurement(
|
|
||||||
this.type,
|
|
||||||
this.variant,
|
|
||||||
this.position,
|
|
||||||
this.windowSize
|
|
||||||
);
|
|
||||||
|
|
||||||
// Copy values and timestamps
|
|
||||||
newMeasurement.values = convertedValues;
|
|
||||||
newMeasurement.timestamps = [...this.timestamps];
|
|
||||||
newMeasurement.unit = targetUnit;
|
|
||||||
|
|
||||||
return newMeasurement;
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error(`Unit conversion failed: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Measurement;
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
const Measurement = require('./Measurement');
|
|
||||||
|
|
||||||
class MeasurementBuilder {
|
|
||||||
constructor() {
|
|
||||||
this.type = null;
|
|
||||||
this.variant = null;
|
|
||||||
this.position = null;
|
|
||||||
this.windowSize = 10; // Default window size
|
|
||||||
}
|
|
||||||
|
|
||||||
// e.g. 'pressure', 'flow', etc.
|
|
||||||
setType(type) {
|
|
||||||
this.type = type;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// e.g. 'predicted' or 'measured', etc..
|
|
||||||
setVariant(variant) {
|
|
||||||
this.variant = variant;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Downstream or upstream of parent object
|
|
||||||
setPosition(position) {
|
|
||||||
this.position = position;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// default size of the data that gets tracked
|
|
||||||
setWindowSize(windowSize) {
|
|
||||||
this.windowSize = windowSize;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
build() {
|
|
||||||
// Validate required fields
|
|
||||||
if (!this.type) {
|
|
||||||
throw new Error('Measurement type is required');
|
|
||||||
}
|
|
||||||
if (!this.variant) {
|
|
||||||
throw new Error('Measurement variant is required');
|
|
||||||
}
|
|
||||||
if (!this.position) {
|
|
||||||
throw new Error('Measurement position is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Measurement(
|
|
||||||
this.type,
|
|
||||||
this.variant,
|
|
||||||
this.position,
|
|
||||||
this.windowSize
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = MeasurementBuilder;
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
const MeasurementBuilder = require('./MeasurementBuilder');
|
|
||||||
|
|
||||||
class MeasurementContainer {
|
|
||||||
constructor(options = {}, logger) {
|
|
||||||
this.logger = logger;
|
|
||||||
this.measurements = {};
|
|
||||||
this.windowSize = options.windowSize || 10; // Default window size
|
|
||||||
|
|
||||||
// For chaining context
|
|
||||||
this._currentType = null;
|
|
||||||
this._currentVariant = null;
|
|
||||||
this._currentPosition = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chainable methods
|
|
||||||
type(typeName) {
|
|
||||||
this._currentType = typeName;
|
|
||||||
this._currentVariant = null;
|
|
||||||
this._currentPosition = null;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
variant(variantName) {
|
|
||||||
if (!this._currentType) {
|
|
||||||
throw new Error('Type must be specified before variant');
|
|
||||||
}
|
|
||||||
this._currentVariant = variantName;
|
|
||||||
this._currentPosition = null;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
position(positionName) {
|
|
||||||
if (!this._currentVariant) {
|
|
||||||
throw new Error('Variant must be specified before position');
|
|
||||||
}
|
|
||||||
this._currentPosition = positionName;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Core methods that complete the chain
|
|
||||||
value(val, timestamp = Date.now()) {
|
|
||||||
if (!this._ensureChainIsValid()) return this;
|
|
||||||
|
|
||||||
const measurement = this._getOrCreateMeasurement();
|
|
||||||
measurement.setValue(val, timestamp);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
unit(unitName) {
|
|
||||||
if (!this._ensureChainIsValid()) return this;
|
|
||||||
|
|
||||||
const measurement = this._getOrCreateMeasurement();
|
|
||||||
measurement.setUnit(unitName);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Terminal operations - get data out
|
|
||||||
get() {
|
|
||||||
if (!this._ensureChainIsValid()) return null;
|
|
||||||
return this._getOrCreateMeasurement();
|
|
||||||
}
|
|
||||||
|
|
||||||
getCurrentValue() {
|
|
||||||
const measurement = this.get();
|
|
||||||
return measurement ? measurement.getCurrentValue() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAverage() {
|
|
||||||
const measurement = this.get();
|
|
||||||
return measurement ? measurement.getAverage() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMin() {
|
|
||||||
const measurement = this.get();
|
|
||||||
return measurement ? measurement.getMin() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMax() {
|
|
||||||
const measurement = this.get();
|
|
||||||
return measurement ? measurement.getMax() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAllValues() {
|
|
||||||
const measurement = this.get();
|
|
||||||
return measurement ? measurement.getAllValues() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Difference calculations between positions
|
|
||||||
difference() {
|
|
||||||
if (!this._currentType || !this._currentVariant) {
|
|
||||||
throw new Error('Type and variant must be specified for difference calculation');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save position to restore chain state after operation
|
|
||||||
const savedPosition = this._currentPosition;
|
|
||||||
|
|
||||||
// Get upstream measurement
|
|
||||||
this._currentPosition = 'upstream';
|
|
||||||
const upstream = this.get();
|
|
||||||
|
|
||||||
// Get downstream measurement
|
|
||||||
this._currentPosition = 'downstream';
|
|
||||||
const downstream = this.get();
|
|
||||||
|
|
||||||
// Restore chain state
|
|
||||||
this._currentPosition = savedPosition;
|
|
||||||
|
|
||||||
if (!upstream || !downstream || upstream.values.length === 0 || downstream.values.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure units match
|
|
||||||
let downstreamForCalc = downstream;
|
|
||||||
if (upstream.unit && downstream.unit && upstream.unit !== downstream.unit) {
|
|
||||||
try {
|
|
||||||
downstreamForCalc = downstream.convertTo(upstream.unit);
|
|
||||||
} catch (error) {
|
|
||||||
if (this.logger) {
|
|
||||||
this.logger.error(`Unit conversion failed: ${error.message}`);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
value: downstreamForCalc.getCurrentValue() - upstream.getCurrentValue() ,
|
|
||||||
avgDiff: downstreamForCalc.getAverage() - upstream.getAverage() ,
|
|
||||||
unit: upstream.unit
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods
|
|
||||||
_ensureChainIsValid() {
|
|
||||||
if (!this._currentType || !this._currentVariant || !this._currentPosition) {
|
|
||||||
if (this.logger) {
|
|
||||||
this.logger.error('Incomplete measurement chain, required: type, variant, and position');
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getOrCreateMeasurement() {
|
|
||||||
// Initialize nested structure if needed
|
|
||||||
if (!this.measurements[this._currentType]) {
|
|
||||||
this.measurements[this._currentType] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.measurements[this._currentType][this._currentVariant]) {
|
|
||||||
this.measurements[this._currentType][this._currentVariant] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.measurements[this._currentType][this._currentVariant][this._currentPosition]) {
|
|
||||||
this.measurements[this._currentType][this._currentVariant][this._currentPosition] =
|
|
||||||
new MeasurementBuilder()
|
|
||||||
.setType(this._currentType)
|
|
||||||
.setVariant(this._currentVariant)
|
|
||||||
.setPosition(this._currentPosition)
|
|
||||||
.setWindowSize(this.windowSize)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.measurements[this._currentType][this._currentVariant][this._currentPosition];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Additional utility methods
|
|
||||||
getTypes() {
|
|
||||||
return Object.keys(this.measurements);
|
|
||||||
}
|
|
||||||
|
|
||||||
getVariants() {
|
|
||||||
if (!this._currentType) {
|
|
||||||
throw new Error('Type must be specified before listing variants');
|
|
||||||
}
|
|
||||||
return this.measurements[this._currentType] ?
|
|
||||||
Object.keys(this.measurements[this._currentType]) : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
getPositions() {
|
|
||||||
if (!this._currentType || !this._currentVariant) {
|
|
||||||
throw new Error('Type and variant must be specified before listing positions');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.measurements[this._currentType] ||
|
|
||||||
!this.measurements[this._currentType][this._currentVariant]) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.keys(this.measurements[this._currentType][this._currentVariant]);
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this.measurements = {};
|
|
||||||
this._currentType = null;
|
|
||||||
this._currentVariant = null;
|
|
||||||
this._currentPosition = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = MeasurementContainer;
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
# Measurement System Documentation
|
|
||||||
|
|
||||||
This system provides a flexible way to store, retrieve, and analyze measurement data using a chainable API.
|
|
||||||
|
|
||||||
## Basic Usage
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const { MeasurementContainer } = require('./index');
|
|
||||||
const container = new MeasurementContainer({ windowSize: 20 });
|
|
||||||
|
|
||||||
// Set values
|
|
||||||
container.type('pressure').variant('measured').position('upstream').value(100).unit('psi');
|
|
||||||
|
|
||||||
// Get values
|
|
||||||
const upstreamPressure = container.type('pressure').variant('measured').position('upstream').getCurrentValue();
|
|
||||||
console.log(`Upstream pressure: ${upstreamPressure}`);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Chainable API Methods
|
|
||||||
|
|
||||||
### Setting Context
|
|
||||||
- `type(typeName)` - Set the measurement type (pressure, flow, etc.)
|
|
||||||
- `variant(variantName)` - Set the variant (measured, predicted, etc.)
|
|
||||||
- `position(positionName)` - Set the position (upstream, downstream, etc.)
|
|
||||||
|
|
||||||
### Setting Data
|
|
||||||
- `value(val, [timestamp])` - Add a value with optional timestamp
|
|
||||||
- `unit(unitName)` - Set the measurement unit
|
|
||||||
|
|
||||||
### Getting Data
|
|
||||||
- `get()` - Get the measurement object
|
|
||||||
- `getCurrentValue()` - Get the most recent value
|
|
||||||
- `getAverage()` - Calculate average of all values
|
|
||||||
- `getMin()` - Get minimum value
|
|
||||||
- `getMax()` - Get maximum value
|
|
||||||
|
|
||||||
### Calculations
|
|
||||||
- `difference()` - Calculate difference between upstream and downstream positions
|
|
||||||
|
|
||||||
### Listing Available Data
|
|
||||||
- `getTypes()` - Get all measurement types
|
|
||||||
- `listVariants()` - List variants for current type
|
|
||||||
- `listPositions()` - List positions for current type and variant
|
|
||||||
|
|
||||||
## Example Workflows
|
|
||||||
|
|
||||||
### Setting and retrieving values
|
|
||||||
```javascript
|
|
||||||
// Set a measurement
|
|
||||||
container.type('flow')
|
|
||||||
.variant('measured')
|
|
||||||
.position('upstream')
|
|
||||||
.value(120)
|
|
||||||
.unit('gpm');
|
|
||||||
|
|
||||||
// Retrieve the same measurement
|
|
||||||
const flow = container.type('flow')
|
|
||||||
.variant('measured')
|
|
||||||
.position('upstream')
|
|
||||||
.getCurrentValue();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Calculating differences
|
|
||||||
```javascript
|
|
||||||
// Set upstream and downstream measurements
|
|
||||||
container.type('pressure').variant('measured').position('upstream').value(100).unit('psi');
|
|
||||||
container.type('pressure').variant('measured').position('downstream').value(95).unit('psi');
|
|
||||||
|
|
||||||
// Calculate the difference
|
|
||||||
const diff = container.type('pressure').variant('measured').difference();
|
|
||||||
console.log(`Pressure drop: ${diff.currentDiff} ${diff.unit}`);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Working with historical data
|
|
||||||
```javascript
|
|
||||||
// Add multiple values
|
|
||||||
container.type('temperature')
|
|
||||||
.variant('measured')
|
|
||||||
.position('outlet')
|
|
||||||
.value(72)
|
|
||||||
.value(74)
|
|
||||||
.value(73)
|
|
||||||
.unit('F');
|
|
||||||
|
|
||||||
// Get statistics
|
|
||||||
const avg = container.type('temperature').variant('measured').position('outlet').getAverage();
|
|
||||||
const min = container.type('temperature').variant('measured').position('outlet').getMin();
|
|
||||||
const max = container.type('temperature').variant('measured').position('outlet').getMax();
|
|
||||||
```
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
const { MeasurementContainer } = require('./index');
|
|
||||||
|
|
||||||
// Create a container
|
|
||||||
const container = new MeasurementContainer({ windowSize: 20 });
|
|
||||||
|
|
||||||
// Example 1: Setting values with chaining
|
|
||||||
console.log('--- Example 1: Setting values ---');
|
|
||||||
container.type('pressure').variant('measured').position('upstream').value(100).unit('psi');
|
|
||||||
container.type('pressure').variant('measured').position('downstream').value(95).unit('psi');
|
|
||||||
container.type('pressure').variant('measured').position('downstream').value(80);
|
|
||||||
|
|
||||||
// Example 2: Getting values with chaining
|
|
||||||
console.log('--- Example 2: Getting values ---');
|
|
||||||
const upstreamValue = container.type('pressure').variant('measured').position('upstream').getCurrentValue();
|
|
||||||
const upstreamUnit = container.type('pressure').variant('measured').position('upstream').get().unit;
|
|
||||||
console.log(`Upstream pressure: ${upstreamValue} ${upstreamUnit}`);
|
|
||||||
const downstreamValue = container.type('pressure').variant('measured').position('downstream').getCurrentValue();
|
|
||||||
const downstreamUnit = container.type('pressure').variant('measured').position('downstream').get().unit;
|
|
||||||
console.log(`Downstream pressure: ${downstreamValue} ${downstreamUnit}`);
|
|
||||||
|
|
||||||
// Example 3: Calculations using chained methods
|
|
||||||
console.log('--- Example 3: Calculations ---');
|
|
||||||
container.type('flow').variant('predicted').position('upstream').value(200).unit('gpm');
|
|
||||||
container.type('flow').variant('predicted').position('downstream').value(195).unit('gpm');
|
|
||||||
|
|
||||||
const flowAvg = container.type('flow').variant('predicted').position('upstream').getAverage();
|
|
||||||
console.log(`Average upstream flow: ${flowAvg} gpm`);
|
|
||||||
|
|
||||||
// Example 4: Getting pressure difference
|
|
||||||
console.log('--- Example 4: Difference calculations ---');
|
|
||||||
const pressureDiff = container.type('pressure').variant('measured').difference();
|
|
||||||
console.log(`Pressure difference: ${pressureDiff.value} ${pressureDiff.unit}`);
|
|
||||||
|
|
||||||
// Example 5: Adding multiple values to track history
|
|
||||||
console.log('--- Example 5: Multiple values ---');
|
|
||||||
// Add several values to upstream flow
|
|
||||||
container.type('flow').variant('measured').position('upstream')
|
|
||||||
.value(210).value(215).value(205).unit('gpm');
|
|
||||||
|
|
||||||
// Then get statistics
|
|
||||||
console.log('Flow statistics:');
|
|
||||||
console.log(`- Current: ${container.type('flow').variant('measured').position('upstream').getCurrentValue()} gpm`);
|
|
||||||
console.log(`- Average: ${container.type('flow').variant('measured').position('upstream').getAverage()} gpm`);
|
|
||||||
console.log(`- Min: ${container.type('flow').variant('measured').position('upstream').getMin()} gpm`);
|
|
||||||
console.log(`- Max: ${container.type('flow').variant('measured').position('upstream').getMax()} gpm`);
|
|
||||||
console.log(`Show all values : ${JSON.stringify(container.type('flow').variant('measured').position('upstream').getAllValues())}`);
|
|
||||||
|
|
||||||
// Example 6: Listing available data
|
|
||||||
console.log('--- Example 6: Listing available data ---');
|
|
||||||
console.log('Types:', container.getTypes());
|
|
||||||
console.log('Pressure variants:', container.type('pressure').getVariants());
|
|
||||||
console.log('Measured pressure positions:', container.type('pressure').variant('measured').getPositions());
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
runExamples: () => {
|
|
||||||
console.log('Examples of the measurement chainable API');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
const MeasurementContainer = require('./MeasurementContainer');
|
|
||||||
const Measurement = require('./Measurement');
|
|
||||||
const MeasurementBuilder = require('./MeasurementBuilder');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
MeasurementContainer,
|
|
||||||
Measurement,
|
|
||||||
MeasurementBuilder
|
|
||||||
};
|
|
||||||
@@ -1,489 +0,0 @@
|
|||||||
class MenuUtils {
|
|
||||||
|
|
||||||
initBasicToggles(elements) {
|
|
||||||
// Toggle visibility for log level
|
|
||||||
elements.logCheckbox.addEventListener("change", function () {
|
|
||||||
elements.rowLogLevel.style.display = this.checked ? "block" : "none";
|
|
||||||
});
|
|
||||||
elements.rowLogLevel.style.display = elements.logCheckbox.checked
|
|
||||||
? "block"
|
|
||||||
: "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define the initialize toggles function within scope
|
|
||||||
initMeasurementToggles(elements) {
|
|
||||||
// Toggle visibility for scaling inputs
|
|
||||||
elements.scalingCheckbox.addEventListener("change", function () {
|
|
||||||
elements.rowInputMin.style.display = this.checked ? "block" : "none";
|
|
||||||
elements.rowInputMax.style.display = this.checked ? "block" : "none";
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set initial states
|
|
||||||
elements.rowInputMin.style.display = elements.scalingCheckbox.checked
|
|
||||||
? "block"
|
|
||||||
: "none";
|
|
||||||
elements.rowInputMax.style.display = elements.scalingCheckbox.checked
|
|
||||||
? "block"
|
|
||||||
: "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
initTensionToggles(elements, node) {
|
|
||||||
const currentMethod = node.interpolationMethod;
|
|
||||||
elements.rowTension.style.display =
|
|
||||||
currentMethod === "monotone_cubic_spline" ? "block" : "none";
|
|
||||||
console.log(
|
|
||||||
"Initial tension row display: ",
|
|
||||||
elements.rowTension.style.display
|
|
||||||
);
|
|
||||||
|
|
||||||
elements.interpolationMethodInput.addEventListener("change", function () {
|
|
||||||
const selectedMethod = this.value;
|
|
||||||
console.log(`Interpolation method changed: ${selectedMethod}`);
|
|
||||||
node.interpolationMethod = selectedMethod;
|
|
||||||
|
|
||||||
// Toggle visibility for tension input
|
|
||||||
elements.rowTension.style.display =
|
|
||||||
selectedMethod === "monotone_cubic_spline" ? "block" : "none";
|
|
||||||
console.log("Tension row display: ", elements.rowTension.style.display);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Define the smoothing methods population function within scope
|
|
||||||
populateSmoothingMethods(configUrls, elements, node) {
|
|
||||||
this.fetchData(configUrls.cloud.config, configUrls.local.config)
|
|
||||||
.then((configData) => {
|
|
||||||
const smoothingMethods =
|
|
||||||
configData.smoothing?.smoothMethod?.rules?.values?.map(
|
|
||||||
(o) => o.value
|
|
||||||
) || [];
|
|
||||||
this.populateDropdown(
|
|
||||||
elements.smoothMethod,
|
|
||||||
smoothingMethods,
|
|
||||||
node,
|
|
||||||
"smooth_method"
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Error loading smoothing methods", err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
populateInterpolationMethods(configUrls, elements, node) {
|
|
||||||
this.fetchData(configUrls.cloud.config, configUrls.local.config)
|
|
||||||
.then((configData) => {
|
|
||||||
const interpolationMethods =
|
|
||||||
configData?.interpolation?.type?.rules?.values.map((m) => m.value) ||
|
|
||||||
[];
|
|
||||||
this.populateDropdown(
|
|
||||||
elements.interpolationMethodInput,
|
|
||||||
interpolationMethods,
|
|
||||||
node,
|
|
||||||
"interpolationMethod"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find the selected method and use it to spawn 1 more field to fill in tension
|
|
||||||
//const selectedMethod = interpolationMethods.find(m => m === node.interpolationMethod);
|
|
||||||
this.initTensionToggles(elements, node);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("Error loading interpolation methods", err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
populateLogLevelOptions(logLevelSelect, configData, node) {
|
|
||||||
// debug log level
|
|
||||||
//console.log("Displaying configData => ", configData) ;
|
|
||||||
|
|
||||||
const logLevels =
|
|
||||||
configData?.general?.logging?.logLevel?.rules?.values?.map(
|
|
||||||
(l) => l.value
|
|
||||||
) || [];
|
|
||||||
|
|
||||||
//console.log("Displaying logLevels => ", logLevels);
|
|
||||||
|
|
||||||
// Reuse your existing generic populateDropdown helper
|
|
||||||
this.populateDropdown(logLevelSelect, logLevels, node.logLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
//cascade dropdowns for asset type, supplier, subType, model, unit
|
|
||||||
fetchAndPopulateDropdowns(configUrls, elements, node) {
|
|
||||||
this.fetchData(configUrls.cloud.config, configUrls.local.config)
|
|
||||||
.then((configData) => {
|
|
||||||
const assetType = configData.asset?.type?.default;
|
|
||||||
const localSuppliersUrl = this.constructUrl(configUrls.local.taggcodeAPI,`${assetType}s`,"suppliers.json");
|
|
||||||
const cloudSuppliersUrl = this.constructCloudURL(configUrls.cloud.taggcodeAPI, "/vendor/get_vendors.php");
|
|
||||||
|
|
||||||
return this.fetchData(cloudSuppliersUrl, localSuppliersUrl)
|
|
||||||
.then((supplierData) => {
|
|
||||||
|
|
||||||
const suppliers = supplierData.map((supplier) => supplier.name);
|
|
||||||
|
|
||||||
// Populate suppliers dropdown and set up its change handler
|
|
||||||
return this.populateDropdown(
|
|
||||||
elements.supplier,
|
|
||||||
suppliers,
|
|
||||||
node,
|
|
||||||
"supplier",
|
|
||||||
function (selectedSupplier) {
|
|
||||||
if (selectedSupplier) {
|
|
||||||
this.populateSubTypes(configUrls, elements, node, selectedSupplier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// If we have a saved supplier, trigger subTypes population
|
|
||||||
if (node.supplier) {
|
|
||||||
this.populateSubTypes(configUrls, elements, node, node.supplier);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Error in initial dropdown population:", error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getSpecificConfigUrl(nodeName,cloudAPI) {
|
|
||||||
|
|
||||||
const cloudConfigURL = cloudAPI + "/config/" + nodeName + ".json";
|
|
||||||
const localConfigURL = "http://localhost:1880/"+ nodeName + "/dependencies/"+ nodeName + "/" + nodeName + "Config.json";
|
|
||||||
|
|
||||||
return { cloudConfigURL, localConfigURL };
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save changes to API
|
|
||||||
async apiCall(node) {
|
|
||||||
try{
|
|
||||||
// OLFIANT when a browser refreshes the tag code is lost!!! fix this later!!!!!
|
|
||||||
// FIX UUID ALSO LATER
|
|
||||||
|
|
||||||
if(node.assetTagCode !== "" || node.assetTagCode !== null){ }
|
|
||||||
// API call to register or check asset in central database
|
|
||||||
let assetregisterAPI = node.configUrls.cloud.taggcodeAPI + "/asset/create_asset.php";
|
|
||||||
|
|
||||||
const assetModelId = node.modelMetadata.id; //asset_product_model_id
|
|
||||||
const uuid = node.uuid; //asset_product_model_uuid
|
|
||||||
const assetName = node.assetType; //asset_name / type?
|
|
||||||
const description = node.name; // asset_description
|
|
||||||
const assetStatus = "actief"; //asset_status -> koppel aan enable / disable node ? or make dropdown ?
|
|
||||||
const assetProfileId = 1; //asset_profile_id these are the rules to check if the childs are valid under this node (parent / child id?)
|
|
||||||
const child_assets = ["63247"]; //child_assets tagnummer of id?
|
|
||||||
const assetProcessId = node.processId; //asset_process_id
|
|
||||||
const assetLocationId = node.locationId; //asset_location_id
|
|
||||||
const tagCode = node.assetTagCode; // if already exists in the node information use it to tell the api it exists and it will update else we will get it from the api call
|
|
||||||
//console.log(`this is my tagCode: ${tagCode}`);
|
|
||||||
|
|
||||||
// Build base URL with required parameters
|
|
||||||
let apiUrl = `?asset_product_model_id=${assetModelId}&asset_product_model_uuid=${uuid}&asset_name=${assetName}&asset_description=${description}&asset_status=${assetStatus}&asset_profile_id=${assetProfileId}&asset_location_id=${assetLocationId}&asset_process_id=${assetProcessId}&child_assets=${child_assets}`;
|
|
||||||
|
|
||||||
// Only add tagCode to URL if it exists
|
|
||||||
if (tagCode) {
|
|
||||||
apiUrl += `&asset_tag_number=${tagCode}`;
|
|
||||||
console.log('hello there');
|
|
||||||
}
|
|
||||||
|
|
||||||
assetregisterAPI += apiUrl;
|
|
||||||
console.log("API call to register asset in central database", assetregisterAPI);
|
|
||||||
|
|
||||||
const response = await fetch(assetregisterAPI, {
|
|
||||||
method: "POST"
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get the response text first
|
|
||||||
const responseText = await response.text();
|
|
||||||
console.log("Raw API response:", responseText);
|
|
||||||
|
|
||||||
// Try to parse the JSON, handling potential parsing errors
|
|
||||||
let jsonResponse;
|
|
||||||
try {
|
|
||||||
jsonResponse = JSON.parse(responseText);
|
|
||||||
} catch (parseError) {
|
|
||||||
console.error("JSON Parsing Error:", parseError);
|
|
||||||
console.error("Response that could not be parsed:", responseText);
|
|
||||||
throw new Error("Failed to parse API response");
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(jsonResponse);
|
|
||||||
|
|
||||||
if(jsonResponse.success){
|
|
||||||
console.log(`${jsonResponse.message}, tag number: ${jsonResponse.asset_tag_number}, asset id: ${jsonResponse.asset_id}`);
|
|
||||||
// Save the asset tag number and id to the node
|
|
||||||
} else {
|
|
||||||
console.log("Asset not registered in central database");
|
|
||||||
}
|
|
||||||
return jsonResponse;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error saving changes to asset register API", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async fetchData(url, fallbackUrl) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(url);
|
|
||||||
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
||||||
const responsData = await response.json();
|
|
||||||
//responsData
|
|
||||||
const data = responsData.data;
|
|
||||||
/* .map(item => {
|
|
||||||
const { vendor_name, ...rest } = item;
|
|
||||||
return {
|
|
||||||
name: vendor_name,
|
|
||||||
...rest
|
|
||||||
};
|
|
||||||
}); */
|
|
||||||
console.log(url);
|
|
||||||
console.log("Response Data: ", data);
|
|
||||||
return data;
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.warn(
|
|
||||||
`Primary URL failed: ${url}. Trying fallback URL: ${fallbackUrl}`,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
const response = await fetch(fallbackUrl);
|
|
||||||
if (!response.ok)
|
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
|
||||||
return await response.json();
|
|
||||||
} catch (fallbackErr) {
|
|
||||||
console.error("Both primary and fallback URLs failed:", fallbackErr);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetchProjectData(url) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(url);
|
|
||||||
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
||||||
const responsData = await response.json();
|
|
||||||
console.log("Response Data: ", responsData);
|
|
||||||
return responsData;
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async populateDropdown(
|
|
||||||
htmlElement,
|
|
||||||
options,
|
|
||||||
node,
|
|
||||||
property,
|
|
||||||
callback
|
|
||||||
) {
|
|
||||||
this.generateHtml(htmlElement, options, node[property]);
|
|
||||||
|
|
||||||
htmlElement.addEventListener("change", async (e) => {
|
|
||||||
const newValue = e.target.value;
|
|
||||||
console.log(`Dropdown changed: ${property} = ${newValue}`);
|
|
||||||
node[property] = newValue;
|
|
||||||
|
|
||||||
RED.nodes.dirty(true);
|
|
||||||
if (callback) await callback(newValue); // Ensure async callback completion
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to construct a URL from a base and path internal
|
|
||||||
constructUrl(base, ...paths) {
|
|
||||||
|
|
||||||
// Remove trailing slash from base and leading slashes from paths
|
|
||||||
const sanitizedBase = (base || "").replace(/\/+$/, "");
|
|
||||||
const sanitizedPaths = paths.map((path) => path.replace(/^\/+|\/+$/g, ""));
|
|
||||||
|
|
||||||
// Join sanitized base and paths
|
|
||||||
const url = `${sanitizedBase}/${sanitizedPaths.join("/")}`;
|
|
||||||
console.log("Base:", sanitizedBase);
|
|
||||||
console.log("Paths:", sanitizedPaths);
|
|
||||||
console.log("Constructed URL:", url);
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Adjust for API Gateway
|
|
||||||
constructCloudURL(base, ...paths) {
|
|
||||||
// Remove trailing slash from base and leading slashes from paths
|
|
||||||
const sanitizedBase = base.replace(/\/+$/, "");
|
|
||||||
const sanitizedPaths = paths.map((path) => path.replace(/^\/+|\/+$/g, ""));
|
|
||||||
// Join sanitized base and paths
|
|
||||||
const url = `${sanitizedBase}/${sanitizedPaths.join("/")}`;
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
populateSubTypes(configUrls, elements, node, selectedSupplier) {
|
|
||||||
|
|
||||||
this.fetchData(configUrls.cloud.config, configUrls.local.config)
|
|
||||||
.then((configData) => {
|
|
||||||
const assetType = configData.asset?.type?.default;
|
|
||||||
const supplierFolder = this.constructUrl( configUrls.local.taggcodeAPI, `${assetType}s`, selectedSupplier );
|
|
||||||
|
|
||||||
const localSubTypesUrl = this.constructUrl(supplierFolder, "subtypes.json");
|
|
||||||
const cloudSubTypesUrl = this.constructCloudURL(configUrls.cloud.taggcodeAPI, "/product/get_subtypesFromVendor.php?vendor_name=" + selectedSupplier);
|
|
||||||
|
|
||||||
return this.fetchData(cloudSubTypesUrl, localSubTypesUrl)
|
|
||||||
.then((subTypeData) => {
|
|
||||||
const subTypes = subTypeData.map((subType) => subType.name);
|
|
||||||
|
|
||||||
return this.populateDropdown(
|
|
||||||
elements.subType,
|
|
||||||
subTypes,
|
|
||||||
node,
|
|
||||||
"subType",
|
|
||||||
function (selectedSubType) {
|
|
||||||
if (selectedSubType) {
|
|
||||||
// When subType changes, update both models and units
|
|
||||||
this.populateModels(
|
|
||||||
configUrls,
|
|
||||||
elements,
|
|
||||||
node,
|
|
||||||
selectedSupplier,
|
|
||||||
selectedSubType
|
|
||||||
);
|
|
||||||
this.populateUnitsForSubType(
|
|
||||||
configUrls,
|
|
||||||
elements,
|
|
||||||
node,
|
|
||||||
selectedSubType
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
// If we have a saved subType, trigger both models and units population
|
|
||||||
if (node.subType) {
|
|
||||||
this.populateModels(
|
|
||||||
configUrls,
|
|
||||||
elements,
|
|
||||||
node,
|
|
||||||
selectedSupplier,
|
|
||||||
node.subType
|
|
||||||
);
|
|
||||||
this.populateUnitsForSubType(configUrls, elements, node, node.subType);
|
|
||||||
}
|
|
||||||
//console.log("In fetch part of subtypes ");
|
|
||||||
// Store all data from selected model
|
|
||||||
/* node["modelMetadata"] = modelData.find(
|
|
||||||
(model) => model.name === node.model
|
|
||||||
);
|
|
||||||
console.log("Model Metadata: ", node["modelMetadata"]); */
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Error populating subtypes:", error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
populateUnitsForSubType(configUrls, elements, node, selectedSubType) {
|
|
||||||
// Fetch the units data
|
|
||||||
this.fetchData(configUrls.cloud.units, configUrls.local.units)
|
|
||||||
.then((unitsData) => {
|
|
||||||
// Find the category that matches the subType name
|
|
||||||
const categoryData = unitsData.units.find(
|
|
||||||
(category) =>
|
|
||||||
category.category.toLowerCase() === selectedSubType.toLowerCase()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (categoryData) {
|
|
||||||
// Extract just the unit values and descriptions
|
|
||||||
const units = categoryData.values.map((unit) => ({
|
|
||||||
value: unit.value,
|
|
||||||
description: unit.description,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Create the options array with descriptions as labels
|
|
||||||
const options = units.map((unit) => ({
|
|
||||||
value: unit.value,
|
|
||||||
label: `${unit.value} - ${unit.description}`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Populate the units dropdown
|
|
||||||
this.populateDropdown(
|
|
||||||
elements.unit,
|
|
||||||
options.map((opt) => opt.value),
|
|
||||||
node,
|
|
||||||
"unit"
|
|
||||||
);
|
|
||||||
|
|
||||||
// If there's no currently selected unit but we have options, select the first one
|
|
||||||
if (!node.unit && options.length > 0) {
|
|
||||||
node.unit = options[0].value;
|
|
||||||
elements.unit.value = options[0].value;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If no matching category is found, provide a default % option
|
|
||||||
const defaultUnits = [{ value: "%", description: "Percentage" }];
|
|
||||||
this.populateDropdown(
|
|
||||||
elements.unit,
|
|
||||||
defaultUnits.map((unit) => unit.value),
|
|
||||||
node,
|
|
||||||
"unit"
|
|
||||||
);
|
|
||||||
console.warn(
|
|
||||||
`No matching unit category found for subType: ${selectedSubType}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Error fetching units:", error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
populateModels(
|
|
||||||
configUrls,
|
|
||||||
elements,
|
|
||||||
node,
|
|
||||||
selectedSupplier,
|
|
||||||
selectedSubType
|
|
||||||
) {
|
|
||||||
|
|
||||||
this.fetchData(configUrls.cloud.config, configUrls.local.config)
|
|
||||||
.then((configData) => {
|
|
||||||
const assetType = configData.asset?.type?.default;
|
|
||||||
// save assetType to fetch later
|
|
||||||
node.assetType = assetType;
|
|
||||||
|
|
||||||
const supplierFolder = this.constructUrl( configUrls.local.taggcodeAPI,`${assetType}s`,selectedSupplier);
|
|
||||||
const subTypeFolder = this.constructUrl(supplierFolder, selectedSubType);
|
|
||||||
const localModelsUrl = this.constructUrl(subTypeFolder, "models.json");
|
|
||||||
const cloudModelsUrl = this.constructCloudURL(configUrls.cloud.taggcodeAPI, "/product/get_product_models.php?vendor_name=" + selectedSupplier + "&product_subtype_name=" + selectedSubType);
|
|
||||||
|
|
||||||
return this.fetchData(cloudModelsUrl, localModelsUrl).then((modelData) => {
|
|
||||||
const models = modelData.map((model) => model.name); // use this to populate the dropdown
|
|
||||||
|
|
||||||
// If a model is already selected, store its metadata immediately
|
|
||||||
if (node.model) {
|
|
||||||
node["modelMetadata"] = modelData.find((model) => model.name === node.model);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.populateDropdown(elements.model, models, node, "model", (selectedModel) => {
|
|
||||||
// Store only the metadata for the selected model
|
|
||||||
node["modelMetadata"] = modelData.find((model) => model.name === selectedModel);
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
console.log('hello here I am:');
|
|
||||||
console.log(node["modelMetadata"]);
|
|
||||||
*/
|
|
||||||
});
|
|
||||||
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Error populating models:", error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
generateHtml(htmlElement, options, savedValue) {
|
|
||||||
htmlElement.innerHTML = options.length
|
|
||||||
? `<option value="">Select...</option>${options
|
|
||||||
.map((opt) => `<option value="${opt}">${opt}</option>`)
|
|
||||||
.join("")}`
|
|
||||||
: "<option value=''>No options available</option>";
|
|
||||||
|
|
||||||
if (savedValue && options.includes(savedValue)) {
|
|
||||||
htmlElement.value = savedValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = MenuUtils;
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
const nodeTemplates = {
|
|
||||||
asset: {
|
|
||||||
category: "digital asset",
|
|
||||||
color: "#4f8582",
|
|
||||||
defaults: {
|
|
||||||
name: { value: "", required: true },
|
|
||||||
enableLog: { value: false },
|
|
||||||
logLevel: { value: "error" },
|
|
||||||
parent: { value: "downstream" }, // indicates the position vs the parent in the process downstream,upstream or none.
|
|
||||||
supplier: { value: "" },
|
|
||||||
subType: { value: "" },
|
|
||||||
model: { value: "" },
|
|
||||||
unit: { value: "" },
|
|
||||||
},
|
|
||||||
inputs: 1,
|
|
||||||
outputs: 3,
|
|
||||||
inputLabels: ["Machine Input"],
|
|
||||||
outputLabels: ["process", "dbase", "parent"],
|
|
||||||
icon: "font-awesome/fa-cogs",
|
|
||||||
elements: {
|
|
||||||
// Basic fields
|
|
||||||
name: "node-input-name",
|
|
||||||
// Logging fields
|
|
||||||
logCheckbox: "node-input-enableLog",
|
|
||||||
logLevelSelect: "node-input-logLevel",
|
|
||||||
rowLogLevel: "row-logLevel",
|
|
||||||
// Asset fields
|
|
||||||
supplier: "node-input-supplier",
|
|
||||||
subType: "node-input-subType",
|
|
||||||
model: "node-input-model",
|
|
||||||
unit: "node-input-unit",
|
|
||||||
//position vs parent
|
|
||||||
parent: "node-input-parent",
|
|
||||||
},
|
|
||||||
projectSettingsURL:
|
|
||||||
"http://localhost:1880/generalFunctions/settings/projectSettings.json",
|
|
||||||
},
|
|
||||||
|
|
||||||
exampleTemplate: {
|
|
||||||
category: "digital twin",
|
|
||||||
color: "#004080",
|
|
||||||
defaults: {
|
|
||||||
name: { value: "", required: true },
|
|
||||||
foo: { value: 42 },
|
|
||||||
},
|
|
||||||
inputs: 2,
|
|
||||||
outputs: 2,
|
|
||||||
inputLabels: ["In A", "In B"],
|
|
||||||
outputLabels: ["Out A", "Out B"],
|
|
||||||
icon: "font-awesome/fa-gears",
|
|
||||||
},
|
|
||||||
|
|
||||||
// …add more node “templates” here…
|
|
||||||
};
|
|
||||||
|
|
||||||
export default nodeTemplates;
|
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
const ErrorMetrics = require('./errorMetrics');
|
|
||||||
|
|
||||||
// Dummy logger for tests
|
|
||||||
const logger = {
|
|
||||||
error: console.error,
|
|
||||||
debug: console.log,
|
|
||||||
info: console.log
|
|
||||||
};
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
thresholds: {
|
|
||||||
NRMSE_LOW: 0.05,
|
|
||||||
NRMSE_MEDIUM: 0.10,
|
|
||||||
NRMSE_HIGH: 0.15,
|
|
||||||
LONG_TERM_LOW: 0.02,
|
|
||||||
LONG_TERM_MEDIUM: 0.04,
|
|
||||||
LONG_TERM_HIGH: 0.06
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ErrorMetricsTester {
|
|
||||||
constructor() {
|
|
||||||
this.totalTests = 0;
|
|
||||||
this.passedTests = 0;
|
|
||||||
this.failedTests = 0;
|
|
||||||
this.errorMetrics = new ErrorMetrics(config, logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(condition, message) {
|
|
||||||
this.totalTests++;
|
|
||||||
if (condition) {
|
|
||||||
console.log(`✓ PASS: ${message}`);
|
|
||||||
this.passedTests++;
|
|
||||||
} else {
|
|
||||||
console.log(`✗ FAIL: ${message}`);
|
|
||||||
this.failedTests++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testMeanSquaredError() {
|
|
||||||
console.log("\nTesting Mean Squared Error...");
|
|
||||||
const predicted = [1, 2, 3];
|
|
||||||
const measured = [1, 3, 5];
|
|
||||||
const mse = this.errorMetrics.meanSquaredError(predicted, measured);
|
|
||||||
this.assert(Math.abs(mse - 1.67) < 0.1, "MSE correctly calculated");
|
|
||||||
}
|
|
||||||
|
|
||||||
testRootMeanSquaredError() {
|
|
||||||
console.log("\nTesting Root Mean Squared Error...");
|
|
||||||
const predicted = [1, 2, 3];
|
|
||||||
const measured = [1, 3, 5];
|
|
||||||
const rmse = this.errorMetrics.rootMeanSquaredError(predicted, measured);
|
|
||||||
this.assert(Math.abs(rmse - 1.29) < 0.1, "RMSE correctly calculated");
|
|
||||||
}
|
|
||||||
|
|
||||||
testNormalizedRMSE() {
|
|
||||||
console.log("\nTesting Normalized RMSE...");
|
|
||||||
const predicted = [100, 102, 104];
|
|
||||||
const measured = [98, 103, 107];
|
|
||||||
const processMin = 90, processMax = 110;
|
|
||||||
const nrmse = this.errorMetrics.normalizedRootMeanSquaredError(predicted, measured, processMin, processMax);
|
|
||||||
this.assert(typeof nrmse === 'number' && nrmse > 0, "Normalized RMSE calculated correctly");
|
|
||||||
}
|
|
||||||
|
|
||||||
testNormalizeUsingRealtime() {
|
|
||||||
console.log("\nTesting Normalize Using Realtime...");
|
|
||||||
const predicted = [100, 102, 104];
|
|
||||||
const measured = [98, 103, 107];
|
|
||||||
|
|
||||||
try {
|
|
||||||
const nrmse = this.errorMetrics.normalizeUsingRealtime(predicted, measured);
|
|
||||||
this.assert(typeof nrmse === 'number' && nrmse > 0, "Normalize using realtime calculated correctly");
|
|
||||||
} catch (error) {
|
|
||||||
this.assert(false, `Normalize using realtime failed: ${error.message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test with identical values to check error handling
|
|
||||||
const sameValues = [100, 100, 100];
|
|
||||||
try {
|
|
||||||
this.errorMetrics.normalizeUsingRealtime(sameValues, sameValues);
|
|
||||||
this.assert(false, "Should throw error with identical values");
|
|
||||||
} catch (error) {
|
|
||||||
this.assert(true, "Correctly throws error when min/max are the same");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testLongTermNRMSD() {
|
|
||||||
console.log("\nTesting Long Term NRMSD Accumulation...");
|
|
||||||
// Reset the accumulation values
|
|
||||||
this.errorMetrics.cumNRMSD = 0;
|
|
||||||
this.errorMetrics.cumCount = 0;
|
|
||||||
|
|
||||||
let lastValue = 0;
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
lastValue = this.errorMetrics.longTermNRMSD(0.1 + i * 0.001);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.assert(
|
|
||||||
this.errorMetrics.cumCount === 100 &&
|
|
||||||
this.errorMetrics.cumNRMSD !== 0 &&
|
|
||||||
lastValue !== 0,
|
|
||||||
"Long term NRMSD accumulates over 100 iterations"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test that values are returned only after accumulating 100 samples
|
|
||||||
this.errorMetrics.cumNRMSD = 0;
|
|
||||||
this.errorMetrics.cumCount = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < 99; i++) {
|
|
||||||
const result = this.errorMetrics.longTermNRMSD(0.1);
|
|
||||||
this.assert(result === 0, "No longTermNRMSD returned before 100 samples");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use a different value for the 100th sample to ensure a non-zero result
|
|
||||||
const result = this.errorMetrics.longTermNRMSD(0.2);
|
|
||||||
this.assert(result !== 0, "longTermNRMSD returned after 100 samples");
|
|
||||||
}
|
|
||||||
|
|
||||||
testDetectImmediateDrift() {
|
|
||||||
console.log("\nTesting Immediate Drift Detection...");
|
|
||||||
|
|
||||||
// Test high drift
|
|
||||||
let drift = this.errorMetrics.detectImmediateDrift(config.thresholds.NRMSE_HIGH + 0.01);
|
|
||||||
this.assert(drift.level === 3, "Detects high immediate drift correctly");
|
|
||||||
|
|
||||||
// Test medium drift
|
|
||||||
drift = this.errorMetrics.detectImmediateDrift(config.thresholds.NRMSE_MEDIUM + 0.01);
|
|
||||||
this.assert(drift.level === 2, "Detects medium immediate drift correctly");
|
|
||||||
|
|
||||||
// Test low drift
|
|
||||||
drift = this.errorMetrics.detectImmediateDrift(config.thresholds.NRMSE_LOW + 0.01);
|
|
||||||
this.assert(drift.level === 1, "Detects low immediate drift correctly");
|
|
||||||
|
|
||||||
// Test no drift
|
|
||||||
drift = this.errorMetrics.detectImmediateDrift(config.thresholds.NRMSE_LOW - 0.01);
|
|
||||||
this.assert(drift.level === 0, "Detects no immediate drift correctly");
|
|
||||||
}
|
|
||||||
|
|
||||||
testDetectLongTermDrift() {
|
|
||||||
console.log("\nTesting Long Term Drift Detection...");
|
|
||||||
|
|
||||||
// Test high drift
|
|
||||||
let drift = this.errorMetrics.detectLongTermDrift(config.thresholds.LONG_TERM_HIGH + 0.01);
|
|
||||||
this.assert(drift.level === 3, "Detects high long-term drift correctly");
|
|
||||||
|
|
||||||
// Test medium drift
|
|
||||||
drift = this.errorMetrics.detectLongTermDrift(config.thresholds.LONG_TERM_MEDIUM + 0.01);
|
|
||||||
this.assert(drift.level === 2, "Detects medium long-term drift correctly");
|
|
||||||
|
|
||||||
// Test low drift
|
|
||||||
drift = this.errorMetrics.detectLongTermDrift(config.thresholds.LONG_TERM_LOW + 0.01);
|
|
||||||
this.assert(drift.level === 1, "Detects low long-term drift correctly");
|
|
||||||
|
|
||||||
// Test no drift
|
|
||||||
drift = this.errorMetrics.detectLongTermDrift(config.thresholds.LONG_TERM_LOW - 0.01);
|
|
||||||
this.assert(drift.level === 0, "Detects no long-term drift correctly");
|
|
||||||
|
|
||||||
// Test negative drift values
|
|
||||||
drift = this.errorMetrics.detectLongTermDrift(-config.thresholds.LONG_TERM_HIGH - 0.01);
|
|
||||||
this.assert(drift.level === 3, "Detects negative high long-term drift correctly");
|
|
||||||
}
|
|
||||||
|
|
||||||
testDriftDetection() {
|
|
||||||
console.log("\nTesting Combined Drift Detection...");
|
|
||||||
|
|
||||||
let nrmseHigh = config.thresholds.NRMSE_HIGH + 0.01;
|
|
||||||
let ltNRMSD = 0;
|
|
||||||
|
|
||||||
let result = this.errorMetrics.detectDrift(nrmseHigh, ltNRMSD);
|
|
||||||
|
|
||||||
this.assert(
|
|
||||||
result !== null &&
|
|
||||||
result.ImmDrift &&
|
|
||||||
result.ImmDrift.level === 3 &&
|
|
||||||
result.LongTermDrift.level === 0,
|
|
||||||
"Detects high immediate drift with no long-term drift"
|
|
||||||
);
|
|
||||||
|
|
||||||
nrmseHigh = config.thresholds.NRMSE_LOW - 0.01;
|
|
||||||
ltNRMSD = config.thresholds.LONG_TERM_MEDIUM + 0.01;
|
|
||||||
result = this.errorMetrics.detectDrift(nrmseHigh, ltNRMSD);
|
|
||||||
this.assert(
|
|
||||||
result !== null &&
|
|
||||||
result.ImmDrift.level === 0 &&
|
|
||||||
result.LongTermDrift &&
|
|
||||||
result.LongTermDrift.level === 2,
|
|
||||||
"Detects medium long-term drift with no immediate drift"
|
|
||||||
);
|
|
||||||
|
|
||||||
nrmseHigh = config.thresholds.NRMSE_MEDIUM + 0.01;
|
|
||||||
ltNRMSD = config.thresholds.LONG_TERM_MEDIUM + 0.01;
|
|
||||||
result = this.errorMetrics.detectDrift(nrmseHigh, ltNRMSD);
|
|
||||||
this.assert(
|
|
||||||
result.ImmDrift.level === 2 &&
|
|
||||||
result.LongTermDrift.level === 2,
|
|
||||||
"Detects both medium immediate and medium long-term drift"
|
|
||||||
);
|
|
||||||
|
|
||||||
nrmseHigh = config.thresholds.NRMSE_LOW - 0.01;
|
|
||||||
ltNRMSD = config.thresholds.LONG_TERM_LOW - 0.01;
|
|
||||||
result = this.errorMetrics.detectDrift(nrmseHigh, ltNRMSD);
|
|
||||||
this.assert(
|
|
||||||
result.ImmDrift.level === 0 &&
|
|
||||||
result.LongTermDrift.level === 0,
|
|
||||||
"No significant drift detected when under thresholds"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
testAssessDrift() {
|
|
||||||
console.log("\nTesting assessDrift function...");
|
|
||||||
|
|
||||||
// Reset accumulation for testing
|
|
||||||
this.errorMetrics.cumNRMSD = 0;
|
|
||||||
this.errorMetrics.cumCount = 0;
|
|
||||||
|
|
||||||
const predicted = [100, 101, 102, 103];
|
|
||||||
const measured = [90, 91, 92, 93];
|
|
||||||
const processMin = 90, processMax = 110;
|
|
||||||
|
|
||||||
let result = this.errorMetrics.assessDrift(predicted, measured, processMin, processMax);
|
|
||||||
|
|
||||||
this.assert(
|
|
||||||
result !== null &&
|
|
||||||
typeof result.nrmse === 'number' &&
|
|
||||||
typeof result.longTermNRMSD === 'number' &&
|
|
||||||
typeof result.immediateLevel === 'number' &&
|
|
||||||
typeof result.immediateFeedback === 'string' &&
|
|
||||||
typeof result.longTermLevel === 'number' &&
|
|
||||||
typeof result.longTermFeedback === 'string',
|
|
||||||
"assessDrift returns complete result structure"
|
|
||||||
);
|
|
||||||
|
|
||||||
this.assert(
|
|
||||||
result.immediateLevel > 0,
|
|
||||||
"assessDrift detects immediate drift with significant difference"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test with identical values
|
|
||||||
result = this.errorMetrics.assessDrift(predicted, predicted, processMin, processMax);
|
|
||||||
this.assert(
|
|
||||||
result.nrmse === 0 &&
|
|
||||||
result.immediateLevel === 0,
|
|
||||||
"assessDrift indicates no immediate drift when predicted equals measured"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test with slight drift
|
|
||||||
const measuredSlight = [100, 100.5, 101, 101.5];
|
|
||||||
result = this.errorMetrics.assessDrift(predicted, measuredSlight, processMin, processMax);
|
|
||||||
|
|
||||||
this.assert(
|
|
||||||
result !== null &&
|
|
||||||
result.nrmse < 0.05 &&
|
|
||||||
(result.immediateLevel < 2),
|
|
||||||
"assessDrift returns appropriate levels for slight drift"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Test long-term drift accumulation
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
this.errorMetrics.assessDrift(
|
|
||||||
predicted,
|
|
||||||
measured.map(m => m + (Math.random() * 2 - 1)), // Add small random fluctuation
|
|
||||||
processMin,
|
|
||||||
processMax
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = this.errorMetrics.assessDrift(predicted, measured, processMin, processMax);
|
|
||||||
this.assert(
|
|
||||||
result.longTermNRMSD !== 0,
|
|
||||||
"Long-term drift accumulates over multiple assessments"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async runAllTests() {
|
|
||||||
console.log("\nStarting Error Metrics Tests...\n");
|
|
||||||
this.testMeanSquaredError();
|
|
||||||
this.testRootMeanSquaredError();
|
|
||||||
this.testNormalizedRMSE();
|
|
||||||
this.testNormalizeUsingRealtime();
|
|
||||||
this.testLongTermNRMSD();
|
|
||||||
this.testDetectImmediateDrift();
|
|
||||||
this.testDetectLongTermDrift();
|
|
||||||
this.testDriftDetection();
|
|
||||||
this.testAssessDrift();
|
|
||||||
|
|
||||||
console.log("\nTest Summary:");
|
|
||||||
console.log(`Total Tests: ${this.totalTests}`);
|
|
||||||
console.log(`Passed: ${this.passedTests}`);
|
|
||||||
console.log(`Failed: ${this.failedTests}`);
|
|
||||||
|
|
||||||
process.exit(this.failedTests > 0 ? 1 : 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run all tests
|
|
||||||
const tester = new ErrorMetricsTester();
|
|
||||||
tester.runAllTests().catch(console.error);
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
//load local dependencies
|
|
||||||
const EventEmitter = require('events');
|
|
||||||
|
|
||||||
//load all config modules
|
|
||||||
const defaultConfig = require('./nrmseConfig.json');
|
|
||||||
const ConfigUtils = require('../configUtils');
|
|
||||||
|
|
||||||
class ErrorMetrics {
|
|
||||||
constructor(config = {}, logger) {
|
|
||||||
|
|
||||||
this.emitter = new EventEmitter(); // Own EventEmitter
|
|
||||||
this.configUtils = new ConfigUtils(defaultConfig);
|
|
||||||
this.config = this.configUtils.initConfig(config);
|
|
||||||
|
|
||||||
// Init after config is set
|
|
||||||
this.logger = logger;
|
|
||||||
|
|
||||||
// For long-term NRMSD accumulation
|
|
||||||
this.cumNRMSD = 0;
|
|
||||||
this.cumCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//INCLUDE timestamps in the next update OLIFANT
|
|
||||||
meanSquaredError(predicted, measured) {
|
|
||||||
if (predicted.length !== measured.length) {
|
|
||||||
this.logger.error("Comparing MSE Arrays must have the same length.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let sumSqError = 0;
|
|
||||||
for (let i = 0; i < predicted.length; i++) {
|
|
||||||
const err = predicted[i] - measured[i];
|
|
||||||
sumSqError += err * err;
|
|
||||||
}
|
|
||||||
return sumSqError / predicted.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
rootMeanSquaredError(predicted, measured) {
|
|
||||||
return Math.sqrt(this.meanSquaredError(predicted, measured));
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizedRootMeanSquaredError(predicted, measured, processMin, processMax) {
|
|
||||||
const range = processMax - processMin;
|
|
||||||
if (range <= 0) {
|
|
||||||
this.logger.error("Invalid process range: processMax must be greater than processMin.");
|
|
||||||
}
|
|
||||||
const rmse = this.rootMeanSquaredError(predicted, measured);
|
|
||||||
return rmse / range;
|
|
||||||
}
|
|
||||||
|
|
||||||
longTermNRMSD(input) {
|
|
||||||
|
|
||||||
const storedNRMSD = this.cumNRMSD;
|
|
||||||
const storedCount = this.cumCount;
|
|
||||||
const newCount = storedCount + 1;
|
|
||||||
|
|
||||||
// Update cumulative values
|
|
||||||
this.cumCount = newCount;
|
|
||||||
|
|
||||||
// Calculate new running average
|
|
||||||
if (storedCount === 0) {
|
|
||||||
this.cumNRMSD = input; // First value
|
|
||||||
} else {
|
|
||||||
// Running average formula: newAvg = oldAvg + (newValue - oldAvg) / newCount
|
|
||||||
this.cumNRMSD = storedNRMSD + (input - storedNRMSD) / newCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(newCount >= 100) {
|
|
||||||
// Return the current NRMSD value, not just the contribution from this sample
|
|
||||||
return this.cumNRMSD;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizeUsingRealtime(predicted, measured) {
|
|
||||||
const realtimeMin = Math.min(Math.min(...predicted), Math.min(...measured));
|
|
||||||
const realtimeMax = Math.max(Math.max(...predicted), Math.max(...measured));
|
|
||||||
const range = realtimeMax - realtimeMin;
|
|
||||||
if (range <= 0) {
|
|
||||||
throw new Error("Invalid process range: processMax must be greater than processMin.");
|
|
||||||
}
|
|
||||||
const rmse = this.rootMeanSquaredError(predicted, measured);
|
|
||||||
return rmse / range;
|
|
||||||
}
|
|
||||||
|
|
||||||
detectImmediateDrift(nrmse) {
|
|
||||||
let ImmDrift = {};
|
|
||||||
this.logger.debug(`checking immediate drift with thresholds : ${this.config.thresholds.NRMSE_HIGH} ${this.config.thresholds.NRMSE_MEDIUM} ${this.config.thresholds.NRMSE_LOW}`);
|
|
||||||
switch (true) {
|
|
||||||
case( nrmse > this.config.thresholds.NRMSE_HIGH ) :
|
|
||||||
ImmDrift = {level : 3 , feedback : "High immediate drift detected"};
|
|
||||||
break;
|
|
||||||
case( nrmse > this.config.thresholds.NRMSE_MEDIUM ) :
|
|
||||||
ImmDrift = {level : 2 , feedback : "Medium immediate drift detected"};
|
|
||||||
break;
|
|
||||||
case(nrmse > this.config.thresholds.NRMSE_LOW ):
|
|
||||||
ImmDrift = {level : 1 , feedback : "Low immediate drift detected"};
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ImmDrift = {level : 0 , feedback : "No drift detected"};
|
|
||||||
}
|
|
||||||
return ImmDrift;
|
|
||||||
}
|
|
||||||
|
|
||||||
detectLongTermDrift(longTermNRMSD) {
|
|
||||||
let LongTermDrift = {};
|
|
||||||
this.logger.debug(`checking longterm drift with thresholds : ${this.config.thresholds.LONG_TERM_HIGH} ${this.config.thresholds.LONG_TERM_MEDIUM} ${this.config.thresholds.LONG_TERM_LOW}`);
|
|
||||||
switch (true) {
|
|
||||||
case(Math.abs(longTermNRMSD) > this.config.thresholds.LONG_TERM_HIGH) :
|
|
||||||
LongTermDrift = {level : 3 , feedback : "High long-term drift detected"};
|
|
||||||
break;
|
|
||||||
case (Math.abs(longTermNRMSD) > this.config.thresholds.LONG_TERM_MEDIUM) :
|
|
||||||
LongTermDrift = {level : 2 , feedback : "Medium long-term drift detected"};
|
|
||||||
break;
|
|
||||||
case ( Math.abs(longTermNRMSD) > this.config.thresholds.LONG_TERM_LOW ) :
|
|
||||||
LongTermDrift = {level : 1 , feedback : "Low long-term drift detected"};
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LongTermDrift = {level : 0 , feedback : "No drift detected"};
|
|
||||||
}
|
|
||||||
return LongTermDrift;
|
|
||||||
}
|
|
||||||
|
|
||||||
detectDrift(nrmse, longTermNRMSD) {
|
|
||||||
const ImmDrift = this.detectImmediateDrift(nrmse);
|
|
||||||
const LongTermDrift = this.detectLongTermDrift(longTermNRMSD);
|
|
||||||
return { ImmDrift, LongTermDrift };
|
|
||||||
}
|
|
||||||
|
|
||||||
// asses the drift
|
|
||||||
assessDrift(predicted, measured, processMin, processMax) {
|
|
||||||
// Compute NRMSE and check for immediate drift
|
|
||||||
const nrmse = this.normalizedRootMeanSquaredError(predicted, measured, processMin, processMax);
|
|
||||||
this.logger.debug(`NRMSE: ${nrmse}`);
|
|
||||||
// cmopute long-term NRMSD and add result to cumalitve NRMSD
|
|
||||||
const longTermNRMSD = this.longTermNRMSD(nrmse);
|
|
||||||
// return the drift
|
|
||||||
// Return the drift assessment object
|
|
||||||
const driftAssessment = this.detectDrift(nrmse, longTermNRMSD);
|
|
||||||
return {
|
|
||||||
nrmse,
|
|
||||||
longTermNRMSD,
|
|
||||||
immediateLevel: driftAssessment.ImmDrift.level,
|
|
||||||
immediateFeedback: driftAssessment.ImmDrift.feedback,
|
|
||||||
longTermLevel: driftAssessment.LongTermDrift.level,
|
|
||||||
longTermFeedback: driftAssessment.LongTermDrift.feedback
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ErrorMetrics;
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
{
|
|
||||||
"general": {
|
|
||||||
"name": {
|
|
||||||
"default": "ErrorMetrics",
|
|
||||||
"rules": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A human-readable name for the configuration."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"default": null,
|
|
||||||
"rules": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true,
|
|
||||||
"description": "A unique identifier for this configuration, assigned dynamically when needed."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unit": {
|
|
||||||
"default": "unitless",
|
|
||||||
"rules": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The unit used for the state values (e.g., 'meters', 'seconds', 'unitless')."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"logging": {
|
|
||||||
"logLevel": {
|
|
||||||
"default": "info",
|
|
||||||
"rules": {
|
|
||||||
"type": "enum",
|
|
||||||
"values": [
|
|
||||||
{
|
|
||||||
"value": "debug",
|
|
||||||
"description": "Log messages are printed for debugging purposes."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "info",
|
|
||||||
"description": "Informational messages are printed."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "warn",
|
|
||||||
"description": "Warning messages are printed."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "error",
|
|
||||||
"description": "Error messages are printed."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enabled": {
|
|
||||||
"default": true,
|
|
||||||
"rules": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Indicates whether logging is active. If true, log messages will be generated."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"functionality": {
|
|
||||||
"softwareType": {
|
|
||||||
"default": "errorMetrics",
|
|
||||||
"rules": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Logical name identifying the software type."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"role": {
|
|
||||||
"default": "error calculation",
|
|
||||||
"rules": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Functional role within the system."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mode": {
|
|
||||||
"current": {
|
|
||||||
"default": "active",
|
|
||||||
"rules": {
|
|
||||||
"type": "enum",
|
|
||||||
"values": [
|
|
||||||
{
|
|
||||||
"value": "active",
|
|
||||||
"description": "The error metrics calculation is active."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "inactive",
|
|
||||||
"description": "The error metrics calculation is inactive."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "The operational mode of the error metrics calculation."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"thresholds": {
|
|
||||||
"NRMSE_LOW": {
|
|
||||||
"default": 0.05,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Low threshold for normalized root mean squared error."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"NRMSE_MEDIUM": {
|
|
||||||
"default": 0.10,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Medium threshold for normalized root mean squared error."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"NRMSE_HIGH": {
|
|
||||||
"default": 0.15,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "High threshold for normalized root mean squared error."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LONG_TERM_LOW": {
|
|
||||||
"default": 0.02,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Low threshold for long-term normalized root mean squared deviation."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LONG_TERM_MEDIUM": {
|
|
||||||
"default": 0.04,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Medium threshold for long-term normalized root mean squared deviation."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LONG_TERM_HIGH": {
|
|
||||||
"default": 0.06,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "High threshold for long-term normalized root mean squared deviation."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
class DynamicClusterDeviation {
|
|
||||||
constructor() {
|
|
||||||
this.clusters = []; // Stores clusters as { center, spread, count }
|
|
||||||
}
|
|
||||||
|
|
||||||
update(value) {
|
|
||||||
console.log(`\nProcessing value: ${value}`);
|
|
||||||
|
|
||||||
// If no clusters exist, create the first one
|
|
||||||
if (this.clusters.length === 0) {
|
|
||||||
this.clusters.push({ center: value, spread: 0, count: 1 });
|
|
||||||
console.log(` → First cluster created at ${value}`);
|
|
||||||
return { value, isOutlier: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Find the closest cluster
|
|
||||||
let bestMatch = null;
|
|
||||||
let minDistance = Infinity;
|
|
||||||
|
|
||||||
for (const cluster of this.clusters) {
|
|
||||||
const distance = Math.abs(value - cluster.center);
|
|
||||||
console.log(` Checking against cluster at ${cluster.center} (spread: ${cluster.spread}, count: ${cluster.count}) → distance: ${distance}`);
|
|
||||||
|
|
||||||
if (distance < minDistance) {
|
|
||||||
bestMatch = cluster;
|
|
||||||
minDistance = distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(` Closest cluster found at ${bestMatch.center} with distance: ${minDistance}`);
|
|
||||||
|
|
||||||
// Step 2: Compute dynamic threshold
|
|
||||||
const dynamicThreshold = 1 + 5 / Math.sqrt(bestMatch.count + 1);
|
|
||||||
const allowedDeviation = dynamicThreshold * (bestMatch.spread || 1);
|
|
||||||
|
|
||||||
console.log(` Dynamic threshold: ${dynamicThreshold.toFixed(2)}, Allowed deviation: ${allowedDeviation.toFixed(2)}`);
|
|
||||||
|
|
||||||
// Step 3: Check if value fits within the dynamically adjusted cluster spread
|
|
||||||
if (minDistance <= allowedDeviation) {
|
|
||||||
// Update cluster dynamically
|
|
||||||
const newCenter = (bestMatch.center * bestMatch.count + value) / (bestMatch.count + 1);
|
|
||||||
const newSpread = Math.max(bestMatch.spread, minDistance);
|
|
||||||
bestMatch.center = newCenter;
|
|
||||||
bestMatch.spread = newSpread;
|
|
||||||
bestMatch.count += 1;
|
|
||||||
|
|
||||||
console.log(` ✅ Value fits in cluster! Updating cluster:`);
|
|
||||||
console.log(` → New center: ${newCenter.toFixed(2)}`);
|
|
||||||
console.log(` → New spread: ${newSpread.toFixed(2)}`);
|
|
||||||
console.log(` → New count: ${bestMatch.count}`);
|
|
||||||
|
|
||||||
return { value, isOutlier: false };
|
|
||||||
} else {
|
|
||||||
// If too far, create a new cluster
|
|
||||||
this.clusters.push({ center: value, spread: 0, count: 1 });
|
|
||||||
|
|
||||||
console.log(` ❌ Outlier detected! New cluster created at ${value}`);
|
|
||||||
|
|
||||||
return { value, isOutlier: true };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rolling window simulation with outlier detection
|
|
||||||
/*
|
|
||||||
const detector = new DynamicClusterDeviation();
|
|
||||||
const dataStream = [10, 10.2, 10.5, 9.8, 11, 50, 10.3, 200, 201, 200.1, 205, 202, 250, 260, 270, 280, 290, 300];
|
|
||||||
|
|
||||||
// Define the number of elements per rolling window chunk.
|
|
||||||
const windowSize = 5;
|
|
||||||
let rollingWindow = [];
|
|
||||||
|
|
||||||
dataStream.forEach((value, index) => {
|
|
||||||
console.log(`\n=== Processing value ${index + 1} ===`);
|
|
||||||
rollingWindow.push(value);
|
|
||||||
const result = detector.update(value);
|
|
||||||
console.log(`Current rolling window: [${rollingWindow.join(', ')}]`);
|
|
||||||
console.log(`Result: value=${result.value} (${result.isOutlier ? 'Outlier' : 'Inlier'})`);
|
|
||||||
|
|
||||||
// Once the window size is reached, show current cluster states and reset the window for the next chunk.
|
|
||||||
if (rollingWindow.length === windowSize) {
|
|
||||||
console.log("\n--- Rolling window chunk finished ---");
|
|
||||||
console.log("Detector cluster states:", JSON.stringify(detector.clusters, null, 2));
|
|
||||||
rollingWindow = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("\nFinal detector cluster states:", JSON.stringify(detector.clusters, null, 2));
|
|
||||||
*/
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
//this class will handle the output events for the node red node
|
|
||||||
class OutputUtils {
|
|
||||||
constructor() {
|
|
||||||
this.output ={};
|
|
||||||
this.output['influxdb'] = {};
|
|
||||||
this.output['process'] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForChanges(output, format) {
|
|
||||||
const changedFields = {};
|
|
||||||
for (const key in output) {
|
|
||||||
if (output.hasOwnProperty(key) && output[key] !== this.output[format][key]) {
|
|
||||||
let value = output[key];
|
|
||||||
// For fields: if the value is an object (and not a Date), stringify it.
|
|
||||||
if (value !== null && typeof value === 'object' && !(value instanceof Date)) {
|
|
||||||
changedFields[key] = JSON.stringify(value);
|
|
||||||
} else {
|
|
||||||
changedFields[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the saved output state.
|
|
||||||
this.output[format] = { ...this.output[format], ...changedFields };
|
|
||||||
|
|
||||||
return changedFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
formatMsg(output, config, format) {
|
|
||||||
|
|
||||||
//define emtpy message
|
|
||||||
let msg = {};
|
|
||||||
|
|
||||||
// Compare output with last output and only include changed values
|
|
||||||
const changedFields = this.checkForChanges(output,format);
|
|
||||||
|
|
||||||
if (Object.keys(changedFields).length > 0) {
|
|
||||||
|
|
||||||
switch (format) {
|
|
||||||
case 'influxdb':
|
|
||||||
// Extract the relevant config properties.
|
|
||||||
const relevantConfig = this.extractRelevantConfig(config);
|
|
||||||
// Flatten the tags so that no nested objects are passed on.
|
|
||||||
const flatTags = this.flattenTags(relevantConfig);
|
|
||||||
msg = this.influxDBFormat(changedFields, config, flatTags);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'process':
|
|
||||||
|
|
||||||
// Compare output with last output and only include changed values
|
|
||||||
msg = this.processFormat(changedFields,config);
|
|
||||||
//console.log(msg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.log('Unknown format in output utils');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
influxDBFormat(changedFields, config , flatTags) {
|
|
||||||
// Create the measurement and topic using softwareType and name config.functionality.softwareType + .
|
|
||||||
const measurement = config.general.name;
|
|
||||||
const payload = {
|
|
||||||
measurement: measurement,
|
|
||||||
fields: changedFields,
|
|
||||||
tags: flatTags,
|
|
||||||
timestamp: new Date(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const topic = measurement;
|
|
||||||
const msg = { topic: topic, payload: payload };
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
flattenTags(obj) {
|
|
||||||
const result = {};
|
|
||||||
for (const key in obj) {
|
|
||||||
if (obj.hasOwnProperty(key)) {
|
|
||||||
const value = obj[key];
|
|
||||||
if (value !== null && typeof value === 'object' && !(value instanceof Date)) {
|
|
||||||
// Recursively flatten the nested object.
|
|
||||||
const flatChild = this.flattenTags(value);
|
|
||||||
for (const childKey in flatChild) {
|
|
||||||
if (flatChild.hasOwnProperty(childKey)) {
|
|
||||||
result[`${key}_${childKey}`] = String(flatChild[childKey]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// InfluxDB tags must be strings.
|
|
||||||
result[key] = String(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
extractRelevantConfig(config) {
|
|
||||||
|
|
||||||
return {
|
|
||||||
// general properties
|
|
||||||
id: config.general?.id,
|
|
||||||
name: config.general?.name,
|
|
||||||
unit: config.general?.unit,
|
|
||||||
// functionality properties
|
|
||||||
softwareType: config.functionality?.softwareType,
|
|
||||||
role: config.functionality?.role,
|
|
||||||
// asset properties (exclude machineCurve)
|
|
||||||
uuid: config.asset?.uuid,
|
|
||||||
geoLocation: config.asset?.geoLocation,
|
|
||||||
supplier: config.asset?.supplier,
|
|
||||||
type: config.asset?.type,
|
|
||||||
subType: config.asset?.subType,
|
|
||||||
model: config.asset?.model,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
processFormat(changedFields,config) {
|
|
||||||
// Create the measurement and topic using softwareType and name config.functionality.softwareType + .
|
|
||||||
const measurement = config.general.name;
|
|
||||||
const payload = changedFields;
|
|
||||||
const topic = measurement;
|
|
||||||
const msg = { topic: topic, payload: payload };
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = OutputUtils;
|
|
||||||
@@ -1,277 +0,0 @@
|
|||||||
//const EventEmitter = require('events');
|
|
||||||
|
|
||||||
class movementManager {
|
|
||||||
constructor(config, logger, emitter) {
|
|
||||||
this.emitter = emitter; //new EventEmitter(); //state class emitter
|
|
||||||
|
|
||||||
const { min, max, initial } = config.position;
|
|
||||||
const { speed, maxSpeed, interval } = config.movement;
|
|
||||||
|
|
||||||
this.minPosition = min;
|
|
||||||
this.maxPosition = max;
|
|
||||||
this.currentPosition = initial;
|
|
||||||
|
|
||||||
this.speed = speed;
|
|
||||||
this.maxSpeed = maxSpeed;
|
|
||||||
this.interval = interval;
|
|
||||||
this.timeleft = 0; // timeleft of current movement
|
|
||||||
|
|
||||||
this.logger = logger;
|
|
||||||
this.movementMode = config.movement.mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCurrentPosition() {
|
|
||||||
return this.currentPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
async moveTo(targetPosition, signal) {
|
|
||||||
// Constrain target position if necessary
|
|
||||||
if (
|
|
||||||
targetPosition < this.minPosition ||
|
|
||||||
targetPosition > this.maxPosition
|
|
||||||
) {
|
|
||||||
targetPosition = this.constrain(targetPosition);
|
|
||||||
this.logger.warn(
|
|
||||||
`New target position=${targetPosition} is constrained to fit between min=${this.minPosition} and max=${this.maxPosition}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.info(
|
|
||||||
`Starting movement to position ${targetPosition} in ${this.movementMode} with avg speed=${this.speed}%/s.`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (signal && signal.aborted) {
|
|
||||||
this.logger.debug("Movement aborted.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Execute the movement logic based on the mode
|
|
||||||
switch (this.movementMode) {
|
|
||||||
case "staticspeed":
|
|
||||||
const movelinFeedback = await this.moveLinear(targetPosition,signal);
|
|
||||||
this.logger.info(`Linear move: ${movelinFeedback} `);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "dynspeed":
|
|
||||||
const moveDynFeedback = await this.moveEaseInOut(targetPosition,signal);
|
|
||||||
this.logger.info(`Dynamic move : ${moveDynFeedback}`);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported movement mode: ${this.movementMode}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
moveLinear(targetPosition, signal) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Immediate abort if already signalled
|
|
||||||
if (signal?.aborted) {
|
|
||||||
return reject(new Error("Movement aborted"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clamp the final target into [minPosition, maxPosition]
|
|
||||||
targetPosition = this.constrain(targetPosition);
|
|
||||||
|
|
||||||
// Compute direction and remaining distance
|
|
||||||
const direction = targetPosition > this.currentPosition ? 1 : -1;
|
|
||||||
const distance = Math.abs(targetPosition - this.currentPosition);
|
|
||||||
|
|
||||||
// Speed is a fraction [0,1] of full-range per second
|
|
||||||
this.speed = Math.min(Math.max(this.speed, 0), 1);
|
|
||||||
const fullRange = this.maxPosition - this.minPosition;
|
|
||||||
const velocity = this.speed * fullRange; // units per second
|
|
||||||
if (velocity === 0) {
|
|
||||||
return reject(new Error("Movement aborted: zero speed"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duration and bookkeeping
|
|
||||||
const duration = distance / velocity; // seconds to go the remaining distance
|
|
||||||
this.timeleft = duration;
|
|
||||||
this.logger.debug(
|
|
||||||
`Linear move: dir=${direction}, dist=${distance}, vel=${velocity.toFixed(2)} u/s, dur=${duration.toFixed(2)}s`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Compute how much to move each tick
|
|
||||||
const intervalMs = this.interval;
|
|
||||||
const intervalSec = intervalMs / 1000;
|
|
||||||
const stepSize = direction * velocity * intervalSec;
|
|
||||||
|
|
||||||
const startTime = Date.now();
|
|
||||||
|
|
||||||
// Kick off the loop
|
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
// 7a) Abort check
|
|
||||||
if (signal?.aborted) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
return reject(new Error("Movement aborted"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance position and clamp
|
|
||||||
this.currentPosition += stepSize;
|
|
||||||
this.currentPosition = this.constrain(this.currentPosition);
|
|
||||||
this.emitPos(this.currentPosition);
|
|
||||||
|
|
||||||
// Update timeleft
|
|
||||||
const elapsed = (Date.now() - startTime) / 1000;
|
|
||||||
this.timeleft = Math.max(0, duration - elapsed);
|
|
||||||
|
|
||||||
this.logger.debug(
|
|
||||||
`pos=${this.currentPosition.toFixed(2)}, timeleft=${this.timeleft.toFixed(2)}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Completed the move?
|
|
||||||
if (
|
|
||||||
(direction > 0 && this.currentPosition >= targetPosition) ||
|
|
||||||
(direction < 0 && this.currentPosition <= targetPosition)
|
|
||||||
) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
this.currentPosition = targetPosition;
|
|
||||||
this.emitPos(this.currentPosition);
|
|
||||||
return resolve("Reached target move.");
|
|
||||||
}
|
|
||||||
}, intervalMs);
|
|
||||||
|
|
||||||
// 8) Also catch aborts that happen before the first tick
|
|
||||||
signal?.addEventListener("abort", () => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
reject(new Error("Movement aborted"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
moveLinearinTime(targetPosition,signal) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Abort immediately if already signalled
|
|
||||||
if (signal?.aborted) {
|
|
||||||
return reject(new Error("Movement aborted"));
|
|
||||||
}
|
|
||||||
|
|
||||||
const direction = targetPosition > this.currentPosition ? 1 : -1;
|
|
||||||
const distance = Math.abs(targetPosition - this.currentPosition);
|
|
||||||
|
|
||||||
// Ensure speed is a percentage [0, 1]
|
|
||||||
this.speed = Math.min(Math.max(this.speed, 0), 1);
|
|
||||||
|
|
||||||
// Calculate duration based on percentage of distance per second
|
|
||||||
const duration = 1 / this.speed; // 1 second for 100% of the distance
|
|
||||||
|
|
||||||
this.timeleft = duration; //set this so other classes can use it
|
|
||||||
this.logger.debug(
|
|
||||||
`Linear movement: Direction=${direction}, Distance=${distance}, Duration=${duration}s`
|
|
||||||
);
|
|
||||||
|
|
||||||
let elapsedTime = 0;
|
|
||||||
const interval = this.interval; // Update every x ms
|
|
||||||
const totalSteps = Math.ceil((duration * 1000) / interval);
|
|
||||||
const stepSize = direction * (distance / totalSteps);
|
|
||||||
|
|
||||||
// 2) Set up the abort listener once
|
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
// 3) Check for abort on each tick
|
|
||||||
if (signal?.aborted) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
return reject(new Error("Movement aborted"));
|
|
||||||
}
|
|
||||||
// Update elapsed time
|
|
||||||
elapsedTime += interval / 1000;
|
|
||||||
|
|
||||||
this.timeleft = duration - elapsedTime; //set this so other classes can use it
|
|
||||||
|
|
||||||
// Update the position incrementally
|
|
||||||
this.currentPosition += stepSize;
|
|
||||||
this.emitPos(this.currentPosition);
|
|
||||||
this.logger.debug(
|
|
||||||
`Using ${this.movementMode} => Current position ${this.currentPosition}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if the target position has been reached
|
|
||||||
if (
|
|
||||||
(direction > 0 && this.currentPosition >= targetPosition) ||
|
|
||||||
(direction < 0 && this.currentPosition <= targetPosition)
|
|
||||||
) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
this.currentPosition = targetPosition;
|
|
||||||
resolve(`Reached target move.`);
|
|
||||||
}
|
|
||||||
}, interval);
|
|
||||||
// Also attach abort outside the interval in case it fires before the first tick:
|
|
||||||
signal?.addEventListener("abort", () => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
reject(new Error("Movement aborted"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
moveEaseInOut(targetPosition, signal) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// 1) Bail immediately if already aborted
|
|
||||||
if (signal?.aborted) {
|
|
||||||
return reject(new Error("Movement aborted"));
|
|
||||||
}
|
|
||||||
|
|
||||||
const direction = targetPosition > this.currentPosition ? 1 : -1;
|
|
||||||
const totalDistance = Math.abs(targetPosition - this.currentPosition);
|
|
||||||
const startPosition = this.currentPosition;
|
|
||||||
this.speed = Math.min(Math.max(this.speed, 0), 1);
|
|
||||||
|
|
||||||
const easeFunction = (t) =>
|
|
||||||
t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
|
|
||||||
|
|
||||||
let elapsedTime = 0;
|
|
||||||
const duration = totalDistance / this.speed;
|
|
||||||
this.timeleft = duration;
|
|
||||||
const interval = this.interval;
|
|
||||||
|
|
||||||
// 2) Start the moving loop
|
|
||||||
const intervalId = setInterval(() => {
|
|
||||||
// 3) Check for abort on each tick
|
|
||||||
if (signal?.aborted) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
return reject(new Error("Movement aborted"));
|
|
||||||
}
|
|
||||||
|
|
||||||
elapsedTime += interval / 1000;
|
|
||||||
const progress = Math.min(elapsedTime / duration, 1);
|
|
||||||
this.timeleft = duration - elapsedTime;
|
|
||||||
const easedProgress = easeFunction(progress);
|
|
||||||
const newPosition =
|
|
||||||
startPosition + (targetPosition - startPosition) * easedProgress;
|
|
||||||
|
|
||||||
this.emitPos(newPosition);
|
|
||||||
this.logger.debug(
|
|
||||||
`Using ${this.movementMode} => Progress=${progress.toFixed(
|
|
||||||
2
|
|
||||||
)}, Eased=${easedProgress.toFixed(2)}`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (progress >= 1) {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
this.currentPosition = targetPosition;
|
|
||||||
resolve(`Reached target move.`);
|
|
||||||
} else {
|
|
||||||
this.currentPosition = newPosition;
|
|
||||||
}
|
|
||||||
}, interval);
|
|
||||||
|
|
||||||
// 4) Also listen once for abort before first tick
|
|
||||||
signal?.addEventListener("abort", () => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
reject(new Error("Movement aborted"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
emitPos(newPosition) {
|
|
||||||
this.emitter.emit("positionChange", newPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
constrain(value) {
|
|
||||||
return Math.min(Math.max(value, this.minPosition), this.maxPosition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = movementManager;
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
//load local dependencies
|
|
||||||
const EventEmitter = require('events');
|
|
||||||
const StateManager = require('./stateManager');
|
|
||||||
const MovementManager = require('./movementManager');
|
|
||||||
|
|
||||||
//load all config modules
|
|
||||||
const defaultConfig = require('./stateConfig.json');
|
|
||||||
const ConfigUtils = require('../../../generalFunctions/helper/configUtils');
|
|
||||||
|
|
||||||
class state{
|
|
||||||
constructor(config = {}, logger) {
|
|
||||||
|
|
||||||
this.emitter = new EventEmitter(); // Own EventEmitter
|
|
||||||
this.configUtils = new ConfigUtils(defaultConfig);
|
|
||||||
this.config = this.configUtils.initConfig(config);
|
|
||||||
this.abortController = null; // new abort controller for aborting async tasks
|
|
||||||
// Init after config is set
|
|
||||||
this.logger = logger;
|
|
||||||
|
|
||||||
// Initialize StateManager for state handling
|
|
||||||
this.stateManager = new StateManager(this.config,this.logger);
|
|
||||||
this.movementManager = new MovementManager(this.config, this.logger, this.emitter);
|
|
||||||
|
|
||||||
this.delayedMove = null;
|
|
||||||
this.mode = this.config.mode.current;
|
|
||||||
|
|
||||||
// Log initialization
|
|
||||||
this.logger.info("State class initialized.");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- Delegate State Management -------- //
|
|
||||||
|
|
||||||
getMoveTimeLeft() {
|
|
||||||
return this.movementManager.timeleft;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCurrentState() {
|
|
||||||
return this.stateManager.currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
getStateDescription() {
|
|
||||||
return this.stateManager.getStateDescription();
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- Movement Methods -------- //
|
|
||||||
getCurrentPosition() {
|
|
||||||
return this.movementManager.getCurrentPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
getRunTimeHours() {
|
|
||||||
return this.stateManager.getRunTimeHours();
|
|
||||||
}
|
|
||||||
|
|
||||||
async moveTo(targetPosition) {
|
|
||||||
|
|
||||||
// Check for invalid conditions and throw errors
|
|
||||||
if (targetPosition === this.getCurrentPosition()) {
|
|
||||||
this.logger.warn(`Target position=${targetPosition} is the same as the current position ${this.getCurrentPosition()}. Not executing move.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.stateManager.getCurrentState() !== "operational") {
|
|
||||||
if (this.config.mode.current === "auto") {
|
|
||||||
this.delayedMove = targetPosition;
|
|
||||||
this.logger.warn(`Saving setpoint=${targetPosition} to execute once back in 'operational' state.`);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
this.logger.warn(`Not able to accept setpoint=${targetPosition} while not in ${this.stateManager.getCurrentState()} state`);
|
|
||||||
}
|
|
||||||
//return early
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.abortController = new AbortController();
|
|
||||||
const { signal } = this.abortController;
|
|
||||||
try {
|
|
||||||
const newState = targetPosition < this.getCurrentPosition() ? "decelerating" : "accelerating";
|
|
||||||
await this.transitionToState(newState,signal); // awaits transition
|
|
||||||
await this.movementManager.moveTo(targetPosition,signal); // awaits moving
|
|
||||||
this.emitter.emit("movementComplete", { position: targetPosition });
|
|
||||||
await this.transitionToState("operational");
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- State Transition Methods -------- //
|
|
||||||
|
|
||||||
async transitionToState(targetState, signal) {
|
|
||||||
|
|
||||||
const fromState = this.getCurrentState();
|
|
||||||
const position = this.getCurrentPosition();
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
this.logger.debug(`Starting transition from ${fromState} to ${targetState}.`);
|
|
||||||
const feedback = await this.stateManager.transitionTo(targetState,signal);
|
|
||||||
this.logger.info(`Statemanager: ${feedback}`);
|
|
||||||
|
|
||||||
/* -- Auto pick setpoints in auto mode when operational--*/
|
|
||||||
if (
|
|
||||||
targetState === "operational" &&
|
|
||||||
this.config.mode.current === "auto" &&
|
|
||||||
this.delayedMove !== position &&
|
|
||||||
this.delayedMove
|
|
||||||
) {
|
|
||||||
this.logger.info(`Automatically picking up on last requested setpoint ${this.delayedMove}`);
|
|
||||||
//trigger move
|
|
||||||
await this.moveTo(this.delayedMove,signal);
|
|
||||||
this.delayedMove = null;
|
|
||||||
|
|
||||||
this.logger.info(`moveTo : ${feedback} `);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.info(`State change to ${targetState} completed.`);
|
|
||||||
this.emitter.emit('stateChange', targetState); // <-- Implement Here
|
|
||||||
} catch (error) {
|
|
||||||
if (
|
|
||||||
error.message === "Transition aborted" ||
|
|
||||||
error.message === "Movement aborted"
|
|
||||||
) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
this.logger.error(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = state;
|
|
||||||
|
|
||||||
@@ -1,331 +0,0 @@
|
|||||||
{
|
|
||||||
"general": {
|
|
||||||
"name": {
|
|
||||||
"default": "State Configuration",
|
|
||||||
"rules": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A human-readable name for the state configuration."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"default": null,
|
|
||||||
"rules": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true,
|
|
||||||
"description": "A unique identifier for this configuration, assigned dynamically when needed."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unit": {
|
|
||||||
"default": "unitless",
|
|
||||||
"rules": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The unit used for the state values (e.g., 'meters', 'seconds', 'unitless')."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"logging": {
|
|
||||||
"logLevel": {
|
|
||||||
"default": "info",
|
|
||||||
"rules": {
|
|
||||||
"type": "enum",
|
|
||||||
"values": [
|
|
||||||
{
|
|
||||||
"value": "debug",
|
|
||||||
"description": "Log messages are printed for debugging purposes."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "info",
|
|
||||||
"description": "Informational messages are printed."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "warn",
|
|
||||||
"description": "Warning messages are printed."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "error",
|
|
||||||
"description": "Error messages are printed."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enabled": {
|
|
||||||
"default": true,
|
|
||||||
"rules": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Indicates whether logging is active. If true, log messages will be generated."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"functionality": {
|
|
||||||
"softwareType": {
|
|
||||||
"default": "state class",
|
|
||||||
"rules": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Logical name identifying the software type."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"role": {
|
|
||||||
"default": "StateController",
|
|
||||||
"rules": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Functional role within the system."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"time": {
|
|
||||||
"starting": {
|
|
||||||
"default": 10,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Time in seconds for the starting phase."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"warmingup": {
|
|
||||||
"default": 5,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Time in seconds for the warming-up phase."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"stopping": {
|
|
||||||
"default": 5,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Time in seconds for the stopping phase."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"coolingdown": {
|
|
||||||
"default": 10,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Time in seconds for the cooling-down phase."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"movement": {
|
|
||||||
"mode": {
|
|
||||||
"default": "dynspeed",
|
|
||||||
"rules": {
|
|
||||||
"type": "enum",
|
|
||||||
"values": [
|
|
||||||
{
|
|
||||||
"value": "staticspeed",
|
|
||||||
"description": "Linear movement to setpoint."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "dynspeed",
|
|
||||||
"description": "Ease-in and ease-out to setpoint."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"speed": {
|
|
||||||
"default": 1,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Current speed setting."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"maxSpeed": {
|
|
||||||
"default": 10,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Maximum speed setting."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"interval": {
|
|
||||||
"default": 1000,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Feedback interval in milliseconds."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"position": {
|
|
||||||
"min": {
|
|
||||||
"default": 0,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Minimum position value."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"max": {
|
|
||||||
"default": 100,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Maximum position value."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"initial": {
|
|
||||||
"default": 0,
|
|
||||||
"rules": {
|
|
||||||
"type": "number",
|
|
||||||
"description": "Initial position value."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"current": {
|
|
||||||
"default": "idle",
|
|
||||||
"rules": {
|
|
||||||
"type": "enum",
|
|
||||||
"values": [
|
|
||||||
{
|
|
||||||
"value": "idle",
|
|
||||||
"description": "Machine is idle."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "starting",
|
|
||||||
"description": "Machine is starting up."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "warmingup",
|
|
||||||
"description": "Machine is warming up."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "operational",
|
|
||||||
"description": "Machine is running."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "accelerating",
|
|
||||||
"description": "Machine is accelerating."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "decelerating",
|
|
||||||
"description": "Machine is decelerating."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "stopping",
|
|
||||||
"description": "Machine is stopping."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "coolingdown",
|
|
||||||
"description": "Machine is cooling down."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "off",
|
|
||||||
"description": "Machine is off."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Current state of the machine."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"allowedTransitions":{
|
|
||||||
"default": {},
|
|
||||||
"rules": {
|
|
||||||
"type": "object",
|
|
||||||
"schema": {
|
|
||||||
"idle": {
|
|
||||||
"default": ["starting", "off","emergencystop"],
|
|
||||||
"rules":{
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Allowed transitions from idle state."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"starting": {
|
|
||||||
"default": ["starting","warmingup","emergencystop"],
|
|
||||||
"rules":{
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Allowed transitions from starting state."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"warmingup": {
|
|
||||||
"default": ["operational","emergencystop"],
|
|
||||||
"rules":{
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Allowed transitions from warmingup state."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"operational": {
|
|
||||||
"default": ["accelerating", "decelerating", "stopping","emergencystop"],
|
|
||||||
"rules":{
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Allowed transitions from operational state."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"accelerating": {
|
|
||||||
"default": ["operational","emergencystop"],
|
|
||||||
"rules":{
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Allowed transitions from accelerating state."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"decelerating": {
|
|
||||||
"default": ["operational","emergencystop"],
|
|
||||||
"rules":{
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Allowed transitions from decelerating state."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"stopping": {
|
|
||||||
"default": ["idle","coolingdown","emergencystop"],
|
|
||||||
"rules":{
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Allowed transitions from stopping state."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"coolingdown": {
|
|
||||||
"default": ["idle","off","emergencystop"],
|
|
||||||
"rules":{
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Allowed transitions from coolingDown state."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"off": {
|
|
||||||
"default": ["idle","emergencystop"],
|
|
||||||
"rules":{
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Allowed transitions from off state."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"emergencystop": {
|
|
||||||
"default": ["idle","off"],
|
|
||||||
"rules":{
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Allowed transitions from emergency stop state."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"description": "Allowed transitions between states."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"activeStates":{
|
|
||||||
"default": ["operational", "starting", "warmingup", "accelerating", "decelerating"],
|
|
||||||
"rules": {
|
|
||||||
"type": "set",
|
|
||||||
"itemType": "string",
|
|
||||||
"description": "Active states."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mode": {
|
|
||||||
"current": {
|
|
||||||
"default": "auto",
|
|
||||||
"rules": {
|
|
||||||
"type": "enum",
|
|
||||||
"values": [
|
|
||||||
{
|
|
||||||
"value": "auto",
|
|
||||||
"description": "Automatically tracks and handles delayed commands for setpoints > 0."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": "manual",
|
|
||||||
"description": "Requires explicit commands to start."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Current mode of the machine."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file stateManager.js
|
|
||||||
*
|
|
||||||
* Permission is hereby granted to any person obtaining a copy of this software
|
|
||||||
* and associated documentation files (the "Software"), to use it for personal
|
|
||||||
* or non-commercial purposes, with the following restrictions:
|
|
||||||
*
|
|
||||||
* 1. **No Copying or Redistribution**: The Software or any of its parts may not
|
|
||||||
* be copied, merged, distributed, sublicensed, or sold without explicit
|
|
||||||
* prior written permission from the author.
|
|
||||||
*
|
|
||||||
* 2. **Commercial Use**: Any use of the Software for commercial purposes requires
|
|
||||||
* a valid license, obtainable only with the explicit consent of the author.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* Ownership of this code remains solely with the original author. Unauthorized
|
|
||||||
* use of this Software is strictly prohibited.
|
|
||||||
*
|
|
||||||
* @summary Class for managing state transitions and state descriptions.
|
|
||||||
* @description Class for managing state transitions and state descriptions.
|
|
||||||
* @module stateManager
|
|
||||||
* @exports stateManager
|
|
||||||
* @version 0.1.0
|
|
||||||
* @since 0.1.0
|
|
||||||
*
|
|
||||||
* Author:
|
|
||||||
* - Rene De Ren
|
|
||||||
* Email:
|
|
||||||
* - rene@thegoldenbasket.nl
|
|
||||||
*/
|
|
||||||
|
|
||||||
class stateManager {
|
|
||||||
constructor(config, logger) {
|
|
||||||
this.currentState = config.state.current;
|
|
||||||
this.availableStates = config.state.available;
|
|
||||||
this.descriptions = config.state.descriptions;
|
|
||||||
this.logger = logger;
|
|
||||||
this.transitionTimeleft = 0;
|
|
||||||
this.transitionTimes = config.time;
|
|
||||||
|
|
||||||
// Define valid transitions (can be extended dynamically if needed)
|
|
||||||
this.validTransitions = config.state.allowedTransitions;
|
|
||||||
|
|
||||||
// NEW: Initialize runtime tracking
|
|
||||||
this.runTimeHours = 0; // cumulative runtime in hours
|
|
||||||
this.runTimeStart = null; // timestamp when active state began
|
|
||||||
|
|
||||||
// Define active states (runtime counts only in these states)
|
|
||||||
this.activeStates = config.state.activeStates;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCurrentState() {
|
|
||||||
return this.currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
transitionTo(newState,signal) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (signal && signal.aborted) {
|
|
||||||
this.logger.debug("Transition aborted.");
|
|
||||||
return reject("Transition aborted.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.isValidTransition(newState)) {
|
|
||||||
return reject(
|
|
||||||
`Invalid transition from ${this.currentState} to ${newState}. Transition not executed.`
|
|
||||||
); //go back early and reject promise
|
|
||||||
}
|
|
||||||
|
|
||||||
// NEW: Handle runtime tracking based on active states
|
|
||||||
this.handleRuntimeTracking(newState);
|
|
||||||
|
|
||||||
const transitionDuration = this.transitionTimes[this.currentState] || 0; // Default to 0 if no transition time
|
|
||||||
this.logger.debug(
|
|
||||||
`Transition from ${this.currentState} to ${newState} will take ${transitionDuration}s.`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (transitionDuration > 0) {
|
|
||||||
const timeoutId = setTimeout(() => {
|
|
||||||
this.currentState = newState;
|
|
||||||
resolve(`Transition from ${this.currentState} to ${newState} completed in ${transitionDuration}s.`);
|
|
||||||
}, transitionDuration * 1000);
|
|
||||||
if (signal) {
|
|
||||||
signal.addEventListener('abort', () => {
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
reject(new Error('Transition aborted'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.currentState = newState;
|
|
||||||
resolve(`Immediate transition to ${this.currentState} completed.`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRuntimeTracking(newState) {
|
|
||||||
// NEW: Handle runtime tracking based on active states
|
|
||||||
const wasActive = this.activeStates.has(this.currentState);
|
|
||||||
const willBeActive = this.activeStates.has(newState);
|
|
||||||
if (wasActive && !willBeActive && this.runTimeStart) {
|
|
||||||
// stop runtime timer and accumulate elapsed time
|
|
||||||
const elapsed = (Date.now() - this.runTimeStart) / 3600000; // hours
|
|
||||||
this.runTimeHours += elapsed;
|
|
||||||
this.runTimeStart = null;
|
|
||||||
this.logger.debug(
|
|
||||||
`Runtime timer stopped; elapsed=${elapsed.toFixed(
|
|
||||||
3
|
|
||||||
)}h, total=${this.runTimeHours.toFixed(3)}h.`
|
|
||||||
);
|
|
||||||
} else if (!wasActive && willBeActive && !this.runTimeStart) {
|
|
||||||
// starting new runtime
|
|
||||||
this.runTimeStart = Date.now();
|
|
||||||
this.logger.debug("Runtime timer started.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isValidTransition(newState) {
|
|
||||||
this.logger.debug(
|
|
||||||
`Check 1 Transition valid ? From ${
|
|
||||||
this.currentState
|
|
||||||
} To ${newState} => ${this.validTransitions[this.currentState]?.has(
|
|
||||||
newState
|
|
||||||
)} `
|
|
||||||
);
|
|
||||||
this.logger.debug(
|
|
||||||
`Check 2 Transition valid ? ${
|
|
||||||
this.currentState
|
|
||||||
} is not equal to ${newState} => ${this.currentState !== newState}`
|
|
||||||
);
|
|
||||||
// check if transition is valid and not the same as before
|
|
||||||
const valid =
|
|
||||||
this.validTransitions[this.currentState]?.has(newState) &&
|
|
||||||
this.currentState !== newState;
|
|
||||||
|
|
||||||
//if not valid
|
|
||||||
if (!valid) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getStateDescription(state = this.currentState) {
|
|
||||||
return this.descriptions[state] || "No description available.";
|
|
||||||
}
|
|
||||||
|
|
||||||
// NEW: Getter to retrieve current cumulative runtime (active time) in hours.
|
|
||||||
getRunTimeHours() {
|
|
||||||
// If currently active add the ongoing duration.
|
|
||||||
let currentElapsed = 0;
|
|
||||||
if (this.runTimeStart) {
|
|
||||||
currentElapsed = (Date.now() - this.runTimeStart) / 3600000;
|
|
||||||
}
|
|
||||||
return this.runTimeHours + currentElapsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = stateManager;
|
|
||||||
@@ -1,528 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file validation.js
|
|
||||||
*
|
|
||||||
* Permission is hereby granted to any person obtaining a copy of this software
|
|
||||||
* and associated documentation files (the "Software"), to use it for personal
|
|
||||||
* or non-commercial purposes, with the following restrictions:
|
|
||||||
*
|
|
||||||
* 1. **No Copying or Redistribution**: The Software or any of its parts may not
|
|
||||||
* be copied, merged, distributed, sublicensed, or sold without explicit
|
|
||||||
* prior written permission from the author.
|
|
||||||
*
|
|
||||||
* 2. **Commercial Use**: Any use of the Software for commercial purposes requires
|
|
||||||
* a valid license, obtainable only with the explicit consent of the author.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* Ownership of this code remains solely with the original author. Unauthorized
|
|
||||||
* use of this Software is strictly prohibited.
|
|
||||||
|
|
||||||
* @summary Validation utility for validating and constraining configuration values.
|
|
||||||
* @description Validation utility for validating and constraining configuration values.
|
|
||||||
* @module ValidationUtils
|
|
||||||
* @requires Logger
|
|
||||||
* @exports ValidationUtils
|
|
||||||
* @version 0.1.0
|
|
||||||
* @since 0.1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
const Logger = require("./logger");
|
|
||||||
|
|
||||||
class ValidationUtils {
|
|
||||||
constructor(IloggerEnabled, IloggerLevel) {
|
|
||||||
const loggerEnabled = IloggerEnabled || true;
|
|
||||||
const loggerLevel = IloggerLevel || "warn";
|
|
||||||
this.logger = new Logger(loggerEnabled, loggerLevel, 'ValidationUtils');
|
|
||||||
}
|
|
||||||
|
|
||||||
constrain(value, min, max) {
|
|
||||||
if (typeof value !== "number") {
|
|
||||||
this.logger?.warn(`Value '${value}' is not a number. Defaulting to ${min}.`);
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
return Math.min(Math.max(value, min), max);
|
|
||||||
}
|
|
||||||
|
|
||||||
validateSchema(config, schema, name) {
|
|
||||||
|
|
||||||
const validatedConfig = {};
|
|
||||||
let configValue;
|
|
||||||
|
|
||||||
// 1. Remove any unknown keys (keys not defined in the schema).
|
|
||||||
// Log a warning and omit them from the final config.
|
|
||||||
for (const key of Object.keys(config)) {
|
|
||||||
if (!(key in schema)) {
|
|
||||||
this.logger.warn(
|
|
||||||
`[${name}] Unknown key '${key}' found in config. Removing it.`
|
|
||||||
);
|
|
||||||
delete config[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate each key in the schema and loop over wildcards if they are not in schema
|
|
||||||
for ( const key in schema ) {
|
|
||||||
|
|
||||||
if (key === "rules" || key === "description" || key === "schema") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldSchema = schema[key];
|
|
||||||
const { rules = {} } = fieldSchema;
|
|
||||||
|
|
||||||
// Default to the schema's default value if the key is missing
|
|
||||||
if (config[key] === undefined) {
|
|
||||||
if (fieldSchema.default === undefined) {
|
|
||||||
// If there's a nested schema, go deeper with an empty object rather than logging "no rule"
|
|
||||||
if (rules.schema) {
|
|
||||||
this.logger.warn(`${name}.${key} has no default, but has a nested schema.`);
|
|
||||||
validatedConfig[key] = this.validateSchema({}, rules.schema, `${name}.${key}`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.logger.info(
|
|
||||||
`There is no rule for ${name}.${key} and no default value. ` +
|
|
||||||
`Using full schema value but validating deeper levels first...`
|
|
||||||
);
|
|
||||||
|
|
||||||
const SubObject = this.validateSchema({}, fieldSchema, `${name}.${key}`);
|
|
||||||
|
|
||||||
validatedConfig[key] = SubObject;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.logger.info(`There is no value provided for ${name}.${key}. Using default value.`);
|
|
||||||
configValue = fieldSchema.default;
|
|
||||||
}
|
|
||||||
//continue;
|
|
||||||
} else {
|
|
||||||
// Use the provided value if it exists, otherwise use the default value
|
|
||||||
configValue = config[key] !== undefined ? config[key] : fieldSchema.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to parse the value to the expected type if possible
|
|
||||||
switch (rules.type) {
|
|
||||||
|
|
||||||
case "number":
|
|
||||||
configValue = this.validateNumber(configValue, rules, fieldSchema, name, key);
|
|
||||||
break;
|
|
||||||
case "boolean":
|
|
||||||
configValue = this.validateBoolean(configValue, name, key);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "string":
|
|
||||||
configValue = this.validateString(configValue,rules,fieldSchema, name, key);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "array":
|
|
||||||
configValue = this.validateArray(configValue, rules, fieldSchema, name, key);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "set":
|
|
||||||
configValue = this.validateSet(configValue, rules, fieldSchema, name, key);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "object":
|
|
||||||
configValue = this.validateObject(configValue, rules, fieldSchema, name, key);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "enum":
|
|
||||||
configValue = this.validateEnum(configValue, rules, fieldSchema, name, key);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "curve":
|
|
||||||
validatedConfig[key] = this.validateCurve(configValue,fieldSchema.default);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case "machineCurve":
|
|
||||||
validatedConfig[key] = this.validateMachineCurve(configValue,fieldSchema.default);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case "integer":
|
|
||||||
validatedConfig[key] = this.validateInteger(configValue, rules, fieldSchema, name, key);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
case undefined:
|
|
||||||
// If we see 'rules.schema' but no 'rules.type', treat it like an object:
|
|
||||||
if (rules.schema && !rules.type) {
|
|
||||||
// Log a warning and skip the extra pass for nested schema
|
|
||||||
this.logger.warn(
|
|
||||||
`${name}.${key} has a nested schema but no type. ` +
|
|
||||||
`Treating it as type="object" to skip extra pass.`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Otherwise, fallback to your existing "validateUndefined" logic
|
|
||||||
validatedConfig[key] = this.validateUndefined(configValue, fieldSchema, name, key);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
|
|
||||||
default:
|
|
||||||
this.logger.warn(`${name}.${key} has an unknown validation type: ${rules.type}. Skipping validation.`);
|
|
||||||
validatedConfig[key] = fieldSchema.default;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign the validated or converted value
|
|
||||||
validatedConfig[key] = configValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore unknown keys by not processing them at all
|
|
||||||
this.logger.info(`Validation completed for ${name}.`);
|
|
||||||
|
|
||||||
return validatedConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeUnwantedKeys(obj) {
|
|
||||||
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
return obj.map((item) => this.removeUnwantedKeys(item));
|
|
||||||
}
|
|
||||||
if (obj && typeof obj === "object") {
|
|
||||||
const newObj = {};
|
|
||||||
for (const [k, v] of Object.entries(obj)) {
|
|
||||||
|
|
||||||
// Skip or remove keys like 'default', 'rules', 'description', etc.
|
|
||||||
if (["rules", "description"].includes(k)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if("default" in v){
|
|
||||||
//put the default value in the object
|
|
||||||
newObj[k] = v.default;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
newObj[k] = this.removeUnwantedKeys(v);
|
|
||||||
}
|
|
||||||
return newObj;
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
validateMachineCurve(curve, defaultCurve) {
|
|
||||||
if (!curve || typeof curve !== "object" || Object.keys(curve).length === 0) {
|
|
||||||
this.logger.warn("Curve is missing or invalid. Defaulting to basic curve.");
|
|
||||||
return defaultCurve;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate that nq and np exist and are objects
|
|
||||||
const { nq, np } = curve;
|
|
||||||
if (!nq || typeof nq !== "object" || !np || typeof np !== "object") {
|
|
||||||
this.logger.warn("Curve must contain valid 'nq' and 'np' objects. Defaulting to basic curve.");
|
|
||||||
return defaultCurve;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate that each dimension key points to a valid object with x and y arrays
|
|
||||||
const validatedNq = this.validateDimensionStructure(nq, "nq");
|
|
||||||
const validatedNp = this.validateDimensionStructure(np, "np");
|
|
||||||
|
|
||||||
if (!validatedNq || !validatedNp) {
|
|
||||||
return defaultCurve;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { nq: validatedNq, np: validatedNp }; // Return the validated curve
|
|
||||||
}
|
|
||||||
|
|
||||||
validateCurve(curve, defaultCurve) {
|
|
||||||
if (!curve || typeof curve !== "object" || Object.keys(curve).length === 0) {
|
|
||||||
this.logger.warn("Curve is missing or invalid. Defaulting to basic curve.");
|
|
||||||
return defaultCurve;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate that each dimension key points to a valid object with x and y arrays
|
|
||||||
const validatedCurve = this.validateDimensionStructure(curve, "curve");
|
|
||||||
if (!validatedCurve) {
|
|
||||||
return defaultCurve;
|
|
||||||
}
|
|
||||||
|
|
||||||
return validatedCurve; // Return the validated curve
|
|
||||||
}
|
|
||||||
|
|
||||||
validateDimensionStructure(dimension, name) {
|
|
||||||
const validatedDimension = {};
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(dimension)) {
|
|
||||||
// Validate that each key points to an object with x and y arrays
|
|
||||||
if (typeof value !== "object") {
|
|
||||||
this.logger.warn(`Dimension '${name}' key '${key}' is not valid. Returning to default.`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Validate that x and y are arrays
|
|
||||||
else if (!Array.isArray(value.x) || !Array.isArray(value.y)) {
|
|
||||||
this.logger.warn(`Dimension '${name}' key '${key}' is missing x or y arrays. Converting to arrays.`);
|
|
||||||
// Try to convert to arrays first
|
|
||||||
value.x = Object.values(value.x);
|
|
||||||
value.y = Object.values(value.y);
|
|
||||||
|
|
||||||
// If still not arrays return false
|
|
||||||
if (!Array.isArray(value.x) || !Array.isArray(value.y)) {
|
|
||||||
this.logger.warn(`Dimension '${name}' key '${key}' is not valid. Returning to default.`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Validate that x and y arrays are the same length
|
|
||||||
else if (value.x.length !== value.y.length) {
|
|
||||||
this.logger.warn(`Dimension '${name}' key '${key}' has mismatched x and y lengths. Ignoring this key.`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Validate that x values are in ascending order
|
|
||||||
else if (!this.isSorted(value.x)) {
|
|
||||||
this.logger.warn(`Dimension '${name}' key '${key}' has unsorted x values. Sorting...`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Validate that x values are unique
|
|
||||||
else if (!this.isUnique(value.x)) {
|
|
||||||
this.logger.warn(`Dimension '${name}' key '${key}' has duplicate x values. Removing duplicates...`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Validate that y values are numbers
|
|
||||||
else if (!this.areNumbers(value.y)) {
|
|
||||||
this.logger.warn(`Dimension '${name}' key '${key}' has non-numeric y values. Ignoring this key.`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
validatedDimension[key] = value;
|
|
||||||
}
|
|
||||||
return validatedDimension;
|
|
||||||
}
|
|
||||||
|
|
||||||
isSorted(arr) {
|
|
||||||
return arr.every((_, i) => i === 0 || arr[i] >= arr[i - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
isUnique(arr) {
|
|
||||||
return new Set(arr).size === arr.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
areNumbers(arr) {
|
|
||||||
return arr.every((x) => typeof x === "number");
|
|
||||||
}
|
|
||||||
|
|
||||||
validateNumber(configValue, rules, fieldSchema, name, key) {
|
|
||||||
|
|
||||||
if (typeof configValue !== "number") {
|
|
||||||
const parsedValue = parseFloat(configValue);
|
|
||||||
if (!isNaN(parsedValue)) {
|
|
||||||
this.logger.warn(`${name}.${key} was parsed to a number: ${configValue} -> ${parsedValue}`);
|
|
||||||
configValue = parsedValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rules.min !== undefined && configValue < rules.min) {
|
|
||||||
this.logger.warn(
|
|
||||||
`${name}.${key} is below the minimum (${rules.min}). Using default value.`
|
|
||||||
);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
if (rules.max !== undefined && configValue > rules.max) {
|
|
||||||
this.logger.warn(
|
|
||||||
`${name}.${key} exceeds the maximum (${rules.max}). Using default value.`
|
|
||||||
);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.debug(`${name}.${key} is a valid number: ${configValue}`);
|
|
||||||
|
|
||||||
return configValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
validateInteger(configValue, rules, fieldSchema, name, key) {
|
|
||||||
if (typeof configValue !== "number" || !Number.isInteger(configValue)) {
|
|
||||||
const parsedValue = parseInt(configValue, 10);
|
|
||||||
if (!isNaN(parsedValue) && Number.isInteger(parsedValue)) {
|
|
||||||
this.logger.warn(`${name}.${key} was parsed to an integer: ${configValue} -> ${parsedValue}`);
|
|
||||||
configValue = parsedValue;
|
|
||||||
} else {
|
|
||||||
this.logger.warn(`${name}.${key} is not a valid integer. Using default value.`);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rules.min !== undefined && configValue < rules.min) {
|
|
||||||
this.logger.warn(`${name}.${key} is below the minimum integer value (${rules.min}). Using default value.`);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rules.max !== undefined && configValue > rules.max) {
|
|
||||||
this.logger.warn(`${name}.${key} exceeds the maximum integer value (${rules.max}). Using default value.`);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.debug(`${name}.${key} is a valid integer: ${configValue}`);
|
|
||||||
return configValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
validateBoolean(configValue, name, key) {
|
|
||||||
if (typeof configValue !== "boolean") {
|
|
||||||
if (configValue === "true" || configValue === "false") {
|
|
||||||
const parsedValue = configValue === "true";
|
|
||||||
this.logger.debug(`${name}.${key} was parsed to a boolean: ${configValue} -> ${parsedValue}`);
|
|
||||||
configValue = parsedValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return configValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
validateString(configValue, rules, fieldSchema, name, key) {
|
|
||||||
let newConfigValue = configValue;
|
|
||||||
|
|
||||||
if (typeof configValue !== "string") {
|
|
||||||
//check if the value is nullable
|
|
||||||
if(rules.nullable){
|
|
||||||
if(configValue === null){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.warn(`${name}.${key} is not a string. Trying to convert to string.`);
|
|
||||||
newConfigValue = String(configValue); // Coerce to string if not already
|
|
||||||
}
|
|
||||||
|
|
||||||
//check if the string is a valid string after conversion
|
|
||||||
if (typeof newConfigValue !== "string") {
|
|
||||||
this.logger.warn(`${name}.${key} is not a valid string. Using default value.`);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newConfigValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
validateSet(configValue, rules, fieldSchema, name, key) {
|
|
||||||
// 1. Ensure we have a Set. If not, use default.
|
|
||||||
if (!(configValue instanceof Set)) {
|
|
||||||
this.logger.info(`${name}.${key} is not a Set. Converting to one using default value.`);
|
|
||||||
return new Set(fieldSchema.default);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Convert the Set to an array for easier filtering.
|
|
||||||
const validatedArray = [...configValue]
|
|
||||||
.filter((item) => {
|
|
||||||
// 3. Filter based on `rules.itemType`.
|
|
||||||
switch (rules.itemType) {
|
|
||||||
case "number":
|
|
||||||
return typeof item === "number";
|
|
||||||
case "string":
|
|
||||||
return typeof item === "string";
|
|
||||||
case "null":
|
|
||||||
// "null" might mean no type restriction (your usage may vary).
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
// Fallback if itemType is something else
|
|
||||||
return typeof item === rules.itemType;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.slice(0, rules.maxLength || Infinity);
|
|
||||||
|
|
||||||
// 4. Check if the filtered array meets the minimum length.
|
|
||||||
if (validatedArray.length < (rules.minLength || 1)) {
|
|
||||||
this.logger.warn(
|
|
||||||
`${name}.${key} contains fewer items than allowed (${rules.minLength}). Using default value.`
|
|
||||||
);
|
|
||||||
return new Set(fieldSchema.default);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Return a new Set containing only the valid items.
|
|
||||||
return new Set(validatedArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
validateArray(configValue, rules, fieldSchema, name, key) {
|
|
||||||
if (!Array.isArray(configValue)) {
|
|
||||||
this.logger.info(`${name}.${key} is not an array. Using default value.`);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate individual items in the array
|
|
||||||
const validatedArray = configValue
|
|
||||||
.filter((item) => {
|
|
||||||
switch (rules.itemType) {
|
|
||||||
case "number":
|
|
||||||
return typeof item === "number";
|
|
||||||
case "string":
|
|
||||||
return typeof item === "string";
|
|
||||||
case "null":
|
|
||||||
// anything goes
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return typeof item === rules.itemType;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.slice(0, rules.maxLength || Infinity);
|
|
||||||
|
|
||||||
if (validatedArray.length < (rules.minLength || 1)) {
|
|
||||||
this.logger.warn(
|
|
||||||
`${name}.${key} contains fewer items than allowed (${rules.minLength}). Using default value.`
|
|
||||||
);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
return validatedArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
validateObject(configValue, rules, fieldSchema, name, key) {
|
|
||||||
if (typeof configValue !== "object" || Array.isArray(configValue)) {
|
|
||||||
this.logger.warn(`${name}.${key} is not a valid object. Using default value.`);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rules.schema) {
|
|
||||||
// Recursively validate nested objects if a schema is defined
|
|
||||||
return this.validateSchema(configValue || {}, rules.schema, `${name}.${key}`);
|
|
||||||
} else {
|
|
||||||
// If no schema is defined, log a warning and use the default
|
|
||||||
this.logger.warn(`${name}.${key} is an object with no schema. Using default value.`);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validateEnum(configValue, rules, fieldSchema, name, key) {
|
|
||||||
|
|
||||||
if (Array.isArray(rules.values)) {
|
|
||||||
|
|
||||||
//if value is null take default
|
|
||||||
if(configValue === null){
|
|
||||||
this.logger.warn(`${name}.${key} is null. Using default value.`);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
|
|
||||||
const validValues = rules.values.map(e => e.value.toLowerCase());
|
|
||||||
|
|
||||||
//remove caps
|
|
||||||
configValue = configValue.toLowerCase();
|
|
||||||
|
|
||||||
if (!validValues.includes(configValue)) {
|
|
||||||
this.logger.warn(
|
|
||||||
`${name}.${key} has an invalid value : ${configValue}. Allowed values: [${validValues.join(", ")}]. Using default value.`
|
|
||||||
);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.logger.warn(
|
|
||||||
`${name}.${key} is an enum with no 'values' array. Using default value.`
|
|
||||||
);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
return configValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
validateUndefined(configValue, fieldSchema, name, key) {
|
|
||||||
if (typeof configValue === "object" && !Array.isArray(configValue)) {
|
|
||||||
|
|
||||||
this.logger.debug(`${name}.${key} has no defined rules but is an object going 1 level deeper.`);
|
|
||||||
|
|
||||||
// Recursively validate the nested object
|
|
||||||
return this.validateSchema( configValue || {}, fieldSchema, `${name}.${key}`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.logger.warn(`${name}.${key} has no defined rules. Using default value.`);
|
|
||||||
return fieldSchema.default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ValidationUtils;
|
|
||||||
8
index.js
8
index.js
@@ -7,13 +7,15 @@
|
|||||||
|
|
||||||
// Core helper modules
|
// Core helper modules
|
||||||
const menuUtils = require('./src/helper/menuUtils.js');
|
const menuUtils = require('./src/helper/menuUtils.js');
|
||||||
|
const outputUtils = require('./src/helper/outputUtils.js');
|
||||||
const logger = require('./src/helper/logger.js');
|
const logger = require('./src/helper/logger.js');
|
||||||
const validation = require('./src/helper/validationUtils.js');
|
const validation = require('./src/helper/validationUtils.js');
|
||||||
|
const configUtils = require('./src/helper/configUtils.js');
|
||||||
|
|
||||||
// Domain-specific modules
|
// Domain-specific modules
|
||||||
const measurements = require('./src/measurements/index.js');
|
const measurements = require('./src/measurements/index.js');
|
||||||
const nrmse = require('./src/nrmse/index.js');
|
const nrmse = require('./src/nrmse/ErrorMetrics.js');
|
||||||
const state = require('./src/state/index.js');
|
const state = require('./src/state/state.js');
|
||||||
|
|
||||||
// Configuration loader with error handling
|
// Configuration loader with error handling
|
||||||
function loadConfig(path) {
|
function loadConfig(path) {
|
||||||
@@ -59,6 +61,8 @@ function loadAssetDatasets() {
|
|||||||
// Export everything
|
// Export everything
|
||||||
module.exports = {
|
module.exports = {
|
||||||
menuUtils,
|
menuUtils,
|
||||||
|
outputUtils,
|
||||||
|
configUtils,
|
||||||
logger,
|
logger,
|
||||||
validation,
|
validation,
|
||||||
measurements,
|
measurements,
|
||||||
|
|||||||
49
src/convert/definitions/angle.js
Normal file
49
src/convert/definitions/angle.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
var angle;
|
||||||
|
|
||||||
|
angle = {
|
||||||
|
rad: {
|
||||||
|
name: {
|
||||||
|
singular: 'radian'
|
||||||
|
, plural: 'radians'
|
||||||
|
}
|
||||||
|
, to_anchor: 180/Math.PI
|
||||||
|
}
|
||||||
|
, deg: {
|
||||||
|
name: {
|
||||||
|
singular: 'degree'
|
||||||
|
, plural: 'degrees'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, grad: {
|
||||||
|
name: {
|
||||||
|
singular: 'gradian'
|
||||||
|
, plural: 'gradians'
|
||||||
|
}
|
||||||
|
, to_anchor: 9/10
|
||||||
|
}
|
||||||
|
, arcmin: {
|
||||||
|
name: {
|
||||||
|
singular: 'arcminute'
|
||||||
|
, plural: 'arcminutes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/60
|
||||||
|
}
|
||||||
|
, arcsec: {
|
||||||
|
name: {
|
||||||
|
singular: 'arcsecond'
|
||||||
|
, plural: 'arcseconds'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/3600
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: angle
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'deg'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
49
src/convert/definitions/apparentPower.js
Normal file
49
src/convert/definitions/apparentPower.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
var apparentPower;
|
||||||
|
|
||||||
|
apparentPower = {
|
||||||
|
VA: {
|
||||||
|
name: {
|
||||||
|
singular: 'Volt-Ampere'
|
||||||
|
, plural: 'Volt-Amperes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, mVA: {
|
||||||
|
name: {
|
||||||
|
singular: 'Millivolt-Ampere'
|
||||||
|
, plural: 'Millivolt-Amperes'
|
||||||
|
}
|
||||||
|
, to_anchor: .001
|
||||||
|
}
|
||||||
|
, kVA: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilovolt-Ampere'
|
||||||
|
, plural: 'Kilovolt-Amperes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, MVA: {
|
||||||
|
name: {
|
||||||
|
singular: 'Megavolt-Ampere'
|
||||||
|
, plural: 'Megavolt-Amperes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000
|
||||||
|
}
|
||||||
|
, GVA: {
|
||||||
|
name: {
|
||||||
|
singular: 'Gigavolt-Ampere'
|
||||||
|
, plural: 'Gigavolt-Amperes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: apparentPower
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'VA'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
93
src/convert/definitions/area.js
Normal file
93
src/convert/definitions/area.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
var metric
|
||||||
|
, imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
mm2: {
|
||||||
|
name: {
|
||||||
|
singular: 'Square Millimeter'
|
||||||
|
, plural: 'Square Millimeters'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000000
|
||||||
|
}
|
||||||
|
, cm2: {
|
||||||
|
name: {
|
||||||
|
singular: 'Centimeter'
|
||||||
|
, plural: 'Centimeters'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/10000
|
||||||
|
}
|
||||||
|
, m2: {
|
||||||
|
name: {
|
||||||
|
singular: 'Square Meter'
|
||||||
|
, plural: 'Square Meters'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, ha: {
|
||||||
|
name: {
|
||||||
|
singular: 'Hectare'
|
||||||
|
, plural: 'Hectares'
|
||||||
|
}
|
||||||
|
, to_anchor: 10000
|
||||||
|
}
|
||||||
|
, km2: {
|
||||||
|
name: {
|
||||||
|
singular: 'Square Kilometer'
|
||||||
|
, plural: 'Square Kilometers'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
imperial = {
|
||||||
|
'in2': {
|
||||||
|
name: {
|
||||||
|
singular: 'Square Inch'
|
||||||
|
, plural: 'Square Inches'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/144
|
||||||
|
}
|
||||||
|
, yd2: {
|
||||||
|
name: {
|
||||||
|
singular: 'Square Yard'
|
||||||
|
, plural: 'Square Yards'
|
||||||
|
}
|
||||||
|
, to_anchor: 9
|
||||||
|
}
|
||||||
|
, ft2: {
|
||||||
|
name: {
|
||||||
|
singular: 'Square Foot'
|
||||||
|
, plural: 'Square Feet'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, ac: {
|
||||||
|
name: {
|
||||||
|
singular: 'Acre'
|
||||||
|
, plural: 'Acres'
|
||||||
|
}
|
||||||
|
, to_anchor: 43560
|
||||||
|
}
|
||||||
|
, mi2: {
|
||||||
|
name: {
|
||||||
|
singular: 'Square Mile'
|
||||||
|
, plural: 'Square Miles'
|
||||||
|
}
|
||||||
|
, to_anchor: 27878400
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric
|
||||||
|
, imperial: imperial
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'm2'
|
||||||
|
, ratio: 10.7639
|
||||||
|
}
|
||||||
|
, imperial: {
|
||||||
|
unit: 'ft2'
|
||||||
|
, ratio: 1/10.7639
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
35
src/convert/definitions/current.js
Normal file
35
src/convert/definitions/current.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
var current;
|
||||||
|
|
||||||
|
current = {
|
||||||
|
A: {
|
||||||
|
name: {
|
||||||
|
singular: 'Ampere'
|
||||||
|
, plural: 'Amperes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, mA: {
|
||||||
|
name: {
|
||||||
|
singular: 'Milliampere'
|
||||||
|
, plural: 'Milliamperes'
|
||||||
|
}
|
||||||
|
, to_anchor: .001
|
||||||
|
}
|
||||||
|
, kA: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kiloampere'
|
||||||
|
, plural: 'Kiloamperes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: current
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'A'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
93
src/convert/definitions/digital.js
Normal file
93
src/convert/definitions/digital.js
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
var bits
|
||||||
|
, bytes;
|
||||||
|
|
||||||
|
bits = {
|
||||||
|
b: {
|
||||||
|
name: {
|
||||||
|
singular: 'Bit'
|
||||||
|
, plural: 'Bits'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, Kb: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilobit'
|
||||||
|
, plural: 'Kilobits'
|
||||||
|
}
|
||||||
|
, to_anchor: 1024
|
||||||
|
}
|
||||||
|
, Mb: {
|
||||||
|
name: {
|
||||||
|
singular: 'Megabit'
|
||||||
|
, plural: 'Megabits'
|
||||||
|
}
|
||||||
|
, to_anchor: 1048576
|
||||||
|
}
|
||||||
|
, Gb: {
|
||||||
|
name: {
|
||||||
|
singular: 'Gigabit'
|
||||||
|
, plural: 'Gigabits'
|
||||||
|
}
|
||||||
|
, to_anchor: 1073741824
|
||||||
|
}
|
||||||
|
, Tb: {
|
||||||
|
name: {
|
||||||
|
singular: 'Terabit'
|
||||||
|
, plural: 'Terabits'
|
||||||
|
}
|
||||||
|
, to_anchor: 1099511627776
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bytes = {
|
||||||
|
B: {
|
||||||
|
name: {
|
||||||
|
singular: 'Byte'
|
||||||
|
, plural: 'Bytes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, KB: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilobyte'
|
||||||
|
, plural: 'Kilobytes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1024
|
||||||
|
}
|
||||||
|
, MB: {
|
||||||
|
name: {
|
||||||
|
singular: 'Megabyte'
|
||||||
|
, plural: 'Megabytes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1048576
|
||||||
|
}
|
||||||
|
, GB: {
|
||||||
|
name: {
|
||||||
|
singular: 'Gigabyte'
|
||||||
|
, plural: 'Gigabytes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1073741824
|
||||||
|
}
|
||||||
|
, TB: {
|
||||||
|
name: {
|
||||||
|
singular: 'Terabyte'
|
||||||
|
, plural: 'Terabytes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1099511627776
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
bits: bits
|
||||||
|
, bytes: bytes
|
||||||
|
, _anchors: {
|
||||||
|
bits: {
|
||||||
|
unit: 'b'
|
||||||
|
, ratio: 1/8
|
||||||
|
}
|
||||||
|
, bytes: {
|
||||||
|
unit: 'B'
|
||||||
|
, ratio: 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
30
src/convert/definitions/each.js
Normal file
30
src/convert/definitions/each.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
var metric
|
||||||
|
, imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
ea: {
|
||||||
|
name: {
|
||||||
|
singular: 'Each'
|
||||||
|
, plural: 'Each'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
},
|
||||||
|
dz: {
|
||||||
|
name: {
|
||||||
|
singular: 'Dozen'
|
||||||
|
, plural: 'Dozens'
|
||||||
|
}
|
||||||
|
, to_anchor: 12
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric
|
||||||
|
, imperial: {}
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'ea'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
63
src/convert/definitions/energy.js
Normal file
63
src/convert/definitions/energy.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
var energy;
|
||||||
|
|
||||||
|
energy = {
|
||||||
|
Wh: {
|
||||||
|
name: {
|
||||||
|
singular: 'Watt-hour'
|
||||||
|
, plural: 'Watt-hours'
|
||||||
|
}
|
||||||
|
, to_anchor: 3600
|
||||||
|
}
|
||||||
|
, mWh: {
|
||||||
|
name: {
|
||||||
|
singular: 'Milliwatt-hour'
|
||||||
|
, plural: 'Milliwatt-hours'
|
||||||
|
}
|
||||||
|
, to_anchor: 3.6
|
||||||
|
}
|
||||||
|
, kWh: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilowatt-hour'
|
||||||
|
, plural: 'Kilowatt-hours'
|
||||||
|
}
|
||||||
|
, to_anchor: 3600000
|
||||||
|
}
|
||||||
|
, MWh: {
|
||||||
|
name: {
|
||||||
|
singular: 'Megawatt-hour'
|
||||||
|
, plural: 'Megawatt-hours'
|
||||||
|
}
|
||||||
|
, to_anchor: 3600000000
|
||||||
|
}
|
||||||
|
, GWh: {
|
||||||
|
name: {
|
||||||
|
singular: 'Gigawatt-hour'
|
||||||
|
, plural: 'Gigawatt-hours'
|
||||||
|
}
|
||||||
|
, to_anchor: 3600000000000
|
||||||
|
}
|
||||||
|
, J: {
|
||||||
|
name: {
|
||||||
|
singular: 'Joule'
|
||||||
|
, plural: 'Joules'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, kJ: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilojoule'
|
||||||
|
, plural: 'Kilojoules'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: energy
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'J'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
78
src/convert/definitions/frequency.js
Normal file
78
src/convert/definitions/frequency.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
var frequency;
|
||||||
|
|
||||||
|
frequency = {
|
||||||
|
mHz: {
|
||||||
|
name: {
|
||||||
|
singular: 'millihertz'
|
||||||
|
, plural: 'millihertz'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000
|
||||||
|
}
|
||||||
|
, Hz: {
|
||||||
|
name: {
|
||||||
|
singular: 'hertz'
|
||||||
|
, plural: 'hertz'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, kHz: {
|
||||||
|
name: {
|
||||||
|
singular: 'kilohertz'
|
||||||
|
, plural: 'kilohertz'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, MHz: {
|
||||||
|
name: {
|
||||||
|
singular: 'megahertz'
|
||||||
|
, plural: 'megahertz'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000 * 1000
|
||||||
|
}
|
||||||
|
, GHz: {
|
||||||
|
name: {
|
||||||
|
singular: 'gigahertz'
|
||||||
|
, plural: 'gigahertz'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000 * 1000 * 1000
|
||||||
|
}
|
||||||
|
, THz: {
|
||||||
|
name: {
|
||||||
|
singular: 'terahertz'
|
||||||
|
, plural: 'terahertz'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000 * 1000 * 1000 * 1000
|
||||||
|
}
|
||||||
|
, rpm: {
|
||||||
|
name: {
|
||||||
|
singular: 'rotation per minute'
|
||||||
|
, plural: 'rotations per minute'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/60
|
||||||
|
}
|
||||||
|
, "deg/s": {
|
||||||
|
name: {
|
||||||
|
singular: 'degree per second'
|
||||||
|
, plural: 'degrees per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/360
|
||||||
|
}
|
||||||
|
, "rad/s": {
|
||||||
|
name: {
|
||||||
|
singular: 'radian per second'
|
||||||
|
, plural: 'radians per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/(Math.PI * 2)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: frequency
|
||||||
|
, _anchors: {
|
||||||
|
frequency: {
|
||||||
|
unit: 'hz'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
37
src/convert/definitions/illuminance.js
Normal file
37
src/convert/definitions/illuminance.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
var metric,
|
||||||
|
imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
'lx': {
|
||||||
|
name: {
|
||||||
|
singular: 'Lux',
|
||||||
|
plural: 'Lux'
|
||||||
|
},
|
||||||
|
to_anchor: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
imperial = {
|
||||||
|
'ft-cd': {
|
||||||
|
name: {
|
||||||
|
singular: 'Foot-candle',
|
||||||
|
plural: 'Foot-candles'
|
||||||
|
},
|
||||||
|
to_anchor: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric,
|
||||||
|
imperial: imperial,
|
||||||
|
_anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'lx',
|
||||||
|
ratio: 1/10.76391
|
||||||
|
},
|
||||||
|
imperial: {
|
||||||
|
unit: 'ft-cd',
|
||||||
|
ratio: 10.76391
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
86
src/convert/definitions/length.js
Normal file
86
src/convert/definitions/length.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
var metric,
|
||||||
|
imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
mm: {
|
||||||
|
name: {
|
||||||
|
singular: 'Millimeter',
|
||||||
|
plural: 'Millimeters'
|
||||||
|
},
|
||||||
|
to_anchor: 1/1000
|
||||||
|
},
|
||||||
|
cm: {
|
||||||
|
name: {
|
||||||
|
singular: 'Centimeter',
|
||||||
|
plural: 'Centimeters'
|
||||||
|
},
|
||||||
|
to_anchor: 1/100
|
||||||
|
},
|
||||||
|
m: {
|
||||||
|
name: {
|
||||||
|
singular: 'Meter',
|
||||||
|
plural: 'Meters'
|
||||||
|
},
|
||||||
|
to_anchor: 1
|
||||||
|
},
|
||||||
|
km: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilometer',
|
||||||
|
plural: 'Kilometers'
|
||||||
|
},
|
||||||
|
to_anchor: 1000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
imperial = {
|
||||||
|
'in': {
|
||||||
|
name: {
|
||||||
|
singular: 'Inch',
|
||||||
|
plural: 'Inches'
|
||||||
|
},
|
||||||
|
to_anchor: 1/12
|
||||||
|
},
|
||||||
|
yd: {
|
||||||
|
name: {
|
||||||
|
singular: 'Yard',
|
||||||
|
plural: 'Yards'
|
||||||
|
},
|
||||||
|
to_anchor: 3
|
||||||
|
},
|
||||||
|
'ft-us': {
|
||||||
|
name: {
|
||||||
|
singular: 'US Survey Foot',
|
||||||
|
plural: 'US Survey Feet'
|
||||||
|
},
|
||||||
|
to_anchor: 1.000002
|
||||||
|
},
|
||||||
|
ft: {
|
||||||
|
name: {
|
||||||
|
singular: 'Foot',
|
||||||
|
plural: 'Feet'
|
||||||
|
},
|
||||||
|
to_anchor: 1
|
||||||
|
},
|
||||||
|
mi: {
|
||||||
|
name: {
|
||||||
|
singular: 'Mile',
|
||||||
|
plural: 'Miles'
|
||||||
|
},
|
||||||
|
to_anchor: 5280
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric,
|
||||||
|
imperial: imperial,
|
||||||
|
_anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'm',
|
||||||
|
ratio: 3.28084
|
||||||
|
},
|
||||||
|
imperial: {
|
||||||
|
unit: 'ft',
|
||||||
|
ratio: 1/3.28084
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
78
src/convert/definitions/mass.js
Normal file
78
src/convert/definitions/mass.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
var metric
|
||||||
|
, imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
mcg: {
|
||||||
|
name: {
|
||||||
|
singular: 'Microgram'
|
||||||
|
, plural: 'Micrograms'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000000
|
||||||
|
}
|
||||||
|
, mg: {
|
||||||
|
name: {
|
||||||
|
singular: 'Milligram'
|
||||||
|
, plural: 'Milligrams'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000
|
||||||
|
}
|
||||||
|
, g: {
|
||||||
|
name: {
|
||||||
|
singular: 'Gram'
|
||||||
|
, plural: 'Grams'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, kg: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilogram'
|
||||||
|
, plural: 'Kilograms'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, mt: {
|
||||||
|
name: {
|
||||||
|
singular: 'Metric Tonne'
|
||||||
|
, plural: 'Metric Tonnes'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
imperial = {
|
||||||
|
oz: {
|
||||||
|
name: {
|
||||||
|
singular: 'Ounce'
|
||||||
|
, plural: 'Ounces'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/16
|
||||||
|
}
|
||||||
|
, lb: {
|
||||||
|
name: {
|
||||||
|
singular: 'Pound'
|
||||||
|
, plural: 'Pounds'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}, t: {
|
||||||
|
name: {
|
||||||
|
singular: 'Ton',
|
||||||
|
plural: 'Tons',
|
||||||
|
},
|
||||||
|
to_anchor: 2000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric
|
||||||
|
, imperial: imperial
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'g'
|
||||||
|
, ratio: 1/453.592
|
||||||
|
}
|
||||||
|
, imperial: {
|
||||||
|
unit: 'lb'
|
||||||
|
, ratio: 453.592
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
51
src/convert/definitions/pace.js
Normal file
51
src/convert/definitions/pace.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
var metric
|
||||||
|
, imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
'min/km': {
|
||||||
|
name: {
|
||||||
|
singular: 'Minute per kilometre'
|
||||||
|
, plural: 'Minutes per kilometre'
|
||||||
|
}
|
||||||
|
, to_anchor: 0.06
|
||||||
|
}
|
||||||
|
,'s/m': {
|
||||||
|
name: {
|
||||||
|
singular: 'Second per metre'
|
||||||
|
, plural: 'Seconds per metre'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imperial = {
|
||||||
|
'min/mi': {
|
||||||
|
name: {
|
||||||
|
singular: 'Minute per mile'
|
||||||
|
, plural: 'Minutes per mile'
|
||||||
|
}
|
||||||
|
, to_anchor: 0.0113636
|
||||||
|
}
|
||||||
|
, 's/ft': {
|
||||||
|
name: {
|
||||||
|
singular: 'Second per foot'
|
||||||
|
, plural: 'Seconds per foot'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric
|
||||||
|
, imperial: imperial
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 's/m'
|
||||||
|
, ratio: 0.3048
|
||||||
|
}
|
||||||
|
, imperial: {
|
||||||
|
unit: 's/ft'
|
||||||
|
, ratio: 1/0.3048
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
44
src/convert/definitions/partsPer.js
Normal file
44
src/convert/definitions/partsPer.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
var metric
|
||||||
|
, imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
ppm: {
|
||||||
|
name: {
|
||||||
|
singular: 'Part-per Million'
|
||||||
|
, plural: 'Parts-per Million'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, ppb: {
|
||||||
|
name: {
|
||||||
|
singular: 'Part-per Billion'
|
||||||
|
, plural: 'Parts-per Billion'
|
||||||
|
}
|
||||||
|
, to_anchor: .001
|
||||||
|
}
|
||||||
|
, ppt: {
|
||||||
|
name: {
|
||||||
|
singular: 'Part-per Trillion'
|
||||||
|
, plural: 'Parts-per Trillion'
|
||||||
|
}
|
||||||
|
, to_anchor: .000001
|
||||||
|
}
|
||||||
|
, ppq: {
|
||||||
|
name: {
|
||||||
|
singular: 'Part-per Quadrillion'
|
||||||
|
, plural: 'Parts-per Quadrillion'
|
||||||
|
}
|
||||||
|
, to_anchor: .000000001
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric
|
||||||
|
, imperial: {}
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'ppm'
|
||||||
|
, ratio: .000001
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
49
src/convert/definitions/power.js
Normal file
49
src/convert/definitions/power.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
var power;
|
||||||
|
|
||||||
|
power = {
|
||||||
|
W: {
|
||||||
|
name: {
|
||||||
|
singular: 'Watt'
|
||||||
|
, plural: 'Watts'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, mW: {
|
||||||
|
name: {
|
||||||
|
singular: 'Milliwatt'
|
||||||
|
, plural: 'Milliwatts'
|
||||||
|
}
|
||||||
|
, to_anchor: .001
|
||||||
|
}
|
||||||
|
, kW: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilowatt'
|
||||||
|
, plural: 'Kilowatts'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, MW: {
|
||||||
|
name: {
|
||||||
|
singular: 'Megawatt'
|
||||||
|
, plural: 'Megawatts'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000
|
||||||
|
}
|
||||||
|
, GW: {
|
||||||
|
name: {
|
||||||
|
singular: 'Gigawatt'
|
||||||
|
, plural: 'Gigawatts'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: power
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'W'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
86
src/convert/definitions/pressure.js
Normal file
86
src/convert/definitions/pressure.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
var metric
|
||||||
|
, imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
Pa: {
|
||||||
|
name: {
|
||||||
|
singular: 'pascal'
|
||||||
|
, plural: 'pascals'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000
|
||||||
|
}
|
||||||
|
, kPa: {
|
||||||
|
name: {
|
||||||
|
singular: 'kilopascal'
|
||||||
|
, plural: 'kilopascals'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, MPa: {
|
||||||
|
name: {
|
||||||
|
singular: 'megapascal'
|
||||||
|
, plural: 'megapascals'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, hPa: {
|
||||||
|
name: {
|
||||||
|
singular: 'hectopascal'
|
||||||
|
, plural: 'hectopascals'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/10
|
||||||
|
}
|
||||||
|
, bar: {
|
||||||
|
name: {
|
||||||
|
singular: 'bar'
|
||||||
|
, plural: 'bar'
|
||||||
|
}
|
||||||
|
, to_anchor: 100
|
||||||
|
}
|
||||||
|
, mbar: {
|
||||||
|
name: {
|
||||||
|
singular: 'mbar'
|
||||||
|
, plural: 'mbar'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/10
|
||||||
|
}
|
||||||
|
, torr: {
|
||||||
|
name: {
|
||||||
|
singular: 'torr'
|
||||||
|
, plural: 'torr'
|
||||||
|
}
|
||||||
|
, to_anchor: 101325/760000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
imperial = {
|
||||||
|
psi: {
|
||||||
|
name: {
|
||||||
|
singular: 'pound per square inch'
|
||||||
|
, plural: 'pounds per square inch'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000
|
||||||
|
}
|
||||||
|
, ksi: {
|
||||||
|
name: {
|
||||||
|
singular: 'kilopound per square inch'
|
||||||
|
, plural: 'kilopound per square inch'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric
|
||||||
|
, imperial: imperial
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'kPa'
|
||||||
|
, ratio: 0.00014503768078
|
||||||
|
}
|
||||||
|
, imperial: {
|
||||||
|
unit: 'psi'
|
||||||
|
, ratio: 1/0.00014503768078
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
49
src/convert/definitions/reactiveEnergy.js
Normal file
49
src/convert/definitions/reactiveEnergy.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
var reactiveEnergy;
|
||||||
|
|
||||||
|
reactiveEnergy = {
|
||||||
|
VARh: {
|
||||||
|
name: {
|
||||||
|
singular: 'Volt-Ampere Reactive Hour'
|
||||||
|
, plural: 'Volt-Amperes Reactive Hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, mVARh: {
|
||||||
|
name: {
|
||||||
|
singular: 'Millivolt-Ampere Reactive Hour'
|
||||||
|
, plural: 'Millivolt-Amperes Reactive Hour'
|
||||||
|
}
|
||||||
|
, to_anchor: .001
|
||||||
|
}
|
||||||
|
, kVARh: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilovolt-Ampere Reactive Hour'
|
||||||
|
, plural: 'Kilovolt-Amperes Reactive Hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, MVARh: {
|
||||||
|
name: {
|
||||||
|
singular: 'Megavolt-Ampere Reactive Hour'
|
||||||
|
, plural: 'Megavolt-Amperes Reactive Hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000
|
||||||
|
}
|
||||||
|
, GVARh: {
|
||||||
|
name: {
|
||||||
|
singular: 'Gigavolt-Ampere Reactive Hour'
|
||||||
|
, plural: 'Gigavolt-Amperes Reactive Hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: reactiveEnergy
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'VARh'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
49
src/convert/definitions/reactivePower.js
Normal file
49
src/convert/definitions/reactivePower.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
var reactivePower;
|
||||||
|
|
||||||
|
reactivePower = {
|
||||||
|
VAR: {
|
||||||
|
name: {
|
||||||
|
singular: 'Volt-Ampere Reactive'
|
||||||
|
, plural: 'Volt-Amperes Reactive'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, mVAR: {
|
||||||
|
name: {
|
||||||
|
singular: 'Millivolt-Ampere Reactive'
|
||||||
|
, plural: 'Millivolt-Amperes Reactive'
|
||||||
|
}
|
||||||
|
, to_anchor: .001
|
||||||
|
}
|
||||||
|
, kVAR: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilovolt-Ampere Reactive'
|
||||||
|
, plural: 'Kilovolt-Amperes Reactive'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, MVAR: {
|
||||||
|
name: {
|
||||||
|
singular: 'Megavolt-Ampere Reactive'
|
||||||
|
, plural: 'Megavolt-Amperes Reactive'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000
|
||||||
|
}
|
||||||
|
, GVAR: {
|
||||||
|
name: {
|
||||||
|
singular: 'Gigavolt-Ampere Reactive'
|
||||||
|
, plural: 'Gigavolt-Amperes Reactive'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: reactivePower
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'VAR'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
58
src/convert/definitions/speed.js
Normal file
58
src/convert/definitions/speed.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
var metric
|
||||||
|
, imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
'm/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Metre per second'
|
||||||
|
, plural: 'Metres per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 3.6
|
||||||
|
}
|
||||||
|
, 'km/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilometre per hour'
|
||||||
|
, plural: 'Kilometres per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imperial = {
|
||||||
|
'm/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Mile per hour'
|
||||||
|
, plural: 'Miles per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, knot: {
|
||||||
|
name: {
|
||||||
|
singular: 'Knot'
|
||||||
|
, plural: 'Knots'
|
||||||
|
}
|
||||||
|
, to_anchor: 1.150779
|
||||||
|
}
|
||||||
|
, 'ft/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Foot per second'
|
||||||
|
, plural: 'Feet per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 0.681818
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric
|
||||||
|
, imperial: imperial
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'km/h'
|
||||||
|
, ratio: 1/1.609344
|
||||||
|
}
|
||||||
|
, imperial: {
|
||||||
|
unit: 'm/h'
|
||||||
|
, ratio: 1.609344
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
55
src/convert/definitions/temperature.js
Normal file
55
src/convert/definitions/temperature.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
var metric
|
||||||
|
, imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
C: {
|
||||||
|
name: {
|
||||||
|
singular: 'degree Celsius'
|
||||||
|
, plural: 'degrees Celsius'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
, anchor_shift: 0
|
||||||
|
},
|
||||||
|
K: {
|
||||||
|
name: {
|
||||||
|
singular: 'degree Kelvin'
|
||||||
|
, plural: 'degrees Kelvin'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
, anchor_shift: 273.15
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
imperial = {
|
||||||
|
F: {
|
||||||
|
name: {
|
||||||
|
singular: 'degree Fahrenheit'
|
||||||
|
, plural: 'degrees Fahrenheit'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
},
|
||||||
|
R: {
|
||||||
|
name: {
|
||||||
|
singular: 'degree Rankine'
|
||||||
|
, plural: 'degrees Rankine'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
, anchor_shift: 459.67
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric
|
||||||
|
, imperial: imperial
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'C'
|
||||||
|
, transform: function (C) { return C / (5/9) + 32 }
|
||||||
|
}
|
||||||
|
, imperial: {
|
||||||
|
unit: 'F'
|
||||||
|
, transform: function (F) { return (F - 32) * (5/9) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
86
src/convert/definitions/time.js
Normal file
86
src/convert/definitions/time.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
var time;
|
||||||
|
var daysInYear = 365.25;
|
||||||
|
|
||||||
|
time = {
|
||||||
|
ns: {
|
||||||
|
name: {
|
||||||
|
singular: 'Nanosecond'
|
||||||
|
, plural: 'Nanoseconds'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000000000
|
||||||
|
}
|
||||||
|
, mu: {
|
||||||
|
name: {
|
||||||
|
singular: 'Microsecond'
|
||||||
|
, plural: 'Microseconds'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000000
|
||||||
|
}
|
||||||
|
, ms: {
|
||||||
|
name: {
|
||||||
|
singular: 'Millisecond'
|
||||||
|
, plural: 'Milliseconds'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000
|
||||||
|
}
|
||||||
|
, s: {
|
||||||
|
name: {
|
||||||
|
singular: 'Second'
|
||||||
|
, plural: 'Seconds'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, min: {
|
||||||
|
name: {
|
||||||
|
singular: 'Minute'
|
||||||
|
, plural: 'Minutes'
|
||||||
|
}
|
||||||
|
, to_anchor: 60
|
||||||
|
}
|
||||||
|
, h: {
|
||||||
|
name: {
|
||||||
|
singular: 'Hour'
|
||||||
|
, plural: 'Hours'
|
||||||
|
}
|
||||||
|
, to_anchor: 60 * 60
|
||||||
|
}
|
||||||
|
, d: {
|
||||||
|
name: {
|
||||||
|
singular: 'Day'
|
||||||
|
, plural: 'Days'
|
||||||
|
}
|
||||||
|
, to_anchor: 60 * 60 * 24
|
||||||
|
}
|
||||||
|
, week: {
|
||||||
|
name: {
|
||||||
|
singular: 'Week'
|
||||||
|
, plural: 'Weeks'
|
||||||
|
}
|
||||||
|
, to_anchor: 60 * 60 * 24 * 7
|
||||||
|
}
|
||||||
|
, month: {
|
||||||
|
name: {
|
||||||
|
singular: 'Month'
|
||||||
|
, plural: 'Months'
|
||||||
|
}
|
||||||
|
, to_anchor: 60 * 60 * 24 * daysInYear / 12
|
||||||
|
}
|
||||||
|
, year: {
|
||||||
|
name: {
|
||||||
|
singular: 'Year'
|
||||||
|
, plural: 'Years'
|
||||||
|
}
|
||||||
|
, to_anchor: 60 * 60 * 24 * daysInYear
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: time
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 's'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
35
src/convert/definitions/voltage.js
Normal file
35
src/convert/definitions/voltage.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
var voltage;
|
||||||
|
|
||||||
|
voltage = {
|
||||||
|
V: {
|
||||||
|
name: {
|
||||||
|
singular: 'Volt'
|
||||||
|
, plural: 'Volts'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, mV: {
|
||||||
|
name: {
|
||||||
|
singular: 'Millivolt'
|
||||||
|
, plural: 'Millivolts'
|
||||||
|
}
|
||||||
|
, to_anchor: .001
|
||||||
|
}
|
||||||
|
, kV: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilovolt'
|
||||||
|
, plural: 'Kilovolts'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: voltage
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'V'
|
||||||
|
, ratio: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
200
src/convert/definitions/volume.js
Normal file
200
src/convert/definitions/volume.js
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
var metric
|
||||||
|
, imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
mm3: {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic Millimeter'
|
||||||
|
, plural: 'Cubic Millimeters'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000000
|
||||||
|
}
|
||||||
|
, cm3: {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic Centimeter'
|
||||||
|
, plural: 'Cubic Centimeters'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000
|
||||||
|
}
|
||||||
|
, ml: {
|
||||||
|
name: {
|
||||||
|
singular: 'Millilitre'
|
||||||
|
, plural: 'Millilitres'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000
|
||||||
|
}
|
||||||
|
, cl: {
|
||||||
|
name: {
|
||||||
|
singular: 'Centilitre'
|
||||||
|
, plural: 'Centilitres'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/100
|
||||||
|
}
|
||||||
|
, dl: {
|
||||||
|
name: {
|
||||||
|
singular: 'Decilitre'
|
||||||
|
, plural: 'Decilitres'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/10
|
||||||
|
}
|
||||||
|
, l: {
|
||||||
|
name: {
|
||||||
|
singular: 'Litre'
|
||||||
|
, plural: 'Litres'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, kl: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilolitre'
|
||||||
|
, plural: 'Kilolitres'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, m3: {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic meter'
|
||||||
|
, plural: 'Cubic meters'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, km3: {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic kilometer'
|
||||||
|
, plural: 'Cubic kilometers'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000000000
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swedish units
|
||||||
|
, krm: {
|
||||||
|
name: {
|
||||||
|
singular: 'Matsked'
|
||||||
|
, plural: 'Matskedar'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000
|
||||||
|
}
|
||||||
|
, tsk: {
|
||||||
|
name: {
|
||||||
|
singular: 'Tesked'
|
||||||
|
, plural: 'Teskedar'
|
||||||
|
}
|
||||||
|
, to_anchor: 5/1000
|
||||||
|
}
|
||||||
|
, msk: {
|
||||||
|
name: {
|
||||||
|
singular: 'Matsked'
|
||||||
|
, plural: 'Matskedar'
|
||||||
|
}
|
||||||
|
, to_anchor: 15/1000
|
||||||
|
}
|
||||||
|
, kkp: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kaffekopp'
|
||||||
|
, plural: 'Kaffekoppar'
|
||||||
|
}
|
||||||
|
, to_anchor: 150/1000
|
||||||
|
}
|
||||||
|
, glas: {
|
||||||
|
name: {
|
||||||
|
singular: 'Glas'
|
||||||
|
, plural: 'Glas'
|
||||||
|
}
|
||||||
|
, to_anchor: 200/1000
|
||||||
|
}
|
||||||
|
, kanna: {
|
||||||
|
name: {
|
||||||
|
singular: 'Kanna'
|
||||||
|
, plural: 'Kannor'
|
||||||
|
}
|
||||||
|
, to_anchor: 2.617
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
imperial = {
|
||||||
|
tsp: {
|
||||||
|
name: {
|
||||||
|
singular: 'Teaspoon'
|
||||||
|
, plural: 'Teaspoons'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/6
|
||||||
|
}
|
||||||
|
, Tbs: {
|
||||||
|
name: {
|
||||||
|
singular: 'Tablespoon'
|
||||||
|
, plural: 'Tablespoons'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/2
|
||||||
|
}
|
||||||
|
, in3: {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic inch'
|
||||||
|
, plural: 'Cubic inches'
|
||||||
|
}
|
||||||
|
, to_anchor: 0.55411
|
||||||
|
}
|
||||||
|
, 'fl-oz': {
|
||||||
|
name: {
|
||||||
|
singular: 'Fluid Ounce'
|
||||||
|
, plural: 'Fluid Ounces'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, cup: {
|
||||||
|
name: {
|
||||||
|
singular: 'Cup'
|
||||||
|
, plural: 'Cups'
|
||||||
|
}
|
||||||
|
, to_anchor: 8
|
||||||
|
}
|
||||||
|
, pnt: {
|
||||||
|
name: {
|
||||||
|
singular: 'Pint'
|
||||||
|
, plural: 'Pints'
|
||||||
|
}
|
||||||
|
, to_anchor: 16
|
||||||
|
}
|
||||||
|
, qt: {
|
||||||
|
name: {
|
||||||
|
singular: 'Quart'
|
||||||
|
, plural: 'Quarts'
|
||||||
|
}
|
||||||
|
, to_anchor: 32
|
||||||
|
}
|
||||||
|
, gal: {
|
||||||
|
name: {
|
||||||
|
singular: 'Gallon'
|
||||||
|
, plural: 'Gallons'
|
||||||
|
}
|
||||||
|
, to_anchor: 128
|
||||||
|
}
|
||||||
|
, ft3: {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic foot'
|
||||||
|
, plural: 'Cubic feet'
|
||||||
|
}
|
||||||
|
, to_anchor: 957.506
|
||||||
|
}
|
||||||
|
, yd3: {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic yard'
|
||||||
|
, plural: 'Cubic yards'
|
||||||
|
}
|
||||||
|
, to_anchor: 25852.7
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric
|
||||||
|
, imperial: imperial
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'l'
|
||||||
|
, ratio: 33.8140226
|
||||||
|
}
|
||||||
|
, imperial: {
|
||||||
|
unit: 'fl-oz'
|
||||||
|
, ratio: 1/33.8140226
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
282
src/convert/definitions/volumeFlowRate.js
Normal file
282
src/convert/definitions/volumeFlowRate.js
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
var metric
|
||||||
|
, imperial;
|
||||||
|
|
||||||
|
metric = {
|
||||||
|
'mm3/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic Millimeter per second'
|
||||||
|
, plural: 'Cubic Millimeters per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000000
|
||||||
|
}
|
||||||
|
, 'cm3/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic Centimeter per second'
|
||||||
|
, plural: 'Cubic Centimeters per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000
|
||||||
|
}
|
||||||
|
, 'ml/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Millilitre per second'
|
||||||
|
, plural: 'Millilitres per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/1000
|
||||||
|
}
|
||||||
|
, 'cl/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Centilitre per second'
|
||||||
|
, plural: 'Centilitres per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/100
|
||||||
|
}
|
||||||
|
, 'dl/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Decilitre per second'
|
||||||
|
, plural: 'Decilitres per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/10
|
||||||
|
}
|
||||||
|
, 'l/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Litre per second'
|
||||||
|
, plural: 'Litres per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, 'l/min': {
|
||||||
|
name: {
|
||||||
|
singular: 'Litre per minute'
|
||||||
|
, plural: 'Litres per minute'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/60
|
||||||
|
}
|
||||||
|
, 'l/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Litre per hour'
|
||||||
|
, plural: 'Litres per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/3600
|
||||||
|
}
|
||||||
|
, 'kl/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilolitre per second'
|
||||||
|
, plural: 'Kilolitres per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, 'kl/min': {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilolitre per minute'
|
||||||
|
, plural: 'Kilolitres per minute'
|
||||||
|
}
|
||||||
|
, to_anchor: 50/3
|
||||||
|
}
|
||||||
|
, 'kl/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Kilolitre per hour'
|
||||||
|
, plural: 'Kilolitres per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 5/18
|
||||||
|
}
|
||||||
|
, 'm3/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic meter per second'
|
||||||
|
, plural: 'Cubic meters per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000
|
||||||
|
}
|
||||||
|
, 'm3/min': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic meter per minute'
|
||||||
|
, plural: 'Cubic meters per minute'
|
||||||
|
}
|
||||||
|
, to_anchor: 50/3
|
||||||
|
}
|
||||||
|
, 'm3/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic meter per hour'
|
||||||
|
, plural: 'Cubic meters per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 5/18
|
||||||
|
}
|
||||||
|
, 'km3/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic kilometer per second'
|
||||||
|
, plural: 'Cubic kilometers per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1000000000000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
imperial = {
|
||||||
|
'tsp/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Teaspoon per second'
|
||||||
|
, plural: 'Teaspoons per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/6
|
||||||
|
}
|
||||||
|
, 'Tbs/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Tablespoon per second'
|
||||||
|
, plural: 'Tablespoons per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/2
|
||||||
|
}
|
||||||
|
, 'in3/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic inch per second'
|
||||||
|
, plural: 'Cubic inches per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 0.55411
|
||||||
|
}
|
||||||
|
, 'in3/min': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic inch per minute'
|
||||||
|
, plural: 'Cubic inches per minute'
|
||||||
|
}
|
||||||
|
, to_anchor: 0.55411/60
|
||||||
|
}
|
||||||
|
, 'in3/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic inch per hour'
|
||||||
|
, plural: 'Cubic inches per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 0.55411/3600
|
||||||
|
}
|
||||||
|
, 'fl-oz/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Fluid Ounce per second'
|
||||||
|
, plural: 'Fluid Ounces per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 1
|
||||||
|
}
|
||||||
|
, 'fl-oz/min': {
|
||||||
|
name: {
|
||||||
|
singular: 'Fluid Ounce per minute'
|
||||||
|
, plural: 'Fluid Ounces per minute'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/60
|
||||||
|
}
|
||||||
|
, 'fl-oz/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Fluid Ounce per hour'
|
||||||
|
, plural: 'Fluid Ounces per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/3600
|
||||||
|
}
|
||||||
|
, 'cup/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cup per second'
|
||||||
|
, plural: 'Cups per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 8
|
||||||
|
}
|
||||||
|
, 'pnt/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Pint per second'
|
||||||
|
, plural: 'Pints per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 16
|
||||||
|
}
|
||||||
|
, 'pnt/min': {
|
||||||
|
name: {
|
||||||
|
singular: 'Pint per minute'
|
||||||
|
, plural: 'Pints per minute'
|
||||||
|
}
|
||||||
|
, to_anchor: 4/15
|
||||||
|
}
|
||||||
|
, 'pnt/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Pint per hour'
|
||||||
|
, plural: 'Pints per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 1/225
|
||||||
|
}
|
||||||
|
, 'qt/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Quart per second'
|
||||||
|
, plural: 'Quarts per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 32
|
||||||
|
}
|
||||||
|
, 'gal/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Gallon per second'
|
||||||
|
, plural: 'Gallons per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 128
|
||||||
|
}
|
||||||
|
, 'gal/min': {
|
||||||
|
name: {
|
||||||
|
singular: 'Gallon per minute'
|
||||||
|
, plural: 'Gallons per minute'
|
||||||
|
}
|
||||||
|
, to_anchor: 32/15
|
||||||
|
}
|
||||||
|
, 'gal/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Gallon per hour'
|
||||||
|
, plural: 'Gallons per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 8/225
|
||||||
|
}
|
||||||
|
, 'ft3/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic foot per second'
|
||||||
|
, plural: 'Cubic feet per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 957.506
|
||||||
|
}
|
||||||
|
, 'ft3/min': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic foot per minute'
|
||||||
|
, plural: 'Cubic feet per minute'
|
||||||
|
}
|
||||||
|
, to_anchor: 957.506/60
|
||||||
|
}
|
||||||
|
, 'ft3/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic foot per hour'
|
||||||
|
, plural: 'Cubic feet per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 957.506/3600
|
||||||
|
}
|
||||||
|
, 'yd3/s': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic yard per second'
|
||||||
|
, plural: 'Cubic yards per second'
|
||||||
|
}
|
||||||
|
, to_anchor: 25852.7
|
||||||
|
}
|
||||||
|
, 'yd3/min': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic yard per minute'
|
||||||
|
, plural: 'Cubic yards per minute'
|
||||||
|
}
|
||||||
|
, to_anchor: 25852.7/60
|
||||||
|
}
|
||||||
|
, 'yd3/h': {
|
||||||
|
name: {
|
||||||
|
singular: 'Cubic yard per hour'
|
||||||
|
, plural: 'Cubic yards per hour'
|
||||||
|
}
|
||||||
|
, to_anchor: 25852.7/3600
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
metric: metric
|
||||||
|
, imperial: imperial
|
||||||
|
, _anchors: {
|
||||||
|
metric: {
|
||||||
|
unit: 'l/s'
|
||||||
|
, ratio: 33.8140227
|
||||||
|
}
|
||||||
|
, imperial: {
|
||||||
|
unit: 'fl-oz/s'
|
||||||
|
, ratio: 1/33.8140227
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
160
src/convert/fysics.js
Normal file
160
src/convert/fysics.js
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
Copyright:
|
||||||
|
Year : (c) 2023
|
||||||
|
Author : Rene De Ren
|
||||||
|
Contact details : zn375ix3@gmail.com
|
||||||
|
Location : The Netherlands
|
||||||
|
|
||||||
|
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, sublicense, 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 author shall be notified of any and all improvements or adaptations this 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,
|
||||||
|
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Fysics{
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
|
||||||
|
//gasses
|
||||||
|
this.air_density = 0; // weight of air
|
||||||
|
this.atm_pressure = 1.01325 ; //atm pressure at sea level
|
||||||
|
this.earth_gravity = 9.80665 ; // acceleration of standard gravity on earth in m/s2
|
||||||
|
this.water_molar_mass = 18.01528 // g/mol
|
||||||
|
this.num_moles_water = 6.022 * 1023; // number of moles in water
|
||||||
|
this.water_density = 997 ; // this.water_molar_mass * this.num_moles_water // water density in kg/m3;
|
||||||
|
|
||||||
|
//load converter
|
||||||
|
this.convert = require('./index');
|
||||||
|
|
||||||
|
// o2 solubility curve based on pressure and temp
|
||||||
|
this.o2Solubility = {
|
||||||
|
1: // abs bar
|
||||||
|
{
|
||||||
|
x:[0,5,10,15,20,25,30,35,40,45,50], // temp in degrees celcius
|
||||||
|
y:[14.6,12.8,11.3,10.1,9.1,8.3,7.6,7,6.5,6,5.6], // mg/l
|
||||||
|
},
|
||||||
|
2: // abs bar
|
||||||
|
{
|
||||||
|
x:[0,5,10,15,20,25,30,35,40,45,50], // temp in degrees celcius
|
||||||
|
y:[29.2,25.5,22.6,20.2,18.2,16.5,15.2,14,12.9,12,11.3], // mg/l
|
||||||
|
},
|
||||||
|
4: // abs bar
|
||||||
|
{
|
||||||
|
x:[0,5,10,15,20,25,30,35,40,45,50], // temp in degrees celcius
|
||||||
|
y:[58.4,51.1,45.1,40.3,36.4,33.1,30.3,27.9,25.9,24,22.7], // mg/l
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*------------------- functions -------------------*/
|
||||||
|
//use input to calculate air density in kg/m3 is valid up to 100 degrees
|
||||||
|
//pressure in bar RH in % and temp in degrees celcius
|
||||||
|
// Antoine Equation is, Log P = A - B / ( T + C )
|
||||||
|
// D8 = temp , d7 = RH , pressure in mbar = d6
|
||||||
|
//=1.2923*(273.15/(273.15+temp))*(((pressure*100000)-0.3783*((((MACHT(10,(8.07-(1730.63/(233.43+temp)))))*1000/760)*RH)*100))/(1.01325*100000))
|
||||||
|
/*
|
||||||
|
calc_air_dens(pressure,RH,temp){
|
||||||
|
|
||||||
|
let air_density =
|
||||||
|
1.2923 *
|
||||||
|
(
|
||||||
|
273.15 / ( 273.15 + temp )
|
||||||
|
)
|
||||||
|
*
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
pressure * 100000
|
||||||
|
)
|
||||||
|
- 0.3783 *
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
Math.pow( 10, ( 8.07 - ( 1730.63 / ( 233.43 + temp) ) ) )
|
||||||
|
)
|
||||||
|
*1000/760
|
||||||
|
)
|
||||||
|
*RH
|
||||||
|
)
|
||||||
|
*100
|
||||||
|
)
|
||||||
|
)
|
||||||
|
/ (this.atm_pressure * 100000 )
|
||||||
|
);
|
||||||
|
|
||||||
|
return air_density ;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
calc_air_dens(pressure, RH, temp) {
|
||||||
|
const Rd = 287.05; // Specific gas constant for dry air in J/(kg·K)
|
||||||
|
const Rv = 461.495; // Specific gas constant for water vapor in J/(kg·K)
|
||||||
|
|
||||||
|
// Convert temperature to Kelvin
|
||||||
|
const T = temp + 273.15;
|
||||||
|
|
||||||
|
// Antoine constants for water vapor
|
||||||
|
const A = 8.07131;
|
||||||
|
const B = 1730.63;
|
||||||
|
const C = 233.426;
|
||||||
|
|
||||||
|
// Calculate saturation vapor pressure (e_s) using the Antoine equation (in hPa)
|
||||||
|
const e_s = Math.pow(10, (A - (B / (C + temp))));
|
||||||
|
|
||||||
|
// Actual vapor pressure (e) in hPa
|
||||||
|
const e = RH * e_s / 100;
|
||||||
|
|
||||||
|
// Convert pressure to Pascals
|
||||||
|
const pressurePa = this.convert(pressure).from('mbar').to('Pa');
|
||||||
|
|
||||||
|
// Partial pressure of dry air (Pa)
|
||||||
|
const p_d = pressurePa - (e * 100);
|
||||||
|
|
||||||
|
// Air density (kg/m³)
|
||||||
|
const air_density = (p_d / (Rd * T)) + ((e * 100) / (Rv * T));
|
||||||
|
|
||||||
|
return air_density;
|
||||||
|
}
|
||||||
|
|
||||||
|
//convert height to pressure ( density => kg/m3 , height => meter) output is in bar
|
||||||
|
heigth_to_pressure(density,height){
|
||||||
|
|
||||||
|
//calc pressure
|
||||||
|
let pressure = density * this.earth_gravity * height;
|
||||||
|
//convert Pa to bar
|
||||||
|
pressure = this.convert(pressure).from('Pa').to('mbar');
|
||||||
|
|
||||||
|
return pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
//input is in meters !
|
||||||
|
calc_volume(height,width,length){
|
||||||
|
let result = 0;
|
||||||
|
result = height * width * length;
|
||||||
|
return result ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
let fysics = new Fysics();
|
||||||
|
console.log("converted pressure : " + fysics.heigth_to_pressure(fysics.water_density,10) + " mbar ");
|
||||||
|
console.log( "air density : " + fysics.calc_air_dens(1.012,0,0) + " kg / m3" );
|
||||||
|
|
||||||
|
//*/
|
||||||
|
|
||||||
|
module.exports = Fysics;
|
||||||
304
src/convert/index.js
Normal file
304
src/convert/index.js
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
var convert
|
||||||
|
, keys = require('./lodash/lodash.keys')
|
||||||
|
, each = require('./lodash/lodash.foreach')
|
||||||
|
, measures = {
|
||||||
|
length: require('./definitions/length')
|
||||||
|
, area: require('./definitions/area')
|
||||||
|
, mass: require('./definitions/mass')
|
||||||
|
, volume: require('./definitions/volume')
|
||||||
|
, each: require('./definitions/each')
|
||||||
|
, temperature: require('./definitions/temperature')
|
||||||
|
, time: require('./definitions/time')
|
||||||
|
, digital: require('./definitions/digital')
|
||||||
|
, partsPer: require('./definitions/partsPer')
|
||||||
|
, speed: require('./definitions/speed')
|
||||||
|
, pace: require('./definitions/pace')
|
||||||
|
, pressure: require('./definitions/pressure')
|
||||||
|
, current: require('./definitions/current')
|
||||||
|
, voltage: require('./definitions/voltage')
|
||||||
|
, power: require('./definitions/power')
|
||||||
|
, reactivePower: require('./definitions/reactivePower')
|
||||||
|
, apparentPower: require('./definitions/apparentPower')
|
||||||
|
, energy: require('./definitions/energy')
|
||||||
|
, reactiveEnergy: require('./definitions/reactiveEnergy')
|
||||||
|
, volumeFlowRate: require('./definitions/volumeFlowRate')
|
||||||
|
, illuminance: require('./definitions/illuminance')
|
||||||
|
, frequency: require('./definitions/frequency')
|
||||||
|
, angle : require('./definitions/angle')
|
||||||
|
}
|
||||||
|
, Converter;
|
||||||
|
|
||||||
|
Converter = function (numerator, denominator) {
|
||||||
|
if(denominator)
|
||||||
|
this.val = numerator / denominator;
|
||||||
|
else
|
||||||
|
this.val = numerator;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lets the converter know the source unit abbreviation
|
||||||
|
*/
|
||||||
|
Converter.prototype.from = function (from) {
|
||||||
|
if(this.destination)
|
||||||
|
throw new Error('.from must be called before .to');
|
||||||
|
|
||||||
|
this.origin = this.getUnit(from);
|
||||||
|
|
||||||
|
if(!this.origin) {
|
||||||
|
this.throwUnsupportedUnitError(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the unit and returns the value
|
||||||
|
*/
|
||||||
|
Converter.prototype.to = function (to) {
|
||||||
|
if(!this.origin)
|
||||||
|
throw new Error('.to must be called after .from');
|
||||||
|
|
||||||
|
this.destination = this.getUnit(to);
|
||||||
|
|
||||||
|
var result
|
||||||
|
, transform;
|
||||||
|
|
||||||
|
if(!this.destination) {
|
||||||
|
this.throwUnsupportedUnitError(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't change the value if origin and destination are the same
|
||||||
|
if (this.origin.abbr === this.destination.abbr) {
|
||||||
|
return this.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can't go from liquid to mass, for example
|
||||||
|
if(this.destination.measure != this.origin.measure) {
|
||||||
|
throw new Error('Cannot convert incompatible measures of '
|
||||||
|
+ this.destination.measure + ' and ' + this.origin.measure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert from the source value to its anchor inside the system
|
||||||
|
*/
|
||||||
|
result = this.val * this.origin.unit.to_anchor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For some changes it's a simple shift (C to K)
|
||||||
|
* So we'll add it when convering into the unit (later)
|
||||||
|
* and subtract it when converting from the unit
|
||||||
|
*/
|
||||||
|
if (this.origin.unit.anchor_shift) {
|
||||||
|
result -= this.origin.unit.anchor_shift
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert from one system to another through the anchor ratio. Some conversions
|
||||||
|
* aren't ratio based or require more than a simple shift. We can provide a custom
|
||||||
|
* transform here to provide the direct result
|
||||||
|
*/
|
||||||
|
if(this.origin.system != this.destination.system) {
|
||||||
|
transform = measures[this.origin.measure]._anchors[this.origin.system].transform;
|
||||||
|
if (typeof transform === 'function') {
|
||||||
|
result = transform(result)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result *= measures[this.origin.measure]._anchors[this.origin.system].ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This shift has to be done after the system conversion business
|
||||||
|
*/
|
||||||
|
if (this.destination.unit.anchor_shift) {
|
||||||
|
result += this.destination.unit.anchor_shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert to another unit inside the destination system
|
||||||
|
*/
|
||||||
|
return result / this.destination.unit.to_anchor;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the unit to the best available unit.
|
||||||
|
*/
|
||||||
|
Converter.prototype.toBest = function(options) {
|
||||||
|
if(!this.origin)
|
||||||
|
throw new Error('.toBest must be called after .from');
|
||||||
|
|
||||||
|
var options = Object.assign({
|
||||||
|
exclude: [],
|
||||||
|
cutOffNumber: 1,
|
||||||
|
}, options)
|
||||||
|
|
||||||
|
var best;
|
||||||
|
/**
|
||||||
|
Looks through every possibility for the 'best' available unit.
|
||||||
|
i.e. Where the value has the fewest numbers before the decimal point,
|
||||||
|
but is still higher than 1.
|
||||||
|
*/
|
||||||
|
each(this.possibilities(), function(possibility) {
|
||||||
|
var unit = this.describe(possibility);
|
||||||
|
var isIncluded = options.exclude.indexOf(possibility) === -1;
|
||||||
|
|
||||||
|
if (isIncluded && unit.system === this.origin.system) {
|
||||||
|
var result = this.to(possibility);
|
||||||
|
if (!best || (result >= options.cutOffNumber && result < best.val)) {
|
||||||
|
best = {
|
||||||
|
val: result,
|
||||||
|
unit: possibility,
|
||||||
|
singular: unit.singular,
|
||||||
|
plural: unit.plural
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the unit
|
||||||
|
*/
|
||||||
|
Converter.prototype.getUnit = function (abbr) {
|
||||||
|
var found;
|
||||||
|
|
||||||
|
each(measures, function (systems, measure) {
|
||||||
|
each(systems, function (units, system) {
|
||||||
|
if(system == '_anchors')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
each(units, function (unit, testAbbr) {
|
||||||
|
if(testAbbr == abbr) {
|
||||||
|
found = {
|
||||||
|
abbr: abbr
|
||||||
|
, measure: measure
|
||||||
|
, system: system
|
||||||
|
, unit: unit
|
||||||
|
};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(found)
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(found)
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return found;
|
||||||
|
};
|
||||||
|
|
||||||
|
var describe = function(resp) {
|
||||||
|
return {
|
||||||
|
abbr: resp.abbr
|
||||||
|
, measure: resp.measure
|
||||||
|
, system: resp.system
|
||||||
|
, singular: resp.unit.name.singular
|
||||||
|
, plural: resp.unit.name.plural
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An alias for getUnit
|
||||||
|
*/
|
||||||
|
Converter.prototype.describe = function (abbr) {
|
||||||
|
var resp = Converter.prototype.getUnit(abbr);
|
||||||
|
var desc = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
desc = describe(resp);
|
||||||
|
} catch(err) {
|
||||||
|
this.throwUnsupportedUnitError(abbr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detailed list of all supported units
|
||||||
|
*/
|
||||||
|
Converter.prototype.list = function (measure) {
|
||||||
|
var list = [];
|
||||||
|
|
||||||
|
each(measures, function (systems, testMeasure) {
|
||||||
|
if(measure && measure !== testMeasure)
|
||||||
|
return;
|
||||||
|
|
||||||
|
each(systems, function (units, system) {
|
||||||
|
if(system == '_anchors')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
each(units, function (unit, abbr) {
|
||||||
|
list = list.concat(describe({
|
||||||
|
abbr: abbr,
|
||||||
|
measure: testMeasure
|
||||||
|
, system: system
|
||||||
|
, unit: unit
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
Converter.prototype.throwUnsupportedUnitError = function (what) {
|
||||||
|
var validUnits = [];
|
||||||
|
|
||||||
|
each(measures, function (systems, measure) {
|
||||||
|
each(systems, function (units, system) {
|
||||||
|
if(system == '_anchors')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
validUnits = validUnits.concat(keys(units));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
throw new Error('Unsupported unit ' + what + ', use one of: ' + validUnits.join(', '));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the abbreviated measures that the value can be
|
||||||
|
* converted to.
|
||||||
|
*/
|
||||||
|
Converter.prototype.possibilities = function (measure) {
|
||||||
|
var possibilities = [];
|
||||||
|
if(!this.origin && !measure) {
|
||||||
|
each(keys(measures), function (measure){
|
||||||
|
each(measures[measure], function (units, system) {
|
||||||
|
if(system == '_anchors')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
possibilities = possibilities.concat(keys(units));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
measure = measure || this.origin.measure;
|
||||||
|
each(measures[measure], function (units, system) {
|
||||||
|
if(system == '_anchors')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
possibilities = possibilities.concat(keys(units));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return possibilities;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the abbreviated measures that the value can be
|
||||||
|
* converted to.
|
||||||
|
*/
|
||||||
|
Converter.prototype.measures = function () {
|
||||||
|
return keys(measures);
|
||||||
|
};
|
||||||
|
|
||||||
|
convert = function (value) {
|
||||||
|
return new Converter(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = convert;
|
||||||
22
src/convert/lodash/lodash._basebind/LICENSE.txt
Normal file
22
src/convert/lodash/lodash._basebind/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash._basebind/README.md
Normal file
15
src/convert/lodash/lodash._basebind/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash._basebind v2.3.0
|
||||||
|
|
||||||
|
The internal [Lo-Dash](http://lodash.com/) function `baseBind` as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
58
src/convert/lodash/lodash._basebind/index.js
Normal file
58
src/convert/lodash/lodash._basebind/index.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
var baseCreate = require('./../lodash._basecreate'),
|
||||||
|
isObject = require('./../lodash.isobject'),
|
||||||
|
setBindData = require('./../lodash._setbinddata');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for `Array` method references.
|
||||||
|
*
|
||||||
|
* Normally `Array.prototype` would suffice, however, using an array literal
|
||||||
|
* avoids issues in Narwhal.
|
||||||
|
*/
|
||||||
|
var arrayRef = [];
|
||||||
|
|
||||||
|
/** Native method shortcuts */
|
||||||
|
var push = arrayRef.push;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base implementation of `_.bind` that creates the bound function and
|
||||||
|
* sets its meta data.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Array} bindData The bind data array.
|
||||||
|
* @returns {Function} Returns the new bound function.
|
||||||
|
*/
|
||||||
|
function baseBind(bindData) {
|
||||||
|
var func = bindData[0],
|
||||||
|
partialArgs = bindData[2],
|
||||||
|
thisArg = bindData[4];
|
||||||
|
|
||||||
|
function bound() {
|
||||||
|
// `Function#bind` spec
|
||||||
|
// http://es5.github.io/#x15.3.4.5
|
||||||
|
if (partialArgs) {
|
||||||
|
var args = partialArgs.slice();
|
||||||
|
push.apply(args, arguments);
|
||||||
|
}
|
||||||
|
// mimic the constructor's `return` behavior
|
||||||
|
// http://es5.github.io/#x13.2.2
|
||||||
|
if (this instanceof bound) {
|
||||||
|
// ensure `new bound` is an instance of `func`
|
||||||
|
var thisBinding = baseCreate(func.prototype),
|
||||||
|
result = func.apply(thisBinding, args || arguments);
|
||||||
|
return isObject(result) ? result : thisBinding;
|
||||||
|
}
|
||||||
|
return func.apply(thisArg, args || arguments);
|
||||||
|
}
|
||||||
|
setBindData(bound, bindData);
|
||||||
|
return bound;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = baseBind;
|
||||||
21
src/convert/lodash/lodash._basebind/package.json
Normal file
21
src/convert/lodash/lodash._basebind/package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash._basebind",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The internal Lo-Dash function `baseBind` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" },
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._basecreate": "~2.3.0",
|
||||||
|
"lodash.isobject": "~2.3.0",
|
||||||
|
"lodash._setbinddata": "~2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash._basecreate/LICENSE.txt
Normal file
22
src/convert/lodash/lodash._basecreate/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash._basecreate/README.md
Normal file
15
src/convert/lodash/lodash._basecreate/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash._basecreate v2.3.0
|
||||||
|
|
||||||
|
The internal [Lo-Dash](http://lodash.com/) function `baseCreate` as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
42
src/convert/lodash/lodash._basecreate/index.js
Normal file
42
src/convert/lodash/lodash._basecreate/index.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
var isObject = require('./../lodash.isobject'),
|
||||||
|
noop = require('./../lodash.noop'),
|
||||||
|
reNative = require('./../lodash._renative');
|
||||||
|
|
||||||
|
/* Native method shortcuts for methods with the same name as other `lodash` methods */
|
||||||
|
var nativeCreate = reNative.test(nativeCreate = Object.create) && nativeCreate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base implementation of `_.create` without support for assigning
|
||||||
|
* properties to the created object.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Object} prototype The object to inherit from.
|
||||||
|
* @returns {Object} Returns the new object.
|
||||||
|
*/
|
||||||
|
function baseCreate(prototype, properties) {
|
||||||
|
return isObject(prototype) ? nativeCreate(prototype) : {};
|
||||||
|
}
|
||||||
|
// fallback for browsers without `Object.create`
|
||||||
|
if (!nativeCreate) {
|
||||||
|
baseCreate = (function() {
|
||||||
|
function Object() {}
|
||||||
|
return function(prototype) {
|
||||||
|
if (isObject(prototype)) {
|
||||||
|
Object.prototype = prototype;
|
||||||
|
var result = new Object;
|
||||||
|
Object.prototype = null;
|
||||||
|
}
|
||||||
|
return result || global.Object();
|
||||||
|
};
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = baseCreate;
|
||||||
21
src/convert/lodash/lodash._basecreate/package.json
Normal file
21
src/convert/lodash/lodash._basecreate/package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash._basecreate",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The internal Lo-Dash function `baseCreate` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" },
|
||||||
|
"dependencies": {
|
||||||
|
"lodash.isobject": "~2.3.0",
|
||||||
|
"lodash.noop": "~2.3.0",
|
||||||
|
"lodash._renative": "~2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash._basecreatecallback/LICENSE.txt
Normal file
22
src/convert/lodash/lodash._basecreatecallback/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash._basecreatecallback/README.md
Normal file
15
src/convert/lodash/lodash._basecreatecallback/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash._basecreatecallback v2.3.0
|
||||||
|
|
||||||
|
The internal [Lo-Dash](http://lodash.com/) function `baseCreateCallback` as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
80
src/convert/lodash/lodash._basecreatecallback/index.js
Normal file
80
src/convert/lodash/lodash._basecreatecallback/index.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
var bind = require('./../lodash.bind'),
|
||||||
|
identity = require('./../lodash.identity'),
|
||||||
|
setBindData = require('./../lodash._setbinddata'),
|
||||||
|
support = require('./../lodash.support');
|
||||||
|
|
||||||
|
/** Used to detected named functions */
|
||||||
|
var reFuncName = /^\s*function[ \n\r\t]+\w/;
|
||||||
|
|
||||||
|
/** Used to detect functions containing a `this` reference */
|
||||||
|
var reThis = /\bthis\b/;
|
||||||
|
|
||||||
|
/** Native method shortcuts */
|
||||||
|
var fnToString = Function.prototype.toString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base implementation of `_.createCallback` without support for creating
|
||||||
|
* "_.pluck" or "_.where" style callbacks.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {*} [func=identity] The value to convert to a callback.
|
||||||
|
* @param {*} [thisArg] The `this` binding of the created callback.
|
||||||
|
* @param {number} [argCount] The number of arguments the callback accepts.
|
||||||
|
* @returns {Function} Returns a callback function.
|
||||||
|
*/
|
||||||
|
function baseCreateCallback(func, thisArg, argCount) {
|
||||||
|
if (typeof func != 'function') {
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
// exit early for no `thisArg` or already bound by `Function#bind`
|
||||||
|
if (typeof thisArg == 'undefined' || !('prototype' in func)) {
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
var bindData = func.__bindData__;
|
||||||
|
if (typeof bindData == 'undefined') {
|
||||||
|
if (support.funcNames) {
|
||||||
|
bindData = !func.name;
|
||||||
|
}
|
||||||
|
bindData = bindData || !support.funcDecomp;
|
||||||
|
if (!bindData) {
|
||||||
|
var source = fnToString.call(func);
|
||||||
|
if (!support.funcNames) {
|
||||||
|
bindData = !reFuncName.test(source);
|
||||||
|
}
|
||||||
|
if (!bindData) {
|
||||||
|
// checks if `func` references the `this` keyword and stores the result
|
||||||
|
bindData = reThis.test(source);
|
||||||
|
setBindData(func, bindData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// exit early if there are no `this` references or `func` is bound
|
||||||
|
if (bindData === false || (bindData !== true && bindData[1] & 1)) {
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
switch (argCount) {
|
||||||
|
case 1: return function(value) {
|
||||||
|
return func.call(thisArg, value);
|
||||||
|
};
|
||||||
|
case 2: return function(a, b) {
|
||||||
|
return func.call(thisArg, a, b);
|
||||||
|
};
|
||||||
|
case 3: return function(value, index, collection) {
|
||||||
|
return func.call(thisArg, value, index, collection);
|
||||||
|
};
|
||||||
|
case 4: return function(accumulator, value, index, collection) {
|
||||||
|
return func.call(thisArg, accumulator, value, index, collection);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return bind(func, thisArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = baseCreateCallback;
|
||||||
22
src/convert/lodash/lodash._basecreatecallback/package.json
Normal file
22
src/convert/lodash/lodash._basecreatecallback/package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash._basecreatecallback",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The internal Lo-Dash function `baseCreateCallback` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" },
|
||||||
|
"dependencies": {
|
||||||
|
"lodash.bind": "~2.3.0",
|
||||||
|
"lodash.identity": "~2.3.0",
|
||||||
|
"lodash._setbinddata": "~2.3.0",
|
||||||
|
"lodash.support": "~2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash._basecreatewrapper/LICENSE.txt
Normal file
22
src/convert/lodash/lodash._basecreatewrapper/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash._basecreatewrapper/README.md
Normal file
15
src/convert/lodash/lodash._basecreatewrapper/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash._basecreatewrapper v2.3.0
|
||||||
|
|
||||||
|
The internal [Lo-Dash](http://lodash.com/) function `baseCreateWrapper` as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
78
src/convert/lodash/lodash._basecreatewrapper/index.js
Normal file
78
src/convert/lodash/lodash._basecreatewrapper/index.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
var baseCreate = require('./../lodash._basecreate'),
|
||||||
|
isObject = require('./../lodash.isobject'),
|
||||||
|
setBindData = require('./../lodash._setbinddata'),
|
||||||
|
slice = require('./../lodash._slice');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for `Array` method references.
|
||||||
|
*
|
||||||
|
* Normally `Array.prototype` would suffice, however, using an array literal
|
||||||
|
* avoids issues in Narwhal.
|
||||||
|
*/
|
||||||
|
var arrayRef = [];
|
||||||
|
|
||||||
|
/** Native method shortcuts */
|
||||||
|
var push = arrayRef.push;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base implementation of `createWrapper` that creates the wrapper and
|
||||||
|
* sets its meta data.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Array} bindData The bind data array.
|
||||||
|
* @returns {Function} Returns the new function.
|
||||||
|
*/
|
||||||
|
function baseCreateWrapper(bindData) {
|
||||||
|
var func = bindData[0],
|
||||||
|
bitmask = bindData[1],
|
||||||
|
partialArgs = bindData[2],
|
||||||
|
partialRightArgs = bindData[3],
|
||||||
|
thisArg = bindData[4],
|
||||||
|
arity = bindData[5];
|
||||||
|
|
||||||
|
var isBind = bitmask & 1,
|
||||||
|
isBindKey = bitmask & 2,
|
||||||
|
isCurry = bitmask & 4,
|
||||||
|
isCurryBound = bitmask & 8,
|
||||||
|
key = func;
|
||||||
|
|
||||||
|
function bound() {
|
||||||
|
var thisBinding = isBind ? thisArg : this;
|
||||||
|
if (partialArgs) {
|
||||||
|
var args = partialArgs.slice();
|
||||||
|
push.apply(args, arguments);
|
||||||
|
}
|
||||||
|
if (partialRightArgs || isCurry) {
|
||||||
|
args || (args = slice(arguments));
|
||||||
|
if (partialRightArgs) {
|
||||||
|
push.apply(args, partialRightArgs);
|
||||||
|
}
|
||||||
|
if (isCurry && args.length < arity) {
|
||||||
|
bitmask |= 16 & ~32;
|
||||||
|
return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args || (args = arguments);
|
||||||
|
if (isBindKey) {
|
||||||
|
func = thisBinding[key];
|
||||||
|
}
|
||||||
|
if (this instanceof bound) {
|
||||||
|
thisBinding = baseCreate(func.prototype);
|
||||||
|
var result = func.apply(thisBinding, args);
|
||||||
|
return isObject(result) ? result : thisBinding;
|
||||||
|
}
|
||||||
|
return func.apply(thisBinding, args);
|
||||||
|
}
|
||||||
|
setBindData(bound, bindData);
|
||||||
|
return bound;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = baseCreateWrapper;
|
||||||
22
src/convert/lodash/lodash._basecreatewrapper/package.json
Normal file
22
src/convert/lodash/lodash._basecreatewrapper/package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash._basecreatewrapper",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The internal Lo-Dash function `baseCreateWrapper` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" },
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._basecreate": "~2.3.0",
|
||||||
|
"lodash.isobject": "~2.3.0",
|
||||||
|
"lodash._setbinddata": "~2.3.0",
|
||||||
|
"lodash._slice": "~2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash._createwrapper/LICENSE.txt
Normal file
22
src/convert/lodash/lodash._createwrapper/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash._createwrapper/README.md
Normal file
15
src/convert/lodash/lodash._createwrapper/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash._createwrapper v2.3.0
|
||||||
|
|
||||||
|
The internal [Lo-Dash](http://lodash.com/) function `createWrapper` as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
98
src/convert/lodash/lodash._createwrapper/index.js
Normal file
98
src/convert/lodash/lodash._createwrapper/index.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
var baseBind = require('./../lodash._basebind'),
|
||||||
|
baseCreateWrapper = require('./../lodash._basecreatewrapper'),
|
||||||
|
isFunction = require('./../lodash.isfunction');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for `Array` method references.
|
||||||
|
*
|
||||||
|
* Normally `Array.prototype` would suffice, however, using an array literal
|
||||||
|
* avoids issues in Narwhal.
|
||||||
|
*/
|
||||||
|
var arrayRef = [];
|
||||||
|
|
||||||
|
/** Native method shortcuts */
|
||||||
|
var push = arrayRef.push;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a function that, when called, either curries or invokes `func`
|
||||||
|
* with an optional `this` binding and partially applied arguments.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Function|string} func The function or method name to reference.
|
||||||
|
* @param {number} bitmask The bitmask of method flags to compose.
|
||||||
|
* The bitmask may be composed of the following flags:
|
||||||
|
* 1 - `_.bind`
|
||||||
|
* 2 - `_.bindKey`
|
||||||
|
* 4 - `_.curry`
|
||||||
|
* 8 - `_.curry` (bound)
|
||||||
|
* 16 - `_.partial`
|
||||||
|
* 32 - `_.partialRight`
|
||||||
|
* @param {Array} [partialArgs] An array of arguments to prepend to those
|
||||||
|
* provided to the new function.
|
||||||
|
* @param {Array} [partialRightArgs] An array of arguments to append to those
|
||||||
|
* provided to the new function.
|
||||||
|
* @param {*} [thisArg] The `this` binding of `func`.
|
||||||
|
* @param {number} [arity] The arity of `func`.
|
||||||
|
* @returns {Function} Returns the new function.
|
||||||
|
*/
|
||||||
|
function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
|
||||||
|
var isBind = bitmask & 1,
|
||||||
|
isBindKey = bitmask & 2,
|
||||||
|
isCurry = bitmask & 4,
|
||||||
|
isCurryBound = bitmask & 8,
|
||||||
|
isPartial = bitmask & 16,
|
||||||
|
isPartialRight = bitmask & 32;
|
||||||
|
|
||||||
|
if (!isBindKey && !isFunction(func)) {
|
||||||
|
throw new TypeError;
|
||||||
|
}
|
||||||
|
if (isPartial && !partialArgs.length) {
|
||||||
|
bitmask &= ~16;
|
||||||
|
isPartial = partialArgs = false;
|
||||||
|
}
|
||||||
|
if (isPartialRight && !partialRightArgs.length) {
|
||||||
|
bitmask &= ~32;
|
||||||
|
isPartialRight = partialRightArgs = false;
|
||||||
|
}
|
||||||
|
var bindData = func && func.__bindData__;
|
||||||
|
if (bindData && bindData !== true) {
|
||||||
|
bindData = bindData.slice();
|
||||||
|
|
||||||
|
// set `thisBinding` is not previously bound
|
||||||
|
if (isBind && !(bindData[1] & 1)) {
|
||||||
|
bindData[4] = thisArg;
|
||||||
|
}
|
||||||
|
// set if previously bound but not currently (subsequent curried functions)
|
||||||
|
if (!isBind && bindData[1] & 1) {
|
||||||
|
bitmask |= 8;
|
||||||
|
}
|
||||||
|
// set curried arity if not yet set
|
||||||
|
if (isCurry && !(bindData[1] & 4)) {
|
||||||
|
bindData[5] = arity;
|
||||||
|
}
|
||||||
|
// append partial left arguments
|
||||||
|
if (isPartial) {
|
||||||
|
push.apply(bindData[2] || (bindData[2] = []), partialArgs);
|
||||||
|
}
|
||||||
|
// append partial right arguments
|
||||||
|
if (isPartialRight) {
|
||||||
|
push.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
|
||||||
|
}
|
||||||
|
// merge flags
|
||||||
|
bindData[1] |= bitmask;
|
||||||
|
return createWrapper.apply(null, bindData);
|
||||||
|
}
|
||||||
|
// fast path for `_.bind`
|
||||||
|
var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
|
||||||
|
return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = createWrapper;
|
||||||
21
src/convert/lodash/lodash._createwrapper/package.json
Normal file
21
src/convert/lodash/lodash._createwrapper/package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash._createwrapper",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The internal Lo-Dash function `createWrapper` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" },
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._basebind": "~2.3.0",
|
||||||
|
"lodash._basecreatewrapper": "~2.3.0",
|
||||||
|
"lodash.isfunction": "~2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash._objecttypes/LICENSE.txt
Normal file
22
src/convert/lodash/lodash._objecttypes/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash._objecttypes/README.md
Normal file
15
src/convert/lodash/lodash._objecttypes/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash._objecttypes v2.3.0
|
||||||
|
|
||||||
|
The internal [Lo-Dash](http://lodash.com/) variable `objectTypes` as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
20
src/convert/lodash/lodash._objecttypes/index.js
Normal file
20
src/convert/lodash/lodash._objecttypes/index.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Used to determine if values are of the language type Object */
|
||||||
|
var objectTypes = {
|
||||||
|
'boolean': false,
|
||||||
|
'function': true,
|
||||||
|
'object': true,
|
||||||
|
'number': false,
|
||||||
|
'string': false,
|
||||||
|
'undefined': false
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = objectTypes;
|
||||||
16
src/convert/lodash/lodash._objecttypes/package.json
Normal file
16
src/convert/lodash/lodash._objecttypes/package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash._objecttypes",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The internal Lo-Dash variable `objectTypes` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" }
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash._renative/LICENSE.txt
Normal file
22
src/convert/lodash/lodash._renative/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash._renative/README.md
Normal file
15
src/convert/lodash/lodash._renative/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash._renative v2.3.0
|
||||||
|
|
||||||
|
The internal [Lo-Dash](http://lodash.com/) variable `reNative` as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
23
src/convert/lodash/lodash._renative/index.js
Normal file
23
src/convert/lodash/lodash._renative/index.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Used for native method references */
|
||||||
|
var objectProto = Object.prototype;
|
||||||
|
|
||||||
|
/** Used to resolve the internal [[Class]] of values */
|
||||||
|
var toString = objectProto.toString;
|
||||||
|
|
||||||
|
/** Used to detect if a method is native */
|
||||||
|
var reNative = RegExp('^' +
|
||||||
|
String(toString)
|
||||||
|
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
||||||
|
.replace(/toString| for [^\]]+/g, '.*?') + '$'
|
||||||
|
);
|
||||||
|
|
||||||
|
module.exports = reNative;
|
||||||
16
src/convert/lodash/lodash._renative/package.json
Normal file
16
src/convert/lodash/lodash._renative/package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash._renative",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The internal Lo-Dash variable `reNative` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" }
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash._setbinddata/LICENSE.txt
Normal file
22
src/convert/lodash/lodash._setbinddata/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash._setbinddata/README.md
Normal file
15
src/convert/lodash/lodash._setbinddata/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash._setbinddata v2.3.0
|
||||||
|
|
||||||
|
The internal [Lo-Dash](http://lodash.com/) function `setBindData` as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
43
src/convert/lodash/lodash._setbinddata/index.js
Normal file
43
src/convert/lodash/lodash._setbinddata/index.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
var noop = require('./../lodash.noop'),
|
||||||
|
reNative = require('./../lodash._renative');
|
||||||
|
|
||||||
|
/** Used as the property descriptor for `__bindData__` */
|
||||||
|
var descriptor = {
|
||||||
|
'configurable': false,
|
||||||
|
'enumerable': false,
|
||||||
|
'value': null,
|
||||||
|
'writable': false
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Used to set meta data on functions */
|
||||||
|
var defineProperty = (function() {
|
||||||
|
// IE 8 only accepts DOM elements
|
||||||
|
try {
|
||||||
|
var o = {},
|
||||||
|
func = reNative.test(func = Object.defineProperty) && func,
|
||||||
|
result = func(o, o, o) && func;
|
||||||
|
} catch(e) { }
|
||||||
|
return result;
|
||||||
|
}());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets `this` binding data on a given function.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Function} func The function to set data on.
|
||||||
|
* @param {Array} value The data array to set.
|
||||||
|
*/
|
||||||
|
var setBindData = !defineProperty ? noop : function(func, value) {
|
||||||
|
descriptor.value = value;
|
||||||
|
defineProperty(func, '__bindData__', descriptor);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = setBindData;
|
||||||
20
src/convert/lodash/lodash._setbinddata/package.json
Normal file
20
src/convert/lodash/lodash._setbinddata/package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash._setbinddata",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The internal Lo-Dash function `setBindData` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" },
|
||||||
|
"dependencies": {
|
||||||
|
"lodash.noop": "~2.3.0",
|
||||||
|
"lodash._renative": "~2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash._shimkeys/LICENSE.txt
Normal file
22
src/convert/lodash/lodash._shimkeys/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash._shimkeys/README.md
Normal file
15
src/convert/lodash/lodash._shimkeys/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash._shimkeys v2.3.0
|
||||||
|
|
||||||
|
The internal [Lo-Dash](http://lodash.com/) function `shimKeys` as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
38
src/convert/lodash/lodash._shimkeys/index.js
Normal file
38
src/convert/lodash/lodash._shimkeys/index.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
var objectTypes = require('./../lodash._objecttypes');
|
||||||
|
|
||||||
|
/** Used for native method references */
|
||||||
|
var objectProto = Object.prototype;
|
||||||
|
|
||||||
|
/** Native method shortcuts */
|
||||||
|
var hasOwnProperty = objectProto.hasOwnProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fallback implementation of `Object.keys` which produces an array of the
|
||||||
|
* given object's own enumerable property names.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @type Function
|
||||||
|
* @param {Object} object The object to inspect.
|
||||||
|
* @returns {Array} Returns an array of property names.
|
||||||
|
*/
|
||||||
|
var shimKeys = function(object) {
|
||||||
|
var index, iterable = object, result = [];
|
||||||
|
if (!iterable) return result;
|
||||||
|
if (!(objectTypes[typeof object])) return result;
|
||||||
|
for (index in iterable) {
|
||||||
|
if (hasOwnProperty.call(iterable, index)) {
|
||||||
|
result.push(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = shimKeys;
|
||||||
19
src/convert/lodash/lodash._shimkeys/package.json
Normal file
19
src/convert/lodash/lodash._shimkeys/package.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash._shimkeys",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The internal Lo-Dash function `shimKeys` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" },
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._objecttypes": "~2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash._slice/LICENSE.txt
Normal file
22
src/convert/lodash/lodash._slice/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash._slice/README.md
Normal file
15
src/convert/lodash/lodash._slice/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash._slice v2.3.0
|
||||||
|
|
||||||
|
The internal [Lo-Dash](http://lodash.com/) function `slice` as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
38
src/convert/lodash/lodash._slice/index.js
Normal file
38
src/convert/lodash/lodash._slice/index.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slices the `collection` from the `start` index up to, but not including,
|
||||||
|
* the `end` index.
|
||||||
|
*
|
||||||
|
* Note: This function is used instead of `Array#slice` to support node lists
|
||||||
|
* in IE < 9 and to ensure dense arrays are returned.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Array|Object|string} collection The collection to slice.
|
||||||
|
* @param {number} start The start index.
|
||||||
|
* @param {number} end The end index.
|
||||||
|
* @returns {Array} Returns the new array.
|
||||||
|
*/
|
||||||
|
function slice(array, start, end) {
|
||||||
|
start || (start = 0);
|
||||||
|
if (typeof end == 'undefined') {
|
||||||
|
end = array ? array.length : 0;
|
||||||
|
}
|
||||||
|
var index = -1,
|
||||||
|
length = end - start || 0,
|
||||||
|
result = Array(length < 0 ? 0 : length);
|
||||||
|
|
||||||
|
while (++index < length) {
|
||||||
|
result[index] = array[start + index];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = slice;
|
||||||
16
src/convert/lodash/lodash._slice/package.json
Normal file
16
src/convert/lodash/lodash._slice/package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash._slice",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The internal Lo-Dash function `slice` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" }
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash.bind/LICENSE.txt
Normal file
22
src/convert/lodash/lodash.bind/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash.bind/README.md
Normal file
15
src/convert/lodash/lodash.bind/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash.bind v2.3.0
|
||||||
|
|
||||||
|
The [Lo-Dash](http://lodash.com/) function [`_.bind`](http://lodash.com/docs#bind) as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
41
src/convert/lodash/lodash.bind/index.js
Normal file
41
src/convert/lodash/lodash.bind/index.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
var createWrapper = require('./../lodash._createwrapper'),
|
||||||
|
reNative = require('./../lodash._renative'),
|
||||||
|
slice = require('./../lodash._slice');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a function that, when called, invokes `func` with the `this`
|
||||||
|
* binding of `thisArg` and prepends any additional `bind` arguments to those
|
||||||
|
* provided to the bound function.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @memberOf _
|
||||||
|
* @category Functions
|
||||||
|
* @param {Function} func The function to bind.
|
||||||
|
* @param {*} [thisArg] The `this` binding of `func`.
|
||||||
|
* @param {...*} [arg] Arguments to be partially applied.
|
||||||
|
* @returns {Function} Returns the new bound function.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* var func = function(greeting) {
|
||||||
|
* return greeting + ' ' + this.name;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* func = _.bind(func, { 'name': 'fred' }, 'hi');
|
||||||
|
* func();
|
||||||
|
* // => 'hi fred'
|
||||||
|
*/
|
||||||
|
function bind(func, thisArg) {
|
||||||
|
return arguments.length > 2
|
||||||
|
? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
|
||||||
|
: createWrapper(func, 1, null, null, thisArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = bind;
|
||||||
22
src/convert/lodash/lodash.bind/package.json
Normal file
22
src/convert/lodash/lodash.bind/package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash.bind",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The Lo-Dash function `_.bind` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": ["functional", "lodash", "lodash-modularized", "server", "util"],
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" },
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._createwrapper": "~2.3.0",
|
||||||
|
"lodash._renative": "~2.3.0",
|
||||||
|
"lodash._slice": "~2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash.foreach/LICENSE.txt
Normal file
22
src/convert/lodash/lodash.foreach/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash.foreach/README.md
Normal file
15
src/convert/lodash/lodash.foreach/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash.foreach v2.3.0
|
||||||
|
|
||||||
|
The [Lo-Dash](http://lodash.com/) function [`_.forEach`](http://lodash.com/docs#forEach) as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
55
src/convert/lodash/lodash.foreach/index.js
Normal file
55
src/convert/lodash/lodash.foreach/index.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
var baseCreateCallback = require('./../lodash._basecreatecallback'),
|
||||||
|
forOwn = require('./../lodash.forown');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over elements of a collection, executing the callback for each
|
||||||
|
* element. The callback is bound to `thisArg` and invoked with three arguments;
|
||||||
|
* (value, index|key, collection). Callbacks may exit iteration early by
|
||||||
|
* explicitly returning `false`.
|
||||||
|
*
|
||||||
|
* Note: As with other "Collections" methods, objects with a `length` property
|
||||||
|
* are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
|
||||||
|
* may be used for object iteration.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @memberOf _
|
||||||
|
* @alias each
|
||||||
|
* @category Collections
|
||||||
|
* @param {Array|Object|string} collection The collection to iterate over.
|
||||||
|
* @param {Function} [callback=identity] The function called per iteration.
|
||||||
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
||||||
|
* @returns {Array|Object|string} Returns `collection`.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
|
||||||
|
* // => logs each number and returns '1,2,3'
|
||||||
|
*
|
||||||
|
* _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
|
||||||
|
* // => logs each number and returns the object (property order is not guaranteed across environments)
|
||||||
|
*/
|
||||||
|
function forEach(collection, callback, thisArg) {
|
||||||
|
var index = -1,
|
||||||
|
length = collection ? collection.length : 0;
|
||||||
|
|
||||||
|
callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
|
||||||
|
if (typeof length == 'number') {
|
||||||
|
while (++index < length) {
|
||||||
|
if (callback(collection[index], index, collection) === false) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
forOwn(collection, callback);
|
||||||
|
}
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = forEach;
|
||||||
21
src/convert/lodash/lodash.foreach/package.json
Normal file
21
src/convert/lodash/lodash.foreach/package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash.foreach",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The Lo-Dash function `_.forEach` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": ["functional", "lodash", "lodash-modularized", "server", "util"],
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" },
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._basecreatecallback": "~2.3.0",
|
||||||
|
"lodash.forown": "~2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/convert/lodash/lodash.forown/LICENSE.txt
Normal file
22
src/convert/lodash/lodash.forown/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
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, sublicense, 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.
|
||||||
15
src/convert/lodash/lodash.forown/README.md
Normal file
15
src/convert/lodash/lodash.forown/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# lodash.forown v2.3.0
|
||||||
|
|
||||||
|
The [Lo-Dash](http://lodash.com/) function [`_.forOwn`](http://lodash.com/docs#forOwn) as a [Node.js](http://nodejs.org/) module generated by [lodash-cli](https://npmjs.org/package/lodash-cli).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
| [](https://twitter.com/jdalton "Follow @jdalton on Twitter") |
|
||||||
|
|---|
|
||||||
|
| [John-David Dalton](http://allyoucanleet.com/) |
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
| [](https://twitter.com/blainebublitz "Follow @BlaineBublitz on Twitter") | [](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter") | [](https://twitter.com/mathias "Follow @mathias on Twitter") |
|
||||||
|
|---|---|---|
|
||||||
|
| [Blaine Bublitz](http://www.iceddev.com/) | [Kit Cambridge](http://kitcambridge.be/) | [Mathias Bynens](http://mathiasbynens.be/) |
|
||||||
50
src/convert/lodash/lodash.forown/index.js
Normal file
50
src/convert/lodash/lodash.forown/index.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
|
||||||
|
* Build: `lodash modularize modern exports="npm" -o ./npm/`
|
||||||
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
||||||
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||||
|
* Available under MIT license <http://lodash.com/license>
|
||||||
|
*/
|
||||||
|
var baseCreateCallback = require('./../lodash._basecreatecallback'),
|
||||||
|
keys = require('./../lodash.keys'),
|
||||||
|
objectTypes = require('./../lodash._objecttypes');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over own enumerable properties of an object, executing the callback
|
||||||
|
* for each property. The callback is bound to `thisArg` and invoked with three
|
||||||
|
* arguments; (value, key, object). Callbacks may exit iteration early by
|
||||||
|
* explicitly returning `false`.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @memberOf _
|
||||||
|
* @type Function
|
||||||
|
* @category Objects
|
||||||
|
* @param {Object} object The object to iterate over.
|
||||||
|
* @param {Function} [callback=identity] The function called per iteration.
|
||||||
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
||||||
|
* @returns {Object} Returns `object`.
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
|
||||||
|
* console.log(key);
|
||||||
|
* });
|
||||||
|
* // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
|
||||||
|
*/
|
||||||
|
var forOwn = function(collection, callback, thisArg) {
|
||||||
|
var index, iterable = collection, result = iterable;
|
||||||
|
if (!iterable) return result;
|
||||||
|
if (!objectTypes[typeof iterable]) return result;
|
||||||
|
callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
|
||||||
|
var ownIndex = -1,
|
||||||
|
ownProps = objectTypes[typeof iterable] && keys(iterable),
|
||||||
|
length = ownProps ? ownProps.length : 0;
|
||||||
|
|
||||||
|
while (++ownIndex < length) {
|
||||||
|
index = ownProps[ownIndex];
|
||||||
|
if (callback(iterable[index], index, collection) === false) return result;
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = forOwn;
|
||||||
22
src/convert/lodash/lodash.forown/package.json
Normal file
22
src/convert/lodash/lodash.forown/package.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "lodash.forown",
|
||||||
|
"version": "2.3.0",
|
||||||
|
"description": "The Lo-Dash function `_.forOwn` as a Node.js module generated by lodash-cli.",
|
||||||
|
"homepage": "http://lodash.com/custom-builds",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": ["functional", "lodash", "lodash-modularized", "server", "util"],
|
||||||
|
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"contributors": [
|
||||||
|
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||||
|
"Blaine Bublitz <blaine@iceddev.com> (http://www.iceddev.com/)",
|
||||||
|
"Kit Cambridge <github@kitcambridge.be> (http://kitcambridge.be/)",
|
||||||
|
"Mathias Bynens <mathias@qiwi.be> (http://mathiasbynens.be/)"
|
||||||
|
],
|
||||||
|
"bugs": "https://github.com/lodash/lodash-cli/issues",
|
||||||
|
"repository": { "type": "git", "url": "https://github.com/lodash/lodash-cli.git" },
|
||||||
|
"dependencies": {
|
||||||
|
"lodash._basecreatecallback": "~2.3.0",
|
||||||
|
"lodash.keys": "~2.3.0",
|
||||||
|
"lodash._objecttypes": "~2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user