@twintag/twintag-core 0.2.276 → 0.2.277

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.
@@ -206,7 +206,7 @@ class IndexedDbService {
206
206
  /**
207
207
  * The SDK version.
208
208
  */
209
- const VERSION = '0.2.276';
209
+ const VERSION = '0.0.0-VERSION';
210
210
 
211
211
  class TwintagErrorValue {
212
212
  }
@@ -11,19 +11,18 @@ const workerMsgId = 'stencil.scanner.worker';
11
11
  const workerPath = /*@__PURE__*/(typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __dirname + '/scanner.worker-b82b2299.js').href : new URL('scanner.worker-b82b2299.js', document.currentScript && document.currentScript.src || document.baseURI).href);
12
12
  const worker = /*@__PURE__*/_workerHelper.createWorker(workerPath, workerName, workerMsgId);
13
13
 
14
- const twintagScannerCss = ":host{--twintag-scanner-bg-select-cameras:white;--twintag-scanner-text-select-cameras:black;--twintag-scanner-radius-select-cameras:6px 0 0 0;--twintag-scanner-min-height:min-content;--twintag-scanner-corners-color:white;--twintag-scanner-corners-width:4px;--twintag-scanner-corners-length:36px;--twintag-scanner-corners-offset:24px;position:relative}:host .icon-container{display:grid;place-items:center;position:absolute;inset:0;z-index:-2222}:host .icon-container .check-icon{opacity:0;z-index:2222}:host .video-container{--crop:0;display:grid;place-items:center;position:relative;width:100%;opacity:0;overflow:hidden;z-index:1}:host .video-container::after{content:\"\";position:absolute;width:100%;height:100%;opacity:inherit;background:rgba(0, 0, 0, 0.5);-webkit-clip-path:polygon(0% 0%, 0% 100%, calc(var(--crop) * 1%) 100%, calc(var(--crop) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) 100%, 100% 100%, 100% 0%);clip-path:polygon(0% 0%, 0% 100%, calc(var(--crop) * 1%) 100%, calc(var(--crop) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) 100%, 100% 100%, 100% 0%)}:host .video-container:before{display:block;content:\"\";width:100%;padding-top:100%}:host .video-container .video-corners{position:absolute;width:calc((1 - var(--crop) / 50) * 100% + var(--twintag-scanner-corners-offset));height:calc((1 - var(--crop) / 50) * 100% + var(--twintag-scanner-corners-offset));opacity:inherit;background:linear-gradient(to right, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 0, linear-gradient(to right, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 100%, linear-gradient(to left, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 0, linear-gradient(to left, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 100%, linear-gradient(to bottom, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 0, linear-gradient(to bottom, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 0, linear-gradient(to top, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 100%, linear-gradient(to top, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 100%;background-size:var(--twintag-scanner-corners-length) var(--twintag-scanner-corners-length);background-repeat:no-repeat;z-index:2222}:host .video-container video{position:absolute !important;top:0 !important;right:0 !important;bottom:0 !important;left:0 !important;width:100% !important;height:100% !important;object-fit:cover;object-position:center;aspect-ratio:1/1 !important;min-height:var(--twintag-scanner-min-height)}:host .cameras-container{position:absolute;right:0;bottom:0;width:100%;min-width:10ch;max-width:max-content;padding:0rem 0.25rem;border:none;cursor:pointer;border-radius:var(--twintag-scanner-radius-select-cameras);background-color:var(--twintag-scanner-bg-select-cameras);line-height:1.1;font-size:1rem;opacity:inherit;z-index:2222}:host .cameras-container select{display:grid;place-items:center;width:max-content;margin:0;padding:0.75rem;border:none;background-color:transparent;appearance:none;--webkit-appearance:none;-moz-appearance:none;color:var(--twintag-scanner-text-select-cameras);cursor:inherit;font-family:inherit;font-size:inherit;line-height:inherit;outline:none}:host .cameras-container select::-ms-expand{display:none}";
14
+ const twintagScannerCss = ":host{--twintag-scanner-bg-select-cameras:white;--twintag-scanner-text-select-cameras:black;--twintag-scanner-radius-select-cameras:6px 0 0 0;--twintag-scanner-min-height:min-content;--twintag-scanner-corners-color:white;--twintag-scanner-corners-width:4px;--twintag-scanner-corners-length:36px;--twintag-scanner-corners-offset:24px;display:grid;place-items:center;position:relative;width:100%}:host .icon-container{display:grid;place-items:center;position:absolute;inset:0;z-index:-2222}:host .icon-container .check-icon{opacity:0;z-index:2222}:host .video-container{--crop:0;display:flex;align-items:center;justify-content:center;position:relative;width:100%;height:100%;opacity:0;overflow:hidden;z-index:1}:host .video-container::after{content:\"\";position:absolute;width:100%;height:100%;opacity:inherit;background:rgba(0, 0, 0, 0.5);-webkit-clip-path:polygon(0% 0%, 0% 100%, calc(var(--crop) * 1%) 100%, calc(var(--crop) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) 100%, 100% 100%, 100% 0%);clip-path:polygon(0% 0%, 0% 100%, calc(var(--crop) * 1%) 100%, calc(var(--crop) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) 100%, 100% 100%, 100% 0%)}:host .video-container:before{display:block;content:\"\";width:100%;padding-top:100%}:host .video-container .video-corners{position:absolute;width:calc((1 - var(--crop) / 50) * 100% + var(--twintag-scanner-corners-offset));height:calc((1 - var(--crop) / 50) * 100% + var(--twintag-scanner-corners-offset));opacity:inherit;background:linear-gradient(to right, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 0, linear-gradient(to right, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 100%, linear-gradient(to left, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 0, linear-gradient(to left, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 100%, linear-gradient(to bottom, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 0, linear-gradient(to bottom, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 0, linear-gradient(to top, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 100%, linear-gradient(to top, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 100%;background-size:var(--twintag-scanner-corners-length) var(--twintag-scanner-corners-length);background-repeat:no-repeat;z-index:2222}:host .video-container video{position:absolute;width:100% !important;object-fit:cover;object-position:center;aspect-ratio:1/1 !important;min-height:var(--twintag-scanner-min-height)}";
15
15
 
