shark-mvc
Version:
Shark-MVC is a small MVC framework born to make our small JS apps easier to build.
1,169 lines (988 loc) • 262 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("jquery"), require("mustache"), require("moment"), require("preact"), require("preact-html-converter"));
else if(typeof define === 'function' && define.amd)
define(["jquery", "mustache", "moment", "preact", "preact-html-converter"], factory);
else if(typeof exports === 'object')
exports["shark-mvc"] = factory(require("jquery"), require("mustache"), require("moment"), require("preact"), require("preact-html-converter"));
else
root["shark-mvc"] = factory(root["$"], root["Mustache"], root["Moment"], root["preact"], root["preact-html-converter"]);
})(this, (__WEBPACK_EXTERNAL_MODULE_jquery__, __WEBPACK_EXTERNAL_MODULE_mustache__, __WEBPACK_EXTERNAL_MODULE_moment__, __WEBPACK_EXTERNAL_MODULE_preact__, __WEBPACK_EXTERNAL_MODULE_preact_html_converter__) => {
return /******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./node_modules/uuid/dist/esm-browser/regex.js"
/*!*****************************************************!*\
!*** ./node_modules/uuid/dist/esm-browser/regex.js ***!
\*****************************************************/
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i);
/***/ },
/***/ "./node_modules/uuid/dist/esm-browser/rng.js"
/*!***************************************************!*\
!*** ./node_modules/uuid/dist/esm-browser/rng.js ***!
\***************************************************/
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (/* binding */ rng)
/* harmony export */ });
// Unique ID creation requires a high quality random # generator. In the browser we therefore
// require the crypto API and do not support built-in fallback to lower quality random number
// generators (like Math.random()).
var getRandomValues;
var rnds8 = new Uint8Array(16);
function rng() {
// lazy load so that environments that need to polyfill have a chance to do so
if (!getRandomValues) {
// getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. Also,
// find the complete implementation of crypto (msCrypto) on IE11.
getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);
if (!getRandomValues) {
throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');
}
}
return getRandomValues(rnds8);
}
/***/ },
/***/ "./node_modules/uuid/dist/esm-browser/stringify.js"
/*!*********************************************************!*\
!*** ./node_modules/uuid/dist/esm-browser/stringify.js ***!
\*********************************************************/
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _validate_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./validate.js */ "./node_modules/uuid/dist/esm-browser/validate.js");
/**
* Convert array of 16 byte values to UUID string format of the form:
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
*/
var byteToHex = [];
for (var i = 0; i < 256; ++i) {
byteToHex.push((i + 0x100).toString(16).substr(1));
}
function stringify(arr) {
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
// Note: Be careful editing this code! It's been tuned for performance
// and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
var uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one
// of the following:
// - One or more input array values don't map to a hex octet (leading to
// "undefined" in the uuid)
// - Invalid input values for the RFC `version` or `variant` fields
if (!(0,_validate_js__WEBPACK_IMPORTED_MODULE_0__["default"])(uuid)) {
throw TypeError('Stringified UUID is invalid');
}
return uuid;
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (stringify);
/***/ },
/***/ "./node_modules/uuid/dist/esm-browser/v4.js"
/*!**************************************************!*\
!*** ./node_modules/uuid/dist/esm-browser/v4.js ***!
\**************************************************/
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _rng_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./rng.js */ "./node_modules/uuid/dist/esm-browser/rng.js");
/* harmony import */ var _stringify_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./stringify.js */ "./node_modules/uuid/dist/esm-browser/stringify.js");
function v4(options, buf, offset) {
options = options || {};
var rnds = options.random || (options.rng || _rng_js__WEBPACK_IMPORTED_MODULE_0__["default"])(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = rnds[6] & 0x0f | 0x40;
rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided
if (buf) {
offset = offset || 0;
for (var i = 0; i < 16; ++i) {
buf[offset + i] = rnds[i];
}
return buf;
}
return (0,_stringify_js__WEBPACK_IMPORTED_MODULE_1__["default"])(rnds);
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (v4);
/***/ },
/***/ "./node_modules/uuid/dist/esm-browser/validate.js"
/*!********************************************************!*\
!*** ./node_modules/uuid/dist/esm-browser/validate.js ***!
\********************************************************/
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _regex_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./regex.js */ "./node_modules/uuid/dist/esm-browser/regex.js");
function validate(uuid) {
return typeof uuid === 'string' && _regex_js__WEBPACK_IMPORTED_MODULE_0__["default"].test(uuid);
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (validate);
/***/ },
/***/ "./src/Jaw.js"
/*!********************!*\
!*** ./src/Jaw.js ***!
\********************/
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _Shark__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Shark */ "./src/Shark.js");
/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Utils */ "./src/Utils.js");
/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! jquery */ "jquery");
/* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_2__);
/**
* Represents an element of the MVC model. Actually could be a Component, a View, a PageTemplate or a Controller, not a Model.
* <br />When defining a Jaw, you could define some "default" methods that would be used by the Shark event model.
* <br /><br />One particular method is <strong><code>beforeAutoOpen</code></strong> that is called passing the settings object that will be used for the View opening. In this method you can edit this object adding or changing parameters, and the, if you return the object as the function return value, then Shark will use the new settings for managing the open flow.
*
* @constructor
* @param {string} className - The id that would identify this Jaw.
*
*/
class Jaw extends EventTarget {
#activeComponents = [];
get activeComponents () {
return this.#activeComponents;
}
constructor (className, jawDefinition) {
super();
if (jawDefinition) {
console.error("Yous must not pass jawDefinition, it's no more supported and it does not have any effect.");
}
if (typeof className !== "string") {
throw (new Error("Jaw id must be passed"));
}
Object.defineProperty(this, "className", {
configurable: false,
enumerable: true,
value: className,
writable: false,
});
Object.defineProperty(this, "id", {
configurable: false,
enumerable: true,
value: className,
writable: false,
});
Object.defineProperty(this, "addComponents", {
configurable: false,
value: this.addComponents,
writable: false,
});
this.__renderFields = [];
//This is set to true by Shark, when the "init" method is called.
// But should be set in the init method and the sub-classes should call "super.init"...
this.isInitialized = false;
// this.templateRoute = "";//Will be deprecated
this.currentOpenOptions = {};
this.route = null;
// this.store = {}; //Will be deprecated
this.$store = {};
//In this object there should be only "default" methods availalbe throughout all the Shark Jaws.
//Actually they are bound to the use of Mustache.
this.__store = {
getLabel: function () {
return function (text, render) {
//console.info(text, render);
if (text.indexOf("{{") > -1) {
text = render(text);
}
return render(_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.labels.labels[text]);
};
}
};
this.__activeComponents = [];
this.__autoBoundProperties = [];
this.__childTemplates = {};
this.__dynamicDataBindings = {};
this.__listeners = {};
this.__methods = {};
this.__viewBinds = {};
this.__viewBinds2 = {};
this.__storeHashMap = new Map();
}
/**
* Add a Component to this Jaw. The added Component will be used to render and manage sub-parts of the view.
*
* @returns {object} The complete list of active Components.
* @static
*
* @param {object} component - The Component to add as a string or Component object reference.
*/
addComponent (component) {
if (typeof component === "undefined") {
throw new Error("Jaw.addComponent: You can't pass this type of data (must be a component)");
}
if (typeof component === "string") {
console.error("You should avoid to use string to add component. The right way is to pass the Component instance.");
component = _Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__components[component];
}
if (_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark?.settings?.enableExperimentalComponentProps) {
if (!this.#activeComponents.find(item => item.id === component.id)) {
const componentInstance = new component.constructor();
// componentInstance.props = componentAttributes;
this.#activeComponents.push(componentInstance);
}
}
else {
if (!this.#activeComponents.find(item => item.id === component.id)) {
this.#activeComponents.push(component);
}
}
return this.#activeComponents;
}
/**
* Add an array of Component to this Jaw. The added Components will be used to render and/or manage sub-parts of the Jaw.
*
* @returns {object} The complete list of active Components.
*
* @param {array} components - The list of Components to add as a string or Component object reference.
*/
addComponents (components) {
if (!Array.isArray(components)) {
throw new Error("Jaw.addComponents: You can't pass this type of data (must be an Array)");
}
components.forEach(item => {
this.addComponent(item);
});
return this.#activeComponents;
}
get (itemName) {
if (arguments.length > 1) {
console.warn("WARNING!! The jaw.get() method has deprecated the 'forcedType' parameter, and now works only on jaw-local data");
}
if (itemName === undefined) {
return this.__store;
}
else if (Array.isArray(itemName)) {
const returnValues = [];
itemName.forEach(item => {
const returnValue = this.__store[item];
returnValues.push(returnValue);
});
return returnValues;
}
else {
return this.__store[itemName];
}
}
/**
* A method called when a Jaw is used for the first time. Should be overrided.
*/
init () {
this.trace("The init method for '" + this.className + "' Jaw has not been implemented. (I'm just setting this as Initialized)", "info");
this.isInitialized = true;
}
_extractComponentsValidMethodList (componentList, processedComponentList, notProcessedComponentList) {
const validMethodList = [];
componentList.forEach(component => {
if (processedComponentList.indexOf(component.id) === -1) {
validMethodList.push(...this._extractValidMethodList(component));
processedComponentList.push(component.id);
}
else {
notProcessedComponentList.push("= NO " + component.id);
}
// if (component.__activeComponents.length > 0) {
if (component.activeComponents.length > 0) {
// validMethodList.push(...this._extractComponentsValidMethodList(component.__activeComponents, processedComponentList, notProcessedComponentList));
validMethodList.push(...this._extractComponentsValidMethodList(component.activeComponents, processedComponentList, notProcessedComponentList));
}
});
return validMethodList;
}
_extractValidMethodList (targetObject) {
const validMethodList = [];
for (var currProp in targetObject) {
if (typeof targetObject[currProp] === "function") {
// if (currProp.indexOf("Handler") > -1) {//Dave: Dovrebbe essere solo in fondo, non "> -1"
if (currProp.slice(-7) === "Handler") {//Dave: Dovrebbe essere solo in fondo, non "> -1"
// Attenzione che potrebbero esserci nomi duplicati per target diversi??
if (!validMethodList.find(item => item.handlerName === currProp)) {
validMethodList.push({handlerName: currProp, handlerHolder: targetObject, _from: "_extractValidMethodList-targetObject" });
}
}
}
}
if (targetObject.__proto__ instanceof Jaw) {
const referenceObject = targetObject.__proto__;
const propertyList = Object.getOwnPropertyNames(targetObject.__proto__);
propertyList.forEach(currProp => {
if (typeof referenceObject[currProp] === "function" && currProp !== "constructor") {
// if (currProp.indexOf("Handler") > -1) {
if (currProp.slice(-7) === "Handler") {
// Attenzione che potrebbero esserci nomi duplicati per target diversi??
if (!validMethodList.find(item => item.handlerName === currProp)) {
validMethodList.push({handlerName: currProp, handlerHolder: targetObject, _from: "_extractValidMethodList-referenceObject" });
}
}
}
});
validMethodList.push(...this._extractValidMethodList(targetObject.__proto__));
}
return validMethodList;
}
_initComponents (componentList = null, processedComponentList = []) {
if (componentList === null) {
componentList = this.#activeComponents;
// componentList = this.__activeComponents;
}
componentList.forEach(function (component) {
if (!this.#activeComponents.find(item => item.id === component.id)) {
// if (!this.__activeComponents.find(item => item.id === component.id)) {
this.#activeComponents.push(component);
// this.__activeComponents.push(component);
}
if (!processedComponentList.includes(component.id)) {
if (typeof component.jawInit === "function") {
component.jawInit.call(component, {jaw: currJaw});
}
if (typeof component.init === "function" && !component.isInitialized) {
component.init.call(component);
component.isInitialized = true;
}
processedComponentList.push(component.id);
if (component.activeComponents.length > 0) {
// if (component.__activeComponents.length > 0) {
this._initComponents(component.activeComponents, processedComponentList);
// this._initComponents(component.__activeComponents, processedComponentList);
}
}
}, this);
}
/**
* Add data to this Jaw internal data store. This data will automatically passed to renderer, so any value saved with this metod will be rendered if its name is printed in html template.
*
* @param {string|object|array} nameOrItems - The name of the object that will contain data. If an empty string is passed, the data as merged into the <i>root</i> data object, usefull to add data that doesn't need to be nested. If an object is passed then every property of the object is cloned into the Jaw internal store. If an array of objects with name and value propeties is passed, then each item in the array will be added to the internal store.
* @param {any} value - The value to store. Used only if the <code>nameOrItems</code> parameter is a string.
*/
set (nameOrItems, value) {
if (arguments.length > 2) {
console.error("WARNING!! The Jaw.set() method has been called with more than 2 parameters.");
}
let changeCount = 0;
if (Array.isArray(nameOrItems)) {
//Dave.ToDo: Must be replaced with forEach
var loopLength = nameOrItems.length;
for (var i = 0; i < loopLength; i++) {
var currElement = nameOrItems[i];
if (currElement.name !== "") {
changeCount += this.__singleSet(currElement.name, currElement.value);
}
else {
throw new Error("Can't pass an empty 'name' for a property");
}
}
}
else if (typeof nameOrItems === "object") {
for (let currProp in nameOrItems) {
changeCount += this.__singleSet(currProp, nameOrItems[currProp]);
}
}
else if (typeof nameOrItems === "string") {
if (nameOrItems !== "") {
changeCount += this.__singleSet(nameOrItems, value);
}
else {
throw new Error("Can't pass an empty 'name' for a property");
}
}
else {
console.error("Jaw.set: This kind of data could not be used.");
}
_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.trace({ __store: this.__store, nameOrItems, value, type: typeof nameOrItems}, _Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.TRACE_LOG)
if (_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.settings.enableJawStoreAutoRender && changeCount > 0/* && autoBoundCount > 0 && notParsedAutoSections.length > 0*/) {
_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.trace(`%c SharkMVC: AUTORENDER TRIGGERED by JAW (${changeCount})`, "background-color: red; color: white; font-weight: bold;", {nameOrItems, value}, _Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.TRACE_INFO);
_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.render();
_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.trace("%c SharkMVC: AUTORENDER COMPLETED by JAW", "background-color: red; color: white; font-weight: bold;", _Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.TRACE_INFO);
}
}
__singleSet (name, value) {
this.__store[name] = value;
const that = this;
if (!this.$store.hasOwnProperty(name)) {
Object.defineProperty(this.$store, name, {
configurable: true,
enumerable: true,
get () {
// console.slog("get", name, this, that);
return that.__store[name];
},
set (newValue) {
const oldValue = that.__store[name];
// console.time("hash")
// console.warn("Cambiato / hash (this, that):", newValue !== that.__store[name], Utils.computeHash(newValue) !== Utils.computeHash(that.__store[name]), this, that);
// console.timeEnd("hash")
that.__store[name] = newValue;
if (_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.settings.enableJawStoreAutoRender && _Utils__WEBPACK_IMPORTED_MODULE_1__["default"].computeHash(newValue) !== _Utils__WEBPACK_IMPORTED_MODULE_1__["default"].computeHash(oldValue)) {
_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.render();
}
},
});
//This will be deprecated
// Object.defineProperty(this.store, name, {
// configurable: true,
// enumerable: true,
// get () {
// // console.slog("get", name, this, that);
// return that.__store[name];
// },
// set (newValue) {
// const oldValue = that.__store[name];
// // console.time("hash")
// // console.warn("Cambiato / hash (this, that):", newValue !== that.__store[name], Utils.computeHash(newValue) !== Utils.computeHash(that.__store[name]), this, that);
// // console.timeEnd("hash")
// that.__store[name] = newValue;
// if (Shark.settings.enableJawStoreAutoRender && Utils.computeHash(newValue) !== Utils.computeHash(oldValue)) {
// Shark.render();
// }
// },
// });
}
let oldValue = this.__store[name];
let oldValueHash = oldValue;
let newValueHash = "";
let valueChanged = oldValueHash !== newValueHash;
if (oldValue !== undefined) {
try {
oldValueHash = _Utils__WEBPACK_IMPORTED_MODULE_1__["default"].computeHash(oldValue);
}
catch (err) {
oldValueHash = oldValue;
}
}
if (value !== undefined) {
try {
newValueHash = _Utils__WEBPACK_IMPORTED_MODULE_1__["default"].computeHash(value);
}
catch (err) {
newValueHash = value;
}
}
//Dave: Questa roba qui sotto è da sistemare, ma ha il potenziale per risolvere per sempre l'autorender.
if (this.__storeHashMap.has(name)) {
valueChanged = this.__storeHashMap.get(name) !== newValueHash;
// Shark.trace(`%cHo un vecchio hash (sul set)!!!${this.__storeHashMap.get(name)}, ${newValueHash}`, `background-color: ${valueChanged ? "yellow" : "green"}; color: ${valueChanged ? "red" : "white"}; font-weight: bold; padding: 3px 6px;`, Shark.TRACE_INFO);
}
else {
valueChanged = oldValueHash !== newValueHash
}
this.__storeHashMap.set(name, newValueHash);
this.__store[name] = value;
//Shark.trace("%cValueChanged", "background-color: #dddddd; boder: 2px solid lime; color: purple; padding: 5px 10px;", valueChanged, { newValueHash }, Shark.TRACE_INFO);
return valueChanged;
}
}
/**
* Makes this Jaw dispatch an event and, optionally, send data with it.
*
* @param {string} eventName - The name of the event that will be dispatched.
* @param {object} [eventData = undefined] - The optional data that will be deliverd to the event listener.
*/
Jaw.prototype.emit = function (eventName, eventData) {
return jquery__WEBPACK_IMPORTED_MODULE_2___default()(this).trigger(eventName, eventData);
};
// /**
// * One of the Shark core feature: automatically match jaw's methods based on the naming selector_eventnameHandler. It both works whit class and id CSS selector.
// * It also assign listeners using name from registered controllers if a function named controllername_eventnameHandler exists.
// * You should call this manually when you know your markup is rendered.
// *
// * @deprecated you should use the [smartListen method]{@link Jaw#smartListen}.
// */
// Jaw.prototype.initialize = function () {
// this.smartListen();
// };
/**
* Permit to unbind a function as handler for an event dispatched by this Jaw.
*
* @param {string} eventName - The name of the event that the handler will be removed from.
* @param {function} eventHandler - The function that will be removed as event handler.
*/
Jaw.prototype.off = function (eventName, eventHandler) {
return jquery__WEBPACK_IMPORTED_MODULE_2___default()(this).off(eventName, eventHandler);
};
/**
* Permit to bind a function as handler for an event dispatched by this Jaw.
*
* @param {string} eventName - The name of the event that the handler will be bound to.
* @param {function} eventHandler - The function that will handle the event when dispatched.
*/
Jaw.prototype.on = function (eventName, eventHandler) {
return jquery__WEBPACK_IMPORTED_MODULE_2___default()(this).on(eventName, eventHandler);
};
/**
* Add multiple data to be rendered by TemplateRenderer object and just after adding the data it render the current page.
*
* @param {object|array} elements - This could be an Array of objects with <code>name</code> and <code>value</code> properties or an object with several properties. In the latter case each property will be copied "as is" in the data to be rendered.
* @deprecated - This method has been definitively replaced by the [set method]{@link Jaw#set}.
*/
// Jaw.prototype.setRenderElements = function (elements) {
// console.warn("Jaw.prototype.setRenderElements: This method MUST NOT be used, replace it with 'set' method.");
// this.set(elements, null, {}, true);
// };
Jaw.__getNodeDepth = function (node) {
let depth = 0;
while (node.nodeName != 'BODY') {
node = node.parentNode;
depth++;
}
return depth;
}
Jaw.prototype.smartListen2 = function (container, selfAssign) {
const validMethodList = [];
for (let currProp in this) {
if (typeof this[currProp] === "function") {
if (currProp.indexOf("Handler") > -1) {
validMethodList.push({
handlerName: currProp,
handlerHolder: this,
});
}
}
} //Dave.ToDo: Questa roba andrebbe fatta all'init, così i validListener li valuto solo una volta e poi restano
// this.__activeComponents.forEach(component => {
this.activeComponents.forEach(component => {
for (let currProp in component) {
if (typeof component[currProp] === "function") {
if (currProp.indexOf("Handler") > -1) {
validMethodList.push({
handlerName: currProp,
handlerHolder: component,
});
}
}
}
});
//Dave.ToDo: Dovrei gestire l'autolistener anche per gli eventi delle istanze dei model
//Dave.ToDo: Dovrei gestire l'autolistener anche per gli eventi dei model (quindi su tutte le istanze in una volta)
//profileEditBtn_clickHandler
//userPageView_profileEditBtn
validMethodList.forEach(currMethod => {
let currHandlerName = currMethod.handlerName;
let eventTargetName;
//Dave.Warn: In teoria non dovrei avere bisogno di questo controllo, perché l'_ ci deve essere per forza...
if (currHandlerName.lastIndexOf("_") > -1) {
eventTargetName = currHandlerName.substr(0, currHandlerName.lastIndexOf("_"));
}
else {
eventTargetName = currHandlerName;
}
let targetId = /*currMethod.handlerHolder.className + "_" +*/ eventTargetName;
let selector = "#" + targetId;
let shouldAssign = true;
let targetNode = document.querySelector(selector);
let targetType = "singleNode";
if (!targetNode) {
selector = "." + targetId;
targetNode = document.querySelectorAll(selector);
//There's no nodes with requested class
if (targetNode.length === 0) {
//Let's check if the name isn't DOM selector but Shark controller name.
if (_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__controllers[eventTargetName]) {
targetType = "manager";
}
else {
//Ok, no DOM, no controller... definetively we shouldn't assign this listener.
shouldAssign = false;
}
}
else {
// targetType = targetNode.length === 1 ? "singleNode" : "multiNode";//Dave.Warn: Ma se è una classe, quindi potenzialmente multi, devo valutare se ce n'è più di uno o no?
targetType = "multiNode";
const depthList = [];
targetNode.forEach(item => {
depthList.push(Jaw.__getNodeDepth(item));
});
const minDepth = Math.min(...depthList);
currMethod.depthList = depthList;
currMethod.minDepth = minDepth;
}
}
currMethod.targetType = targetType;
if (shouldAssign) {
let addNeeded = false;
if (targetType === "manager") {
//The code below handle only the assignment to managers (controllers in Shark) and not to DOM items.
if (!currMethod.handlerHolder.__listeners[eventTargetName]) {
addNeeded = true;
currMethod.handlerHolder.__listeners[eventTargetName] = {};
}
else if (!currMethod.handlerHolder.__listeners[eventTargetName][eventName]) {
addNeeded = true;
}
else if (currMethod.handlerHolder.type === "component") {
addNeeded = true;
}
if (addNeeded) {
this.trace("Auto-listener assigned to the '" + _Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__controllers[eventTargetName].className + "' Controller for the '" + eventName + "' event.", "log");
//this.__listeners[eventTargetName][eventName] = "autoListener";
currMethod.handlerHolder.__listeners[eventTargetName][eventName] = { handler: currHandler, target: "controller", type: "autoListener" };
if (currMethod.handlerHolder.type === "component") {
jquery__WEBPACK_IMPORTED_MODULE_2___default()(_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__controllers[eventTargetName]).off(eventName, currMethod.handlerHolder[currHandler]);//, currMethod.handlerHolder[currHandler]);//Dave.Warn: Non è meglio un off generico?
}
jquery__WEBPACK_IMPORTED_MODULE_2___default()(_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__controllers[eventTargetName]).on(eventName, currMethod.handlerHolder[currHandler]);
}
}
else {
}
}
}, this);
this.trace("Jaw.prototype.smartListen2: validMethodList", validMethodList, "log");
}
/**
* This is an <i>internal</i> method, not meant to be used by the user.
* One of the Shark core feature: automatically match jaw's methods based on this naming convention: selector_eventnameHandler.
* It both works whit class and id CSS selector.
* It also assign listeners using name from registered controllers if a function named controllername_eventnameHandler exists.
* You should call this manually when you know your markup is rendered.
*
* @param {string|jQuerySelection} [container] - If a CSS selector or a DOM element selected with jQuery is passed, the smartListen function will parse only that element and its childs. Useful if you just need to parse a freshly-rendered container.
*/
Jaw.prototype.smartListen = function (container, selfAssign) {
if (typeof selfAssign === "undefined") {
selfAssign = false;
}
var validMethodList = this._extractValidMethodList(this);
this.trace("smartListen", {__validMethodList: this.__validMethodList, jaw: this}, "log");
const processedComponentList = [];
const notProcessedComponentList = [];
//Dave.ToDo: Questa roba andrebbe fatta all'init, così i validListener li valuto solo una volta e poi restano
// validMethodList.push(...this._extractComponentsValidMethodList(this.__activeComponents, processedComponentList, notProcessedComponentList));
validMethodList.push(...this._extractComponentsValidMethodList(this.activeComponents, processedComponentList, notProcessedComponentList));
this.trace("Jaw.prototype.smartListen: data", {processedComponentList, __activeComponents: this.activeComponents, notProcessedComponentList, validMethodList}, "log");
this.trace("Jaw.prototype.smartListen: validMethodList", validMethodList, "log");
this.__validMethodList = validMethodList;
validMethodList.forEach(function (currMethod) {
var currHandler = currMethod.handlerName;
var shouldAssign = true;
var eventTarget;//Non è il target, ma il jaw direi...
if (currHandler.indexOf("_") > -1) {
eventTarget = currHandler.substr(0, currHandler.indexOf("_"));
}
else {
eventTarget = currHandler;
}
var targetId = currMethod.handlerHolder.className + "_" + eventTarget;
var selector = "#" + targetId;
var eventName = currHandler.substring(currHandler.indexOf("_") + 1, currHandler.indexOf("Handler"));
var $target = jquery__WEBPACK_IMPORTED_MODULE_2___default()(selector);
if (container) {
var $container = jquery__WEBPACK_IMPORTED_MODULE_2___default()(container);
//Dave.Warn: Questa roba qui sotto è una schifezza necessaria per gestire l'assegnazione nel caso il container sia il target stesso.
if (selfAssign && "#" + eventTarget === container) {
this.trace($target, $container, "log");
$target = $container;
}
else {
$target = $container.find(selector);
if ($target.length === 0) {
selector = "#" + eventTarget;
$target = $container.find(selector);
}
if ($target.length === 0) {
selector = "." + targetId;
$target = $container.find(selector);
}
if ($target.length === 0) {
selector = "." + eventTarget;
$target = $container.find(selector);
}
//Dave.Warning: Non ha senso fare questa cosa sotto, se no l'if successivo è sempre falso poiché il selector è necessariamente lo stesso
/*if ($target.length === 0) {
$target = $container;
}*/
if ($target.selector !== $container.selector && $target.closest($container).length === 0) {
shouldAssign = false;
}
}
}
else {
if ($target.length === 0) {
selector = "#" + eventTarget;
$target = jquery__WEBPACK_IMPORTED_MODULE_2___default()(selector);
}
if ($target.length === 0) {
selector = "." + targetId;
$target = jquery__WEBPACK_IMPORTED_MODULE_2___default()(selector);
}
if ($target.length === 0) {
selector = "." + eventTarget;
$target = jquery__WEBPACK_IMPORTED_MODULE_2___default()(selector);
}
}
if (shouldAssign) {
var addNeeded = false;
if ($target.length > 0) {
//Dave.Warn: I'm using this "internal" jQUery method that is quite dangerous because it could disappear without notice. It should be replaced by something mine.
//Dave.Warn: Questa roba qui sotto mica mi piace troppo e non sono sicuro del perché non stia "ereditando" __listeners
if (!currMethod.handlerHolder.__listeners) {
currMethod.handlerHolder.__listeners = {};
}
if (!currMethod.handlerHolder.__listeners[selector]) {
addNeeded = true;
currMethod.handlerHolder.__listeners[selector] = {};
}
else if (!currMethod.handlerHolder.__listeners[selector][eventName]) {
addNeeded = true;
}
else if (currMethod.handlerHolder.type === "component") {
addNeeded = true;
}
if (addNeeded) {
var that = this;
this.trace("Auto-listener assigned to '" + selector + "' item of the '" + currMethod.handlerHolder.className + "' " + currMethod.handlerHolder.type + " for the '" + eventName + "' event.", "log");
var sharkHandler = function (event) {
// event.data.handler.call(currMethod.handlerHolder, event, $(this).data());//Questo "data()" dovrebbe servire a passare i dataset
event.data.handler.call(currMethod.handlerHolder, event, this.dataset);
};
if (currMethod.handlerHolder.type === "component") {
//Dave.Warn: Qui "sharkHandler" viene creato nuovo ogni volta, per questo non posso usarlo per l'off, ma devo recupera l'handler assegnato in origine. Queta roba che ho scritto sotto, cmq, non funziona, devo farlo funzionare.
if (currMethod.handlerHolder.__listeners[selector][eventName] && currMethod.handlerHolder.__listeners[selector][eventName].handler) {
$target.off(eventName, currMethod.handlerHolder.__listeners[selector][eventName].handler);//, sharkHandler);//Dave.Warn: Non è meglio un off generico?
}
}
currMethod.handlerHolder.__listeners[selector][eventName] = {context: "jaw", handler: sharkHandler, originalHandler: currHandler, target: "DOM", type: "autoListener"};
$target.on(eventName, {handler: currMethod.handlerHolder[currHandler], handlerHolder: currMethod.handlerHolder}, sharkHandler);
//Dave.Warn: Questa roba qui sotto mica mi piace troppo e non sono sicuro del perché non stia "ereditando" __methods
if (!currMethod.handlerHolder.__methods) {
currMethod.handlerHolder.__methods = {};
}
if (currMethod.handlerHolder.__methods[currHandler]) {
if (currMethod.handlerHolder.__methods[currHandler].settings) {
if (currMethod.handlerHolder.__methods[currHandler].settings.smartUnlisten !== undefined) {
currMethod.handlerHolder.__listeners[selector][eventName].smartUnlisten = currMethod.handlerHolder.__methods[currHandler].settings.smartUnlisten;
}
}
}
}
}
else {
//The code below handle only the assignment to managers (controllers in Shark) and not to DOM items.
// Dave: Dobbiamo girare tutto: prima di tutto valutiamo se esiste un componente con i metodi giusti devo gestire gli eventi emessi dai Componenti
if (_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__controllers[eventTarget]) {
if (!currMethod.handlerHolder.__listeners[eventTarget]) {
addNeeded = true;
currMethod.handlerHolder.__listeners[eventTarget] = {};
}
else if (!currMethod.handlerHolder.__listeners[eventTarget][eventName]) {
addNeeded = true;
}
else if (currMethod.handlerHolder.type === "component") { // Questo dovrebbe essere "controller" non "component"
addNeeded = true;
}
if (addNeeded) {
this.trace("Auto-listener assigned to the '" + _Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__controllers[eventTarget].className + "' Controller for the '" + eventName + "' event.", "log");
//this.__listeners[eventTarget][eventName] = "autoListener";
currMethod.handlerHolder.__listeners[eventTarget][eventName] = {handler: currHandler, target: "controller", type: "autoListener"};
if (currMethod.handlerHolder.type === "component") {
jquery__WEBPACK_IMPORTED_MODULE_2___default()(_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__controllers[eventTarget]).off(eventName, currMethod.handlerHolder[currHandler]);//, currMethod.handlerHolder[currHandler]);//Dave.Warn: Non è meglio un off generico?
}
jquery__WEBPACK_IMPORTED_MODULE_2___default()(_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__controllers[eventTarget]).on(eventName, currMethod.handlerHolder[currHandler]);
}
}
else if (_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__components[eventTarget]) {
// console.slog("Assegnerei il listener al componente", {eventTarget, eventName});
if (!currMethod.handlerHolder.__listeners[eventTarget]) {
addNeeded = true;
currMethod.handlerHolder.__listeners[eventTarget] = {};
}
else if (!currMethod.handlerHolder.__listeners[eventTarget][eventName]) {
addNeeded = true;
}
else if (currMethod.handlerHolder.type === "component") {
addNeeded = true;
}
if (addNeeded) {
this.trace("Auto-listener assigned to the '" + _Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__components[eventTarget].className + "' Component for the '" + eventName + "' event.", "log");
currMethod.handlerHolder.__listeners[eventTarget][eventName] = {handler: currHandler, target: "component", type: "autoListener"};
currMethod.handlerHolder[currHandler] = currMethod.handlerHolder[currHandler].bind(currMethod.handlerHolder)
// if (currMethod.handlerHolder.type === "component") {
jquery__WEBPACK_IMPORTED_MODULE_2___default()(_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__components[eventTarget]).off(eventName, currMethod.handlerHolder[currHandler]);
// Shark.__components[eventTarget].removeEventListener(eventName, currMethod.handlerHolder[currHandler]);
// }
jquery__WEBPACK_IMPORTED_MODULE_2___default()(_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__components[eventTarget]).on(eventName, currMethod.handlerHolder[currHandler]);
// Shark.__components[eventTarget].addEventListener(eventName, currMethod.handlerHolder[currHandler]);
}
}
}
}
else {
this.trace("Auto-listener not assigned for the '" + eventName + "' event because of the container selector. The target: ", $target, "log");
}
}, this);
};
/**
* One of the Shark core feature: automatically remove listener added by the [smartListen method]{@link Jaw#smartListen}.
* <br />It works with both DOM and controller assignment.
*
* @param {string|jQuerySelection} [container] - If a CSS selector or a DOM element selected with jQuery is passed, the smartUnlisten function will parse only that element and its childs. Useful if you just need to parse a freshly-rendered container.
*/
Jaw.prototype.smartUnlisten = function (container) {
this.trace("smartUnlisten", {__validMethodList: this.__validMethodList}, "log");
if (this.__validMethodList) {
this.__validMethodList.forEach(currMethod => {
for (var currObjectName in currMethod.handlerHolder.__listeners) {
var currObject = currMethod.handlerHolder.__listeners[currObjectName];
for (var currEventName in currObject) {
var currEvent = currObject[currEventName];
if (currEvent.smartUnlisten === true || currEvent.smartUnlisten === undefined) {
switch (currEvent.target) {
case "DOM":
jquery__WEBPACK_IMPORTED_MODULE_2___default()(currObjectName).off(currEventName, currMethod.handlerHolder[currEvent.handler]);
break;
case "component":
jquery__WEBPACK_IMPORTED_MODULE_2___default()(_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__components[currObjectName]).off(currEventName, currMethod.handlerHolder[currEvent.handler]);
break;
case "controller":
jquery__WEBPACK_IMPORTED_MODULE_2___default()(_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__components[currObjectName]).off(currEventName, currMethod.handlerHolder[currEvent.handler]);
jquery__WEBPACK_IMPORTED_MODULE_2___default()(_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.__controllers[currObjectName]).off(currEventName, currMethod.handlerHolder[currEvent.handler]);
break;
}
delete currMethod.handlerHolder.__listeners[currObjectName][currEventName];
}
}
delete currMethod.handlerHolder.__listeners[currObjectName];
}
});
}
};
Jaw.prototype.trace = function (message, level) {
if (_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark) {
if (_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.trace) {
_Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.trace(message, level);
}
}
};
// Dave: ma dai, questa roba non esiste più, ne sono certo... vero?
Jaw.prototype.updateDisplay = function (referenceModelPath, sourceEvent) {
var targetModel = _Shark__WEBPACK_IMPORTED_MODULE_0__.Shark.get(referenceModelPath, "appData");
console.warn("UPDATE DISPLAY!!!!!!");
throw new Error("UPDATE DISPLAY!!!!!!");
};
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Jaw);
/***/ },
/***/ "./src/Shark.js"
/*!**********************!*\
!*** ./src/Shark.js ***!
\**********************/
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Shark: () => (/* binding */ Shark)
/* harmony export */ });
/* harmony import */ var _Jaw__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./Jaw */ "./src/Jaw.js");
/* harmony import */ var _renderers_MustacheRenderer__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./renderers/MustacheRenderer */ "./src/renderers/MustacheRenderer.js");
/* harmony import */ var _SmartModel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./SmartModel */ "./src/SmartModel.js");
/* harmony import */ var _Utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Utils */ "./src/Utils.js");
/* harmony import */ var mustache__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! mustache */ "mustache");
/* harmony import */ var mustache__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(mustache__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var uuid__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! uuid */ "./node_modules/uuid/dist/esm-browser/v4.js");
/* harmony import */ var _SwimWay__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./SwimWay */ "./src/SwimWay.js");
// import VirtualDOMRenderer from "./renderers/VirtualDOMRenderer";
/**
* Represents the Shark main class. It's has only static methods and should be considered as a Singleton.
* @constructor
* @version 0.7.49-alpha.113, 2026.03.10
*/
class Shark {
static TRACE_ERROR = "error";
static TRACE_INFO = "info";
static TRACE_LOG = "log";
static TRACE_NONE = "none";
static TRACE_WARN = "warn";
static __store = {};
static __stores = [];//For future implementantion of multiple stores
static __views = {};
// static __storeHashMap = new Map();
static #storeHashMap = new Map();
static requestedView = "";
static activeFeatures = {
moment: false
};
static activePage = {id: ""};
static appData = {};
static appDataItems = {};
static history = {content: [], currentPos: -1};
static isInitialized = false;
static labels = {};
static languagesPath = "";
static models = {};
static pageTemplates = {};
static requiredPage = "";
static templates = {};
static traceLevel = Shark.TRACE_WARN;
static __appDataMap = [];
static __autoBoundProperties = [];
static __components = {};
static __controllers = {};
static __globalListeners = [];
// static __internalData = {
// waitBoxOpenCount: 0
// };
// static __internalSettings = {};
static __listeners = {
viewToModelBinds: {},
viewToModelBinds2: {},
};
// static __modelProcessors = {};
static __models = {};
// static __hashMap = new Map();
static __templateMap = {};
constructor () {
console.warn("Shark constructor is deprecated, use Shark.init() method.");
}
/**
* @param {object} [options] - An object containing the options for initializing.
* @param {object} [options.commonEndPointsBase = ""] - DEPRECATED and REMOVED The "base" parte of the URL for the remote request. Use this value only if you have common destination for all your API call.
* Otehrwise you can set different URL for each call.
* @param {object} [options.labels = []] - An array of objects containing a list of labels to be used in conjunction with localization features.
* @param {integer} [options.remoteCallTimeOutDuration = 15000] - The number of millisecods that Shark should wait befor abort remote call.
* @param {object} [options.templates = {}] - An object containing html templates as strings. (documentation has to be written about creating templates object)
* @param {object} [options.templateRenderer = MustacheRenderer] - The Class responsible for rendering Views on screen. Must be a SharRenderer sub-class. Not yet fully implemented.
* @param {boolean} [forceReinit = false] - If true process initialize step even if the initialization phase has alredy been completed.
*/
static init (options, forceReinit) {
console.log(`Initializing Shark v0.7.49-alpha.113`);
Object.defineProperty(this, "$store", {
configurable: false,
enumerable: true,
value: {},
writable: false,
});
// Dave.Warn: Tutta la gestione dei template a livello Shark deve sparire, i template stanno nelle View!
if (options.templates) {
console.warn("WARNING!!!! You are using 'templates' option that has been removed and it's been ignored.");
}
if (options.commonEndPointsBase) {
console.warn("WARNING!!!! You are using 'commonEndPointsBase' option that has been removed and it's been ignored.");
}
if (options.dataManager) {
console.warn("WARNING!!!! You are using 'dataManager' option that has been removed and it's been ignored.");
}
Shark.trace("Shark.init, Shark.isInitialized: " + Shark.isInitialized + ", forceReinit: " + forceReinit, Shark.TRACE_INFO);
if (!Shark.isInitialized || forceReinit) {
let settings = {
enableMainStoreAutoRender: false,
enableJawStoreAutoRender: false,
enableSmartModelSetUpdatesView: false,
enableSmartModelTypeInfer: false,
enableJawsCompatibilityMode: false,
enableExperimentalComponentProps: false,
enableExperimentalJawTemplates: false,
labels: [],
preventDeepLinking: false,
remoteCallTimeOutDuration: 15000,
routingMethod: "hash",//optional, can be "url"
//templates: typeof Templates !== "undefined" ? Templates : {},
templateRenderer: typeof _renderers_MustacheRenderer__WEBPACK_IMPORTED_MODULE_1__.MustacheRenderer !== "undefined" ? new _renderers_MustacheRenderer__WEBPACK_IMPORTED_MODULE_1__.MustacheRenderer() : null,
};
Object.assign(settings, options);
Shark.activeFeatures.moment = typeof moment !==