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 | ;
|
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 | ;
|
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 | "&": "&",
|
657 | "<": "<",
|
658 | ">": ">"
|
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 | "à": "à",
|
1523 | "á": "á",
|
1524 | "è": "è",
|
1525 | "é": "é",
|
1526 | "ê": "ê"
|
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 |