@breakside/jskit 2023.20.0 → 2023.22.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/JSImage.js +9 -7
- package/Frameworks/Foundation.jsframework/JS/JSURLResponse.js +1 -1
- package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
- package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
- package/Frameworks/SecurityKit.jsframework/JS/SECCipher+HTML.js +31 -44
- package/Frameworks/SecurityKit.jsframework/JS/SECCipher+Node.js +16 -38
- package/Frameworks/SecurityKit.jsframework/JS/SECCipher.js +33 -3
- package/Frameworks/SecurityKit.jsframework/JS/SECHMAC.js +17 -0
- package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
- package/Info.json +2 -2
- package/Node/HTMLBuilder.js +76 -20
- package/Node/Resources.js +21 -120
- 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/JSImage.js +9 -7
- package/Root/Frameworks/Foundation/JSURLResponse.js +1 -1
- 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 +2 -1
- package/Root/Frameworks/NotificationKit/NKHTMLUserNotificationCenter.js +59 -18
- package/Root/Frameworks/NotificationKit/NKUserNotification.js +12 -1
- package/Root/Frameworks/NotificationKit/NKUserNotificationCenter.js +5 -0
- package/Root/Frameworks/NotificationKit/NKWebPushService.js +145 -38
- package/Root/Frameworks/NotificationKit/NotificationKit+Node.js +19 -0
- 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/SecurityKit/SECCipher+HTML.js +31 -44
- package/Root/Frameworks/SecurityKit/SECCipher+Node.js +16 -38
- package/Root/Frameworks/SecurityKit/SECCipher.js +33 -3
- package/Root/Frameworks/SecurityKit/SECHMAC.js +17 -0
- 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/UIHTMLApplication.js +2 -0
- package/Root/Frameworks/UIKit/UIHTMLContentEditableTextInputManager.js +1 -1
- package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +8 -1
- package/Root/Frameworks/UIKit/UIListView.js +4 -2
- package/Root/Frameworks/UIKit/UIWindowServer.js +7 -13
- package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
- package/Root/Templates/html/${PROJECT_NAME}/www/${PROJECT_NAME}.webmanifest +9 -0
- package/Root/Templates/html/${PROJECT_NAME}/www/HTMLAppBootstrapper.js +7 -1
- package/Root/Templates/html/${PROJECT_NAME}/www/index.html +1 -0
- package/Root/Templates/html/${PROJECT_NAME}/www/service-worker.js +25 -1
- package/package.json +1 -1
package/Node/HTMLBuilder.js
CHANGED
|
@@ -484,7 +484,8 @@ JSClass("HTMLBuilder", Builder, {
|
|
|
484
484
|
wwwCSSPaths: null,
|
|
485
485
|
wwwPaths: null,
|
|
486
486
|
uncachedWWWPaths: null,
|
|
487
|
-
|
|
487
|
+
webApplicationManifestURL: null,
|
|
488
|
+
legacyAppcacheManifestURL: null,
|
|
488
489
|
preflightId: null,
|
|
489
490
|
preflightURL: null,
|
|
490
491
|
preflightFeatures: null,
|
|
@@ -497,14 +498,14 @@ JSClass("HTMLBuilder", Builder, {
|
|
|
497
498
|
// As of Dec 2019...
|
|
498
499
|
// Browsers are in the process of removing the appcache API soon (Chrome
|
|
499
500
|
// says it'll be gone by April 2020), so there's no point to contunue
|
|
500
|
-
// supporting it. Keeping this `
|
|
501
|
+
// supporting it. Keeping this `legacyAppcacheManifestURL` `null` will disable the
|
|
501
502
|
// appcache behavior in the built application, but has the advantage of
|
|
502
503
|
// keeping our builder code otherwise as-is should we need to reference
|
|
503
504
|
// it or turn it back on (like for IE 11 support). The way we use
|
|
504
505
|
// service workers (the appcache replacement API) is to essentially
|
|
505
506
|
// mimic the way appcache worked because although others didn't like it,
|
|
506
507
|
// appcache was perfect for our use case.
|
|
507
|
-
// this.
|
|
508
|
+
// this.legacyAppcacheManifestURL = this.wwwURL.appendingPathComponent("manifest.appcache");
|
|
508
509
|
|
|
509
510
|
await this.buildCSS();
|
|
510
511
|
await this.buildPreflight();
|
|
@@ -552,7 +553,8 @@ JSClass("HTMLBuilder", Builder, {
|
|
|
552
553
|
}
|
|
553
554
|
}
|
|
554
555
|
|
|
555
|
-
await this.
|
|
556
|
+
await this.buildLegacyAppcacheManifest();
|
|
557
|
+
await this.updateWebApplicationManifest();
|
|
556
558
|
},
|
|
557
559
|
|
|
558
560
|
_copyWWWEntry: async function(entry, wwwPath, manifestConfiguration){
|
|
@@ -612,6 +614,11 @@ JSClass("HTMLBuilder", Builder, {
|
|
|
612
614
|
}else if (element.localName == 'title' && element.parentNode.localName == 'head'){
|
|
613
615
|
let title = this.project.getInfoString('UIApplicationTitle', this.resources);
|
|
614
616
|
element.appendChild(document.createTextNode(title));
|
|
617
|
+
}else if (element.localName == 'link' && element.parentNode.localName == 'head' && element.getAttribute("rel") == "manifest"){
|
|
618
|
+
let href = element.getAttribute("href");
|
|
619
|
+
if (href !== "" && href !== null){
|
|
620
|
+
this.webApplicationManifestURL = JSURL.initWithString(href, toURL);
|
|
621
|
+
}
|
|
615
622
|
}else if (element.localName == 'script' && element.getAttribute('type') == 'text/javascript'){
|
|
616
623
|
if (element.hasAttribute("jskit")){
|
|
617
624
|
let params = {
|
|
@@ -653,8 +660,8 @@ JSClass("HTMLBuilder", Builder, {
|
|
|
653
660
|
}
|
|
654
661
|
}
|
|
655
662
|
}
|
|
656
|
-
if (this.
|
|
657
|
-
document.documentElement.setAttribute('manifest', this.
|
|
663
|
+
if (this.legacyAppcacheManifestURL){
|
|
664
|
+
document.documentElement.setAttribute('manifest', this.legacyAppcacheManifestURL.encodedStringRelativeTo(this.wwwURL));
|
|
658
665
|
}
|
|
659
666
|
let serializer = new XMLSerializer();
|
|
660
667
|
html = serializer.serializeToString(document);
|
|
@@ -785,8 +792,24 @@ JSClass("HTMLBuilder", Builder, {
|
|
|
785
792
|
await this.fileManager.createFileAtURL(this.preflightURL, contents);
|
|
786
793
|
},
|
|
787
794
|
|
|
788
|
-
|
|
789
|
-
if (!this.
|
|
795
|
+
updateWebApplicationManifest: async function(){
|
|
796
|
+
if (!this.webApplicationManifestURL){
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
let data = await this.fileManager.contentsAtURL(this.webApplicationManifestURL);
|
|
800
|
+
let manifest = JSON.parse(data.stringByDecodingUTF8());
|
|
801
|
+
if (!manifest.name){
|
|
802
|
+
manifest.name = this.project.getInfoString('UIApplicationTitle', this.resources);
|
|
803
|
+
}
|
|
804
|
+
if (!manifest.icons){
|
|
805
|
+
manifest.icons = this.manifestIcons();
|
|
806
|
+
}
|
|
807
|
+
data = JSON.stringify(manifest).utf8();
|
|
808
|
+
await this.fileManager.createFileAtURL(this.webApplicationManifestURL, data);
|
|
809
|
+
},
|
|
810
|
+
|
|
811
|
+
buildLegacyAppcacheManifest: async function(){
|
|
812
|
+
if (!this.legacyAppcacheManifestURL){
|
|
790
813
|
return;
|
|
791
814
|
}
|
|
792
815
|
this.printer.setStatus("Creating manifest.appcache...");
|
|
@@ -827,7 +850,7 @@ JSClass("HTMLBuilder", Builder, {
|
|
|
827
850
|
lines.push("*");
|
|
828
851
|
lines.push("");
|
|
829
852
|
let txt = lines.join("\n");
|
|
830
|
-
await this.fileManager.createFileAtURL(this.
|
|
853
|
+
await this.fileManager.createFileAtURL(this.legacyAppcacheManifestURL, txt.utf8());
|
|
831
854
|
},
|
|
832
855
|
|
|
833
856
|
applicationIcons: function(){
|
|
@@ -840,15 +863,48 @@ JSClass("HTMLBuilder", Builder, {
|
|
|
840
863
|
let images = contents.images;
|
|
841
864
|
for (let i = 0, l = images.length; i < l; ++i){
|
|
842
865
|
let image = images[i];
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
rel
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
866
|
+
if (image.use === undefined || image.use === "web-icon" || image.use == "app-icon-base"){
|
|
867
|
+
let metadata = this.resources.getMetadata(image.filename, setName);
|
|
868
|
+
let bundledURL = this.resourcesURL.appendingPathComponent(metadata.hash + metadata.ext);
|
|
869
|
+
let rel = "icon";
|
|
870
|
+
if (image.use === "app-icon-base"){
|
|
871
|
+
rel = "apple-touch-icon";
|
|
872
|
+
}else if (image.mask){
|
|
873
|
+
rel = "mask-icon";
|
|
874
|
+
}
|
|
875
|
+
icons.push({
|
|
876
|
+
rel: rel,
|
|
877
|
+
href: bundledURL.encodedStringRelativeTo(this.wwwURL),
|
|
878
|
+
type: metadata.mimetype,
|
|
879
|
+
sizes: (image.vector || metadata.image.vector) ? "any" : '%dx%d'.sprintf(metadata.image.width, metadata.image.height),
|
|
880
|
+
color: image.color || null
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
return icons;
|
|
886
|
+
},
|
|
887
|
+
|
|
888
|
+
manifestIcons: function(){
|
|
889
|
+
var icons = [];
|
|
890
|
+
let setName = this.project.info.UIApplicationIcon;
|
|
891
|
+
if (setName){
|
|
892
|
+
setName += '.imageset';
|
|
893
|
+
let metadata = this.resources.getMetadata('Contents.json', setName);
|
|
894
|
+
let contents = metadata.value;
|
|
895
|
+
let images = contents.images;
|
|
896
|
+
for (let i = 0, l = images.length; i < l; ++i){
|
|
897
|
+
let image = images[i];
|
|
898
|
+
if (image.use === "app-icon" || image.use === "app-icon-base"){
|
|
899
|
+
let metadata = this.resources.getMetadata(image.filename, setName);
|
|
900
|
+
let bundledURL = this.resourcesURL.appendingPathComponent(metadata.hash + metadata.ext);
|
|
901
|
+
icons.push({
|
|
902
|
+
src: bundledURL.encodedStringRelativeTo(this.wwwURL),
|
|
903
|
+
type: metadata.mimetype,
|
|
904
|
+
sizes: metadata.image.vector ? "any" : '%dx%d'.sprintf(metadata.image.width, metadata.image.height),
|
|
905
|
+
purpose: image.use === "app-icon-base" ? "maskable" : "any"
|
|
906
|
+
});
|
|
907
|
+
}
|
|
852
908
|
}
|
|
853
909
|
}
|
|
854
910
|
return icons;
|
|
@@ -948,8 +1004,8 @@ JSClass("HTMLBuilder", Builder, {
|
|
|
948
1004
|
indexPath,
|
|
949
1005
|
bootstrapperPath
|
|
950
1006
|
];
|
|
951
|
-
if (this.
|
|
952
|
-
manifestPath = this.
|
|
1007
|
+
if (this.legacyAppcacheManifestURL !== null){
|
|
1008
|
+
manifestPath = this.legacyAppcacheManifestURL.encodedStringRelativeTo(this.wwwURL);
|
|
953
1009
|
excludes.push(manifestPath);
|
|
954
1010
|
}
|
|
955
1011
|
if (this.serviceWorkerURL !== null){
|
package/Node/Resources.js
CHANGED
|
@@ -291,136 +291,37 @@ var addMetadata = {
|
|
|
291
291
|
},
|
|
292
292
|
|
|
293
293
|
'.png': async function(name, contents, metadata){
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
contents[1] == 0x50 &&
|
|
298
|
-
contents[2] == 0x4E &&
|
|
299
|
-
contents[3] == 0x47 &&
|
|
300
|
-
contents[4] == 0x0D &&
|
|
301
|
-
contents[5] == 0x0A &&
|
|
302
|
-
contents[6] == 0x1A &&
|
|
303
|
-
contents[7] == 0x0A &&
|
|
304
|
-
|
|
305
|
-
// IHDR
|
|
306
|
-
contents[12] == 0x49 &&
|
|
307
|
-
contents[13] == 0x48 &&
|
|
308
|
-
contents[14] == 0x44 &&
|
|
309
|
-
contents[15] == 0x52)
|
|
310
|
-
{
|
|
311
|
-
var dataView = contents.dataView();
|
|
294
|
+
var contentType = JSImage.contentTypeOfData(contents);
|
|
295
|
+
if (contentType !== null && contentType.subtype === "png"){
|
|
296
|
+
var image = JSImage.initWithData(contents);
|
|
312
297
|
metadata.image = {
|
|
313
|
-
width:
|
|
314
|
-
height:
|
|
298
|
+
width: image.size.width,
|
|
299
|
+
height: image.size.height
|
|
315
300
|
};
|
|
316
301
|
}
|
|
317
302
|
},
|
|
318
303
|
|
|
319
304
|
'.jpg': async function(name, contents, metadata){
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
var l = contents.length;
|
|
328
|
-
var blockLength;
|
|
329
|
-
var blockdata;
|
|
330
|
-
while (i < l){
|
|
331
|
-
b = contents[i++];
|
|
332
|
-
if (b != 0xFF){
|
|
333
|
-
// TODO: Error, not at a maker
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
if (i == l){
|
|
337
|
-
// TODO: Error, not enough room for marker
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
b = contents[i++];
|
|
341
|
-
if (b == 0x00){
|
|
342
|
-
// TODO: Error, invalid marker
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
// D0-D9 are standalone markers...make sure not to look for a length
|
|
346
|
-
if (b < 0xD0 || b > 0xD9){
|
|
347
|
-
if (i >= l - 2){
|
|
348
|
-
// TODO: Error, not enough room for block header
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
blockLength = dataView.getUint16(i);
|
|
352
|
-
if (i + blockLength > l){
|
|
353
|
-
// TODO: Error, not enough room for block data
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
// C0-CF are start of frame blocks, expect for C4 and CC
|
|
357
|
-
// start of frame blocks have image sizes
|
|
358
|
-
if (b >= 0xC0 && b <= 0xCF && b != 0xC4 && b != 0xCC){
|
|
359
|
-
if (blockLength >= 7){
|
|
360
|
-
metadata.image = {
|
|
361
|
-
height: dataView.getUint16(i + 3),
|
|
362
|
-
width: dataView.getUint16(i + 5)
|
|
363
|
-
};
|
|
364
|
-
}
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
i += blockLength;
|
|
368
|
-
}
|
|
305
|
+
var contentType = JSImage.contentTypeOfData(contents);
|
|
306
|
+
if (contentType !== null && contentType.subtype === "jpeg"){
|
|
307
|
+
var image = JSImage.initWithData(contents);
|
|
308
|
+
metadata.image = {
|
|
309
|
+
width: image.size.width,
|
|
310
|
+
height: image.size.height
|
|
311
|
+
};
|
|
369
312
|
}
|
|
370
313
|
},
|
|
371
314
|
|
|
372
315
|
'.svg': async function(name, contents, metadata){
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
'px': 1,
|
|
383
|
-
'in': 72,
|
|
384
|
-
'cm': 72/2.54,
|
|
385
|
-
'mm': 72/25.4,
|
|
386
|
-
'pt': 1,
|
|
387
|
-
'pc': 12
|
|
388
|
-
};
|
|
389
|
-
var px = function(length){
|
|
390
|
-
if (length === undefined || length === null){
|
|
391
|
-
return undefined;
|
|
392
|
-
}
|
|
393
|
-
var matches = length.match(/^\s*(\d+)\s*(em|ex|px|in|cm|mm|pt|pc|%)?\s*$/);
|
|
394
|
-
if (!matches){
|
|
395
|
-
return undefined;
|
|
396
|
-
}
|
|
397
|
-
let n = parseInt(matches[1]);
|
|
398
|
-
if (!matches[2]){
|
|
399
|
-
return n;
|
|
400
|
-
}
|
|
401
|
-
let unit = matches[2];
|
|
402
|
-
if (unit == '%'){
|
|
403
|
-
return undefined;
|
|
404
|
-
}
|
|
405
|
-
return multiple[unit] * n;
|
|
406
|
-
};
|
|
407
|
-
if (namespace == 'http://www.w3.org/2000/svg' && name.toLowerCase() == 'svg'){
|
|
408
|
-
let width = attributes.get("width");
|
|
409
|
-
let height = attributes.get("height");
|
|
410
|
-
let viewBox = attributes.get("viewBox");
|
|
411
|
-
if (width && height){
|
|
412
|
-
metadata.image.width = px(width);
|
|
413
|
-
metadata.image.height = px(height);
|
|
414
|
-
}else if (viewBox){
|
|
415
|
-
var box = viewBox.split(/\s+/).map(n => parseInt(n));
|
|
416
|
-
metadata.image.width = box[2];
|
|
417
|
-
metadata.image.height = box[3];
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
parser.stop();
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
parser.parse();
|
|
316
|
+
var contentType = JSImage.contentTypeOfData(contents);
|
|
317
|
+
if (contentType !== null && contentType.subtype === "svg+xml"){
|
|
318
|
+
var image = JSImage.initWithData(contents);
|
|
319
|
+
metadata.image = {
|
|
320
|
+
vector: true,
|
|
321
|
+
width: image.size.width,
|
|
322
|
+
height: image.size.height
|
|
323
|
+
};
|
|
324
|
+
}
|
|
424
325
|
},
|
|
425
326
|
|
|
426
327
|
'.ttf': async function(name, contents, metadata){
|
|
@@ -3,14 +3,14 @@ JSBundle.bundles['io.breakside.jskit'] = {
|
|
|
3
3
|
"Info": {
|
|
4
4
|
"JSBundleType": "node",
|
|
5
5
|
"JSBundleIdentifier": "io.breakside.jskit",
|
|
6
|
-
"JSBundleVersion": "2023.
|
|
6
|
+
"JSBundleVersion": "2023.22.0",
|
|
7
7
|
"JSExecutableName": "jskit",
|
|
8
8
|
"NPMOrganization": "breakside",
|
|
9
9
|
"JSResources": [
|
|
10
10
|
"Tests/HTMLTestRunner.js",
|
|
11
11
|
"Tests/NodeTestRunner.js"
|
|
12
12
|
],
|
|
13
|
-
"GitRevision": "
|
|
13
|
+
"GitRevision": "c5e74a677a557876be14d6e2bf15b95202dcea6e"
|
|
14
14
|
},
|
|
15
15
|
"Resources": [
|
|
16
16
|
{
|
|
@@ -417,16 +417,18 @@ _JSDataImage.sizeFromSVGData = function(data){
|
|
|
417
417
|
return multiple[unit] * n;
|
|
418
418
|
};
|
|
419
419
|
if (namespace == 'http://www.w3.org/2000/svg' && name.toLowerCase() == 'svg'){
|
|
420
|
-
var width = attributes.get("width");
|
|
421
|
-
var height = attributes.get("height");
|
|
420
|
+
var width = px(attributes.get("width"));
|
|
421
|
+
var height = px(attributes.get("height"));
|
|
422
422
|
var viewBox = attributes.get("viewBox");
|
|
423
|
-
if (width && height){
|
|
424
|
-
size.width =
|
|
425
|
-
size.height =
|
|
423
|
+
if (width !== undefined && height !== undefined){
|
|
424
|
+
size.width = width;
|
|
425
|
+
size.height = height;
|
|
426
426
|
}else if (viewBox){
|
|
427
427
|
var box = viewBox.split(/\s+/).map(function(n){ return parseInt(n); });
|
|
428
|
-
|
|
429
|
-
|
|
428
|
+
if (!isNaN(box[2]) && !isNaN(box[3])){
|
|
429
|
+
size.width = box[2];
|
|
430
|
+
size.height = box[3];
|
|
431
|
+
}
|
|
430
432
|
}
|
|
431
433
|
}
|
|
432
434
|
parser.stop();
|
|
@@ -175,7 +175,7 @@ JSURLResponse.error = function(statusCode, object){
|
|
|
175
175
|
|
|
176
176
|
JSGlobalObject.JSURLResponseError = function(response){
|
|
177
177
|
if (this === undefined){
|
|
178
|
-
return new JSURLResponseError();
|
|
178
|
+
return new JSURLResponseError(response);
|
|
179
179
|
}
|
|
180
180
|
this.name = "JSURLResponseError";
|
|
181
181
|
if (response instanceof JSURLResponseError){
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
JSBundleType: framework
|
|
2
2
|
JSBundleIdentifier: io.breakside.JSKit.NotificationKit
|
|
3
|
-
JSBundleVersion: 2023.
|
|
3
|
+
JSBundleVersion: 2023.22.0
|
|
4
4
|
JSDevelopmentLanguage: en
|
|
5
5
|
JSCopyright: Copyright © 2021 Breakside Inc.
|
|
6
6
|
JSBundleEnvironments:
|
|
7
7
|
html: NotificationKit+HTML.js
|
|
8
|
+
node: NotificationKit+Node.js
|
|
8
9
|
JSLicenseNotice: |
|
|
9
10
|
Licensed under the Breakside Public License, Version 1.0 (the "License");
|
|
10
11
|
you may not use this file except in compliance with the License.
|
|
@@ -35,44 +35,85 @@ JSClass("NKHTMLUserNotificationCenter", NKUserNotificationCenter, {
|
|
|
35
35
|
domDocument: null,
|
|
36
36
|
serviceWorkerContainer: null,
|
|
37
37
|
|
|
38
|
-
registerForRemoteNotifications: function(
|
|
38
|
+
registerForRemoteNotifications: function(completion, target){
|
|
39
39
|
if (!completion){
|
|
40
|
-
completion = Promise.completion();
|
|
40
|
+
completion = Promise.completion(Promise.rejectNonNullSecondArgument);
|
|
41
41
|
}
|
|
42
42
|
if (this.serviceWorkerContainer === null){
|
|
43
|
-
|
|
44
|
-
JSRunLoop.main.schedule(completion, target, null);
|
|
43
|
+
JSRunLoop.main.schedule(completion, target, null, new Error("Remote notifications not supported on this device (no service workers)"));
|
|
45
44
|
}else{
|
|
45
|
+
var notificationCenter = this;
|
|
46
46
|
this.serviceWorkerContainer.getRegistration().then(function(serviceWorkerRegistration){
|
|
47
47
|
if (!serviceWorkerRegistration){
|
|
48
|
-
|
|
49
|
-
completion.call(target, null);
|
|
48
|
+
completion.call(target, null, new Error("Missing service worker registration"));
|
|
50
49
|
return;
|
|
51
50
|
}
|
|
52
51
|
if (!serviceWorkerRegistration.pushManager){
|
|
53
|
-
|
|
54
|
-
completion.call(target, null);
|
|
52
|
+
completion.call(target, null, new Error("Remote notifications not supported on this device (no push manager)"));
|
|
55
53
|
return;
|
|
56
54
|
}
|
|
57
|
-
var options = {
|
|
58
|
-
|
|
55
|
+
var options = {
|
|
56
|
+
userVisibleOnly: true
|
|
57
|
+
};
|
|
58
|
+
if (notificationCenter.webPushApplicationServerJWK){
|
|
59
59
|
options.applicationServerKey = JSData.initWithChunks([
|
|
60
60
|
JSData.initWithArray([0x04]),
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
notificationCenter.webPushApplicationServerJWK.x.dataByDecodingBase64URL(),
|
|
62
|
+
notificationCenter.webPushApplicationServerJWK.y.dataByDecodingBase64URL()
|
|
63
63
|
]).base64URLStringRepresentation();
|
|
64
64
|
}
|
|
65
65
|
serviceWorkerRegistration.pushManager.subscribe(options).then(function(subscription){
|
|
66
66
|
var registration = {
|
|
67
67
|
type: NKUserNotificationCenter.RegistrationType.web,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
options: {
|
|
69
|
+
applicationServerJWK: notificationCenter.webPushApplicationServerJWK,
|
|
70
|
+
},
|
|
71
|
+
subscription: {
|
|
72
|
+
endpoint: subscription.endpoint,
|
|
73
|
+
expirationTime: subscription.expirationTime,
|
|
74
|
+
keys: {
|
|
75
|
+
p256dh: JSData.initWithBuffer(subscription.getKey("p256dh")).base64URLStringRepresentation(),
|
|
76
|
+
auth: JSData.initWithBuffer(subscription.getKey("auth")).base64URLStringRepresentation()
|
|
77
|
+
},
|
|
78
|
+
}
|
|
71
79
|
};
|
|
72
|
-
completion.call(target, registration);
|
|
80
|
+
completion.call(target, registration, null);
|
|
81
|
+
}, function(error){
|
|
82
|
+
completion.call(target, null, error);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return completion.promise;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
unregisterForRemoteNotifications: function(completion, target){
|
|
90
|
+
if (!completion){
|
|
91
|
+
completion = Promise.completion(Promise.rejectNonNullFirstArgument);
|
|
92
|
+
}
|
|
93
|
+
if (this.serviceWorkerContainer === null){
|
|
94
|
+
JSRunLoop.main.schedule(completion, target, new Error("Remote notifications not supported on this device (no service workers)"));
|
|
95
|
+
}else{
|
|
96
|
+
this.serviceWorkerContainer.getRegistration().then(function(serviceWorkerRegistration){
|
|
97
|
+
if (!serviceWorkerRegistration){
|
|
98
|
+
completion.call(target, new Error("Missing service worker registration"));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (!serviceWorkerRegistration.pushManager){
|
|
102
|
+
completion.call(target, new Error("Remote notifications not supported on this device (no push manager)"));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
serviceWorkerRegistration.pushManager.getSubscription().then(function(subscription){
|
|
106
|
+
if (subscription){
|
|
107
|
+
subscription.unsubscribe(function(){
|
|
108
|
+
completion.call(target, null);
|
|
109
|
+
}, function(error){
|
|
110
|
+
completion.call(target, error);
|
|
111
|
+
});
|
|
112
|
+
}else{
|
|
113
|
+
completion.call(target, null);
|
|
114
|
+
}
|
|
73
115
|
}, function(error){
|
|
74
|
-
|
|
75
|
-
completion.call(target, null);
|
|
116
|
+
completion.call(target, error);
|
|
76
117
|
});
|
|
77
118
|
});
|
|
78
119
|
}
|
|
@@ -21,6 +21,8 @@ JSClass("NKUserNotification", JSObject, {
|
|
|
21
21
|
title: null,
|
|
22
22
|
body: null,
|
|
23
23
|
identifier: null,
|
|
24
|
+
timeToLive: 3600,
|
|
25
|
+
priority: 0,
|
|
24
26
|
|
|
25
27
|
initWithTitle: function(title, body){
|
|
26
28
|
this.title = title;
|
|
@@ -28,4 +30,13 @@ JSClass("NKUserNotification", JSObject, {
|
|
|
28
30
|
this.identifier = UUID();
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
NKUserNotification.Priority = {
|
|
36
|
+
|
|
37
|
+
normal: 0,
|
|
38
|
+
veryLow: 1,
|
|
39
|
+
low: 2,
|
|
40
|
+
high: 3,
|
|
41
|
+
|
|
42
|
+
};
|