@breakside/jskit 2025.8.0 → 2025.10.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.
- package/Frameworks/DOM.jsframework/Info.json +2 -2
- package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
- package/Frameworks/FontKit.jsframework/Info.json +2 -2
- package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
- package/Frameworks/Foundation.jsframework/Info.json +2 -2
- package/Frameworks/Foundation.jsframework/JS/JSAttributedString.js +6 -0
- package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
- package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
- package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
- package/Info.json +2 -2
- package/Node/io.breakside.jskit-bundle.js +2 -2
- package/Root/Frameworks/APIKit/Info.yaml +1 -1
- package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
- package/Root/Frameworks/AuthKit/Info.yaml +1 -1
- package/Root/Frameworks/CSSOM/Info.yaml +1 -1
- package/Root/Frameworks/ChartKit/Info.yaml +1 -1
- package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
- package/Root/Frameworks/DBKit/Info.yaml +1 -1
- package/Root/Frameworks/DOM/Info.yaml +1 -1
- package/Root/Frameworks/Dispatch/Info.yaml +1 -1
- package/Root/Frameworks/FontKit/Info.yaml +1 -1
- package/Root/Frameworks/Foundation/Info.yaml +1 -1
- package/Root/Frameworks/Foundation/JSAttributedString.js +6 -0
- package/Root/Frameworks/ImageKit/Info.yaml +1 -1
- package/Root/Frameworks/MediaKit/Info.yaml +1 -1
- package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
- package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
- package/Root/Frameworks/PDFKit/Info.yaml +1 -1
- package/Root/Frameworks/QRKit/Info.yaml +1 -1
- package/Root/Frameworks/SearchKit/Info.yaml +1 -1
- package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
- package/Root/Frameworks/ServerKit/Info.yaml +1 -1
- package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
- package/Root/Frameworks/TestKit/Info.yaml +1 -1
- package/Root/Frameworks/UIKit/Info.yaml +1 -1
- package/Root/Frameworks/UIKit/UIAnimationTransaction.js +5 -4
- package/Root/Frameworks/UIKit/UIApplication.js +1 -1
- package/Root/Frameworks/UIKit/UIHTMLApplication.js +8 -4
- package/Root/Frameworks/UIKit/UIHTMLContentEditableTextInputManager.js +23 -15
- package/Root/Frameworks/UIKit/UIHTMLOpenPanel.js +19 -4
- package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +49 -2
- package/Root/Frameworks/UIKit/UIListView.js +1 -1
- package/Root/Frameworks/UIKit/UIPopupWindow.js +1 -20
- package/Root/Frameworks/UIKit/UITextEditor.js +6 -0
- package/Root/Frameworks/UIKit/UITextField.js +3 -1
- package/Root/Frameworks/UIKit/UIWindow.js +3 -0
- package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
- package/Root/Templates/html/${PROJECT_NAME}/www/HTMLAppBootstrapper.js +198 -89
- package/Root/Templates/html/${PROJECT_NAME}/www/index.html +1 -10
- package/package.json +1 -1
|
@@ -497,6 +497,9 @@ JSClass("UIHTMLWindowServer", UIWindowServer, {
|
|
|
497
497
|
},
|
|
498
498
|
|
|
499
499
|
_correctedEventKey: function(key){
|
|
500
|
+
if (key === null || key === undefined || key === ""){
|
|
501
|
+
return "Unidentified";
|
|
502
|
+
}
|
|
500
503
|
if (key === 'Left'){
|
|
501
504
|
return 'ArrowLeft';
|
|
502
505
|
}
|
|
@@ -881,7 +884,16 @@ JSClass("UIHTMLWindowServer", UIWindowServer, {
|
|
|
881
884
|
return modifiers;
|
|
882
885
|
},
|
|
883
886
|
|
|
887
|
+
_hasWarnedAboutMouseLocation: false,
|
|
888
|
+
|
|
884
889
|
_updateMouseLocation: function(e){
|
|
890
|
+
if (isNaN(e.clientX) || isNaN(e.clientY)){
|
|
891
|
+
if (!this._hasWarnedAboutMouseLocation){
|
|
892
|
+
this._hasWarnedAboutMouseLocation = true;
|
|
893
|
+
logger.warn("not updating mouse location: e.clientX = %{public}, e.clientY = %{public}", typeof(e.clientX), typeof(e.clientY));
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
885
897
|
this.mouseLocation.x = e.clientX - this._screenClientOrigin.x;
|
|
886
898
|
this.mouseLocation.y = e.clientY - this._screenClientOrigin.y;
|
|
887
899
|
},
|
|
@@ -1391,8 +1403,43 @@ JSClass("UIHTMLDataTransferPasteboard", UIPasteboard, {
|
|
|
1391
1403
|
_updateDataTranserObjectForType: function(type){
|
|
1392
1404
|
if (this._dataTransfer !== null){
|
|
1393
1405
|
var objects = this.objectsForType(type);
|
|
1394
|
-
|
|
1395
|
-
|
|
1406
|
+
if (objects !== null){
|
|
1407
|
+
var dictionaries = [];
|
|
1408
|
+
var dictionary;
|
|
1409
|
+
var obj;
|
|
1410
|
+
var i, l;
|
|
1411
|
+
for (i = 0, l = objects.length; i < l; ++i){
|
|
1412
|
+
obj = objects[i];
|
|
1413
|
+
if (obj instanceof JSObject){
|
|
1414
|
+
if (obj.dictionaryRepresentation){
|
|
1415
|
+
dictionaries.push(obj.dictionaryRepresentation());
|
|
1416
|
+
}else if (obj.encodeToStorableDictionary){
|
|
1417
|
+
dictionary = {};
|
|
1418
|
+
obj.encodeToStorableDictionary(dictionary);
|
|
1419
|
+
dictionaries.push(dictionary);
|
|
1420
|
+
}else if (obj.encodeToDictionary){
|
|
1421
|
+
dictionary = {};
|
|
1422
|
+
obj.encodeToDictionary(dictionary);
|
|
1423
|
+
dictionaries.push(dictionary);
|
|
1424
|
+
}else{
|
|
1425
|
+
// intentionally dropping object form html pasteboard
|
|
1426
|
+
// because we don't know how to get a plain dictionary
|
|
1427
|
+
// representation of it. These objects will not be
|
|
1428
|
+
// droppable outside of the source window, but will
|
|
1429
|
+
// still be droppable within the source window as
|
|
1430
|
+
// in-memory objects.
|
|
1431
|
+
}
|
|
1432
|
+
}else{
|
|
1433
|
+
dictionaries.push(obj);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
try{
|
|
1437
|
+
var json = JSON.stringify(dictionaries);
|
|
1438
|
+
this._dataTransfer.setData(type, json);
|
|
1439
|
+
}catch (e){
|
|
1440
|
+
logger.warning("Unable to serialize pasteboard objects to JSON: %{error}", e);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1396
1443
|
}
|
|
1397
1444
|
},
|
|
1398
1445
|
|
|
@@ -2765,7 +2765,7 @@ VisibleItem.prototype = Object.create(Function.prototype, {
|
|
|
2765
2765
|
view: {
|
|
2766
2766
|
configurable: true,
|
|
2767
2767
|
set: function(view){
|
|
2768
|
-
Object.defineProperty(this, 'view', {value: view});
|
|
2768
|
+
Object.defineProperty(this, 'view', {writable: true, value: view});
|
|
2769
2769
|
Object.defineProperty(this, 'rect', {
|
|
2770
2770
|
get: function(){
|
|
2771
2771
|
return JSRect(
|
|
@@ -297,26 +297,7 @@ JSClass("UIPopupWindow", UIWindow, {
|
|
|
297
297
|
}
|
|
298
298
|
return;
|
|
299
299
|
}
|
|
300
|
-
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
var window = this;
|
|
304
|
-
var transform = this.transform;
|
|
305
|
-
this._modalAnimator = UIViewPropertyAnimator.initWithDuration(0.075);
|
|
306
|
-
this._modalAnimator.addAnimations(function(){
|
|
307
|
-
window.transform = transform.scaledBy(1.15);
|
|
308
|
-
});
|
|
309
|
-
this._modalAnimator.addCompletion(function(){
|
|
310
|
-
window._modalAnimator = UIViewPropertyAnimator.initWithDuration(0.075);
|
|
311
|
-
window._modalAnimator.addAnimations(function(){
|
|
312
|
-
window.transform = transform;
|
|
313
|
-
});
|
|
314
|
-
window._modalAnimator.addCompletion(function(){
|
|
315
|
-
window._modalAnimator = null;
|
|
316
|
-
});
|
|
317
|
-
window._modalAnimator.start();
|
|
318
|
-
});
|
|
319
|
-
this._modalAnimator.start();
|
|
300
|
+
UIPopupWindow.$super.indicateModalStatus.call(this);
|
|
320
301
|
},
|
|
321
302
|
|
|
322
303
|
accessibilityRole: UIAccessibility.Role.popover
|
|
@@ -169,6 +169,12 @@ JSClass("UITextEditor", JSObject, {
|
|
|
169
169
|
|
|
170
170
|
handleMouseDraggedAtLocation: function(location, event){
|
|
171
171
|
if (this._handledSelectOnMouseDown){
|
|
172
|
+
if (this.selections.length === 0){
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (this._draggingSelectionIndex < 0 || this._draggingSelectionIndex >= this.selections.length){
|
|
176
|
+
this._draggingSelectionIndex = 0;
|
|
177
|
+
}
|
|
172
178
|
var draggingSelection = this.selections[this._draggingSelectionIndex];
|
|
173
179
|
this.selections.splice(this._draggingSelectionIndex, 1);
|
|
174
180
|
var newSelection = this._createSelectionAtLocation(location);
|
|
@@ -620,7 +620,9 @@ JSClass("UITextField", UIControl, {
|
|
|
620
620
|
pasteAndMatchStyle: function(){
|
|
621
621
|
if (UIPasteboard.general.containsType(UIPasteboard.ContentType.plainText)){
|
|
622
622
|
var text = UIPasteboard.general.stringForType(UIPasteboard.ContentType.plainText);
|
|
623
|
-
|
|
623
|
+
if (text !== null){
|
|
624
|
+
this.insertText(text);
|
|
625
|
+
}
|
|
624
626
|
}
|
|
625
627
|
},
|
|
626
628
|
|
|
@@ -19,14 +19,12 @@ window.HTMLAppBootstrapper = function(rootElement, jskitapp){
|
|
|
19
19
|
this.appSrc = jskitapp.appSrc;
|
|
20
20
|
this.appCss = jskitapp.appCss;
|
|
21
21
|
}
|
|
22
|
+
this.id = this._generateRandomID();
|
|
22
23
|
this.status = HTMLAppBootstrapper.STATUS.notstarted;
|
|
23
|
-
this.statusDispatchTimeoutID = null;
|
|
24
24
|
this.preflightChecks = [];
|
|
25
|
-
this.minStatusInterval = 30;
|
|
26
25
|
this.loadingScripts = {};
|
|
27
26
|
this.preflightStorageKey = this.preflightID;
|
|
28
27
|
this.preflightStorageValue = navigator.userAgent;
|
|
29
|
-
this._isAppcacheInstalled = false;
|
|
30
28
|
this.serviceWorker = null;
|
|
31
29
|
this.application = null;
|
|
32
30
|
this.error = null;
|
|
@@ -58,26 +56,38 @@ HTMLAppBootstrapper.prototype = {
|
|
|
58
56
|
|
|
59
57
|
// expected to be overridden by whatver code instantiates the bootstrapper, in order
|
|
60
58
|
// to receive callbacks for status changes so the bootstrap UI can be updated
|
|
61
|
-
onstatus: function(){
|
|
59
|
+
onstatus: function(status){
|
|
62
60
|
},
|
|
63
61
|
|
|
64
|
-
onprogress: function(){
|
|
62
|
+
onprogress: function(loaded, total){
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
onlongload: function(i){
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
onerror: function(error){
|
|
65
69
|
},
|
|
66
70
|
|
|
67
71
|
onlog: function(record){
|
|
68
72
|
},
|
|
69
73
|
|
|
74
|
+
ontimeout: function(){
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
onhidden: function(){
|
|
78
|
+
},
|
|
79
|
+
|
|
70
80
|
run: function(){
|
|
71
81
|
if (this.app === null){
|
|
72
82
|
return;
|
|
73
83
|
}
|
|
74
|
-
this.
|
|
84
|
+
this.rootElement.ownerDocument.addEventListener("visibilitychange", this);
|
|
85
|
+
this._resetTimeout();
|
|
86
|
+
this.log_debug("boot", "Booting " + this.app.bundleId + ", version " + this.app.bundleVersion + " (build " + this.app.buildId + ")");
|
|
75
87
|
if (this.serviceWorkerSrc && window.navigator.serviceWorker){
|
|
76
88
|
this._installUsingServiceWorker(window.navigator.serviceWorker);
|
|
77
|
-
}else if (document.documentElement.getAttribute("manifest") && window.applicationCache){
|
|
78
|
-
this._installUsingAppcache(window.applicationCache);
|
|
79
89
|
}else{
|
|
80
|
-
this.log_warn("app", "Service worker
|
|
90
|
+
this.log_warn("app", "Service worker not available");
|
|
81
91
|
this.load();
|
|
82
92
|
}
|
|
83
93
|
},
|
|
@@ -114,6 +124,7 @@ HTMLAppBootstrapper.prototype = {
|
|
|
114
124
|
}, function HTMLAppBootstrapper_preflightLoadError(e){
|
|
115
125
|
bootstrapper.error = e;
|
|
116
126
|
bootstrapper.setStatus(HTMLAppBootstrapper.STATUS.preflightLoadError);
|
|
127
|
+
bootstrapper.reportError(e);
|
|
117
128
|
});
|
|
118
129
|
},
|
|
119
130
|
|
|
@@ -153,6 +164,7 @@ HTMLAppBootstrapper.prototype = {
|
|
|
153
164
|
this.linkStylesheets();
|
|
154
165
|
window.addEventListener('error', this);
|
|
155
166
|
window.addEventListener('unhandledrejection', this);
|
|
167
|
+
this.log_debug("boot", "including " + this.appSrc.length + " scripts");
|
|
156
168
|
this.includeAppSrc(this.appSrc.shift());
|
|
157
169
|
},
|
|
158
170
|
|
|
@@ -169,6 +181,9 @@ HTMLAppBootstrapper.prototype = {
|
|
|
169
181
|
|
|
170
182
|
includeAppSrc: function(src){
|
|
171
183
|
var bootstrapper = this;
|
|
184
|
+
if (!this.app.debug){
|
|
185
|
+
this.log_debug("boot", "including " + src);
|
|
186
|
+
}
|
|
172
187
|
this.include(src, true, function HTMLAppBootstrapper_appScriptLoaded(){
|
|
173
188
|
if (bootstrapper.appSrc.length){
|
|
174
189
|
bootstrapper.includeAppSrc(bootstrapper.appSrc.shift());
|
|
@@ -178,6 +193,7 @@ HTMLAppBootstrapper.prototype = {
|
|
|
178
193
|
}, function HTMLAppBootstrapper_appScriptLoadError(e){
|
|
179
194
|
bootstrapper.error = e;
|
|
180
195
|
bootstrapper.setStatus(HTMLAppBootstrapper.STATUS.appLoadError);
|
|
196
|
+
bootstrapper.reportError(e);
|
|
181
197
|
});
|
|
182
198
|
},
|
|
183
199
|
|
|
@@ -199,6 +215,7 @@ HTMLAppBootstrapper.prototype = {
|
|
|
199
215
|
this.log_error("app", "Error calling main(): " + e.message);
|
|
200
216
|
this.error = e;
|
|
201
217
|
this.setStatus(HTMLAppBootstrapper.STATUS.appRunError);
|
|
218
|
+
this.reportError(e);
|
|
202
219
|
}
|
|
203
220
|
},
|
|
204
221
|
|
|
@@ -209,6 +226,7 @@ HTMLAppBootstrapper.prototype = {
|
|
|
209
226
|
}else{
|
|
210
227
|
this.error = error;
|
|
211
228
|
this.setStatus(HTMLAppBootstrapper.STATUS.appLaunchFailure);
|
|
229
|
+
this.reportError(error);
|
|
212
230
|
}
|
|
213
231
|
return;
|
|
214
232
|
}
|
|
@@ -258,22 +276,51 @@ HTMLAppBootstrapper.prototype = {
|
|
|
258
276
|
|
|
259
277
|
setStatus: function(status){
|
|
260
278
|
if (status !== HTMLAppBootstrapper.STATUS.appCrashed){
|
|
261
|
-
this.
|
|
279
|
+
this.log_debug("boot", this.status + " -> " + status);
|
|
262
280
|
}
|
|
263
281
|
this.status = status;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
282
|
+
switch (this.status){
|
|
283
|
+
case HTMLAppBootstrapper.STATUS.preflightLoading:
|
|
284
|
+
case HTMLAppBootstrapper.STATUS.preflightRunning:
|
|
285
|
+
case HTMLAppBootstrapper.STATUS.appLoading:
|
|
286
|
+
case HTMLAppBootstrapper.STATUS.appRunning:
|
|
287
|
+
case HTMLAppBootstrapper.STATUS.updating:
|
|
288
|
+
this._resetTimeout();
|
|
289
|
+
break;
|
|
290
|
+
case HTMLAppBootstrapper.STATUS.checkingForUpdate:
|
|
291
|
+
case HTMLAppBootstrapper.STATUS.installing:
|
|
292
|
+
this._resetTimeout();
|
|
293
|
+
this._startLongLoadInterval();
|
|
294
|
+
break;
|
|
295
|
+
case HTMLAppBootstrapper.STATUS.preflightFailedChecks:
|
|
296
|
+
case HTMLAppBootstrapper.STATUS.preflightLoadError:
|
|
297
|
+
case HTMLAppBootstrapper.STATUS.updateError:
|
|
298
|
+
case HTMLAppBootstrapper.STATUS.appLoadError:
|
|
299
|
+
case HTMLAppBootstrapper.STATUS.appRunError:
|
|
300
|
+
case HTMLAppBootstrapper.STATUS.appLaunchFailure:
|
|
301
|
+
case HTMLAppBootstrapper.STATUS.appRequiresNoOtherInstances:
|
|
302
|
+
case HTMLAppBootstrapper.STATUS.appLaunched:
|
|
303
|
+
this.rootElement.ownerDocument.removeEventListener("visibilitychange", this);
|
|
304
|
+
this._cancelTimeout();
|
|
305
|
+
this._cancelLongLoadInterval();
|
|
306
|
+
break;
|
|
307
|
+
case HTMLAppBootstrapper.STATUS.appCrashed:
|
|
308
|
+
break;
|
|
276
309
|
}
|
|
310
|
+
this.onstatus(this.status);
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
_hasSeenProgress: false,
|
|
314
|
+
|
|
315
|
+
setProgress: function(loaded, total){
|
|
316
|
+
if (!this._hasSeenProgress){
|
|
317
|
+
this._hasSeenProgress = true;
|
|
318
|
+
this._cancelTimeout();
|
|
319
|
+
}
|
|
320
|
+
if (loaded === total){
|
|
321
|
+
this._resetTimeout();
|
|
322
|
+
}
|
|
323
|
+
this.onprogress(loaded, total);
|
|
277
324
|
},
|
|
278
325
|
|
|
279
326
|
handleEvent: function(e){
|
|
@@ -284,12 +331,14 @@ HTMLAppBootstrapper.prototype = {
|
|
|
284
331
|
if (window.applicationCache && e.target === window.applicationCache){
|
|
285
332
|
this.error = e.error;
|
|
286
333
|
this.setStatus(HTMLAppBootstrapper.STATUS.updateError);
|
|
334
|
+
this.reportError(e.error);
|
|
287
335
|
return;
|
|
288
336
|
}
|
|
289
337
|
if (e.target === this.serviceWorker){
|
|
290
338
|
if (!this._hasLoaded){
|
|
291
339
|
this.error = e.error;
|
|
292
340
|
this.setStatus(HTMLAppBootstrapper.STATUS.updateError);
|
|
341
|
+
this.reportError(e.error);
|
|
293
342
|
return;
|
|
294
343
|
}
|
|
295
344
|
}
|
|
@@ -312,6 +361,91 @@ HTMLAppBootstrapper.prototype = {
|
|
|
312
361
|
// TODO:
|
|
313
362
|
},
|
|
314
363
|
|
|
364
|
+
event_visibilitychange: function(e){
|
|
365
|
+
if (this.rootElement.ownerDocument.visibilityState === "hidden"){
|
|
366
|
+
this.onhidden();
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
|
|
370
|
+
// MARK: - Timeouts
|
|
371
|
+
|
|
372
|
+
timeoutInterval: 30,
|
|
373
|
+
_timeoutID: null,
|
|
374
|
+
|
|
375
|
+
_resetTimeout: function(){
|
|
376
|
+
if (this._timeoutID !== null){
|
|
377
|
+
clearTimeout(this._timeoutID);
|
|
378
|
+
}
|
|
379
|
+
var bootstrapper = this;
|
|
380
|
+
this._timeoutID = setTimeout(function(){
|
|
381
|
+
bootstrapper._handleTimeout();
|
|
382
|
+
}, this.timeoutInterval * 1000);
|
|
383
|
+
},
|
|
384
|
+
|
|
385
|
+
_cancelTimeout: function(){
|
|
386
|
+
if (this._timeoutID !== null){
|
|
387
|
+
clearTimeout(this._timeoutID);
|
|
388
|
+
this._timeoutID = null;
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
_handleTimeout: function(){
|
|
393
|
+
this._timeoutID = null;
|
|
394
|
+
this.ontimeout();
|
|
395
|
+
window.location.reload();
|
|
396
|
+
},
|
|
397
|
+
|
|
398
|
+
_longLoadIntervalID: null,
|
|
399
|
+
_longLoadCount: 0,
|
|
400
|
+
longLoadInterval: 5,
|
|
401
|
+
|
|
402
|
+
_startLongLoadInterval: function(){
|
|
403
|
+
if (this._longLoadIntervalID === null){
|
|
404
|
+
var bootstrapper = this;
|
|
405
|
+
this._longLoadIntervalID = setInterval(function(){
|
|
406
|
+
bootstrapper._handleLongLoadInterval();
|
|
407
|
+
}, this.longLoadInterval * 1000);
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
_cancelLongLoadInterval: function(){
|
|
412
|
+
if (this._longLoadIntervalID !== null){
|
|
413
|
+
clearInterval(this._longLoadIntervalID);
|
|
414
|
+
this._longLoadIntervalID = null;
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
|
|
418
|
+
_handleLongLoadInterval: function(){
|
|
419
|
+
this.onlongload(this._longLoadCount);
|
|
420
|
+
++this._longLoadCount;
|
|
421
|
+
},
|
|
422
|
+
|
|
423
|
+
_currentTimestamp: function(){
|
|
424
|
+
return Date.now() / 1000;
|
|
425
|
+
},
|
|
426
|
+
|
|
427
|
+
_generateRandomID: function(){
|
|
428
|
+
var data = new Uint8Array(20);
|
|
429
|
+
var i, l;
|
|
430
|
+
if (window.crypto){
|
|
431
|
+
window.crypto.getRandomValues(data);
|
|
432
|
+
}else{
|
|
433
|
+
for (i = 0, l = data.length; i < l; ++i){
|
|
434
|
+
data[i] = Math.floor(Math.random() * 256);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
var id = "";
|
|
438
|
+
var h;
|
|
439
|
+
for (i = 0, l = data.length; i < l; ++i){
|
|
440
|
+
h = data[i].toString(16);
|
|
441
|
+
if (h.length == 1){
|
|
442
|
+
id += "0";
|
|
443
|
+
}
|
|
444
|
+
id += h;
|
|
445
|
+
}
|
|
446
|
+
return id;
|
|
447
|
+
},
|
|
448
|
+
|
|
315
449
|
// MARK: - Service Worker
|
|
316
450
|
|
|
317
451
|
serviceWorkerRegistration: null,
|
|
@@ -327,6 +461,7 @@ HTMLAppBootstrapper.prototype = {
|
|
|
327
461
|
bootstrapper.log_debug("serviceWorker", "Checking for update");
|
|
328
462
|
bootstrapper.setStatus(HTMLAppBootstrapper.STATUS.checkingForUpdate);
|
|
329
463
|
registration.addEventListener('updatefound', bootstrapper);
|
|
464
|
+
bootstrapper.log_debug("serviceWorker", "calling update()");
|
|
330
465
|
return registration.update();
|
|
331
466
|
}
|
|
332
467
|
bootstrapper.log_debug("serviceWorker", "No registration found");
|
|
@@ -334,22 +469,30 @@ HTMLAppBootstrapper.prototype = {
|
|
|
334
469
|
bootstrapper.log_debug("serviceWorker", "Registering");
|
|
335
470
|
return container.register(bootstrapper.serviceWorkerSrc);
|
|
336
471
|
}).then(function(registration){
|
|
337
|
-
bootstrapper.
|
|
472
|
+
bootstrapper.registration = registration;
|
|
473
|
+
bootstrapper.log_debug("serviceWorker", "update() or register() resolved");
|
|
474
|
+
bootstrapper.log_debug("serviceWorker", "registration.installing = " + (registration.installing ? "ServiceWorker" : "null"));
|
|
475
|
+
bootstrapper.log_debug("serviceWorker", "registration.waiting = " + (registration.waiting ? "ServiceWorker" : "null"));
|
|
476
|
+
bootstrapper.log_debug("serviceWorker", "registration.active = " + (registration.active ? "ServiceWorker" : "null"));
|
|
338
477
|
if (registration.installing){
|
|
478
|
+
bootstrapper.serviceWorker = registration.installing;
|
|
479
|
+
bootstrapper.serviceWorker.addEventListener('statechange', bootstrapper);
|
|
480
|
+
bootstrapper.log_debug("serviceWorker", "Installing registration found (state = " + bootstrapper.serviceWorker.state + ")");
|
|
339
481
|
if (bootstrapper.status === HTMLAppBootstrapper.STATUS.checkingForUpdate){
|
|
340
482
|
bootstrapper.log_debug("serviceWorker", "Installing Update");
|
|
341
483
|
bootstrapper.setStatus(HTMLAppBootstrapper.STATUS.updating);
|
|
342
484
|
}else{
|
|
343
485
|
bootstrapper.log_debug("serviceWorker", "Installing for the first time");
|
|
344
486
|
}
|
|
345
|
-
bootstrapper.serviceWorker = registration.installing;
|
|
346
487
|
}else if (registration.waiting){
|
|
347
|
-
bootstrapper.log_debug("serviceWorker", "Waiting registration found. Activating");
|
|
348
488
|
bootstrapper.serviceWorker = registration.waiting;
|
|
489
|
+
bootstrapper.serviceWorker.addEventListener('statechange', bootstrapper);
|
|
490
|
+
bootstrapper.log_debug("serviceWorker", "Waiting registration found (state = " + bootstrapper.serviceWorker.state + "). Activating");
|
|
349
491
|
bootstrapper.serviceWorker.postMessage({type: "activate"});
|
|
350
492
|
}else if (registration.active){
|
|
351
493
|
bootstrapper.serviceWorker = registration.active;
|
|
352
|
-
bootstrapper.
|
|
494
|
+
bootstrapper.serviceWorker.addEventListener('statechange', bootstrapper);
|
|
495
|
+
bootstrapper.log_debug("serviceWorker", "Active registration found (state = " + bootstrapper.serviceWorker.state + ")");
|
|
353
496
|
if (bootstrapper.serviceWorker.state == 'activating'){
|
|
354
497
|
bootstrapper.log_debug("serviceWorker", "Activating");
|
|
355
498
|
}else{
|
|
@@ -360,15 +503,18 @@ HTMLAppBootstrapper.prototype = {
|
|
|
360
503
|
bootstrapper.log_error("serviceWorker", "Nothing to install???");
|
|
361
504
|
throw new Error("No service worker found on registration");
|
|
362
505
|
}
|
|
363
|
-
bootstrapper.serviceWorker.addEventListener('statechange', bootstrapper);
|
|
364
506
|
}).catch(function(error){
|
|
365
507
|
bootstrapper.log_warn("serviceWorker", "Error with registration: " + error.message);
|
|
366
508
|
if (container.controller){
|
|
509
|
+
bootstrapper.serviceWorker = container.controller;
|
|
367
510
|
bootstrapper.log_debug("serviceWorker", "Proceeding with cached version");
|
|
368
511
|
bootstrapper.load();
|
|
512
|
+
bootstrapper.error = error;
|
|
513
|
+
bootstrapper.reportError(error);
|
|
369
514
|
}else{
|
|
370
515
|
bootstrapper.error = error;
|
|
371
516
|
bootstrapper.setStatus(HTMLAppBootstrapper.STATUS.updateError);
|
|
517
|
+
bootstrapper.reportError(error);
|
|
372
518
|
}
|
|
373
519
|
});
|
|
374
520
|
},
|
|
@@ -382,8 +528,11 @@ HTMLAppBootstrapper.prototype = {
|
|
|
382
528
|
}else if (worker.state === "activated"){
|
|
383
529
|
this.log_debug("serviceWorker", "Activated");
|
|
384
530
|
this.load();
|
|
531
|
+
}else{
|
|
532
|
+
this.log_debug("serviceWorker", "statechange " + worker.state);
|
|
385
533
|
}
|
|
386
534
|
}else{
|
|
535
|
+
this.log_debug("serviceWorker", "other statechange " + worker.state);
|
|
387
536
|
if (worker.state == "installed"){
|
|
388
537
|
this.log_debug("serviceWorker", "Update Installed");
|
|
389
538
|
if (this.application !== null){
|
|
@@ -403,15 +552,18 @@ HTMLAppBootstrapper.prototype = {
|
|
|
403
552
|
if ((e.data.loaded % d === 0) || (e.data.loaded === e.data.total)){
|
|
404
553
|
this.log_debug("serviceWorker", "Progress " + e.data.loaded + '/' + e.data.total);
|
|
405
554
|
}
|
|
406
|
-
this.
|
|
555
|
+
this.setProgress(e.data.loaded, e.data.total);
|
|
407
556
|
}else if (e.data.type == 'error'){
|
|
408
557
|
this.log_warn("serviceWorker", "Install error: " + e.data.message);
|
|
409
558
|
if (container.controller){
|
|
559
|
+
this.serviceWorker = worker;
|
|
410
560
|
this.log_debug("serviceWorker", "Proceeding with cached version");
|
|
411
561
|
this.load();
|
|
412
562
|
}else{
|
|
413
|
-
|
|
563
|
+
var error = new Error(e.data.message);
|
|
564
|
+
this.error = error;
|
|
414
565
|
this.setStatus(HTMLAppBootstrapper.STATUS.updateError);
|
|
566
|
+
this.reportError(error);
|
|
415
567
|
}
|
|
416
568
|
}
|
|
417
569
|
}else{
|
|
@@ -431,7 +583,7 @@ HTMLAppBootstrapper.prototype = {
|
|
|
431
583
|
// But in Firefox, the update() promise doesn't resolve until after
|
|
432
584
|
// the install is complete, so this is where we have to detect an update starting.
|
|
433
585
|
if (this.serviceWorker === null){
|
|
434
|
-
this.log_debug("
|
|
586
|
+
this.log_debug("serviceWorker", "Update found before service worker set");
|
|
435
587
|
this.setStatus(HTMLAppBootstrapper.STATUS.updating);
|
|
436
588
|
this.serviceWorker = worker;
|
|
437
589
|
}
|
|
@@ -447,69 +599,14 @@ HTMLAppBootstrapper.prototype = {
|
|
|
447
599
|
this.serviceWorker = worker;
|
|
448
600
|
if (!this.serviceWorker){
|
|
449
601
|
this.log_warn("serviceWorker", "null installer");
|
|
450
|
-
|
|
602
|
+
var error = new Error("null installing worker");
|
|
603
|
+
this.error = error;
|
|
451
604
|
this.setStatus(HTMLAppBootstrapper.STATUS.updateError);
|
|
605
|
+
this.reportError(error);
|
|
452
606
|
}
|
|
453
607
|
}
|
|
454
608
|
},
|
|
455
609
|
|
|
456
|
-
// MARK: - Appcache
|
|
457
|
-
|
|
458
|
-
_installUsingAppcache: function(appcache){
|
|
459
|
-
this._isAppcacheInstalled = window.applicationCache.status !== window.applicationCache.UNCACHED;
|
|
460
|
-
appcache.addEventListener('checking', this);
|
|
461
|
-
appcache.addEventListener('downloading', this);
|
|
462
|
-
appcache.addEventListener('noupdate', this);
|
|
463
|
-
appcache.addEventListener('progress', this);
|
|
464
|
-
appcache.addEventListener('error', this);
|
|
465
|
-
appcache.addEventListener('updateready', this);
|
|
466
|
-
appcache.addEventListener('cached', this);
|
|
467
|
-
appcache.addEventListener('obsoleted', this);
|
|
468
|
-
},
|
|
469
|
-
|
|
470
|
-
event_checking: function(e){
|
|
471
|
-
this.log_info("appcache", 'checking app cache');
|
|
472
|
-
this.setStatus(HTMLAppBootstrapper.STATUS.checkingForUpdate);
|
|
473
|
-
},
|
|
474
|
-
|
|
475
|
-
event_downloading: function(e){
|
|
476
|
-
this.log_info("appcache", 'cache downloading');
|
|
477
|
-
this.setStatus(this._isAppcacheInstalled ? HTMLAppBootstrapper.STATUS.updating : HTMLAppBootstrapper.STATUS.installing);
|
|
478
|
-
},
|
|
479
|
-
|
|
480
|
-
event_noupdate: function(e){
|
|
481
|
-
this.log_info("appcache", 'no update');
|
|
482
|
-
this.load();
|
|
483
|
-
},
|
|
484
|
-
|
|
485
|
-
event_progress: function(e){
|
|
486
|
-
if (e.lengthComputable){
|
|
487
|
-
this.onprogress(e.loaded, e.total);
|
|
488
|
-
}else{
|
|
489
|
-
// this.log_info("appcache", 'progress is not length computable');
|
|
490
|
-
}
|
|
491
|
-
},
|
|
492
|
-
|
|
493
|
-
event_cached: function(e){
|
|
494
|
-
this.log_info("appcache", 'first version cached');
|
|
495
|
-
this.load();
|
|
496
|
-
},
|
|
497
|
-
|
|
498
|
-
event_updateready: function(e){
|
|
499
|
-
this.log_info("appcache", 'new version available');
|
|
500
|
-
if (this._hasLoaded){
|
|
501
|
-
// TODO: communicate to app
|
|
502
|
-
}else{
|
|
503
|
-
e.target.swapCache();
|
|
504
|
-
this.load();
|
|
505
|
-
}
|
|
506
|
-
},
|
|
507
|
-
|
|
508
|
-
event_obsoleted: function(e){
|
|
509
|
-
this.log_info("appcache", 'cache obsoleted');
|
|
510
|
-
this.load();
|
|
511
|
-
},
|
|
512
|
-
|
|
513
610
|
// MARK: - Logging
|
|
514
611
|
|
|
515
612
|
log_debug: function(category, message){
|
|
@@ -537,7 +634,7 @@ HTMLAppBootstrapper.prototype = {
|
|
|
537
634
|
level: level,
|
|
538
635
|
subsystem: "boot",
|
|
539
636
|
category: category,
|
|
540
|
-
timestamp:
|
|
637
|
+
timestamp: this._currentTimestamp(),
|
|
541
638
|
message: message,
|
|
542
639
|
args: []
|
|
543
640
|
};
|
|
@@ -546,6 +643,9 @@ HTMLAppBootstrapper.prototype = {
|
|
|
546
643
|
|
|
547
644
|
_recordLog: function(record){
|
|
548
645
|
this.logs.push(record);
|
|
646
|
+
if (this.app.debug){
|
|
647
|
+
console[record.level](HTMLAppBootstrapper.formatter.log(record));
|
|
648
|
+
}
|
|
549
649
|
this.onlog(record);
|
|
550
650
|
},
|
|
551
651
|
|
|
@@ -566,6 +666,15 @@ HTMLAppBootstrapper.prototype = {
|
|
|
566
666
|
|
|
567
667
|
_getLogsJSLog: function(){
|
|
568
668
|
return window.JSLog.getRecords();
|
|
669
|
+
},
|
|
670
|
+
|
|
671
|
+
// MARK: - Telemetry
|
|
672
|
+
|
|
673
|
+
reportError: function(error, fingerprint){
|
|
674
|
+
if (this.app.debug){
|
|
675
|
+
console.error(error);
|
|
676
|
+
}
|
|
677
|
+
this.onerror(error);
|
|
569
678
|
}
|
|
570
679
|
|
|
571
680
|
};
|
|
@@ -172,7 +172,6 @@
|
|
|
172
172
|
statusLabel.innerHTML = "Unable to Start Application";
|
|
173
173
|
detailsLabel.style.opacity = 1;
|
|
174
174
|
detailsLabel.innerHTML = "Apologies, but we couldn't start the application. Don't worry, it wasn't anything you did, and it isn't something we expect to happen under normal usage.<br>We've notified our engineers of the problem, and the best thing you can do is try again soon.";
|
|
175
|
-
reportError(bootstrapper.error, bootstrapper.getLogs());
|
|
176
175
|
break;
|
|
177
176
|
case S.appRequiresNoOtherInstances:
|
|
178
177
|
statusLabel.style.opacity = 1;
|
|
@@ -209,15 +208,7 @@
|
|
|
209
208
|
progressBar.style.width = (loaded * 100 / total) + '%';
|
|
210
209
|
progress.setAttribute("aria-valuenow", Math.round(loaded * 100 / total));
|
|
211
210
|
};
|
|
212
|
-
|
|
213
|
-
bootstrapper.onlog = function(record){
|
|
214
|
-
console[record.level](HTMLAppBootstrapper.formatter.log(record));
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
var reportError = function(error, logs){
|
|
218
|
-
if (app.debug){
|
|
219
|
-
console.error(error);
|
|
220
|
-
}
|
|
211
|
+
bootstrapper.onerror = function(error){
|
|
221
212
|
};
|
|
222
213
|
window.JSKIT_PREFLIGHT_CONTEXT = bootstrapper;
|
|
223
214
|
bootstrapper.run();
|