16
16
  const TwintagQRScanner = class {
17
17
  constructor(hostRef) {
18
18
  index.registerInstance(this, hostRef);
19
19
  this.symbol = index.createEvent(this, "symbol", 7);
20
20
  this.streamIsActive = index.createEvent(this, "streamIsActive", 7);
21
- this.stageWidth = 0;
22
- this.stageHeight = 0;
21
+ this.frame = index.createEvent(this, "frame", 7);
23
22
  this.devices = [];
24
23
  this.stream = null;
25
24
  this.mode = 'single';
26
- this.format = '';
25
+ this.format = 'QRCode';
27
26
  this.crop = 1;
28
27
  this.zoom = 0;
29
28
  }
@@ -45,15 +44,15 @@ const TwintagQRScanner = class {
45
44
  willReadFrequently: true,
46
45
  });
47
46
  this.videoContainerRef.style.setProperty('--crop', `${(1 - this.crop) * 50}`);
48
- this.updateVideoStream();
47
+ await this.startStream();
49
48
  }
50
49
  async readBarcodeFromCanvas() {
51
50
  const imgWidth = this.canvas.width;
52
51
  const imgHeight = this.canvas.height;
53
52
  const imageData = this.canvas.getContext('2d').getImageData(0, 0, imgWidth, imgHeight);
54
53
  const sourceBuffer = imageData.data;
55
- this.drawInCanvas();
56
54
  await this.updateVideoZoom();
55
+ this.drawInCanvas();
57
56
  worker.postMessage({
58
57
  type: 'read',
59
58
  format: this.format,
@@ -63,50 +62,86 @@ const TwintagQRScanner = class {
63
62
  sourceBuffer: sourceBuffer
64
63
  }
65
64
  });
65
+ this.frame.emit(this.canvas.toDataURL('image/png'));
66
66
  requestAnimationFrame(this.readBarcodeFromCanvas.bind(this));
67
67
  }
