tfjs-evolution 0.0.3 → 0.0.5

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/README.md CHANGED
@@ -4,6 +4,78 @@ This is a placeholder for a package I am planning to publish.
4
4
  I have experimented with Evolutionary Computing in TensorFlow.js, and I am planning a paper and a NPN package.
5
5
  I was unable to find a NPM package, and decided to create one!
6
6
 
7
+ I am releasing the parts by parts.
8
+ You can start to test the released parts, and let me know your thoughts.
9
+
10
+ # Seeing your images
11
+
12
+ You can see your images, it can useful to have an idea about how it looks like, what you are trying to feed the model.
13
+ Remember: "garbage in garbage out".
14
+
15
+ Import the directive for the panel.
16
+
17
+ `import { DisplayPanelComponent } from 'tfjs-evolution';`
18
+
19
+
20
+ Make sure to import the component. Angular is now standalone by default.
21
+
22
+ ` imports: [DisplayPanelComponent]`
23
+
24
+ Create a local instance, for calling the component method.
25
+
26
+ ` @ViewChild(DisplayPanelComponent) child!:DisplayPanelComponent;`
27
+
28
+ Add a list of class names. It must be the same on the folder names.
29
+
30
+ ```` typescript
31
+ classes_names=[
32
+ "Canário-da-Terra",
33
+ "Pardal",
34
+ "Bem-Te-Vi",
35
+ "Suiriri-Cavaleiro",
36
+ "Lavadeira-Mascarada",
37
+ "Anu-Branco",
38
+ "Beija-Flor-Tesoura",
39
+ "Casaca-de-Couro-da-Lama",
40
+ "Garibaldi",
41
+ "Periquitão",
42
+ "Pombo-Doméstico",
43
+ "Quero-Quero",
44
+ "Rolinha-Roxa",
45
+ "Sabiá-Do-Campo",
46
+ "Urubu-de-Cabeça-Preta"
47
+ ]
48
+ ````
49
+ Make the call for loading images. You could most likely make the call at the constructor.
50
+
51
+
52
+ ```typescript
53
+ ngAfterViewInit(): void {
54
+ const options={
55
+ base: "./assets/dataset",//base where are your iamges
56
+ file_name:"image",//how your images are named
57
+ file_extension:"jpeg" //extension used
58
+ };
59
+
60
+ //Finally, call the load images methods
61
+ this.child.loadImages(5, options)
62
+ }
63
+ ```
64
+
65
+ Finally, do not forget to add the HTML code
66
+
67
+ ```html
68
+ <h1>Images used on the training</h1>
69
+ <neuroevolution-display-panel></neuroevolution-display-panel>
70
+
71
+ ```
72
+
73
+ You should see in our HTML file, in Angular, the images
74
+
75
+
76
+ # Updates
77
+
78
+ I have finished! Hope to publish a paper soon!
7
79
 
8
80
  # Further help
9
81
 
@@ -1,29 +1,15 @@
1
1
  import { Component } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
+ import { TeachableMobileNet } from '../../models/teachable-evolution';
3
4
  import * as i0 from "@angular/core";
4
5
  import * as i1 from "@angular/common";
5
6
  export class DisplayPanelComponent {
6
7
  constructor() {
7
8
  this.classes = [];
8
- this.classes_names = [
9
- "Canário-da-Terra",
10
- "Pardal",
11
- "Bem-Te-Vi",
12
- "Suiriri-Cavaleiro",
13
- "Lavadeira-Mascarada",
14
- "Anu-Branco",
15
- "Beija-Flor-Tesoura",
16
- "Casaca-de-Couro-da-Lama",
17
- "Garibaldi",
18
- "Periquitão",
19
- "Pombo-Doméstico",
20
- "Quero-Quero",
21
- "Rolinha-Roxa",
22
- "Sabiá-Do-Campo",
23
- "Urubu-de-Cabeça-Preta"
24
- ];
25
9
  }
26
- loadImages(number_of_species, options) {
10
+ loadImages(number_of_species, classes_names, options) {
11
+ this.classes_names = classes_names;
12
+ this.number_of_samples_per_class = number_of_species;
27
13
  this.add_species(number_of_species, options);
28
14
  }
29
15
  add_species(number_of_species, options) {
@@ -33,16 +19,41 @@ export class DisplayPanelComponent {
33
19
  }
34
20
  add_images(name, number_of_species, options) {
35
21
  const class_add = [];
36
- for (let i = 1; i < number_of_species; i++) {
37
- class_add.push(`${options.base}/${name}/${options.file_name} ${i}.${options.file_extension}`);
22
+ for (let i = 0; i < number_of_species; i++) {
23
+ class_add.push(`${options.base}/${name}/${options.file_name} ${i + 1}.${options.file_extension}`);
38
24
  }
39
25
  this.classes.push({ name: name, images: class_add });
40
26
  }
27
+ async addexamples() {
28
+ //This is needed to make sure it gives time for the images to upload
29
+ //The images upload very fast, what makes this method execute before the images are on HTML
30
+ //It can be removed if somehow this method is just called after the images are available.
31
+ // console.log("Loading examples as tensors....")
32
+ await this.delay(0);
33
+ for (let i = 0; i < this.classes_names.length; i++) {
34
+ await this.add_example(this.classes_names[i], this.number_of_samples_per_class);
35
+ }
36
+ }
37
+ async add_example(name, number_of_species) {
38
+ const class_add = [];
39
+ // console.log(name)
40
+ for (let i = 0; i < number_of_species; i++) {
41
+ //Collecting the images from HTML
42
+ const aux = document.getElementById(`class-${name}-${i}`);
43
+ //Adding the example
44
+ const index = this.classes_names.findIndex((elem) => elem === name);
45
+ await TeachableMobileNet.addExample(index, name, aux);
46
+ }
47
+ // this.classes.push({name: name, images: class_add})
48
+ }
49
+ delay(ms) {
50
+ return new Promise((resolve) => setTimeout(resolve, ms));
51
+ }
41
52
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.4", ngImport: i0, type: DisplayPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
42
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.4", type: DisplayPanelComponent, isStandalone: true, selector: "neuroevolution-display-panel", ngImport: i0, template: "<div *ngFor=\"let class of classes; index as i\">\r\n <h1>{{class.name}}</h1>\r\n <img *ngFor=\"let item of class.images; index as i\" [src]=\"item\" width=\"10%\" height=\"10%\" [id]=\"'class-1-' + i\" crossorigin=\"anonymous\" >\r\n</div>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] }); }
53
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.4", type: DisplayPanelComponent, isStandalone: true, selector: "neuroevolution-display-panel", ngImport: i0, template: "<div *ngFor=\"let class of classes; index as i\">\r\n <h1>{{class.name}}</h1>\r\n <img *ngFor=\"let item of class.images; index as i\" [src]=\"item\" width=\"224\" height=\"224\" [id]=\"'class-' + class.name + '-' + i\" crossorigin=\"anonymous\" >\r\n</div>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] }); }
43
54
  }
44
55
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.4", ngImport: i0, type: DisplayPanelComponent, decorators: [{
45
56
  type: Component,
46
- args: [{ selector: 'neuroevolution-display-panel', standalone: true, imports: [CommonModule], template: "<div *ngFor=\"let class of classes; index as i\">\r\n <h1>{{class.name}}</h1>\r\n <img *ngFor=\"let item of class.images; index as i\" [src]=\"item\" width=\"10%\" height=\"10%\" [id]=\"'class-1-' + i\" crossorigin=\"anonymous\" >\r\n</div>\r\n" }]
57
+ args: [{ selector: 'neuroevolution-display-panel', standalone: true, imports: [CommonModule], template: "<div *ngFor=\"let class of classes; index as i\">\r\n <h1>{{class.name}}</h1>\r\n <img *ngFor=\"let item of class.images; index as i\" [src]=\"item\" width=\"224\" height=\"224\" [id]=\"'class-' + class.name + '-' + i\" crossorigin=\"anonymous\" >\r\n</div>\r\n" }]
47
58
  }] });
