@leaddreamer/inventorylogic 0.0.3-beta.39 → 0.0.3-beta.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -334,3 +334,5 @@ export function choicesForm(fieldname) {
334
334
  ? Forms.Choices[fieldname]
335
335
  : {};
336
336
  }
337
+
338
+ export * from "./other.js";
@@ -0,0 +1,469 @@
1
+ /**
2
+ * Typedefs, constants, support functions and database operations for
3
+ * general Utilities
4
+ * @module Utilities
5
+ */
6
+ import dayjs from "dayjs";
7
+ import promisePool from "es6-promise-pool";
8
+ import isSameOrAfter from "dayjs/plugin/isSameOrAfter";
9
+ import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
10
+ import {
11
+ fetchRecordByRefPath,
12
+ runTransaction,
13
+ writeBack,
14
+ deleteRecordByRefPath,
15
+ } from "@leaddreamer/firebase-wrapper";
16
+ //the following inits the dayjs plugins
17
+
18
+ dayjs.extend(isSameOrAfter);
19
+ dayjs.extend(isSameOrBefore);
20
+
21
+ /**
22
+ * A convenience function for some shared artist/host front-end pages
23
+ * @param {OrganizationType} host
24
+ * @returns {Promise.OrganizationType}
25
+ */
26
+ export async function saveHost(host, batch = null) {
27
+ return writeBack(host, batch);
28
+ }
29
+
30
+ /**
31
+ * Simple top-level object compare - assumes objects have similar structure - or
32
+ * AT LEAST all the fields of object1 are present in object2
33
+ * @param {object} object1
34
+ * @param {object} object2
35
+ * @returns {boolean}
36
+ */
37
+ export function simpleObjectCompare(object1, object2) {
38
+ let result = Object.keys(object1).reduce((accumulate, key) => {
39
+ // eslint-disable-next-line
40
+ return object1[key] == object2[key] ? accumulate : false;
41
+ }, true); // note simple (not object) compare
42
+ return result;
43
+ }
44
+
45
+ /**
46
+ * Allows string substitution from an object
47
+ * field names support Dot notation
48
+ * field names allow ":" extension for alternate formatting
49
+ * @param {object} thisItem - object to create formated string from
50
+ * @param {string} thisFormat - string describing formatted output - fieldname wrapped in {} will be substituted. DOT notation allowed for fields.
51
+ * @returns {string}
52
+ */
53
+ export function formatItem(thisObject, thisFormat) {
54
+ let final = thisFormat.replace(
55
+ /{.+?}/g, //global, non-greedy - i.e. multiple {} bracketed elements
56
+ function (field) {
57
+ let fieldRaw = field.slice(1, -1);
58
+ //below allows for DOT notation access within the item object
59
+ let fieldparts = fieldRaw.split(":");
60
+ let [fieldname, fieldStyle] = fieldparts;
61
+ let fieldArray = fieldname.split(".");
62
+ //below walks into an object - note: ASSUMES an object
63
+ const bob = fieldArray.reduce((lastObject, nextName) => {
64
+ return lastObject[nextName];
65
+ }, thisObject);
66
+ return bob ? specialFormat(bob, fieldStyle) : "----";
67
+ }
68
+ );
69
+ return final;
70
+ }
71
+
72
+ /**
73
+ * @private
74
+ * @param {any} value
75
+ * @param {!string} style
76
+ * @returns {any|string}
77
+ */
78
+ function specialFormat(value, style) {
79
+ switch (style) {
80
+ case "timestamp":
81
+ return dayjs(value.toDate()).format("MMM DD, YYYY");
82
+ default:
83
+ return value;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * @param {object} thisItem - one of two objects being compared
89
+ * @param {object} anotherItem - the other of two objects being compared
90
+ * @param {!string} sort - string describing sort - format "fieldName:sortType" - sort Type is "string, "date" or "number" (default)
91
+ * @return {string}
92
+ */
93
+ export function compare(thisItem, anotherItem, sort = "Id:number") {
94
+ let [field, type] = sort.split(":");
95
+ type = type ? type : "number";
96
+ switch (type) {
97
+ case "string":
98
+ let nameArray = thisItem[field].split("The ");
99
+ let anotherArray = anotherItem[field].split("The ");
100
+ return (nameArray[0] ? nameArray[0] : nameArray[1]).localeCompare(
101
+ anotherArray[0] ? anotherArray[0] : anotherArray[1]
102
+ );
103
+
104
+ case "date":
105
+ //if (!thisItem[field] || !anotherItem[field]) return 0;
106
+ return !thisItem[field] ||
107
+ dayjs(thisItem[field]).isBefore(anotherItem[field], "day")
108
+ ? -1
109
+ : anotherItem[field] &&
110
+ dayjs(thisItem[field]).isSame(anotherItem[field], "day")
111
+ ? 0
112
+ : 1;
113
+
114
+ default:
115
+ return Number(thisItem[field]) - Number(anotherItem[field]);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * An array based compare function - is any entry of findFrom present in findIn
121
+ * @param {Array.any} findFrom
122
+ * @param {Array.any} findIn
123
+ * @returns {boolean}
124
+ */
125
+ export function anyInAny(findFrom, findIn) {
126
+ let mapFindIn = new Map();
127
+
128
+ findIn.forEach((entry, index) => {
129
+ mapFindIn.set(entry, true);
130
+ }); //speed up by avoiding second loop
131
+
132
+ const findFromLength = findFrom.length;
133
+ let found = false;
134
+ for (
135
+ let findFromIndex = 0;
136
+ findFromIndex < findFromLength && !found;
137
+ findFromIndex++
138
+ )
139
+ if (
140
+ mapFindIn.get(findFrom[findFromIndex].valueOf())
141
+ //findFrom[findFromIndex].valueOf() === findIn[arrayTwoIndex].valueOf()
142
+ )
143
+ found = true; //note this breaks both loops
144
+
145
+ return found;
146
+ }
147
+
148
+ /**
149
+ * An Action Iterator Generator. Cycles through the entries in the passed array,
150
+ * yielding the result of a function for each entry.
151
+ * Only cares that inputs are an array, and a function that returns a
152
+ * promise
153
+ * @generator
154
+ * @param {Array.any} entryArray
155
+ * @param {callback} actionFunction
156
+ * @yields {function}
157
+ */
158
+ function* generateIterator(entryArray, actionFunction) {
159
+ let countLimit = entryArray.length;
160
+ //next is testing remnant
161
+ //countLimit = lessorOf(10, countLimit);
162
+ for (let count = 0; count < countLimit; count++) {
163
+ yield actionFunction(entryArray[count]);
164
+ }
165
+ }
166
+
167
+ /**
168
+ *
169
+ */
170
+ const PromisePool = promisePool.PromisePool;
171
+
172
+ /**
173
+ * @typedef {object} PromiseOptions
174
+ * @property {number} concurrency
175
+ * @property {number} rate
176
+ */
177
+ /**
178
+ * @readonly
179
+ * @constant
180
+ * @type {PromiseOptions}
181
+ * @default
182
+ */
183
+ const promiseOptionsDefault = {
184
+ concurrency: 3, //simultaneous
185
+ rate: 10, //per second
186
+ };
187
+
188
+ /**
189
+ * @param {Array.object} entryArray
190
+ * @param {callback} actionFunction
191
+ * @param {PromiseOptions} promiseOptions
192
+ * @returns {Promise}
193
+ */
194
+ export async function PromisePoolRunner(
195
+ entryArray,
196
+ actionFunction,
197
+ promiseOptions = promiseOptionsDefault
198
+ ) {
199
+ if (entryArray.length) {
200
+ const promiseIterator = generateIterator(entryArray, actionFunction);
201
+ const promisePool = new PromisePool(
202
+ promiseIterator,
203
+ promiseOptions.concurrency
204
+ );
205
+ return promisePool.start();
206
+ } else {
207
+ return Promise.resolve(null);
208
+ }
209
+ }
210
+
211
+ /**
212
+ * A convenience function for legibility
213
+ * @param {any} args unbounded list of arguments
214
+ * @returns {number} the least of any of the arguments
215
+ b */
216
+ export function lesserOf(...args) {
217
+ return Math.min(...args);
218
+ }
219
+
220
+ /**
221
+ * @param {any} args unbounded list of arguments
222
+ * @returns {number} the largest of any of the arguments
223
+ */
224
+ export function largerOf(...args) {
225
+ return Math.max(...args);
226
+ }
227
+
228
+ /**
229
+ * "Moves" a record from our standard tree structureto a parallel archival
230
+ * structure, by prepending a "_" to the collection names in the path
231
+ * This prevents the archived record from appearing in collectionGroup queries
232
+ * @param {RecordDocument} record
233
+ * @param {!string} record.refPath
234
+ * @returns {Promise}
235
+ */
236
+ export async function archiveRecord(record) {
237
+ //split the reference path into it's constituent parts
238
+ const parts = record.refPath.split("/");
239
+ const partsCount = parts.length;
240
+ if (Math.floor(partsCount / 2) * 2 !== partsCount) {
241
+ //throw "safe" error
242
+ return; //returns a null promise resolved
243
+ }
244
+
245
+ let archivePath = "";
246
+ let index = 0;
247
+ //adde a "_" to the start of every collection named in the path
248
+ //this avoids collectionGroup issues, but follows a parallel path
249
+ do {
250
+ archivePath = `${archivePath}/_${parts[index]}/${parts[index + 1]}`;
251
+ index += 2;
252
+ } while (index < partsCount);
253
+ await runTransaction(async (batch) => {
254
+ //////////////////////////////
255
+ //Read a current full copy of the record
256
+ const realRecord = await fetchRecordByRefPath(record.refPath, batch);
257
+ //Write it to the "archivePath"
258
+ await writeBack({ ...realRecord, refPath: archivePath }, batch);
259
+ //delete the original copy
260
+ await deleteRecordByRefPath(realRecord.refPath, batch);
261
+ });
262
+ }
263
+
264
+ /**
265
+ * uses a passed refPath to create the most basic document record form
266
+ * @param {!string} refPath - "/" separated document path reference
267
+ * @returns {RecordDocument}
268
+ */
269
+ export function refPathToBaseRecord(refPath) {
270
+ return {
271
+ Id: refPath.split("/").pop(),
272
+ refPath: refPath,
273
+ };
274
+ }
275
+
276
+ /**
277
+ * clips value to given bounds
278
+ * @param {number} value
279
+ * @param {number} lowerBound
280
+ * @param {number} upperBound
281
+ * @returns {number}
282
+ */
283
+ export function bound(value, lowerBound, upperBound) {
284
+ return largerOf(lesserOf(value, upperBound), lowerBound);
285
+ }
286
+
287
+ /**
288
+ * function to push an element to an array which may not be initialized
289
+ * @param {Array.object} targetArray
290
+ * @param {object} newObject
291
+ *
292
+ */
293
+ export function blindPush(targetArray, newObject) {
294
+ targetArray
295
+ ? targetArray.push(newObject)
296
+ : (targetArray = [newObject]);
297
+ return targetArray;
298
+ }
299
+
300
+ /**
301
+ * @param {Array.Array.object} arrayObject
302
+ * @param {string} key key or index to choose which Array.object to add *this* object to
303
+ * @param {object} object
304
+ */
305
+ export function addToArrayObject(arrayObject, key, object) {
306
+ if (arrayObject[key] && arrayObject[key].length) {
307
+ //if the key is already present, *and* there are already objects in the keyed array
308
+ arrayObject[key].push(object); //push the object onto the indexed array
309
+ } else {
310
+ arrayObject[key] = [object];
311
+ }
312
+ }
313
+
314
+
315
+
316
+ /**
317
+ * @param {string} color name of color
318
+ * @returns {string} hex value of named color
319
+ */
320
+ export function makeHexAsNeeded(color) {
321
+ var colours = {
322
+ aliceblue: "#f0f8ff",
323
+ antiquewhite: "#faebd7",
324
+ aqua: "#00ffff",
325
+ aquamarine: "#7fffd4",
326
+ azure: "#f0ffff",
327
+ beige: "#f5f5dc",
328
+ bisque: "#ffe4c4",
329
+ black: "#000000",
330
+ blanchedalmond: "#ffebcd",
331
+ blue: "#0000ff",
332
+ blueviolet: "#8a2be2",
333
+ brown: "#a52a2a",
334
+ burlywood: "#deb887",
335
+ cadetblue: "#5f9ea0",
336
+ chartreuse: "#7fff00",
337
+ chocolate: "#d2691e",
338
+ coral: "#ff7f50",
339
+ cornflowerblue: "#6495ed",
340
+ cornsilk: "#fff8dc",
341
+ crimson: "#dc143c",
342
+ cyan: "#00ffff",
343
+ darkblue: "#00008b",
344
+ darkcyan: "#008b8b",
345
+ darkgoldenrod: "#b8860b",
346
+ darkgray: "#a9a9a9",
347
+ darkgreen: "#006400",
348
+ darkkhaki: "#bdb76b",
349
+ darkmagenta: "#8b008b",
350
+ darkolivegreen: "#556b2f",
351
+ darkorange: "#ff8c00",
352
+ darkorchid: "#9932cc",
353
+ darkred: "#8b0000",
354
+ darksalmon: "#e9967a",
355
+ darkseagreen: "#8fbc8f",
356
+ darkslateblue: "#483d8b",
357
+ darkslategray: "#2f4f4f",
358
+ darkturquoise: "#00ced1",
359
+ darkviolet: "#9400d3",
360
+ deeppink: "#ff1493",
361
+ deepskyblue: "#00bfff",
362
+ dimgray: "#696969",
363
+ dodgerblue: "#1e90ff",
364
+ firebrick: "#b22222",
365
+ floralwhite: "#fffaf0",
366
+ forestgreen: "#228b22",
367
+ fuchsia: "#ff00ff",
368
+ gainsboro: "#dcdcdc",
369
+ ghostwhite: "#f8f8ff",
370
+ gold: "#ffd700",
371
+ goldenrod: "#daa520",
372
+ gray: "#808080",
373
+ green: "#008000",
374
+ greenyellow: "#adff2f",
375
+ honeydew: "#f0fff0",
376
+ hotpink: "#ff69b4",
377
+ "indianred ": "#cd5c5c",
378
+ indigo: "#4b0082",
379
+ ivory: "#fffff0",
380
+ khaki: "#f0e68c",
381
+ lavender: "#e6e6fa",
382
+ lavenderblush: "#fff0f5",
383
+ lawngreen: "#7cfc00",
384
+ lemonchiffon: "#fffacd",
385
+ lightblue: "#add8e6",
386
+ lightcoral: "#f08080",
387
+ lightcyan: "#e0ffff",
388
+ lightgoldenrodyellow: "#fafad2",
389
+ lightgrey: "#d3d3d3",
390
+ lightgreen: "#90ee90",
391
+ lightpink: "#ffb6c1",
392
+ lightsalmon: "#ffa07a",
393
+ lightseagreen: "#20b2aa",
394
+ lightskyblue: "#87cefa",
395
+ lightslategray: "#778899",
396
+ lightsteelblue: "#b0c4de",
397
+ lightyellow: "#ffffe0",
398
+ lime: "#00ff00",
399
+ limegreen: "#32cd32",
400
+ linen: "#faf0e6",
401
+ magenta: "#ff00ff",
402
+ maroon: "#800000",
403
+ mediumaquamarine: "#66cdaa",
404
+ mediumblue: "#0000cd",
405
+ mediumorchid: "#ba55d3",
406
+ mediumpurple: "#9370d8",
407
+ mediumseagreen: "#3cb371",
408
+ mediumslateblue: "#7b68ee",
409
+ mediumspringgreen: "#00fa9a",
410
+ mediumturquoise: "#48d1cc",
411
+ mediumvioletred: "#c71585",
412
+ midnightblue: "#191970",
413
+ mintcream: "#f5fffa",
414
+ mistyrose: "#ffe4e1",
415
+ moccasin: "#ffe4b5",
416
+ navajowhite: "#ffdead",
417
+ navy: "#000080",
418
+ oldlace: "#fdf5e6",
419
+ olive: "#808000",
420
+ olivedrab: "#6b8e23",
421
+ orange: "#ffa500",
422
+ orangered: "#ff4500",
423
+ orchid: "#da70d6",
424
+ palegoldenrod: "#eee8aa",
425
+ palegreen: "#98fb98",
426
+ paleturquoise: "#afeeee",
427
+ palevioletred: "#d87093",
428
+ papayawhip: "#ffefd5",
429
+ peachpuff: "#ffdab9",
430
+ peru: "#cd853f",
431
+ pink: "#ffc0cb",
432
+ plum: "#dda0dd",
433
+ powderblue: "#b0e0e6",
434
+ purple: "#800080",
435
+ rebeccapurple: "#663399",
436
+ red: "#ff0000",
437
+ rosybrown: "#bc8f8f",
438
+ royalblue: "#4169e1",
439
+ saddlebrown: "#8b4513",
440
+ salmon: "#fa8072",
441
+ sandybrown: "#f4a460",
442
+ seagreen: "#2e8b57",
443
+ seashell: "#fff5ee",
444
+ sienna: "#a0522d",
445
+ silver: "#c0c0c0",
446
+ skyblue: "#87ceeb",
447
+ slateblue: "#6a5acd",
448
+ slategray: "#708090",
449
+ snow: "#fffafa",
450
+ springgreen: "#00ff7f",
451
+ steelblue: "#4682b4",
452
+ tan: "#d2b48c",
453
+ teal: "#008080",
454
+ thistle: "#d8bfd8",
455
+ tomato: "#ff6347",
456
+ turquoise: "#40e0d0",
457
+ violet: "#ee82ee",
458
+ wheat: "#f5deb3",
459
+ white: "#ffffff",
460
+ whitesmoke: "#f5f5f5",
461
+ yellow: "#ffff00",
462
+ yellowgreen: "#9acd32",
463
+ };
464
+
465
+ if (colours[color.toLowerCase()]) return colours[color.toLowerCase()];
466
+ return color;
467
+ }
468
+
469
+ export * from "./promise-queue";
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Typedefs, constants, support functions and database operations related to Promise getQueueSize
3
+ * Thes era euseful for scheduling large serial asynchronous operations
4
+ * @module PromiseQueue
5
+ */
6
+ (function () {
7
+ /**
8
+ * Creates a new rate limited queue. The queue is created with stopped state, i.e. you have to call start() to run jobs.
9
+ * @param {number} maxCallsPerSecond=1 - The rate limit of this queue in maximum calls per seconds which may be executed at any time. Pass values less than 1 to have less than one call per second, e.g. 1/3 for 3 one call in three seconds at maximum.
10
+ * @param logger
11
+ * @constructor
12
+ */
13
+ function PromiseQueue(maxCallsPerSecond, logger) {
14
+ this.maxCallsPerSecond =
15
+ typeof maxCallsPerSecond === "undefined" ? 1 : maxCallsPerSecond;
16
+ this.logger = logger;
17
+
18
+ this.tasks = [];
19
+ this.isActive = false;
20
+ this.jobIntervalMillis = 1000 / maxCallsPerSecond;
21
+
22
+ this.lastTaskTimestamp = null;
23
+ this.currentTaskTimeoutId = null;
24
+ }
25
+
26
+ /**
27
+ * Processes the next task and returns the id of the time used to wait for the next invocation of this method.
28
+ * @private
29
+ */
30
+ PromiseQueue.prototype.updateTaskSchedule = function () {
31
+ if (this.logger && this.logger.debug) {
32
+ this.logger.debug("PromiseQueue.updateTaskSchedule");
33
+ }
34
+
35
+ if (this.isEmpty() || this.isStopped()) {
36
+ return;
37
+ }
38
+
39
+ const queue = this;
40
+
41
+ function processNextTask() {
42
+ if (queue.logger && queue.logger.debug) {
43
+ queue.logger.debug("PromiseQueue.processNextTask", queue.tasks.length);
44
+ }
45
+
46
+ var nextTaskItem = queue.tasks.shift();
47
+ if (nextTaskItem) {
48
+ try {
49
+ const result = nextTaskItem.task.call();
50
+
51
+ if (queue.logger && queue.logger.debug) {
52
+ queue.logger.debug(
53
+ "Resolving queued promise",
54
+ result,
55
+ result instanceof Promise
56
+ );
57
+ }
58
+
59
+ nextTaskItem.resolve(result);
60
+ } catch (e) {
61
+ queue.logger.info("rejecting queued promise result", e);
62
+ nextTaskItem.reject(e);
63
+ }
64
+ }
65
+ }
66
+
67
+ function scheduleNext(timeoutMillisOverride) {
68
+ if (queue.logger && queue.logger.debug) {
69
+ queue.logger.debug("PromiseQueue.scheduleNext", timeoutMillisOverride);
70
+ }
71
+
72
+ if (queue.isEmpty()) {
73
+ queue.currentTaskTimeoutId = null;
74
+ return;
75
+ }
76
+
77
+ queue.currentTaskTimeoutId = setTimeout(function () {
78
+ queue.lastTaskTimestamp = Date.now();
79
+ processNextTask();
80
+
81
+ scheduleNext(queue.jobIntervalMillis);
82
+ }, timeoutMillisOverride);
83
+ }
84
+
85
+ if (this.currentTaskTimeoutId == null) {
86
+ if (this.lastTaskTimestamp) {
87
+ let passedTime = Date.now() - this.lastTaskTimestamp;
88
+ if (passedTime > this.jobIntervalMillis) {
89
+ scheduleNext(this.jobIntervalMillis);
90
+ } else {
91
+ scheduleNext(Math.max(0, this.jobIntervalMillis - passedTime));
92
+ }
93
+ } else {
94
+ scheduleNext(0);
95
+ }
96
+ }
97
+ };
98
+
99
+ /**
100
+ * Starts the task processing of this queue.
101
+ *
102
+ * If it is already started, then false will be returned.
103
+ * If it is not yet started then the first task will be processed in the background and true will be returned.
104
+ *
105
+ * @return {boolean} true if this queue wasn't started before and now started to process tasks.
106
+ */
107
+ PromiseQueue.prototype.start = function () {
108
+ if (this.isActive) {
109
+ return false;
110
+ }
111
+
112
+ this.isActive = true;
113
+
114
+ this.updateTaskSchedule();
115
+
116
+ return true;
117
+ };
118
+
119
+ /**
120
+ * Stops the processing of this queue.
121
+ * @return true if the queue was started and is now stopped. false if it was already stopped.
122
+ */
123
+ PromiseQueue.prototype.stop = function () {
124
+ if (this.isActive) {
125
+ this.isActive = false;
126
+
127
+ if (this.currentTaskTimeoutId) {
128
+ clearInterval(this.currentTaskTimeoutId);
129
+ this.currentTaskTimeoutId = null;
130
+ this.lastTaskTimestamp = null;
131
+ }
132
+ }
133
+ };
134
+
135
+ PromiseQueue.prototype.getMaxCallsPerSecond = function () {
136
+ return this.maxCallsPerSecond;
137
+ };
138
+
139
+ PromiseQueue.prototype.isStarted = function () {
140
+ return this.isActive;
141
+ };
142
+
143
+ PromiseQueue.prototype.isStopped = function () {
144
+ return !this.isStarted();
145
+ };
146
+
147
+ /**
148
+ * @returns {boolean} true if this queue is currently empty, i.e. if it has no tasks in the queue.
149
+ */
150
+ PromiseQueue.prototype.isEmpty = function () {
151
+ return this.tasks.length === 0;
152
+ };
153
+
154
+ /**
155
+ * @returns {boolean} true if this queue has one or more entries.
156
+ */
157
+ PromiseQueue.prototype.isNotEmpty = function () {
158
+ return !this.isEmpty();
159
+ };
160
+
161
+ /**
162
+ * @param {function} task - The task to add.
163
+ * @param {boolean} append - if the task should be appended or prepended to the list of queued tasks
164
+ * @return {Promise} A promise which is resolved/rejected when the task was executed by the queue.
165
+ */
166
+ PromiseQueue.prototype.add = function (task, append) {
167
+ var self = this;
168
+
169
+ var promise = new Promise(function (resolve, reject) {
170
+ var newTaskItem = { task: task, resolve: resolve, reject: reject };
171
+
172
+ if (self.isEmpty() || append) {
173
+ self.tasks.push(newTaskItem);
174
+ } else {
175
+ self.tasks.unshift(newTaskItem);
176
+ }
177
+ });
178
+
179
+ self.updateTaskSchedule();
180
+
181
+ return promise;
182
+ };
183
+
184
+ /**
185
+ * Appends a new task at the end of the queue.
186
+ * @param {function} task - The task to perform. This function will be called with no arguments.
187
+ * @return {Promise} A promise which is resolved/rejected When the task was executed by the queue
188
+ */
189
+ PromiseQueue.prototype.append = function (task) {
190
+ return this.add(task, true);
191
+ };
192
+
193
+ /**
194
+ * Adds a new task as first item in the queue.
195
+ * @param task
196
+ * @return {Promise} A promise which is resolved/rejected When the task was executed by the queue
197
+ */
198
+ PromiseQueue.prototype.prepend = function (task) {
199
+ return this.add(task, false);
200
+ };
201
+
202
+ /**
203
+ * Removes the task from the qeueue. If the task is contained more than one time then all occurrences will be removed.
204
+ * @param {function} task - The task to remove.
205
+ * @return {number} - The number of items which were removed.
206
+ */
207
+ PromiseQueue.prototype.remove = function (task) {
208
+ if (this.isEmpty()) {
209
+ return 0;
210
+ }
211
+
212
+ var removedItems = 0;
213
+ for (var i = this.tasks.length - 1; i >= 0; i--) {
214
+ if (this.tasks[i].task === task) {
215
+ this.tasks.splice(i, 1);
216
+ removedItems++;
217
+ }
218
+ }
219
+
220
+ return removedItems;
221
+ };
222
+
223
+ /**
224
+ * @returns {Number} - The number of queued/waiting tasks in the queue.
225
+ */
226
+ PromiseQueue.prototype.getQueueSize = function () {
227
+ return this.tasks.length;
228
+ };
229
+
230
+ module.exports = PromiseQueue;
231
+ })();