68
- stopStream() {
69
- worker.removeEventListener('message', this.messageHandler.bind(this));
70
- this.stream.getTracks().forEach((track) => {
71
- return track.stop();
72
- });
73
- index$1.gsapWithCSS.to(this.videoContainerRef, {
74
- duration: 0.2,
75
- opacity: 0,
76
- onComplete: () => {
77
- this.video.srcObject = null;
78
- this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
79
- },
80
- });
81
- }
82
68
  drawInCanvas() {
83
- const ratio = this.video.videoWidth / this.video.videoHeight;
84
- if (ratio > 1) {
85
- this.stageWidth = this.video.videoHeight;
86
- this.stageHeight = this.video.videoHeight;
87
- }
88
- else if (ratio < 1) {
89
- this.stageWidth = this.video.videoWidth;
90
- this.stageHeight = this.video.videoWidth;
91
- }
92
- else if (ratio === 1) {
93
- this.stageWidth = this.video.videoWidth;
94
- this.stageHeight = this.video.videoHeight;
95
- }
69
+ const stage = Math.min(this.video.videoWidth, this.video.videoHeight);
70
+ const squareLength = stage * this.crop;
96
71
  const center = {
97
72
  x: this.video.videoWidth / 2,
98
73
  y: this.video.videoHeight / 2,
99
74
  };
100
- const sx = center.x - (this.stageWidth * this.crop) / 2;
101
- const sy = center.y - (this.stageHeight * this.crop) / 2;
102
- const sWidth = this.stageWidth * this.crop;
103
- const sHeight = this.stageHeight * this.crop;
104
- if (this.video.videoHeight !== this.stageHeight ||
105
- this.video.videoWidth !== this.stageWidth) {
106
- this.canvas.width = sWidth;
107
- this.canvas.height = sHeight;
75
+ const sx = center.x - squareLength / 2;
76
+ const sy = center.y - squareLength / 2;
77
+ this.context.drawImage(this.video, sx, sy, squareLength, squareLength, 0, 0, stage, stage);
78
+ }
79
+ async updateStream(constraints) {
80
+ if (this.stream) {
81
+ this.stream.getTracks().forEach((track) => {
82
+ return track.stop();
83
+ });
84
+ }
85
+ try {
86
+ this.stream = await navigator.mediaDevices.getUserMedia(constraints);
87
+ this.video.srcObject = this.stream;
88
+ this.video.setAttribute("playsinline", 'true'); // required to tell iOS safari we don't want fullscreen
89
+ await this.video.play();
90
+ }
91
+ catch (err) {
92
+ console.error(err);
93
+ }
94
+ }
95
+ async getTrackSettings(constraints) {
96
+ try {
97
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
98
+ return stream.getVideoTracks()[0].getSettings();
99
+ }
100
+ catch (err) {
101
+ console.error(err);
102
+ }
103
+ }
104
+ async getMedia(constraints) {
105
+ constraints.video.width = { min: 1080, ideal: 1600, max: 1920 };
106
+ try {
107
+ await this.updateStream(constraints);
108
+ const devices = await navigator.mediaDevices.enumerateDevices();
109
+ this.devices = devices.filter((device) => device.kind.toLowerCase() === 'videoinput' && device.label.toLowerCase().includes('back'));
110
+ if (this.devices.length === 0) {
111
+ return;
112
+ }
113
+ if (this.devices.length === 1) {
114
+ constraints.video.deviceId = { exact: this.devices[0].deviceId };
115
+ await this.updateStream(constraints);
116
+ }
117
+ let backCameraIndex = this.devices.length - 1;
118
+ const cameraResolutions = this.devices.map(camera => {
119
+ const match = camera.label.match(/\b([0-9]+)MP?\b/i);
120
+ if (match != null) {
121
+ return parseInt(match[1], 10);
122
+ }
123
+ return NaN;
124
+ });
125
+ if (!cameraResolutions.some(cameraResolution => isNaN(cameraResolution))) {
126
+ backCameraIndex = cameraResolutions.lastIndexOf(Math.max(...cameraResolutions));
127
+ }
128
+ // back camera with the highest resolution
129
+ const backCamera = this.devices[backCameraIndex];
130
+ if (backCamera) {
131
+ constraints.video.deviceId = { exact: backCamera.deviceId };
132
+ await this.updateStream(constraints);
133
+ return;
134
+ }
135
+ const settings = await this.getTrackSettings(constraints);
136
+ if (settings.focusDistance) {
137
+ const focusDistance = settings.focusDistance;
138
+ constraints.video.advanced = [{ focusMode: 'continuous', focusDistance: focusDistance }];
139
+ await this.updateStream(constraints);
140
+ }
141
+ }
142
+ catch (err) {
143
+ console.error(err);
108
144
  }
109
- this.context.drawImage(this.video, sx, sy, sWidth, sHeight, 0, 0, sWidth, sHeight);
110
145
  }
111
146
  async updateVideoZoom() {
112
147
  const [videoTrack] = this.stream.getVideoTracks();
@@ -126,64 +161,52 @@ const TwintagQRScanner = class {
126
161
  }
127
162
  }
128
163
  }
