UNPKG

lightstreamer-client-web

Version:

This package includes the resources needed to write a Lightstreamer client.

1,238 lines (1,155 loc) 1.32 MB
/** * @preserve * LIGHTSTREAMER - www.lightstreamer.com * Lightstreamer Web Client * Version 9.2.3+20250225 * Copyright (c) Lightstreamer Srl. All Rights Reserved. * Contains: LightstreamerClient, Subscription, ConsoleLogLevel, * ConsoleLoggerProvider, MpnDevice, MpnSubscription, SafariMpnBuilder, * FirebaseMpnBuilder, Chart, DynaGrid, SimpleChartListener, * StaticGrid, StatusWidget, Logger, LoggerProvider, * ClientListener, ClientMessageListener, ItemUpdate, SubscriptionListener, * MpnDeviceListener, MpnSubscriptionListener, ChartListener, DynaGridListener, * StaticGridListener * UMD */ (function (root, factory) { var hx_exports = factory(); if (typeof define === "function" && define.amd) { define("lightstreamer", ["module"], function(module) { var namespace = (module.config()["ns"] ? module.config()["ns"] + "/" : ""); define(namespace + "LightstreamerClient", function() { return hx_exports.LightstreamerClient; }); define(namespace + "Subscription", function() { return hx_exports.Subscription; }); define(namespace + "ConsoleLogLevel", function() { return hx_exports.ConsoleLogLevel; }); define(namespace + "ConsoleLoggerProvider", function() { return hx_exports.ConsoleLoggerProvider; }); define(namespace + "MpnDevice", function() { return hx_exports.MpnDevice; }); define(namespace + "MpnSubscription", function() { return hx_exports.MpnSubscription; }); define(namespace + "SafariMpnBuilder", function() { return hx_exports.SafariMpnBuilder; }); define(namespace + "FirebaseMpnBuilder", function() { return hx_exports.FirebaseMpnBuilder; }); define(namespace + "Chart", function() { return hx_exports.Chart; }); define(namespace + "DynaGrid", function() { return hx_exports.DynaGrid; }); define(namespace + "SimpleChartListener", function() { return hx_exports.SimpleChartListener; }); define(namespace + "StaticGrid", function() { return hx_exports.StaticGrid; }); define(namespace + "StatusWidget", function() { return hx_exports.StatusWidget; }); define(namespace + "Logger", function() { return hx_exports.Logger; }); define(namespace + "LoggerProvider", function() { return hx_exports.LoggerProvider; }); define(namespace + "ClientListener", function() { return hx_exports.ClientListener; }); define(namespace + "ClientMessageListener", function() { return hx_exports.ClientMessageListener; }); define(namespace + "ItemUpdate", function() { return hx_exports.ItemUpdate; }); define(namespace + "SubscriptionListener", function() { return hx_exports.SubscriptionListener; }); define(namespace + "MpnDeviceListener", function() { return hx_exports.MpnDeviceListener; }); define(namespace + "MpnSubscriptionListener", function() { return hx_exports.MpnSubscriptionListener; }); define(namespace + "ChartListener", function() { return hx_exports.ChartListener; }); define(namespace + "DynaGridListener", function() { return hx_exports.DynaGridListener; }); define(namespace + "StaticGridListener", function() { return hx_exports.StaticGridListener; }); }); require(["lightstreamer"]); } else { var namespace = createNs(extractNs(), root); namespace.LightstreamerClient = hx_exports.LightstreamerClient; namespace.Subscription = hx_exports.Subscription; namespace.ConsoleLogLevel = hx_exports.ConsoleLogLevel; namespace.ConsoleLoggerProvider = hx_exports.ConsoleLoggerProvider; namespace.MpnDevice = hx_exports.MpnDevice; namespace.MpnSubscription = hx_exports.MpnSubscription; namespace.SafariMpnBuilder = hx_exports.SafariMpnBuilder; namespace.FirebaseMpnBuilder = hx_exports.FirebaseMpnBuilder; namespace.Chart = hx_exports.Chart; namespace.DynaGrid = hx_exports.DynaGrid; namespace.SimpleChartListener = hx_exports.SimpleChartListener; namespace.StaticGrid = hx_exports.StaticGrid; namespace.StatusWidget = hx_exports.StatusWidget; namespace.Logger = hx_exports.Logger; namespace.LoggerProvider = hx_exports.LoggerProvider; namespace.ClientListener = hx_exports.ClientListener; namespace.ClientMessageListener = hx_exports.ClientMessageListener; namespace.ItemUpdate = hx_exports.ItemUpdate; namespace.SubscriptionListener = hx_exports.SubscriptionListener; namespace.MpnDeviceListener = hx_exports.MpnDeviceListener; namespace.MpnSubscriptionListener = hx_exports.MpnSubscriptionListener; namespace.ChartListener = hx_exports.ChartListener; namespace.DynaGridListener = hx_exports.DynaGridListener; namespace.StaticGridListener = hx_exports.StaticGridListener; } function lsIsBrowser() { return typeof(window) !== 'undefined' && typeof(window.document) !== 'undefined'; } function extractNs() { if (!lsIsBrowser()) { return null; } var scripts = window.document.getElementsByTagName("script"); for (var i = 0, len = scripts.length; i < len; i++) { if ("data-lightstreamer-ns" in scripts[i].attributes) { return scripts[i].attributes["data-lightstreamer-ns"].value; } } return null; } function createNs(ns, root) { if (! ns) { return root; } var pieces = ns.split("."); var parent = root || window; for (var j = 0; j < pieces.length; j++) { var qualifier = pieces[j]; var obj = parent[qualifier]; if (! (obj && typeof obj == "object")) { obj = parent[qualifier] = {}; } parent = obj; } return parent; } }(typeof self !== "undefined" ? self : this, function () { var lightstreamerExports = (function (exports) { 'use strict'; var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function getAugmentedNamespace(n) { if (n.__esModule) return n; var f = n.default; if (typeof f == "function") { var a = function a () { if (this instanceof a) { return Reflect.construct(f, arguments, this.constructor); } return f.apply(this, arguments); }; a.prototype = f.prototype; } else a = {}; Object.defineProperty(a, '__esModule', {value: true}); Object.keys(n).forEach(function (k) { var d = Object.getOwnPropertyDescriptor(n, k); Object.defineProperty(a, k, d.get ? d : { enumerable: true, get: function () { return n[k]; } }); }); return a; } const createCache = (lastNumberWeakMap) => { return (collection, nextNumber) => { lastNumberWeakMap.set(collection, nextNumber); return nextNumber; }; }; /* * The value of the constant Number.MAX_SAFE_INTEGER equals (2 ** 53 - 1) but it * is fairly new. */ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER === undefined ? 9007199254740991 : Number.MAX_SAFE_INTEGER; const TWO_TO_THE_POWER_OF_TWENTY_NINE = 536870912; const TWO_TO_THE_POWER_OF_THIRTY = TWO_TO_THE_POWER_OF_TWENTY_NINE * 2; const createGenerateUniqueNumber = (cache, lastNumberWeakMap) => { return (collection) => { const lastNumber = lastNumberWeakMap.get(collection); /* * Let's try the cheapest algorithm first. It might fail to produce a new * number, but it is so cheap that it is okay to take the risk. Just * increase the last number by one or reset it to 0 if we reached the upper * bound of SMIs (which stands for small integers). When the last number is * unknown it is assumed that the collection contains zero based consecutive * numbers. */ let nextNumber = lastNumber === undefined ? collection.size : lastNumber < TWO_TO_THE_POWER_OF_THIRTY ? lastNumber + 1 : 0; if (!collection.has(nextNumber)) { return cache(collection, nextNumber); } /* * If there are less than half of 2 ** 30 numbers stored in the collection, * the chance to generate a new random number in the range from 0 to 2 ** 30 * is at least 50%. It's benifitial to use only SMIs because they perform * much better in any environment based on V8. */ if (collection.size < TWO_TO_THE_POWER_OF_TWENTY_NINE) { while (collection.has(nextNumber)) { nextNumber = Math.floor(Math.random() * TWO_TO_THE_POWER_OF_THIRTY); } return cache(collection, nextNumber); } // Quickly check if there is a theoretical chance to generate a new number. if (collection.size > MAX_SAFE_INTEGER) { throw new Error('Congratulations, you created a collection of unique numbers which uses all available integers!'); } // Otherwise use the full scale of safely usable integers. while (collection.has(nextNumber)) { nextNumber = Math.floor(Math.random() * MAX_SAFE_INTEGER); } return cache(collection, nextNumber); }; }; const LAST_NUMBER_WEAK_MAP = new WeakMap(); const cache = createCache(LAST_NUMBER_WEAK_MAP); const generateUniqueNumber = createGenerateUniqueNumber(cache, LAST_NUMBER_WEAK_MAP); const isCallNotification = (message) => { return message.method !== undefined && message.method === 'call'; }; const isClearResponse = (message) => { return message.error === null && typeof message.id === 'number'; }; const load = (url) => { // Prefilling the Maps with a function indexed by zero is necessary to be compliant with the specification. const scheduledIntervalFunctions = new Map([[0, () => { }]]); // tslint:disable-line no-empty const scheduledTimeoutFunctions = new Map([[0, () => { }]]); // tslint:disable-line no-empty const unrespondedRequests = new Map(); const worker = new Worker(url); worker.addEventListener('message', ({ data }) => { if (isCallNotification(data)) { const { params: { timerId, timerType } } = data; if (timerType === 'interval') { const idOrFunc = scheduledIntervalFunctions.get(timerId); if (typeof idOrFunc === 'number') { const timerIdAndTimerType = unrespondedRequests.get(idOrFunc); if (timerIdAndTimerType === undefined || timerIdAndTimerType.timerId !== timerId || timerIdAndTimerType.timerType !== timerType) { throw new Error('The timer is in an undefined state.'); } } else if (typeof idOrFunc !== 'undefined') { idOrFunc(); } else { throw new Error('The timer is in an undefined state.'); } } else if (timerType === 'timeout') { const idOrFunc = scheduledTimeoutFunctions.get(timerId); if (typeof idOrFunc === 'number') { const timerIdAndTimerType = unrespondedRequests.get(idOrFunc); if (timerIdAndTimerType === undefined || timerIdAndTimerType.timerId !== timerId || timerIdAndTimerType.timerType !== timerType) { throw new Error('The timer is in an undefined state.'); } } else if (typeof idOrFunc !== 'undefined') { idOrFunc(); // A timeout can be savely deleted because it is only called once. scheduledTimeoutFunctions.delete(timerId); } else { throw new Error('The timer is in an undefined state.'); } } } else if (isClearResponse(data)) { const { id } = data; const timerIdAndTimerType = unrespondedRequests.get(id); if (timerIdAndTimerType === undefined) { throw new Error('The timer is in an undefined state.'); } const { timerId, timerType } = timerIdAndTimerType; unrespondedRequests.delete(id); if (timerType === 'interval') { scheduledIntervalFunctions.delete(timerId); } else { scheduledTimeoutFunctions.delete(timerId); } } else { const { error: { message } } = data; throw new Error(message); } }); const clearInterval = (timerId) => { const id = generateUniqueNumber(unrespondedRequests); unrespondedRequests.set(id, { timerId, timerType: 'interval' }); scheduledIntervalFunctions.set(timerId, id); worker.postMessage({ id, method: 'clear', params: { timerId, timerType: 'interval' } }); }; const clearTimeout = (timerId) => { const id = generateUniqueNumber(unrespondedRequests); unrespondedRequests.set(id, { timerId, timerType: 'timeout' }); scheduledTimeoutFunctions.set(timerId, id); worker.postMessage({ id, method: 'clear', params: { timerId, timerType: 'timeout' } }); }; const setInterval = (func, delay = 0) => { const timerId = generateUniqueNumber(scheduledIntervalFunctions); scheduledIntervalFunctions.set(timerId, () => { func(); // Doublecheck if the interval should still be rescheduled because it could have been cleared inside of func(). if (typeof scheduledIntervalFunctions.get(timerId) === 'function') { worker.postMessage({ id: null, method: 'set', params: { delay, now: performance.now(), timerId, timerType: 'interval' } }); } }); worker.postMessage({ id: null, method: 'set', params: { delay, now: performance.now(), timerId, timerType: 'interval' } }); return timerId; }; const setTimeout = (func, delay = 0) => { const timerId = generateUniqueNumber(scheduledTimeoutFunctions); scheduledTimeoutFunctions.set(timerId, func); worker.postMessage({ id: null, method: 'set', params: { delay, now: performance.now(), timerId, timerType: 'timeout' } }); return timerId; }; return { clearInterval, clearTimeout, setInterval, setTimeout }; }; const createLoadOrReturnBroker = (loadBroker, worker) => { let broker = null; return () => { if (broker !== null) { return broker; } const blob = new Blob([worker], { type: 'application/javascript; charset=utf-8' }); const url = URL.createObjectURL(blob); broker = loadBroker(url); // Bug #1: Edge up until v18 didn't like the URL to be revoked directly. setTimeout(() => URL.revokeObjectURL(url)); return broker; }; }; // This is the minified and stringified code of the worker-timers-worker package. const worker = `(()=>{"use strict";const e=new Map,t=new Map,r=(e,t)=>{let r,o;const i=performance.now();r=i,o=e-Math.max(0,i-t);return{expected:r+o,remainingDelay:o}},o=(e,t,r,i)=>{const s=performance.now();s>r?postMessage({id:null,method:"call",params:{timerId:t,timerType:i}}):e.set(t,setTimeout(o,r-s,e,t,r,i))};addEventListener("message",(i=>{let{data:s}=i;try{if("clear"===s.method){const{id:r,params:{timerId:o,timerType:i}}=s;if("interval"===i)(t=>{const r=e.get(t);if(void 0===r)throw new Error('There is no interval scheduled with the given id "'.concat(t,'".'));clearTimeout(r),e.delete(t)})(o),postMessage({error:null,id:r});else{if("timeout"!==i)throw new Error('The given type "'.concat(i,'" is not supported'));(e=>{const r=t.get(e);if(void 0===r)throw new Error('There is no timeout scheduled with the given id "'.concat(e,'".'));clearTimeout(r),t.delete(e)})(o),postMessage({error:null,id:r})}}else{if("set"!==s.method)throw new Error('The given method "'.concat(s.method,'" is not supported'));{const{params:{delay:i,now:n,timerId:a,timerType:d}}=s;if("interval"===d)((t,i,s)=>{const{expected:n,remainingDelay:a}=r(t,s);e.set(i,setTimeout(o,a,e,i,n,"interval"))})(i,a,n);else{if("timeout"!==d)throw new Error('The given type "'.concat(d,'" is not supported'));((e,i,s)=>{const{expected:n,remainingDelay:a}=r(e,s);t.set(i,setTimeout(o,a,t,i,n,"timeout"))})(i,a,n)}}}}catch(e){postMessage({error:{message:e.message},id:s.id,result:null})}}))})();`; // tslint:disable-line:max-line-length const loadOrReturnBroker = createLoadOrReturnBroker(load, worker); const clearInterval$1 = (timerId) => loadOrReturnBroker().clearInterval(timerId); const clearTimeout = (timerId) => loadOrReturnBroker().clearTimeout(timerId); const setInterval$1 = (...args) => loadOrReturnBroker().setInterval(...args); const setTimeout$1 = (...args) => loadOrReturnBroker().setTimeout(...args); var workerTimers = /*#__PURE__*/Object.freeze({ __proto__: null, clearInterval: clearInterval$1, clearTimeout: clearTimeout, setInterval: setInterval$1, setTimeout: setTimeout$1 }); var require$$0 = /*@__PURE__*/getAugmentedNamespace(workerTimers); var jsonpatch = {exports: {}}; /* @preserve * JSONPatch.js * * A Dharmafly project written by Thomas Parslow * <tom@almostobsolete.net> and released with the kind permission of * NetDev. * * Copyright 2011-2013 Thomas Parslow. All rights reserved. * Permission is hereby granted,y 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. * * Implements the JSON Patch IETF RFC 6902 as specified at: * * http://tools.ietf.org/html/rfc6902 * * Also implements the JSON Pointer IETF RFC 6901 as specified at: * * http://tools.ietf.org/html/rfc6901 * */ var hasRequiredJsonpatch; function requireJsonpatch () { if (hasRequiredJsonpatch) return jsonpatch.exports; hasRequiredJsonpatch = 1; (function (module, exports) { (function (root, factory) { { // Node factory(module.exports); } }(commonjsGlobal, function (exports) { var JSONPatch, JSONPointer,_operationRequired,isArray; // Taken from underscore.js isArray = Array.isArray || function(obj) { return Object.prototype.toString.call(obj) == '[object Array]'; }; /* Public: Shortcut to apply a patch the document without having to * create a patch object first. Returns the patched document. Does * not damage the original document, but will reuse parts of its * structure in the new one. * * doc - The target document to which the patch should be applied. * patch - A JSON Patch document specifying the changes to the * target documentment * * Example (node.js) * * jsonpatch = require('jsonpatch'); * doc = JSON.parse(sourceJSON); * doc = jsonpatch.apply_patch(doc, thepatch); * destJSON = JSON.stringify(doc); * * Example (in browser) * * <script src="jsonpatch.js" type="text/javascript"></script> * <script type="application/javascript"> * doc = JSON.parse(sourceJSON); * doc = jsonpatch.apply_patch(doc, thepatch); * destJSON = JSON.stringify(doc); * </script> * * Returns the patched document */ exports.apply_patch = function (doc, patch) { return (new JSONPatch(patch)).apply(doc); }; /* Public: Error thrown if the patch supplied is invalid. */ function InvalidPatch(message) { Error.call(this, message); this.message = message; } exports.InvalidPatch = InvalidPatch; InvalidPatch.prototype = new Error(); /* Public: Error thrown if the patch can not be apllied to the given document */ function PatchApplyError(message) { Error.call(this, message); this.message = message; } exports.PatchApplyError = PatchApplyError; PatchApplyError.prototype = new Error(); /* Public: A class representing a JSON Pointer. A JSON Pointer is * used to point to a specific sub-item within a JSON document. * * Example (node.js); * * jsonpatch = require('jsonpatch'); * var pointer = new jsonpatch.JSONPointer('/path/to/item'); * var item = pointer.follow(doc) * */ exports.JSONPointer = JSONPointer = function JSONPointer (pathStr) { var i,split,path=[]; // Split up the path split = pathStr.split('/'); if ('' !== split[0]) { throw new InvalidPatch('JSONPointer must start with a slash (or be an empty string)!'); } for (i = 1; i < split.length; i++) { path[i-1] = split[i].replace(/~1/g,'/').replace(/~0/g,'~'); } this.path = path; this.length = path.length; }; /* Private: Get a segment of the pointer given a current doc * context. */ JSONPointer.prototype._get_segment = function (index, node) { var segment = this.path[index]; if(isArray(node)) { if ('-' === segment) { segment = node.length; } else { // Must be a non-negative integer in base-10 without leading zeros if (!segment.match(/^0$|^[1-9][0-9]*$/)) { throw new PatchApplyError('Expected a number to segment an array'); } segment = parseInt(segment,10); } } return segment; }; // Return a shallow copy of an object function clone(o) { var cloned, key; if (isArray(o)) { return o.slice(); // typeof null is "object", but we want to copy it as null } if (o === null) { return o; } else if (typeof o === "object") { cloned = {}; for(key in o) { if (Object.hasOwnProperty.call(o, key)) { cloned[key] = o[key]; } } return cloned; } else { return o; } } /* Private: Follow the pointer to its penultimate segment then call * the handler with the current doc and the last key (converted to * an int if the current doc is an array). The handler is expected to * return a new copy of the penultimate part. * * doc - The document to search within * handler - The callback function to handle the last part * * Returns the result of calling the handler */ JSONPointer.prototype._action = function (doc, handler, mutate) { var that = this; function follow_pointer(node, index) { var segment, subnode; if (!mutate) { node = clone(node); } segment = that._get_segment(index, node); // Is this the last segment? if (index == that.path.length-1) { node = handler(node, segment); } else { // Make sure we can follow the segment if (isArray(node)) { if (node.length <= segment) { throw new PatchApplyError('Path not found in document'); } } else if (typeof node === "object") { if (!Object.hasOwnProperty.call(node, segment)) { throw new PatchApplyError('Path not found in document'); } } else { throw new PatchApplyError('Path not found in document'); } subnode = follow_pointer(node[segment], index+1); if (!mutate) { node[segment] = subnode; } } return node; } return follow_pointer(doc, 0); }; /* Public: Takes a JSON document and a value and adds the value into * the doc at the position pointed to. If the position pointed to is * in an array then the existing element at that position (if any) * and all that follow it have their position incremented to make * room. It is an error to add to a parent object that doesn't exist * or to try to replace an existing value in an object. * * doc - The document to operate against. Will be mutated so should * not be reused after the call. * value - The value to insert at the position pointed to * * Examples * * var doc = new JSONPointer("/obj/new").add({obj: {old: "hello"}}, "world"); * // doc now equals {obj: {old: "hello", new: "world"}} * * Returns the updated doc (the value passed in may also have been mutated) */ JSONPointer.prototype.add = function (doc, value, mutate) { // Special case for a pointer to the root if (0 === this.length) { return value; } return this._action(doc, function (node, lastSegment) { if (isArray(node)) { if (lastSegment > node.length) { throw new PatchApplyError('Add operation must not attempt to create a sparse array!'); } node.splice(lastSegment, 0, value); } else { node[lastSegment] = value; } return node; }, mutate); }; /* Public: Takes a JSON document and removes the value pointed to. * It is an error to attempt to remove a value that doesn't exist. * * doc - The document to operate against. May be mutated so should * not be reused after the call. * * Examples * * var doc = new JSONPointer("/obj/old").add({obj: {old: "hello"}}); * // doc now equals {obj: {}} * * Returns the updated doc (the value passed in may also have been mutated) */ JSONPointer.prototype.remove = function (doc, mutate) { // Special case for a pointer to the root if (0 === this.length) { // Removing the root makes the whole value undefined. // NOTE: Should it be an error to remove the root if it is // ALREADY undefined? I'm not sure... return undefined; } return this._action(doc, function (node, lastSegment) { if (!Object.hasOwnProperty.call(node,lastSegment)) { throw new PatchApplyError('Remove operation must point to an existing value!'); } if (isArray(node)) { node.splice(lastSegment, 1); } else { delete node[lastSegment]; } return node; }, mutate); }; /* Public: Semantically equivalent to a remove followed by an add * except when the pointer points to the root element in which case * the whole document is replaced. * * doc - The document to operate against. May be mutated so should * not be reused after the call. * * Examples * * var doc = new JSONPointer("/obj/old").replace({obj: {old: "hello"}}, "world"); * // doc now equals {obj: {old: "world"}} * * Returns the updated doc (the value passed in may also have been mutated) */ JSONPointer.prototype.replace = function (doc, value, mutate) { // Special case for a pointer to the root if (0 === this.length) { return value; } return this._action(doc, function (node, lastSegment) { if (!Object.hasOwnProperty.call(node,lastSegment)) { throw new PatchApplyError('Replace operation must point to an existing value!'); } if (isArray(node)) { node.splice(lastSegment, 1, value); } else { node[lastSegment] = value; } return node; }, mutate); }; /* Public: Returns the value pointed to by the pointer in the given doc. * * doc - The document to operate against. * * Examples * * var value = new JSONPointer("/obj/value").get({obj: {value: "hello"}}); * // value now equals 'hello' * * Returns the value */ JSONPointer.prototype.get = function (doc) { var value; if (0 === this.length) { return doc; } this._action(doc, function (node, lastSegment) { if (!Object.hasOwnProperty.call(node,lastSegment)) { throw new PatchApplyError('Path not found in document'); } value = node[lastSegment]; return node; }, true); return value; }; /* Public: returns true if this pointer points to a child of the * other pointer given. Returns true if both point to the same place. * * otherPointer - Another JSONPointer object * * Examples * * var pointer1 = new JSONPointer('/animals/mammals/cats/holly'); * var pointer2 = new JSONPointer('/animals/mammals/cats'); * var isChild = pointer1.subsetOf(pointer2); * * Returns a boolean */ JSONPointer.prototype.subsetOf = function (otherPointer) { if (this.length <= otherPointer.length) { return false; } for (var i = 0; i < otherPointer.length; i++) { if (otherPointer.path[i] !== this.path[i]) { return false; } } return true; }; _operationRequired = { add: ['value'], replace: ['value'], test: ['value'], remove: [], move: ['from'], copy: ['from'] }; // Check if a is deep equal to b (by the rules given in the // JSONPatch spec) function deepEqual(a,b) { var key; if (a === b) { return true; } else if (typeof a !== typeof b) { return false; } else if ('object' === typeof(a)) { var aIsArray = isArray(a), bIsArray = isArray(b); if (aIsArray !== bIsArray) { return false; } else if (aIsArray) { // Both are arrays if (a.length != b.length) { return false; } else { for (var i = 0; i < a.length; i++) { if(!deepEqual(a[i], b[i])) { return false; } } } return true; } else { // Check each key of the object recursively for(key in a) { if (Object.hasOwnProperty(a, key)) { if (!(Object.hasOwnProperty(b,key) && deepEqual(a[key], b[key]))) { return false; } } } for(key in b) { if(Object.hasOwnProperty(b,key) && !Object.hasOwnProperty(a, key)) { return false; } } return true; } } else { return false; } } function validateOp(operation) { var i, required; if (!operation.op) { throw new InvalidPatch('Operation missing!'); } if (!_operationRequired.hasOwnProperty(operation.op)) { throw new InvalidPatch('Invalid operation!'); } if (!('path' in operation)) { throw new InvalidPatch('Path missing!'); } required = _operationRequired[operation.op]; // Check that all required keys are present for(i = 0; i < required.length; i++) { if(!(required[i] in operation)) { throw new InvalidPatch(operation.op + ' must have key ' + required[i]); } } } function compileOperation(operation, mutate) { validateOp(operation); var op = operation.op; var path = new JSONPointer(operation.path); var value = operation.value; var from = operation.from ? new JSONPointer(operation.from) : null; switch (op) { case 'add': return function (doc) { return path.add(doc, value, mutate); }; case 'remove': return function (doc) { return path.remove(doc, mutate); }; case 'replace': return function (doc) { return path.replace(doc, value, mutate); }; case 'move': // Check that destination isn't inside the source if (path.subsetOf(from)) { throw new InvalidPatch('destination must not be a child of source'); } return function (doc) { var value = from.get(doc); var intermediate = from.remove(doc, mutate); return path.add(intermediate, value, mutate); }; case 'copy': return function (doc) { var value = from.get(doc); return path.add(doc, value, mutate); }; case 'test': return function (doc) { if (!deepEqual(path.get(doc), value)) { throw new PatchApplyError("Test operation failed. Value did not match."); } return doc; }; } } /* Public: A class representing a patch. * * patch - The patch as an array or as a JSON string (containing an * array) * mutate - Indicates that input documents should be mutated * (default is for the input to be unaffected.) This will * not work correctly if the patch replaces the root of * the document. */ exports.JSONPatch = JSONPatch = function JSONPatch(patch, mutate) { this._compile(patch, mutate); }; JSONPatch.prototype._compile = function (patch, mutate) { var i, _this = this; this.compiledOps = []; if ('string' === typeof patch) { patch = JSON.parse(patch); } if(!isArray(patch)) { throw new InvalidPatch('Patch must be an array of operations'); } for(i = 0; i < patch.length; i++) { var compiled = compileOperation(patch[i], mutate); _this.compiledOps.push(compiled); } }; /* Public: Apply the patch to a document and returns the patched * document. * * doc - The document to which the patch should be applied. * * Returns the patched document */ exports.JSONPatch.prototype.apply = function (doc) { var i; for(i = 0; i < this.compiledOps.length; i++) { doc = this.compiledOps[i](doc); } return doc; }; })); } (jsonpatch)); return jsonpatch.exports; } /** * This is a dummy constructor not to be used in any case. * @constructor * * @exports Logger * @class Simple Interface to be implemented to produce log. */ var Logger = function() { }; Logger.prototype = { /** * Receives log messages at FATAL level. * * @param {String} message The message to be logged. * @param {Error} [exception] An Exception instance related to the current log message. * * @see LoggerProvider */ fatal: function(message,exception) { }, /** * Checks if this Logger is enabled for the FATAL level. * The method should return true if this Logger is enabled for FATAL events, * false otherwise. * <BR>This property is intended to let the library save computational cost by suppressing the generation of * log FATAL statements. However, even if the method returns false, FATAL log * lines may still be received by the {@link Logger#fatal} method * and should be ignored by the Logger implementation. * * @return {boolean} true if FATAL logging is enabled, false otherwise */ isFatalEnabled: function() { }, /** * Receives log messages at ERROR level. * * @param {String} message The message to be logged. * @param {Error} [exception] An Exception instance related to the current log message. */ error: function(message,exception) { }, /** * Checks if this Logger is enabled for the ERROR level. * The method should return true if this Logger is enabled for ERROR events, * false otherwise. * <BR>This property is intended to let the library save computational cost by suppressing the generation of * log ERROR statements. However, even if the method returns false, ERROR log * lines may still be received by the {@link Logger#error} method * and should be ignored by the Logger implementation. * * @return {boolean} true if ERROR logging is enabled, false otherwise */ isErrorEnabled: function() { }, /** * Receives log messages at WARN level. * * @param {String} message The message to be logged. * @param {Error} [exception] An Exception instance related to the current log message. */ warn: function(message,exception) { }, /** * Checks if this Logger is enabled for the WARN level. * The method should return true if this Logger is enabled for WARN events, * false otherwise. * <BR>This property is intended to let the library save computational cost by suppressing the generation of * log WARN statements. However, even if the method returns false, WARN log * lines may still be received by the {@link Logger#warn} method * and should be ignored by the Logger implementation. * * @return {boolean} true if WARN logging is enabled, false otherwise */ isWarnEnabled: function() { }, /** * Receives log messages at INFO level. * * @param {String} message The message to be logged. * @param {Error} [exception] An Exception instance related to the current log message. */ info: function(message,exception) { }, /** * Checks if this Logger is enabled for the INFO level. * The method should return true if this Logger is enabled for INFO events, * false otherwise. * <BR>This property is intended to let the library save computational cost by suppressing the generation of * log INFO statements. However, even if the method returns false, INFO log * lines may still be received by the {@link Logger#info} method * and should be ignored by the Logger implementation. * * @return {boolean} true if INFO logging is enabled, false otherwise */ isInfoEnabled: function() { }, /** * Receives log messages at DEBUG level. * * @param {String} message The message to be logged. * @param {Error} [exception] An Exception instance related to the current log message. */ debug: function(message,exception) { }, /** * Checks if this Logger is enabled for the DEBUG level. * The method should return true if this Logger is enabled for DEBUG events, * false otherwise. * <BR>This property is intended to let the library save computational cost by suppressing the generation of * log DEBUG statements. However, even if the method returns false, DEBUG log * lines may still be received by the {@link Logger#debug} method * and should be ignored by the Logger implementation. * * @return {boolean} true if DEBUG logging is enabled, false otherwise */ isDebugEnabled: function() { }, /** * Receives log messages at TRACE level. * * @param {String} message The message to be logged. * @param {Error} [exception] An Exception instance related to the current log message. */ trace: function(message,exception) { }, /** * Checks if this Logger is enabled for the TRACE level. * The method should return true if this Logger is enabled for TRACE events, * false otherwise. * <BR>This property is intended to let the library save computational cost by suppressing the generation of * log TRACE statements. However, even if the method returns false, TRACE log * lines may still be received by the {@link Logger#trace} method * and should be ignored by the Logger implementation. * * @return {boolean} true if TRACE logging is enabled, false otherwise */ isTraceEnabled: function() { } };/** * This is a dummy constructor not to be used in any case. * @constructor * * @exports LoggerProvider * @class Simple interface to be implemented to provide custom log producers. * * <BR>A simple implementation of this interface is included with this library: * {@link ConsoleLoggerProvider}. */ var LoggerProvider = function() { }; LoggerProvider.prototype = { /** * Invoked to request a {@link Logger} instance that will be used for logging occurring * on the given category. It is suggested, but not mandatory, that subsequent * calls to this method related to the same category return the same {@link Logger} * instance. * * @param {String} category the log category all messages passed to the given * Logger instance will pertain to. * * @return {Logger} A Logger instance that will receive log lines related to * the given category. */ getLogger: function(category) { } };/** * This is a dummy constructor not to be used in any case. * @constructor * * @exports ClientListener * @class Interface to be implemented to listen to {@link LightstreamerClient} events * comprehending notifications of connection activity and errors. * <BR>Events for these listeners are executed asynchronously with respect to the code * that generates them. This means that, upon reception of an event, it is possible that * the current state of the client has changed furtherly. * <BR>Note that it is not necessary to implement all of the interface methods for * the listener to be successfully passed to the {@link LightstreamerClient#addListener} * method. * <BR>A ClientListener implementation is distributed together with the library: * {@link StatusWidget}. */ function ClientListener() { } ClientListener.prototype = { /** * Event handler that is called when the Server notifies a refusal on the * client attempt to open a new connection or the interruption of a * streaming connection. In both cases, the {@link ClientListener#onStatusChange} * event handler has already been invoked with a "DISCONNECTED" status and * no recovery attempt has been performed. By setting a custom handler, however, * it is possible to override this and perform custom recovery actions. * * @param {Number} errorCode The error code. It can be one of the * following: * <ul> * <li>1 - user/password check failed</li> * <li>2 - requested Adapter Set not available</li> * <li>7 - licensed maximum number of sessions reached * (this can only happen with some licenses)</li> * <li>8 - configured maximum number of sessions reached</li> * <li>9 - configured maximum server load reached</li> * <li>10 - new sessions temporarily blocked</li> * <li>11 - streaming is not available because of Server license * restrictions (this can only happen with special licenses)</li> * <li>21 - a bind request has unexpectedly reached a wrong Server instance, which suggests that a routing issue may be in place</li> * <li>30-41 - the current connection or the whole session has been closed * by external agents; the possible cause may be: * <ul> * <li>The session was closed on the Server side (via software or by * the administrator) (32) or through a client "destroy" request (31);</li> * <li>The Metadata Adapter imposes limits on the overall open sessions * for the current user and has requested the closure of the current session * upon opening of a new session for the same user * on a different browser window * (35);</li> * <li>An unexpected error occurred on the Server while the session was in * activity (33, 34);</li> * <li>An unknown or unexpected cause; any code different from the ones * identified in the above cases could be issued.</li> * </ul> * A detailed description for the specific cause is currently not supplied * (i.e. errorMessage is null in this case).</li> * <li>60 - this version of the client is not allowed by the current license terms.</li> * <li>61 - there was an error in the parsing of the server response thus the client cannot continue with the current session.</li> * <li>66 - an unexpected exception was thrown by the Metadata Adapter while authorizing the connection.</li> * <li>68 - the Server could not open or continue with the session because of an internal error.</li> * <li>70 - an unusable port was configured on the server address.</li> * <li>71 - this kind of client is not allowed by the current license terms.</li> * <li>&lt;= 0 - the Metadata Adapter has refused the user connection; * the code value is dependent on the specific Metadata Adapter * implementation</li> * </ul> * @param {String} errorMessage The description of the error as sent * by the Server. * * @see ConnectionDetails#setAdapterSet * @see ClientListener#onStatusChange */ onServerError: function(errorCode, errorMessage) { }, /** * Event handler that receives a notification each time the LightstreamerClient * status has changed. The status changes may be originated either by custom * actions (e.g. by calling {@link LightstreamerClient#disconnect}) or by * internal actions. * <BR/><BR/>The normal cases are the following: * <ul> * <li>After issuing connect(), if the current status is "DISCONNECTED*", the * client will switch to "CONNECTING" first and * to "CONNECTED:STREAM-SENSING" as soon as the pre-flight request receives its * answer. * <BR>As soon as the new session is established, it will switch to * "CONNECTED:WS-STREAMING" if the browser/environment permits WebSockets; * otherwise it will switch to "CONNECTED:HTTP-STREAMING" if the * browser/environment permits streaming or to "CONNECTED:HTTP-POLLING" * as a last resort. * <BR>On the other hand if the status is already "CONNECTED:*" a * switch to "CONNECTING" is usually not needed.</li> * <li>After issuing disconnect(), the status will switch to "DISCONNECTED".</li> * <li>In case of a server connection refusal, the status may switch from * "CONNECTING" directly to "DISCONNECTED". After that, the * {@link ClientListener#onServerError} event handler will be invoked.</li> * </ul> * <BR/>Possible special cases are the following: * <ul> * <li>In case of Server unavailability during streaming, the status may * switch from "CONNECTED:*-STREAMING" to "STALLED" (see * {@link ConnectionOptions#setStalledTimeout}). * If the unavailability ceases, the status will switch back to * ""CONNECTED:*-STREAMING""; * otherwise, if the unavailability persists (see * {@link ConnectionOptions#setReconnectTimeout}), * the status will switch to "DISCONNECTED:TRYING-RECOVERY" and eventually to * "CONNECTED:*-STREAMING".</li> * <li>In case the connection or the whole session is forcibly closed * by the Server, the status may switch from "CONNECTED:*-STREAMING" * or "CONNECTED:*-POLLING" directly to "DISCONNECTED". After that, the * {@link ClientListener#onServerError} event handler will be invoked.</li> * <li>Depending on the setting in {@link ConnectionOptions#setSlowingEnabled}, * in case of slow update processing, the status may switch from * "CONNECTED:WS-STREAMING" to "CONNECTED:WS-POLLING" or from * "CONNECTED:HTTP-STREAMING" to "CONNECTED:HTTP-POLLING".</li> * <li>If the status is "CONNECTED:*-POLLING" and any problem during an * intermediate poll occurs, the status may switch to "CONNECTING" and * eventually to "CONNECTED:*-POLLING". The same may hold for the * "CONNECTED:*-STREAMING" case, when a rebind is needed.</li> * <li>In case a forced transport was set through * {@link ConnectionOptions#setForcedTransport}, only the related final * status or statuses are possible. Note that if the transport is forced