@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.
Files changed (63) hide show
  1. package/Frameworks/DOM.jsframework/Info.json +2 -2
  2. package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
  3. package/Frameworks/FontKit.jsframework/Info.json +2 -2
  4. package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
  5. package/Frameworks/Foundation.jsframework/Info.json +2 -2
  6. package/Frameworks/Foundation.jsframework/JS/JSImage.js +9 -7
  7. package/Frameworks/Foundation.jsframework/JS/JSURLResponse.js +1 -1
  8. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  9. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  10. package/Frameworks/SecurityKit.jsframework/JS/SECCipher+HTML.js +31 -44
  11. package/Frameworks/SecurityKit.jsframework/JS/SECCipher+Node.js +16 -38
  12. package/Frameworks/SecurityKit.jsframework/JS/SECCipher.js +33 -3
  13. package/Frameworks/SecurityKit.jsframework/JS/SECHMAC.js +17 -0
  14. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  15. package/Info.json +2 -2
  16. package/Node/HTMLBuilder.js +76 -20
  17. package/Node/Resources.js +21 -120
  18. package/Node/io.breakside.jskit-bundle.js +2 -2
  19. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  20. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  21. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  22. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  23. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  24. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  25. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  26. package/Root/Frameworks/DOM/Info.yaml +1 -1
  27. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  28. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  29. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  30. package/Root/Frameworks/Foundation/JSImage.js +9 -7
  31. package/Root/Frameworks/Foundation/JSURLResponse.js +1 -1
  32. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  33. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  34. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  35. package/Root/Frameworks/NotificationKit/Info.yaml +2 -1
  36. package/Root/Frameworks/NotificationKit/NKHTMLUserNotificationCenter.js +59 -18
  37. package/Root/Frameworks/NotificationKit/NKUserNotification.js +12 -1
  38. package/Root/Frameworks/NotificationKit/NKUserNotificationCenter.js +5 -0
  39. package/Root/Frameworks/NotificationKit/NKWebPushService.js +145 -38
  40. package/Root/Frameworks/NotificationKit/NotificationKit+Node.js +19 -0
  41. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  42. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  43. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  44. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  45. package/Root/Frameworks/SecurityKit/SECCipher+HTML.js +31 -44
  46. package/Root/Frameworks/SecurityKit/SECCipher+Node.js +16 -38
  47. package/Root/Frameworks/SecurityKit/SECCipher.js +33 -3
  48. package/Root/Frameworks/SecurityKit/SECHMAC.js +17 -0
  49. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  50. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  51. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  52. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  53. package/Root/Frameworks/UIKit/UIHTMLApplication.js +2 -0
  54. package/Root/Frameworks/UIKit/UIHTMLContentEditableTextInputManager.js +1 -1
  55. package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +8 -1
  56. package/Root/Frameworks/UIKit/UIListView.js +4 -2
  57. package/Root/Frameworks/UIKit/UIWindowServer.js +7 -13
  58. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  59. package/Root/Templates/html/${PROJECT_NAME}/www/${PROJECT_NAME}.webmanifest +9 -0
  60. package/Root/Templates/html/${PROJECT_NAME}/www/HTMLAppBootstrapper.js +7 -1
  61. package/Root/Templates/html/${PROJECT_NAME}/www/index.html +1 -0
  62. package/Root/Templates/html/${PROJECT_NAME}/www/service-worker.js +25 -1
  63. package/package.json +1 -1