129
- async updateVideoStream(deviceId) {
130
- const selectedCamera = window.localStorage.getItem('twintag-scanner-camera');
164
+ async startStream() {
131
165
  const constraints = {
132
166
  audio: false,
133
- video: Object.create(null),
167
+ video: {
168
+ aspectRatio: { ideal: 1 },
169
+ facingMode: 'environment',
170
+ }
134
171
  };
135
172
  const supports = navigator.mediaDevices.getSupportedConstraints();
136
173
  if (supports.zoom) {
137
174
  constraints.video['zoom'] = true;
138
175
  }
139
- if (deviceId) {
140
- window.localStorage.setItem('twintag-scanner-camera', deviceId);
141
- constraints.video['deviceId'] = {
142
- exact: deviceId,
143
- };
144
- }
145
- else if (selectedCamera && selectedCamera !== 'undefined') {
146
- constraints.video['deviceId'] = {
147
- exact: selectedCamera,
148
- };
149
- }
150
- else {
151
- constraints.video['facingMode'] = 'environment';
152
- }
153
- if (!this.stream) {
154
- const stream = await navigator.mediaDevices.getUserMedia(constraints);
155
- this.stream = stream;
156
- }
157
- else {
158
- this.stream = this.stream;
159
- }
160
- this.streamIsActive.emit(true);
161
- const allDevices = await navigator.mediaDevices.enumerateDevices();
162
- this.devices = allDevices.filter((device) => device.kind === 'videoinput' && device.label.includes('back'));
176
+ await this.getMedia(constraints);
163
177
  await this.updateVideoZoom();
164
- this.video.srcObject = this.stream;
165
- this.video.setAttribute("playsinline", 'true'); // required to tell iOS safari we don't want fullscreen
166
- await this.video.play();
167
- this.canvas.width = this.video.clientWidth;
168
- this.canvas.height = this.video.clientHeight;
169
178
  index$1.gsapWithCSS.to(this.videoContainerRef, {
170
179
  duration: 0.2,
171
180
  opacity: 1,
172
181
  onComplete: () => {
182
+ this.streamIsActive.emit(true);
183
+ const stage = Math.min(this.video.videoWidth, this.video.videoHeight);
184
+ this.canvas.width = stage;
185
+ this.canvas.height = stage;
173
186
  this.readBarcodeFromCanvas();
174
187
  }
175
188
  });
176
189
  }
177
190
  ;
191
+ stopStream() {
192
+ worker.removeEventListener('message', this.messageHandler.bind(this));
193
+ index$1.gsapWithCSS.to(this.videoContainerRef, {
194
+ duration: 0.2,
195
+ opacity: 0,
196
+ onComplete: () => {
197
+ this.stream.getTracks().forEach((track) => {
198
+ return track.stop();
199
+ });
200
+ this.video.srcObject = null;
201
+ this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
202
+ },
203
+ });
204
+ }
178
205
  disconnectedCallback() {
179
206
  this.stopStream();
180
207
  }
181
208
  render() {
182
- return (index.h(index.Host, null, index.h("div", { class: "video-container", ref: (el) => (this.videoContainerRef = el) }, index.h("div", { class: "video-corners" }), index.h("video", { crossOrigin: "Anonymous", autoplay: true, playsinline: true, ref: (el) => (this.video = el) }), this.devices.length > 1 ? (index.h("div", { class: "cameras-container" }, index.h("select", { onChange: (e) => {
183
- this.stopStream();
184
- this.updateVideoStream(e.target.value);
185
- } }, index.h("option", { hidden: true, disabled: true }, "-- Select a camera --"), this.devices.map((deviceInfo, idx) => (index.h("option", { key: idx, value: deviceInfo.deviceId, selected: window.localStorage.getItem('twintag-scanner-camera') ===
186
- deviceInfo.deviceId }, `${deviceInfo.label}`)))))) : null)));
209
+ return (index.h(index.Host, null, index.h("div", { class: "video-container", ref: (el) => (this.videoContainerRef = el) }, index.h("div", { class: "video-corners" }), index.h("video", { crossOrigin: "Anonymous", autoplay: true, playsinline: true, ref: (el) => (this.video = el) }))));
187
210
  }
188
211
  static get assetsDirs() { return ["assets"]; }
189
212
  };
@@ -36218,7 +36218,7 @@ class Preprocessing {
36218
36218
  }
36219
36219
  }
36220
36220
 