48
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzcGxheS1wYW5lbC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy90ZmpzLWV2b2x1dGlvbi9zcmMvbGliL2NvbXBvbmVudHMvZGlzcGxheS1wYW5lbC9kaXNwbGF5LXBhbmVsLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RmanMtZXZvbHV0aW9uL3NyYy9saWIvY29tcG9uZW50cy9kaXNwbGF5LXBhbmVsL2Rpc3BsYXktcGFuZWwuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUV6QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7OztBQVMvQyxNQUFNLE9BQU8scUJBQXFCO0lBUGxDO1FBU0UsWUFBTyxHQUFVLEVBQUUsQ0FBQztRQUVwQixrQkFBYSxHQUFDO1lBQ1osa0JBQWtCO1lBQ2xCLFFBQVE7WUFDUixXQUFXO1lBQ1gsbUJBQW1CO1lBQ25CLHFCQUFxQjtZQUNyQixZQUFZO1lBQ1osb0JBQW9CO1lBQ3BCLHlCQUF5QjtZQUN6QixXQUFXO1lBQ1gsWUFBWTtZQUNaLGlCQUFpQjtZQUNqQixhQUFhO1lBQ2IsY0FBYztZQUNkLGdCQUFnQjtZQUNoQix1QkFBdUI7U0FDeEIsQ0FBQTtLQTJCRjtJQXhCQyxVQUFVLENBQUMsaUJBQXlCLEVBQUUsT0FBZTtRQUNuRCxJQUFJLENBQUMsV0FBVyxDQUFDLGlCQUFpQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQy9DLENBQUM7SUFHRCxXQUFXLENBQUMsaUJBQXlCLEVBQUUsT0FBZTtRQUVwRCxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDbEQsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxFQUFFLGlCQUFpQixFQUFFLE9BQU8sQ0FBQyxDQUFDO1NBQ3BFO0lBQ0gsQ0FBQztJQUVELFVBQVUsQ0FBQyxJQUFZLEVBQUUsaUJBQXlCLEVBQUUsT0FBWTtRQUU5RCxNQUFNLFNBQVMsR0FBTyxFQUFFLENBQUM7UUFFekIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLGlCQUFpQixFQUFFLENBQUMsRUFBRSxFQUFFO1lBQzFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsR0FBRyxPQUFPLENBQUMsSUFBSSxJQUFJLElBQUksSUFBSSxPQUFPLENBQUMsU0FBUyxJQUFJLENBQUMsSUFBSSxPQUFPLENBQUMsY0FBYyxFQUFFLENBQUMsQ0FBQztTQUNqRztRQUVELElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFDLENBQUMsQ0FBQTtJQUVsRCxDQUFDOzhHQTdDVSxxQkFBcUI7a0dBQXJCLHFCQUFxQix3RkNYbEMseVBBSUEseURER1ksWUFBWTs7MkZBSVgscUJBQXFCO2tCQVBqQyxTQUFTOytCQUNFLDhCQUE4QixjQUM1QixJQUFJLFdBQ1AsQ0FBQyxZQUFZLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnR9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBHcm91cCB9IGZyb20gJy4uLy4uL2ludGVyZmFjZXMvZ3JvdXAnO1xyXG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICduZXVyb2V2b2x1dGlvbi1kaXNwbGF5LXBhbmVsJyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtDb21tb25Nb2R1bGVdLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9kaXNwbGF5LXBhbmVsLmNvbXBvbmVudC5odG1sJyxcclxuICBzdHlsZVVybDogJy4vZGlzcGxheS1wYW5lbC5jb21wb25lbnQuY3NzJ1xyXG59KVxyXG5leHBvcnQgY2xhc3MgRGlzcGxheVBhbmVsQ29tcG9uZW50IHtcclxuXHJcbiAgY2xhc3NlczogR3JvdXBbXT1bXTtcclxuXHJcbiAgY2xhc3Nlc19uYW1lcz1bXHJcbiAgICBcIkNhbsOhcmlvLWRhLVRlcnJhXCIsXHJcbiAgICBcIlBhcmRhbFwiLFxyXG4gICAgXCJCZW0tVGUtVmlcIixcclxuICAgIFwiU3VpcmlyaS1DYXZhbGVpcm9cIixcclxuICAgIFwiTGF2YWRlaXJhLU1hc2NhcmFkYVwiLFxyXG4gICAgXCJBbnUtQnJhbmNvXCIsICBcclxuICAgIFwiQmVpamEtRmxvci1UZXNvdXJhXCIsIFxyXG4gICAgXCJDYXNhY2EtZGUtQ291cm8tZGEtTGFtYVwiLFxyXG4gICAgXCJHYXJpYmFsZGlcIixcclxuICAgIFwiUGVyaXF1aXTDo29cIixcclxuICAgIFwiUG9tYm8tRG9tw6lzdGljb1wiLFxyXG4gICAgXCJRdWVyby1RdWVyb1wiLFxyXG4gICAgXCJSb2xpbmhhLVJveGFcIixcclxuICAgIFwiU2FiacOhLURvLUNhbXBvXCIsXHJcbiAgICBcIlVydWJ1LWRlLUNhYmXDp2EtUHJldGFcIlxyXG4gIF1cclxuXHJcblxyXG4gIGxvYWRJbWFnZXMobnVtYmVyX29mX3NwZWNpZXM6IG51bWJlciwgb3B0aW9uczogb2JqZWN0KXtcclxuICAgIHRoaXMuYWRkX3NwZWNpZXMobnVtYmVyX29mX3NwZWNpZXMsIG9wdGlvbnMpO1xyXG4gIH1cclxuXHJcblxyXG4gIGFkZF9zcGVjaWVzKG51bWJlcl9vZl9zcGVjaWVzOiBudW1iZXIsIG9wdGlvbnM6IG9iamVjdCl7XHJcblxyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmNsYXNzZXNfbmFtZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdGhpcy5hZGRfaW1hZ2VzKHRoaXMuY2xhc3Nlc19uYW1lc1tpXSwgbnVtYmVyX29mX3NwZWNpZXMsIG9wdGlvbnMpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgYWRkX2ltYWdlcyhuYW1lOiBzdHJpbmcsIG51bWJlcl9vZl9zcGVjaWVzOiBudW1iZXIsIG9wdGlvbnM6IGFueSl7ICAgXHJcblxyXG4gICAgY29uc3QgY2xhc3NfYWRkOiBhbnk9IFtdO1xyXG5cclxuICAgIGZvciAobGV0IGkgPSAxOyBpIDwgbnVtYmVyX29mX3NwZWNpZXM7IGkrKykgeyAgICAgIFxyXG4gICAgICBjbGFzc19hZGQucHVzaChgJHtvcHRpb25zLmJhc2V9LyR7bmFtZX0vJHtvcHRpb25zLmZpbGVfbmFtZX0gJHtpfS4ke29wdGlvbnMuZmlsZV9leHRlbnNpb259YCk7XHJcbiAgfVxyXG5cclxuICB0aGlzLmNsYXNzZXMucHVzaCh7bmFtZTogbmFtZSwgaW1hZ2VzOiBjbGFzc19hZGR9KSAgXHJcblxyXG4gIH1cclxuXHJcbn1cclxuIiwiPGRpdiAqbmdGb3I9XCJsZXQgY2xhc3Mgb2YgY2xhc3NlczsgaW5kZXggYXMgaVwiPlxyXG4gIDxoMT57e2NsYXNzLm5hbWV9fTwvaDE+XHJcbiAgPGltZyAgKm5nRm9yPVwibGV0IGl0ZW0gb2YgY2xhc3MuaW1hZ2VzOyBpbmRleCBhcyBpXCIgW3NyY109XCJpdGVtXCIgd2lkdGg9XCIxMCVcIiBoZWlnaHQ9XCIxMCVcIiBbaWRdPVwiJ2NsYXNzLTEtJyArIGlcIiBjcm9zc29yaWdpbj1cImFub255bW91c1wiID5cclxuPC9kaXY+XHJcbiJdfQ==
59
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGlzcGxheS1wYW5lbC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy90ZmpzLWV2b2x1dGlvbi9zcmMvbGliL2NvbXBvbmVudHMvZGlzcGxheS1wYW5lbC9kaXNwbGF5LXBhbmVsLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RmanMtZXZvbHV0aW9uL3NyYy9saWIvY29tcG9uZW50cy9kaXNwbGF5LXBhbmVsL2Rpc3BsYXktcGFuZWwuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBQyxNQUFNLGVBQWUsQ0FBQztBQUV6QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7OztBQVN0RSxNQUFNLE9BQU8scUJBQXFCO0lBUGxDO1FBU0UsWUFBTyxHQUFVLEVBQUUsQ0FBQztLQXlFckI7SUFuRUQsVUFBVSxDQUFDLGlCQUF5QixFQUFFLGFBQXVCLEVBQUUsT0FBZTtRQUUxRSxJQUFJLENBQUMsYUFBYSxHQUFDLGFBQWEsQ0FBQztRQUNqQyxJQUFJLENBQUMsMkJBQTJCLEdBQUMsaUJBQWlCLENBQUM7UUFFbkQsSUFBSSxDQUFDLFdBQVcsQ0FBQyxpQkFBaUIsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUUvQyxDQUFDO0lBR0gsV0FBVyxDQUFDLGlCQUF5QixFQUFFLE9BQWU7UUFFbEQsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ2xELElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsRUFBRSxpQkFBaUIsRUFBRSxPQUFPLENBQUMsQ0FBQztTQUVwRTtJQUVILENBQUM7SUFFRCxVQUFVLENBQUMsSUFBWSxFQUFFLGlCQUF5QixFQUFFLE9BQVk7UUFFOUQsTUFBTSxTQUFTLEdBQU8sRUFBRSxDQUFDO1FBRXpCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxpQkFBaUIsRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUMxQyxTQUFTLENBQUMsSUFBSSxDQUFDLEdBQUcsT0FBTyxDQUFDLElBQUksSUFBSSxJQUFJLElBQUksT0FBTyxDQUFDLFNBQVMsSUFBSSxDQUFDLEdBQUMsQ0FBQyxJQUFJLE9BQU8sQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDO1NBQ25HO1FBRUQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUMsQ0FBQyxDQUFBO0lBRWxELENBQUM7SUFFRixLQUFLLENBQUMsV0FBVztRQUVoQixvRUFBb0U7UUFDcEUsMkZBQTJGO1FBQzNGLHlGQUF5RjtRQUN6RixpREFBaUQ7UUFDakQsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBRXBCLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUNsRCxNQUFNLElBQUksQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsMkJBQTJCLENBQUMsQ0FBQztTQUNqRjtJQUNELENBQUM7SUFFRCxLQUFLLENBQUMsV0FBVyxDQUFDLElBQVksRUFBRSxpQkFBeUI7UUFFdkQsTUFBTSxTQUFTLEdBQU8sRUFBRSxDQUFDO1FBQ3pCLG9CQUFvQjtRQUNwQixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsaUJBQWlCLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFFMUMsaUNBQWlDO1lBQ2pDLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxjQUFjLENBQUMsU0FBUyxJQUFJLElBQUksQ0FBQyxFQUFFLENBQXFCLENBQUM7WUFFOUUsb0JBQW9CO1lBQ3BCLE1BQU0sS0FBSyxHQUFFLElBQUksQ0FBQyxhQUFhLENBQUMsU0FBUyxDQUFDLENBQUMsSUFBSSxFQUFDLEVBQUUsQ0FBQSxJQUFJLEtBQUcsSUFBSSxDQUFDLENBQUE7WUFFOUQsTUFBTSxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQztTQUV6RDtRQUVELHVEQUF1RDtJQUV2RCxDQUFDO0lBRUQsS0FBSyxDQUFDLEVBQVU7UUFDZCxPQUFPLElBQUksT0FBTyxDQUFPLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxVQUFVLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDbkUsQ0FBQzs4R0ExRVkscUJBQXFCO2tHQUFyQixxQkFBcUIsd0ZDWmxDLDBRQUlBLHlERElZLFlBQVk7OzJGQUlYLHFCQUFxQjtrQkFQakMsU0FBUzsrQkFDRSw4QkFBOEIsY0FDNUIsSUFBSSxXQUNQLENBQUMsWUFBWSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50fSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgR3JvdXAgfSBmcm9tICcuLi8uLi9pbnRlcmZhY2VzL2dyb3VwJztcclxuaW1wb3J0IHsgQ29tbW9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcclxuaW1wb3J0IHsgVGVhY2hhYmxlTW9iaWxlTmV0IH0gZnJvbSAnLi4vLi4vbW9kZWxzL3RlYWNoYWJsZS1ldm9sdXRpb24nO1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICduZXVyb2V2b2x1dGlvbi1kaXNwbGF5LXBhbmVsJyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtDb21tb25Nb2R1bGVdLFxyXG4gIHRlbXBsYXRlVXJsOiAnLi9kaXNwbGF5LXBhbmVsLmNvbXBvbmVudC5odG1sJyxcclxuICBzdHlsZVVybDogJy4vZGlzcGxheS1wYW5lbC5jb21wb25lbnQuY3NzJ1xyXG59KVxyXG5leHBvcnQgY2xhc3MgRGlzcGxheVBhbmVsQ29tcG9uZW50IHtcclxuXHJcbiAgY2xhc3NlczogR3JvdXBbXT1bXTtcclxuXHJcbiAgY2xhc3Nlc19uYW1lcyE6IHN0cmluZ1tdO1xyXG5cclxuICBudW1iZXJfb2Zfc2FtcGxlc19wZXJfY2xhc3MhOiBudW1iZXI7XHJcblxyXG5sb2FkSW1hZ2VzKG51bWJlcl9vZl9zcGVjaWVzOiBudW1iZXIsIGNsYXNzZXNfbmFtZXM6IHN0cmluZ1tdLCBvcHRpb25zOiBvYmplY3Qpe1xyXG4gICAgXHJcbiAgICB0aGlzLmNsYXNzZXNfbmFtZXM9Y2xhc3Nlc19uYW1lcztcclxuICAgIHRoaXMubnVtYmVyX29mX3NhbXBsZXNfcGVyX2NsYXNzPW51bWJlcl9vZl9zcGVjaWVzO1xyXG5cclxuICAgIHRoaXMuYWRkX3NwZWNpZXMobnVtYmVyX29mX3NwZWNpZXMsIG9wdGlvbnMpOyAgICBcclxuXHJcbiAgfVxyXG5cclxuXHJcbmFkZF9zcGVjaWVzKG51bWJlcl9vZl9zcGVjaWVzOiBudW1iZXIsIG9wdGlvbnM6IG9iamVjdCl7XHJcblxyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLmNsYXNzZXNfbmFtZXMubGVuZ3RoOyBpKyspIHtcclxuICAgICAgdGhpcy5hZGRfaW1hZ2VzKHRoaXMuY2xhc3Nlc19uYW1lc1tpXSwgbnVtYmVyX29mX3NwZWNpZXMsIG9wdGlvbnMpO1xyXG4gICAgICBcclxuICAgIH1cclxuXHJcbiAgfVxyXG5cclxuICBhZGRfaW1hZ2VzKG5hbWU6IHN0cmluZywgbnVtYmVyX29mX3NwZWNpZXM6IG51bWJlciwgb3B0aW9uczogYW55KXsgICBcclxuXHJcbiAgICBjb25zdCBjbGFzc19hZGQ6IGFueT0gW107XHJcblxyXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBudW1iZXJfb2Zfc3BlY2llczsgaSsrKSB7ICAgICAgXHJcbiAgICAgIGNsYXNzX2FkZC5wdXNoKGAke29wdGlvbnMuYmFzZX0vJHtuYW1lfS8ke29wdGlvbnMuZmlsZV9uYW1lfSAke2krMX0uJHtvcHRpb25zLmZpbGVfZXh0ZW5zaW9ufWApO1xyXG4gIH1cclxuXHJcbiAgdGhpcy5jbGFzc2VzLnB1c2goe25hbWU6IG5hbWUsIGltYWdlczogY2xhc3NfYWRkfSkgIFxyXG5cclxuICB9XHJcblxyXG4gYXN5bmMgYWRkZXhhbXBsZXMoKXtcclxuXHJcbiAgLy9UaGlzIGlzIG5lZWRlZCB0byBtYWtlIHN1cmUgaXQgZ2l2ZXMgdGltZSBmb3IgdGhlIGltYWdlcyB0byB1cGxvYWRcclxuICAvL1RoZSBpbWFnZXMgdXBsb2FkIHZlcnkgZmFzdCwgd2hhdCBtYWtlcyB0aGlzIG1ldGhvZCBleGVjdXRlIGJlZm9yZSB0aGUgaW1hZ2VzIGFyZSBvbiBIVE1MXHJcbiAgLy9JdCBjYW4gYmUgcmVtb3ZlZCBpZiBzb21laG93IHRoaXMgbWV0aG9kIGlzIGp1c3QgY2FsbGVkIGFmdGVyIHRoZSBpbWFnZXMgYXJlIGF2YWlsYWJsZS5cclxuICAvLyBjb25zb2xlLmxvZyhcIkxvYWRpbmcgZXhhbXBsZXMgYXMgdGVuc29ycy4uLi5cIilcclxuICBhd2FpdCB0aGlzLmRlbGF5KDApO1xyXG5cclxuICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMuY2xhc3Nlc19uYW1lcy5sZW5ndGg7IGkrKykge1xyXG4gICAgYXdhaXQgdGhpcy5hZGRfZXhhbXBsZSh0aGlzLmNsYXNzZXNfbmFtZXNbaV0sIHRoaXMubnVtYmVyX29mX3NhbXBsZXNfcGVyX2NsYXNzKTtcclxuICB9XHJcbiAgfVxyXG5cclxuICBhc3luYyBhZGRfZXhhbXBsZShuYW1lOiBzdHJpbmcsIG51bWJlcl9vZl9zcGVjaWVzOiBudW1iZXIpeyAgIFxyXG5cclxuICAgIGNvbnN0IGNsYXNzX2FkZDogYW55PSBbXTtcclxuICAgIC8vIGNvbnNvbGUubG9nKG5hbWUpXHJcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG51bWJlcl9vZl9zcGVjaWVzOyBpKyspIHsgXHJcbiAgICAgIFxyXG4gICAgICAvL0NvbGxlY3RpbmcgdGhlIGltYWdlcyBmcm9tIEhUTUxcclxuICAgICAgY29uc3QgYXV4ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoYGNsYXNzLSR7bmFtZX0tJHtpfWApIGFzIEhUTUxJbWFnZUVsZW1lbnQ7ICAgICAgXHJcblxyXG4gICAgICAvL0FkZGluZyB0aGUgZXhhbXBsZVxyXG4gICAgICBjb25zdCBpbmRleD0gdGhpcy5jbGFzc2VzX25hbWVzLmZpbmRJbmRleCgoZWxlbSk9PmVsZW09PT1uYW1lKVxyXG5cclxuICAgICAgYXdhaXQgVGVhY2hhYmxlTW9iaWxlTmV0LmFkZEV4YW1wbGUoaW5kZXgsIG5hbWUsIGF1eCk7ICAgIFxyXG4gICAgICBcclxuICB9XHJcblxyXG4gIC8vIHRoaXMuY2xhc3Nlcy5wdXNoKHtuYW1lOiBuYW1lLCBpbWFnZXM6IGNsYXNzX2FkZH0pICBcclxuXHJcbiAgfVxyXG5cclxuICBkZWxheShtczogbnVtYmVyKSB7XHJcbiAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgbXMpKTtcclxufVxyXG59XHJcbiIsIjxkaXYgKm5nRm9yPVwibGV0IGNsYXNzIG9mIGNsYXNzZXM7IGluZGV4IGFzIGlcIj5cclxuICA8aDE+e3tjbGFzcy5uYW1lfX08L2gxPlxyXG4gIDxpbWcgICpuZ0Zvcj1cImxldCBpdGVtIG9mIGNsYXNzLmltYWdlczsgaW5kZXggYXMgaVwiIFtzcmNdPVwiaXRlbVwiIHdpZHRoPVwiMjI0XCIgaGVpZ2h0PVwiMjI0XCIgW2lkXT1cIidjbGFzcy0nICsgY2xhc3MubmFtZSArICctJyArIGlcIiBjcm9zc29yaWdpbj1cImFub255bW91c1wiID5cclxuPC9kaXY+XHJcbiJdfQ==
@@ -0,0 +1,182 @@
1
+ import * as tf from '@tensorflow/tfjs';
2
+ export const IMAGE_SIZE = 224;
3
+ const DEFAULT_MOBILENET_VERSION = 2;
4
+ const DEFAULT_TRAINING_LAYER_V1 = 'conv_pw_13_relu';
5
+ const DEFAULT_TRAINING_LAYER_V2 = "out_relu";
6
+ const DEFAULT_ALPHA_V1_v2 = 0.35;
7
+ const DEFAULT_ALPHA_V1 = 0.25; //256
8
+ const DEFAULT_ALPHA_V2 = 0.5; //512
9
+ const DEFAULT_ALPHA_V3 = 0.75; //768 features
10
+ const DEFAULT_ALPHA_V4 = 1; //1024 features
11
+ const DEFAULT_ALPHA = 1; //1024 features
12
+ // v2: 0.35, 0.50, 0.75 or 1.00.
13
+ const isAlphaValid = (version, alpha) => {
14
+ if (version === 1) {
15
+ if (alpha !== 0.25 && alpha !== 0.5 && alpha !== 0.75 && alpha !== 1) {
16
+ console.warn("Invalid alpha. Options are: 0.25, 0.50, 0.75 or 1.00.");
17
+ console.log("Loading model with alpha: ", DEFAULT_ALPHA_V1.toFixed(2));
18
+ return DEFAULT_ALPHA_V1;
19
+ }
20
+ }
21
+ else {
22
+ if (alpha !== 0.35 && alpha !== 0.5 && alpha !== 0.75 && alpha !== 1) {
23
+ console.warn("Invalid alpha. Options are: 0.35, 0.50, 0.75 or 1.00.");
24
+ console.log("Loading model with alpha: ", DEFAULT_ALPHA_V2.toFixed(2));
25
+ return DEFAULT_ALPHA_V2;
26
+ }
27
+ }
28
+ return alpha;
29
+ };
30
+ const parseModelOptions = (options) => {
31
+ options = options || {};
32
+ if (options.checkpointUrl && options.trainingLayer) {
33
+ if (options.alpha || options.version) {
34
+ console.warn("Checkpoint URL passed to modelOptions, alpha options are ignored");
35
+ }
36
+ return [options.checkpointUrl, options.trainingLayer];
37
+ }
38
+ else {
39
+ options.version = options.version || DEFAULT_MOBILENET_VERSION;
40
+ if (options.version === 1) {
41
+ options.alpha = options.alpha || DEFAULT_ALPHA_V4;
42
+ options.alpha = isAlphaValid(options.version, options.alpha);
43
+ console.log(`Loading mobilenet ${options.version} and alpha ${options.alpha}`);
44
+ // exception is alpha o f 1 can only be 1.0
45
+ let alphaString = options.alpha.toFixed(2);
46
+ if (alphaString === "1.00") {
47
+ alphaString = "1.0";
48
+ }
49
+ console.log("Using the model: ");
50
+ return [
51
+ // tslint:disable-next-line:max-line-length
52
+ //They are loading MobileNet_v1
53
+ `https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_${alphaString}_${IMAGE_SIZE}/model.json`,
54
+ DEFAULT_TRAINING_LAYER_V1
55
+ ];
56
+ }
57
+ else if (options.version === 2) {
58
+ options.alpha = options.alpha || DEFAULT_ALPHA_V4;
59
+ options.alpha = isAlphaValid(options.version, options.alpha);
60
+ console.log(`Loading mobilenet ${options.version} and alpha ${options.alpha}`);
61
+ console.log(`Loading mobilenet ${options.version} and alpha ${options.alpha}`);
62
+ return [
63
+ // tslint:disable-next-line:max-line-length
64
+ `https://storage.googleapis.com/teachable-machine-models/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_${options.alpha.toFixed(2)}_${IMAGE_SIZE}_no_top/model.json`,
65
+ DEFAULT_TRAINING_LAYER_V2
66
+ ];
67
+ }
68
+ else {
69
+ throw new Error(`MobileNet V${options.version} doesn't exist`);
70
+ }
71
+ }
72
+ };
73
+ /**
74
+ * load the base mobilenet model
75
+ * @param modelOptions options determining what model to load
76
+ */
77
+ export async function loadTruncatedMobileNet(modelOptions) {
78
+ const [checkpointUrl, trainingLayer] = parseModelOptions(modelOptions);
79
+ const mobilenet = await tf.loadLayersModel(checkpointUrl);
80
+ if (modelOptions && modelOptions.version === 1) {
81
+ const layer = mobilenet.getLayer(trainingLayer);
82
+ const truncatedModel = tf.model({ inputs: mobilenet.inputs, outputs: layer.output });
83
+ console.log("Feature model loaded, memory: ", tf.memory().numBytes);
84
+ const model = tf.sequential();
85
+ model.add(truncatedModel);
86
+ model.add(tf.layers.flatten());
87
+ return model;
88
+ }
89
+ else {
90
+ const layer = mobilenet.getLayer(trainingLayer);
91
+ const truncatedModel = tf.model({ inputs: mobilenet.inputs, outputs: layer.output });
92
+ console.log("Feature model loaded, memory: ", tf.memory().numBytes);
93
+ const model = tf.sequential();
94
+ model.add(truncatedModel);
95
+ model.add(tf.layers.globalAveragePooling2d({})); // go from shape [7, 7, 1280] to [1280]
96
+ return model;
97
+ }
98
+ }
99
+ export class CustomMobileNet {
100
+ static getinputShape() {
101
+ /**truncatedModel is the base model, the model used to apply transfer learning */
102
+ const inputShape = this.truncatedModel.outputs[0].shape.slice(1); // [ 7 x 7 x 1280] (not sure about those dimensions)
103
+ // console.log("Input Shape(complete): ", this.truncatedModel.outputs[0].shape);
104
+ // console.log("Input Shape: ", inputShape);
105
+ const inputSize = tf.util.sizeFromShape(inputShape);
106
+ // console.log("Input Size: ", inputSize);
107
+ return inputSize;
108
+ }
109
+ static get EXPECTED_IMAGE_SIZE() {
110
+ return IMAGE_SIZE;
111
+ }
112
+ getMetadata() {
113
+ return this._metadata;
114
+ }
115
+ constructor() {
116
+ // this._metadata = fillMetadata(metadata);
117
+ //Loading the truncated model
118
+ // loadTruncatedMobileNet();
119
+ // this.loadFeatureModel();
120
+ }
121
+ static async loadFeatureModel() {
122
+ this.truncatedModel = await loadTruncatedMobileNet();
123
+ }
124
+ /**
125
+ * get the total number of classes existing within model
126
+ */
127
+ // getTotalClasses() {
128
+ // const output = this.model.output as SymbolicTensor;
129
+ // const totalClasses = output.shape[1];
130
+ // return totalClasses;
131
+ // }
132
+ /**
133
+ * get the model labels
134
+ */
135
+ getClassLabels() {
136
+ return this._metadata.labels;
137
+ }
138
+ /**
139
+ * Given an image element, makes a prediction through mobilenet returning the
140
+ * probabilities of the top K classes.
141
+ * @param image the image to classify
142
+ * @param maxPredictions the maximum number of classification predictions
143
+ */
144
+ // async predictTopK(image: ClassifierInputSource, maxPredictions = 10, flipped = false) {
145
+ // const croppedImage = cropTo(image, this._metadata.imageSize, flipped);
146
+ // const logits = tf.tidy(() => {
147
+ // const captured = capture(croppedImage, this._metadata.grayscale);
148
+ // return this.model.predict(captured);
149
+ // });
150
+ // // Convert logits to probabilities and class names.
151
+ // const classes = await getTopKClasses(this._metadata.labels, logits as tf.Tensor<tf.Rank>, maxPredictions);
152
+ // dispose(logits);
153
+ // return classes;
154
+ // }
155
+ /**
156
+ * Given an image element, makes a prediction through mobilenet returning the
157
+ * probabilities for ALL classes.
158
+ * @param image the image to classify
159
+ * @param flipped whether to flip the image on X
160
+ */
161
+ // async predict(image: ClassifierInputSource, flipped = false) {
162
+ // const croppedImage = cropTo(image, this._metadata.imageSize, flipped);
163
+ // const logits = tf.tidy(() => {
164
+ // const captured = capture(croppedImage, this._metadata.grayscale);
165
+ // return this.model.predict(captured);
166
+ // });
167
+ // const values = await (logits as tf.Tensor<tf.Rank>).data();
168
+ // const classes = [];
169
+ // for (let i = 0; i < values.length; i++) {
170
+ // classes.push({
171
+ // className: this._metadata.labels[i],
172
+ // probability: values[i]
173
+ // });
174
+ // }
175
+ // dispose(logits);
176
+ // return classes;
177
+ // }
178
+ dispose() {
179
+ this.truncatedModel.dispose();
180
+ }
181
+ } // end of CustomMobileNet
182
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3VzdG9tLW1vYmlsZW5ldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL3RmanMtZXZvbHV0aW9uL3NyYy9saWIvbW9kZWxzL2N1c3RvbS1tb2JpbGVuZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUt2QyxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsR0FBRyxDQUFDO0FBNEI5QixNQUFNLHlCQUF5QixHQUFHLENBQUMsQ0FBQztBQUNwQyxNQUFNLHlCQUF5QixHQUFHLGlCQUFpQixDQUFDO0FBQ3BELE1BQU0seUJBQXlCLEdBQUcsVUFBVSxDQUFDO0FBQzdDLE1BQU0sbUJBQW1CLEdBQUcsSUFBSSxDQUFDO0FBQ2pDLE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLENBQUEsS0FBSztBQUNuQyxNQUFNLGdCQUFnQixHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUs7QUFDbkMsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsQ0FBQSxjQUFjO0FBQzVDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDLENBQUEsZUFBZTtBQUMxQyxNQUFNLGFBQWEsR0FBRyxDQUFDLENBQUMsQ0FBQSxlQUFlO0FBRXZDLGdDQUFnQztBQUVoQyxNQUFNLFlBQVksR0FBRyxDQUFDLE9BQWUsRUFBRSxLQUFhLEVBQUUsRUFBRTtJQUNwRCxJQUFJLE9BQU8sS0FBSyxDQUFDLEVBQUU7UUFDZixJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksS0FBSyxLQUFLLEdBQUcsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLEtBQUssS0FBSyxDQUFDLEVBQUU7WUFDbEUsT0FBTyxDQUFDLElBQUksQ0FBQyx1REFBdUQsQ0FBQyxDQUFDO1lBQ3RFLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLEVBQUUsZ0JBQWdCLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENB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QUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQztJQUNqQyxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSCwwRkFBMEY7SUFDMUYsNkVBQTZFO0lBRTdFLHFDQUFxQztJQUNyQyw0RUFBNEU7SUFDNUUsK0NBQStDO0lBQy9DLFVBQVU7SUFFViwwREFBMEQ7SUFDMUQsaUhBQWlIO0lBQ2pILHVCQUF1QjtJQUV2QixzQkFBc0I7SUFDdEIsSUFBSTtJQUVKOzs7OztPQUtHO0lBQ0gsaUVBQWlFO0lBQ2pFLDZFQUE2RTtJQUU3RSxxQ0FBcUM7SUFDckMsNEVBQTRFO0lBQzVFLCtDQUErQztJQUMvQyxVQUFVO0lBRVYsa0VBQWtFO0lBRWxFLDBCQUEwQjtJQUMxQixnREFBZ0Q7SUFDaEQseUJBQXlCO0lBQ3pCLG1EQUFtRDtJQUNuRCxxQ0FBcUM7SUFDckMsY0FBYztJQUNkLFFBQVE7SUFFUix1QkFBdUI7SUFFdkIsc0JBQXNCO0lBQ3RCLElBQUk7SUFFRyxPQUFPO1FBQ1YsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUNsQyxDQUFDO0NBQ0osQ0FBQSx5QkFBeUIiLCJzb3VyY2VzQ29udGVudCI6WyJcclxuaW1wb3J0ICogYXMgdGYgZnJvbSAnQHRlbnNvcmZsb3cvdGZqcyc7XHJcblxyXG5pbXBvcnQgeyBTeW1ib2xpY1RlbnNvciB9IGZyb20gJ0B0ZW5zb3JmbG93L3RmanMnO1xyXG5cclxuXHJcbmV4cG9ydCBjb25zdCBJTUFHRV9TSVpFID0gMjI0O1xyXG5cclxuXHJcbi8qKlxyXG4gKiB0aGUgbWV0YWRhdGEgdG8gZGVzY3JpYmUgdGhlIG1vZGVsJ3MgY3JlYXRpb24sXHJcbiAqIGluY2x1ZGVzIHRoZSBsYWJlbHMgYXNzb2NpYXRlZCB3aXRoIHRoZSBjbGFzc2VzXHJcbiAqIGFuZCB2ZXJzaW9uaW5nIGluZm9ybWF0aW9uIGZyb20gdHJhaW5pbmcuXHJcbiAqL1xyXG5leHBvcnQgaW50ZXJmYWNlIE1ldGFkYXRhIHtcclxuICAgIHRmanNWZXJzaW9uOiBzdHJpbmc7XHJcbiAgICB0bVZlcnNpb24/OiBzdHJpbmc7XHJcbiAgICBwYWNrYWdlVmVyc2lvbjogc3RyaW5nO1xyXG4gICAgcGFja2FnZU5hbWU6IHN0cmluZztcclxuICAgIG1vZGVsTmFtZT86IHN0cmluZztcclxuICAgIHRpbWVTdGFtcD86IHN0cmluZztcclxuICAgIGxhYmVsczogc3RyaW5nW107XHJcbiAgICB1c2VyTWV0YWRhdGE/OiB7fTtcclxuICAgIGdyYXlzY2FsZT86IGJvb2xlYW47XHJcbiAgICBpbWFnZVNpemU/OiBudW1iZXI7XHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgTW9kZWxPcHRpb25zIHtcclxuICAgIHZlcnNpb24/OiBudW1iZXI7XHJcbiAgICBjaGVja3BvaW50VXJsPzogc3RyaW5nO1xyXG4gICAgYWxwaGE/OiBudW1iZXI7XHJcbiAgICB0cmFpbmluZ0xheWVyPzogc3RyaW5nO1xyXG59XHJcblxyXG5jb25zdCBERUZBVUxUX01PQklMRU5FVF9WRVJTSU9OID0gMjtcclxuY29uc3QgREVGQVVMVF9UUkFJTklOR19MQVlFUl9WMSA9ICdjb252X3B3XzEzX3JlbHUnO1xyXG5jb25zdCBERUZBVUxUX1RSQUlOSU5HX0xBWUVSX1YyID0gXCJvdXRfcmVsdVwiOyBcclxuY29uc3QgREVGQVVMVF9BTFBIQV9WMV92MiA9IDAuMzU7XHJcbmNvbnN0IERFRkFVTFRfQUxQSEFfVjEgPSAwLjI1Oy8vMjU2XHJcbmNvbnN0IERFRkFVTFRfQUxQSEFfVjIgPSAwLjU7IC8vNTEyXHJcbmNvbnN0IERFRkFVTFRfQUxQSEFfVjMgPSAwLjc1Oy8vNzY4IGZlYXR1cmVzXHJcbmNvbnN0IERFRkFVTFRfQUxQSEFfVjQgPSAxOy8vMTAyNCBmZWF0dXJlc1xyXG5jb25zdCBERUZBVUxUX0FMUEhBID0gMTsvLzEwMjQgZmVhdHVyZXNcclxuXHJcbi8vIHYyOiAwLjM1LCAwLjUwLCAwLjc1IG9yIDEuMDAuXHJcblxyXG5jb25zdCBpc0FscGhhVmFsaWQgPSAodmVyc2lvbjogbnVtYmVyLCBhbHBoYTogbnVtYmVyKSA9PiB7XHJcbiAgICBpZiAodmVyc2lvbiA9PT0gMSkge1xyXG4gICAgICAgIGlmIChhbHBoYSAhPT0gMC4yNSAmJiBhbHBoYSAhPT0gMC41ICYmIGFscGhhICE9PSAwLjc1ICYmIGFscGhhICE9PSAxKSB7XHJcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIkludmFsaWQgYWxwaGEuIE9wdGlvbnMgYXJlOiAwLjI1LCAwLjUwLCAwLjc1IG9yIDEuMDAuXCIpO1xyXG4gICAgICAgICAgICBjb25zb2xlLmxvZyhcIkxvYWRpbmcgbW9kZWwgd2l0aCBhbHBoYTogXCIsIERFRkFVTFRfQUxQSEFfVjEudG9GaXhlZCgyKSk7IFxyXG4gICAgICAgICAgICByZXR1cm4gREVGQVVMVF9BTFBIQV9WMTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBlbHNlIHtcclxuICAgICAgICBpZiAoYWxwaGEgIT09IDAuMzUgJiYgYWxwaGEgIT09IDAuNSAmJiBhbHBoYSAhPT0gMC43NSAmJiBhbHBoYSAhPT0gMSkge1xyXG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXCJJbnZhbGlkIGFscGhhLiBPcHRpb25zIGFyZTogMC4zNSwgMC41MCwgMC43NSBvciAxLjAwLlwiKTtcclxuICAgICAgICAgICAgY29uc29sZS5sb2coXCJMb2FkaW5nIG1vZGVsIHdpdGggYWxwaGE6IFwiLCBERUZBVUxUX0FMUEhBX1YyLnRvRml4ZWQoMikpOyBcclxuICAgICAgICAgICAgcmV0dXJuIERFRkFVTFRfQUxQSEFfVjI7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHJldHVybiBhbHBoYTtcclxufTtcclxuXHJcblxyXG5jb25zdCBwYXJzZU1vZGVsT3B0aW9ucyA9IChvcHRpb25zPzogTW9kZWxPcHRpb25zKSA9PiB7XHJcbiAgICBvcHRpb25zID0gb3B0aW9ucyB8fCB7fVxyXG5cclxuICAgIGlmIChvcHRpb25zLmNoZWNrcG9pbnRVcmwgJiYgb3B0aW9ucy50cmFpbmluZ0xheWVyKSB7XHJcbiAgICAgICAgaWYgKG9wdGlvbnMuYWxwaGEgfHwgb3B0aW9ucy52ZXJzaW9uKXtcclxuICAgICAgICAgICAgY29uc29sZS53YXJuKFwiQ2hlY2twb2ludCBVUkwgcGFzc2VkIHRvIG1vZGVsT3B0aW9ucywgYWxwaGEgb3B0aW9ucyBhcmUgaWdub3JlZFwiKTtcclxuICAgICAgICB9ICAgICAgICBcclxuICAgICAgICByZXR1cm4gW29wdGlvbnMuY2hlY2twb2ludFVybCwgb3B0aW9ucy50cmFpbmluZ0xheWVyXTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgICAgb3B0aW9ucy52ZXJzaW9uID0gb3B0aW9ucy52ZXJzaW9uIHx8IERFRkFVTFRfTU9CSUxFTkVUX1ZFUlNJT047XHJcbiAgICAgICAgXHJcbiAgICAgICAgaWYob3B0aW9ucy52ZXJzaW9uID09PSAxKXtcclxuICAgICAgICAgICAgb3B0aW9ucy5hbHBoYSA9IG9wdGlvbnMuYWxwaGEgfHwgREVGQVVMVF9BTFBIQV9WNDsgIFxyXG4gICAgICAgICAgICBvcHRpb25zLmFscGhhID0gaXNBbHBoYVZhbGlkKG9wdGlvbnMudmVyc2lvbiwgb3B0aW9ucy5hbHBoYSk7XHJcblxyXG4gICAgICAgICAgICBjb25zb2xlLmxvZyhgTG9hZGluZyBtb2JpbGVuZXQgJHtvcHRpb25zLnZlcnNpb259IGFuZCBhbHBoYSAke29wdGlvbnMuYWxwaGF9YCk7XHJcbiAgICAgICAgICAgIC8vIGV4Y2VwdGlvbiBpcyBhbHBoYSBvIGYgMSBjYW4gb25seSBiZSAxLjBcclxuICAgICAgICAgICAgbGV0IGFscGhhU3RyaW5nID0gb3B0aW9ucy5hbHBoYS50b0ZpeGVkKDIpO1xyXG4gICAgICAgICAgICBpZiAoYWxwaGFTdHJpbmcgPT09IFwiMS4wMFwiKSB7IGFscGhhU3RyaW5nID0gXCIxLjBcIjsgfVxyXG5cclxuICAgICAgICAgICAgY29uc29sZS5sb2coXCJVc2luZyB0aGUgbW9kZWw6IFwiLCAgKVxyXG5cclxuICAgICAgICAgICAgcmV0dXJuIFtcclxuICAgICAgICAgICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTptYXgtbGluZS1sZW5ndGhcclxuICAgICAgICAgICAgICAgIC8vVGhleSBhcmUgbG9hZGluZyBNb2JpbGVOZXRfdjEgICAgICAgIFxyXG4gICAgICAgICAgICAgICAgYGh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS90ZmpzLW1vZGVscy90ZmpzL21vYmlsZW5ldF92MV8ke2FscGhhU3RyaW5nfV8ke0lNQUdFX1NJWkV9L21vZGVsLmpzb25gLFxyXG4gICAgICAgICAgICAgICAgREVGQVVMVF9UUkFJTklOR19MQVlFUl9WMVxyXG4gICAgICAgICAgICBdO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIGlmIChvcHRpb25zLnZlcnNpb24gPT09IDIpe1xyXG4gICAgICAgICAgICBvcHRpb25zLmFscGhhID0gb3B0aW9ucy5hbHBoYSB8fCBERUZBVUxUX0FMUEhBX1Y0OyAgXHJcbiAgICAgICAgICAgIG9wdGlvbnMuYWxwaGEgPSBpc0FscGhhVmFsaWQob3B0aW9ucy52ZXJzaW9uLCBvcHRpb25zLmFscGhhKTtcclxuXHJcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKGBMb2FkaW5nIG1vYmlsZW5ldCAke29wdGlvbnMudmVyc2lvbn0gYW5kIGFscGhhICR7b3B0aW9ucy5hbHBoYX1gKTtcclxuICAgICAgICAgICAgY29uc29sZS5sb2coYExvYWRpbmcgbW9iaWxlbmV0ICR7b3B0aW9ucy52ZXJzaW9ufSBhbmQgYWxwaGEgJHtvcHRpb25zLmFscGhhfWApO1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIFtcclxuICAgICAgICAgICAgICAgIC8vIHRzbGludDpkaXNhYmxlLW5leHQtbGluZTptYXgtbGluZS1sZW5ndGggICAgICAgIFxyXG4gICAgICAgICAgICAgICAgYGh0dHBzOi8vc3RvcmFnZS5nb29nbGVhcGlzLmNvbS90ZWFjaGFibGUtbWFjaGluZS1tb2RlbHMvbW9iaWxlbmV0X3YyX3dlaWdodHNfdGZfZGltX29yZGVyaW5nX3RmX2tlcm5lbHNfJHtvcHRpb25zLmFscGhhLnRvRml4ZWQoMil9XyR7SU1BR0VfU0laRX1fbm9fdG9wL21vZGVsLmpzb25gLFxyXG4gICAgICAgICAgICAgICAgREVGQVVMVF9UUkFJTklOR19MQVlFUl9WMlxyXG4gICAgICAgICAgICBdO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgTW9iaWxlTmV0IFYke29wdGlvbnMudmVyc2lvbn0gZG9lc24ndCBleGlzdGApO1xyXG4gICAgICAgIH0gICBcclxuICAgIH1cclxufTtcclxuXHJcblxyXG4vKipcclxuICogbG9hZCB0aGUgYmFzZSBtb2JpbGVuZXQgbW9kZWxcclxuICogQHBhcmFtIG1vZGVsT3B0aW9ucyBvcHRpb25zIGRldGVybWluaW5nIHdoYXQgbW9kZWwgdG8gbG9hZFxyXG4gKi9cclxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRUcnVuY2F0ZWRNb2JpbGVOZXQobW9kZWxPcHRpb25zPzogTW9kZWxPcHRpb25zKSB7XHJcbiAgICBjb25zdCBbY2hlY2twb2ludFVybCwgdHJhaW5pbmdMYXllcl0gPSBwYXJzZU1vZGVsT3B0aW9ucyhtb2RlbE9wdGlvbnMpO1xyXG4gICAgXHJcbiAgICBcclxuICAgIGNvbnN0IG1vYmlsZW5ldCA9IGF3YWl0IHRmLmxvYWRMYXllcnNNb2RlbChjaGVja3BvaW50VXJsKTtcclxuXHJcbiAgICBpZiAobW9kZWxPcHRpb25zICYmIG1vZGVsT3B0aW9ucy52ZXJzaW9uID09PSAxKXtcclxuICAgICAgICBjb25zdCBsYXllciA9IG1vYmlsZW5ldC5nZXRMYXllcih0cmFpbmluZ0xheWVyKTtcclxuICAgICAgICBcclxuICAgICAgICBjb25zdCB0cnVuY2F0ZWRNb2RlbCA9IHRmLm1vZGVsKHsgaW5wdXRzOiBtb2JpbGVuZXQuaW5wdXRzLCBvdXRwdXRzOiBsYXllci5vdXRwdXQgfSk7XHJcbiAgICAgICAgY29uc29sZS5sb2coXCJGZWF0dXJlIG1vZGVsIGxvYWRlZCwgbWVtb3J5OiBcIiwgdGYubWVtb3J5KCkubnVtQnl0ZXMpO1xyXG5cclxuICAgICAgICBcclxuICAgICAgICBjb25zdCBtb2RlbCA9IHRmLnNlcXVlbnRpYWwoKTtcclxuICAgICAgICBtb2RlbC5hZGQodHJ1bmNhdGVkTW9kZWwpO1xyXG4gICAgICAgIG1vZGVsLmFkZCh0Zi5sYXllcnMuZmxhdHRlbigpKTsgICAgICAgIFxyXG4gICAgICAgIFxyXG4gICAgICAgIFxyXG4gICAgICAgIHJldHVybiBtb2RlbDtcclxuICAgIH1cclxuICAgIGVsc2Uge1xyXG4gICAgICAgIGNvbnN0IGxheWVyID0gbW9iaWxlbmV0LmdldExheWVyKHRyYWluaW5nTGF5ZXIpO1xyXG4gICAgICAgIGNvbnN0IHRydW5jYXRlZE1vZGVsID0gdGYubW9kZWwoeyBpbnB1dHM6IG1vYmlsZW5ldC5pbnB1dHMsIG91dHB1dHM6IGxheWVyLm91dHB1dCB9KTtcclxuICAgICAgICBjb25zb2xlLmxvZyhcIkZlYXR1cmUgbW9kZWwgbG9hZGVkLCBtZW1vcnk6IFwiLCB0Zi5tZW1vcnkoKS5udW1CeXRlcyk7XHJcbiAgICAgICAgY29uc3QgbW9kZWwgPSB0Zi5zZXF1ZW50aWFsKCk7XHJcbiAgICAgICAgbW9kZWwuYWRkKHRydW5jYXRlZE1vZGVsKTtcclxuICAgICAgICBtb2RlbC5hZGQodGYubGF5ZXJzLmdsb2JhbEF2ZXJhZ2VQb29saW5nMmQoe30pKTsgLy8gZ28gZnJvbSBzaGFwZSBbNywgNywgMTI4MF0gdG8gWzEyODBdXHJcbiAgICAgICAgcmV0dXJuIG1vZGVsO1xyXG4gICAgfVxyXG59XHJcblxyXG5cclxuZXhwb3J0IGNsYXNzIEN1c3RvbU1vYmlsZU5ldCB7XHJcbiAgICAvKipcclxuICAgICAqIHRoZSB0cnVuY2F0ZWQgbW9iaWxlbmV0IG1vZGVsIHdlIHdpbGwgdHJhaW4gb24gdG9wIG9mXHJcbiAgICAgKi9cclxuICAgIHByb3RlY3RlZCB0cnVuY2F0ZWRNb2RlbCE6IHRmLkxheWVyc01vZGVsO1xyXG4gICAgc3RhdGljIHRydW5jYXRlZE1vZGVsOiB0Zi5TZXF1ZW50aWFsO1xyXG5cclxuc3RhdGljIGdldGlucHV0U2hhcGUoKXtcclxuICAgLyoqdHJ1bmNhdGVkTW9kZWwgaXMgdGhlIGJhc2UgbW9kZWwsIHRoZSBtb2RlbCB1c2VkIHRvIGFwcGx5IHRyYW5zZmVyIGxlYXJuaW5nICovXHJcbiAgY29uc3QgaW5wdXRTaGFwZTogYW55ID0gdGhpcy50cnVuY2F0ZWRNb2RlbC5vdXRwdXRzWzBdLnNoYXBlLnNsaWNlKDEpOyAvLyBbIDcgeCA3IHggMTI4MF0gKG5vdCBzdXJlIGFib3V0IHRob3NlIGRpbWVuc2lvbnMpXHJcbi8vICAgY29uc29sZS5sb2coXCJJbnB1dCBTaGFwZShjb21wbGV0ZSk6IFwiLCB0aGlzLnRydW5jYXRlZE1vZGVsLm91dHB1dHNbMF0uc2hhcGUpO1xyXG4vLyAgIGNvbnNvbGUubG9nKFwiSW5wdXQgU2hhcGU6IFwiLCBpbnB1dFNoYXBlKTtcclxuXHJcbiAgY29uc3QgaW5wdXRTaXplID0gdGYudXRpbC5zaXplRnJvbVNoYXBlKGlucHV0U2hhcGUpO1xyXG5cclxuLy8gICBjb25zb2xlLmxvZyhcIklucHV0IFNpemU6IFwiLCBpbnB1dFNpemUpO1xyXG5cclxuICByZXR1cm4gaW5wdXRTaXplO1xyXG59XHJcblxyXG4gICAgc3RhdGljIGdldCBFWFBFQ1RFRF9JTUFHRV9TSVpFKCkge1xyXG4gICAgICAgIHJldHVybiBJTUFHRV9TSVpFO1xyXG4gICAgfVxyXG5cclxuICAgIHByb3RlY3RlZCBfbWV0YWRhdGEhOiBNZXRhZGF0YTtcclxuXHJcbiAgICBwdWJsaWMgZ2V0TWV0YWRhdGEoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX21ldGFkYXRhO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0cnVjdG9yKCkge1xyXG4gICAgICAgIC8vIHRoaXMuX21ldGFkYXRhID0gZmlsbE1ldGFkYXRhKG1ldGFkYXRhKTtcclxuICAgICAgICAvL0xvYWRpbmcgdGhlIHRydW5jYXRlZCBtb2RlbFxyXG4gICAgICAgIC8vIGxvYWRUcnVuY2F0ZWRNb2JpbGVOZXQoKTtcclxuICAgICAgICAvLyB0aGlzLmxvYWRGZWF0dXJlTW9kZWwoKTtcclxuXHJcbiAgICB9XHJcblxyXG4gICBzdGF0aWMgIGFzeW5jIGxvYWRGZWF0dXJlTW9kZWwoKXtcclxuXHJcbiAgICAgICAgdGhpcy50cnVuY2F0ZWRNb2RlbCA9IGF3YWl0IGxvYWRUcnVuY2F0ZWRNb2JpbGVOZXQoKTtcclxuXHJcbiAgICB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBnZXQgdGhlIHRvdGFsIG51bWJlciBvZiBjbGFzc2VzIGV4aXN0aW5nIHdpdGhpbiBtb2RlbFxyXG4gICAgICovXHJcbiAgICAvLyBnZXRUb3RhbENsYXNzZXMoKSB7XHJcbiAgICAvLyAgICAgY29uc3Qgb3V0cHV0ID0gdGhpcy5tb2RlbC5vdXRwdXQgYXMgU3ltYm9saWNUZW5zb3I7XHJcbiAgICAvLyAgICAgY29uc3QgdG90YWxDbGFzc2VzID0gb3V0cHV0LnNoYXBlWzFdO1xyXG4gICAgLy8gICAgIHJldHVybiB0b3RhbENsYXNzZXM7XHJcbiAgICAvLyB9XHJcblxyXG4gICAgLyoqXHJcbiAgICAgKiBnZXQgdGhlIG1vZGVsIGxhYmVsc1xyXG4gICAgICovXHJcbiAgICBnZXRDbGFzc0xhYmVscygpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fbWV0YWRhdGEubGFiZWxzO1xyXG4gICAgfVxyXG5cclxuICAgIC8qKlxyXG4gICAgICogR2l2ZW4gYW4gaW1hZ2UgZWxlbWVudCwgbWFrZXMgYSBwcmVkaWN0aW9uIHRocm91Z2ggbW9iaWxlbmV0IHJldHVybmluZyB0aGVcclxuICAgICAqIHByb2JhYmlsaXRpZXMgb2YgdGhlIHRvcCBLIGNsYXNzZXMuXHJcbiAgICAgKiBAcGFyYW0gaW1hZ2UgdGhlIGltYWdlIHRvIGNsYXNzaWZ5XHJcbiAgICAgKiBAcGFyYW0gbWF4UHJlZGljdGlvbnMgdGhlIG1heGltdW0gbnVtYmVyIG9mIGNsYXNzaWZpY2F0aW9uIHByZWRpY3Rpb25zXHJcbiAgICAgKi9cclxuICAgIC8vIGFzeW5jIHByZWRpY3RUb3BLKGltYWdlOiBDbGFzc2lmaWVySW5wdXRTb3VyY2UsIG1heFByZWRpY3Rpb25zID0gMTAsIGZsaXBwZWQgPSBmYWxzZSkge1xyXG4gICAgLy8gICAgIGNvbnN0IGNyb3BwZWRJbWFnZSA9IGNyb3BUbyhpbWFnZSwgdGhpcy5fbWV0YWRhdGEuaW1hZ2VTaXplLCBmbGlwcGVkKTtcclxuXHJcbiAgICAvLyAgICAgY29uc3QgbG9naXRzID0gdGYudGlkeSgoKSA9PiB7XHJcbiAgICAvLyAgICAgICAgIGNvbnN0IGNhcHR1cmVkID0gY2FwdHVyZShjcm9wcGVkSW1hZ2UsIHRoaXMuX21ldGFkYXRhLmdyYXlzY2FsZSk7XHJcbiAgICAvLyAgICAgICAgIHJldHVybiB0aGlzLm1vZGVsLnByZWRpY3QoY2FwdHVyZWQpO1xyXG4gICAgLy8gICAgIH0pO1xyXG5cclxuICAgIC8vICAgICAvLyBDb252ZXJ0IGxvZ2l0cyB0byBwcm9iYWJpbGl0aWVzIGFuZCBjbGFzcyBuYW1lcy5cclxuICAgIC8vICAgICBjb25zdCBjbGFzc2VzID0gYXdhaXQgZ2V0VG9wS0NsYXNzZXModGhpcy5fbWV0YWRhdGEubGFiZWxzLCBsb2dpdHMgYXMgdGYuVGVuc29yPHRmLlJhbms+LCBtYXhQcmVkaWN0aW9ucyk7XHJcbiAgICAvLyAgICAgZGlzcG9zZShsb2dpdHMpO1xyXG5cclxuICAgIC8vICAgICByZXR1cm4gY2xhc3NlcztcclxuICAgIC8vIH1cclxuXHJcbiAgICAvKipcclxuICAgICAqIEdpdmVuIGFuIGltYWdlIGVsZW1lbnQsIG1ha2VzIGEgcHJlZGljdGlvbiB0aHJvdWdoIG1vYmlsZW5ldCByZXR1cm5pbmcgdGhlXHJcbiAgICAgKiBwcm9iYWJpbGl0aWVzIGZvciBBTEwgY2xhc3Nlcy5cclxuICAgICAqIEBwYXJhbSBpbWFnZSB0aGUgaW1hZ2UgdG8gY2xhc3NpZnlcclxuICAgICAqIEBwYXJhbSBmbGlwcGVkIHdoZXRoZXIgdG8gZmxpcCB0aGUgaW1hZ2Ugb24gWFxyXG4gICAgICovXHJcbiAgICAvLyBhc3luYyBwcmVkaWN0KGltYWdlOiBDbGFzc2lmaWVySW5wdXRTb3VyY2UsIGZsaXBwZWQgPSBmYWxzZSkge1xyXG4gICAgLy8gICAgIGNvbnN0IGNyb3BwZWRJbWFnZSA9IGNyb3BUbyhpbWFnZSwgdGhpcy5fbWV0YWRhdGEuaW1hZ2VTaXplLCBmbGlwcGVkKTtcclxuXHJcbiAgICAvLyAgICAgY29uc3QgbG9naXRzID0gdGYudGlkeSgoKSA9PiB7XHJcbiAgICAvLyAgICAgICAgIGNvbnN0IGNhcHR1cmVkID0gY2FwdHVyZShjcm9wcGVkSW1hZ2UsIHRoaXMuX21ldGFkYXRhLmdyYXlzY2FsZSk7XHJcbiAgICAvLyAgICAgICAgIHJldHVybiB0aGlzLm1vZGVsLnByZWRpY3QoY2FwdHVyZWQpO1xyXG4gICAgLy8gICAgIH0pO1xyXG5cclxuICAgIC8vICAgICBjb25zdCB2YWx1ZXMgPSBhd2FpdCAobG9naXRzIGFzIHRmLlRlbnNvcjx0Zi5SYW5rPikuZGF0YSgpO1xyXG5cclxuICAgIC8vICAgICBjb25zdCBjbGFzc2VzID0gW107XHJcbiAgICAvLyAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2YWx1ZXMubGVuZ3RoOyBpKyspIHtcclxuICAgIC8vICAgICAgICAgY2xhc3Nlcy5wdXNoKHtcclxuICAgIC8vICAgICAgICAgICAgIGNsYXNzTmFtZTogdGhpcy5fbWV0YWRhdGEubGFiZWxzW2ldLFxyXG4gICAgLy8gICAgICAgICAgICAgcHJvYmFiaWxpdHk6IHZhbHVlc1tpXVxyXG4gICAgLy8gICAgICAgICB9KTtcclxuICAgIC8vICAgICB9XHJcblxyXG4gICAgLy8gICAgIGRpc3Bvc2UobG9naXRzKTtcclxuXHJcbiAgICAvLyAgICAgcmV0dXJuIGNsYXNzZXM7XHJcbiAgICAvLyB9XHJcblxyXG4gICAgcHVibGljIGRpc3Bvc2UoKSB7XHJcbiAgICAgICAgdGhpcy50cnVuY2F0ZWRNb2RlbC5kaXNwb3NlKCk7XHJcbiAgICB9XHJcbn0vLyBlbmQgb2YgQ3VzdG9tTW9iaWxlTmV0XHJcbiJdfQ==