handsontable
Version:
Handsontable is a JavaScript Data Grid available for React, Angular and Vue.
244 lines (230 loc) • 8.94 kB
JavaScript
import "core-js/modules/es.array.push.js";
import "core-js/modules/esnext.iterator.constructor.js";
import "core-js/modules/esnext.iterator.for-each.js";
import "core-js/modules/esnext.iterator.map.js";
import { staticRegister } from "../../../utils/staticRegister.mjs";
import { isUndefined } from "../../../helpers/mixed.mjs";
import { toSingleLine } from "../../../helpers/templateLiteralTag.mjs";
import { warn } from "../../../helpers/console.mjs";
import { isObjectEqual } from "../../../helpers/object.mjs";
import { PLUGIN_KEY } from "../formulas.mjs";
import { DEFAULT_LICENSE_KEY, DEFAULT_SETTINGS, getEngineSettingsWithDefaultsAndOverrides } from "./settings.mjs";
/**
* Prepares and returns the collection for the engine relationship with the HoT instances.
*
* @returns {Map}
*/
function getEngineRelationshipRegistry() {
const registryKey = 'engine_relationship';
const pluginStaticRegistry = staticRegister(PLUGIN_KEY);
if (!pluginStaticRegistry.hasItem(registryKey)) {
pluginStaticRegistry.register(registryKey, new Map());
}
return pluginStaticRegistry.getItem(registryKey);
}
/**
* Prepares and returns the collection for the engine shared usage.
*
* @returns {Map}
*/
function getSharedEngineUsageRegistry() {
const registryKey = 'shared_engine_usage';
const pluginStaticRegistry = staticRegister(PLUGIN_KEY);
if (!pluginStaticRegistry.hasItem(registryKey)) {
pluginStaticRegistry.register(registryKey, new Map());
}
return pluginStaticRegistry.getItem(registryKey);
}
/**
* Setups the engine instance. It either creates a new (possibly shared) engine instance, or attaches
* the plugin to an already-existing instance.
*
* @param {Handsontable} hotInstance Handsontable instance.
* @returns {null|object} Returns the engine instance if everything worked right and `null` otherwise.
*/
export function setupEngine(hotInstance) {
const hotSettings = hotInstance.getSettings();
const pluginSettings = hotSettings[PLUGIN_KEY];
const engineConfigItem = pluginSettings === null || pluginSettings === void 0 ? void 0 : pluginSettings.engine;
if (pluginSettings === true) {
return null;
}
if (isUndefined(engineConfigItem)) {
return null;
}
// `engine.hyperformula` or `engine` is the engine class
if (typeof engineConfigItem.hyperformula === 'function' || typeof engineConfigItem === 'function') {
var _engineConfigItem$hyp;
return registerEngine((_engineConfigItem$hyp = engineConfigItem.hyperformula) !== null && _engineConfigItem$hyp !== void 0 ? _engineConfigItem$hyp : engineConfigItem, hotSettings, hotInstance);
// `engine` is the engine instance
} else if (typeof engineConfigItem === 'object' && isUndefined(engineConfigItem.hyperformula)) {
const engineRelationship = getEngineRelationshipRegistry();
const sharedEngineUsage = getSharedEngineUsageRegistry().get(engineConfigItem);
if (!engineRelationship.has(engineConfigItem)) {
engineRelationship.set(engineConfigItem, []);
}
engineRelationship.get(engineConfigItem).push(hotInstance);
if (sharedEngineUsage) {
sharedEngineUsage.push(hotInstance.guid);
}
if (!engineConfigItem.getConfig().licenseKey) {
engineConfigItem.updateConfig({
licenseKey: DEFAULT_LICENSE_KEY
});
}
if (engineConfigItem.getConfig().leapYear1900 !== DEFAULT_SETTINGS.leapYear1900 || isObjectEqual(engineConfigItem.getConfig().nullDate, DEFAULT_SETTINGS.nullDate) === false) {
warn(toSingleLine`If you use HyperFormula with Handsontable, keep the default \`leapYear1900\` and \`nullDate\`
settings. Otherwise, HyperFormula's dates may not sync correctly with Handsontable's dates.`);
}
return engineConfigItem;
}
return null;
}
/**
* Registers the engine in the global register and attaches the needed event listeners.
*
* @param {Function} engineClass The engine class.
* @param {object} hotSettings The Handsontable settings.
* @param {Handsontable} hotInstance Handsontable instance.
* @returns {object} Returns the engine instance.
*/
export function registerEngine(engineClass, hotSettings, hotInstance) {
const pluginSettings = hotSettings[PLUGIN_KEY];
const engineSettings = getEngineSettingsWithDefaultsAndOverrides(hotSettings);
const engineRegistry = getEngineRelationshipRegistry();
const sharedEngineRegistry = getSharedEngineUsageRegistry();
registerCustomFunctions(engineClass, pluginSettings.functions);
registerLanguage(engineClass, pluginSettings.language);
// Create instance
const engineInstance = engineClass.buildEmpty(engineSettings);
// Add it to global registry
engineRegistry.set(engineInstance, [hotInstance]);
sharedEngineRegistry.set(engineInstance, [hotInstance.guid]);
registerNamedExpressions(engineInstance, pluginSettings.namedExpressions);
// Add hooks needed for cross-referencing sheets
engineInstance.on('sheetAdded', () => {
engineInstance.rebuildAndRecalculate();
});
engineInstance.on('sheetRemoved', () => {
engineInstance.rebuildAndRecalculate();
});
return engineInstance;
}
/**
* Returns the list of the Handsontable instances linked to the specific engine instance.
*
* @param {object} engine The engine instance.
* @returns {Map<number, Handsontable>} Returns Map with Handsontable instances.
*/
export function getRegisteredHotInstances(engine) {
var _engineRegistry$get;
const engineRegistry = getEngineRelationshipRegistry();
const hotInstances = engineRegistry.size === 0 ? [] : Array.from((_engineRegistry$get = engineRegistry.get(engine)) !== null && _engineRegistry$get !== void 0 ? _engineRegistry$get : []);
return new Map(hotInstances.map(hot => [hot.getPlugin('formulas').sheetId, hot]));
}
/**
* Removes the HOT instance from the global register's engine usage array, and if there are no HOT instances left,
* unregisters the engine itself.
*
* @param {object} engine The engine instance.
* @param {string} hotInstance The Handsontable instance.
*/
export function unregisterEngine(engine, hotInstance) {
if (engine) {
const engineRegistry = getEngineRelationshipRegistry();
const engineHotRelationship = engineRegistry.get(engine);
const sharedEngineRegistry = getSharedEngineUsageRegistry();
const sharedEngineUsage = sharedEngineRegistry.get(engine);
if (engineHotRelationship && engineHotRelationship.includes(hotInstance)) {
engineHotRelationship.splice(engineHotRelationship.indexOf(hotInstance), 1);
if (engineHotRelationship.length === 0) {
engineRegistry.delete(engine);
}
}
if (sharedEngineUsage && sharedEngineUsage.includes(hotInstance.guid)) {
sharedEngineUsage.splice(sharedEngineUsage.indexOf(hotInstance.guid), 1);
if (sharedEngineUsage.length === 0) {
sharedEngineRegistry.delete(engine);
engine.destroy();
}
}
}
}
/**
* Registers the custom functions for the engine.
*
* @param {Function} engineClass The engine class.
* @param {Array} customFunctions The custom functions array.
*/
export function registerCustomFunctions(engineClass, customFunctions) {
if (customFunctions) {
customFunctions.forEach(func => {
const {
name,
plugin,
translations
} = func;
try {
engineClass.registerFunction(name, plugin, translations);
} catch (e) {
warn(e.message);
}
});
}
}
/**
* Registers the provided language for the engine.
*
* @param {Function} engineClass The engine class.
* @param {object} languageSetting The engine's language object.
*/
export function registerLanguage(engineClass, languageSetting) {
if (languageSetting) {
const {
langCode
} = languageSetting;
try {
engineClass.registerLanguage(langCode, languageSetting);
} catch (e) {
warn(e.message);
}
}
}
/**
* Registers the provided named expressions in the engine instance.
*
* @param {object} engineInstance The engine instance.
* @param {Array} namedExpressions Array of the named expressions to be registered.
*/
export function registerNamedExpressions(engineInstance, namedExpressions) {
if (namedExpressions) {
engineInstance.suspendEvaluation();
namedExpressions.forEach(namedExp => {
const {
name,
expression,
scope,
options
} = namedExp;
try {
engineInstance.addNamedExpression(name, expression, scope, options);
} catch (e) {
warn(e.message);
}
});
engineInstance.resumeEvaluation();
}
}
/**
* Sets up a new sheet.
*
* @param {object} engineInstance The engine instance.
* @param {string} sheetName The new sheet name.
* @returns {*}
*/
export function setupSheet(engineInstance, sheetName) {
if (isUndefined(sheetName) || !engineInstance.doesSheetExist(sheetName)) {
sheetName = engineInstance.addSheet(sheetName);
}
return sheetName;
}