36221
- const twintagScannerCss = ":host{--twintag-scanner-bg-select-cameras:white;--twintag-scanner-text-select-cameras:black;--twintag-scanner-radius-select-cameras:6px 0 0 0;--twintag-scanner-min-height:min-content;--twintag-scanner-corners-color:white;--twintag-scanner-corners-width:4px;--twintag-scanner-corners-length:36px;--twintag-scanner-corners-offset:24px;position:relative}:host .icon-container{display:grid;place-items:center;position:absolute;inset:0;z-index:-2222}:host .icon-container .check-icon{opacity:0;z-index:2222}:host .video-container{--crop:0;display:grid;place-items:center;position:relative;width:100%;opacity:0;overflow:hidden;z-index:1}:host .video-container::after{content:\"\";position:absolute;width:100%;height:100%;opacity:inherit;background:rgba(0, 0, 0, 0.5);-webkit-clip-path:polygon(0% 0%, 0% 100%, calc(var(--crop) * 1%) 100%, calc(var(--crop) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) 100%, 100% 100%, 100% 0%);clip-path:polygon(0% 0%, 0% 100%, calc(var(--crop) * 1%) 100%, calc(var(--crop) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) 100%, 100% 100%, 100% 0%)}:host .video-container:before{display:block;content:\"\";width:100%;padding-top:100%}:host .video-container .video-corners{position:absolute;width:calc((1 - var(--crop) / 50) * 100% + var(--twintag-scanner-corners-offset));height:calc((1 - var(--crop) / 50) * 100% + var(--twintag-scanner-corners-offset));opacity:inherit;background:linear-gradient(to right, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 0, linear-gradient(to right, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 100%, linear-gradient(to left, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 0, linear-gradient(to left, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 100%, linear-gradient(to bottom, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 0, linear-gradient(to bottom, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 0, linear-gradient(to top, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 100%, linear-gradient(to top, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 100%;background-size:var(--twintag-scanner-corners-length) var(--twintag-scanner-corners-length);background-repeat:no-repeat;z-index:2222}:host .video-container video{position:absolute !important;top:0 !important;right:0 !important;bottom:0 !important;left:0 !important;width:100% !important;height:100% !important;object-fit:cover;object-position:center;aspect-ratio:1/1 !important;min-height:var(--twintag-scanner-min-height)}:host .cameras-container{position:absolute;right:0;bottom:0;width:100%;min-width:10ch;max-width:max-content;padding:0rem 0.25rem;border:none;cursor:pointer;border-radius:var(--twintag-scanner-radius-select-cameras);background-color:var(--twintag-scanner-bg-select-cameras);line-height:1.1;font-size:1rem;opacity:inherit;z-index:2222}:host .cameras-container select{display:grid;place-items:center;width:max-content;margin:0;padding:0.75rem;border:none;background-color:transparent;appearance:none;--webkit-appearance:none;-moz-appearance:none;color:var(--twintag-scanner-text-select-cameras);cursor:inherit;font-family:inherit;font-size:inherit;line-height:inherit;outline:none}:host .cameras-container select::-ms-expand{display:none}";
36221
+ const twintagScannerCss = ":host{--twintag-scanner-bg-select-cameras:white;--twintag-scanner-text-select-cameras:black;--twintag-scanner-radius-select-cameras:6px 0 0 0;--twintag-scanner-min-height:min-content;--twintag-scanner-corners-color:white;--twintag-scanner-corners-width:4px;--twintag-scanner-corners-length:36px;--twintag-scanner-corners-offset:24px;display:grid;place-items:center;position:relative;width:100%}:host .icon-container{display:grid;place-items:center;position:absolute;inset:0;z-index:-2222}:host .icon-container .check-icon{opacity:0;z-index:2222}:host .video-container{--crop:0;display:flex;align-items:center;justify-content:center;position:relative;width:100%;height:100%;opacity:0;overflow:hidden;z-index:1}:host .video-container::after{content:\"\";position:absolute;width:100%;height:100%;opacity:inherit;background:rgba(0, 0, 0, 0.5);-webkit-clip-path:polygon(0% 0%, 0% 100%, calc(var(--crop) * 1%) 100%, calc(var(--crop) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) 100%, 100% 100%, 100% 0%);clip-path:polygon(0% 0%, 0% 100%, calc(var(--crop) * 1%) 100%, calc(var(--crop) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(var(--crop) * 1%), calc(calc(100 - var(--crop)) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) calc(calc(100 - var(--crop)) * 1%), calc(var(--crop) * 1%) 100%, 100% 100%, 100% 0%)}:host .video-container:before{display:block;content:\"\";width:100%;padding-top:100%}:host .video-container .video-corners{position:absolute;width:calc((1 - var(--crop) / 50) * 100% + var(--twintag-scanner-corners-offset));height:calc((1 - var(--crop) / 50) * 100% + var(--twintag-scanner-corners-offset));opacity:inherit;background:linear-gradient(to right, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 0, linear-gradient(to right, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 100%, linear-gradient(to left, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 0, linear-gradient(to left, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 100%, linear-gradient(to bottom, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 0, linear-gradient(to bottom, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 0, linear-gradient(to top, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 0 100%, linear-gradient(to top, var(--twintag-scanner-corners-color) var(--twintag-scanner-corners-width), transparent var(--twintag-scanner-corners-width)) 100% 100%;background-size:var(--twintag-scanner-corners-length) var(--twintag-scanner-corners-length);background-repeat:no-repeat;z-index:2222}:host .video-container video{position:absolute;width:100% !important;object-fit:cover;object-position:center;aspect-ratio:1/1 !important;min-height:var(--twintag-scanner-min-height)}";
36222
36222
 
