@lmjs/core 1.0.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1097 @@
1
+ //https://github.com/tanzeelkazi/web-worker
2
+ /*eslint no-native-reassign:0 */
3
+ (function ($) {
4
+ 'use strict';
5
+
6
+ /**
7
+ * The WebWorker module.
8
+ * @module WebWorker
9
+ */
10
+ var WebWorker = null,
11
+ _WebWorker = null,
12
+ NativeWorker = null,
13
+
14
+ context = null,
15
+ className = null,
16
+
17
+ defaultContext = window,
18
+ defaultClassName = 'WebWorker',
19
+
20
+ State = null,
21
+
22
+ Action = null,
23
+
24
+ Event = null,
25
+ eventPrefix = 'webworker:',
26
+
27
+ EventMap = null,
28
+
29
+ Error = null,
30
+
31
+ slice = null,
32
+
33
+ key = null;
34
+
35
+ context = context || defaultContext;
36
+ className = className || defaultClassName;
37
+
38
+ WebWorker = context[className] || null;
39
+
40
+ if (WebWorker !== null) {
41
+ _WebWorker = WebWorker;
42
+ }
43
+
44
+ slice = Array.prototype.slice;
45
+ NativeWorker = window.Worker;
46
+
47
+ /**
48
+ * The main WebWorker class.
49
+ * @class WebWorker
50
+ * @constructor
51
+ * @param {Object|String} opts
52
+ * The options to be passed into the constructor.
53
+ * <br />
54
+ * If you pass a `String`, the code will first search
55
+ * for an element selector with the worker script.
56
+ * If it does not find the element it will then use
57
+ * it as a URL to the actual worker script.
58
+ */
59
+ WebWorker = function () {
60
+ this._constructor.apply(this, arguments);
61
+ return;
62
+ };
63
+
64
+ /**
65
+ * A jQuery object created from the webworker instance.
66
+ * @property _$
67
+ * @private
68
+ * @type {jQuery}
69
+ * @default $(this)
70
+ * @readOnly
71
+ */
72
+ WebWorker.prototype._$ = null;
73
+
74
+ /**
75
+ * The callback stack for the
76
+ * {{#crossLink "WebWorker/loading:method"}}{{/crossLink}},
77
+ * {{#crossLink "WebWorker/loaded:method"}}{{/crossLink}},
78
+ * {{#crossLink "WebWorker/starting:method"}}{{/crossLink}},
79
+ * {{#crossLink "WebWorker/started:method"}}{{/crossLink}},
80
+ * {{#crossLink "WebWorker/terminating:method"}}{{/crossLink}},
81
+ * and
82
+ * {{#crossLink "WebWorker/terminated:method"}}{{/crossLink}}
83
+ * hooks.
84
+ * @property _callbackStack
85
+ * @type {Object}
86
+ * @private
87
+ * @default null
88
+ */
89
+ WebWorker.prototype._callbackStack = null;
90
+
91
+ /**
92
+ * Stores the last error generated by the worker instance.
93
+ * @property _lastError
94
+ * @type {String}
95
+ * @private
96
+ * @default null
97
+ */
98
+ WebWorker.prototype._lastError = null;
99
+
100
+ /**
101
+ * Stores the log information for this worker instance. The log is not
102
+ * initialized unless explicity invoked by the
103
+ * {{#crossLink "WebWorker/log:method"}}{{/crossLink}} or
104
+ * {{#crossLink "WebWorker/getLog:method"}}{{/crossLink}} methods
105
+ * which internally call {{#crossLink "WebWorker/_initLog:method"}}{{/crossLink}}
106
+ * to initialize this property.
107
+ * @property _log
108
+ * @type {Array}
109
+ * @private
110
+ * @default null
111
+ */
112
+ WebWorker.prototype._log = null;
113
+
114
+ /**
115
+ * The native browser worker object. This is generated once the worker is loaded.
116
+ * @property _nativeWorker
117
+ * @type {Object}
118
+ * @private
119
+ * @default null
120
+ */
121
+ WebWorker.prototype._nativeWorker = null;
122
+
123
+ /**
124
+ * The current state of the worker. This value is updated internally by the worker.
125
+ * Valid values for this property are defined in {{#crossLink "WebWorker/State:property"}}{{/crossLink}}.
126
+ * @property _state
127
+ * @type {Number}
128
+ * @private
129
+ * @default null
130
+ */
131
+ WebWorker.prototype._state = null;
132
+
133
+ /**
134
+ * The `Blob` URL generated for the worker.
135
+ * @property _workerBlobUrl
136
+ * @type {String}
137
+ * @private
138
+ * @default null
139
+ */
140
+ WebWorker.prototype._workerBlobUrl = null;
141
+
142
+ /**
143
+ * The worker script contents (cached for further use).
144
+ * @property _workerScript
145
+ * @type {String}
146
+ * @private
147
+ * @default null
148
+ */
149
+ WebWorker.prototype._workerScript = null;
150
+
151
+ /**
152
+ * The worker URL (if provided)
153
+ * @property _workerUrl
154
+ * @type {String}
155
+ * @private
156
+ * @default null
157
+ */
158
+ WebWorker.prototype._workerUrl = null;
159
+
160
+
161
+ /**
162
+ * The main constructor calls this method. This method
163
+ * was moved out of the main constructor to keep the
164
+ * code clean.
165
+ * @method _constructor
166
+ * @param {Object} opts* Arguments to the main constructor
167
+ * @private
168
+ */
169
+ WebWorker.prototype._constructor = function (opts) {
170
+ var $scriptElement = null,
171
+ scriptContents = null,
172
+ workerUrl = null;
173
+
174
+ Object.defineProperty(this, '_$', {
175
+ "configurable": false,
176
+ "enumerable": true,
177
+ "value": $(this),
178
+ "writable": false
179
+ });
180
+
181
+ opts = opts || null;
182
+
183
+ if (opts === null) {
184
+ this.throwError(Error.INVALID_ARGUMENTS, null, true);
185
+ }
186
+
187
+ if (typeof opts === 'string') {
188
+ let strtr = "";
189
+ opts = $.trim(opts);
190
+
191
+ try {
192
+ if (window.hasOwnProperty(opts) && typeof window[opts] === "string") {
193
+ strtr = window[opts];
194
+ this._workerScript = strtr;
195
+ } else {
196
+ $scriptElement = $(opts);
197
+ }
198
+ } catch (err) {
199
+ // Cannot be resolved as a selector
200
+ }
201
+
202
+
203
+ if ($scriptElement !== null && $scriptElement.length > 0) {
204
+ // Matching script element found
205
+ // Cache its contents
206
+ scriptContents = $scriptElement.text();
207
+ this._workerScript = scriptContents;
208
+ } else if (strtr == "") {
209
+ workerUrl = opts;
210
+ }
211
+ }
212
+
213
+ this._workerUrl = workerUrl;
214
+
215
+ this._callbackStack = {
216
+ "error": [],
217
+ "loading": [],
218
+ "loaded": [],
219
+ "starting": [],
220
+ "started": [],
221
+ "terminating": [],
222
+ "terminated": []
223
+ };
224
+
225
+ this._assignEventHandlers();
226
+
227
+ this._state = State.INITIALIZED;
228
+
229
+ this.trigger(Event.INITIALIZED);
230
+
231
+ return;
232
+ };
233
+
234
+ /**
235
+ * Initializes the log array when invoked.
236
+ * @method _initLog
237
+ * @private
238
+ */
239
+ WebWorker.prototype._initLog = function () {
240
+ this._log = [];
241
+ return this._log;
242
+ };
243
+
244
+ /**
245
+ * Returns the worker URL if one was provided during object instantiation.
246
+ * @method getUrl
247
+ * @return {String} The worker URL, `null` otherwise.
248
+ */
249
+ WebWorker.prototype.getUrl = function () {
250
+ return this._workerUrl;
251
+ };
252
+
253
+ /**
254
+ * Returns the worker blob URL if one has been generated.
255
+ * @method getBlobUrl
256
+ * @return {String} The worker blob URL, `null` otherwise.
257
+ */
258
+ WebWorker.prototype.getBlobUrl = function () {
259
+ return this._workerBlobUrl;
260
+ };
261
+
262
+ /**
263
+ * Returns the worker blob URL if one has been generated.
264
+ * @method getBlobUrl
265
+ * @return {Array} The log array of the worker.
266
+ */
267
+ WebWorker.prototype.getLog = function () {
268
+ var log;
269
+ log = this._log || this._initLog();
270
+ return log;
271
+ };
272
+
273
+ /**
274
+ * Returns the native browser worker instance if one has been generated.
275
+ * @method getNativeWorker
276
+ * @return {Object} The native worker instance, `null` otherwise.
277
+ */
278
+ WebWorker.prototype.getNativeWorker = function () {
279
+ return this._nativeWorker;
280
+ };
281
+
282
+ /**
283
+ * Returns the current state of the worker as an integer.
284
+ * Refer to {{#crossLink "WebWorker/State:property"}}{{/crossLink}} for state values.
285
+ * @method getState
286
+ * @return {Number} An integer representing the current state of the worker.
287
+ */
288
+ WebWorker.prototype.getState = function () {
289
+ return this._state;
290
+ };
291
+
292
+ /**
293
+ * Returns whether the worker script is loading.
294
+ * @method isLoading
295
+ * @return {Boolean} Returns `true` if the worker script is loading, `false` otherwise.
296
+ */
297
+ WebWorker.prototype.isLoading = function () {
298
+ return this.getState() === State.LOADING;
299
+ };
300
+
301
+ /**
302
+ * Returns whether the worker script is loaded.
303
+ * @method isLoaded
304
+ * @return {Boolean} Returns `true` if the worker script is loaded, `false` otherwise.
305
+ */
306
+ WebWorker.prototype.isLoaded = function () {
307
+ var state = this.getState();
308
+ return state >= State.LOADED && !this.isTerminated();
309
+ };
310
+
311
+ /**
312
+ * Returns whether the worker script is starting.
313
+ * @method isStarting
314
+ * @return {Boolean} Returns `true` if the worker script is starting, `false` otherwise.
315
+ */
316
+ WebWorker.prototype.isStarting = function () {
317
+ return this.getState() === State.STARTING;
318
+ };
319
+
320
+ /**
321
+ * Returns whether the worker script is started.
322
+ * @method isStarted
323
+ * @return {Boolean} Returns `true` if the worker script is started, `false` otherwise.
324
+ */
325
+ WebWorker.prototype.isStarted = function () {
326
+ var state = this.getState();
327
+ return state >= State.STARTED && !this.isTerminated();
328
+ };
329
+
330
+ /**
331
+ * Returns whether the worker script is terminating.
332
+ * @method isTerminating
333
+ * @return {Boolean} Returns `true` if the worker script is terminating, `false` otherwise.
334
+ */
335
+ WebWorker.prototype.isTerminating = function () {
336
+ return this.getState() === State.TERMINATING;
337
+ };
338
+
339
+ /**
340
+ * Returns whether the worker script is terminated.
341
+ * @method isTerminated
342
+ * @return {Boolean} Returns `true` if the worker script is terminated, `false` otherwise.
343
+ */
344
+ WebWorker.prototype.isTerminated = function () {
345
+ return this.getState() === State.TERMINATED;
346
+ };
347
+
348
+ /**
349
+ * Triggers the instance to load the worker script and generate a native browser worker.
350
+ * @method load
351
+ * @chainable
352
+ */
353
+ WebWorker.prototype.load = function () {
354
+ var self = this,
355
+ workerUrl = null,
356
+ onScriptLoaded = null;
357
+
358
+ if (self.isLoading() || self.isLoaded()) {
359
+ return self;
360
+ }
361
+
362
+ // Trigger event
363
+ self.trigger(Event.WORKER_LOADING);
364
+
365
+ workerUrl = self.getUrl() || null;
366
+ onScriptLoaded = function () {
367
+ var blob = null,
368
+ scriptContents = null;
369
+
370
+ scriptContents = self._workerScript;
371
+ scriptContents = WebWorker._workerScriptWrapper.replace(/\{\{main-function\}\}/g, scriptContents);
372
+
373
+ blob = new window.Blob([scriptContents], {
374
+ "type": "text/javascript"
375
+ });
376
+ self._workerBlobUrl = window.URL.createObjectURL(blob);
377
+
378
+ self._createWorker();
379
+ };
380
+
381
+ if (workerUrl === null) {
382
+ // Script already available
383
+ onScriptLoaded();
384
+ } else {
385
+ // Ajax request
386
+ $.ajax({
387
+ "async": true,
388
+ "url": workerUrl,
389
+ "dataType": 'text',
390
+ "crossDomain": true,
391
+ "success": function (responseText) {
392
+ self._workerScript = responseText;
393
+ onScriptLoaded();
394
+ },
395
+ "error": function () {
396
+ self.throwError(Error.WORKER_DID_NOT_LOAD, arguments);
397
+ }
398
+ });
399
+ }
400
+
401
+ return self;
402
+ };
403
+
404
+ /**
405
+ * Concatenates log messages to internal log array.
406
+ * @method log
407
+ * @param {Mixed} data The data that you wish to be logged.
408
+ * @chainable
409
+ */
410
+ WebWorker.prototype.log = function (data) {
411
+ var log;
412
+
413
+ log = this._log || this._initLog();
414
+ log.push(data);
415
+
416
+ return this;
417
+ };
418
+
419
+ /**
420
+ * Generates the native browser worker and attaches the message parser to it.
421
+ * @method _createWorker
422
+ * @private
423
+ * @chainable
424
+ */
425
+ WebWorker.prototype._createWorker = function () {
426
+ this._nativeWorker = new NativeWorker(this.getBlobUrl());
427
+ this._attachMessageParser();
428
+ return this;
429
+ };
430
+
431
+ /**
432
+ * Assigns the basic internal event handlers to the worker instance.
433
+ * @method _assignEventHandlers
434
+ * @private
435
+ * @chainable
436
+ */
437
+ WebWorker.prototype._assignEventHandlers = function () {
438
+ function getCallbackStackExecutor(stackId) {
439
+ return function () {
440
+ var callbackStack = this._callbackStack[stackId],
441
+ stateId,
442
+ curCallback;
443
+
444
+ stateId = stackId.toUpperCase();
445
+
446
+ if (stateId in State) {
447
+ // Set the current state of the worker
448
+ // as long as it's a valid state.
449
+ this._state = State[stateId];
450
+ }
451
+
452
+ /* eslint no-cond-assign:0 */
453
+ while (curCallback = callbackStack.pop()) {
454
+ curCallback.apply(this, arguments);
455
+ }
456
+ };
457
+ }
458
+
459
+ this.on(Event.ERROR, getCallbackStackExecutor('error'));
460
+ this.on(Event.WORKER_LOADING, getCallbackStackExecutor('loading'));
461
+ this.on(Event.WORKER_LOADED, getCallbackStackExecutor('loaded'));
462
+ this.on(Event.WORKER_STARTING, getCallbackStackExecutor('starting'));
463
+ this.on(Event.WORKER_STARTED, getCallbackStackExecutor('started'));
464
+ this.on(Event.WORKER_TERMINATING, getCallbackStackExecutor('terminating'));
465
+ this.on(Event.WORKER_TERMINATED, getCallbackStackExecutor('terminated'));
466
+
467
+ return this;
468
+ };
469
+
470
+ function getCallbackStackUpdater(stackId) {
471
+ return function (callback) {
472
+ var callbackStack = this._callbackStack[stackId];
473
+
474
+ if (typeof callback === 'function') {
475
+ callbackStack.push(callback);
476
+ }
477
+
478
+ return this;
479
+ };
480
+ }
481
+
482
+ /**
483
+ * Hook for the error event.
484
+ * @method error
485
+ * @param {Function} callback The callback function that get's called when the event get's triggered.
486
+ * @chainable
487
+ */
488
+ WebWorker.prototype.error = getCallbackStackUpdater('error');
489
+
490
+ /**
491
+ * Hook for the loading event.
492
+ * @method loading
493
+ * @param {Function} callback The callback function that get's called when the event get's triggered.
494
+ * @chainable
495
+ */
496
+ WebWorker.prototype.loading = getCallbackStackUpdater('loading');
497
+
498
+ /**
499
+ * Hook for the loaded event.
500
+ * @method loaded
501
+ * @param {Function} callback The callback function that get's called when the event get's triggered.
502
+ * @chainable
503
+ */
504
+ WebWorker.prototype.loaded = getCallbackStackUpdater('loaded');
505
+
506
+ /**
507
+ * Hook for the starting event.
508
+ * @method starting
509
+ * @param {Function} callback The callback function that get's called when the event get's triggered.
510
+ * @chainable
511
+ */
512
+ WebWorker.prototype.starting = getCallbackStackUpdater('starting');
513
+
514
+ /**
515
+ * Hook for the started event.
516
+ * @method started
517
+ * @param {Function} callback The callback function that get's called when the event get's triggered.
518
+ * @chainable
519
+ */
520
+ WebWorker.prototype.started = getCallbackStackUpdater('started');
521
+
522
+ /**
523
+ * Hook for the terminating event.
524
+ * @method terminating
525
+ * @param {Function} callback The callback function that get's called when the event get's triggered.
526
+ * @chainable
527
+ */
528
+ WebWorker.prototype.terminating = getCallbackStackUpdater('terminating');
529
+
530
+ /**
531
+ * Hook for the terminated event.
532
+ * @method terminated
533
+ * @param {Function} callback The callback function that get's called when the event get's triggered.
534
+ * @chainable
535
+ */
536
+ WebWorker.prototype.terminated = getCallbackStackUpdater('terminated');
537
+
538
+ /**
539
+ * Start the worker (if it has loaded). Any arguments passed to this method
540
+ * will be passed internally to the worker script.
541
+ * <br />
542
+ * Note that you should start the worker only AFTER it has loaded. If you call this
543
+ * method before the worker has loaded it will fail silently.
544
+ * @method start
545
+ * @param {Mixed} args* Arguments to be passed internally to the worker
546
+ * script.<br />
547
+ * Please ensure that arguments are basic
548
+ * objects that can be cloned by the browser
549
+ * for `postMessage` interface.
550
+ * @chainable
551
+ */
552
+ WebWorker.prototype.start = function () {
553
+ var args = null;
554
+
555
+ if (this.isStarting() || this.isStarted()) {
556
+ return this;
557
+ }
558
+
559
+ args = slice.call(arguments);
560
+
561
+ if (!this.isLoaded()) {
562
+ this.on(Event.WORKER_LOADED, function () {
563
+ this.start.apply(this, args);
564
+ });
565
+
566
+ this.load();
567
+ return this;
568
+ }
569
+
570
+ this.trigger(Event.WORKER_STARTING);
571
+
572
+ this.sendMessage(Action.START, args);
573
+
574
+ return this;
575
+ };
576
+
577
+ /**
578
+ * This method acts as the main communications interface with the worker and is used internally.
579
+ * <br /><br />
580
+ * **You are recommended to use the trigger interface _ONLY_ as it is the most reliable mode of
581
+ * communication between the instance and the actual worker.**
582
+ * <br /><br />
583
+ * If you do understand the risks, you are welcome to use this method (a reason why it is
584
+ * left public instead of private).
585
+ * <br /><br />
586
+ * It's basic usage is very simple. Specify the `action`, which is the
587
+ * method name to call _inside_ the worker script, and the arguments to pass in. The worker
588
+ * does the rest of the heavy lifting.
589
+ * <br /><br />
590
+ * Note: This method is NOT synchronous and will not warn you of any errors.
591
+ * @method sendMessage
592
+ * @param {String} action The name of the method within the worker script to call.
593
+ * @param {Array} args The array of arguments to pass to the worker. Ensure that this data
594
+ * can be cloned by the browser for the `postMessage` interface.
595
+ * @chainable
596
+ */
597
+ WebWorker.prototype.sendMessage = function (action, args) {
598
+ var nativeWorker = null,
599
+ message = null;
600
+
601
+ action = action || null;
602
+ args = args || null;
603
+
604
+ if (action === null) {
605
+ return this;
606
+ }
607
+
608
+ message = {
609
+ "__isWebWorkerMsg": true
610
+ };
611
+
612
+ message.action = action;
613
+ message.args = args;
614
+
615
+ nativeWorker = this.getNativeWorker();
616
+
617
+ if (nativeWorker) {
618
+ nativeWorker.postMessage(message);
619
+ }
620
+
621
+ return this;
622
+ };
623
+
624
+ /**
625
+ * Attaches the message parser to the native worker. This allows the worker to hook into
626
+ * `postMessage` API and set up a communications protocol between the instance and the
627
+ * native browser worker.
628
+ * @method _attachMessageParser
629
+ * @private
630
+ * @chainable
631
+ */
632
+ WebWorker.prototype._attachMessageParser = function () {
633
+ var $nativeWorker = null;
634
+
635
+ $nativeWorker = $(this.getNativeWorker());
636
+
637
+ $nativeWorker.on('message', $.proxy(function (event) {
638
+ var originalEvent = event.originalEvent || event,
639
+ msg = originalEvent.data,
640
+ action = null,
641
+ args = null;
642
+
643
+ if (typeof msg === 'object'
644
+ && '__isWebWorkerMsg' in msg
645
+ && msg.__isWebWorkerMsg) {
646
+ action = msg.action;
647
+ args = msg.args;
648
+
649
+ this[action].apply(this, args);
650
+ }
651
+
652
+ return;
653
+ }, this));
654
+
655
+ $nativeWorker.on('error', $.proxy(this.throwError, this));
656
+
657
+ return this;
658
+ };
659
+
660
+ /**
661
+ * This method triggers the worker to terminate. Terminating is ASYNCHRONOUS. This method
662
+ * sets the ball rolling. It sends the message to the worker which may have a
663
+ * `terminateHandler` that can do some cleanup operations before control is passed back
664
+ * to the instance and the final terminate (`terminateNow`) is executed.
665
+ * @method terminate
666
+ * @chainable
667
+ */
668
+ WebWorker.prototype.terminate = function () {
669
+ var nativeWorker = this.getNativeWorker() || null;
670
+
671
+ if (nativeWorker) {
672
+ this.trigger(Event.WORKER_TERMINATING);
673
+ this.sendMessage(Action.TERMINATE, slice.call(arguments));
674
+ }
675
+
676
+ return this;
677
+ };
678
+
679
+ /**
680
+ * Terminates the worker _immediately_.
681
+ * <br />
682
+ * This method can be used to terminate the worker
683
+ * _immediately_, instead of waiting for the asynchronous terminate cycle to complete.
684
+ * <br />
685
+ * It is also called internally once the asynchronous `terminate` cycle
686
+ * passes control back to the instance to execute the final terminate.
687
+ * @method terminateNow
688
+ * @chainable
689
+ */
690
+ WebWorker.prototype.terminateNow = function () {
691
+ var nativeWorker = null;
692
+
693
+ if (!this.isTerminating()) {
694
+ this.trigger(Event.WORKER_TERMINATING);
695
+ }
696
+
697
+ nativeWorker = this.getNativeWorker() || null;
698
+
699
+ if (nativeWorker) {
700
+ nativeWorker.terminate();
701
+ this._nativeWorker = null;
702
+ this.trigger(Event.WORKER_TERMINATED);
703
+ }
704
+
705
+ return this;
706
+ };
707
+
708
+ /**
709
+ * Used to bind event listeners to the worker. Internally it uses the jQuery `.on` method.
710
+ * @method on
711
+ * @param {Mixed} args* Lookup the jQuery `.on` API for argument list.
712
+ * @chainable
713
+ */
714
+ WebWorker.prototype.on = function () {
715
+ var $worker = this._$;
716
+ $worker.on.apply($worker, arguments);
717
+ return this;
718
+ };
719
+
720
+ /**
721
+ * Used to bind event listeners to the worker for single execution. Internally it uses the jQuery `.one` method.
722
+ * @method one
723
+ * @param {Mixed} args* Lookup the jQuery `.one` API for argument list.
724
+ * @chainable
725
+ */
726
+ WebWorker.prototype.one = function () {
727
+ var $worker = this._$;
728
+ $worker.one.apply($worker, arguments);
729
+ return this;
730
+ };
731
+
732
+ /**
733
+ * Used to unbind event listeners from the worker. Internally it uses the jQuery `.off` method.
734
+ * @method off
735
+ * @param {Mixed} args* Lookup the jQuery `.off` API for argument list.
736
+ * @chainable
737
+ */
738
+ WebWorker.prototype.off = function () {
739
+ var $worker = this._$;
740
+
741
+ $worker.off.apply($worker, arguments);
742
+ this._assignEventHandlers();
743
+
744
+ return this;
745
+ };
746
+
747
+ /**
748
+ * Used to trigger events on the worker. Internally it uses a combination of the jQuery `.trigger` method
749
+ * and a custom trigger for triggering events on the native worker script.
750
+ * <br /><br />
751
+ * If an event type is recognized as an internal event type, the trigger is executed on the base instance
752
+ * using jQuery. Otherwise the `.sendMessage` API is used to pass the trigger to the worker script.
753
+ * @method trigger
754
+ * @param {Object} event This can be a string `eventType` or an object `event`. Please ensure that if using
755
+ * object events, that they are clonable by the browser for the `postMessage` API.
756
+ * @chainable
757
+ */
758
+ WebWorker.prototype.trigger = function (event) {
759
+ var passedEventString = false,
760
+ eventType = null,
761
+ eventArgs = null;
762
+
763
+ if (typeof event === 'object') {
764
+ eventType = event.type || null;
765
+ }
766
+
767
+ if (typeof event === 'string') {
768
+ eventType = event || null;
769
+ passedEventString = true;
770
+ }
771
+
772
+ if (eventType === null) {
773
+ return this;
774
+ }
775
+
776
+ if (eventType in EventMap) {
777
+ if (passedEventString) {
778
+ event = new $.Event(eventType);
779
+ }
780
+ event.worker = this;
781
+ eventArgs = [event];
782
+ if (arguments.length > 1) {
783
+ eventArgs = eventArgs.concat(slice.call(arguments, 1));
784
+ }
785
+
786
+ this._triggerSelf.apply(this, eventArgs);
787
+ return this;
788
+ }
789
+
790
+ if (passedEventString) {
791
+ event = {
792
+ "type": eventType,
793
+ "data": arguments[1] ? arguments[1] : {}
794
+ };
795
+ }
796
+ eventArgs = [event];
797
+
798
+ this.sendMessage(Action.TRIGGER_SELF, eventArgs);
799
+
800
+ return this;
801
+ };
802
+
803
+ /**
804
+ * You may want to trigger an event on the base worker instance irrespective of it's event type. Use this
805
+ * method to achieve the same.
806
+ * @method triggerSelf
807
+ * @param {Object} event This can be a string `eventType` or an object `event`.
808
+ * @chainable
809
+ */
810
+ WebWorker.prototype.triggerSelf = function (event) {
811
+ var eventType = null,
812
+ eventArgs = null;
813
+
814
+ event = event || null;
815
+
816
+ if (event === null) {
817
+ return this;
818
+ }
819
+
820
+ if (typeof event === 'string') {
821
+ eventType = event;
822
+ event = new $.Event(eventType);
823
+ }
824
+
825
+ event.originalEvent = event.originalEvent || event;
826
+ event.worker = this;
827
+ eventArgs = [event];
828
+ if (arguments.length > 1) {
829
+ eventArgs = eventArgs.concat(slice.call(arguments, 1));
830
+ }
831
+
832
+ this._triggerSelf.apply(this, eventArgs);
833
+
834
+ return this;
835
+ };
836
+
837
+ /**
838
+ * Internal method to trigger events on the base worker instance.
839
+ * @method _triggerSelf
840
+ * @private
841
+ * @param {Mixed} args* Arguments to `trigger`.
842
+ * @chainable
843
+ */
844
+ WebWorker.prototype._triggerSelf = function () {
845
+ var $worker = null;
846
+
847
+ $worker = this._$;
848
+ $worker.trigger.apply($worker, arguments);
849
+
850
+ return this;
851
+ };
852
+
853
+ /**
854
+ * Throw error on the worker instance. This internally triggers the `error` event.
855
+ * Optionally you can also make this method throw an exception.
856
+ * <br />
857
+ * This method also internally updates the `lastError` value on the instance
858
+ * and static properties.
859
+ * @method throwError
860
+ * @param {String} error Error string that describes the error.
861
+ * @param {Mixed} [data] Data that is to be associated with the error event.
862
+ * @param {Boolean} [throwException] Set to `true` if you want to throw an exception in addition to the error event.
863
+ * @chainable
864
+ */
865
+ WebWorker.prototype.throwError = function (error, data, throwException) {
866
+ var errorData = null;
867
+
868
+ error = error || Error.UNKNOWN;
869
+
870
+ if (typeof error === 'object') {
871
+ error = error.originalEvent || error;
872
+ errorData = error.data || null;
873
+ }
874
+
875
+ errorData = typeof data === 'undefined' ? errorData : data;
876
+ throwException = throwException || false;
877
+
878
+ this._lastError = error;
879
+ WebWorker._lastError = error;
880
+
881
+ if ('_triggerError' in this) {
882
+ this._triggerError(error, data, throwException);
883
+ }
884
+
885
+ if (throwException) {
886
+ throw new window.Error(error);
887
+ }
888
+
889
+ return this;
890
+ };
891
+
892
+ /**
893
+ * Internal method used to trigger the `error` event on the instance.
894
+ * @method _triggerError
895
+ * @private
896
+ * @param {String} error Error string that describes the error.
897
+ * @param {Mixed} [data] Data that is to be associated with the error event.
898
+ * @param {Boolean} [throwException] Set to `true` if you want to throw an exception in addition to the error event.
899
+ * @chainable
900
+ */
901
+ WebWorker.prototype._triggerError = function (error, data, throwException) {
902
+ var errorEvent = null;
903
+
904
+ errorEvent = new $.Event(Event.ERROR);
905
+ errorEvent.message = this.getLastError();
906
+ errorEvent.errorData = data;
907
+ errorEvent.throwsException = !!throwException;
908
+
909
+ this.trigger(errorEvent);
910
+ return this;
911
+ };
912
+
913
+ /**
914
+ * Returns the last error that was trigger on this worker instance.
915
+ * @method getLastError
916
+ * @return {String} The last error message that was thrown on this worker instance.
917
+ */
918
+ WebWorker.prototype.getLastError = function () {
919
+ return this._lastError;
920
+ };
921
+
922
+
923
+ // Static
924
+
925
+ /**
926
+ * The last error encountered across all WebWorker instances.
927
+ * @property _lastError
928
+ * @private
929
+ * @static
930
+ * @type {String}
931
+ */
932
+ WebWorker._lastError = null;
933
+
934
+ /**
935
+ * List of possible states the worker instance can be in. The worker instance
936
+ * can only be in one of these states at any given time.
937
+ * @property {Object} State
938
+ * @property {Number} State.INITIALIZED 0
939
+ * @property {Number} State.LOADING 1
940
+ * @property {Number} State.LOADED 2
941
+ * @property {Number} State.STARTING 3
942
+ * @property {Number} State.STARTED 4
943
+ * @property {Number} State.TERMINATING 5
944
+ * @property {Number} State.TERMINATED 6
945
+ *
946
+ * @static
947
+ */
948
+ State = {
949
+ "INITIALIZED": 0,
950
+ "LOADING": 1,
951
+ "LOADED": 2,
952
+ "STARTING": 3,
953
+ "STARTED": 4,
954
+ "TERMINATING": 5,
955
+ "TERMINATED": 6
956
+ };
957
+ WebWorker.State = State;
958
+
959
+ /**
960
+ * List of pre-defined actions for the WebWorker instance.
961
+ *
962
+ * @property {Object} Action
963
+ * @property {String} Action.START 'start'
964
+ * @property {String} Action.TERMINATE 'terminate'
965
+ * @property {String} Action.TERMINATE_NOW 'terminateNow'
966
+ * @property {String} Action.TRIGGER 'trigger'
967
+ * @property {String} Action.TRIGGER_SELF 'triggerSelf'
968
+ *
969
+ * @static
970
+ */
971
+ Action = {
972
+ "LOG": 'log',
973
+ "START": 'start',
974
+ "TERMINATE": 'terminate',
975
+ "TERMINATE_NOW": 'terminateNow',
976
+ "TRIGGER": 'trigger',
977
+ "TRIGGER_SELF": 'triggerSelf'
978
+ };
979
+ WebWorker.Action = Action;
980
+
981
+ /**
982
+ * List of pre-defined events for the WebWorker instance.
983
+ *
984
+ * @property {Object} Event
985
+ * @property {String} Event.INITIALIZED 'webworker:initialized'
986
+ * @property {String} Event.ERROR 'webworker:error'
987
+ * @property {String} Event.WORKER_LOADING 'webworker:worker-loading'
988
+ * @property {String} Event.WORKER_LOADED 'webworker:worker-loaded'
989
+ * @property {String} Event.WORKER_STARTING 'webworker:worker-starting'
990
+ * @property {String} Event.WORKER_STARTED 'webworker:worker-started'
991
+ * @property {String} Event.WORKER_TERMINATING 'webworker:worker-terminating'
992
+ * @property {String} Event.WORKER_TERMINATED 'webworker:worker-terminated'
993
+ *
994
+ * @static
995
+ */
996
+ Event = {
997
+ "INITIALIZED": 'initialized',
998
+ "ERROR": 'error',
999
+
1000
+ "WORKER_LOADING": 'worker-loading',
1001
+ "WORKER_LOADED": 'worker-loaded',
1002
+
1003
+ "WORKER_STARTING": 'worker-starting',
1004
+ "WORKER_STARTED": 'worker-started',
1005
+
1006
+ "WORKER_TERMINATING": 'worker-terminating',
1007
+ "WORKER_TERMINATED": 'worker-terminated'
1008
+ };
1009
+ WebWorker.Event = Event;
1010
+
1011
+ /**
1012
+ * Reverse mapping of pre-defined events for faster lookup.
1013
+ * @property EventMap
1014
+ * @static
1015
+ * @type {Object}
1016
+ */
1017
+ EventMap = {};
1018
+ WebWorker.EventMap = EventMap;
1019
+
1020
+ // Add eventPrefix to all event types
1021
+ for (key in Event) {
1022
+ Event[key] = eventPrefix + Event[key];
1023
+ EventMap[Event[key]] = key;
1024
+ }
1025
+
1026
+ /**
1027
+ * Pre-defined list of errors for the WebWorker instance.
1028
+ *
1029
+ * @property {Object} Error
1030
+ * @property {String} Error.UNKNOWN "An unknown error occured."
1031
+ * @property {String} Error.INVALID_ARGUMENTS "Invalid arguments were supplied to this method."
1032
+ * @property {String} Error.WORKER_DID_NOT_LOAD "Unable to load worker."
1033
+ *
1034
+ * @static
1035
+ */
1036
+ Error = {
1037
+ "UNKNOWN": "An unknown error occured.",
1038
+ "INVALID_ARGUMENTS": "Invalid arguments were supplied to this method.",
1039
+ "WORKER_DID_NOT_LOAD": "Unable to load worker."
1040
+ };
1041
+ WebWorker.Error = Error;
1042
+
1043
+ /**
1044
+ * This is the worker script wrapper that is internally used by the base class
1045
+ * to establish communication from within the worker.
1046
+ * @property _workerScriptWrapper
1047
+ * @type {String}
1048
+ * @private
1049
+ */
1050
+ WebWorker._workerScriptWrapper = 'var e=null,t=null,n=null,r={};e={{state-data}};t={{action-data}};n={{event-data}};self._callbackStack=null;self._listeners=r;self._isTerminating=false;self._state=null;self.State=e;self.Action=t;self.Event=n;self._assignEventHandlers=function(){function t(t){return function(){var n=self._callbackStack[t],r,i;r=t.toUpperCase();if(r in e){self._state=e[r]}while(i=n.pop()){i.apply(self,arguments)}}}self.on(n.WORKER_TERMINATING,t("terminating"));return self};self.getState=function(){return this._state};self.isInitialized=function(){var e=self.getState();return e!==null&&e>=0};self.isTerminating=function(){return self.getState()===e.TERMINATING};self._main=function(){var startArgs=arguments;{{main-function}};return self};self._init=function(){self._callbackStack={terminating:[]};self._assignEventHandlers();self._state=e.INITIALIZED;self.trigger(n.WORKER_LOADED);return self};self.start=function(){if(!self.isInitialized()){return self}self.triggerSelf(n.WORKER_STARTING);self._main.apply(self,arguments);self.triggerSelf(n.WORKER_STARTED);self.trigger(n.WORKER_STARTED);return self};self.log=function(e){self.sendMessage(t.LOG,[e]);return self};self.on=function(e,t){e+="";t=t||null;if(typeof t!=="function"){return self}if(!(e in r)){r[e]=[]}r[e].push(t);return self};self.one=function(e,t){var n=null;n=function(){t.apply(this,arguments);self.off(e,n);return};self.on(e,n);return};self.off=function(e,t){var n=null;e=e||null;t=t||null;if(e===null&&t===null){for(n in r){delete r[n]}self._assignEventHandlers();return self}self._removeListenerFromEventType(e,t);return self};self._removeListenerFromEventType=function(e,t){var n=r[e],i=0;t=t||null;if(t===null){r[e]=[];return self}for(;i<n.length;i++){if(n[i]===t){n.splice(i,1);i--}}return self};self.terminating=function(e){var t=self._callbackStack.terminating;if(typeof e==="function"){t.push(e)}return self};self.trigger=function(e,n){var r=null;e=e||null;if(e===null){return self}if(typeof e==="string"){r=e||null;e={type:r}}else if(typeof e==="object"){r=e.type||null;n=e.data}if(r===null){return self}e.data=n;self.sendMessage(t.TRIGGER_SELF,[e]);return self};self.triggerSelf=function(e,t){var n=null,i=null,s=null,o=null,u=0;e=e||null;if(e===null){return this}if(typeof e==="string"){n=e||null;e={type:e}}else if(typeof e==="object"){n=e.type||null;t=e.data}if(n===null){return this}e.data=t;i=r[n]||null;if(i===null){return this}s=i.length;for(u=0;u<s;u++){o=i[u];o.apply(this,[e]);if(s!==i.length){u--;s=i.length}}return this};self.sendMessage=function(e,t){var n=null;e=e||null;t=t||[];if(e===null){return self}n={__isWebWorkerMsg:true};n.action=e;n.args=t;self.postMessage(n);return self};self.terminate=function(e){e=!!e;if(!self.isTerminating()){self._setTerminatingStatus();self.triggerSelf(n.WORKER_TERMINATING);self.trigger(n.WORKER_TERMINATING)}self.sendMessage(t.TERMINATE_NOW,[]);if(e){self._nativeClose()}return self};self.terminateNow=function(){return self.terminate(true)};self._setTerminatingStatus=function(){self._state=e.TERMINATING;return self};self._nativeClose=self.close;self.close=self.terminate;self.addEventListener("message",function(e){var t=e.originalEvent||e,n=t.data,r=null,i=null;if(typeof n==="object"&&"__isWebWorkerMsg"in n&&n.__isWebWorkerMsg){r=n.action;i=n.args;self[r].apply(self,i)}},false);self._init()';
1051
+
1052
+ WebWorker._workerScriptWrapper = WebWorker._workerScriptWrapper.replace(/\{\{state-data\}\}/g, JSON.stringify(State))
1053
+ .replace(/\{\{action-data\}\}/g, JSON.stringify(Action))
1054
+ .replace(/\{\{event-data\}\}/g, JSON.stringify(Event));
1055
+
1056
+
1057
+ /**
1058
+ * Retrieves the last error thrown by any WebWorker instance.
1059
+ * @method getLastError
1060
+ * @static
1061
+ * @return {String} The last error string thrown by any WebWorker instance.
1062
+ */
1063
+ WebWorker.getLastError = WebWorker.prototype.getLastError;
1064
+
1065
+ /**
1066
+ * A jQuery-like method to assign the WebWorker class under a different context and className.
1067
+ * You may either provide the context and class name as params
1068
+ * OR use the return value to set it
1069
+ * OR both.
1070
+ * @method noConflict
1071
+ * @static
1072
+ * @param {Object} context The context to which the WebWorker instance needs to be associated.
1073
+ * @param {String} className The class name with which the WebWorker instance will be identified.
1074
+ * @return {String} The last error string thrown by any WebWorker instance.
1075
+ */
1076
+ WebWorker.noConflict = function (context, className) {
1077
+ context = context || null;
1078
+ className = className || null;
1079
+
1080
+ if (defaultContext[defaultClassName] === WebWorker) {
1081
+ delete defaultContext[defaultClassName];
1082
+ if (_WebWorker) {
1083
+ defaultContext[defaultClassName] = _WebWorker;
1084
+ }
1085
+ }
1086
+
1087
+ if (context && className) {
1088
+ context[className] = WebWorker;
1089
+ }
1090
+
1091
+ return WebWorker;
1092
+ };
1093
+
1094
+ context[className] = WebWorker;
1095
+
1096
+ return;
1097
+ })(jQuery);