2019-07-02 09:44:32 +02:00
|
|
|
|
/*! Icinga Web 2 | (c) 2019 Icinga GmbH | GPLv2+ */
|
|
|
|
|
|
2019-07-17 12:46:14 +02:00
|
|
|
|
;(function(Icinga) {
|
2019-07-02 09:44:32 +02:00
|
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
2019-07-09 15:55:52 +02:00
|
|
|
|
const KEY_TTL = 7776000000; // 90 days (90×24×60×60×1000)
|
|
|
|
|
|
2019-07-02 09:44:32 +02:00
|
|
|
|
/**
|
|
|
|
|
* Icinga.Storage
|
|
|
|
|
*
|
|
|
|
|
* localStorage access
|
2019-07-04 10:21:50 +02:00
|
|
|
|
*
|
|
|
|
|
* @param {string} prefix
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*/
|
2019-07-04 10:21:50 +02:00
|
|
|
|
Icinga.Storage = function(prefix) {
|
2019-07-02 09:44:32 +02:00
|
|
|
|
|
|
|
|
|
/**
|
2019-07-04 10:21:50 +02:00
|
|
|
|
* Prefix to use for keys
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @type {string}
|
|
|
|
|
*/
|
2019-07-04 10:21:50 +02:00
|
|
|
|
this.prefix = prefix;
|
2019-07-29 10:16:36 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Storage backend
|
|
|
|
|
*
|
|
|
|
|
* @type {Storage}
|
|
|
|
|
*/
|
|
|
|
|
this.backend = window.localStorage;
|
2019-07-10 09:32:33 +02:00
|
|
|
|
};
|
2019-07-02 09:44:32 +02:00
|
|
|
|
|
2019-07-10 09:32:33 +02:00
|
|
|
|
/**
|
|
|
|
|
* Callbacks for storage events on particular keys
|
|
|
|
|
*
|
|
|
|
|
* @type {{function}}
|
|
|
|
|
*/
|
|
|
|
|
Icinga.Storage.subscribers = {};
|
2019-07-02 09:44:32 +02:00
|
|
|
|
|
2019-07-10 09:32:33 +02:00
|
|
|
|
/**
|
|
|
|
|
* Pass storage events to subscribers
|
|
|
|
|
*
|
|
|
|
|
* @param {StorageEvent} event
|
|
|
|
|
*/
|
|
|
|
|
window.addEventListener('storage', function(event) {
|
|
|
|
|
var url = icinga.utils.parseUrl(event.url);
|
2019-07-15 13:31:55 +02:00
|
|
|
|
if (! url.path.substring(0, icinga.config.baseUrl.length) === icinga.config.baseUrl) {
|
2019-07-10 09:32:33 +02:00
|
|
|
|
// A localStorage is shared between all paths on the same origin.
|
|
|
|
|
// So we need to make sure it's us who made a change.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof Icinga.Storage.subscribers[event.key] !== 'undefined') {
|
2019-07-15 13:31:01 +02:00
|
|
|
|
var newValue = null,
|
|
|
|
|
oldValue = null;
|
2019-07-17 11:22:46 +02:00
|
|
|
|
if (!! event.newValue) {
|
|
|
|
|
try {
|
|
|
|
|
newValue = JSON.parse(event.newValue);
|
|
|
|
|
} catch(error) {
|
|
|
|
|
icinga.logger.error('[Storage] Failed to parse new value (\`' + event.newValue
|
|
|
|
|
+ '\`) for key "' + event.key + '". Error was: ' + error);
|
|
|
|
|
event.storageArea.removeItem(event.key);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-07-15 13:31:01 +02:00
|
|
|
|
}
|
2019-07-17 11:22:46 +02:00
|
|
|
|
if (!! event.oldValue) {
|
|
|
|
|
try {
|
|
|
|
|
oldValue = JSON.parse(event.oldValue);
|
|
|
|
|
} catch(error) {
|
|
|
|
|
icinga.logger.warn('[Storage] Failed to parse old value (\`' + event.oldValue
|
|
|
|
|
+ '\`) of key "' + event.key + '". Error was: ' + error);
|
|
|
|
|
oldValue = null;
|
|
|
|
|
}
|
2019-07-15 13:31:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-10 16:12:04 +02:00
|
|
|
|
Icinga.Storage.subscribers[event.key].forEach(function (subscriber) {
|
2019-07-15 13:31:01 +02:00
|
|
|
|
subscriber[0].call(subscriber[1], newValue, oldValue, event);
|
2019-07-10 16:12:04 +02:00
|
|
|
|
});
|
2019-07-10 09:32:33 +02:00
|
|
|
|
}
|
|
|
|
|
});
|
2019-07-02 09:44:32 +02:00
|
|
|
|
|
2019-07-04 10:21:50 +02:00
|
|
|
|
/**
|
|
|
|
|
* Create a new storage with `behavior.<name>` as prefix
|
|
|
|
|
*
|
|
|
|
|
* @param {string} name
|
|
|
|
|
*
|
|
|
|
|
* @returns {Icinga.Storage}
|
|
|
|
|
*/
|
|
|
|
|
Icinga.Storage.BehaviorStorage = function(name) {
|
|
|
|
|
return new Icinga.Storage('behavior.' + name);
|
|
|
|
|
};
|
|
|
|
|
|
2019-07-02 09:44:32 +02:00
|
|
|
|
Icinga.Storage.prototype = {
|
|
|
|
|
|
2019-07-29 10:16:36 +02:00
|
|
|
|
/**
|
|
|
|
|
* Set the storage backend
|
|
|
|
|
*
|
|
|
|
|
* @param {Storage} backend
|
|
|
|
|
*/
|
|
|
|
|
setBackend: function(backend) {
|
|
|
|
|
this.backend = backend;
|
|
|
|
|
},
|
|
|
|
|
|
2019-07-02 09:44:32 +02:00
|
|
|
|
/**
|
|
|
|
|
* Prefix the given key
|
|
|
|
|
*
|
|
|
|
|
* @param {string} key
|
2019-07-09 11:35:41 +02:00
|
|
|
|
*
|
2019-07-02 09:44:32 +02:00
|
|
|
|
* @returns {string}
|
|
|
|
|
*/
|
|
|
|
|
prefixKey: function(key) {
|
2019-07-10 08:33:53 +02:00
|
|
|
|
var prefix = 'icinga.';
|
2019-07-04 10:21:50 +02:00
|
|
|
|
if (typeof this.prefix !== 'undefined') {
|
2019-07-10 08:33:53 +02:00
|
|
|
|
prefix = prefix + this.prefix + '.';
|
2019-07-04 10:21:50 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-10 08:33:53 +02:00
|
|
|
|
return prefix + key;
|
2019-07-02 09:44:32 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Store the given key-value pair
|
|
|
|
|
*
|
|
|
|
|
* @param {string} key
|
|
|
|
|
* @param {*} value
|
|
|
|
|
*
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
|
|
|
|
set: function(key, value) {
|
2019-07-29 10:16:36 +02:00
|
|
|
|
this.backend.setItem(this.prefixKey(key), JSON.stringify(value));
|
2019-07-02 09:44:32 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get value for the given key
|
|
|
|
|
*
|
|
|
|
|
* @param {string} key
|
|
|
|
|
*
|
|
|
|
|
* @returns {*}
|
|
|
|
|
*/
|
|
|
|
|
get: function(key) {
|
2019-07-17 11:22:46 +02:00
|
|
|
|
key = this.prefixKey(key);
|
2019-07-29 10:16:36 +02:00
|
|
|
|
var value = this.backend.getItem(key);
|
2019-07-17 11:22:46 +02:00
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return JSON.parse(value);
|
|
|
|
|
} catch(error) {
|
|
|
|
|
icinga.logger.error('[Storage] Failed to parse value (\`' + value
|
|
|
|
|
+ '\`) of key "' + key + '". Error was: ' + error);
|
2019-07-29 10:16:36 +02:00
|
|
|
|
this.backend.removeItem(key);
|
2019-07-17 11:22:46 +02:00
|
|
|
|
return null;
|
|
|
|
|
}
|
2019-07-02 09:44:32 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Remove given key from storage
|
|
|
|
|
*
|
|
|
|
|
* @param {string} key
|
|
|
|
|
*
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
|
|
|
|
remove: function(key) {
|
2019-07-29 10:16:36 +02:00
|
|
|
|
this.backend.removeItem(this.prefixKey(key));
|
2019-07-02 09:44:32 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Subscribe with a callback for events on a particular key
|
|
|
|
|
*
|
|
|
|
|
* @param {string} key
|
|
|
|
|
* @param {function} callback
|
2019-07-08 13:34:38 +02:00
|
|
|
|
* @param {object} context
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
2019-07-10 07:33:07 +02:00
|
|
|
|
onChange: function(key, callback, context) {
|
2019-07-30 08:17:29 +02:00
|
|
|
|
if (this.backend !== window.localStorage) {
|
|
|
|
|
throw new Error('[Storage] Only the localStorage emits events');
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-10 16:12:04 +02:00
|
|
|
|
var prefixedKey = this.prefixKey(key);
|
|
|
|
|
|
|
|
|
|
if (typeof Icinga.Storage.subscribers[prefixedKey] === 'undefined') {
|
|
|
|
|
Icinga.Storage.subscribers[prefixedKey] = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Icinga.Storage.subscribers[prefixedKey].push([callback, context]);
|
2019-07-02 09:44:32 +02:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Icinga.Storage.StorageAwareMap
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* @param {object} items
|
2019-07-02 09:44:32 +02:00
|
|
|
|
* @constructor
|
|
|
|
|
*/
|
2019-07-09 11:35:41 +02:00
|
|
|
|
Icinga.Storage.StorageAwareMap = function(items) {
|
2019-07-02 09:44:32 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Storage object
|
|
|
|
|
*
|
|
|
|
|
* @type {Icinga.Storage}
|
|
|
|
|
*/
|
|
|
|
|
this.storage = undefined;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Storage key
|
|
|
|
|
*
|
|
|
|
|
* @type {string}
|
|
|
|
|
*/
|
|
|
|
|
this.key = undefined;
|
|
|
|
|
|
2019-07-10 16:03:47 +02:00
|
|
|
|
/**
|
|
|
|
|
* Event listeners for our internal events
|
|
|
|
|
*
|
|
|
|
|
* @type {{}}
|
|
|
|
|
*/
|
|
|
|
|
this.eventListeners = {
|
|
|
|
|
'add': [],
|
|
|
|
|
'delete': []
|
|
|
|
|
};
|
|
|
|
|
|
2019-07-02 09:44:32 +02:00
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* The internal (real) map
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* @type {Map<*>}
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*/
|
2019-07-09 11:35:41 +02:00
|
|
|
|
this.data = new Map();
|
2019-07-02 09:44:32 +02:00
|
|
|
|
|
|
|
|
|
// items is not passed directly because IE11 doesn't support constructor arguments
|
2019-07-09 11:35:41 +02:00
|
|
|
|
if (typeof items !== 'undefined' && !! items) {
|
|
|
|
|
Object.keys(items).forEach(function(key) {
|
|
|
|
|
this.data.set(key, items[key]);
|
2019-07-02 09:44:32 +02:00
|
|
|
|
}, this);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Create a new StorageAwareMap for the given storage and key
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @param {Icinga.Storage} storage
|
|
|
|
|
* @param {string} key
|
|
|
|
|
*
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* @returns {Icinga.Storage.StorageAwareMap}
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*/
|
2019-07-09 11:35:41 +02:00
|
|
|
|
Icinga.Storage.StorageAwareMap.withStorage = function(storage, key) {
|
2019-07-09 15:55:52 +02:00
|
|
|
|
var items = storage.get(key);
|
|
|
|
|
if (typeof items !== 'undefined' && !! items) {
|
|
|
|
|
Object.keys(items).forEach(function(key) {
|
|
|
|
|
var value = items[key];
|
|
|
|
|
|
|
|
|
|
if (typeof value !== 'object' || typeof value['lastAccess'] === 'undefined') {
|
|
|
|
|
items[key] = {'value': value, 'lastAccess': Date.now()};
|
|
|
|
|
} else if (Date.now() - value['lastAccess'] > KEY_TTL) {
|
|
|
|
|
delete items[key];
|
|
|
|
|
}
|
|
|
|
|
}, this);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-25 10:14:48 +02:00
|
|
|
|
if (!! items && Object.keys(items).length) {
|
2019-07-18 07:43:42 +02:00
|
|
|
|
storage.set(key, items);
|
2019-07-25 10:14:48 +02:00
|
|
|
|
} else if (items !== null) {
|
2019-07-18 07:43:42 +02:00
|
|
|
|
storage.remove(key);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-09 15:55:52 +02:00
|
|
|
|
return (new Icinga.Storage.StorageAwareMap(items).setStorage(storage, key));
|
2019-07-02 09:44:32 +02:00
|
|
|
|
};
|
|
|
|
|
|
2019-07-09 11:35:41 +02:00
|
|
|
|
Icinga.Storage.StorageAwareMap.prototype = {
|
2019-07-02 09:44:32 +02:00
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Bind this map to the given storage and key
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @param {Icinga.Storage} storage
|
|
|
|
|
* @param {string} key
|
|
|
|
|
*
|
|
|
|
|
* @returns {this}
|
|
|
|
|
*/
|
|
|
|
|
setStorage: function(storage, key) {
|
|
|
|
|
this.storage = storage;
|
|
|
|
|
this.key = key;
|
|
|
|
|
|
2019-07-30 08:17:29 +02:00
|
|
|
|
if (storage.backend === window.localStorage) {
|
|
|
|
|
storage.onChange(key, this.onChange, this);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-02 09:44:32 +02:00
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Return a boolean indicating this map got a storage
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @returns {boolean}
|
|
|
|
|
*/
|
|
|
|
|
hasStorage: function() {
|
|
|
|
|
return typeof this.storage !== 'undefined' && typeof this.key !== 'undefined';
|
|
|
|
|
},
|
|
|
|
|
|
2019-07-09 15:55:52 +02:00
|
|
|
|
/**
|
|
|
|
|
* Update the storage
|
|
|
|
|
*
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
|
|
|
|
updateStorage: function() {
|
|
|
|
|
if (! this.hasStorage()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.size > 0) {
|
|
|
|
|
this.storage.set(this.key, this.toObject());
|
|
|
|
|
} else {
|
|
|
|
|
this.storage.remove(this.key);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2019-07-02 09:44:32 +02:00
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Update the map
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
2019-07-09 15:55:52 +02:00
|
|
|
|
* @param {object} newValue
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*/
|
2019-07-10 07:51:48 +02:00
|
|
|
|
onChange: function(newValue) {
|
2019-07-09 15:55:52 +02:00
|
|
|
|
// Check for deletions first. Uses keys() to iterate over a copy
|
2019-07-09 11:35:41 +02:00
|
|
|
|
this.keys().forEach(function (key) {
|
2019-07-10 07:51:48 +02:00
|
|
|
|
if (newValue === null || typeof newValue[key] === 'undefined') {
|
2019-07-10 16:03:47 +02:00
|
|
|
|
var value = this.data.get(key)['value'];
|
2019-07-09 11:35:41 +02:00
|
|
|
|
this.data.delete(key);
|
2019-07-10 16:03:47 +02:00
|
|
|
|
this.trigger('delete', key, value);
|
2019-07-02 09:44:32 +02:00
|
|
|
|
}
|
|
|
|
|
}, this);
|
|
|
|
|
|
2019-07-10 07:51:48 +02:00
|
|
|
|
if (newValue === null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-02 09:44:32 +02:00
|
|
|
|
// Now check for new entries
|
2019-07-09 11:35:41 +02:00
|
|
|
|
Object.keys(newValue).forEach(function(key) {
|
2019-07-09 15:55:52 +02:00
|
|
|
|
var known = this.data.has(key);
|
|
|
|
|
// Always override any known value as we want to keep track of all `lastAccess` changes
|
|
|
|
|
this.data.set(key, newValue[key]);
|
|
|
|
|
|
|
|
|
|
if (! known) {
|
2019-07-10 16:03:47 +02:00
|
|
|
|
this.trigger('add', key, newValue[key]['value']);
|
2019-07-02 09:44:32 +02:00
|
|
|
|
}
|
|
|
|
|
}, this);
|
|
|
|
|
},
|
|
|
|
|
|
2019-07-03 15:56:08 +02:00
|
|
|
|
/**
|
|
|
|
|
* Register an event handler to handle storage updates
|
|
|
|
|
*
|
2019-07-10 16:03:47 +02:00
|
|
|
|
* Available events are: add, delete. The callback receives the
|
|
|
|
|
* key and its value as first and second argument, respectively.
|
2019-07-03 15:56:08 +02:00
|
|
|
|
*
|
|
|
|
|
* @param {string} event
|
2019-07-10 16:03:47 +02:00
|
|
|
|
* @param {function} callback
|
|
|
|
|
* @param {object} thisArg
|
2019-07-03 15:56:08 +02:00
|
|
|
|
*
|
|
|
|
|
* @returns {this}
|
|
|
|
|
*/
|
2019-07-10 16:03:47 +02:00
|
|
|
|
on: function(event, callback, thisArg) {
|
|
|
|
|
if (typeof this.eventListeners[event] === 'undefined') {
|
|
|
|
|
throw new Error('Invalid event "' + event + '"');
|
|
|
|
|
}
|
2019-07-03 15:56:08 +02:00
|
|
|
|
|
2019-07-10 16:03:47 +02:00
|
|
|
|
this.eventListeners[event].push([callback, thisArg]);
|
2019-07-03 15:56:08 +02:00
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
|
2019-07-10 16:03:47 +02:00
|
|
|
|
/**
|
|
|
|
|
* Trigger all event handlers for the given event
|
|
|
|
|
*
|
|
|
|
|
* @param {string} event
|
|
|
|
|
* @param {string} key
|
|
|
|
|
* @param {*} value
|
|
|
|
|
*/
|
|
|
|
|
trigger: function(event, key, value) {
|
|
|
|
|
this.eventListeners[event].forEach(function (handler) {
|
|
|
|
|
var thisArg = handler[1];
|
|
|
|
|
if (typeof thisArg === 'undefined') {
|
|
|
|
|
thisArg = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handler[0].call(thisArg, key, value);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2019-07-02 09:44:32 +02:00
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Return the number of key/value pairs in the map
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @returns {number}
|
|
|
|
|
*/
|
|
|
|
|
get size() {
|
|
|
|
|
return this.data.size;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Set the value for the key in the map
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* @param {string} key
|
2019-07-10 08:04:10 +02:00
|
|
|
|
* @param {*} value Default null
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @returns {this}
|
|
|
|
|
*/
|
2019-07-09 11:35:41 +02:00
|
|
|
|
set: function(key, value) {
|
2019-07-10 08:04:10 +02:00
|
|
|
|
if (typeof value === 'undefined') {
|
|
|
|
|
value = null;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-09 15:55:52 +02:00
|
|
|
|
this.data.set(key, {'value': value, 'lastAccess': Date.now()});
|
2019-07-02 09:44:32 +02:00
|
|
|
|
|
2019-07-09 15:55:52 +02:00
|
|
|
|
this.updateStorage();
|
2019-07-02 09:44:32 +02:00
|
|
|
|
return this;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Remove all key/value pairs from the map
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
|
|
|
|
clear: function() {
|
2019-07-09 15:55:52 +02:00
|
|
|
|
this.data.clear();
|
|
|
|
|
this.updateStorage();
|
2019-07-02 09:44:32 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Remove the given key from the map
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* @param {string} key
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @returns {boolean}
|
|
|
|
|
*/
|
2019-07-09 11:35:41 +02:00
|
|
|
|
delete: function(key) {
|
|
|
|
|
var retVal = this.data.delete(key);
|
2019-07-02 09:44:32 +02:00
|
|
|
|
|
2019-07-09 15:55:52 +02:00
|
|
|
|
this.updateStorage();
|
2019-07-02 09:44:32 +02:00
|
|
|
|
return retVal;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Return a list of [key, value] pairs for every item in the map
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* @returns {Array}
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*/
|
|
|
|
|
entries: function() {
|
2019-07-09 11:35:41 +02:00
|
|
|
|
var list = [];
|
|
|
|
|
|
|
|
|
|
if (this.size > 0) {
|
2019-07-09 15:55:52 +02:00
|
|
|
|
this.data.forEach(function (value, key) {
|
|
|
|
|
list.push([key, value['value']]);
|
2019-07-09 11:35:41 +02:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return list;
|
2019-07-02 09:44:32 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Execute a provided function once for each item in the map, in insertion order
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* @param {function} callback
|
2019-07-09 15:55:52 +02:00
|
|
|
|
* @param {object} thisArg
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
2019-07-09 15:55:52 +02:00
|
|
|
|
forEach: function(callback, thisArg) {
|
|
|
|
|
if (typeof thisArg === 'undefined') {
|
|
|
|
|
thisArg = this;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-17 12:46:14 +02:00
|
|
|
|
this.data.forEach(function(value, key) {
|
2019-07-09 15:55:52 +02:00
|
|
|
|
callback.call(thisArg, value['value'], key);
|
|
|
|
|
});
|
2019-07-02 09:44:32 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Return the value associated to the key, or undefined if there is none
|
|
|
|
|
*
|
|
|
|
|
* @param {string} key
|
|
|
|
|
*
|
|
|
|
|
* @returns {*}
|
|
|
|
|
*/
|
|
|
|
|
get: function(key) {
|
2019-07-09 15:55:52 +02:00
|
|
|
|
var value = this.data.get(key)['value'];
|
|
|
|
|
this.set(key, value); // Update `lastAccess`
|
|
|
|
|
|
|
|
|
|
return value;
|
2019-07-09 11:35:41 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a boolean asserting whether a value has been associated to the key in the map
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* @param {string} key
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @returns {boolean}
|
|
|
|
|
*/
|
2019-07-09 11:35:41 +02:00
|
|
|
|
has: function(key) {
|
|
|
|
|
return this.data.has(key);
|
2019-07-02 09:44:32 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
2019-07-09 11:35:41 +02:00
|
|
|
|
* Return an array of keys in the map
|
|
|
|
|
*
|
|
|
|
|
* @returns {Array}
|
|
|
|
|
*/
|
|
|
|
|
keys: function() {
|
|
|
|
|
var list = [];
|
|
|
|
|
|
|
|
|
|
if (this.size > 0) {
|
|
|
|
|
// .forEach() is used because IE11 doesn't support .keys()
|
2019-07-09 15:55:52 +02:00
|
|
|
|
this.data.forEach(function(_, key) {
|
2019-07-09 11:35:41 +02:00
|
|
|
|
list.push(key);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return an array of values in the map
|
2019-07-02 09:44:32 +02:00
|
|
|
|
*
|
|
|
|
|
* @returns {Array}
|
|
|
|
|
*/
|
|
|
|
|
values: function() {
|
|
|
|
|
var list = [];
|
|
|
|
|
|
|
|
|
|
if (this.size > 0) {
|
|
|
|
|
// .forEach() is used because IE11 doesn't support .values()
|
2019-07-09 15:55:52 +02:00
|
|
|
|
this.data.forEach(function(value) {
|
|
|
|
|
list.push(value['value']);
|
2019-07-02 09:44:32 +02:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return list;
|
2019-07-09 11:35:41 +02:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return this map as simple object
|
|
|
|
|
*
|
|
|
|
|
* @returns {object}
|
|
|
|
|
*/
|
|
|
|
|
toObject: function() {
|
|
|
|
|
var obj = {};
|
|
|
|
|
|
|
|
|
|
if (this.size > 0) {
|
2019-07-09 15:55:52 +02:00
|
|
|
|
this.data.forEach(function (value, key) {
|
2019-07-09 11:35:41 +02:00
|
|
|
|
obj[key] = value;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return obj;
|
2019-07-02 09:44:32 +02:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-07-10 16:03:47 +02:00
|
|
|
|
}(Icinga));
|