36223
36223
  const TwintagScanner = class {
36224
36224
  openHandler() {
@@ -3,12 +3,10 @@ import gsap from 'gsap';
3
3
  import { worker } from '../../scanner.worker?worker';
4
4
  export class TwintagQRScanner {
5
5
  constructor() {
6
- this.stageWidth = 0;
7
- this.stageHeight = 0;
8
6
  this.devices = [];
9
7
  this.stream = null;
10
8
  this.mode = 'single';
11
- this.format = '';
9
+ this.format = 'QRCode';
12
10
  this.crop = 1;
13
11
  this.zoom = 0;
14
12
  }
@@ -30,15 +28,15 @@ export class TwintagQRScanner {
30
28
  willReadFrequently: true,
31
29
  });
32
30
  this.videoContainerRef.style.setProperty('--crop', `${(1 - this.crop) * 50}`);
33
- this.updateVideoStream();
31
+ await this.startStream();
34
32
  }
35
33
  async readBarcodeFromCanvas() {
36
34
  const imgWidth = this.canvas.width;
37
35
  const imgHeight = this.canvas.height;
38
36
  const imageData = this.canvas.getContext('2d').getImageData(0, 0, imgWidth, imgHeight);
39
37
  const sourceBuffer = imageData.data;
40
- this.drawInCanvas();
41
38
  await this.updateVideoZoom();
39
+ this.drawInCanvas();
42
40
  worker.postMessage({
43
41
  type: 'read',
44
42
  format: this.format,
@@ -48,50 +46,86 @@ export class TwintagQRScanner {
48
46
  sourceBuffer: sourceBuffer
49
47
  }
50
48
  });
49
+ this.frame.emit(this.canvas.toDataURL('image/png'));
51
50
  requestAnimationFrame(this.readBarcodeFromCanvas.bind(this));
52
51
  }
53
- stopStream() {
54
- worker.removeEventListener('message', this.messageHandler.bind(this));
55
- this.stream.getTracks().forEach((track) => {
56
- return track.stop();
57
- });
58
- gsap.to(this.videoContainerRef, {
59
- duration: 0.2,
60
- opacity: 0,
61
- onComplete: () => {
62
- this.video.srcObject = null;
63
- this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
64
- },
65
- });
66
- }
67
52
  drawInCanvas() {
68
- const ratio = this.video.videoWidth / this.video.videoHeight;
69
- if (ratio > 1) {
70
- this.stageWidth = this.video.videoHeight;
71
- this.stageHeight = this.video.videoHeight;
72
- }
73
- else if (ratio < 1) {
74
- this.stageWidth = this.video.videoWidth;
75
- this.stageHeight = this.video.videoWidth;
76
- }
77
- else if (ratio === 1) {
78
- this.stageWidth = this.video.videoWidth;
79
- this.stageHeight = this.video.videoHeight;
80
- }
53
+ const stage = Math.min(this.video.videoWidth, this.video.videoHeight);
54
+ const squareLength = stage * this.crop;
81
55
  const center = {
82
56
  x: this.video.videoWidth / 2,
83
57
  y: this.video.videoHeight / 2,
84
58
  };
85
- const sx = center.x - (this.stageWidth * this.crop) / 2;
86
- const sy = center.y - (this.stageHeight * this.crop) / 2;
87
- const sWidth = this.stageWidth * this.crop;
88
- const sHeight = this.stageHeight * this.crop;
89
- if (this.video.videoHeight !== this.stageHeight ||
90
- this.video.videoWidth !== this.stageWidth) {
91
- this.canvas.width = sWidth;
92
- this.canvas.height = sHeight;
59
+ const sx = center.x - squareLength / 2;
60
+ const sy = center.y - squareLength / 2;
61
+ this.context.drawImage(this.video, sx, sy, squareLength, squareLength, 0, 0, stage, stage);
62
+ }
63
+ async updateStream(constraints) {
64
+ if (this.stream) {
65
+ this.stream.getTracks().forEach((track) => {
66
+ return track.stop();
67
+ });
68
+ }
69
+ try {
70
+ this.stream = await navigator.mediaDevices.getUserMedia(constraints);
71
+ this.video.srcObject = this.stream;
72
+ this.video.setAttribute("playsinline", 'true'); // required to tell iOS safari we don't want fullscreen
73
+ await this.video.play();
74
+ }
75
+ catch (err) {
76
+ console.error(err);
77
+ }
78
+ }
79
+ async getTrackSettings(constraints) {
80
+ try {
81
+ const stream = await navigator.mediaDevices.getUserMedia(constraints);
82
+ return stream.getVideoTracks()[0].getSettings();
83
+ }
84
+ catch (err) {
85
+ console.error(err);
86
+ }
87
+ }
88
+ async getMedia(constraints) {
89
+ constraints.video.width = { min: 1080, ideal: 1600, max: 1920 };
90
+ try {
91
+ await this.updateStream(constraints);
92
+ const devices = await navigator.mediaDevices.enumerateDevices();
93
+ this.devices = devices.filter((device) => device.kind.toLowerCase() === 'videoinput' && device.label.toLowerCase().includes('back'));
94
+ if (this.devices.length === 0) {
95
+ return;
96
+ }
97
+ if (this.devices.length === 1) {
98
+ constraints.video.deviceId = { exact: this.devices[0].deviceId };
99
+ await this.updateStream(constraints);
100
+ }
101
+ let backCameraIndex = this.devices.length - 1;
102
+ const cameraResolutions = this.devices.map(camera => {
103
+ const match = camera.label.match(/\b([0-9]+)MP?\b/i);
104
+ if (match != null) {
105
+ return parseInt(match[1], 10);
106
+ }
107
+ return NaN;
108
+ });
109
+ if (!cameraResolutions.some(cameraResolution => isNaN(cameraResolution))) {
110
+ backCameraIndex = cameraResolutions.lastIndexOf(Math.max(...cameraResolutions));
111
+ }
112
+ // back camera with the highest resolution
113
+ const backCamera = this.devices[backCameraIndex];
114
+ if (backCamera) {
115
+ constraints.video.deviceId = { exact: backCamera.deviceId };
116
+ await this.updateStream(constraints);
117
+ return;
118
+ }
119
+ const settings = await this.getTrackSettings(constraints);
120
+ if (settings.focusDistance) {
121
+ const focusDistance = settings.focusDistance;
122
+ constraints.video.advanced = [{ focusMode: 'continuous', focusDistance: focusDistance }];
123
+ await this.updateStream(constraints);
124
+ }
125
+ }
126
+ catch (err) {
127
+ console.error(err);
93
128
  }
94
- this.context.drawImage(this.video, sx, sy, sWidth, sHeight, 0, 0, sWidth, sHeight);
95
129
  }
96
130
  async updateVideoZoom() {
97
131
  const [videoTrack] = this.stream.getVideoTracks();
@@ -111,64 +145,52 @@ export class TwintagQRScanner {
111
145
  }
112
146
  }
113
147
  }
114
- async updateVideoStream(deviceId) {
115
- const selectedCamera = window.localStorage.getItem('twintag-scanner-camera');
148
+ async startStream() {
116
149
  const constraints = {
117
150
  audio: false,
118
- video: Object.create(null),
151
+ video: {
152
+ aspectRatio: { ideal: 1 },
153
+ facingMode: 'environment',
154
+ }
119
155
  };
120
156
  const supports = navigator.mediaDevices.getSupportedConstraints();
121
157
  if (supports.zoom) {
122
158
  constraints.video['zoom'] = true;
123
159
  }
124
- if (deviceId) {
125
- window.localStorage.setItem('twintag-scanner-camera', deviceId);
126
- constraints.video['deviceId'] = {
127
- exact: deviceId,
128
- };
129
- }
130
- else if (selectedCamera && selectedCamera !== 'undefined') {
131
- constraints.video['deviceId'] = {
132
- exact: selectedCamera,
133
- };
134
- }
135
- else {
136
- constraints.video['facingMode'] = 'environment';
137
- }
138
- if (!this.stream) {
139
- const stream = await navigator.mediaDevices.getUserMedia(constraints);
140
- this.stream = stream;
141
- }
142
- else {
143
- this.stream = this.stream;
144
- }
145
- this.streamIsActive.emit(true);
146
- const allDevices = await navigator.mediaDevices.enumerateDevices();
147
- this.devices = allDevices.filter((device) => device.kind === 'videoinput' && device.label.includes('back'));
160
+ await this.getMedia(constraints);
148
161
  await this.updateVideoZoom();
149
- this.video.srcObject = this.stream;
150
- this.video.setAttribute("playsinline", 'true'); // required to tell iOS safari we don't want fullscreen
151
- await this.video.play();
152
- this.canvas.width = this.video.clientWidth;
153
- this.canvas.height = this.video.clientHeight;
154
162
  gsap.to(this.videoContainerRef, {
155
163
  duration: 0.2,
156
164
  opacity: 1,
157
165
  onComplete: () => {
166
+ this.streamIsActive.emit(true);
167
+ const stage = Math.min(this.video.videoWidth, this.video.videoHeight);
168
+ this.canvas.width = stage;
169
+ this.canvas.height = stage;
158
170
  this.readBarcodeFromCanvas();
159
171
  }
160
172
  });
161
173
  }
162
174
  ;
175
+ stopStream() {
176
+ worker.removeEventListener('message', this.messageHandler.bind(this));
177
+ gsap.to(this.videoContainerRef, {
178
+ duration: 0.2,
179
+ opacity: 0,
180
+ onComplete: () => {
181
+ this.stream.getTracks().forEach((track) => {
182
+ return track.stop();
183
+ });
184
+ this.video.srcObject = null;
185
+ this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
186
+ },
187
+ });
188
+ }
163
189
  disconnectedCallback() {
164
190
  this.stopStream();
165
191
  }
166
192
  render() {
167
- return (h(Host, null, h("div", { class: "video-container", ref: (el) => (this.videoContainerRef = el) }, h("div", { class: "video-corners" }), h("video", { crossOrigin: "Anonymous", autoplay: true, playsinline: true, ref: (el) => (this.video = el) }), this.devices.length > 1 ? (h("div", { class: "cameras-container" }, h("select", { onChange: (e) => {
168
- this.stopStream();
169
- this.updateVideoStream(e.target.value);
170
- } }, h("option", { hidden: true, disabled: true }, "-- Select a camera --"), this.devices.map((deviceInfo, idx) => (h("option", { key: idx, value: deviceInfo.deviceId, selected: window.localStorage.getItem('twintag-scanner-camera') ===
171
- deviceInfo.deviceId }, `${deviceInfo.label}`)))))) : null)));
193
+ return (h(Host, null, h("div", { class: "video-container", ref: (el) => (this.videoContainerRef = el) }, h("div", { class: "video-corners" }), h("video", { crossOrigin: "Anonymous", autoplay: true, playsinline: true, ref: (el) => (this.video = el) }))));
172
194
  }
173
195
  static get is() { return "twintag-qr-scanner"; }
174
196
  static get encapsulation() { return "shadow"; }
@@ -244,7 +266,7 @@ export class TwintagQRScanner {
244
266
  },
245
267
  "attribute": "format",
246
268
  "reflect": false,
247
- "defaultValue": "''"
269
+ "defaultValue": "'QRCode'"
248
270
  },
249
271
  "crop": {
250
272
  "type": "number",
@@ -320,6 +342,21 @@ export class TwintagQRScanner {
320
342
  "resolved": "boolean",
321
343
  "references": {}
322
344
  }
345
+ }, {
346
+ "method": "frame",
347
+ "name": "frame",
348
+ "bubbles": true,
349
+ "cancelable": true,
350
+ "composed": true,
351
+ "docs": {
352
+ "tags": [],
353
+ "text": ""
354
+ },
355
+ "complexType": {
356
+ "original": "string",
357
+ "resolved": "string",
358
+ "references": {}
359
+ }
323
360
  }];
