UNPKG

199 kBJavaScriptView Raw
1/*global define, exports*/
2/*jshint -W098*/
3// ignore unused gpf
4/*eslint no-unused-vars: 0*/
5// ignore unused gpf
6/*eslint strict: [2, "function"]*/
7// To be more modular
8/*global __gpf__*/
9(function (root, factory) {
10 "use strict";
11 /**
12 * Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
13 * Rhino, and plain browser loading.
14 *
15 * 2014-12-04 ABZ Extended for PhantomJS
16 * 2015-05-29 ABZ Modified to catch former value of gpf
17 */
18 if (typeof define === "function" && define.amd) {
19 define(["exports"], factory);
20 } else if (typeof exports !== "undefined") {
21 factory(exports);
22 } else if (typeof module !== "undefined" && module.exports) {
23 factory(module.exports);
24 } else {
25 var newGpf = {};
26 factory(newGpf);
27 root.gpf = newGpf;
28 }
29}(this, function (gpf) {
30 "use strict";
31 function _gpfEmptyFunc() {
32 }
33 var
34 /**
35 * GPF Version
36 * @since 0.1.5
37 */
38 _gpfVersion = "0.2.2",
39 /**
40 * Host constants
41 * @since 0.1.5
42 */
43 _GPF_HOST = {
44 BROWSER: "browser",
45 NODEJS: "nodejs",
46 PHANTOMJS: "phantomjs",
47 RHINO: "rhino",
48 UNKNOWN: "unknown",
49 WSCRIPT: "wscript"
50 },
51 /**
52 * Current host type
53 *
54 * @type {_GPF_HOST}
55 * @since 0.1.5
56 */
57 _gpfHost = _GPF_HOST.UNKNOWN,
58 /**
59 * Indicates that paths are DOS-like (i.e. case insensitive with /)
60 * @since 0.1.5
61 */
62 _gpfDosPath = false,
63 /*jshint -W040*/
64 // This is the common way to get the global context
65 /**
66 * Main context object
67 *
68 * @type {Object}
69 * @since 0.1.5
70 */
71 _gpfMainContext = this,
72 //eslint-disable-line no-invalid-this, consistent-this
73 /*jshint +W040*/
74 /**
75 * Helper to ignore unused parameter
76 *
77 * @param {*} param
78 * @since 0.1.5
79 */
80 /*gpf:nop*/
81 _gpfIgnore = _gpfEmptyFunc,
82 /**
83 * Exit function
84 *
85 * @param {Number} code
86 * @since 0.1.5
87 */
88 _gpfExit = _gpfEmptyFunc,
89 /**
90 * Browser window object
91 *
92 * @type {Object}
93 * @since 0.1.5
94 */
95 _gpfWebWindow,
96 /**
97 * Browser [document](https://developer.mozilla.org/en-US/docs/Web/API/Document) object
98 *
99 * @type {Object}
100 * @since 0.1.5
101 */
102 _gpfWebDocument,
103 /**
104 * [Scripting.FileSystemObject](https://msdn.microsoft.com/en-us/library/aa711216(v=vs.71).aspx) Object
105 *
106 * @type {Object}
107 * @since 0.1.5
108 */
109 _gpfMsFSO,
110 /**
111 * Node [require("fs")](https://nodejs.org/api/fs.html)
112 *
113 * @type {Object}
114 * @since 0.1.5
115 */
116 _gpfNodeFs,
117 /**
118 * Node [require("path")](https://nodejs.org/api/path.html)
119 *
120 * @type {Object}
121 * @since 0.1.5
122 */
123 _gpfNodePath,
124 /**
125 * Boot host specific implementation per host
126 *
127 * @type {Object}
128 * @since 0.2.1
129 */
130 _gpfBootImplByHost = {};
131 _gpfVersion += "-debug";
132 /* Host detection */
133 // Microsoft cscript / wscript
134 if ("undefined" !== typeof WScript) {
135 _gpfHost = _GPF_HOST.WSCRIPT;
136 _gpfDosPath = true;
137 } else if ("undefined" !== typeof print && "undefined" !== typeof java) {
138 _gpfHost = _GPF_HOST.RHINO;
139 _gpfDosPath = false; // PhantomJS - When used as a command line (otherwise considered as a browser)
140 } else if ("undefined" !== typeof phantom && phantom.version && !document.currentScript) {
141 _gpfHost = _GPF_HOST.PHANTOMJS;
142 _gpfDosPath = require("fs").separator === "\\";
143 _gpfMainContext = window; // Nodejs
144 } else if ("undefined" !== typeof module && module.exports) {
145 _gpfHost = _GPF_HOST.NODEJS;
146 _gpfNodePath = require("path");
147 _gpfDosPath = _gpfNodePath.sep === "\\";
148 _gpfMainContext = global; // Browser
149 /* istanbul ignore else */
150 // unknown.1
151 } else if ("undefined" !== typeof window) {
152 _gpfHost = _GPF_HOST.BROWSER;
153 _gpfMainContext = window;
154 }
155 /**
156 * Host type enumeration
157 *
158 * @enum {String}
159 * @readonly
160 * @since 0.1.5
161 */
162 gpf.hosts = {
163 /**
164 * Any browser (phantomjs is recognized separately)
165 * @since 0.1.5
166 */
167 browser: _GPF_HOST.BROWSER,
168 /**
169 * [NodeJs](http://nodejs.org/)
170 * @since 0.1.5
171 */
172 nodejs: _GPF_HOST.NODEJS,
173 /**
174 * [PhantomJS](http://phantomjs.org/)
175 * @since 0.1.5
176 */
177 phantomjs: _GPF_HOST.PHANTOMJS,
178 /**
179 * [Rhino](http://developer.mozilla.org/en/docs/Rhino)
180 * @since 0.1.5
181 */
182 rhino: _GPF_HOST.RHINO,
183 /**
184 * Unknown (detection failed or the host is unknown)
185 * @since 0.1.5
186 */
187 unknown: _GPF_HOST.UNKNOWN,
188 /**
189 * [cscript/wscript](http://technet.microsoft.com/en-us/library/bb490887.aspx)
190 * @since 0.1.5
191 */
192 wscript: _GPF_HOST.WSCRIPT
193 };
194 /**
195 * Returns the detected host type
196 *
197 * @return {gpf.hosts} Host type
198 * @since 0.1.5
199 */
200 gpf.host = function () {
201 return _gpfHost;
202 };
203 /**
204 * Returns the current version
205 *
206 * @return {String} Version
207 * @since 0.1.5
208 */
209 gpf.version = function () {
210 return _gpfVersion;
211 };
212 function _gpfConsoleGenerate(outputLine) {
213 return {
214 log: function (text) {
215 outputLine(" " + text);
216 },
217 info: function (text) {
218 outputLine("[?] " + text);
219 },
220 warn: function (text) {
221 outputLine("/!\\ " + text);
222 },
223 error: function (text) {
224 outputLine("(X) " + text);
225 }
226 };
227 }
228 _gpfBootImplByHost[_GPF_HOST.BROWSER] = function () {
229 /* istanbul ignore next */
230 // exit.1
231 _gpfExit = function (code) {
232 window.location = "https://arnaudbuchholz.github.io/gpf/exit.html?" + (code || 0);
233 };
234 _gpfWebWindow = window;
235 _gpfWebDocument = document;
236 };
237 var
238 /**
239 * require("http")
240 *
241 * @type {Object}
242 * @since 0.2.1
243 */
244 _gpfNodeHttp,
245 /**
246 * require("url")
247 *
248 * @type {Object}
249 * @since 0.2.1
250 */
251 _gpfNodeUrl;
252 /**
253 * @namespace gpf.node
254 * @description Root namespace for NodeJS specifics
255 * @since 0.1.5
256 */
257 gpf.node = {};
258 _gpfBootImplByHost[_GPF_HOST.NODEJS] = function () {
259 _gpfNodeFs = require("fs");
260 _gpfNodeHttp = require("http");
261 _gpfNodeUrl = require("url");
262 /* istanbul ignore next */
263 // exit.1
264 _gpfExit = function (code) {
265 process.exit(code);
266 };
267 };
268 _gpfBootImplByHost[_GPF_HOST.PHANTOMJS] = function () {
269 /* istanbul ignore next */
270 // exit.1
271 _gpfExit = function (code) {
272 phantom.exit(code);
273 };
274 _gpfWebWindow = window;
275 _gpfWebDocument = document;
276 _gpfNodeFs = require("fs");
277 };
278 gpf.rhino = {};
279 _gpfBootImplByHost[_GPF_HOST.RHINO] = function () {
280 // Define console APIs
281 _gpfMainContext.console = _gpfConsoleGenerate(print);
282 /* istanbul ignore next */
283 // exit.1
284 _gpfExit = function (code) {
285 java.lang.System.exit(code);
286 };
287 };
288 _gpfBootImplByHost[_GPF_HOST.UNKNOWN] = _gpfEmptyFunc;
289 gpf.wscript = {};
290 /* istanbul ignore next */
291 // wscript.echo.1
292 function _gpfWScriptEcho(text) {
293 WScript.Echo(text);
294 }
295 _gpfBootImplByHost[_GPF_HOST.WSCRIPT] = function () {
296 _gpfMsFSO = new ActiveXObject("Scripting.FileSystemObject");
297 // Define console APIs
298 _gpfMainContext.console = _gpfConsoleGenerate(_gpfWScriptEcho);
299 /* istanbul ignore next */
300 // exit.1
301 _gpfExit = function (code) {
302 WScript.Quit(code);
303 };
304 };
305 _gpfBootImplByHost[_gpfHost]();
306 var _gpfIsArray = Array.isArray;
307 function _gpfArrayHasValidLengthProperty(obj) {
308 if (obj) {
309 return Math.floor(obj.length) === obj.length;
310 }
311 return false;
312 }
313 /**
314 * Return true if the parameter looks like an array, meaning a property length is available and members can be
315 * accessed through the [] operator. The length property does not have to be writable.
316 *
317 * @param {Object} obj Object to test
318 * @return {Boolean} True if array-like
319 * @since 0.1.5
320 */
321 function _gpfIsArrayLike(obj) {
322 return _gpfIsArray(obj) || _gpfArrayHasValidLengthProperty(obj);
323 }
324 /**
325 * @gpf:sameas _gpfIsArrayLike
326 * @since 0.1.5
327 */
328 gpf.isArrayLike = _gpfIsArrayLike;
329 function _gpfArrayForEach(array, callback, thisArg) {
330 var index, length = array.length;
331 for (index = 0; index < length; ++index) {
332 callback.call(thisArg, array[index], index, array);
333 }
334 }
335 function _gpfObjectForEachOwnProperty(object, callback, thisArg) {
336 for (var property in object) {
337 /* istanbul ignore else */
338 // hasOwnProperty.1
339 if (object.hasOwnProperty(property)) {
340 callback.call(thisArg, object[property], property, object);
341 }
342 }
343 }
344 function _gpfObjectForEachOwnPropertyWScript(object, callback, thisArg) {
345 _gpfObjectForEachOwnProperty(object, callback, thisArg);
346 [
347 "constructor",
348 "toString"
349 ].forEach(function (property) {
350 if (object.hasOwnProperty(property)) {
351 callback.call(thisArg, object[property], property, object);
352 }
353 });
354 }
355 /**
356 * _gpfArrayForEach that returns first truthy value computed by the callback
357 *
358 * @param {Array} array Array-like object
359 * @param {gpf.typedef.forEachCallback} callback Callback function executed on each array item
360 * @param {*} [thisArg] thisArg Value to use as this when executing callback
361 * @return {*} first truthy value returned by the callback or undefined after all items were enumerated
362 * @since 0.2.2
363 */
364 function _gpfArrayForEachFalsy(array, callback, thisArg) {
365 var result, index, length = array.length;
366 for (index = 0; index < length && !result; ++index) {
367 result = callback.call(thisArg, array[index], index, array);
368 }
369 return result;
370 }
371 /**
372 * Similar to [].forEach but for objects
373 *
374 * @param {Object} object Object
375 * @param {gpf.typedef.forEachCallback} callback Callback function executed on each own property
376 * @param {*} [thisArg] thisArg Value to use as this when executing callback
377 * @since 0.1.5
378 */
379 var _gpfObjectForEach;
380 if (_GPF_HOST.WSCRIPT === _gpfHost) {
381 _gpfObjectForEach = _gpfObjectForEachOwnPropertyWScript;
382 } else {
383 _gpfObjectForEach = _gpfObjectForEachOwnProperty;
384 }
385 /**
386 * Executes a provided function once per structure element.
387 * NOTE: unlike [].forEach, non own properties are also enumerated
388 *
389 * @param {Array|Object} container Container to enumerate
390 * @param {gpf.typedef.forEachCallback} callback Callback function executed on each item or own property
391 * @param {*} [thisArg=undefined] thisArg Value to use as this when executing callback
392 * @since 0.1.5
393 */
394 gpf.forEach = function (container, callback, thisArg) {
395 if (_gpfIsArrayLike(container)) {
396 _gpfArrayForEach(container, callback, thisArg);
397 return;
398 }
399 _gpfObjectForEach(container, callback, thisArg);
400 };
401 var _gpfAssert, _gpfAsserts, _gpfAssertWarn = true;
402 function _gpfAssertConsoleWarn(message) {
403 if (_gpfAssertWarn) {
404 console.warn("ASSERTION FAILED: " + message);
405 }
406 }
407 function _gpfAssertFailIfConditionFalsy(condition, message) {
408 if (!condition) {
409 _gpfAssertConsoleWarn(message);
410 gpf.Error.assertionFailed({ message: message });
411 }
412 }
413 /**
414 * Assertion helper
415 *
416 * @param {Boolean} condition Truthy / [Falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) value
417 * @param {String} message Assertion message explaining the violation when the condition is false
418 * @throws {gpf.Error.AssertionFailed}
419 * @since 0.1.5
420 */
421 function _gpfAssertImpl(condition, message) {
422 if (undefined === message) {
423 message = "Assertion with no message";
424 condition = false;
425 }
426 _gpfAssertFailIfConditionFalsy(condition, message);
427 }
428 /**
429 * Batch assertion helper
430 *
431 * @param {Object} assertions Dictionary of messages associated to condition values
432 * @throws {gpf.Error.AssertionFailed}
433 * @since 0.1.5
434 */
435 function _gpfAssertsImpl(assertions) {
436 _gpfObjectForEach(assertions, _gpfAssertFailIfConditionFalsy);
437 }
438 /**
439 * @gpf:sameas _gpfAssertImpl
440 * @since 0.1.5
441 */
442 gpf.assert = _gpfAssertImpl;
443 /**
444 * @gpf:sameas _gpfAssertsImpl
445 * @since 0.1.5
446 */
447 gpf.asserts = _gpfAssertsImpl;
448 /**
449 * By default, a failing assert will generate a console warning.
450 * Use this method to prevent console warnings to be generated.
451 *
452 * @param {Boolean} silent True to prevent console warnings on failing assertions
453 * @since 0.1.8
454 */
455 gpf.preventAssertWarnings = function (silent) {
456 _gpfAssertWarn = !silent;
457 };
458 // DEBUG specifics
459 _gpfAssert = _gpfAssertImpl;
460 _gpfAsserts = _gpfAssertsImpl;
461 /* istanbul ignore if */
462 // assert.1
463 if (!_gpfAssert) {
464 }
465 var
466 // https://github.com/jshint/jshint/issues/525
467 _GpfFunc = Function,
468 // avoid JSHint error
469 // Max value on 31 bits
470 _gpfMax31 = 2147483647,
471 // Max value on 32 bits
472 _gpfMax32 = 4294967295,
473 // Letters (lowercase)
474 _gpfAlpha = "abcdefghijklmnopqrstuvwxyz",
475 // Letters (uppercase)
476 _gpfALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
477 // Digits
478 _gpfDigit = "0123456789",
479 // List of allowed first char in an identifier
480 _gpfIdentifierFirstChar = _gpfAlpha + _gpfALPHA + "_$",
481 // List of allowed other chars in an identifier
482 _gpfIdentifierOtherChars = _gpfAlpha + _gpfALPHA + _gpfDigit + "_$",
483 // List of JavaScript keywords
484 _gpfJsKeywords = [
485 "abstract",
486 "arguments",
487 "await",
488 "boolean",
489 "break",
490 "byte",
491 "case",
492 "catch",
493 "char",
494 "class",
495 "const",
496 "continue",
497 "debugger",
498 "default",
499 "delete",
500 "do",
501 "double",
502 "else",
503 "enum",
504 "eval",
505 "export",
506 "extends",
507 "false",
508 "final",
509 "finally",
510 "float",
511 "for",
512 "function",
513 "goto",
514 "if",
515 "implements",
516 "import",
517 "in",
518 "instanceof",
519 "int",
520 "interface",
521 "let",
522 "long",
523 "native",
524 "new",
525 "null",
526 "package",
527 "private",
528 "protected",
529 "public",
530 "return",
531 "short",
532 "static",
533 "super",
534 "switch",
535 "synchronized",
536 "this",
537 "throw",
538 "throws",
539 "transient",
540 "true",
541 "try",
542 "typeof",
543 "var",
544 "void",
545 "volatile",
546 "while",
547 "with",
548 "yield"
549 ];
550 // Unprotected version of _gpfFunc
551 function _gpfFuncUnsafe(params, source) {
552 var args;
553 if (0 === params.length) {
554 return _GpfFunc(source);
555 }
556 args = [].concat(params);
557 args.push(source);
558 return _GpfFunc.apply(null, args);
559 }
560 // Protected version of _gpfFunc
561 function _gpfFuncImpl(params, source) {
562 _gpfAssert("string" === typeof source && source.length, "Source expected (or use _gpfEmptyFunc)");
563 try {
564 return _gpfFuncUnsafe(params, source);
565 } catch (e) {
566 // Makes it easier to debug
567 throw new Error("_gpfFuncImpl exception: " + e.message + "\r\n" + source);
568 }
569 }
570 /**
571 * Create a new function from the source and parameter list.
572 * In DEBUG mode, it catches any error to log the problem.
573 *
574 * @param {String[]} [params] params Parameter names list
575 * @param {String} source Body of the function
576 * @return {Function} New function
577 * @since 0.1.5
578 */
579 function _gpfFunc(params, source) {
580 if (undefined === source) {
581 source = params;
582 params = [];
583 }
584 return _gpfFuncImpl(params, source);
585 }
586 /**
587 * Check if the value is in the range defined by min and max
588 *
589 * @param {Number} value Value to check
590 * @param {Number} min Minimum value (inclusive)
591 * @param {Number} max Maximum value (inclusive)
592 * @return {Boolean} True if the value is in the range
593 * @since 0.1.6
594 */
595 function _gpfIsInRange(value, min, max) {
596 return min <= value && value <= max;
597 }
598 /**
599 * Check if the value is an unsigned byte
600 *
601 * @param {*} value
602 * @returns {Boolean} True if the value is an unsigned byte
603 * @since 0.1.6
604 */
605 // Returns true if the value is an unsigned byte
606 function _gpfIsUnsignedByte(value) {
607 return "number" === typeof value && _gpfIsInRange(value, 0, 255);
608 }
609 /**
610 * @namespace gpf.web
611 * @description Root namespace for web-related tools (even if not in a browser)
612 * @since 0.1.5
613 */
614 gpf.web = {};
615 function _gpfStringCapitalize(that) {
616 return that.charAt(0).toUpperCase() + that.substr(1);
617 }
618 function _gpfStringReplaceEx(that, replacements) {
619 var result = that;
620 _gpfObjectForEach(replacements, function (replacement, key) {
621 result = result.split(key).join(replacement);
622 });
623 return result;
624 }
625 var _gpfStringEscapes = {};
626 function _gpfStringEscapePostProcessFor(that, language) {
627 if ("javascript" === language) {
628 return "\"" + that + "\"";
629 }
630 return that;
631 }
632 /**
633 *
634 * Make the string content compatible with a given language
635 *
636 * @param {String} that String to escape
637 * @param {String} language Language to escape the string for. Supported values are:
638 * - **"javascript"**: escape \ and formatting characters then adds double quotes around the string
639 * - **"xml"**: escape &, < and >
640 * - **"html"**: xml + some accentuated characters
641 * @return {String} Escaped string
642 * @since 0.1.5
643 */
644 function _gpfStringEscapeFor(that, language) {
645 _gpfAssert(undefined !== _gpfStringEscapes[language], "Unknown language");
646 return _gpfStringEscapePostProcessFor(_gpfStringReplaceEx(that, _gpfStringEscapes[language]), language);
647 }
648 _gpfStringEscapes.javascript = {
649 "\\": "\\\\",
650 "\"": "\\\"",
651 "\n": "\\n",
652 "\r": "\\r",
653 "\t": "\\t"
654 };
655 _gpfStringEscapes.xml = {
656 "&": "&amp;",
657 "<": "&lt;",
658 ">": "&gt;"
659 };
660 function _gpfInstallMissingMembers(on, members) {
661 _gpfObjectForEach(members, function (value, memberName) {
662 if (on[memberName] === undefined) {
663 on[memberName] = value;
664 }
665 });
666 }
667 function _gpfInstallCompatibleMethods(on, methods) {
668 if (methods) {
669 _gpfInstallMissingMembers(on.prototype, methods);
670 }
671 }
672 function _gpfInstallCompatibleStatics(on, statics) {
673 if (statics) {
674 _gpfInstallMissingMembers(on, statics);
675 }
676 }
677 /**
678 * Define and install compatible methods
679 *
680 * @param {String} typeName Type name ("Object", "String"...)
681 * @param {_GpfCompatibilityDescription} description Description of compatible methods
682 * @since 0.1.5
683 */
684 function _gpfInstallCompatibility(typeName, description) {
685 var on = description.on;
686 _gpfInstallCompatibleMethods(on, description.methods);
687 _gpfInstallCompatibleStatics(on, description.statics);
688 }
689 function _gpfArrayBind(callback, thisArg) {
690 if (undefined !== thisArg) {
691 return callback.bind(thisArg);
692 }
693 return callback;
694 }
695 function _gpfArrayForEachOwn(array, callback) {
696 var len = array.length, idx = 0;
697 while (idx < len) {
698 if (array.hasOwnProperty(idx)) {
699 callback(array[idx], idx, array);
700 }
701 ++idx;
702 }
703 }
704 function _gpfArrayEveryOwn(array, callback, idx) {
705 var len = array.length;
706 while (idx < len) {
707 if (array.hasOwnProperty(idx) && true !== callback(array[idx], idx, array)) {
708 return false;
709 }
710 ++idx;
711 }
712 return true;
713 }
714 function _gpfArrayEveryOwnFrom0(array, callback) {
715 return _gpfArrayEveryOwn(array, callback, 0);
716 }
717 //endregion
718 //region Array.from
719 function _gpfArrayFromString(array, string) {
720 var length = string.length, index;
721 for (index = 0; index < length; ++index) {
722 array.push(string.charAt(index));
723 }
724 }
725 function _gpfArrayConvertFrom(arrayLike) {
726 var array = [];
727 if ("string" === typeof arrayLike) {
728 // Required for cscript
729 _gpfArrayFromString(array, arrayLike);
730 } else {
731 _gpfArrayForEach(arrayLike, function (value) {
732 array.push(value);
733 }, 0);
734 }
735 return array;
736 }
737 function _gpfArrayFrom(arrayLike) {
738 var array = _gpfArrayConvertFrom(arrayLike), callback = arguments[1];
739 if ("function" === typeof callback) {
740 array = array.map(callback, arguments[2]);
741 }
742 return array;
743 }
744 //endregion
745 _gpfInstallCompatibility("Array", {
746 on: Array,
747 methods: {
748 // Introduced with JavaScript 1.6
749 every: function (callback) {
750 return _gpfArrayEveryOwnFrom0(this, _gpfArrayBind(callback, arguments[1]));
751 },
752 // Introduced with JavaScript 1.6
753 filter: function (callback) {
754 var result = [];
755 callback = _gpfArrayBind(callback, arguments[1]);
756 _gpfArrayForEachOwn(this, function (item, idx, array) {
757 if (callback(item, idx, array)) {
758 result.push(item);
759 }
760 });
761 return result;
762 },
763 // Introduced with JavaScript 1.6
764 forEach: function (callback) {
765 _gpfArrayForEachOwn(this, _gpfArrayBind(callback, arguments[1]));
766 },
767 // Introduced with JavaScript 1.5
768 indexOf: function (searchElement) {
769 var result = -1;
770 _gpfArrayEveryOwn(this, function (value, index) {
771 if (value === searchElement) {
772 result = index;
773 return false;
774 }
775 return true;
776 }, arguments[1] || 0);
777 return result;
778 },
779 // Introduced with JavaScript 1.6
780 map: function (callback) {
781 var result = new Array(this.length);
782 callback = _gpfArrayBind(callback, arguments[1]);
783 _gpfArrayForEachOwn(this, function (item, index, array) {
784 result[index] = callback(item, index, array);
785 });
786 return result;
787 },
788 // Introduced with JavaScript 1.6
789 some: function (callback) {
790 callback = _gpfArrayBind(callback, arguments[1]);
791 return !_gpfArrayEveryOwnFrom0(this, function (item, index, array) {
792 return !callback(item, index, array);
793 });
794 },
795 // Introduced with JavaScript 1.8
796 reduce: function (callback) {
797 var initialValue = arguments[1], thisLength = this.length, index = 0, value;
798 if (undefined === initialValue) {
799 value = this[index++];
800 } else {
801 value = initialValue;
802 }
803 for (; index < thisLength; ++index) {
804 value = callback(value, this[index], index, this);
805 }
806 return value;
807 }
808 },
809 statics: {
810 // Introduced with ECMAScript 2015
811 from: _gpfArrayFrom,
812 // Introduced with JavaScript 1.8.5
813 isArray: function (arrayLike) {
814 return "[object Array]" === {}.toString.call(arrayLike);
815 }
816 }
817 });
818 // Update if it was not defined
819 if (!_gpfIsArray) {
820 _gpfIsArray = Array.isArray;
821 }
822 function _gpfBuildFunctionParameterList(count) {
823 if (0 === count) {
824 return [];
825 }
826 return new Array(count).join(" ").split(" ").map(function (value, index) {
827 return "p" + index;
828 });
829 }
830 function _gpfGenerateGenericFactorySource(parameters) {
831 var src = [
832 "var C = this, l = arguments.length;",
833 "if (0 === l) { return new C();}"
834 ];
835 parameters.forEach(function (value, index) {
836 var count = index + 1;
837 src.push("if (" + count + " === l) { return new C(" + parameters.slice(0, count).join(", ") + ");}");
838 });
839 return src.join("\r\n");
840 }
841 function _gpfGenerateGenericFactory(maxParameters) {
842 var parameters = _gpfBuildFunctionParameterList(maxParameters);
843 return _gpfFunc(parameters, _gpfGenerateGenericFactorySource(parameters));
844 }
845 /**
846 * Create any class by passing the right number of parameters
847 *
848 * @this {Function} constructor to invoke
849 * @since 0.1.5
850 */
851 var _gpfGenericFactory = _gpfGenerateGenericFactory(10);
852 /**
853 * Call a constructor with an array of parameters.
854 *
855 * It is impossible to mix [new](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new)
856 * and [apply](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply)
857 * in the same call.
858 *
859 * This helper workarounds this problem.
860 *
861 * @param {Function} Constructor Class constructor
862 * @param {Array} parameters Parameters to pass to the constructor
863 * @return {Object} New object
864 * @since 0.1.5
865 */
866 function _gpfNewApply(Constructor, parameters) {
867 if (parameters.length > _gpfGenericFactory.length) {
868 _gpfGenericFactory = _gpfGenerateGenericFactory(parameters.length);
869 }
870 return _gpfGenericFactory.apply(Constructor, parameters);
871 }
872 function _pad(number) {
873 if (10 > number) {
874 return "0" + number;
875 }
876 return number;
877 }
878 function _gpfDateToISOString(date) {
879 return date.getUTCFullYear() + "-" + _pad(date.getUTCMonth() + 1) + "-" + _pad(date.getUTCDate()) + "T" + _pad(date.getUTCHours()) + ":" + _pad(date.getUTCMinutes()) + ":" + _pad(date.getUTCSeconds()) + "." + (date.getUTCMilliseconds() / 1000).toFixed(3).slice(2, 5) + "Z";
880 }
881 _gpfInstallCompatibility("Date", {
882 on: Date,
883 methods: {
884 // Introduced with JavaScript 1.8
885 toISOString: function () {
886 return _gpfDateToISOString(this);
887 }
888 },
889 statics: {
890 now: function () {
891 return new Date().getTime();
892 }
893 }
894 });
895 //region Date override
896 var _gpfISO8601RegExp = new RegExp("^([0-9][0-9][0-9][0-9])\\-([0-9][0-9])\\-([0-9][0-9])" + "(?:T([0-9][0-9])\\:([0-9][0-9])\\:([0-9][0-9])(?:\\.([0-9][0-9][0-9])Z)?)?$");
897 function _gpfIsValidDateInDateArray(dateArray) {
898 return dateArray[1] < 12 && dateArray[2] < 32;
899 }
900 function _gpfIsValidTimeInDateArray(dateArray) {
901 return dateArray[3] < 24 && dateArray[4] < 60 && dateArray[5] < 60;
902 }
903 function _gpfCheckDateArray(dateArray) {
904 if (_gpfIsValidDateInDateArray(dateArray) && _gpfIsValidTimeInDateArray(dateArray)) {
905 return dateArray;
906 }
907 }
908 function _gpfAddDatePartToArray(dateArray, datePart) {
909 if (datePart) {
910 dateArray.push(parseInt(datePart, 10));
911 } else {
912 dateArray.push(0);
913 }
914 }
915 function _gpfToDateArray(matchResult) {
916 var dateArray = [], len = matchResult.length,
917 // 0 is the recognized string
918 idx;
919 for (idx = 1; idx < len; ++idx) {
920 _gpfAddDatePartToArray(dateArray, matchResult[idx]);
921 }
922 return dateArray;
923 }
924 function _gpfProcessISO8601MatchResult(matchResult) {
925 var dateArray;
926 if (matchResult) {
927 dateArray = _gpfToDateArray(matchResult);
928 // Month must be corrected (0-based)
929 --dateArray[1];
930 // Some validation
931 return _gpfCheckDateArray(dateArray);
932 }
933 }
934 /**
935 * Check if the value is a string respecting the ISO 8601 representation of a date. If so, the string is parsed and the
936 * date details is returned.
937 *
938 * The function supports supports long and short syntax.
939 *
940 * @param {*} value Value to test
941 * @return {Number[]|undefined} 7 numbers composing the date (Month is 0-based). undefined if not matching.
942 * @since 0.1.5
943 */
944 function _gpfIsISO8601String(value) {
945 if ("string" === typeof value) {
946 _gpfISO8601RegExp.lastIndex = 0;
947 return _gpfProcessISO8601MatchResult(_gpfISO8601RegExp.exec(value));
948 }
949 }
950 // Backup original Date constructor
951 var _GpfGenuineDate = _gpfMainContext.Date;
952 /**
953 * Date constructor supporting ISO 8601 format
954 *
955 * @constructor
956 * @since 0.1.5
957 */
958 function _GpfDate() {
959 var firstArgument = arguments[0], values = _gpfIsISO8601String(firstArgument);
960 if (values) {
961 return new _GpfGenuineDate(_GpfGenuineDate.UTC.apply(_GpfGenuineDate.UTC, values));
962 }
963 return _gpfNewApply(_GpfGenuineDate, arguments);
964 }
965 function _gpfCopyDateStatics() {
966 _gpfArrayForEach([
967 "prototype",
968 // Ensure instanceof
969 "UTC",
970 "parse",
971 "now"
972 ], function (member) {
973 _GpfDate[member] = _GpfGenuineDate[member];
974 });
975 }
976 function _gpfInstallCompatibleDate() {
977 _gpfCopyDateStatics();
978 // Test if ISO 8601 format variations are supported
979 var longDateAsString, shortDateAsString;
980 try {
981 longDateAsString = _gpfDateToISOString(new Date("2003-01-22T22:45:34.075Z"));
982 shortDateAsString = _gpfDateToISOString(new Date("2003-01-22"));
983 } catch (e) {
984 }
985 //eslint-disable-line no-empty
986 if (longDateAsString !== "2003-01-22T22:45:34.075Z" || shortDateAsString !== "2003-01-22T00:00:00.000Z") {
987 // Replace constructor with new one
988 _gpfMainContext.Date = _GpfDate;
989 }
990 }
991 _gpfInstallCompatibleDate(); //endregion
992 var _gpfArrayPrototypeSlice = Array.prototype.slice;
993 function _generateBindBuilderSource(length) {
994 return "var me = this;\n" + "return function (" + _gpfBuildFunctionParameterList(length).join(", ") + ") {\n" + " var args = _gpfArrayPrototypeSlice.call(arguments, 0);\n" + " return me.apply(thisArg, prependArgs.concat(args));\n" + "};";
995 }
996 _gpfInstallCompatibility("Function", {
997 on: Function,
998 methods: {
999 // Introduced with JavaScript 1.8.5
1000 bind: function (thisArg) {
1001 var me = this, prependArgs = _gpfArrayPrototypeSlice.call(arguments, 1), builderSource = _generateBindBuilderSource(Math.max(this.length - prependArgs.length, 0));
1002 return _gpfFunc([
1003 "thisArg",
1004 "prependArgs",
1005 "_gpfArrayPrototypeSlice"
1006 ], builderSource).call(me, thisArg, prependArgs, _gpfArrayPrototypeSlice);
1007 }
1008 }
1009 });
1010 //region Function name
1011 // Get the name of a function if bound to the call
1012 var _gpfJsCommentsRegExp = new RegExp("//.*$|/\\*(?:[^\\*]*|\\*[^/]*)\\*/", "gm");
1013 function _gpfGetFunctionName() {
1014 // Use simple parsing
1015 /*jshint validthis:true*/
1016 var functionSource = _gpfEmptyFunc.toString.call(this),
1017 //eslint-disable-line no-invalid-this
1018 functionKeywordPos = functionSource.indexOf("function"), parameterListStartPos = functionSource.indexOf("(", functionKeywordPos);
1019 return functionSource.substr(functionKeywordPos + 9, parameterListStartPos - functionKeywordPos - 9).replace(_gpfJsCommentsRegExp, "") // remove comments
1020.trim();
1021 }
1022 // Handling function name properly
1023 if (function () {
1024 // Trick source minification
1025 var testFunction = _gpfFunc("return function functionName () {};")();
1026 return testFunction.name !== "functionName";
1027 }()) {
1028 Function.prototype.compatibleName = _gpfGetFunctionName;
1029 } else {
1030 /**
1031 * Return function name
1032 *
1033 * @return {String} Function name
1034 * @since 0.1.5
1035 */
1036 Function.prototype.compatibleName = function () {
1037 return this.name;
1038 };
1039 } //endregion
1040 function _gpfObjectAssign(value, memberName) {
1041 /*jshint validthis:true*/
1042 this[memberName] = value; //eslint-disable-line no-invalid-this
1043 }
1044 _gpfInstallCompatibility("Object", {
1045 on: Object,
1046 statics: {
1047 // Introduced with ECMAScript 2015
1048 assign: function (destination, source) {
1049 _gpfIgnore(source);
1050 [].slice.call(arguments, 1).forEach(function (nthSource) {
1051 _gpfObjectForEach(nthSource, _gpfObjectAssign, destination);
1052 });
1053 return destination;
1054 },
1055 // Introduced with JavaScript 1.8.5
1056 create: function () {
1057 function Temp() {
1058 }
1059 return function (O) {
1060 Temp.prototype = O;
1061 var obj = new Temp();
1062 Temp.prototype = null;
1063 if (!obj.__proto__) {
1064 obj.__proto__ = O;
1065 }
1066 return obj;
1067 };
1068 }(),
1069 // Introduced with JavaScript 1.8.5
1070 getPrototypeOf: function (object) {
1071 if (object.__proto__) {
1072 return object.__proto__;
1073 }
1074 // May break if the constructor has been tampered with
1075 return object.constructor.prototype;
1076 },
1077 // Introduced with JavaScript 1.8.5
1078 keys: function (object) {
1079 var result = [], key;
1080 for (key in object) {
1081 if (object.hasOwnProperty(key)) {
1082 result.push(key);
1083 }
1084 }
1085 return result;
1086 },
1087 // Introduced with JavaScript 1.8.5
1088 values: function (object) {
1089 var result = [], key;
1090 for (key in object) {
1091 if (object.hasOwnProperty(key)) {
1092 result.push(object[key]);
1093 }
1094 }
1095 return result;
1096 }
1097 }
1098 });
1099 _gpfInstallCompatibility("String", {
1100 on: String,
1101 methods: {
1102 // Introduced with JavaScript 1.8.1
1103 trim: function () {
1104 var rtrim = new RegExp("^[\\s\uFEFF\xA0]+|[\\s\uFEFF\xA0]+$", "g");
1105 return function () {
1106 return this.replace(rtrim, "");
1107 };
1108 }()
1109 }
1110 });
1111 function _gpfPromiseSafeResolve(fn, onFulfilled, onRejected) {
1112 var safe = true;
1113 function makeSafe(callback) {
1114 return function (value) {
1115 if (safe) {
1116 safe = false;
1117 callback(value);
1118 }
1119 };
1120 }
1121 try {
1122 fn(makeSafe(onFulfilled), makeSafe(onRejected));
1123 } catch (e) {
1124 /* istanbul ignore else */
1125 // compability.promise.1
1126 if (safe) {
1127 safe = false;
1128 onRejected(e);
1129 }
1130 }
1131 }
1132 function _gpfPromiseFinale() {
1133 /*jshint validthis:true*/
1134 var me = this;
1135 //eslint-disable-line no-invalid-this
1136 me._handlers.forEach(function (handler) {
1137 handler.process(me);
1138 });
1139 me._handlers = []; // Reset list
1140 }
1141 function _gpfPromiseReject(newValue) {
1142 /*jshint validthis:true*/
1143 var me = this;
1144 //eslint-disable-line no-invalid-this
1145 me._state = false;
1146 me._value = newValue;
1147 _gpfPromiseFinale.call(me);
1148 }
1149 //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
1150 function _gpfPromiseResolve(newValue) {
1151 /*jshint validthis:true*/
1152 var me = this;
1153 //eslint-disable-line no-invalid-this
1154 try {
1155 _gpfAssert(newValue !== me, "A promise cannot be resolved with itself.");
1156 if (newValue && (typeof newValue === "object" || typeof newValue === "function")) {
1157 var then = newValue.then;
1158 if ("function" === typeof then) {
1159 _gpfPromiseSafeResolve(then.bind(newValue), _gpfPromiseResolve.bind(me), _gpfPromiseReject.bind(me));
1160 return;
1161 }
1162 }
1163 me._state = true;
1164 me._value = newValue;
1165 _gpfPromiseFinale.call(me);
1166 } catch (e) {
1167 /* istanbul ignore next */
1168 // compability.promise.1
1169 _gpfPromiseReject.call(me, e);
1170 }
1171 }
1172 var _GpfPromise = gpf.Promise = function (fn) {
1173 _gpfPromiseSafeResolve(fn, _gpfPromiseResolve.bind(this), _gpfPromiseReject.bind(this));
1174 };
1175 function _gpfPromiseHandler() {
1176 }
1177 _gpfPromiseHandler.prototype = {
1178 onFulfilled: null,
1179 onRejected: null,
1180 resolve: null,
1181 reject: null,
1182 process: function (promise) {
1183 /*jshint validthis:true*/
1184 var me = this;
1185 //eslint-disable-line no-invalid-this
1186 if (promise._state === null) {
1187 /* istanbul ignore else */
1188 // hasOwnProperty.1
1189 if (!promise.hasOwnProperty("_handlers")) {
1190 promise._handlers = [];
1191 }
1192 promise._handlers.push(me);
1193 return;
1194 }
1195 setTimeout(function () {
1196 var callback, result;
1197 if (promise._state) {
1198 callback = me.onFulfilled;
1199 } else {
1200 callback = me.onRejected;
1201 }
1202 if (null === callback) {
1203 if (promise._state) {
1204 me.resolve(promise._value);
1205 } else {
1206 me.reject(promise._value);
1207 }
1208 return;
1209 }
1210 try {
1211 result = callback(promise._value);
1212 } catch (e) {
1213 me.reject(e);
1214 return;
1215 }
1216 me.resolve(result);
1217 }, 0);
1218 }
1219 };
1220 _GpfPromise.prototype = {
1221 // @property {Boolean|null} state of the promise
1222 _state: null,
1223 // @property {*} fufilment value
1224 _value: null,
1225 // @property {Handler[]} list of handlers
1226 _handlers: [],
1227 then: function (onFulfilled, onRejected) {
1228 var me = this;
1229 return new _GpfPromise(function (resolve, reject) {
1230 var handler = new _gpfPromiseHandler();
1231 if (undefined !== onFulfilled) {
1232 handler.onFulfilled = onFulfilled;
1233 }
1234 if (undefined !== onRejected) {
1235 handler.onRejected = onRejected;
1236 }
1237 handler.resolve = resolve;
1238 handler.reject = reject;
1239 handler.process(me);
1240 });
1241 },
1242 "catch": function (onRejected) {
1243 return this.then(null, onRejected);
1244 }
1245 };
1246 _GpfPromise.resolve = function (value) {
1247 return new _GpfPromise(function (resolve) {
1248 resolve(value);
1249 });
1250 };
1251 _GpfPromise.reject = function (value) {
1252 return new _GpfPromise(function (resolve, reject) {
1253 _gpfIgnore(resolve);
1254 reject(value);
1255 });
1256 };
1257 _GpfPromise.all = function (promises) {
1258 if (0 === promises.length) {
1259 return _GpfPromise.resolve([]);
1260 }
1261 return new _GpfPromise(function (resolve, reject) {
1262 var remaining = promises.length;
1263 function handle(result, index) {
1264 try {
1265 if (result && result instanceof _GpfPromise) {
1266 result.then(function (value) {
1267 handle(value, index);
1268 }, reject);
1269 return;
1270 }
1271 promises[index] = result;
1272 if (--remaining === 0) {
1273 resolve(promises);
1274 }
1275 } catch (e) {
1276 /* istanbul ignore next */
1277 // compability.promise.1
1278 reject(e);
1279 }
1280 }
1281 promises.forEach(handle);
1282 });
1283 };
1284 _GpfPromise.race = function (promises) {
1285 return new _GpfPromise(function (resolve, reject) {
1286 promises.forEach(function (promise) {
1287 promise.then(resolve, reject);
1288 });
1289 });
1290 };
1291 if (undefined === _gpfMainContext.Promise) {
1292 _gpfMainContext.Promise = _GpfPromise;
1293 }
1294 var
1295 // List of pending callbacks (sorted by execution time)
1296 _gpfTimeoutQueue = [],
1297 // Last allocated timeoutID
1298 _gpfTimeoutID = 0,
1299 // Sleep function
1300 _gpfSleep = _gpfEmptyFunc;
1301 // Handle timeouts (mandatory for some environments)
1302 gpf.handleTimeout = _gpfEmptyFunc;
1303 // Sorting function used to reorder the async queue
1304 function _gpfSortOnDt(a, b) {
1305 if (a.dt === b.dt) {
1306 return a.id - b.id;
1307 }
1308 return a.dt - b.dt;
1309 }
1310 function _gpSetTimeoutPolyfill(callback, timeout) {
1311 _gpfAssert("number" === typeof timeout, "Timeout is required");
1312 var timeoutItem = {
1313 id: ++_gpfTimeoutID,
1314 dt: new Date().getTime() + timeout,
1315 cb: callback
1316 };
1317 _gpfTimeoutQueue.push(timeoutItem);
1318 _gpfTimeoutQueue.sort(_gpfSortOnDt);
1319 return _gpfTimeoutID;
1320 }
1321 function _gpfClearTimeoutPolyfill(timeoutId) {
1322 var pos;
1323 if (!_gpfTimeoutQueue.every(function (timeoutItem, index) {
1324 if (timeoutItem.id === timeoutId) {
1325 pos = index;
1326 return false;
1327 }
1328 return true;
1329 })) {
1330 _gpfTimeoutQueue.splice(pos, 1);
1331 }
1332 }
1333 /**
1334 * For WSCRIPT and RHINO environments, this function must be used to process the timeout queue when using
1335 * [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout)
1336 * @since 0.1.5
1337 */
1338 function _gpfHandleTimeout() {
1339 var queue = _gpfTimeoutQueue, timeoutItem, now;
1340 while (queue.length) {
1341 timeoutItem = queue.shift();
1342 now = new Date().getTime();
1343 while (timeoutItem.dt > now) {
1344 _gpfSleep(timeoutItem.dt - now);
1345 now = new Date().getTime();
1346 }
1347 timeoutItem.cb();
1348 }
1349 }
1350 // Used only for WSCRIPT & RHINO environments
1351 if ("undefined" === typeof setTimeout) {
1352 /*jshint wsh: true*/
1353 /*eslint-env wsh*/
1354 /*jshint rhino: true*/
1355 /*eslint-env rhino*/
1356 if (_GPF_HOST.WSCRIPT === _gpfHost) {
1357 _gpfSleep = function (t) {
1358 WScript.Sleep(t); //eslint-disable-line new-cap
1359 }; /* istanbul ignore else */
1360 // unknown.1
1361 } else if (_GPF_HOST.RHINO === _gpfHost) {
1362 _gpfSleep = java.lang.Thread.sleep;
1363 } else {
1364 console.warn("No implementation for setTimeout");
1365 }
1366 _gpfMainContext.setTimeout = _gpSetTimeoutPolyfill;
1367 _gpfMainContext.clearTimeout = _gpfClearTimeoutPolyfill;
1368 /**
1369 * @gpf:sameas _gpfHandleTimeout
1370 * @since 0.1.5
1371 */
1372 gpf.handleTimeout = _gpfHandleTimeout;
1373 }
1374 var
1375 /**
1376 * The JSON.stringify() method converts a JavaScript value to a JSON string
1377 *
1378 * @param {*} value the value to convert to a JSON string
1379 * @return {String} JSON representation of the value
1380 * @since 0.1.5
1381 */
1382 _gpfJsonStringify,
1383 /**
1384 * The JSON.parse() method parses a string as JSON
1385 *
1386 * @param {*} text The string to parse as JSON
1387 * @return {Object} Parsed value
1388 * @since 0.1.5
1389 */
1390 _gpfJsonParse;
1391 // Filter functions and escape values
1392 function _gpfPreprocessValueForJson(callback) {
1393 return function (value, index) {
1394 if ("function" === typeof value) {
1395 return; // ignore
1396 }
1397 callback(_gpfJsonStringifyPolyfill(value), index);
1398 };
1399 }
1400 function _gpfObject2Json(object) {
1401 var isArray, results;
1402 isArray = object instanceof Array;
1403 results = [];
1404 if (isArray) {
1405 _gpfArrayForEach(object, _gpfPreprocessValueForJson(function (value) {
1406 results.push(value);
1407 }));
1408 return "[" + results.join(",") + "]";
1409 }
1410 _gpfObjectForEach(object, _gpfPreprocessValueForJson(function (value, property) {
1411 results.push(_gpfStringEscapeFor(property, "javascript") + ":" + value);
1412 }));
1413 return "{" + results.join(",") + "}";
1414 }
1415 function _gpfJsonStringifyObject(object) {
1416 return object.toString();
1417 }
1418 var _gpfJsonStringifyMapping = {
1419 undefined: _gpfEmptyFunc,
1420 "function": _gpfEmptyFunc,
1421 number: _gpfJsonStringifyObject,
1422 "boolean": _gpfJsonStringifyObject,
1423 string: function (object) {
1424 return _gpfStringEscapeFor(object, "javascript");
1425 },
1426 object: function (object) {
1427 if (null === object) {
1428 return "null";
1429 }
1430 return _gpfObject2Json(object);
1431 }
1432 };
1433 /*jshint -W003*/
1434 // Circular reference _gpfJsonStringifyPolyfill <-> _gpfObject2Json
1435 function _gpfJsonStringifyPolyfill(object) {
1436 return _gpfJsonStringifyMapping[typeof object](object);
1437 }
1438 /*jshint +W003*/
1439 function _gpfJsonParsePolyfill(test) {
1440 return _gpfFunc("return " + test)();
1441 }
1442 // Used only for environments where JSON is not defined
1443 if ("undefined" === typeof JSON) {
1444 _gpfJsonStringify = _gpfJsonStringifyPolyfill;
1445 _gpfJsonParse = _gpfJsonParsePolyfill;
1446 // Creates the JSON global object
1447 _gpfMainContext.JSON = {
1448 stringify: _gpfJsonStringify,
1449 parse: _gpfJsonParse
1450 };
1451 } else {
1452 _gpfJsonStringify = JSON.stringify;
1453 _gpfJsonParse = JSON.parse;
1454 }
1455 function _gpfGetObjectProperty(parent, name) {
1456 if (undefined !== parent) {
1457 return parent[name];
1458 }
1459 }
1460 function _gpfGetOrCreateObjectProperty(parent, name) {
1461 var result = parent[name];
1462 if (undefined === result) {
1463 result = parent[name] = {};
1464 }
1465 return result;
1466 }
1467 // Apply reducer on path
1468 function _gpfReduceContext(path, reducer) {
1469 var rootContext;
1470 if (path[0] === "gpf") {
1471 rootContext = gpf;
1472 path = path.slice(1);
1473 } else {
1474 rootContext = _gpfMainContext;
1475 }
1476 return path.reduce(reducer, rootContext);
1477 }
1478 /**
1479 * Result of {@link gpf.context} call, depends on the specified path
1480 * - when not specified, it returns the current host main context object
1481 * - when `"gpf"`, it **always** returns the GPF object
1482 * - when it leads to nothing, `undefined` is returned
1483
1484 * @typedef {*} gpf.typedef.contextResult
1485 * @since 0.1.5
1486 */
1487 /**
1488 * Resolve the provided contextual path and returns the result
1489 *
1490 * @param {String[]} path Array of identifiers
1491 * @param {Boolean} [createMissingParts=false] If the path includes undefined parts and createMissingParts is true,
1492 * it allocates a default empty object. This allows building namespaces on the fly.
1493 *
1494 * @return {gpf.typedef.contextResult} Resolved path
1495 * @since 0.1.5
1496 */
1497 function _gpfContext(path, createMissingParts) {
1498 var reducer;
1499 if (createMissingParts) {
1500 reducer = _gpfGetOrCreateObjectProperty;
1501 } else {
1502 reducer = _gpfGetObjectProperty;
1503 }
1504 return _gpfReduceContext(path, reducer);
1505 }
1506 /**
1507 * Resolve the provided contextual path and returns the result.
1508 *
1509 * @param {String} path Dot separated list of identifiers
1510 *
1511 * @return {gpf.typedef.contextResult} Resolved path
1512 * @since 0.1.5
1513 */
1514 gpf.context = function (path) {
1515 if (undefined === path) {
1516 return _gpfMainContext;
1517 }
1518 return _gpfContext(path.split("."));
1519 };
1520 gpf.extend = Object.assign;
1521 _gpfStringEscapes.html = Object.assign({}, _gpfStringEscapes.xml, {
1522 "à": "&agrave;",
1523 "á": "&aacute;",
1524 "è": "&egrave;",
1525 "é": "&eacute;",
1526 "ê": "&ecirc;"
1527 });
1528 var _GpfError = gpf.Error = function () {
1529 };
1530 _GpfError.prototype = new Error();
1531 Object.assign(_GpfError.prototype, /** @lends gpf.Error.prototype */
1532 {
1533 constructor: _GpfError,
1534 /**
1535 * Error code
1536 *
1537 * @readonly
1538 * @since 0.1.5
1539 */
1540 code: 0,
1541 /**
1542 * Error name
1543 *
1544 * @readonly
1545 * @since 0.1.5
1546 */
1547 name: "Error",
1548 /**
1549 * Error message
1550 *
1551 * @readonly
1552 * @since 0.1.5
1553 */
1554 message: "",
1555 /**
1556 * Build message by substituting context variables
1557 *
1558 * @param {Object} context Dictionary of named keys
1559 * @since 0.1.5
1560 */
1561 _buildMessage: function (context) {
1562 var replacements;
1563 if (context) {
1564 replacements = {};
1565 _gpfObjectForEach(context, function (value, key) {
1566 replacements["{" + key + "}"] = value.toString();
1567 });
1568 this.message = _gpfStringReplaceEx(this.message, replacements);
1569 }
1570 }
1571 });
1572 function _gpfErrorFactory(code, name, message) {
1573 function NewErrorClass(context) {
1574 this._buildMessage(context);
1575 }
1576 NewErrorClass.prototype = new _GpfError();
1577 Object.assign(NewErrorClass.prototype, {
1578 code: code,
1579 name: name,
1580 message: message
1581 });
1582 // constructor can't be enumerated with wscript
1583 NewErrorClass.prototype.constructor = NewErrorClass;
1584 _GpfError[_gpfStringCapitalize(name)] = NewErrorClass;
1585 return function (context) {
1586 throw new NewErrorClass(context);
1587 };
1588 }
1589 /**
1590 * Generates an error class
1591 *
1592 * @param {Number} code Error code
1593 * @param {String} name Error name
1594 * @param {String} message Error message
1595 * @return {Function} New error class
1596 * @gpf:closure
1597 * @since 0.1.5
1598 */
1599 function _gpfGenenerateErrorFunction(code, name, message) {
1600 var result = _gpfErrorFactory(code, name, message);
1601 result.CODE = code;
1602 result.NAME = name;
1603 result.MESSAGE = message;
1604 return result;
1605 }
1606 // Last allocated error code
1607 var _gpfLastErrorCode = 0;
1608 /**
1609 * Declare error messages.
1610 * Each source declares its own errors.
1611 *
1612 * @param {String} source Source name
1613 * @param {Object} dictionary Dictionary of error name to message
1614 * @since 0.1.5
1615 */
1616 function _gpfErrorDeclare(source, dictionary) {
1617 _gpfIgnore(source);
1618 _gpfObjectForEach(dictionary, function (message, name) {
1619 var code = ++_gpfLastErrorCode;
1620 gpf.Error["CODE_" + name.toUpperCase()] = code;
1621 gpf.Error[name] = _gpfGenenerateErrorFunction(code, name, message);
1622 });
1623 }
1624 _gpfErrorDeclare("error", {
1625 /**
1626 * ### Summary
1627 *
1628 * Method or function is not implemented
1629 *
1630 * ### Description
1631 *
1632 * This error is used to flag methods or functions that are not yet implemented.
1633 * @since 0.1.5
1634 */
1635 notImplemented: "Not implemented",
1636 /**
1637 * ### Summary
1638 *
1639 * An assertion failed
1640 *
1641 * ### Description
1642 *
1643 * This error is triggered when an assertion fails
1644 *
1645 * @see {@link gpf.assert}
1646 * @see {@link gpf.asserts}
1647 * @since 0.1.5
1648 */
1649 assertionFailed: "Assertion failed: {message}",
1650 /**
1651 * ### Summary
1652 *
1653 * Method or function was called with an invalid parameter
1654 *
1655 * ### Description
1656 *
1657 * This error is used when a parameter is invalid
1658 * @since 0.1.5
1659 */
1660 invalidParameter: "Invalid parameter"
1661 });
1662 function _gpfStringTrim(that) {
1663 return that.trim();
1664 }
1665 function _gpfFunctionDescribeName(functionToDescribe, resultDescription) {
1666 var name = functionToDescribe.compatibleName();
1667 if (name) {
1668 resultDescription.name = name;
1669 }
1670 }
1671 function _gpfFunctionDescribeParameters(functionToDescribe, functionSource, resultDescription) {
1672 if (functionToDescribe.length) {
1673 resultDescription.parameters = new RegExp("\\(\\s*(\\w+(?:\\s*,\\s*\\w+)*)\\s*\\)").exec(functionSource)[1].split(",").map(_gpfStringTrim);
1674 }
1675 }
1676 function _gpfFunctionDescribeBody(functionSource, resultDescription) {
1677 var body = _gpfStringTrim(new RegExp("{((?:.*\\n)*.*)}").exec(functionSource)[1]);
1678 if (body) {
1679 resultDescription.body = body;
1680 }
1681 }
1682 function _gpfFunctionDescribeSource(functionToDescribe, resultDescription) {
1683 var source = _gpfEmptyFunc.toString.call(functionToDescribe).replace(_gpfJsCommentsRegExp, "");
1684 _gpfFunctionDescribeParameters(functionToDescribe, source, resultDescription);
1685 _gpfFunctionDescribeBody(source, resultDescription);
1686 }
1687 /**
1688 * Extract function description
1689 *
1690 * @param {Function} functionToDescribe Function to describe
1691 * @return {gpf.typedef._functionDescription} Function description
1692 * @since 0.1.6
1693 */
1694 function _gpfFunctionDescribe(functionToDescribe) {
1695 var result = {};
1696 _gpfFunctionDescribeName(functionToDescribe, result);
1697 _gpfFunctionDescribeSource(functionToDescribe, result);
1698 return result;
1699 }
1700 function _gpfFunctionBuildSourceName(functionDescription) {
1701 if (functionDescription.name) {
1702 return " " + functionDescription.name;
1703 }
1704 return "";
1705 }
1706 function _gpfFunctionBuildSourceParameters(functionDescription) {
1707 if (functionDescription.parameters) {
1708 return functionDescription.parameters.join(", ");
1709 }
1710 return "";
1711 }
1712 function _gpfFunctionBuildSourceBody(functionDescription) {
1713 if (functionDescription.body) {
1714 return functionDescription.body.toString();
1715 }
1716 return "";
1717 }
1718 /**
1719 * Build function source from description
1720 *
1721 * @param {gpf.typedef._functionDescription} functionDescription Function description
1722 * @return {String} Function source
1723 * @since 0.1.6
1724 */
1725 function _gpfFunctionBuildSource(functionDescription) {
1726 return "function" + _gpfFunctionBuildSourceName(functionDescription) + "(" + _gpfFunctionBuildSourceParameters(functionDescription) + ") {\n\t\"use strict\"\n" + _gpfFunctionBuildSourceBody(functionDescription) + "\n}";
1727 }
1728 function _gpfFunctionBuildWithContext(functionSource, context) {
1729 var parameterNames = Object.keys(context), parameterValues = parameterNames.map(function (name) {
1730 return context[name];
1731 });
1732 return _gpfFunc(parameterNames, "return " + functionSource).apply(null, parameterValues);
1733 }
1734 /**
1735 * Build function from description and context
1736 *
1737 * @param {gpf.typedef._functionDescription} functionDescription Function description
1738 * @param {Object} [context] Function context
1739 * @return {Function} Function
1740 * @since 0.1.6
1741 */
1742 function _gpfFunctionBuild(functionDescription, context) {
1743 return _gpfFunctionBuildWithContext(_gpfFunctionBuildSource(functionDescription), context || {});
1744 }
1745 _gpfErrorDeclare("define/detect", {
1746 /**
1747 * ### Summary
1748 *
1749 * Entity type is invalid in the definition passed to {@link gpf.define}
1750 *
1751 * ### Description
1752 *
1753 * The entity type is either passed explicitly using the $type property or deduced from the type $ property
1754 * (for instance $class). This error is thrown when the entity type is either missing or invalid.
1755 * @since 0.1.6
1756 */
1757 invalidEntityType: "Invalid entity type"
1758 });
1759 /**
1760 * Dictionary mapping type (class...) to the corresponding typed Entity constructor.
1761 *
1762 * This dictionary is filled by subsequent entity types.
1763 * @since 0.1.6
1764 */
1765 var _gpfDefineTypedBuilders = {};
1766 /**
1767 * Search for type specific properties ($class...) and return associated builder function
1768 *
1769 * @param {Object} definition Entity definition literal object
1770 * @return {Function|undefined} Entity builder or undefined
1771 * @since 0.1.6
1772 */
1773 function _gpfDefineRead$TypedProperties(definition) {
1774 var ResultEntityBuilder;
1775 _gpfObjectForEach(_gpfDefineTypedBuilders, function (TypedEntityBuilder, type) {
1776 if (definition["$" + type]) {
1777 ResultEntityBuilder = TypedEntityBuilder;
1778 }
1779 });
1780 return ResultEntityBuilder;
1781 }
1782 /**
1783 * Check the $type property to return the associated builder function
1784 *
1785 * @param {Object} definition Entity definition literal object
1786 * @return {Function} Entity builder
1787 * @throws {gpf.Error.InvalidEntityType}
1788 * @since 0.1.6
1789 */
1790 function _gpfDefineCheck$TypeProperty(definition) {
1791 var typedEntityBuilder = _gpfDefineTypedBuilders[definition.$type];
1792 if (undefined === typedEntityBuilder) {
1793 gpf.Error.invalidEntityType();
1794 }
1795 return typedEntityBuilder;
1796 }
1797 /**
1798 * Factory to create the correct entity type
1799 *
1800 * @param {Object} definition Entity definition literal object
1801 * @return {_GpfEntityDefinition} Entity definition instance
1802 * @throws {gpf.Error.InvalidEntityType}
1803 * @since 0.1.6
1804 */
1805 function _gpfDefineBuildTypedEntity(definition) {
1806 var EntityBuilder = _gpfDefineRead$TypedProperties(definition), entityDefinition;
1807 if (!EntityBuilder) {
1808 EntityBuilder = _gpfDefineCheck$TypeProperty(definition);
1809 }
1810 entityDefinition = new EntityBuilder(definition);
1811 entityDefinition.check();
1812 return entityDefinition;
1813 }
1814 function _gpfRegExpGetNextMatch(regexp, string) {
1815 return regexp.exec(string);
1816 }
1817 function _gpfRegExpGetFirstMatch(regexp, string) {
1818 regexp.lastIndex = 0;
1819 return _gpfRegExpGetNextMatch(regexp, string);
1820 }
1821 /**
1822 * Executes the callback for each match of the regular expression.
1823 * When configured with /g and used before,
1824 * the [lastIndex](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/lastIndex)
1825 * must be first reset
1826 *
1827 * @param {RegExp} regexp Regular expression to execute
1828 * @param {String} string String to match
1829 * @return {Array} Array of matches
1830 * @since 0.2.1
1831 * @version 0.2.2 Reset lastIndex and returns the array of matches
1832 */
1833 function _gpfRegExpForEach(regexp, string) {
1834 var matches = [], match = _gpfRegExpGetFirstMatch(regexp, string);
1835 while (match) {
1836 matches.push(match);
1837 match = _gpfRegExpGetNextMatch(regexp, string);
1838 }
1839 return matches;
1840 }
1841 function _GpfEntityDefinition(definition) {
1842 _gpfAssert(definition && "object" === typeof definition, "Expected an entity definition");
1843 /*jshint validthis:true*/
1844 // constructor
1845 /*eslint-disable no-invalid-this*/
1846 this._initialDefinition = definition; /*eslint-enable no-invalid-this*/
1847 }
1848 _GpfEntityDefinition.prototype = {
1849 constructor: _GpfEntityDefinition,
1850 /**
1851 * Entity initial definition passed to {@link gpf.define}
1852 *
1853 * @readonly
1854 * @constant
1855 * @since 0.1.6
1856 */
1857 _initialDefinition: {}
1858 };
1859 _gpfErrorDeclare("abstract", {
1860 /**
1861 * ### Summary
1862 *
1863 * Method is abstract
1864 *
1865 * ### Description
1866 *
1867 * This error is used to implement abstract methods. Mostly used for interfaces.
1868 * @since 0.1.5
1869 */
1870 abstractMethod: "Abstract method"
1871 });
1872 /**
1873 * Build a function that throws the abstractMethod exception
1874 *
1875 * @param {Number} numberOfParameters Defines the signature of the resulting function
1876 * @return {Function} Function that throws the abstractMethod exception
1877 * @since 0.1.8
1878 */
1879 function _gpfCreateAbstractFunction(numberOfParameters) {
1880 return _gpfFunctionBuild({
1881 parameters: _gpfBuildFunctionParameterList(numberOfParameters),
1882 body: "_throw_();"
1883 }, { _throw_: gpf.Error.abstractMethod });
1884 }
1885 _gpfErrorDeclare("define/check", {
1886 /**
1887 * ### Summary
1888 *
1889 * One of the $ properties is invalid in the definition passed to {@link gpf.define}
1890 *
1891 * ### Description
1892 *
1893 * The list of possible $ properties is fixed and depends on the entity type.
1894 * This error is thrown when one $ property is not allowed.
1895 * @since 0.1.6
1896 */
1897 invalidEntity$Property: "Invalid entity $ property",
1898 /**
1899 * ### Summary
1900 *
1901 * Entity name is missing in the definition passed to {@link gpf.define}
1902 *
1903 * ### Description
1904 *
1905 * This error is thrown when the entity name is missing
1906 * @since 0.1.6
1907 */
1908 missingEntityName: "Missing entity name",
1909 /**
1910 * ### Summary
1911 *
1912 * Entity namespace is invalid in the definition passed to {@link gpf.define}
1913 *
1914 * ### Description
1915 *
1916 * This error is thrown when the namespace is invalid
1917 * @since 0.1.6
1918 */
1919 invalidEntityNamespace: "Invalid entity namespace"
1920 });
1921 function _gpfDefineEntityCheck$PropertyInAllowed$Properties(name, allowedList) {
1922 if (-1 === allowedList.indexOf(name)) {
1923 gpf.Error.invalidEntity$Property();
1924 }
1925 }
1926 function _gpfDefineEntityCheckProperty(value, name) {
1927 _gpfIgnore(value);
1928 /*jshint -W040*/
1929 /*eslint-disable no-invalid-this*/
1930 // bound through thisArg
1931 if (name.charAt(0) === "$") {
1932 this._check$Property(name.substr(1), value);
1933 } else {
1934 this._checkProperty(name, value);
1935 } /*jshint -W040*/
1936 /*eslint-enable no-invalid-this*/
1937 }
1938 Object.assign(_GpfEntityDefinition.prototype, /** @lends _GpfEntityDefinition.prototype */
1939 {
1940 /**
1941 * Entity type (class...)
1942 *
1943 * @readonly
1944 * @since 0.1.6
1945 */
1946 _type: "",
1947 /**
1948 * List of allowed $ properties
1949 *
1950 * @type {String[]}
1951 * @readonly
1952 * @since 0.1.6
1953 */
1954 _allowed$Properties: "type,name,namespace".split(","),
1955 /**
1956 * Check if the $ property is allowed
1957 *
1958 * @param {String} name $ Property name (without the starting $)
1959 * @param {*} value $ Property value
1960 * @since 0.1.6
1961 */
1962 _check$Property: function (name, value) {
1963 _gpfIgnore(value);
1964 if (name !== this._type) {
1965 _gpfDefineEntityCheck$PropertyInAllowed$Properties(name, this._allowed$Properties);
1966 }
1967 },
1968 /**
1969 * Throw the invalid property error
1970 *
1971 * @abstract
1972 * @protected
1973 * @since 0.1.8
1974 */
1975 _throwInvalidProperty: _gpfCreateAbstractFunction(0),
1976 /**
1977 * Regular expression used to validate member name
1978 *
1979 * @type {RegExp}
1980 * @readonly
1981 * @protected
1982 * @since 0.1.8
1983 */
1984 _reMemberName: new RegExp(".*"),
1985 /**
1986 * Check that the member name is a valid one
1987 *
1988 * @param {String} name Member name
1989 * @since 0.1.8
1990 */
1991 _checkMemberName: function (name) {
1992 if (!this._reMemberName.exec(name)) {
1993 this._throwInvalidProperty();
1994 }
1995 },
1996 /**
1997 * List of reserved member names
1998 *
1999 * @type {String[]}
2000 * @readonly
2001 * @constant
2002 * @since 0.1.8
2003 */
2004 _reservedNames: "super,class,public,private,protected,static,mixin".split(","),
2005 /**
2006 * Check that the member name is not a reserved one
2007 *
2008 * @param {String} name Member name
2009 * @since 0.1.6
2010 */
2011 _checkReservedMemberName: function (name) {
2012 if (-1 !== this._reservedNames.indexOf(name)) {
2013 this._throwInvalidProperty();
2014 }
2015 },
2016 /**
2017 * Check the value of the member
2018 *
2019 * @param {String} name Property name
2020 * @param {*} value Property value
2021 * @protected
2022 * @since 0.1.8
2023 */
2024 _checkMemberValue: _gpfFunc([
2025 "name",
2026 "value"
2027 ], " "),
2028 /**
2029 * Check if the property is allowed
2030 * NOTE: $ properties are handled by {@link _check$Property}
2031 *
2032 * @param {String} name Property name
2033 * @param {*} value Property value
2034 * @since 0.1.6
2035 */
2036 _checkProperty: function (name, value) {
2037 this._checkMemberName(name);
2038 this._checkReservedMemberName(name);
2039 this._checkMemberValue(name, value);
2040 },
2041 /**
2042 * Check the properties contained in the definition passed to {@link gpf.define}
2043 * @since 0.1.6
2044 */
2045 _checkProperties: function () {
2046 _gpfObjectForEach(this._initialDefinition, _gpfDefineEntityCheckProperty, this);
2047 },
2048 /**
2049 * Entity name
2050 * @since 0.1.6
2051 */
2052 _name: "",
2053 /**
2054 * Compute name property
2055 * @since 0.1.6
2056 */
2057 _readName: function () {
2058 var definition = this._initialDefinition;
2059 this._name = definition["$" + this._type] || definition.$name;
2060 },
2061 /**
2062 * Check if name property is not empty (throw the error otherwise)
2063 *
2064 * @throws {gpf.Error.MissingEntityName}
2065 * @since 0.1.6
2066 */
2067 _checkNameIsNotEmpty: function () {
2068 if (!this._name) {
2069 gpf.Error.missingEntityName();
2070 }
2071 },
2072 /**
2073 * Throw the invalid name error
2074 *
2075 * @abstract
2076 * @protected
2077 * @since 0.1.8
2078 */
2079 _throwInvalidName: _gpfCreateAbstractFunction(0),
2080 /**
2081 * Regular expression used to validate entity name
2082 *
2083 * @type {RegExp}
2084 * @readonly
2085 * @protected
2086 * @since 0.1.8
2087 */
2088 _reName: new RegExp(".*"),
2089 /**
2090 * Check name property (content)
2091 *
2092 * @since 0.1.6
2093 */
2094 _checkName: function () {
2095 if (!this._reName.exec(this._name)) {
2096 this._throwInvalidName();
2097 }
2098 },
2099 /**
2100 * Entity namespace
2101 * @since 0.1.6
2102 */
2103 _namespace: "",
2104 /**
2105 * If the name is prefixed with a namespace, isolate it and update name property
2106 *
2107 * @return {String|undefined} Namespace contained in the name or undefined if none
2108 * @since 0.1.6
2109 */
2110 _extractRelativeNamespaceFromName: function () {
2111 var parts = new RegExp("(.*)\\.([^\\.]+)$").exec(this._name);
2112 if (parts) {
2113 this._name = parts[2];
2114 return parts[1];
2115 }
2116 },
2117 /**
2118 * Compute namespace property
2119 * @since 0.1.6
2120 */
2121 _readNamespace: function () {
2122 var namespaces = [
2123 this._initialDefinition.$namespace,
2124 this._extractRelativeNamespaceFromName()
2125 ].filter(function (namespacePart) {
2126 return namespacePart;
2127 });
2128 if (namespaces.length > 0) {
2129 this._namespace = namespaces.join(".");
2130 }
2131 },
2132 /**
2133 * Check namespace property
2134 *
2135 * @throws {gpf.Error.InvalidEntityNamespace}
2136 * @since 0.1.6
2137 */
2138 _checkNamespace: function () {
2139 if (!new RegExp("^(:?[a-z_$][a-zA-Z0-9]+(:?\\.[a-z_$][a-zA-Z0-9]+)*)?$").exec(this._namespace)) {
2140 gpf.Error.invalidEntityNamespace();
2141 }
2142 },
2143 check: function () {
2144 this._checkProperties();
2145 this._readName();
2146 this._checkNameIsNotEmpty();
2147 this._readNamespace();
2148 this._checkName();
2149 this._checkNamespace();
2150 }
2151 });
2152 Object.assign(_GpfEntityDefinition.prototype, /** @lends _GpfEntityDefinition.prototype */
2153 {
2154 /**
2155 * Instance builder function (a.k.a. public constructor)
2156 *
2157 * @type {Function}
2158 * @since 0.1.6
2159 */
2160 _instanceBuilder: null,
2161 /**
2162 * @gpf:read _instanceBuilder
2163 * @since 0.1.6
2164 */
2165 getInstanceBuilder: function () {
2166 /* istanbul ignore else */
2167 // define.build.1
2168 if (!this._instanceBuilder) {
2169 this._setInstanceBuilder(this._build());
2170 }
2171 return this._instanceBuilder;
2172 },
2173 /**
2174 * @gpf:write _instanceBuilder
2175 * @since 0.1.6
2176 */
2177 _setInstanceBuilder: function (value) {
2178 if (this._namespace) {
2179 _gpfContext(this._namespace.split("."), true)[this._name] = value;
2180 }
2181 this._instanceBuilder = value;
2182 },
2183 /**
2184 * Process initial definition and generate instance builder function
2185 *
2186 * @return {Function} Instance builder function
2187 * @protected
2188 * @since 0.1.6
2189 */
2190 _build: _gpfEmptyFunc
2191 });
2192 function _GpfClassDefinition(definition) {
2193 /*jshint validthis:true*/
2194 // constructor
2195 /*eslint-disable no-invalid-this*/
2196 _GpfEntityDefinition.call(this, definition); /*eslint-enable no-invalid-this*/
2197 }
2198 _GpfClassDefinition.prototype = Object.create(_GpfEntityDefinition.prototype);
2199 Object.assign(_GpfClassDefinition.prototype, /** @lends _GpfClassDefinition.prototype */
2200 {
2201 constructor: _GpfClassDefinition,
2202 /**
2203 * @inheritdoc
2204 * @since 0.1.6
2205 */
2206 _type: "class"
2207 });
2208 _gpfDefineTypedBuilders["class"] = _GpfClassDefinition;
2209 _gpfErrorDeclare("define/class/check", {
2210 /**
2211 * ### Summary
2212 *
2213 * The class name is invalid
2214 *
2215 * ### Description
2216 *
2217 * Only a valid JavaScript identifier (starting with an uppercase letter, $ or _) is allowed
2218 * @since 0.1.6
2219 */
2220 invalidClassName: "Invalid class name",
2221 /**
2222 * ### Summary
2223 *
2224 * The class definition contains an invalid property
2225 *
2226 * ### Description
2227 *
2228 * Some keywords are reserved
2229 * @since 0.1.6
2230 */
2231 invalidClassProperty: "Invalid class property",
2232 /**
2233 * ### Summary
2234 *
2235 * The class definition contains an invalid $extend
2236 *
2237 * ### Description
2238 *
2239 * $extend can be either a class or a string that must resolve to a class using {@see gpf.context}
2240 * @since 0.1.6
2241 */
2242 invalidClassExtend: "Invalid class extend",
2243 /**
2244 * ### Summary
2245 *
2246 * The class constructor must be a method
2247 *
2248 * ### Description
2249 *
2250 * The constructor member is a special one, see {@tutorial DEFINE}
2251 *
2252 * @since 0.1.7
2253 */
2254 invalidClassConstructor: "Invalid class constructor",
2255 /**
2256 * ### Summary
2257 *
2258 * A member override is changing the type
2259 *
2260 * ### Description
2261 *
2262 * The constructor member is a special one, see {@tutorial DEFINE}
2263 *
2264 * @since 0.1.7
2265 */
2266 invalidClassOverride: "Invalid class override"
2267 });
2268 /**
2269 * If extend is a string, apply _gpfContext on it
2270 *
2271 * @param {*} extend Extend value
2272 * @return {*} The initial value or the context one
2273 * @since 0.1.6
2274 */
2275 function _gpfDefineClassDecontextifyExtend(extend) {
2276 if ("string" === typeof extend) {
2277 return _gpfContext(extend.split("."));
2278 }
2279 return extend;
2280 }
2281 Object.assign(_GpfClassDefinition.prototype, /** @lends _gpfClassDefinition.prototype */
2282 {
2283 /**
2284 * @inheritdoc
2285 * @since 0.1.6
2286 */
2287 _allowed$Properties: _GpfEntityDefinition.prototype._allowed$Properties.concat(["extend"]),
2288 /**
2289 * @iheritdoc
2290 * @since 0.1.8
2291 */
2292 _throwInvalidProperty: gpf.Error.invalidClassProperty,
2293 /**
2294 * @inheritdoc
2295 * @since 0.1.8
2296 */
2297 _reMemberName: new RegExp("^[a-z_][a-zA-Z0-9]*$"),
2298 /**
2299 * Check that the constructor is a method
2300 *
2301 * @param {*} constructorValue Value read from definition dictionary
2302 * @throws {gpf.Error.InvalidClassConstructor}
2303 * @since 0.1.7
2304 */
2305 _checkConstructorMember: function (constructorValue) {
2306 if ("function" !== typeof constructorValue) {
2307 gpf.Error.invalidClassConstructor();
2308 }
2309 },
2310 /**
2311 * Check if the value correspond to the overridden value
2312 *
2313 * @param {*} value Member value
2314 * @param {*} overriddenValue Overridden member value
2315 * @throws {gpf.Error.InvalidClassOverride}
2316 * @since 0.1.7
2317 */
2318 _checkOverridenMember: function (value, overriddenValue) {
2319 if (typeof value !== typeof overriddenValue) {
2320 gpf.Error.invalidClassOverride();
2321 }
2322 },
2323 /**
2324 * Check if the member overrides an inherited one
2325 *
2326 * @param {String} name Member name
2327 * @param {*} value Member value
2328 * @throws {gpf.Error.InvalidClassOverride}
2329 * @since 0.1.7
2330 */
2331 _checkIfOverriddenMember: function (name, value) {
2332 var overriddenMember = this._extend.prototype[name];
2333 if (undefined !== overriddenMember) {
2334 this._checkOverridenMember(value, overriddenMember);
2335 }
2336 },
2337 /**
2338 * Check the value of the member:
2339 * - If the member name is "constructor", it must be a function
2340 *
2341 * @param {String} name Property name
2342 * @param {*} value Property value
2343 * @since 0.1.7
2344 */
2345 _checkMemberValue: function (name, value) {
2346 if ("constructor" === name) {
2347 this._checkConstructorMember(value);
2348 } else {
2349 this._checkIfOverriddenMember(name, value);
2350 }
2351 },
2352 /**
2353 * @inheritdoc
2354 * @since 0.1.8
2355 */
2356 _reName: new RegExp("^[A-Z_$][a-zA-Z0-9]*$"),
2357 /**
2358 * @iheritdoc
2359 * @since 0.1.8
2360 */
2361 _throwInvalidName: gpf.Error.invalidClassName,
2362 /**
2363 * Base class
2364 *
2365 * @type {Function}
2366 * @since 0.1.6
2367 */
2368 _extend: Object,
2369 /**
2370 * Read extend property
2371 * @since 0.1.6
2372 */
2373 _readExtend: function () {
2374 var extend = _gpfDefineClassDecontextifyExtend(this._initialDefinition.$extend);
2375 if (extend) {
2376 this._extend = extend;
2377 }
2378 },
2379 /**
2380 * Check if the extend property points to an interface
2381 *
2382 * @throws {gpf.Error.InvalidClassExtend}
2383 * @since 0.1.8
2384 */
2385 _checkExtendIsNotAnInterface: function () {
2386 if (-1 !== _gpfEmptyFunc.toString.call(this._extend).indexOf("interfaceConstructorFunction")) {
2387 gpf.Error.invalidClassExtend();
2388 }
2389 },
2390 /**
2391 * Check extend property
2392 *
2393 * @throws {gpf.Error.InvalidClassExtend}
2394 * @since 0.1.6
2395 */
2396 _checkExtend: function () {
2397 if ("function" !== typeof this._extend) {
2398 gpf.Error.invalidClassExtend();
2399 }
2400 this._checkExtendIsNotAnInterface();
2401 },
2402 /**
2403 * @inheritdoc
2404 * @since 0.1.6
2405 */
2406 check: function () {
2407 this._readExtend();
2408 this._checkExtend();
2409 _GpfEntityDefinition.prototype.check.call(this);
2410 }
2411 });
2412 _gpfErrorDeclare("define/class/constructor", { "classConstructorFunction": "This is a class constructor function, use with new" });
2413 Object.assign(_GpfClassDefinition.prototype, /** @lends _GpfClassDefinition.prototype */
2414 {
2415 /**
2416 * Resolved constructor
2417 *
2418 * @type {Function}
2419 * @since 0.1.6
2420 */
2421 _resolvedConstructor: _gpfEmptyFunc
2422 });
2423 function _gpfDefineGetClassSecuredConstructorDefinition(classDefinition) {
2424 var name = classDefinition._name;
2425 return {
2426 name: name,
2427 parameters: _gpfFunctionDescribe(classDefinition._resolvedConstructor).parameters,
2428 body: "if (!(this instanceof _classDef_._instanceBuilder)) gpf.Error.classConstructorFunction();\n" + "_classDef_._resolvedConstructor.apply(this, arguments);"
2429 };
2430 }
2431 function _gpfDefineGetClassSecuredConstructorContext(classDefinition) {
2432 return {
2433 gpf: gpf,
2434 _classDef_: classDefinition
2435 };
2436 }
2437 /**
2438 * Allocate a secured named constructor
2439 *
2440 * @param {_GpfClassDefinition} classDefinition Class definition
2441 * @return {Function} Secured named constructor
2442 * @gpf:closure
2443 * @since 0.1.6
2444 */
2445 function _gpfDefineGetClassSecuredConstructor(classDefinition) {
2446 return _gpfFunctionBuild(_gpfDefineGetClassSecuredConstructorDefinition(classDefinition), _gpfDefineGetClassSecuredConstructorContext(classDefinition));
2447 }
2448 _gpfErrorDeclare("define/class/super", {
2449 /**
2450 * ### Summary
2451 *
2452 * $super used in a member that doesn't override a method
2453 *
2454 * ### Description
2455 *
2456 * $super can't be used if the method does not override an inherited one
2457 * @since 0.1.7
2458 */
2459 invalidClassSuper: "Invalid class super",
2460 /**
2461 * ### Summary
2462 *
2463 * An invalid member of $super was used
2464 *
2465 * ### Description
2466 *
2467 * $super members must point to a method exposed by the inherited prototype.
2468 * @since 0.1.7
2469 */
2470 invalidClassSuperMember: "Invalid class super member"
2471 });
2472 /**
2473 * Used when $super points to a non existent member
2474 *
2475 * @throws {gpf.Error.InvalidClassSuper}
2476 * @since 0.1.7
2477 */
2478 function _gpfClassNoSuper() {
2479 gpf.Error.invalidClassSuper();
2480 }
2481 /**
2482 * Copy super method signature and invokes it.
2483 * NOTE: it is required to create a new function as it will receive additional members
2484 *
2485 * @param {Function} superMethod Super method to copy
2486 * @return {Function} New function that wraps the super method
2487 * @since 0.1.7
2488 */
2489 function _gpfClassSuperCreateWithSameSignature(superMethod) {
2490 var definition = _gpfFunctionDescribe(superMethod);
2491 definition.body = "return _superMethod_.apply(this, arguments);";
2492 return _gpfFunctionBuild(definition, { _superMethod_: superMethod });
2493 }
2494 /**
2495 * Create $super function, either based on super method or triggering an error
2496 *
2497 * @param {*} superMember Member extracted from inherited prototype
2498 * @return {Function} $super function
2499 * @since 0.1.7
2500 */
2501 function _gpfClassSuperCreate(superMember) {
2502 if ("function" !== typeof superMember) {
2503 superMember = _gpfClassNoSuper;
2504 }
2505 return _gpfClassSuperCreateWithSameSignature(superMember);
2506 }
2507 /**
2508 * Copy super method signature and apply weak binding.
2509 *
2510 * @param {Object} that Object instance
2511 * @param {Function} $super $super member
2512 * @param {*} superMethod superMember Member extracted from inherited prototype
2513 * @return {Function} $super method
2514 * @since 0.1.7
2515 */
2516 function _gpfClassSuperCreateWeakBoundWithSameSignature(that, $super, superMethod) {
2517 var definition = _gpfFunctionDescribe(superMethod);
2518 definition.body = "return _superMethod_.apply(this === _$super_ ? _that_ : this, arguments);";
2519 return _gpfFunctionBuild(definition, {
2520 _that_: that,
2521 _$super_: $super,
2522 _superMethod_: superMethod
2523 });
2524 }
2525 /**
2526 * Create $super method
2527 * NOTE: if the super method is not a function, an exception is thrown
2528 *
2529 * @param {Object} that Object instance
2530 * @param {Function} $super $super member
2531 * @param {*} superMethod superMember Member extracted from inherited prototype
2532 * @return {Function} $super method
2533 * @throws {gpf.Error.InvalidClassSuperMember}
2534 * @since 0.1.7
2535 */
2536 function _gpfClassSuperCreateMember(that, $super, superMethod) {
2537 if ("function" !== typeof superMethod) {
2538 gpf.Error.invalidClassSuperMember();
2539 }
2540 return _gpfClassSuperCreateWeakBoundWithSameSignature(that, $super, superMethod);
2541 }
2542 /**
2543 * Regular expression detecting .$super use
2544 *
2545 * @type {RegExp}
2546 * @since 0.2.1
2547 */
2548 var _gpfClassSuperRegExp = new RegExp("\\.\\$super\\.(\\w+)\\b", "g");
2549 /**
2550 * Extract all 'members' that are used on $super
2551 *
2552 * @param {Function} method Method to analyze
2553 * @return {String[]} Member names that are used
2554 * @since 0.1.7
2555 */
2556 function _gpfClassMethodExtractSuperMembers(method) {
2557 return _gpfRegExpForEach(_gpfClassSuperRegExp, method).map(function (match) {
2558 return match[1];
2559 });
2560 }
2561 Object.assign(_GpfClassDefinition.prototype, /** @lends _GpfClassDefinition.prototype */
2562 {
2563 /**
2564 * Called before invoking a that contains $super method, it is responsible of allocating the $super object
2565 *
2566 * @param {Object} that Object instance
2567 * @param {String} methodName Name of the method that uses $super
2568 * @param {String[]} superMembers Expected member names on $super
2569 * @return {Function} $super method
2570 * @since 0.1.7
2571 */
2572 _get$Super: function (that, methodName, superMembers) {
2573 var superProto = this._extend.prototype, $super = _gpfClassSuperCreate(superProto[methodName]);
2574 superMembers.forEach(function (memberName) {
2575 $super[memberName] = _gpfClassSuperCreateMember(that, $super, superProto[memberName]);
2576 });
2577 return $super;
2578 },
2579 /**
2580 * Body of superified method
2581 * @since 0.1.7
2582 */
2583 _superifiedBody: "var _super_;\n" + "if (this.hasOwnProperty(\"$super\")) {\n" + " _super_ = this.$super;\n" + "}\n" + "this.$super = _classDef_._get$Super(this, _methodName_, _superMembers_);\n" + "try{\n" + " var _result_ = _method_.apply(this, arguments);\n" + "} finally {\n" + " if (undefined === _super_) {\n" + " delete this.$super;\n" + " } else {\n" + " this.$super = _super_;\n" + " }\n" + "}\n" + "return _result_;",
2584 /**
2585 * Generates context for the superified method
2586 *
2587 * @param {Function} method Method to superify
2588 * @param {String} methodName Name of the method (used to search in object prototype)
2589 * @param {String[]} superMembers Detected $super members used in the method
2590 * @return {Object} Context of superified method
2591 * @since 0.1.7
2592 */
2593 _getSuperifiedContext: function (method, methodName, superMembers) {
2594 return {
2595 _method_: method,
2596 _methodName_: methodName,
2597 _superMembers_: superMembers,
2598 _classDef_: this
2599 };
2600 },
2601 /**
2602 * Generates the superified version of the method
2603 *
2604 * @param {Function} method Method to superify
2605 * @param {String} methodName Name of the method (used to search in object prototype)
2606 * @param {String[]} superMembers Detected $super members used in the method
2607 * @return {Function} Superified method
2608 * @since 0.1.7
2609 */
2610 _createSuperified: function (method, methodName, superMembers) {
2611 // Keep signature
2612 var description = _gpfFunctionDescribe(method);
2613 description.body = this._superifiedBody;
2614 return _gpfFunctionBuild(description, this._getSuperifiedContext(method, methodName, superMembers));
2615 },
2616 /**
2617 * Create a method that can use this.$super
2618 *
2619 * @param {Function} method Method to superify
2620 * @param {String} methodName Name of the method (used to search in object prototype)
2621 * @return {Function} Superified method
2622 * @since 0.1.7
2623 */
2624 _superify: function (method, methodName) {
2625 if (new RegExp("\\.\\$super\\b").exec(method)) {
2626 return this._createSuperified(method, methodName, _gpfClassMethodExtractSuperMembers(method));
2627 }
2628 return method;
2629 }
2630 });
2631 Object.assign(_GpfClassDefinition.prototype, /** @lends _GpfClassDefinition.prototype */
2632 {
2633 /**
2634 * @inheritdoc
2635 * @since 0.1.6
2636 */
2637 _build: function () {
2638 var newClass, newPrototype;
2639 this._resolveConstructor();
2640 newClass = _gpfDefineGetClassSecuredConstructor(this);
2641 // Basic JavaScript inheritance mechanism: Defines the newClass prototype as an instance of the super class
2642 newPrototype = Object.create(this._extend.prototype);
2643 // Populate our constructed prototype object
2644 newClass.prototype = newPrototype;
2645 // Enforce the constructor to be what we expect
2646 newPrototype.constructor = newClass;
2647 this._buildPrototype(newPrototype);
2648 return newClass;
2649 },
2650 /**
2651 * Add method to the new class prototype
2652 *
2653 * @param {Object} newPrototype New class prototype
2654 * @param {String} methodName Method name
2655 * @param {Function} method Method
2656 * @since 0.1.7
2657 */
2658 _addMethodToPrototype: function (newPrototype, methodName, method) {
2659 newPrototype[methodName] = this._superify(method, methodName);
2660 },
2661 /**
2662 * Add member to the new class prototype
2663 *
2664 * @param {Object} newPrototype New class prototype
2665 * @param {String} memberName Member name
2666 * @param {*} value Member value
2667 * @since 0.1.7
2668 */
2669 _addMemberToPrototype: function (newPrototype, memberName, value) {
2670 if ("function" === typeof value) {
2671 this._addMethodToPrototype(newPrototype, memberName, value);
2672 } else {
2673 newPrototype[memberName] = value;
2674 }
2675 },
2676 /**
2677 * Build the new class prototype
2678 *
2679 * @param {Object} newPrototype New class prototype
2680 * @since 0.1.7
2681 */
2682 _buildPrototype: function (newPrototype) {
2683 _gpfObjectForEach(this._initialDefinition, function (value, memberName) {
2684 if (memberName.charAt(0) !== "$" && memberName !== "constructor") {
2685 this._addMemberToPrototype(newPrototype, memberName, value); //eslint-disable-line no-invalid-this
2686 }
2687 }, this);
2688 },
2689 /**
2690 * Set the inherited constructor if not Object
2691 * @since 0.1.7
2692 */
2693 _setResolvedConstructorToInherited: function () {
2694 if (this._extend !== Object) {
2695 this._resolvedConstructor = this._extend;
2696 }
2697 },
2698 /**
2699 * Assign the proper constructor to _resolvedConstructor
2700 * @since 0.1.7
2701 */
2702 _resolveConstructor: function () {
2703 if (this._initialDefinition.hasOwnProperty("constructor")) {
2704 /* jshint -W069*/
2705 /*eslint-disable dot-notation*/
2706 this._resolvedConstructor = this._superify(this._initialDefinition["constructor"], "constructor"); /* jshint +W069*/
2707 /*eslint-enable dot-notation*/
2708 } else {
2709 this._setResolvedConstructorToInherited();
2710 }
2711 }
2712 });
2713 function _GpfInterfaceDefinition(definition) {
2714 /*jshint validthis:true*/
2715 // constructor
2716 /*eslint-disable no-invalid-this*/
2717 _GpfEntityDefinition.call(this, definition); /*eslint-enable no-invalid-this*/
2718 }
2719 _GpfInterfaceDefinition.prototype = Object.create(_GpfEntityDefinition.prototype);
2720 Object.assign(_GpfInterfaceDefinition.prototype, /** @lends _GpfInterfaceDefinition.prototype */
2721 {
2722 constructor: _GpfInterfaceDefinition,
2723 /**
2724 * @inheritdoc
2725 * @since 0.1.8
2726 */
2727 _type: "interface"
2728 });
2729 _gpfDefineTypedBuilders["interface"] = _GpfInterfaceDefinition;
2730 _gpfErrorDeclare("define/interface/check", {
2731 /**
2732 * ### Summary
2733 *
2734 * The interface name is invalid
2735 *
2736 * ### Description
2737 *
2738 * Only a valid JavaScript identifier (starting with an uppercase I) is allowed
2739 * @since 0.1.8
2740 */
2741 invalidInterfaceName: "Invalid interface name",
2742 /**
2743 * ### Summary
2744 *
2745 * The interface definition contains an invalid property
2746 *
2747 * ### Description
2748 *
2749 * An interface can contain only methods and no constructor
2750 * @since 0.1.8
2751 */
2752 invalidInterfaceProperty: "Invalid interface property"
2753 });
2754 Object.assign(_GpfInterfaceDefinition.prototype, /** @lends _GpfInterfaceDefinition.prototype */
2755 {
2756 /**
2757 * @iheritdoc
2758 * @since 0.1.8
2759 */
2760 _throwInvalidProperty: gpf.Error.invalidInterfaceProperty,
2761 /**
2762 * @inheritdoc
2763 * @since 0.1.8
2764 */
2765 _reMemberName: new RegExp("^[a-z][a-zA-Z0-9]*$"),
2766 /**
2767 * @inheritdoc
2768 * @since 0.1.8
2769 */
2770 _reservedNames: _GpfEntityDefinition.prototype._reservedNames.concat("constructor"),
2771 /**
2772 * @inheritdoc
2773 * @since 0.1.8
2774 */
2775 _checkMemberValue: function (name, value) {
2776 if ("function" !== typeof value) {
2777 gpf.Error.invalidInterfaceProperty();
2778 }
2779 },
2780 /**
2781 * @inheritdoc
2782 * @since 0.1.8
2783 */
2784 _reName: new RegExp("^I[a-zA-Z0-9]*$"),
2785 /**
2786 * @iheritdoc
2787 * @since 0.1.8
2788 */
2789 _throwInvalidName: gpf.Error.invalidInterfaceName
2790 });
2791 _gpfErrorDeclare("define/interface/constructor", { "interfaceConstructorFunction": "This is an interface constructor function, do not invoke" });
2792 function _gpfDefineGetInterfaceConstructorDefinition(interfaceDefinition) {
2793 var name = interfaceDefinition._name;
2794 return {
2795 name: name,
2796 body: "gpf.Error.interfaceConstructorFunction();"
2797 };
2798 }
2799 function _gpfDefineGetInterfaceConstructorContext(interfaceDefinition) {
2800 return {
2801 gpf: gpf,
2802 _classDef_: interfaceDefinition
2803 };
2804 }
2805 /**
2806 * Allocate a secured named constructor
2807 *
2808 * @param {_GpfInterfaceDefinition} interfaceDefinition Interface definition
2809 * @return {Function} Secured named constructor
2810 * @gpf:closure
2811 * @since 0.1.8
2812 */
2813 function _gpfDefineGetInterfaceConstructor(interfaceDefinition) {
2814 return _gpfFunctionBuild(_gpfDefineGetInterfaceConstructorDefinition(interfaceDefinition), _gpfDefineGetInterfaceConstructorContext(interfaceDefinition));
2815 }
2816 Object.assign(_GpfInterfaceDefinition.prototype, /** @lends _GpfInterfaceDefinition.prototype */
2817 {
2818 /**
2819 * @inheritdoc
2820 * @since 0.1.8
2821 */
2822 _build: function () {
2823 var newClass, newPrototype;
2824 newClass = _gpfDefineGetInterfaceConstructor(this);
2825 // Populate our constructed prototype object
2826 newPrototype = newClass.prototype;
2827 // Ensure no constructor on prototype (because of interface)
2828 delete newPrototype.constructor;
2829 this._buildPrototype(newPrototype);
2830 return newClass;
2831 },
2832 /**
2833 * Build the new class prototype
2834 *
2835 * @param {Object} newPrototype New class prototype
2836 * @since 0.1.7
2837 */
2838 _buildPrototype: function (newPrototype) {
2839 _gpfObjectForEach(this._initialDefinition, function (value, memberName) {
2840 if (memberName.charAt(0) !== "$") {
2841 newPrototype[memberName] = value;
2842 }
2843 }, this);
2844 }
2845 });
2846 function _gpfDefine(definition) {
2847 var entityDefinition = _gpfDefineBuildTypedEntity(definition);
2848 return entityDefinition.getInstanceBuilder();
2849 }
2850 /**
2851 * @gpf:sameas _gpfDefine
2852 * @since 0.1.6
2853 */
2854 gpf.define = _gpfDefine;
2855 _gpfErrorDeclare("interfaces", {
2856 /**
2857 * ### Summary
2858 *
2859 * Expected interface not implemented
2860 *
2861 * ### Description
2862 *
2863 * This error is used when a function expects a specific interface and it can't be resolved with the provided
2864 * parameter.
2865 * @since 0.1.8
2866 */
2867 interfaceExpected: "Expected interface not implemented: {name}"
2868 });
2869 function _gpfInterfaceIsInvalidMethod(referenceMethod, method) {
2870 return "function" !== typeof method || referenceMethod.length !== method.length;
2871 }
2872 /**
2873 * Verify that the object implements the specified interface
2874 *
2875 * @param {Function} interfaceSpecifier Reference interface
2876 * @param {Object} inspectedObject Object (or class prototype) to inspect
2877 * @return {Boolean} True if implemented
2878 * @since 0.1.8
2879 */
2880 function _gpfInterfaceIsImplementedBy(interfaceSpecifier, inspectedObject) {
2881 var result = true;
2882 _gpfObjectForEach(interfaceSpecifier.prototype, function (referenceMethod, name) {
2883 if (_gpfInterfaceIsInvalidMethod(referenceMethod, inspectedObject[name])) {
2884 result = false;
2885 }
2886 });
2887 return result;
2888 }
2889 /**
2890 * Retrieve an object implementing the expected interface from an object using the IUnknown interface
2891 *
2892 * @param {Function} interfaceSpecifier Reference interface
2893 * @param {Object} queriedObject Object to query
2894 * @return {Object|null} Object implementing the interface or null
2895 * @since 0.1.8
2896 */
2897 function _gpfInterfaceQueryThroughIUnknown(interfaceSpecifier, queriedObject) {
2898 var result = queriedObject.queryInterface(interfaceSpecifier);
2899 _gpfAssert(null === result || _gpfInterfaceIsImplementedBy(interfaceSpecifier, result), "Invalid result of queryInterface (must be null or an object implementing the interface)");
2900 return result;
2901 }
2902 /**
2903 * Retrieve an object implementing the expected interface from an object trying the IUnknown interface
2904 *
2905 * @param {Function} interfaceSpecifier Reference interface
2906 * @param {Object} queriedObject Object to query
2907 * @return {Object|null|undefined} Object implementing the interface or null,
2908 * undefined is returned when IUnknown is not implemented
2909 * @since 0.1.8
2910 */
2911 function _gpfInterfaceQueryTryIUnknown(interfaceSpecifier, queriedObject) {
2912 if (_gpfInterfaceIsImplementedBy(gpf.interfaces.IUnknown, queriedObject)) {
2913 return _gpfInterfaceQueryThroughIUnknown(interfaceSpecifier, queriedObject);
2914 }
2915 }
2916 /**
2917 * Retrieve an object implementing the expected interface from an object:
2918 * - Either the object implements the interface, it is returned
2919 * - Or the object implements IUnknown, then queryInterface is used
2920 *
2921 * @param {Function} interfaceSpecifier Reference interface
2922 * @param {Object} queriedObject Object to query
2923 * @return {Object|null|undefined} Object implementing the interface or null,
2924 * undefined is returned when IUnknown is not implemented
2925 * @since 0.1.8
2926 */
2927 function _gpfInterfaceQuery(interfaceSpecifier, queriedObject) {
2928 if (_gpfInterfaceIsImplementedBy(interfaceSpecifier, queriedObject)) {
2929 return queriedObject;
2930 }
2931 return _gpfInterfaceQueryTryIUnknown(interfaceSpecifier, queriedObject);
2932 }
2933 function _gpfInterfaceResolveSpecifier(interfaceSpecifier) {
2934 if ("string" === typeof interfaceSpecifier) {
2935 return _gpfContext(interfaceSpecifier.split("."));
2936 }
2937 return interfaceSpecifier;
2938 }
2939 function _gpfInterfaceToInspectableObject(inspectedObject) {
2940 if (inspectedObject instanceof Function) {
2941 return inspectedObject.prototype;
2942 }
2943 return inspectedObject;
2944 }
2945 /**
2946 * @namespace gpf.interfaces
2947 * @description Root namespace for GPF interfaces
2948 * @since 0.1.8
2949 */
2950 var _gpfI = gpf.interfaces = {
2951 /**
2952 * Verify that the object (or class) implements the current interface
2953 *
2954 * @param {Function|String} interfaceSpecifier Interface specifier
2955 * @param {Object|Function} inspectedObject object (or class) to inspect.
2956 * @return {Boolean} True if implemented
2957 * @since 0.1.8
2958 */
2959 isImplementedBy: function (interfaceSpecifier, inspectedObject) {
2960 if (!inspectedObject) {
2961 return false;
2962 }
2963 return _gpfInterfaceIsImplementedBy(_gpfInterfaceResolveSpecifier(interfaceSpecifier), _gpfInterfaceToInspectableObject(inspectedObject));
2964 },
2965 /**
2966 * Retrieve an object implementing the expected interface from an object:
2967 * - Either the object implements the interface, it is returned
2968 * - Or the object implements IUnknown, then queryInterface is used
2969 *
2970 * @param {Function|String} interfaceSpecifier Interface specifier
2971 * @param {Object} queriedObject Object to query
2972 * @return {Object|null|undefined} Object implementing the interface or null,
2973 * undefined is returned when IUnknown is not implemented
2974 * @since 0.1.8
2975 */
2976 query: function (interfaceSpecifier, queriedObject) {
2977 return _gpfInterfaceQuery(_gpfInterfaceResolveSpecifier(interfaceSpecifier), queriedObject);
2978 }
2979 };
2980 /**
2981 * Internal interface definition helper
2982 *
2983 * @param {String} name Interface name
2984 * @param {Object} definition Interface definition association method names to the number
2985 * of parameters
2986 * @return {Function} Interface specifier
2987 * @since 0.1.9
2988 */
2989 function _gpfDefineInterface(name, definition) {
2990 var interfaceDefinition = { $interface: "gpf.interfaces.I" + name };
2991 Object.keys(definition).forEach(function (methodName) {
2992 interfaceDefinition[methodName] = _gpfCreateAbstractFunction(definition[methodName]);
2993 });
2994 return _gpfDefine(interfaceDefinition);
2995 }
2996 _gpfDefineInterface("Unknown", { "queryInterface": 1 });
2997 function _gpfCreateSortVariables(specifications) {
2998 return "var " + specifications.map(function (specification, index) {
2999 return "a" + index + "=a." + specification.property + ",b" + index + "=b." + specification.property;
3000 }).join(",") + ";";
3001 }
3002 function _gpfCreateSortComparison(type, left, right) {
3003 if ("string" === type) {
3004 return left + ".localeCompare(" + right + ")";
3005 }
3006 // default is number
3007 return left + "-" + right;
3008 }
3009 function _gpfCreateSortCondition(specification, index) {
3010 var left, right;
3011 if (specification.ascending === false) {
3012 left = "b" + index;
3013 right = "a" + index;
3014 } else {
3015 left = "a" + index;
3016 right = "b" + index;
3017 }
3018 return "if(" + left + "!==" + right + ")return " + _gpfCreateSortComparison(specification.type, left, right) + ";";
3019 }
3020 function _gpfCreateSortBody(specifications) {
3021 return _gpfCreateSortVariables(specifications) + specifications.map(_gpfCreateSortCondition).join("") + "return 0;";
3022 }
3023 /**
3024 * Create a sorting function based on the given specification
3025 *
3026 * @param {gpf.typedef.sortItem[]} specifications Sort specification
3027 * @return {Function} Function that can compare two objects
3028 * @since 0.1.9
3029 */
3030 function _gpfCreateSortFunction(specifications) {
3031 return _gpfFunc([
3032 "a",
3033 "b"
3034 ], _gpfCreateSortBody(specifications));
3035 }
3036 /**
3037 * Create a sorting function based on the given specification
3038 *
3039 * @param {gpf.typedef.sortItem|gpf.typedef.sortItem[]} specifications Sort specification
3040 * @return {Function} Function that can compare two objects
3041 * @since 0.1.9
3042 */
3043 gpf.createSortFunction = function (specifications) {
3044 if (!_gpfIsArray(specifications)) {
3045 specifications = [specifications];
3046 }
3047 return _gpfCreateSortFunction(specifications);
3048 };
3049 var _gpfCreateFilterConvert;
3050 function _gpfCreateFilterArrayConverter(member, operator) {
3051 return function (specification) {
3052 return "(" + specification[member].map(_gpfCreateFilterConvert).join(operator) + ")";
3053 };
3054 }
3055 function _gpfCreateFilterLike(specification) {
3056 return "/" + specification.regexp + "/.exec(" + _gpfCreateFilterConvert(specification.like) + ")";
3057 }
3058 var
3059 // List of converters
3060 _gpfCreateFilterConverters = {
3061 property: function (specification) {
3062 return "i." + specification.property;
3063 },
3064 eq: _gpfCreateFilterArrayConverter("eq", "==="),
3065 ne: _gpfCreateFilterArrayConverter("ne", "!=="),
3066 lt: _gpfCreateFilterArrayConverter("lt", "<"),
3067 lte: _gpfCreateFilterArrayConverter("lte", "<="),
3068 gt: _gpfCreateFilterArrayConverter("gt", ">"),
3069 gte: _gpfCreateFilterArrayConverter("gte", ">="),
3070 not: function (specification) {
3071 return "!" + _gpfCreateFilterConvert(specification.not);
3072 },
3073 like: function (specification) {
3074 var like = _gpfCreateFilterLike(specification);
3075 if (specification.group) {
3076 return "(" + like + "||[])[" + specification.group + "]";
3077 }
3078 return like;
3079 },
3080 or: _gpfCreateFilterArrayConverter("or", "||"),
3081 and: _gpfCreateFilterArrayConverter("and", "&&"),
3082 undefined: function (specification) {
3083 return JSON.stringify(specification);
3084 }
3085 }, _gpfCreateFilterTypes = Object.keys(_gpfCreateFilterConverters);
3086 function _gpfCreateFilterGetType(specification) {
3087 if ("object" === typeof specification) {
3088 return Object.keys(specification).filter(function (property) {
3089 return -1 < _gpfCreateFilterTypes.indexOf(property);
3090 })[0];
3091 }
3092 }
3093 _gpfCreateFilterConvert = function (specification) {
3094 var type = _gpfCreateFilterGetType(specification), converter = _gpfCreateFilterConverters[type];
3095 return converter(specification);
3096 };
3097 function _gpfCreateFilterBody(specification) {
3098 return "return " + _gpfCreateFilterConvert(specification);
3099 }
3100 /**
3101 * Create a filtering function based on the given specification.
3102 *
3103 * @param {gpf.typedef.filterItem} specification Filter specification
3104 * @return {Function} Function that takes an object and return a truthy / falsy value indicating if the object
3105 * matches the filter
3106 * @since 0.1.9
3107 */
3108 gpf.createFilterFunction = function (specification) {
3109 return _gpfFunc(["i"], _gpfCreateFilterBody(specification));
3110 };
3111 var _gpfIReadableStream = _gpfDefineInterface("ReadableStream", { "read": 1 });
3112 var _gpfIWritableStream = _gpfDefineInterface("WritableStream", { "write": 1 });
3113 _gpfDefineInterface("Enumerator", {
3114 "reset": 0,
3115 "moveNext": 0,
3116 "getCurrent": 0
3117 });
3118 _gpfDefineInterface("FileStorage", {
3119 "getInfo": 1,
3120 "openTextStream": 2,
3121 "close": 1,
3122 "explore": 1,
3123 "createDirectory": 1,
3124 "deleteFile": 1,
3125 "deleteDirectory": 1
3126 });
3127 _gpfErrorDeclare("stream", {
3128 /**
3129 * ### Summary
3130 *
3131 * A read operation is already in progress
3132 *
3133 * ### Description
3134 *
3135 * This error is triggered if two reads are made simultaneously on the stream
3136 * @since 0.1.9
3137 */
3138 readInProgress: "A read operation is already in progress",
3139 /**
3140 * ### Summary
3141 *
3142 * A write operation is already in progress
3143 *
3144 * ### Description
3145 *
3146 * This error is triggered if two writes are made simultaneously on the stream
3147 * @since 0.1.9
3148 */
3149 writeInProgress: "A write operation is already in progress",
3150 /**
3151 * ### Summary
3152 *
3153 * Stream is in an invalid state
3154 *
3155 * ### Description
3156 *
3157 * If an error occurred while using the stream, no additional operations can be made
3158 * @since 0.1.9
3159 */
3160 invalidStreamState: "Stream is in an invalid state"
3161 });
3162 /**
3163 * @namespace gpf.stream
3164 * @description Root namespace for GPF streams
3165 * @since 0.1.9
3166 */
3167 gpf.stream = {};
3168 /**
3169 * Get an IReadableStream or fail if not implemented
3170 *
3171 * @param {Object} queriedObject Object to query
3172 * @return {gpf.interfaces.IReadableStream} IReadableStream interface
3173 * @throws {gpf.Error.InterfaceExpected}
3174 * @since 0.1.9
3175 */
3176 function _gpfStreamQueryReadable(queriedObject) {
3177 var iReadableStream = _gpfInterfaceQuery(_gpfIReadableStream, queriedObject);
3178 if (!iReadableStream) {
3179 gpf.Error.interfaceExpected({ name: "gpf.interfaces.IReadableStream" });
3180 }
3181 return iReadableStream;
3182 }
3183 /**
3184 * Get an IWritableStream or fail if not implemented
3185 *
3186 * @param {Object} queriedObject Object to query
3187 * @return {gpf.interfaces.IWritableStream} IWritableStream interface
3188 * @throws {gpf.Error.InterfaceExpected}
3189 * @since 0.1.9
3190 */
3191 function _gpfStreamQueryWritable(queriedObject) {
3192 var iWritableStream = _gpfInterfaceQuery(_gpfIWritableStream, queriedObject);
3193 if (!iWritableStream) {
3194 gpf.Error.interfaceExpected({ name: "gpf.interfaces.IWritableStream" });
3195 }
3196 return iWritableStream;
3197 }
3198 var _gpfStreamInProgressPropertyName = "gpf.stream#inProgress";
3199 /**
3200 * Install the progress flag used by _gpfStreamSecureRead and Write
3201 *
3202 * @param {Function} constructor Class constructor
3203 * @since 0.1.9
3204 */
3205 function _gpfStreamSecureInstallProgressFlag(constructor) {
3206 constructor.prototype[_gpfStreamInProgressPropertyName] = false;
3207 }
3208 /**
3209 * Generate a wrapper to query IWritableStream from the parameter and secure multiple calls to stream#read
3210 *
3211 * @param {Function} read Read function
3212 * @return {Function} Function exposing {@see gpf.interfaces.IReadableStream#read}
3213 * @gpf:closure
3214 * @since 0.1.9
3215 */
3216 function _gpfStreamSecureRead(read) {
3217 return function (output) {
3218 var me = this;
3219 //eslint-disable-line no-invalid-this
3220 if (me[_gpfStreamInProgressPropertyName]) {
3221 gpf.Error.readInProgress();
3222 }
3223 var iWritableStream = _gpfStreamQueryWritable(output);
3224 me[_gpfStreamInProgressPropertyName] = true;
3225 return read.call(me, iWritableStream).then(function (result) {
3226 me[_gpfStreamInProgressPropertyName] = false;
3227 return Promise.resolve(result);
3228 }, function (reason) {
3229 me[_gpfStreamInProgressPropertyName] = false;
3230 return Promise.reject(reason);
3231 });
3232 };
3233 }
3234 /**
3235 * Generate a wrapper to secure multiple calls to stream#write
3236 *
3237 * @param {Function} write Write function
3238 * @return {Function} Function exposing {@see gpf.interfaces.IWritableStream#write}
3239 * @gpf:closure
3240 * @since 0.1.9
3241 */
3242 function _gpfStreamSecureWrite(write) {
3243 return function (buffer) {
3244 var me = this;
3245 //eslint-disable-line no-invalid-this
3246 if (me[_gpfStreamInProgressPropertyName]) {
3247 gpf.Error.writeInProgress();
3248 }
3249 me[_gpfStreamInProgressPropertyName] = true;
3250 return write.call(me, buffer) //eslint-disable-line no-invalid-this
3251.then(function (result) {
3252 me[_gpfStreamInProgressPropertyName] = false;
3253 return Promise.resolve(result);
3254 }, function (reason) {
3255 me[_gpfStreamInProgressPropertyName] = false;
3256 return Promise.reject(reason);
3257 });
3258 };
3259 }
3260 var _GpfStreamReadableString = _gpfDefine(/** @lends gpf.stream.ReadableString.prototype */
3261 {
3262 $class: "gpf.stream.ReadableString",
3263 /**
3264 * Wraps a string inside a readable stream
3265 *
3266 * @constructor gpf.stream.ReadableString
3267 * @implements {gpf.interfaces.IReadableStream}
3268 * @param {String} buffer String buffer
3269 * @since 0.1.9
3270 */
3271 constructor: function (buffer) {
3272 this._buffer = buffer;
3273 },
3274 //region gpf.interfaces.IReadableStream
3275 /**
3276 * @gpf:sameas gpf.interfaces.IReadableStream#read
3277 * @since 0.1.9
3278 */
3279 read: _gpfStreamSecureRead(function (output) {
3280 return output.write(this._buffer); //eslint-disable-line no-invalid-this
3281 }),
3282 //endregion
3283 /**
3284 * Buffer
3285 * @since 0.1.9
3286 */
3287 _buffer: ""
3288 }),
3289 /**
3290 * @since 0.1.9
3291 */
3292 _GpfStreamWritableString = _gpfDefine(/** @lends gpf.stream.WritableString.prototype */
3293 {
3294 $class: "gpf.stream.WritableString",
3295 /**
3296 * Creates a writable stream that can be converted to string
3297 *
3298 * @constructor gpf.stream.WritableString
3299 * @implements {gpf.interfaces.IWritableStream}
3300 * @since 0.1.9
3301 */
3302 constructor: function () {
3303 this._buffer = [];
3304 },
3305 //region gpf.interfaces.IReadableStream
3306 /**
3307 * @gpf:sameas gpf.interfaces.IWritableStream#write
3308 * @since 0.1.9
3309 */
3310 write: _gpfStreamSecureWrite(function (buffer) {
3311 this._buffer.push(buffer.toString());
3312 //eslint-disable-line no-invalid-this
3313 return Promise.resolve();
3314 }),
3315 //endregion
3316 toString: function () {
3317 return this._buffer.join("");
3318 },
3319 /**
3320 * Buffer
3321 *
3322 * @type {String[]}
3323 * @since 0.1.9
3324 */
3325 _buffer: []
3326 });
3327 _gpfStreamSecureInstallProgressFlag(_GpfStreamReadableString);
3328 _gpfStreamSecureInstallProgressFlag(_GpfStreamWritableString);
3329 _gpfErrorDeclare("fs", {
3330 /**
3331 * ### Summary
3332 *
3333 * Incompatible stream
3334 *
3335 * ### Description
3336 *
3337 * This error is used when a file storage tries to close a stream that was not allocaetd by itself.
3338 * @since 0.1.9
3339 */
3340 incompatibleStream: "Incompatible stream"
3341 });
3342 var
3343 /**
3344 * File system type constants
3345 * @since 0.1.9
3346 */
3347 _GPF_FS_TYPES = {
3348 NOT_FOUND: 0,
3349 DIRECTORY: 1,
3350 FILE: 2,
3351 UNKNOWN: 3
3352 },
3353 /**
3354 * File system stream opening mode
3355 * @since 0.1.9
3356 */
3357 _GPF_FS_OPENFOR = {
3358 READING: 0,
3359 APPENDING: 1
3360 },
3361 /**
3362 * {@see gpf.interfaces.IFileStorage} per host
3363 *
3364 * @type {Object}
3365 * @since 0.2.1
3366 */
3367 _gpfFileStorageByHost = {},
3368 /**
3369 * {@see gpf.fs.read} per host
3370 *
3371 * @type {Object}
3372 * @since 0.2.2
3373 */
3374 _gpfFsReadImplByHost = {};
3375 /**
3376 * Generic read method using FileStorage
3377 *
3378 * @param {String} path File path
3379 * @return {Promise<String>} Resolved with the file content
3380 * @since 0.2.2
3381 */
3382 function _gpfFileStorageRead(path) {
3383 var fs = _gpfFileStorageByHost[_gpfHost], iWritableStream = new _GpfStreamWritableString();
3384 return fs.openTextStream(path, _GPF_FS_OPENFOR.READING).then(function (iReadStream) {
3385 return iReadStream.read(iWritableStream);
3386 }).then(function () {
3387 return iWritableStream.toString();
3388 });
3389 }
3390 /**
3391 * Generic read method
3392 *
3393 * @param {String} path File path
3394 * @return {Promise<String>} Resolved with the file content
3395 * @since 0.2.2
3396 */
3397 function _gpfFsRead(path) {
3398 return _gpfFsReadImplByHost[_gpfHost](path);
3399 }
3400 /**
3401 * @namespace gpf.fs
3402 * @description Root namespace for filesystem specifics
3403 * @since 0.1.9
3404 */
3405 gpf.fs = {
3406 /**
3407 * File system object type enumeration
3408 *
3409 * @enum {Number}
3410 * @readonly
3411 * @since 0.1.9
3412 */
3413 types: {
3414 /**
3415 * Storage path does not exist
3416 * @since 0.1.9
3417 */
3418 notFound: _GPF_FS_TYPES.NOT_FOUND,
3419 /**
3420 * Storage path points to a container of files
3421 * @since 0.1.9
3422 */
3423 directory: _GPF_FS_TYPES.DIRECTORY,
3424 /**
3425 * Storage path points to a stream-able file
3426 * @since 0.1.9
3427 */
3428 file: _GPF_FS_TYPES.FILE,
3429 /**
3430 * Storage path points to an object but it can't be handled
3431 * @since 0.1.9
3432 */
3433 unknown: _GPF_FS_TYPES.UNKNOWN
3434 },
3435 /**
3436 * File system open mode enumeration
3437 *
3438 * @enum {Number}
3439 * @readonly
3440 * @since 0.1.9
3441 */
3442 openFor: {
3443 /**
3444 * Read as a IReadableStream from the beginning of the file
3445 * @since 0.1.9
3446 */
3447 reading: _GPF_FS_OPENFOR.READING,
3448 /**
3449 * Append as a IWritableStream to the end of the file.
3450 * NOTE: if you want to overwrite a file, delete it first
3451 * @since 0.1.9
3452 */
3453 appending: _GPF_FS_OPENFOR.APPENDING
3454 },
3455 /**
3456 * Get the current host file storage (null if none)
3457 *
3458 * @return {gpf.interfaces.IFileStorage|null} IFileStorage interface
3459 * @since 0.1.9
3460 */
3461 getFileStorage: function () {
3462 return _gpfFileStorageByHost[_gpfHost] || null;
3463 },
3464 /**
3465 * @gpf:sameas _gpfFsRead
3466 * @since 0.2.2
3467 */
3468 read: _gpfFsRead
3469 };
3470 _gpfErrorDeclare("path", {
3471 /**
3472 * ### Summary
3473 *
3474 * Unreachable path
3475 *
3476 * ### Description
3477 *
3478 * This error is triggered when trying to get the parent of a root path using gpf.path.parent or
3479 * gpf.path.join with ..
3480 * @see gpf.path.join
3481 * @see gpf.path.parent
3482 * @since 0.1.9
3483 */
3484 unreachablePath: "Unreachable path"
3485 });
3486 //region _gpfPathDecompose
3487 function _gpfPathSplit(path) {
3488 if (-1 < path.indexOf("\\")) {
3489 // DOS path is case insensitive, hence lowercase it
3490 return path.toLowerCase().split("\\");
3491 }
3492 // Assuming a Unix-like path
3493 return path.split("/");
3494 }
3495 function _gpfPathRemoveTrailingBlank(splitPath) {
3496 if (!splitPath[splitPath.length - 1]) {
3497 splitPath.pop();
3498 }
3499 }
3500 function _gpfPathUp(splitPath) {
3501 if (splitPath.length) {
3502 splitPath.pop();
3503 } else {
3504 gpf.Error.unreachablePath();
3505 }
3506 }
3507 /**
3508 * Normalize paths and returns an array of parts.
3509 * If a DOS-like path is detected (use of \), it is lower-cased
3510 *
3511 * @param {String} path Path to normalize
3512 * @return {String[]} Normalized path represented as an array of parts
3513 * @since 0.1.9
3514 */
3515 function _gpfPathDecompose(path) {
3516 var splitPath = _gpfPathSplit(path);
3517 _gpfPathRemoveTrailingBlank(splitPath);
3518 return splitPath;
3519 }
3520 //endregion
3521 /**
3522 * Normalize path
3523 *
3524 * @param {String} path Path to normalize
3525 * @return {String} Normalized path
3526 * @since 0.1.9
3527 */
3528 function _gpfPathNormalize(path) {
3529 return _gpfPathDecompose(path).join("/");
3530 }
3531 /**
3532 * Get the last name of a path
3533 *
3534 * @param {String} path Path to analyze
3535 * @return {String} Name
3536 * @since 0.1.9
3537 */
3538 function _gpfPathName(path) {
3539 if (path) {
3540 return _gpfPathDecompose(path).pop();
3541 }
3542 return "";
3543 }
3544 /**
3545 * Get the extension of the last name of a path (including dot)
3546 *
3547 * @param {String} path Path to analyze
3548 * @return {String} Extension (including dot)
3549 * @since 0.1.9
3550 */
3551 function _gpfPathExtension(path) {
3552 var name = _gpfPathName(path), pos = name.lastIndexOf(".");
3553 if (-1 === pos) {
3554 return "";
3555 }
3556 return name.substr(pos);
3557 }
3558 function _gpfPathAppend(splitPath, relativePath) {
3559 _gpfPathDecompose(relativePath).forEach(function (relativeItem) {
3560 if (".." === relativeItem) {
3561 _gpfPathUp(splitPath);
3562 } else {
3563 splitPath.push(relativeItem);
3564 }
3565 });
3566 }
3567 /**
3568 * Join all arguments together and normalize the resulting path
3569 *
3570 * @param {String} path Base path
3571 * @param {...String} relativePath Relative parts to append to the base path
3572 * @return {String} Joined path
3573 * @throws {gpf.Error.UnreachablePath}
3574 * @since 0.1.9
3575 */
3576 function _gpfPathJoin(path) {
3577 var splitPath = _gpfPathDecompose(path);
3578 [].slice.call(arguments, 1).forEach(_gpfPathAppend.bind(null, splitPath));
3579 return splitPath.join("/");
3580 }
3581 function _gpfPathSafeShiftIdenticalBeginning(splitFromPath, splitToPath) {
3582 while (splitFromPath[0] === splitToPath[0]) {
3583 splitFromPath.shift();
3584 splitToPath.shift();
3585 }
3586 }
3587 function _gpfPathShiftIdenticalBeginning(splitFromPath, splitToPath) {
3588 if (splitFromPath.length * splitToPath.length) {
3589 // equivalent to splitFromPath.length && splitToPath.length
3590 _gpfPathSafeShiftIdenticalBeginning(splitFromPath, splitToPath);
3591 }
3592 }
3593 /**
3594 * Get the parent of a path
3595 *
3596 * @param {String} path Path to analyze
3597 * @return {String} Parent path
3598 * @throws {gpf.Error.UnreachablePath}
3599 * @since 0.1.9
3600 */
3601 function _gpfPathParent(path) {
3602 path = _gpfPathDecompose(path);
3603 _gpfPathUp(path);
3604 return path.join("/");
3605 }
3606 /**
3607 * Solve the relative path from from to to
3608 *
3609 * @param {String} from From path
3610 * @param {String} to To path
3611 * @return {String} Relative path
3612 * @since 0.1.9
3613 */
3614 function _gpfPathRelative(from, to) {
3615 var length, splitFrom = _gpfPathDecompose(from), splitTo = _gpfPathDecompose(to);
3616 _gpfPathShiftIdenticalBeginning(splitFrom, splitTo);
3617 // For each remaining part in from, unshift .. in to
3618 length = splitFrom.length + 1;
3619 while (--length) {
3620 splitTo.unshift("..");
3621 }
3622 return splitTo.join("/");
3623 }
3624 /**
3625 * @namespace gpf.path
3626 * @description Root namespace for path manipulation.
3627 *
3628 * As the library works with several hosts (Windows and Unix-like, see {@tutorial LOADING}),
3629 * the API accepts any kind of [path separator](https://en.wikipedia.org/wiki/Path_%28computing%29).
3630 * However, they can't be mixed.
3631 *
3632 * When giving a path, the rule is:
3633 * - If the path contains at least one \, it is considered a Windows one
3634 * - Otherwise, the path is considered a Unix one
3635 *
3636 * On the other hand, all path returned by the API are using the Unix-like formalism.
3637 *
3638 * @since 0.1.9
3639 */
3640 gpf.path = {
3641 /**
3642 * @gpf:sameas _gpfPathJoin
3643 * @since 0.1.9
3644 */
3645 join: _gpfPathJoin,
3646 /**
3647 * @gpf:sameas _gpfPathParent
3648 * @since 0.1.9
3649 */
3650 parent: _gpfPathParent,
3651 /**
3652 * @gpf:sameas _gpfPathName
3653 * @since 0.1.9
3654 */
3655 name: _gpfPathName,
3656 /**
3657 * Get the last name of a path without the extension
3658 *
3659 * @param {String} path Path to analyze
3660 * @return {String} Name without the extension
3661 * @since 0.1.9
3662 */
3663 nameOnly: function (path) {
3664 var name = _gpfPathName(path), pos = name.lastIndexOf(".");
3665 if (-1 === pos) {
3666 return name;
3667 }
3668 return name.substr(0, pos);
3669 },
3670 /**
3671 * @gpf:sameas _gpfPathExtension
3672 * @since 0.1.9
3673 */
3674 extension: _gpfPathExtension,
3675 /**
3676 * @gpf:sameas _gpfPathRelative
3677 * @since 0.1.9
3678 */
3679 relative: _gpfPathRelative
3680 };
3681 function _gpfFsExploreEnumerator(iFileStorage, listOfPaths) {
3682 var pos = -1, info;
3683 return {
3684 reset: function () {
3685 pos = -1;
3686 return Promise.resolve();
3687 },
3688 moveNext: function () {
3689 ++pos;
3690 info = undefined;
3691 if (pos < listOfPaths.length) {
3692 return iFileStorage.getInfo(listOfPaths[pos]).then(function (fsInfo) {
3693 info = fsInfo;
3694 return info;
3695 });
3696 }
3697 return Promise.resolve();
3698 },
3699 getCurrent: function () {
3700 return info;
3701 }
3702 };
3703 }
3704 var _GpfNodeBaseStream = _gpfDefine(/** @lends gpf.node.BaseStream.prototype */
3705 {
3706 $class: "gpf.node.BaseStream",
3707 /**
3708 * Base class wrapping NodeJS streams
3709 *
3710 * @param {Object} stream NodeJS stream object
3711 * @param {Function} [close] Close handler
3712 *
3713 * @constructor gpf.node.BaseStream
3714 * @private
3715 * @since 0.1.9
3716 */
3717 constructor: function (stream, close) {
3718 this._stream = stream;
3719 if ("function" === typeof close) {
3720 this._close = close;
3721 }
3722 stream.on("error", this._onError.bind(this));
3723 },
3724 /**
3725 * Function to be called when the stream is closed
3726 * @type {Function}
3727 * @since 0.1.9
3728 */
3729 _close: _gpfEmptyFunc,
3730 /**
3731 * Close the stream
3732 *
3733 * @return {Promise} Resolved when closed
3734 * @since 0.1.9
3735 */
3736 close: function () {
3737 return this._close();
3738 },
3739 //region Error handling
3740 /**
3741 * NodeJS stream object
3742 * @since 0.1.9
3743 */
3744 _stream: null,
3745 /**
3746 * The stream has an invalid state and can't be used anymore
3747 * @since 0.1.9
3748 */
3749 _invalid: false,
3750 /**
3751 * Current promise rejection callback
3752 * @type {Function}
3753 * @since 0.1.9
3754 */
3755 _reject: gpf.Error.invalidStreamState,
3756 /**
3757 * If the stream has an invalid state, the exception {@see gpf.Error.InvalidStreamState} is thrown
3758 *
3759 * @throws {gpf.Error.InvalidStreamState}
3760 * @since 0.1.9
3761 */
3762 _checkIfValid: function () {
3763 if (this._invalid) {
3764 gpf.Error.invalidStreamState();
3765 }
3766 },
3767 /**
3768 * Bound to the error event of the stream, reject the current promise if it occurs.
3769 *
3770 * @param {*} error Stream error
3771 * @since 0.1.9
3772 */
3773 _onError: function (error) {
3774 this._invalid = true;
3775 this._reject(error);
3776 } //endregion
3777 }),
3778 /**
3779 * Wraps a readable stream from NodeJS into a IReadableStream
3780 *
3781 * @param {Object} stream NodeJS stream object
3782 * @param {Function} [close] Close handler
3783 *
3784 * @class gpf.node.ReadableStream
3785 * @extends gpf.node.BaseStream
3786 * @implements {gpf.interfaces.IReadableStream}
3787 * @since 0.1.9
3788 */
3789 _GpfNodeReadableStream = _gpfDefine(/** @lends gpf.node.ReadableStream.prototype */
3790 {
3791 $class: "gpf.node.ReadableStream",
3792 $extend: "gpf.node.BaseStream",
3793 //region gpf.interfaces.IReadableStream
3794 /**
3795 * @gpf:sameas gpf.interfaces.IReadableStream#read
3796 * @since 0.1.9
3797 */
3798 read: _gpfStreamSecureRead(function (output) {
3799 var me = this,
3800 //eslint-disable-line no-invalid-this
3801 stream = me._stream;
3802 return new Promise(function (resolve, reject) {
3803 me._reject = reject;
3804 me._checkIfValid();
3805 stream.on("data", me._onData.bind(me, output)).on("end", function () {
3806 me._invalid = true;
3807 resolve();
3808 });
3809 });
3810 }),
3811 //endregion
3812 /**
3813 * Stream 'data' event handler
3814 *
3815 * @param {gpf.interfaces.IWritableStream} output Output stream
3816 * @param {Object} chunk Buffer
3817 * @since 0.1.9
3818 */
3819 _onData: function (output, chunk) {
3820 var me = this, stream = me._stream;
3821 stream.pause();
3822 output.write(chunk).then(function () {
3823 stream.resume();
3824 }, me._reject);
3825 }
3826 }),
3827 /**
3828 * Wraps a writable stream from NodeJS into a IWritableStream
3829 *
3830 * @param {Object} stream NodeJS stream object
3831 * @param {Function} [close] Close handler
3832 *
3833 * @class gpf.node.WritableStream
3834 * @extends gpf.node.BaseStream
3835 * @implements {gpf.interfaces.IWritableStream}
3836 * @since 0.1.9
3837 */
3838 _GpfNodeWritableStream = _gpfDefine(/** @lends gpf.node.WritableStream.prototype */
3839 {
3840 $class: "gpf.node.WritableStream",
3841 $extend: "gpf.node.BaseStream",
3842 //region gpf.interfaces.IWritableStream
3843 /**
3844 * @gpf:sameas gpf.interfaces.IWritableStream#write
3845 * @since 0.1.9
3846 */
3847 write: _gpfStreamSecureWrite(function (buffer) {
3848 var me = this,
3849 //eslint-disable-line no-invalid-this
3850 stream = me._stream;
3851 return new Promise(function (resolve, reject) {
3852 me._reject = reject;
3853 me._checkIfValid();
3854 if (stream.write(buffer)) {
3855 return resolve();
3856 }
3857 stream.once("drain", resolve);
3858 });
3859 }) //endregion
3860 });
3861 function _gpfFsNodeFsCall(methodName, args) {
3862 return new Promise(function (resolve, reject) {
3863 _gpfNodeFs[methodName].apply(_gpfNodeFs, args.concat([function (err, result) {
3864 if (err) {
3865 reject(err);
3866 } else {
3867 resolve(result);
3868 }
3869 }]));
3870 });
3871 }
3872 /**
3873 * Encapsulate fs API taking a path parameter inside a Promise
3874 *
3875 * @param {String} methodName fs method name
3876 * @param {String} path file path
3877 * @return {Promise<*>} Resolved with API result
3878 * @gpf:closure
3879 * @since 0.1.9
3880 */
3881 function _gpfFsNodeFsCallWithPath(methodName, path) {
3882 return _gpfFsNodeFsCall(methodName, [_gpfPathNormalize(path)]);
3883 }
3884 function _gpfFsNodeOpenTextStream(path, options) {
3885 return _gpfFsNodeFsCall("open", [
3886 _gpfPathNormalize(path),
3887 options.flags
3888 ]).then(function (fd) {
3889 return new options.GpfStream(_gpfNodeFs[options.nodeStream]("", {
3890 fd: fd,
3891 autoClose: false
3892 }), _gpfFsNodeFsCall.bind(null, "close", [fd]));
3893 });
3894 }
3895 /**
3896 * Open a text stream for reading
3897 *
3898 * @param {String} path File path
3899 * @return {Promise<gpf.interfaces.IReadableStream>} gpf.node.ReadableStream
3900 * @since 0.1.9
3901 */
3902 function _gpfFsNodeOpenTextStreamForReading(path) {
3903 return _gpfFsNodeOpenTextStream(path, {
3904 flags: "r",
3905 GpfStream: _GpfNodeReadableStream,
3906 nodeStream: "createReadStream"
3907 });
3908 }
3909 /**
3910 * Open a text stream for appending
3911 *
3912 * @param {String} path File path
3913 * @return {Promise<gpf.interfaces.IWritableStream>} gpf.node.WritableStream
3914 * @since 0.1.9
3915 */
3916 function _gpfFsNodeOpenTextStreamForAppending(path) {
3917 return _gpfFsNodeOpenTextStream(path, {
3918 flags: "a",
3919 GpfStream: _GpfNodeWritableStream,
3920 nodeStream: "createWriteStream"
3921 });
3922 }
3923 function _gpfFsNodeGetFileType(stats) {
3924 if (stats.isFile()) {
3925 return _GPF_FS_TYPES.FILE;
3926 }
3927 return _GPF_FS_TYPES.UNKNOWN;
3928 }
3929 function _gpfFsNodeGetType(stats) {
3930 if (stats.isDirectory()) {
3931 return _GPF_FS_TYPES.DIRECTORY;
3932 }
3933 return _gpfFsNodeGetFileType(stats);
3934 }
3935 /**
3936 * NodeJS specific IFileStorage implementation
3937 *
3938 * @class gpf.node.FileStorage
3939 * @implements {gpf.interfaces.IFileStorage}
3940 * @private
3941 * @since 0.1.9
3942 */
3943 var _GpfNodeFileStorage = _gpfDefine(/** @lends gpf.node.FileStorage.prototype */
3944 {
3945 $class: "gpf.node.FileStorage",
3946 //region gpf.interfaces.IFileStorage
3947 /**
3948 * @gpf:sameas gpf.interfaces.IFileStorage#getInfo
3949 * @since 0.1.9
3950 */
3951 getInfo: function (path) {
3952 path = _gpfPathNormalize(path);
3953 return new Promise(function (resolve) {
3954 _gpfNodeFs.exists(path, resolve);
3955 }).then(function (exists) {
3956 if (exists) {
3957 return _gpfFsNodeFsCallWithPath("stat", path).then(function (stats) {
3958 return {
3959 fileName: _gpfNodePath.basename(path),
3960 filePath: _gpfPathNormalize(_gpfNodePath.resolve(path)),
3961 size: stats.size,
3962 createdDateTime: stats.ctime,
3963 modifiedDateTime: stats.mtime,
3964 type: _gpfFsNodeGetType(stats)
3965 };
3966 });
3967 }
3968 return { type: _GPF_FS_TYPES.NOT_FOUND };
3969 });
3970 },
3971 /**
3972 * @gpf:sameas gpf.interfaces.IFileStorage#openTextStream
3973 * @since 0.1.9
3974 */
3975 openTextStream: function (path, mode) {
3976 if (_GPF_FS_OPENFOR.READING === mode) {
3977 return _gpfFsNodeOpenTextStreamForReading(path);
3978 }
3979 return _gpfFsNodeOpenTextStreamForAppending(path);
3980 },
3981 /**
3982 * @gpf:sameas gpf.interfaces.IFileStorage#close
3983 * @since 0.1.9
3984 */
3985 close: function (stream) {
3986 if (stream instanceof _GpfNodeBaseStream) {
3987 return stream.close();
3988 }
3989 return Promise.reject(new gpf.Error.IncompatibleStream());
3990 },
3991 /**
3992 * @gpf:sameas gpf.interfaces.IFileStorage#explore
3993 * @since 0.1.9
3994 */
3995 explore: function (path) {
3996 var me = this;
3997 return _gpfFsNodeFsCallWithPath("readdir", path).then(function (content) {
3998 return _gpfFsExploreEnumerator(me, content.map(function (name) {
3999 return _gpfPathJoin(path, name);
4000 }));
4001 });
4002 },
4003 /**
4004 * @gpf:sameas gpf.interfaces.IFileStorage#createDirectory
4005 * @since 0.1.9
4006 */
4007 createDirectory: _gpfFsNodeFsCallWithPath.bind(null, "mkdir"),
4008 /**
4009 * @gpf:sameas gpf.interfaces.IFileStorage#deleteFile
4010 * @since 0.1.9
4011 */
4012 deleteFile: _gpfFsNodeFsCallWithPath.bind(null, "unlink"),
4013 /**
4014 * @gpf:sameas gpf.interfaces.IFileStorage#deleteDirectory
4015 * @since 0.1.9
4016 */
4017 deleteDirectory: _gpfFsNodeFsCallWithPath.bind(null, "rmdir") //endregion
4018 });
4019 _gpfFileStorageByHost[_GPF_HOST.NODEJS] = new _GpfNodeFileStorage();
4020 _gpfFsReadImplByHost[_GPF_HOST.NODEJS] = _gpfFileStorageRead;
4021 var _GpfWscriptBaseStream = _gpfDefine(/** @lends gpf.wscript.BaseStream.prototype */
4022 {
4023 $class: "gpf.wscript.BaseStream",
4024 /**
4025 * Base class wrapping NodeJS streams
4026 *
4027 * @constructor gpf.wscript.BaseStream
4028 * @param {Object} file File object
4029 * @private
4030 * @since 0.1.9
4031 */
4032 constructor: function (file) {
4033 this._file = file;
4034 },
4035 /**
4036 * Close the stream
4037 *
4038 * @return {Promise} Resolved when closed
4039 * @since 0.1.9
4040 */
4041 close: function () {
4042 return new Promise(function (resolve) {
4043 this._file.Close();
4044 resolve();
4045 }.bind(this));
4046 }
4047 }),
4048 /**
4049 * Wraps a file object from FileSystemObject into a IReadableStream
4050 *
4051 * @class gpf.wscript.ReadableStream
4052 * @extends gpf.wscript.BaseStream
4053 * @implements {gpf.interfaces.IReadableStream}
4054 * @private
4055 * @since 0.1.9
4056 */
4057 _GpfWscriptReadableStream = _gpfDefine(/** @lends gpf.wscript.ReadableStream.prototype */
4058 {
4059 $class: "gpf.wscript.ReadableStream",
4060 $extend: "gpf.wscript.BaseStream",
4061 //region gpf.interfaces.IReadableStream
4062 /**
4063 * @gpf:sameas gpf.interfaces.IReadableStream#read
4064 * @since 0.1.9
4065 */
4066 read: _gpfStreamSecureRead(function (output) {
4067 var me = this,
4068 //eslint-disable-line no-invalid-this
4069 file = me._file;
4070 return new Promise(function (resolve) {
4071 function read() {
4072 return output.write(file.Read(4096)) // buffer size
4073.then(function () {
4074 if (!file.AtEndOfStream) {
4075 return read();
4076 }
4077 });
4078 }
4079 return read().then(resolve);
4080 });
4081 }) //endregion
4082 }),
4083 /**
4084 * Wraps a file object from FileSystemObject into a IWritableStream
4085 *
4086 * @class gpf.wscript.WritableStream
4087 * @extends gpf.wscript.BaseStream
4088 * @implements {gpf.interfaces.IWritableStream}
4089 * @private
4090 * @since 0.1.9
4091 */
4092 _GpfWscriptWritableStream = _gpfDefine(/** @lends gpf.wscript.WritableStream.prototype */
4093 {
4094 $class: "gpf.wscript.WritableStream",
4095 $extend: "gpf.wscript.BaseStream",
4096 //region gpf.interfaces.IWritableStream
4097 /**
4098 * @gpf:sameas gpf.interfaces.IWritableStream#write
4099 * @since 0.1.9
4100 */
4101 write: _gpfStreamSecureWrite(function (buffer) {
4102 var me = this,
4103 //eslint-disable-line no-invalid-this
4104 file = me._file;
4105 return new Promise(function (resolve) {
4106 file.Write(buffer);
4107 resolve();
4108 });
4109 }) //endregion
4110 });
4111 _gpfErrorDeclare("fs/wscript", {
4112 /**
4113 * ### Summary
4114 *
4115 * Path not explorable
4116 *
4117 * ### Description
4118 *
4119 * This error is used when explore is used with a path that does not point to a folder.
4120 * @since 0.2.1
4121 */
4122 pathNotExplorable: "Path not explorable"
4123 });
4124 /**
4125 * Translate WScript file object into a {@link gpf.typedef.fileStorageInfo}
4126 *
4127 * @param {Object} obj WScript file object
4128 * @param {gpf.fs.types} type Object type
4129 * @return {gpf.typedef.fileStorageInfo} Information about the object
4130 * @since 0.1.9
4131 */
4132 function _gpfFsWScriptObjToFileStorageInfo(obj, type) {
4133 return {
4134 type: type,
4135 fileName: obj.Name.toLowerCase(),
4136 filePath: _gpfPathNormalize(obj.Path),
4137 size: obj.Size,
4138 createdDateTime: new Date(obj.DateCreated),
4139 modifiedDateTime: new Date(obj.DateLastModified)
4140 };
4141 }
4142 function _gpfFsWscriptFSOCallWithArg(name, path) {
4143 return new Promise(function (resolve) {
4144 _gpfMsFSO[name](_gpfPathDecompose(path).join("\\"));
4145 resolve();
4146 });
4147 }
4148 function _gpfFsWscriptFSOCallWithArgAndTrue(name, path) {
4149 return new Promise(function (resolve) {
4150 _gpfMsFSO[name](_gpfPathDecompose(path).join("\\"), true);
4151 resolve();
4152 });
4153 }
4154 function _gpfFsWscriptGetFileInfo(path) {
4155 if (_gpfMsFSO.FileExists(path)) {
4156 return _gpfFsWScriptObjToFileStorageInfo(_gpfMsFSO.GetFile(path), _GPF_FS_TYPES.FILE);
4157 }
4158 return { type: _GPF_FS_TYPES.NOT_FOUND };
4159 }
4160 function _gpfFsWscriptGetInfo(path) {
4161 if (_gpfMsFSO.FolderExists(path)) {
4162 return _gpfFsWScriptObjToFileStorageInfo(_gpfMsFSO.GetFolder(path), _GPF_FS_TYPES.DIRECTORY);
4163 }
4164 return _gpfFsWscriptGetFileInfo(path);
4165 }
4166 function _gpfFsWScriptExploreList(collection) {
4167 var fsoEnum = new Enumerator(collection), results = [];
4168 for (; !fsoEnum.atEnd(); fsoEnum.moveNext()) {
4169 results.push(fsoEnum.item().Path);
4170 }
4171 return results;
4172 }
4173 function _gpfFsWScriptExplore(path) {
4174 var folder;
4175 if (_gpfMsFSO.FolderExists(path)) {
4176 folder = _gpfMsFSO.GetFolder(path);
4177 return _gpfFsWScriptExploreList(folder.SubFolders).concat(_gpfFsWScriptExploreList(folder.Files));
4178 }
4179 gpf.Error.pathNotExplorable();
4180 }
4181 /**
4182 * WScript specific IFileStorage implementation
4183 *
4184 * @class gpf.wscript.FileStorage
4185 * @implements {gpf.interfaces.IFileStorage}
4186 * @private
4187 * @since 0.1.9
4188 */
4189 var _GpfWScriptFileStorage = _gpfDefine(/** @lends gpf.wscript.FileStorage.prototype */
4190 {
4191 $class: "gpf.wscript.FileStorage",
4192 //region gpf.interfaces.IFileStorage
4193 /**
4194 * @gpf:sameas gpf.interfaces.IFileStorage#getInfo
4195 * @since 0.1.9
4196 */
4197 getInfo: function (path) {
4198 return Promise.resolve(_gpfFsWscriptGetInfo(_gpfPathDecompose(path).join("\\")));
4199 },
4200 /**
4201 * @gpf:sameas gpf.interfaces.IFileStorage#openTextStream
4202 * @since 0.1.9
4203 */
4204 openTextStream: function (path, mode) {
4205 path = _gpfPathDecompose(path).join("\\");
4206 return new Promise(function (resolve) {
4207 var stream;
4208 if (_GPF_FS_OPENFOR.READING === mode) {
4209 stream = new _GpfWscriptReadableStream(_gpfMsFSO.OpenTextFile(path, 1, false));
4210 } else {
4211 stream = new _GpfWscriptWritableStream(_gpfMsFSO.OpenTextFile(path, 8, true));
4212 }
4213 resolve(stream);
4214 });
4215 },
4216 /**
4217 * @gpf:sameas gpf.interfaces.IFileStorage#close
4218 * @since 0.1.9
4219 */
4220 close: function (stream) {
4221 if (stream instanceof _GpfWscriptBaseStream) {
4222 return stream.close();
4223 }
4224 return Promise.reject(new gpf.Error.IncompatibleStream());
4225 },
4226 /**
4227 * @gpf:sameas gpf.interfaces.IFileStorage#explore
4228 * @since 0.1.9
4229 */
4230 explore: function (path) {
4231 var me = this;
4232 return new Promise(function (resolve) {
4233 resolve(_gpfFsExploreEnumerator(me, _gpfFsWScriptExplore(_gpfPathDecompose(path).join("\\"))));
4234 });
4235 },
4236 /**
4237 * @gpf:sameas gpf.interfaces.IFileStorage#createDirectory
4238 * @since 0.1.9
4239 */
4240 createDirectory: _gpfFsWscriptFSOCallWithArg.bind(null, "CreateFolder"),
4241 /**
4242 * @gpf:sameas gpf.interfaces.IFileStorage#deleteFile
4243 * @since 0.1.9
4244 */
4245 deleteFile: _gpfFsWscriptFSOCallWithArgAndTrue.bind(null, "DeleteFile"),
4246 /**
4247 * @gpf:sameas gpf.interfaces.IFileStorage#deleteDirectory
4248 * @since 0.1.9
4249 */
4250 deleteDirectory: _gpfFsWscriptFSOCallWithArgAndTrue.bind(null, "DeleteFolder") //endregion
4251 });
4252 _gpfFileStorageByHost[_GPF_HOST.WSCRIPT] = new _GpfWScriptFileStorage();
4253 _gpfFsReadImplByHost[_GPF_HOST.WSCRIPT] = _gpfFileStorageRead;
4254 _gpfFsReadImplByHost[_GPF_HOST.BROWSER] = gpf.Error.notImplemented;
4255 _gpfFsReadImplByHost[_GPF_HOST.RHINO] = function (path) {
4256 return new Promise(function (resolve) {
4257 resolve(readFile(path));
4258 });
4259 };
4260 _gpfFsReadImplByHost[_GPF_HOST.PHANTOMJS] = function (path) {
4261 return new Promise(function (resolve, reject) {
4262 try {
4263 resolve(_gpfNodeFs.read(path));
4264 } catch (e) {
4265 // Error is a string
4266 reject(new Error(e));
4267 }
4268 });
4269 };
4270 var _gpfObjectToString = Object.prototype.toString;
4271 /**
4272 * Check if the parameter is a literal object
4273 *
4274 * @param {*} value Value to check
4275 * @return {Boolean} True if the value is a literal object
4276 * @since 0.2.1
4277 */
4278 function _gpfIsLiteralObject(value) {
4279 return value instanceof Object && _gpfObjectToString.call(value) === "[object Object]" && Object.getPrototypeOf(value) === Object.getPrototypeOf({});
4280 }
4281 /**
4282 * @gpf:sameas _gpfIsLiteralObject
4283 * @since 0.2.1
4284 */
4285 gpf.isLiteralObject = _gpfIsLiteralObject;
4286 _gpfErrorDeclare("web/tag", {
4287 /**
4288 * ### Summary
4289 *
4290 * Missing node name
4291 *
4292 * ### Description
4293 *
4294 * A tag can't be created if the node name is missing
4295 * @since 0.2.1
4296 */
4297 missingNodeName: "Missing node name",
4298 /**
4299 * ### Summary
4300 *
4301 * Unknown namespace prefix
4302 *
4303 * ### Description
4304 *
4305 * A prefix has been used prior to be associated with a namespace
4306 * @since 0.2.2
4307 */
4308 unknownNamespacePrefix: "Unknown namespace prefix",
4309 /**
4310 * ### Summary
4311 *
4312 * Unable to use namespace in string
4313 *
4314 * ### Description
4315 *
4316 * A prefix associated to a namespace has been used and can't be converted to string
4317 * @since 0.2.2
4318 */
4319 unableToUseNamespaceInString: "Unable to use namespace in string"
4320 });
4321 /**
4322 * Mapping of attribute name aliases
4323 * @type {Object}
4324 * @since 0.2.1
4325 */
4326 var _gpfWebTagAttributeAliases = { "className": "class" };
4327 /**
4328 * Mapping of prefixes for namespaces
4329 * @type {Object}
4330 * @since 0.2.2
4331 */
4332 var _gpfWebNamespacePrefix = {
4333 "svg": "http://www.w3.org/2000/svg",
4334 "xlink": "http://www.w3.org/1999/xlink"
4335 };
4336 /**
4337 * Retrieves namespace associated to the prefix or fail
4338 *
4339 * @param {String} prefix Namespace prefix
4340 * @return {String} Namespace URI
4341 * @throws {gpf.Error.UnknownNamespacePrefix}
4342 * @since 0.2.2
4343 */
4344 function _gpfWebGetNamespace(prefix) {
4345 var namespace = _gpfWebNamespacePrefix[prefix];
4346 if (undefined === namespace) {
4347 gpf.Error.unknownNamespacePrefix();
4348 }
4349 return namespace;
4350 }
4351 /**
4352 * Resolves prefixed name to namespace and name
4353 *
4354 * @param {String} name Attribute or node name
4355 * @return {{namespace, name}|undefined} Namespace and name in a structure if prefixed, undefined otherwise
4356 * @since 0.2.2
4357 */
4358 function _gpfWebGetNamespaceAndName(name) {
4359 var parts = name.split(":");
4360 if (parts.length === 2) {
4361 return {
4362 namespace: _gpfWebGetNamespace(parts[0]),
4363 name: parts[1]
4364 };
4365 }
4366 }
4367 /**
4368 * Fails if the name includes namespace prefix
4369 *
4370 * @param {String} name Attribute or node name
4371 * @throws {gpf.Error.UnableToUseNamespaceInString}
4372 * @since 0.2.2
4373 */
4374 function _gpfWebCheckNamespaceSafe(name) {
4375 if (-1 !== name.indexOf(":")) {
4376 gpf.Error.unableToUseNamespaceInString();
4377 }
4378 }
4379 /**
4380 * Resolve attribute name
4381 *
4382 * @param {String} name Attribute name used in the tag function
4383 * @return {String} Attribute to set on the node ele,ment
4384 * @since 0.2.1
4385 */
4386 function _gpfWebTagAttributeAlias(name) {
4387 return _gpfWebTagAttributeAliases[name] || name;
4388 }
4389 /**
4390 * Apply the callback to each array item,
4391 * process recursively if the array item is an array
4392 *
4393 * @param {Array} array array of items
4394 * @param {Function} callback Function to apply on each array item
4395 * @since 0.2.1
4396 */
4397 function _gpfWebTagFlattenChildren(array, callback) {
4398 array.forEach(function (item) {
4399 if (_gpfIsArray(item)) {
4400 _gpfWebTagFlattenChildren(item, callback);
4401 } else {
4402 callback(item);
4403 }
4404 });
4405 }
4406 var _GpfWebTag = _gpfDefine(/** @lends gpf.web.Tag.prototype */
4407 {
4408 $class: "gpf.web.Tag",
4409 /**
4410 * Tag object
4411 *
4412 * @param {String} nodeName Node name
4413 * @param {Object} [attributes] Dictionary of attributes to set
4414 * @param {Array} [children] Children
4415 *
4416 * @constructor gpf.web.Tag
4417 * @private
4418 * @since 0.2.1
4419 */
4420 constructor: function (nodeName, attributes, children) {
4421 this._nodeName = nodeName;
4422 this._attributes = attributes || {};
4423 this._children = children;
4424 },
4425 /**
4426 * Node name
4427 * @since 0.2.1
4428 */
4429 _nodeName: "",
4430 /**
4431 * Node attributes
4432 * @since 0.2.1
4433 */
4434 _attributes: {},
4435 /**
4436 * Node children
4437 * @since 0.2.1
4438 */
4439 _children: [],
4440 //region toString implementation
4441 _getAttributesAsString: function () {
4442 return Object.keys(this._attributes).map(function (name) {
4443 _gpfWebCheckNamespaceSafe(name);
4444 return " " + _gpfWebTagAttributeAlias(name) + "=\"" + _gpfStringEscapeFor(this._attributes[name], "html") + "\"";
4445 }, this).join("");
4446 },
4447 _getChildrenAsString: function () {
4448 var result = [];
4449 _gpfWebTagFlattenChildren(this._children, function (child) {
4450 result.push(child.toString());
4451 });
4452 return result.join("");
4453 },
4454 _getClosingString: function () {
4455 if (this._children.length) {
4456 return ">" + this._getChildrenAsString() + "</" + this._nodeName + ">";
4457 }
4458 return "/>";
4459 },
4460 /**
4461 * Convert the current tag into HTML
4462 *
4463 * @return {String} HTML representation of the tag
4464 * @since 0.2.1
4465 */
4466 toString: function () {
4467 _gpfWebCheckNamespaceSafe(this._nodeName);
4468 return "<" + this._nodeName + this._getAttributesAsString() + this._getClosingString();
4469 },
4470 //endregion
4471 //region appendTo implementation
4472 _createElement: function (node) {
4473 var ownerDocument = node.ownerDocument, qualified = _gpfWebGetNamespaceAndName(this._nodeName);
4474 if (qualified) {
4475 return ownerDocument.createElementNS(qualified.namespace, qualified.name);
4476 }
4477 return ownerDocument.createElement(this._nodeName);
4478 },
4479 _setAttributesTo: function (node) {
4480 _gpfObjectForEach(this._attributes, function (value, name) {
4481 var qualified = _gpfWebGetNamespaceAndName(name);
4482 if (qualified) {
4483 node.setAttributeNS(qualified.namespace, _gpfWebTagAttributeAlias(qualified.name), value);
4484 } else {
4485 node.setAttribute(_gpfWebTagAttributeAlias(name), value);
4486 }
4487 });
4488 },
4489 _appendChildrenTo: function (node) {
4490 var ownerDocument = node.ownerDocument;
4491 _gpfWebTagFlattenChildren(this._children, function (child) {
4492 if (child instanceof _GpfWebTag) {
4493 child.appendTo(node);
4494 } else {
4495 node.appendChild(ownerDocument.createTextNode(child.toString()));
4496 }
4497 });
4498 },
4499 /**
4500 * Appends the tag to the provided node
4501 *
4502 * @param {Object} node Expected to be a DOM node
4503 * @return {Object} Created node
4504 * @since 0.2.1
4505 */
4506 appendTo: function (node) {
4507 var element = this._createElement(node);
4508 this._setAttributesTo(element);
4509 this._appendChildrenTo(element);
4510 return node.appendChild(element);
4511 } //endregion
4512 });
4513 /**
4514 * Create a tag generation function
4515 *
4516 * @param {String} nodeName tag name
4517 * @return {gpf.typedef.tagFunc} The tag generation function
4518 * @gpf:closure
4519 * @since 0.2.1
4520 */
4521 function _gpfWebTagCreateFunction(nodeName) {
4522 if (!nodeName) {
4523 gpf.Error.missingNodeName();
4524 }
4525 return function (firstParam) {
4526 var sliceFrom, attributes;
4527 if (_gpfIsLiteralObject(firstParam)) {
4528 attributes = firstParam;
4529 sliceFrom = 1;
4530 } else {
4531 sliceFrom = 0;
4532 }
4533 return new _GpfWebTag(nodeName, attributes, [].slice.call(arguments, sliceFrom));
4534 };
4535 }
4536 /**
4537 * @gpf:sameas _gpfWebTagCreateFunction
4538 * @since 0.2.1
4539 *
4540 * @example <caption>Tree building to string</caption>
4541 * var div = gpf.web.createTagFunction("div"),
4542 * span = gpf.web.createTagFunction("span"),
4543 * tree = div({className: "test1"}, "Hello ", span({className: "test2"}, "World!"));
4544 * // tree.toString() gives <div class="test1">Hello <span class="test2">World!</span></div>
4545 *
4546 * @example <caption>Tree building to DOM</caption>
4547 * var mockNode = mockDocument.createElement("any"),
4548 * div = gpf.web.createTagFunction("div"),
4549 * span = gpf.web.createTagFunction("span"),
4550 * tree = div({className: "test"}, "Hello ", span("World!")),
4551 * result = tree.appendTo(mockNode);
4552 */
4553 gpf.web.createTagFunction = _gpfWebTagCreateFunction;
4554 gpf.http = {};
4555 /**
4556 * Http methods
4557 * @since 0.2.1
4558 */
4559 var _GPF_HTTP_METHODS = {
4560 GET: "GET",
4561 POST: "POST",
4562 PUT: "PUT",
4563 OPTIONS: "OPTIONS",
4564 DELETE: "DELETE",
4565 HEAD: "HEAD"
4566 };
4567 var _gpfHttpHeadersParserRE = new RegExp("([^:\\s]+)\\s*: ?([^\\r]*)", "gm");
4568 /**
4569 * Parse HTTP response headers
4570 *
4571 * @param {String} headers Response headers
4572 * @return {Object} headers dictionary
4573 * @since 0.2.1
4574 */
4575 function _gpfHttpParseHeaders(headers) {
4576 var result = {};
4577 _gpfArrayForEach(_gpfRegExpForEach(_gpfHttpHeadersParserRE, headers), function (match) {
4578 result[match[1]] = match[2];
4579 });
4580 return result;
4581 }
4582 /**
4583 * Generates a function that transmit headers to the http object
4584 *
4585 * @param {String} methodName Name of the method to call
4586 * @return {Function} Method to set the headers
4587 * @gpf:closure
4588 * @since 0.2.1
4589 */
4590 function _gpfHttpGenSetHeaders(methodName) {
4591 return function (httpObj, headers) {
4592 if (headers) {
4593 Object.keys(headers).forEach(function (headerName) {
4594 httpObj[methodName](headerName, headers[headerName]);
4595 });
4596 }
4597 };
4598 }
4599 /**
4600 * Generates a function that implements the http send logic
4601 *
4602 * @param {String} methodName Name of the method to call
4603 * @return {Function} Method to trigger the send
4604 * @gpf:closure
4605 * @since 0.2.1
4606 */
4607 function _gpfHttpGenSend(methodName) {
4608 return function (httpObj, data) {
4609 if (data) {
4610 httpObj[methodName](data);
4611 } else {
4612 httpObj[methodName]();
4613 }
4614 };
4615 }
4616 var _gpfIThenable = _gpfDefineInterface("Thenable", { "then": 2 });
4617 /**
4618 * Converts any value into a promise.
4619 * If the value implements {@link gpf.interfaces.IThenable}, it is considered as a promise.
4620 *
4621 * @param {*} value Value to convert
4622 * @return {Promise<*>} Promisified version of the value
4623 * @since 0.2.2
4624 */
4625 function _gpfPromisify(value) {
4626 if (gpf.interfaces.isImplementedBy(gpf.interfaces.IThenable, value)) {
4627 return value;
4628 }
4629 return Promise.resolve(value);
4630 }
4631 /**
4632 * Converts value into a promise if not undefined.
4633 * If the value implements {@link gpf.interfaces.IThenable}, it is considered as a promise.
4634 *
4635 * @param {*} value Value to convert
4636 * @return {Promise<*>|undefined} Promisified version of the value or undefined
4637 * @since 0.2.2
4638 */
4639 function _gpfPromisifyDefined(value) {
4640 if (undefined !== value) {
4641 return _gpfPromisify(value);
4642 }
4643 }
4644 /**
4645 * @gpf:sameas _gpfPromisify
4646 * @since 0.2.2
4647 */
4648 gpf.promisify = _gpfPromisify;
4649 /**
4650 * @gpf:sameas _gpfPromisifyDefined
4651 * @since 0.2.2
4652 */
4653 gpf.promisifyDefined = _gpfPromisifyDefined;
4654 var _gpfHttpMockedRequests = {};
4655 /**
4656 * Match the provided request with the mocked one
4657 *
4658 * @param {gpf.typedef.mockedRequest} mockedRequest Mocked request to match
4659 * @return {Promise<gpf.typedef.httpRequestResponse>|undefined} undefined if not matching
4660 * @this {gpf.typedef.httpRequestSettings}
4661 * @since 0.2.2
4662 */
4663 function _gpfHttMockMatchRequest(mockedRequest) {
4664 /*jshint validthis:true*/
4665 var url = mockedRequest.url, match;
4666 url.lastIndex = 0;
4667 match = url.exec(this.url);
4668 if (match) {
4669 return _gpfPromisifyDefined(mockedRequest.response.apply(null, [this].concat([].slice.call(match, 1))));
4670 }
4671 }
4672 /**
4673 * Match the provided request to the list of mocked ones
4674 *
4675 * @param {gpf.typedef.mockedRequest[]} mockedRequests List of mocked requests for the given method
4676 * @param {gpf.typedef.httpRequestSettings} request Request to match
4677 * @return {Promise<gpf.typedef.httpRequestResponse>|undefined} undefined if no mocked request matches
4678 * @since 0.2.2
4679 */
4680 function _gpfHttMockMatch(mockedRequests, request) {
4681 return _gpfArrayForEachFalsy(mockedRequests, _gpfHttMockMatchRequest.bind(request));
4682 }
4683 /**
4684 * Check if the provided request match any of the mocked one
4685 *
4686 * @param {gpf.typedef.httpRequestSettings} request Request to check
4687 * @return {Promise<gpf.typedef.httpRequestResponse>|undefined} undefined if no mocked request matches
4688 * @since 0.2.2
4689 */
4690 function _gpfHttpMockCheck(request) {
4691 return _gpfHttMockMatch(_gpfHttpMockedRequests[request.method], request);
4692 }
4693 var _gpfHttpMockLastId = 0;
4694 /**
4695 * Add a mocked request
4696 *
4697 * @param {gpf.typedef.mockedRequest} definition Mocked request definition
4698 * @return {gpf.typedef.mockedRequestID} Mocked request identifier, to be used with {@link gpf.http.mock.remove}
4699 * @see gpf.http
4700 * @since 0.2.2
4701 */
4702 function _gpfHttpMockAdd(definition) {
4703 var method = definition.method, id;
4704 ++_gpfHttpMockLastId;
4705 id = method + "." + _gpfHttpMockLastId;
4706 _gpfHttpMockedRequests[method].unshift(Object.assign({
4707 method: method,
4708 id: id
4709 }, definition));
4710 return id;
4711 }
4712 /**
4713 * Removes a mocked request
4714 *
4715 * @param {gpf.typedef.mockedRequestID} id Mocked request identifier returned by {@link gpf.http.mock}
4716 * @since 0.2.2
4717 */
4718 function _gpfHttpMockRemove(id) {
4719 var method = id.split(".")[0];
4720 _gpfHttpMockedRequests[method] = _gpfHttpMockedRequests[method].filter(function (mockedRequest) {
4721 return mockedRequest.id !== id;
4722 });
4723 }
4724 /**
4725 * Clears all mocked requests
4726 * @since 0.2.2
4727 */
4728 function _gpfHttpMockReset() {
4729 Object.keys(_GPF_HTTP_METHODS).forEach(function (method) {
4730 _gpfHttpMockedRequests[method] = [];
4731 });
4732 }
4733 _gpfHttpMockReset();
4734 /**
4735 * @gpf:sameas _gpfHttpMockAdd
4736 * @since 0.2.2
4737 */
4738 gpf.http.mock = _gpfHttpMockAdd;
4739 /**
4740 * @gpf:sameas _gpfHttpMockRemove
4741 * @since 0.2.2
4742 */
4743 gpf.http.mock.remove = _gpfHttpMockRemove;
4744 /**
4745 * @gpf:sameas _gpfHttpMockReset
4746 * @since 0.2.2
4747 */
4748 gpf.http.mock.reset = _gpfHttpMockReset;
4749 var _gpfHttpRequestImplByHost = {};
4750 /**
4751 * HTTP request common implementation
4752 *
4753 * @param {gpf.typedef.httpRequestSettings} request HTTP Request settings
4754 * @return {Promise<gpf.typedef.httpRequestResponse>} Resolved on request completion
4755 * @since 0.2.1
4756 */
4757 function _gpfHttpRequest(request) {
4758 return _gpfHttpMockCheck(request) || new Promise(function (resolve, reject) {
4759 _gpfHttpRequestImplByHost[_gpfHost](request, resolve, reject);
4760 });
4761 }
4762 /**
4763 * Implementation of aliases
4764 *
4765 * @param {String} method HTTP method to apply
4766 * @param {String|gpf.typedef.httpRequestSettings} url Url to send the request to or a request settings object
4767 * @param {*} [data] Data to be sent to the server
4768 * @return {Promise<gpf.typedef.httpRequestResponse>} HTTP request promise
4769 * @since 0.2.1
4770 */
4771 function _gpfProcessAlias(method, url, data) {
4772 if ("string" === typeof url) {
4773 return _gpfHttpRequest({
4774 method: method,
4775 url: url,
4776 data: data
4777 });
4778 }
4779 return _gpfHttpRequest(Object.assign({ method: method }, url));
4780 }
4781 Object.assign(gpf.http, /** @lends gpf.http */
4782 {
4783 /**
4784 * HTTP methods enumeration
4785 *
4786 * @enum {String}
4787 * @readonly
4788 * @since 0.2.1
4789 */
4790 methods: {
4791 /**
4792 * GET
4793 * @since 0.2.1
4794 */
4795 get: _GPF_HTTP_METHODS.GET,
4796 /**
4797 * POST
4798 * @since 0.2.1
4799 */
4800 post: _GPF_HTTP_METHODS.POST,
4801 /**
4802 * PUT
4803 * @since 0.2.1
4804 */
4805 put: _GPF_HTTP_METHODS.PUT,
4806 /**
4807 * OPTIONS
4808 * @since 0.2.1
4809 */
4810 options: _GPF_HTTP_METHODS.OPTIONS,
4811 /**
4812 * DELETE
4813 * @since 0.2.1
4814 */
4815 "delete": _GPF_HTTP_METHODS.DELETE,
4816 /**
4817 * HEAD
4818 * @since 0.2.2
4819 */
4820 head: _GPF_HTTP_METHODS.HEAD
4821 },
4822 /**
4823 * @gpf:sameas _gpfHttpRequest
4824 * @since 0.2.1
4825 */
4826 request: _gpfHttpRequest,
4827 /**
4828 * HTTP GET request
4829 *
4830 * @method
4831 * @param {String|gpf.typedef.httpRequestSettings} urlOrRequest URL or HTTP Request settings
4832 * @return {Promise<gpf.typedef.httpRequestResponse>} Resolved on request completion
4833 * @since 0.2.1
4834 */
4835 get: _gpfProcessAlias.bind(gpf.http, _GPF_HTTP_METHODS.GET),
4836 /**
4837 * HTTP POST request
4838 *
4839 * @method
4840 * @param {String|gpf.typedef.httpRequestSettings} urlOrRequest URL or HTTP Request settings
4841 * @param {String} data Data to POST
4842 * @return {Promise<gpf.typedef.httpRequestResponse>} Resolved on request completion
4843 * @since 0.2.1
4844 */
4845 post: _gpfProcessAlias.bind(gpf.http, _GPF_HTTP_METHODS.POST),
4846 /**
4847 * HTTP PUT request
4848 *
4849 * @method
4850 * @param {String|gpf.typedef.httpRequestSettings} urlOrRequest URL or HTTP Request settings
4851 * @param {String} data Data to PUT
4852 * @return {Promise<gpf.typedef.httpRequestResponse>} Resolved on request completion
4853 * @since 0.2.1
4854 */
4855 put: _gpfProcessAlias.bind(gpf.http, _GPF_HTTP_METHODS.PUT),
4856 /**
4857 * HTTP OPTIONS request
4858 *
4859 * @method
4860 * @param {String|gpf.typedef.httpRequestSettings} urlOrRequest URL or HTTP Request settings
4861 * @return {Promise<gpf.typedef.httpRequestResponse>} Resolved on request completion
4862 * @since 0.2.1
4863 */
4864 options: _gpfProcessAlias.bind(gpf.http, _GPF_HTTP_METHODS.OPTIONS),
4865 /**
4866 * HTTP DELETE request
4867 *
4868 * @method
4869 * @param {String|gpf.typedef.httpRequestSettings} urlOrRequest URL or HTTP Request settings
4870 * @return {Promise<gpf.typedef.httpRequestResponse>} Resolved on request completion
4871 * @since 0.2.1
4872 */
4873 "delete": _gpfProcessAlias.bind(gpf.http, _GPF_HTTP_METHODS.DELETE),
4874 /**
4875 * HTTP HEAD request
4876 *
4877 * @method
4878 * @param {String|gpf.typedef.httpRequestSettings} urlOrRequest URL or HTTP Request settings
4879 * @return {Promise<gpf.typedef.httpRequestResponse>} Resolved on request completion
4880 * @since 0.2.2
4881 */
4882 head: _gpfProcessAlias.bind(gpf.http, _GPF_HTTP_METHODS.HEAD)
4883 });
4884 var _gpfHttpXhrSetHeaders = _gpfHttpGenSetHeaders("setRequestHeader"), _gpfHttpXhrSend = _gpfHttpGenSend("send");
4885 function _gpfHttpXhrOpen(request) {
4886 var xhr = new XMLHttpRequest();
4887 xhr.open(request.method, request.url);
4888 return xhr;
4889 }
4890 function _gpfHttpXhrWaitForCompletion(xhr, callback) {
4891 xhr.onreadystatechange = function () {
4892 if (4 === xhr.readyState) {
4893 callback();
4894 }
4895 };
4896 }
4897 _gpfHttpRequestImplByHost[_GPF_HOST.BROWSER] = _gpfHttpRequestImplByHost[_GPF_HOST.PHANTOMJS] = function (request, resolve) {
4898 var xhr = _gpfHttpXhrOpen(request);
4899 _gpfHttpXhrSetHeaders(xhr, request.headers);
4900 _gpfHttpXhrWaitForCompletion(xhr, function () {
4901 resolve({
4902 status: xhr.status,
4903 headers: _gpfHttpParseHeaders(xhr.getAllResponseHeaders()),
4904 responseText: xhr.responseText
4905 });
4906 });
4907 _gpfHttpXhrSend(xhr, request.data);
4908 };
4909 function _gpfStringFromStream(readableStream) {
4910 var iWritableString = new _GpfStreamWritableString(), iReadableStream = _gpfStreamQueryReadable(readableStream);
4911 return iReadableStream.read(iWritableString).then(function () {
4912 return iWritableString.toString();
4913 });
4914 }
4915 function _gpfHttpNodeProcessResponse(nodeResponse, resolve) {
4916 nodeResponse.setEncoding("utf8");
4917 var iReadableStream = new _GpfNodeReadableStream(nodeResponse);
4918 _gpfStringFromStream(iReadableStream).then(function (responseText) {
4919 iReadableStream.close();
4920 resolve({
4921 status: nodeResponse.statusCode,
4922 headers: nodeResponse.headers,
4923 responseText: responseText
4924 });
4925 });
4926 }
4927 function _gpfHttpNodeAdjustSettingsForSend(settings, data) {
4928 if (data) {
4929 settings.headers = Object.assign({
4930 "Content-Type": "application/x-www-form-urlencoded",
4931 "Content-Length": Buffer.byteLength(data)
4932 }, settings.headers);
4933 }
4934 }
4935 function _gpfHttpNodeBuildRequestSettings(request) {
4936 var settings = Object.assign(_gpfNodeUrl.parse(request.url), request);
4937 _gpfHttpNodeAdjustSettingsForSend(settings, request.data);
4938 return settings;
4939 }
4940 function _gpfHttpNodeAllocate(request, resolve) {
4941 var settings = _gpfHttpNodeBuildRequestSettings(request);
4942 return _gpfNodeHttp.request(settings, function (nodeResponse) {
4943 _gpfHttpNodeProcessResponse(nodeResponse, resolve);
4944 });
4945 }
4946 function _gpfHttpNodeSend(clientRequest, data) {
4947 if (data) {
4948 clientRequest.write(data, "utf8", function () {
4949 clientRequest.end();
4950 });
4951 } else {
4952 clientRequest.end();
4953 }
4954 }
4955 _gpfHttpRequestImplByHost[_GPF_HOST.NODEJS] = function (request, resolve, reject) {
4956 var clientRequest = _gpfHttpNodeAllocate(request, resolve);
4957 clientRequest.on("error", reject);
4958 _gpfHttpNodeSend(clientRequest, request.data);
4959 };
4960 var _gpfHttpWScriptSetHeaders = _gpfHttpGenSetHeaders("setRequestHeader"), _gpfHttpWScriptSend = _gpfHttpGenSend("Send");
4961 function _gpfHttpWScriptAllocate(request) {
4962 var winHttp = new ActiveXObject("WinHttp.WinHttpRequest.5.1");
4963 winHttp.Open(request.method, request.url);
4964 return winHttp;
4965 }
4966 function _gpfHttpWScriptResolve(winHttp, resolve) {
4967 resolve({
4968 status: winHttp.Status,
4969 headers: _gpfHttpParseHeaders(winHttp.GetAllResponseHeaders()),
4970 responseText: winHttp.ResponseText
4971 });
4972 }
4973 _gpfHttpRequestImplByHost[_GPF_HOST.WSCRIPT] = function (request, resolve) {
4974 var winHttp = _gpfHttpWScriptAllocate(request);
4975 _gpfHttpWScriptSetHeaders(winHttp, request.headers);
4976 _gpfHttpWScriptSend(winHttp, request.data);
4977 _gpfHttpWScriptResolve(winHttp, resolve);
4978 };
4979 var _GpfRhinoBaseStream = _gpfDefine(/** @lends gpf.rhino.BaseStream.prototype */
4980 {
4981 $class: "gpf.rhino.BaseStream",
4982 /**
4983 * Base class wrapping Rhino streams
4984 *
4985 * @param {java.io.InputStream|java.io.OutputStream} stream Rhino input or output stream object
4986 *
4987 * @constructor gpf.rhino.BaseStream
4988 * @private
4989 * @since 0.2.1
4990 */
4991 constructor: function (stream) {
4992 this._stream = stream;
4993 },
4994 /**
4995 * Close the stream
4996 *
4997 * @return {Promise} Resolved when closed
4998 * @since 0.2.1
4999 */
5000 close: function () {
5001 this._stream.close();
5002 return Promise.resolve();
5003 },
5004 /**
5005 * Rhino stream object
5006 *
5007 * @type {java.io.InputStream|java.io.OutputStream}
5008 * @since 0.2.1
5009 */
5010 _stream: null
5011 }),
5012 /**
5013 * Wraps a readable stream from Rhino into a IReadableStream
5014 *
5015 * @param {java.io.InputStream} stream Rhino stream object
5016 *
5017 * @class gpf.rhino.ReadableStream
5018 * @extends gpf.rhino.BaseStream
5019 * @implements {gpf.interfaces.IReadableStream}
5020 * @since 0.2.1
5021 */
5022 _GpfRhinoReadableStream = _gpfDefine(/** @lends gpf.rhino.ReadableStream.prototype */
5023 {
5024 $class: "gpf.rhino.ReadableStream",
5025 $extend: "gpf.rhino.BaseStream",
5026 //region gpf.interfaces.IReadableStream
5027 /**
5028 * Process error that occurred during the stream reading
5029 *
5030 * @param {Error} e Error coming from read
5031 * @return {Promise} Read result replacement
5032 * @since 0.2.1
5033 */
5034 _handleError: function (e) {
5035 if (e.message.indexOf("java.util.NoSuchElementException") === 0) {
5036 // Empty stream
5037 return Promise.resolve();
5038 }
5039 return Promise.reject(e);
5040 },
5041 /**
5042 * @gpf:sameas gpf.interfaces.IReadableStream#read
5043 * @since 0.2.1
5044 */
5045 read: _gpfStreamSecureRead(function (output) {
5046 try {
5047 var scanner = new java.util.Scanner(this._stream);
5048 //eslint-disable-line no-invalid-this
5049 return output.write(String(scanner.useDelimiter("\\A").next()));
5050 } catch (e) {
5051 return this._handleError(e); //eslint-disable-line no-invalid-this
5052 }
5053 }) //endregion
5054 }),
5055 /**
5056 * Wraps a writable stream from Rhino into a IWritableStream
5057 *
5058 * @param {java.io.OutputStream} stream Rhino stream object
5059 *
5060 * @class gpf.rhino.WritableStream
5061 * @extends gpf.rhino.BaseStream
5062 * @implements {gpf.interfaces.IWritableStream}
5063 * @since 0.2.1
5064 */
5065 _GpfRhinoWritableStream = _gpfDefine(/** @lends gpf.rhino.WritableStream.prototype */
5066 {
5067 $class: "gpf.rhino.WritableStream",
5068 $extend: "gpf.rhino.BaseStream",
5069 constructor: function (stream) {
5070 this.$super(stream);
5071 this._writer = new java.io.OutputStreamWriter(stream);
5072 },
5073 //region gpf.interfaces.IWritableStream
5074 /**
5075 * @gpf:sameas gpf.interfaces.IWritableStream#write
5076 * @since 0.2.1
5077 */
5078 write: _gpfStreamSecureWrite(function (buffer) {
5079 var writer = this._writer;
5080 //eslint-disable-line no-invalid-this
5081 writer.write(buffer);
5082 writer.flush();
5083 return Promise.resolve();
5084 }),
5085 //endregion
5086 /**
5087 * @inheritdoc
5088 * @since 0.2.1
5089 */
5090 close: function () {
5091 this._writer.close();
5092 return this.$super();
5093 },
5094 /**
5095 * Stream writer
5096 *
5097 * @type {java.io.OutputStreamWriter}
5098 * @since 0.2.1
5099 */
5100 _writer: null
5101 });
5102 var _gpfHttpRhinoSetHeaders = _gpfHttpGenSetHeaders("setRequestProperty");
5103 function _gpfHttpRhinoSendData(httpConnection, data) {
5104 if (data) {
5105 httpConnection.setDoOutput(true);
5106 var iWritableStream = new _GpfRhinoWritableStream(httpConnection.getOutputStream());
5107 return iWritableStream.write(data).then(function () {
5108 iWritableStream.close();
5109 });
5110 }
5111 return Promise.resolve();
5112 }
5113 function _gpfHttpRhinoGetResponse(httpConnection) {
5114 try {
5115 return httpConnection.getInputStream();
5116 } catch (e) {
5117 return httpConnection.getErrorStream();
5118 }
5119 }
5120 function _gpfHttpRhinoGetResponseText(httpConnection) {
5121 var iReadableStream = new _GpfRhinoReadableStream(_gpfHttpRhinoGetResponse(httpConnection));
5122 return _gpfStringFromStream(iReadableStream).then(function (responseText) {
5123 iReadableStream.close();
5124 return responseText;
5125 });
5126 }
5127 function _gpfHttpRhinoGetHeaders(httpConnection) {
5128 var headers = {}, headerFields = httpConnection.getHeaderFields(), keys = headerFields.keySet().toArray();
5129 keys.forEach(function (key) {
5130 headers[String(key)] = String(headerFields.get(key).get(0));
5131 });
5132 return headers;
5133 }
5134 function _gpfHttpRhinoResolve(httpConnection, resolve) {
5135 _gpfHttpRhinoGetResponseText(httpConnection).then(function (responseText) {
5136 resolve({
5137 status: httpConnection.getResponseCode(),
5138 headers: _gpfHttpRhinoGetHeaders(httpConnection),
5139 responseText: responseText
5140 });
5141 });
5142 }
5143 _gpfHttpRequestImplByHost[_GPF_HOST.RHINO] = function (request, resolve) {
5144 var httpConnection = new java.net.URL(request.url).openConnection();
5145 httpConnection.setRequestMethod(request.method);
5146 _gpfHttpRhinoSetHeaders(httpConnection, request.headers);
5147 _gpfHttpRhinoSendData(httpConnection, request.data).then(function () {
5148 _gpfHttpRhinoResolve(httpConnection, resolve);
5149 });
5150 };
5151 function _gpfStreamLineLastDoesntEndsWithLF(buffer) {
5152 var lastItem = buffer[buffer.length - 1];
5153 return lastItem.charAt(lastItem.length - 1) !== "\n";
5154 }
5155 function _gpfStreamLineTrimCR(line) {
5156 var lengthMinus1 = line.length - 1;
5157 if (line.lastIndexOf("\r") === lengthMinus1) {
5158 return line.substr(0, lengthMinus1);
5159 }
5160 return line;
5161 }
5162 function _gpfStreamLineWrite(output, lines) {
5163 if (!lines.length) {
5164 return Promise.resolve();
5165 }
5166 return output.write(_gpfStreamLineTrimCR(lines.shift())).then(function () {
5167 return _gpfStreamLineWrite(output, lines);
5168 });
5169 }
5170 var _GpfStreamLineAdatper = _gpfDefine(/** @lends gpf.stream.LineAdapter.prototype */
5171 {
5172 $class: "gpf.stream.LineAdapter",
5173 /**
5174 * Stream line adapter
5175 *
5176 * @constructor gpf.stream.LineAdapter
5177 * @implements {gpf.interfaces.IReadableStream}
5178 * @implements {gpf.interfaces.IWritableStream}
5179 * @implements {gpf.interfaces.IFlushableStream}
5180 * @since 0.2.1
5181 */
5182 constructor: function () {
5183 this._buffer = [];
5184 },
5185 //region gpf.interfaces.IReadableStream
5186 /**
5187 * @gpf:sameas gpf.interfaces.IReadableStream#read
5188 * @since 0.2.1
5189 */
5190 read: _gpfStreamSecureRead(function (output) {
5191 var me = this;
5192 //eslint-disable-line no-invalid-this
5193 me._output = output;
5194 if (me._buffer.length) {
5195 return me._process();
5196 }
5197 return Promise.resolve();
5198 }),
5199 //endregion
5200 //region gpf.interfaces.IReadableStream
5201 /**
5202 * @gpf:sameas gpf.interfaces.IWritableStream#write
5203 * @since 0.2.1
5204 */
5205 write: _gpfStreamSecureWrite(function (buffer) {
5206 var me = this;
5207 //eslint-disable-line no-invalid-this
5208 me._buffer.push(buffer.toString());
5209 if (me._output) {
5210 return me._process();
5211 }
5212 return Promise.resolve();
5213 }),
5214 //endregion
5215 //region gpf.interfaces.IFlushableStream
5216 flush: function () {
5217 if (_gpfStreamLineLastDoesntEndsWithLF(this._buffer)) {
5218 return this.write("\n");
5219 }
5220 return Promise.resolve();
5221 },
5222 //endregion
5223 /**
5224 * Completes the stream, flush the remaining characters as the last line if any
5225 *
5226 * @return {Promise} Resolve when written to the output
5227 * @deprecated since version 0.2.2, use flush
5228 * @since 0.2.1
5229 */
5230 endOfStream: function () {
5231 return this.flush();
5232 },
5233 /**
5234 * Output stream
5235 *
5236 * @type {gpf.interfaces.IWritableStream}
5237 * @since 0.2.1
5238 */
5239 _output: null,
5240 /**
5241 * Buffer
5242 * @since 0.2.1
5243 */
5244 _buffer: [],
5245 /**
5246 * Extract lines from buffer
5247 *
5248 * @return {String[]} Array of lines
5249 * @since 0.2.1
5250 */
5251 _extractLines: function () {
5252 return this._buffer.join("").split("\n");
5253 },
5254 /**
5255 * The array lines is built using split on \n. Hence, the last line is what comes after the last \n.
5256 * If not empty, it must be pushed back to the buffer.
5257 *
5258 * @param {String[]} lines Array of lines
5259 * @since 0.2.1
5260 */
5261 _pushBackLastLineIfNotEmpty: function (lines) {
5262 var lastLine = lines.pop();
5263 if (lastLine.length) {
5264 this._buffer.push(lastLine);
5265 }
5266 },
5267 /**
5268 * Check if the buffer contains any carriage return and write to output
5269 *
5270 * @return {Promise} Resolve when all lines were written
5271 * @since 0.2.1
5272 */
5273 _process: function () {
5274 var lines = this._extractLines();
5275 this._buffer.length = 0;
5276 this._pushBackLastLineIfNotEmpty(lines);
5277 return _gpfStreamLineWrite(this._output, lines);
5278 }
5279 });
5280 _gpfStreamSecureInstallProgressFlag(_GpfStreamLineAdatper);
5281 var _gpfIFlushableStream = _gpfDefineInterface("FlushableStream", { "flush": 0 });
5282 var _gpfRequireLoadImpl;
5283 function _gpfRequireLoadHTTP(name) {
5284 return _gpfHttpRequest({
5285 method: _GPF_HTTP_METHODS.GET,
5286 url: name
5287 }).then(function (response) {
5288 return response.responseText;
5289 });
5290 }
5291 function _gpfRequireLoadFS(name) {
5292 // Must be relative to the current execution path
5293 return _gpfFsRead(_gpfPathJoin(".", name));
5294 }
5295 if (_gpfHost === _GPF_HOST.BROWSER) {
5296 _gpfRequireLoadImpl = _gpfRequireLoadHTTP;
5297 } else {
5298 _gpfRequireLoadImpl = _gpfRequireLoadFS;
5299 }
5300 /**
5301 * Mapping of resource extension to processor function
5302 *
5303 * @type {Object}
5304 * @since 0.2.2
5305 */
5306 var _gpfRequireProcessor = {};
5307 /**
5308 * Load the resource
5309 *
5310 * @param {String} name Resource name
5311 * @return {Promise<*>} Resolved with the resource result
5312 * @since 0.2.2
5313 */
5314 function _gpfRequireLoad(name) {
5315 var me = this;
5316 return _gpfRequireLoadImpl(name).then(function (content) {
5317 var processor = _gpfRequireProcessor[_gpfPathExtension(name).toLowerCase()];
5318 if (processor) {
5319 return processor.call(me, name, content);
5320 }
5321 // Treated as simple text file by default
5322 return content;
5323 });
5324 }
5325 function _gpfRequireAllocateWrapper() {
5326 return {
5327 gpf: Object.create(gpf),
5328 promise: Promise.resolve(),
5329 _initialDefine: null
5330 };
5331 }
5332 function _gpfRequireWrappedDefine() {
5333 /*jshint validthis:true*/
5334 var wrapper = this,
5335 //eslint-disable-line
5336 gpfRequire = wrapper.gpf.require, gpfRequireDefine = wrapper._initialDefine;
5337 wrapper.promise = gpfRequireDefine.apply(gpfRequire, arguments);
5338 gpfRequire.define = gpfRequireDefine;
5339 return wrapper.promise;
5340 }
5341 function _gpfRequirePlugWrapper(wrapper, require) {
5342 wrapper._initialDefine = require.define;
5343 require.define = _gpfRequireWrappedDefine.bind(wrapper);
5344 wrapper.gpf.require = require;
5345 return wrapper;
5346 }
5347 /**
5348 * Wrap gpf to fit the new context and give access to gpf.require.define promise
5349 *
5350 * @param {Object} context Require context
5351 * @param {String} name Resource (resolved) name
5352 * @return {gpf.typedef._requireWrapper} Wrapper
5353 * @since 0.2.2
5354 */
5355 function _gpfRequireWrapGpf(context, name) {
5356 return _gpfRequirePlugWrapper(_gpfRequireAllocateWrapper(), _gpfRequireAllocate(context, { base: _gpfPathParent(name) }));
5357 }
5358 _gpfRequireProcessor[".json"] = function (name, content) {
5359 return JSON.parse(content);
5360 };
5361 _gpfErrorDeclare("require/javascript", {
5362 /**
5363 * ### Summary
5364 *
5365 * Dynamic require not supported
5366 *
5367 * ### Description
5368 *
5369 * When loading a [CommonJS](http://www.commonjs.org/) module, a first pass is done to extract all requires being
5370 * called. If the require is based on a complex parameter (variable or string manipulation), the loader won't be
5371 * able to understand the require. No fallback mechanism is implemented yet
5372 * @since 0.2.2
5373 */
5374 noCommonJSDynamicRequire: "Dynamic require not supported"
5375 });
5376 //region CommonJS
5377 var _gpfRequireJsModuleRegEx = /[^\.]\brequire\b\s*\(\s*(?:['|"]([^"']+)['|"]|[^\)]+)\s*\)/g;
5378 function _gpfRequireCommonJSBuildNamedDependencies(requires) {
5379 return requires.reduce(function (dictionary, name) {
5380 dictionary[name] = name;
5381 return dictionary;
5382 }, {});
5383 }
5384 function _gpfRequireCommonJs(myGpf, content, requires) {
5385 return myGpf.require.define(_gpfRequireCommonJSBuildNamedDependencies(requires), function (require) {
5386 var module = {};
5387 _gpfFunc([
5388 "gpf",
5389 "module",
5390 "require"
5391 ], content)(myGpf, module, function (name) {
5392 return require[name] || gpf.Error.noCommonJSDynamicRequire();
5393 });
5394 return module.exports;
5395 });
5396 }
5397 //endregion
5398 //region GPF, AMD (define) and others
5399 function _gpfRequireAmdDefineParamsFactoryOnly(params) {
5400 return {
5401 dependencies: [],
5402 factory: params[0]
5403 };
5404 }
5405 function _gpfRequireAmdDefineParamsDependenciesAndFactory(params) {
5406 return {
5407 dependencies: params[0],
5408 factory: params[1]
5409 };
5410 }
5411 function _gpfRequireAmdDefineParamsAll(params) {
5412 return {
5413 dependencies: params[1],
5414 factory: params[2]
5415 };
5416 }
5417 var
5418 /**
5419 * Mapping of define parameter count to dependencies / factory
5420 *
5421 * @type {Function[]}
5422 * @since 0.2.2
5423 */
5424 _gpfRequireAmdDefineParamsMapping = [
5425 null,
5426 _gpfRequireAmdDefineParamsFactoryOnly,
5427 _gpfRequireAmdDefineParamsDependenciesAndFactory,
5428 _gpfRequireAmdDefineParamsAll
5429 ];
5430 function _gpfRequireAmdDefine(name, dependencies, factory) {
5431 /*jshint validthis:true*/
5432 _gpfIgnore(name, dependencies, factory);
5433 var myGpf = this,
5434 //eslint-disable-line
5435 params = _gpfRequireAmdDefineParamsMapping[arguments.length](arguments);
5436 myGpf.require.define(params.dependencies, function (require) {
5437 require.length = params.dependencies.length;
5438 return params.factory.apply(null, [].slice.call(require));
5439 });
5440 }
5441 function _gpfRequireOtherJs(myGpf, content) {
5442 _gpfFunc([
5443 "gpf",
5444 "define"
5445 ], content)(myGpf, _gpfRequireAmdDefine.bind(myGpf));
5446 }
5447 //endregion
5448 _gpfRequireProcessor[".js"] = function (name, content) {
5449 var wrapper = _gpfRequireWrapGpf(this, name);
5450 // CommonJS ?
5451 var requires = _gpfRegExpForEach(_gpfRequireJsModuleRegEx, content);
5452 if (requires.length) {
5453 return _gpfRequireCommonJs(wrapper.gpf, content, requires.map(function (match) {
5454 return match[1]; // may be undefined if dynamic
5455 }).filter(function (require) {
5456 return require;
5457 }));
5458 }
5459 _gpfRequireOtherJs(wrapper.gpf, content);
5460 return wrapper.promise;
5461 };
5462 _gpfErrorDeclare("require", {
5463 /**
5464 * ### Summary
5465 *
5466 * Invalid {@link gpf.require.configure} option
5467 *
5468 * ### Description
5469 *
5470 * This error is triggered whenever an option passed to {@link gpf.require.configure} is not recognized.
5471 * Please check the {@link gpf.typedef.requireOptions} documentation.
5472 * @since 0.2.2
5473 */
5474 invalidRequireConfigureOption: "Invalid configuration option"
5475 });
5476 /**
5477 * @namespace gpf.require
5478 * @description Root namespace for the modularization helpers.
5479 * @since 0.2.2
5480 */
5481 /**
5482 * @typedef gpf.typedef.requireOptions
5483 * @property {String} [base] Base path used to resolve names
5484 * @property {Object} [cache] Inject names into the require cache
5485 * @property {Boolean} [clearCache=false] When set, the require cache is first cleared
5486 * @since 0.2.2
5487 */
5488 /**
5489 * @typedef gpf.typedef._requireContext
5490 * @property {String} base Base path used to resolve names
5491 * @property {Object} cache Dictionary of loaded requires
5492 * @since 0.2.2
5493 */
5494 /**
5495 * Valuate the option priority to have them executed in the proper order
5496 *
5497 * @param {String} name option name
5498 * @return {Number} Option priority
5499 * @since 0.2.2
5500 */
5501 function _gpfRequireOptionPriority(name) {
5502 if ("clearCache" === name) {
5503 return -1;
5504 }
5505 return 1;
5506 }
5507 /**
5508 * Dictionary of option name to function handling the option
5509 * @type {Object}
5510 * @since 0.2.2
5511 */
5512 var _gpfRequireOptionHandler = {
5513 base: function (base) {
5514 this.base = base;
5515 },
5516 cache: function (cache) {
5517 _gpfArrayForEach(Object.keys(cache), function (name) {
5518 this.cache[name] = _gpfPromisify(cache[name]);
5519 }, this);
5520 },
5521 clearCache: function () {
5522 this.cache = {};
5523 }
5524 };
5525 /**
5526 * Configure the {@link gpf.require} layer
5527 *
5528 * @param {gpf.typedef.requireOptions} options Options to configure
5529 * @since 0.2.2
5530 */
5531 function _gpfRequireConfigure(options) {
5532 var me = this;
5533 _gpfArrayForEach(Object.keys(options).sort(function (key1, key2) {
5534 // Some keys must be processed first
5535 return _gpfRequireOptionPriority(key1) - _gpfRequireOptionPriority(key2);
5536 }), function (key) {
5537 (_gpfRequireOptionHandler[key] || gpf.Error.invalidRequireConfigureOption).call(me, options[key]);
5538 }, me);
5539 }
5540 /**
5541 * Resolves the resource name according to current require context.
5542 *
5543 * @param {String} name Relative resource name
5544 * @return {String} Resolved name
5545 * @since 0.2.2
5546 *
5547 */
5548 function _gpfRequireResolve(name) {
5549 return _gpfPathJoin(this.base, name);
5550 }
5551 /**
5552 * Get the cached resource or load it
5553 *
5554 * @param {String} name Resource name
5555 * @return {Promise<*>} Resource association
5556 * @since 0.2.2
5557 */
5558 function _gpfRequireGet(name) {
5559 var me = this, promise;
5560 if (me.cache[name]) {
5561 return me.cache[name];
5562 }
5563 promise = _gpfRequireLoad.call(me, name);
5564 me.cache[name] = promise;
5565 return promise;
5566 }
5567 /**
5568 * Defines a new module by executing the factory function with the specified dependent resources,
5569 * see {@tutorial REQUIRE}
5570 *
5571 *
5572 * @param {Object} dependencies Dictionary of dependencies, the keys are preserved while passing the result
5573 * dictionary to the factory function
5574 * @param {*} factory Can be either:
5575 * * A factory function executed when all resources are resolved, the first parameter will be a dictionary
5576 * with all dependencies indexed by their name (as initially specified in the dependencies parameter).
5577 * The result of the factory function will be cached as the result of this resource
5578 * * Any value that will be cached as the result of this resource
5579 * @return {Promise<*>} Resolved with the factory function result or the object
5580 * @since 0.2.2
5581 */
5582 function _gpfRequireDefine(dependencies, factory) {
5583 var me = this, promises = [], keys = Object.keys(dependencies);
5584 _gpfArrayForEach(keys, function (key) {
5585 promises.push(_gpfRequireGet.call(me, _gpfRequireResolve.call(me, dependencies[key])));
5586 }, me);
5587 return Promise.all(promises).then(function (resources) {
5588 var result, require;
5589 if ("function" === typeof factory) {
5590 require = {};
5591 _gpfArrayForEach(keys, function (key, index) {
5592 require[key] = resources[index];
5593 });
5594 result = factory(require);
5595 } else {
5596 result = factory;
5597 }
5598 return result;
5599 });
5600 }
5601 /**
5602 * Allocate a new require context with the proper methods
5603 *
5604 * @param {Object} parentContext Context to inherit from
5605 * @param {gpf.typedef.requireOptions} [options] Options to configure
5606 * @return {Object} Containing {@link gpf.require.define}, {@link gpf.require.resolve} and {@link gpf.require.configure}
5607 * @since 0.2.2
5608 */
5609 function _gpfRequireAllocate(parentContext, options) {
5610 var context = Object.create(parentContext),
5611 // cache content is shared but other properties are protected
5612 require = {};
5613 require.define = _gpfRequireDefine.bind(context);
5614 require.resolve = _gpfRequireResolve.bind(context);
5615 require.configure = _gpfRequireConfigure.bind(context);
5616 if (options) {
5617 require.configure(options);
5618 }
5619 return require;
5620 }
5621 gpf.require = _gpfRequireAllocate({
5622 base: "",
5623 cache: {}
5624 }); /**
5625 * @method gpf.require.define
5626 * @gpf:sameas _gpfRequireDefine
5627 * @since 0.2.2
5628 */
5629 /**
5630 * @method gpf.require.configure
5631 * @gpf:sameas _gpfRequireConfigure
5632 * @since 0.2.2
5633 *
5634 * @example <caption>Setting the base path</caption>
5635 * gpf.require.configure({
5636 * base: "/test/require"
5637 * });
5638 * assert(gpf.require.resolve("file.js") === "/test/require/file.js");
5639 *
5640 * @example <caption>Injecting in the cache</caption>
5641 * var cache = {};
5642 * cache[gpf.require.resolve("data.json")] = {};
5643 * gpf.require.configure({
5644 * clearCache: true,
5645 * cache: cache
5646 * });
5647 */
5648 /**
5649 * @method gpf.require.resolve
5650 * @gpf:sameas _gpfRequireResolve
5651 * @since 0.2.2
5652 *
5653 * @example <caption>Setting the base path</caption>
5654 * gpf.require.configure({
5655 * base: "/test/require"
5656 * });
5657 * assert(gpf.require.resolve("file.js") === "/test/require/file.js");
5658 */
5659 var _GpfStreamReadableArray = _gpfDefine(/** @lends gpf.stream.ReadableArray.prototype */
5660 {
5661 $class: "gpf.stream.ReadableArray",
5662 /**
5663 * Wraps an array inside a readable stream.
5664 * Each array item is written separately to the output
5665 *
5666 * @constructor gpf.stream.ReadableArray
5667 * @implements {gpf.interfaces.IReadableStream}
5668 * @param {Array} buffer Array buffer
5669 * @since 0.2.2
5670 */
5671 constructor: function (buffer) {
5672 this._buffer = buffer;
5673 },
5674 //region gpf.interfaces.IReadableStream
5675 /**
5676 * @gpf:sameas gpf.interfaces.IReadableStream#read
5677 * @since 0.2.2
5678 */
5679 read: _gpfStreamSecureRead(function (output) {
5680 var buffer = this._buffer,
5681 //eslint-disable-line no-invalid-this
5682 step = 0;
5683 function write() {
5684 if (buffer.length === step) {
5685 return Promise.resolve();
5686 }
5687 return output.write(buffer[step++]).then(write);
5688 }
5689 return write();
5690 }),
5691 //endregion
5692 /**
5693 * Buffer
5694 * @since 0.2.2
5695 */
5696 _buffer: []
5697 }), _GpfStreamWritableArray = _gpfDefine(/** @lends gpf.stream.WritableArray.prototype */
5698 {
5699 $class: "gpf.stream.WritableArray",
5700 /**
5701 * Creates a writable stream that pushes all writes into an array
5702 *
5703 * @constructor gpf.stream.WritableArray
5704 * @implements {gpf.interfaces.IWritableStream}
5705 * @since 0.2.2
5706 */
5707 constructor: function () {
5708 this._buffer = [];
5709 },
5710 //region gpf.interfaces.IReadableStream
5711 /**
5712 * @gpf:sameas gpf.interfaces.IWritableStream#write
5713 * @since 0.2.2
5714 */
5715 write: _gpfStreamSecureWrite(function (buffer) {
5716 this._buffer.push(buffer);
5717 //eslint-disable-line no-invalid-this
5718 return Promise.resolve();
5719 }),
5720 //endregion
5721 toArray: function () {
5722 return this._buffer;
5723 },
5724 /**
5725 * Buffer
5726 * @since 0.2.2
5727 */
5728 _buffer: []
5729 });
5730 _gpfStreamSecureInstallProgressFlag(_GpfStreamReadableArray);
5731 _gpfStreamSecureInstallProgressFlag(_GpfStreamWritableArray);
5732}));
\No newline at end of file