@@ -484,7 +484,8 @@ JSClass("HTMLBuilder", Builder, {
484
484
  wwwCSSPaths: null,
485
485
  wwwPaths: null,
486
486
  uncachedWWWPaths: null,
487
- manifestURL: null,
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 `manifestURL` `null` will disable the
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.manifestURL = this.wwwURL.appendingPathComponent("manifest.appcache");
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.buildManifest();
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.manifestURL){
657
- document.documentElement.setAttribute('manifest', this.manifestURL.encodedStringRelativeTo(this.wwwURL));
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
- buildManifest: async function(){
789
- if (!this.manifestURL){
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.manifestURL, txt.utf8());
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
- let metadata = this.resources.getMetadata(image.filename, setName);
844
- let bundledURL = this.resourcesURL.appendingPathComponent(metadata.hash + metadata.ext);
845
- icons.push({
846
- rel: image.mask ? "mask-icon" : "icon",
847
- href: bundledURL.encodedStringRelativeTo(this.wwwURL),
848
- type: metadata.mimetype,
849
- sizes: image.vector ? "any" : '%dx%d'.sprintf(metadata.image.width, metadata.image.height),
850
- color: image.color || null
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.manifestURL !== null){
952
- manifestPath = this.manifestURL.encodedStringRelativeTo(this.wwwURL);
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
- if (contents.length >= 24 &&
295
- // magic
296
- contents[0] == 0x89 &&
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: dataView.getUint32(16),
314
- height: dataView.getUint32(20)
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
- if (contents.length < 2 || contents[0] != 0xFF || contents[1] != 0xD8){
321
- // not a jpeg
322
- return;
323
- }
324
- var dataView = contents.dataView();
325
- var i = 0;
326
- var b;
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
- metadata.image = {
374
- vector: true
375
- };
376
- var parser = JSXMLParser.initWithData(contents);
377
- parser.delegate = {
378
- xmlParserDidBeginElement: function(parser, name, prefix, namespace, attributes){
379
- var multiple = {
380
- 'em': 12,
381
- 'ex': 24,
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.20.0",
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": "494ac01eeb44fd4791c7d3e18b161ad60ba0f8da"
13
+ "GitRevision": "c5e74a677a557876be14d6e2bf15b95202dcea6e"
14
14
  },
15
15
  "Resources": [
16
16
  {
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.APIKit
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSIncludeDirectories:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.APIKitTesting
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.AuthKit
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSLicenseNotice: |
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.CSSOM
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ChartKit
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2021 Breakside Inc.
6
6
  # JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ConferenceKit
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.DBKit
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.DOM
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.Dispatch
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSCopyright: Copyright © 2020 Breakside Inc.
5
5
  JSBundleEnvironments:
6
6
  html: Dispatch+HTML.js
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.FontKit
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.Foundation
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -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 = px(width);
425
- size.height = px(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
- size.width = box[2];
429
- size.height = box[3];
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,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.ImageKit
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.MediaKit
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,6 +1,6 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.MediaKitUI
3
- JSBundleVersion: 2023.20.0
3
+ JSBundleVersion: 2023.22.0
4
4
  JSDevelopmentLanguage: en
5
5
  JSCopyright: Copyright © 2020 Breakside Inc.
6
6
  JSBundleEnvironments:
@@ -1,10 +1,11 @@
1
1
  JSBundleType: framework
2
2
  JSBundleIdentifier: io.breakside.JSKit.NotificationKit
3
- JSBundleVersion: 2023.20.0
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(request, completion, target){
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
- logger.warn("Remote notifications not supported on this device (no service workers)");
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
- logger.warn("Missing service worker registration");
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
- logger.warn("Remote notifications not supported on this device (no push manager)");
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
- if (request.signingJWK !== null){
55
+ var options = {
56
+ userVisibleOnly: true
57
+ };
58
+ if (notificationCenter.webPushApplicationServerJWK){
59
59
  options.applicationServerKey = JSData.initWithChunks([
60
60
  JSData.initWithArray([0x04]),
61
- request.signingJWK.x.dataByDecodingBase64URL(),
62
- request.signingJWK.y.dataByDecodingBase64URL()
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
- endpoint: subscription.endpoint,
69
- encryptionKey: JSData.initWithBuffer(subscription.getKey("p256dh")).base64URLStringRepresentation(),
70
- secret: JSData.initWithBuffer(subscription.getKey("auth")).base64URLStringRepresentation()
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
- logger.error("Failed to subscribe to push: %{error}", error);
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
+ };