324
361
  }
325
362
  }
@@ -7,7 +7,10 @@
7
7
  --twintag-scanner-corners-width: 4px;
8
8
  --twintag-scanner-corners-length: 36px;
9
9
  --twintag-scanner-corners-offset: 24px;
10
+ display: grid;
11
+ place-items: center;
10
12
  position: relative;
13
+ width: 100%;
11
14
  }
12
15
  :host .icon-container {
13
16
  display: grid;
@@ -22,10 +25,12 @@
22
25
  }
23
26
  :host .video-container {
24
27
  --crop: 0;
25
- display: grid;
26
- place-items: center;
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
27
31
  position: relative;
28
32
  width: 100%;
33
+ height: 100%;
29
34
  opacity: 0;
30
35
  overflow: hidden;
31
36
  z-index: 1;
@@ -57,53 +62,10 @@
57
62
  z-index: 2222;
58
63
  }
59
64
  :host .video-container video {
60
- position: absolute !important;
61
- top: 0 !important;
62
- right: 0 !important;
63
- bottom: 0 !important;
64
- left: 0 !important;
65
+ position: absolute;
65
66
  width: 100% !important;
66
- height: 100% !important;
67
67
  object-fit: cover;
68
68
  object-position: center;
69
69
  aspect-ratio: 1/1 !important;
70
70
  min-height: var(--twintag-scanner-min-height);
71
- }
72
- :host .cameras-container {
73
- position: absolute;
74
- right: 0;
75
- bottom: 0;
76
- width: 100%;
77
- min-width: 10ch;
78
- max-width: max-content;
79
- padding: 0rem 0.25rem;
80
- border: none;
81
- cursor: pointer;
82
- border-radius: var(--twintag-scanner-radius-select-cameras);
83
- background-color: var(--twintag-scanner-bg-select-cameras);
84
- line-height: 1.1;
85
- font-size: 1rem;
86
- opacity: inherit;
87
- z-index: 2222;
88
- }
89
- :host .cameras-container select {
90
- display: grid;
91
- place-items: center;
92
- width: max-content;
93
- margin: 0;
94
- padding: 0.75rem;
95
- border: none;
96
- background-color: transparent;
97
- appearance: none;
98
- --webkit-appearance: none;
99
- -moz-appearance: none;
100
- color: var(--twintag-scanner-text-select-cameras);
101
- cursor: inherit;
102
- font-family: inherit;
103
- font-size: inherit;
104
- line-height: inherit;
105
- outline: none;
106
- }
107
- :host .cameras-container select::-ms-expand {
108
- display: none;
109
71
  }
@@ -2,4 +2,4 @@
2
2
  /**
3
3
  * The library version.
4
4
  */
5
- export const VERSION = '0.2.276';
5
+ export const VERSION = '0.2.277';
@@ -223,7 +223,7 @@ class IndexedDbService {
223
223
  /**
224
224
  * The SDK version.
225
225
  */
226
- const VERSION = '0.2.276';
226
+ const VERSION = '0.0.0-VERSION';
227
227
 
228
228
  class TwintagErrorValue {
229
229
  }