@bitstack/ng-boundary 14.0.6-alpha.4 → 14.0.6-alpha.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/esm2020/lib/bs-boundary.mjs +28 -32
- package/fesm2015/bitstack-ng-boundary.mjs +27 -31
- package/fesm2015/bitstack-ng-boundary.mjs.map +1 -1
- package/fesm2020/bitstack-ng-boundary.mjs +27 -31
- package/fesm2020/bitstack-ng-boundary.mjs.map +1 -1
- package/lib/bs-boundary.d.ts +19 -6
- package/package.json +1 -1
|
@@ -113,7 +113,7 @@ export class BsBoundary {
|
|
|
113
113
|
this.setupResizeObserver();
|
|
114
114
|
}
|
|
115
115
|
ngAfterViewInit() {
|
|
116
|
-
//
|
|
116
|
+
// 使用 @Input 參數,可以立即計算(無需等待 CSS)
|
|
117
117
|
this.updateBoundaryCalculations();
|
|
118
118
|
}
|
|
119
119
|
ngOnDestroy() {
|
|
@@ -197,44 +197,35 @@ export class BsBoundary {
|
|
|
197
197
|
* 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)
|
|
198
198
|
*
|
|
199
199
|
* 工作原理:
|
|
200
|
-
* 1.
|
|
201
|
-
* 2.
|
|
202
|
-
* 3.
|
|
203
|
-
* 4.
|
|
204
|
-
* 5.
|
|
200
|
+
* 1. 從 @Input 參數讀取設計尺寸
|
|
201
|
+
* 2. 讀取視口尺寸(vw100, vh100)
|
|
202
|
+
* 3. 處理 lockRotation 模式的視口尺寸交換
|
|
203
|
+
* 4. 計算邊界最大值和實際邊界尺寸
|
|
204
|
+
* 5. 計算 px2vw 轉換比例
|
|
205
|
+
* 6. 設定 CSS 變數供樣式使用
|
|
205
206
|
*
|
|
206
207
|
* 設定的 CSS 變數:
|
|
208
|
+
* - --design-width: 設計稿寬度
|
|
209
|
+
* - --design-height: 設計稿高度
|
|
207
210
|
* - --boundary-width: 實際邊界寬度(px)
|
|
208
211
|
* - --boundary-height: 實際邊界高度(px)
|
|
209
212
|
* - --px2vw-ratio: px 轉 vw 的比例係數
|
|
210
213
|
*/
|
|
211
214
|
updateBoundaryCalculations() {
|
|
212
215
|
const hostElement = this.elementRef.nativeElement;
|
|
213
|
-
const computedStyle = getComputedStyle(hostElement);
|
|
214
216
|
// ========================================
|
|
215
|
-
// Step 1:
|
|
217
|
+
// Step 1: 讀取設計尺寸並設定 CSS 變數
|
|
216
218
|
// ========================================
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
console.error('[BsBoundary][BS002] CSS variable --design-height is not set or invalid');
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
if (designWidth === 0) {
|
|
231
|
-
console.error('[BsBoundary][BS003] CSS variable --design-width cannot be zero');
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
if (designHeight === 0) {
|
|
235
|
-
console.error('[BsBoundary][BS004] CSS variable --design-height cannot be zero');
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
219
|
+
const designWidth = this.designWidth;
|
|
220
|
+
const designHeight = this.designHeight;
|
|
221
|
+
// 設定 CSS 變數,供樣式使用(例如 px2vw() 函數)
|
|
222
|
+
hostElement.style.setProperty('--design-width', `${designWidth}`);
|
|
223
|
+
hostElement.style.setProperty('--design-height', `${designHeight}`);
|
|
224
|
+
// ========================================
|
|
225
|
+
// Step 2: 讀取視口尺寸(直接使用 window.inner* 確保一致性)
|
|
226
|
+
// ========================================
|
|
227
|
+
let vw100 = window.innerWidth;
|
|
228
|
+
let vh100 = window.innerHeight;
|
|
238
229
|
// ========================================
|
|
239
230
|
// Step 2: 處理 lockRotation 模式的視口尺寸交換
|
|
240
231
|
// ========================================
|
|
@@ -281,17 +272,18 @@ export class BsBoundary {
|
|
|
281
272
|
}
|
|
282
273
|
}
|
|
283
274
|
BsBoundary.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
284
|
-
BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", orientation: "orientation", forwardStyle: "forwardStyle" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.landscape": "isLandscape && orientation === \"landscape\"", "class.portrait": "!isLandscape && orientation === \"portrait\"", "class.landscape-lock-rotation": "orientation === \"landscapeLockRotation\"", "class.portrait-lock-rotation": "orientation === \"portraitLockRotation\"", "class.actual-portrait": "actualIsPortrait", "class.actual-landscape": "!actualIsPortrait" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))
|
|
275
|
+
BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", orientation: "orientation", forwardStyle: "forwardStyle", designWidth: "designWidth", designHeight: "designHeight" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.auto": "orientation === \"auto\"", "class.landscape": "isLandscape && orientation === \"landscape\"", "class.portrait": "!isLandscape && orientation === \"portrait\"", "class.landscape-lock-rotation": "orientation === \"landscapeLockRotation\"", "class.portrait-lock-rotation": "orientation === \"portraitLockRotation\"", "class.actual-portrait": "actualIsPortrait", "class.actual-landscape": "!actualIsPortrait" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.portrait{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}@media (orientation: landscape){:host.auto{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}:host.landscape-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}:host.landscape-lock-rotation.actual-portrait .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}:host.portrait-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.portrait-lock-rotation.actual-landscape .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}\n"] });
|
|
285
276
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, decorators: [{
|
|
286
277
|
type: Component,
|
|
287
278
|
args: [{ selector: 'bs-boundary', standalone: true, providers: [BsBoundaryContextService], host: {
|
|
279
|
+
'[class.auto]': 'orientation === "auto"',
|
|
288
280
|
'[class.landscape]': 'isLandscape && orientation === "landscape"',
|
|
289
281
|
'[class.portrait]': '!isLandscape && orientation === "portrait"',
|
|
290
282
|
'[class.landscape-lock-rotation]': 'orientation === "landscapeLockRotation"',
|
|
291
283
|
'[class.portrait-lock-rotation]': 'orientation === "portraitLockRotation"',
|
|
292
284
|
'[class.actual-portrait]': 'actualIsPortrait',
|
|
293
285
|
'[class.actual-landscape]': '!actualIsPortrait'
|
|
294
|
-
}, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))
|
|
286
|
+
}, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.portrait{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}@media (orientation: landscape){:host.auto{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}:host.landscape-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}:host.landscape-lock-rotation.actual-portrait .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}:host.portrait-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.portrait-lock-rotation.actual-landscape .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}\n"] }]
|
|
295
287
|
}], propDecorators: { isFixedCenter: [{
|
|
296
288
|
type: Input
|
|
297
289
|
}], pointerEventsNone: [{
|
|
@@ -300,7 +292,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
300
292
|
type: Input
|
|
301
293
|
}], forwardStyle: [{
|
|
302
294
|
type: Input
|
|
295
|
+
}], designWidth: [{
|
|
296
|
+
type: Input
|
|
297
|
+
}], designHeight: [{
|
|
298
|
+
type: Input
|
|
303
299
|
}], orientationChange: [{
|
|
304
300
|
type: Output
|
|
305
301
|
}] } });
|
|
306
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnMtYm91bmRhcnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9iaXRzdGFjay1uZy1ib3VuZGFyeS9zcmMvbGliL2JzLWJvdW5kYXJ5LnRzIiwiLi4vLi4vLi4vLi4vcHJvamVjdHMvYml0c3RhY2stbmctYm91bmRhcnkvc3JjL2xpYi90ZW1wbGF0ZS5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBQyxrQkFBa0IsRUFBQyxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZELE9BQU8sRUFFSCxTQUFTLEVBQ1QsVUFBVSxFQUNWLFlBQVksRUFDWixNQUFNLEVBQ04sS0FBSyxFQUVMLE1BQU0sR0FDVCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQWUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUMsTUFBTSxNQUFNLENBQUM7QUFFakUsT0FBTyxFQUFDLHdCQUF3QixFQUFDLE1BQU0sK0JBQStCLENBQUM7O0FBR3ZFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBeUJHO0FBZ0JILE1BQU0sT0FBTyxVQUFVO0lBZnZCO1FBaUJJLCtEQUErRDtRQUMvRCxtQkFBbUI7UUFDbkIsK0RBQStEO1FBRS9EOzs7V0FHRztRQUNNLGtCQUFhLEdBQWEsS0FBSyxDQUFDO1FBRXpDOzs7V0FHRztRQUNNLHNCQUFpQixHQUFhLEtBQUssQ0FBQztRQUU3Qzs7Ozs7Ozs7V0FRRztRQUNNLGdCQUFXLEdBQXNCLE1BQU0sQ0FBQztRQU9qRDs7OztXQUlHO1FBQ08sc0JBQWlCLEdBQUcsSUFBSSxZQUFZLEVBQVcsQ0FBQztRQUUxRCwrREFBK0Q7UUFDL0QsdUJBQXVCO1FBQ3ZCLCtEQUErRDtRQUV2RCx1QkFBa0IsR0FBRyxNQUFNLENBQUMsa0JBQWtCLENBQUMsQ0FBQztRQUNoRCxvQkFBZSxHQUFHLE1BQU0sQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO1FBQ25ELGVBQVUsR0FBRyxNQUFNLENBQUMsVUFBVSxD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ZUFBZSxFQUFFLEdBQUcsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUNwRSxDQUFDOzt1R0E5UlEsVUFBVTsyRkFBVixVQUFVLGtwQkFWUixDQUFDLHdCQUF3QixDQUFDLDBCQy9DekMseU1BUUE7MkZEaURhLFVBQVU7a0JBZnRCLFNBQVM7K0JBQ0ksYUFBYSxjQUVYLElBQUksYUFFTCxDQUFDLHdCQUF3QixDQUFDLFFBQy9CO3dCQUNGLG1CQUFtQixFQUFFLDRDQUE0Qzt3QkFDakUsa0JBQWtCLEVBQUUsNENBQTRDO3dCQUNoRSxpQ0FBaUMsRUFBRSx5Q0FBeUM7d0JBQzVFLGdDQUFnQyxFQUFFLHdDQUF3Qzt3QkFDMUUseUJBQXlCLEVBQUUsa0JBQWtCO3dCQUM3QywwQkFBMEIsRUFBRSxtQkFBbUI7cUJBQ2xEOzhCQVlRLGFBQWE7c0JBQXJCLEtBQUs7Z0JBTUcsaUJBQWlCO3NCQUF6QixLQUFLO2dCQVdHLFdBQVc7c0JBQW5CLEtBQUs7Z0JBS0csWUFBWTtzQkFBcEIsS0FBSztnQkFPSSxpQkFBaUI7c0JBQTFCLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge0JyZWFrcG9pbnRPYnNlcnZlcn0gZnJvbSAnQGFuZ3VsYXIvY2RrL2xheW91dCc7XG5pbXBvcnQge1xuICAgIEFmdGVyVmlld0luaXQsXG4gICAgQ29tcG9uZW50LFxuICAgIEVsZW1lbnRSZWYsXG4gICAgRXZlbnRFbWl0dGVyLFxuICAgIGluamVjdCxcbiAgICBJbnB1dCwgT25EZXN0cm95LFxuICAgIE9uSW5pdCxcbiAgICBPdXRwdXQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtkZWJvdW5jZVRpbWUsIGZyb21FdmVudCwgU3ViamVjdCwgdGFrZVVudGlsfSBmcm9tICdyeGpzJztcblxuaW1wb3J0IHtCc0JvdW5kYXJ5Q29udGV4dFNlcnZpY2V9IGZyb20gJy4vYnMtYm91bmRhcnktY29udGV4dC5zZXJ2aWNlJztcbmltcG9ydCB7VE9yaWVudGF0aW9uTW9kZX0gZnJvbSAnLi9tb2RlbCc7XG5cbi8qKlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBCc0JvdW5kYXJ5IENvbXBvbmVudCAtIEFXRCDoh6rpganmh4npgornlYzlrrnlmahcbiAqID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICpcbiAqIOWKn+iDveiqquaYju+8mlxuICogMS4g5o+Q5L6b6Z+/5oeJ5byP6YKK55WM5a655Zmo77yM6Ieq5YuV6YGp5oeJ5LiN5ZCM6Kit5YKZ5bC65a+4XG4gKiAyLiDmlK/mj7QgNSDnqK7mlrnlkJHmqKHlvI/vvJphdXRvLCBwb3J0cmFpdCwgbGFuZHNjYXBlLCBwb3J0cmFpdExvY2tSb3RhdGlvbiwgbGFuZHNjYXBlTG9ja1JvdGF0aW9uXG4gKiAzLiDkvb/nlKggQ1NTIOiuiuaVuOWvpuePvuWLleaFi+ioiOeul++8jOaUr+aPtCBweDJ2dygpIOWHveaVuFxuICogNC4g55u45a65IENocm9tZSA3NCvvvIzkvb/nlKggSmF2YVNjcmlwdCDoqIjnrpfpgornlYzlsLrlr7hcbiAqXG4gKiDkvb/nlKjnr4TkvovvvJpcbiAqIGBgYGh0bWxcbiAqIDxicy1ib3VuZGFyeSBbb3JpZW50YXRpb25dPVwiJ2F1dG8nXCIgW2lzRml4ZWRDZW50ZXJdPVwidHJ1ZVwiPlxuICogICA8ZGl2IGNsYXNzPVwiY29udGVudFwiPi4uLjwvZGl2PlxuICogPC9icy1ib3VuZGFyeT5cbiAqIGBgYFxuICpcbiAqIENTUyDorormlbjoqK3lrprvvJpcbiAqIGBgYHNjc3NcbiAqIGJzLWJvdW5kYXJ5IHtcbiAqICAgLS1kZXNpZ24td2lkdGg6IDM3NTsgICAvLyDoqK3oqIjnqL/lr6zluqZcbiAqICAgLS1kZXNpZ24taGVpZ2h0OiA2Njc7ICAvLyDoqK3oqIjnqL/pq5jluqZcbiAqIH1cbiAqIGBgYFxuICovXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ2JzLWJvdW5kYXJ5JyxcbiAgICB0ZW1wbGF0ZVVybDogJy4vdGVtcGxhdGUuaHRtbCcsXG4gICAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgICBzdHlsZVVybHM6IFsnLi9zdHlsZXMuc2NzcyddLFxuICAgIHByb3ZpZGVyczogW0JzQm91bmRhcnlDb250ZXh0U2VydmljZV0sXG4gICAgaG9zdDoge1xuICAgICAgICAnW2NsYXNzLmxhbmRzY2FwZV0nOiAnaXNMYW5kc2NhcGUgJiYgb3JpZW50YXRpb24gPT09IFwibGFuZHNjYXBlXCInLFxuICAgICAgICAnW2NsYXNzLnBvcnRyYWl0XSc6ICchaXNMYW5kc2NhcGUgJiYgb3JpZW50YXRpb24gPT09IFwicG9ydHJhaXRcIicsXG4gICAgICAgICdbY2xhc3MubGFuZHNjYXBlLWxvY2stcm90YXRpb25dJzogJ29yaWVudGF0aW9uID09PSBcImxhbmRzY2FwZUxvY2tSb3RhdGlvblwiJyxcbiAgICAgICAgJ1tjbGFzcy5wb3J0cmFpdC1sb2NrLXJvdGF0aW9uXSc6ICdvcmllbnRhdGlvbiA9PT0gXCJwb3J0cmFpdExvY2tSb3RhdGlvblwiJyxcbiAgICAgICAgJ1tjbGFzcy5hY3R1YWwtcG9ydHJhaXRdJzogJ2FjdHVhbElzUG9ydHJhaXQnLFxuICAgICAgICAnW2NsYXNzLmFjdHVhbC1sYW5kc2NhcGVdJzogJyFhY3R1YWxJc1BvcnRyYWl0J1xuICAgIH1cbn0pXG5leHBvcnQgY2xhc3MgQnNCb3VuZGFyeSBpbXBsZW1lbnRzIE9uSW5pdCwgQWZ0ZXJWaWV3SW5pdCwgT25EZXN0cm95IHtcblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIElucHV0cyAmIE91dHB1dHNcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIC8qKlxuICAgICAqIOaYr+WQpuWwh+WFp+WuueWbuuWumuWcqOimlueql+S4reWkrlxuICAgICAqIEBkZWZhdWx0IGZhbHNlXG4gICAgICovXG4gICAgQElucHV0KCkgaXNGaXhlZENlbnRlcj86IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKlxuICAgICAqIOaYr+WQpuemgeeUqOaMh+aomeS6i+S7tlxuICAgICAqIEBkZWZhdWx0IGZhbHNlXG4gICAgICovXG4gICAgQElucHV0KCkgcG9pbnRlckV2ZW50c05vbmU/OiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKipcbiAgICAgKiDmlrnlkJHmqKHlvI9cbiAgICAgKiAtIGF1dG86IOiHquWLleaqoua4rOaWueWQkVxuICAgICAqIC0gcG9ydHJhaXQ6IOmOluWumuebtOWQkVxuICAgICAqIC0gbGFuZHNjYXBlOiDpjpblrprmqavlkJFcbiAgICAgKiAtIHBvcnRyYWl0TG9ja1JvdGF0aW9uOiDlvLfliLbnm7TlkJHvvIjmqavlkJHoqK3lgpnmmYLml4vovYkgLTkwwrDvvIlcbiAgICAgKiAtIGxhbmRzY2FwZUxvY2tSb3RhdGlvbjog5by35Yi25qmr5ZCR77yI55u05ZCR6Kit5YKZ5pmC5peL6L2JIDkwwrDvvIlcbiAgICAgKiBAZGVmYXVsdCAnYXV0bydcbiAgICAgKi9cbiAgICBASW5wdXQoKSBvcmllbnRhdGlvbj86IFRPcmllbnRhdGlvbk1vZGUgPSAnYXV0byc7XG5cbiAgICAvKipcbiAgICAgKiDlkJHlhaflsaTlrrnlmajovYnnmbznmoToh6roqILmqKPlvI9cbiAgICAgKi9cbiAgICBASW5wdXQoKSBmb3J3YXJkU3R5bGU/OiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuXG4gICAgLyoqXG4gICAgICog5pa55ZCR6K6K5YyW5LqL5Lu2XG4gICAgICogQGVtaXRzIHRydWU6IGxhbmRzY2FwZe+8iOapq+Wxj++8iVxuICAgICAqIEBlbWl0cyBmYWxzZTogcG9ydHJhaXTvvIjnm7TlsY/vvIlcbiAgICAgKi9cbiAgICBAT3V0cHV0KCkgb3JpZW50YXRpb25DaGFuZ2UgPSBuZXcgRXZlbnRFbWl0dGVyPGJvb2xlYW4+KCk7XG5cbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAvLyBQcml2YXRlIERlcGVuZGVuY2llc1xuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gICAgcHJpdmF0ZSBicmVha3BvaW50T2JzZXJ2ZXIgPSBpbmplY3QoQnJlYWtwb2ludE9ic2VydmVyKTtcbiAgICBwcml2YXRlIGJvdW5kYXJ5Q29udGV4dCA9IGluamVjdChCc0JvdW5kYXJ5Q29udGV4dFNlcnZpY2UpO1xuICAgIHByaXZhdGUgZWxlbWVudFJlZiA9IGluamVjdChFbGVtZW50UmVmKTtcbiAgICBwcml2YXRlIGRlc3Ryb3kkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIFB1YmxpYyBQcm9wZXJ0aWVzXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgICAvKipcbiAgICAgKiDlr6bpmpvoqK3lgpnmlrnlkJHvvIjln7rmlrzoppblj6PlsLrlr7jvvIlcbiAgICAgKiB0cnVlOiDnm7TlkJEgKGhlaWdodCA+IHdpZHRoKVxuICAgICAqIGZhbHNlOiDmqavlkJEgKHdpZHRoID49IGhlaWdodClcbiAgICAgKi9cbiAgICBhY3R1YWxJc1BvcnRyYWl0ID0gZmFsc2U7XG5cbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAvLyBHZXR0ZXJzXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgICAvKipcbiAgICAgKiDliKTmlrfnlbbliY3mmK/lkKbngrrmqavlkJHmqKHlvI9cbiAgICAgKiDmoLnmk5ogb3JpZW50YXRpb24g6Kit5a6a5ZKM5a+m6Zqb6Kit5YKZ5pa55ZCR5Yik5pa3XG4gICAgICovXG4gICAgZ2V0IGlzTGFuZHNjYXBlKCk6IGJvb2xlYW4ge1xuICAgICAgICAvLyBsYW5kc2NhcGVMb2NrUm90YXRpb246IOW8t+WItuapq+Wxj+aooeW8j1xuICAgICAgICBpZiAodGhpcy5vcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZUxvY2tSb3RhdGlvbicpIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIC8vIHBvcnRyYWl0TG9ja1JvdGF0aW9uOiDlvLfliLbnm7TlsY/mqKHlvI9cbiAgICAgICAgaWYgKHRoaXMub3JpZW50YXRpb24gPT09ICdwb3J0cmFpdExvY2tSb3RhdGlvbicpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICAvLyBsYW5kc2NhcGU6IOmOluWumuapq+WQkVxuICAgICAgICBpZiAodGhpcy5vcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZScpIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIC8vIHBvcnRyYWl0OiDpjpblrprnm7TlkJFcbiAgICAgICAgaWYgKHRoaXMub3JpZW50YXRpb24gPT09ICdwb3J0cmFpdCcpIHtcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgfVxuICAgICAgICAvLyBhdXRvOiDoh6rli5XmqqLmuKxcbiAgICAgICAgcmV0dXJuIHRoaXMuYm91bmRhcnlDb250ZXh0LmdldElzTGFuZHNjYXBlKCk7XG4gICAgfVxuXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLy8gTGlmZWN5Y2xlIEhvb2tzXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy51cGRhdGVBY3R1YWxPcmllbnRhdGlvbigpO1xuICAgICAgICB0aGlzLnNldHVwQnJlYWtwb2ludE9ic2VydmVyKCk7XG4gICAgICAgIHRoaXMuc2V0dXBSZXNpemVPYnNlcnZlcigpO1xuICAgIH1cblxuICAgIG5nQWZ0ZXJWaWV3SW5pdCgpOiB2b2lkIHtcbiAgICAgICAgLy8g5ZyoIHZpZXcg5Yid5aeL5YyW5b6M6KiI566X77yI56K65L+dIENTUyDlt7Llrozlhajmh4nnlKjvvIlcbiAgICAgICAgdGhpcy51cGRhdGVCb3VuZGFyeUNhbGN1bGF0aW9ucygpO1xuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICB0aGlzLmRlc3Ryb3kkLm5leHQoKTtcbiAgICAgICAgdGhpcy5kZXN0cm95JC5jb21wbGV0ZSgpO1xuICAgIH1cblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIE9ic2VydmVycyBTZXR1cCAtIOebo+iBveWZqOioreWumlxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gICAgLyoqXG4gICAgICog6Kit5a6a5pa55ZCR6K6K5YyW55uj5o6nXG4gICAgICpcbiAgICAgKiDlt6XkvZzljp/nkIbvvJpcbiAgICAgKiAtIGxvY2sg5qih5byP77yIcG9ydHJhaXQvbGFuZHNjYXBl77yJ77ya55u05o6l6Kit5a6a5Zu65a6a5pa55ZCR77yM5LiN55uj6IG96K6K5YyWXG4gICAgICogLSBsb2NrUm90YXRpb24g5qih5byP77ya6Kit5a6a5Zu65a6a5pa55ZCR77yM5L2G6ZyA55uj6IG95a+m6Zqb6Kit5YKZ5pa55ZCR5Lul6Ke455m85peL6L2JXG4gICAgICogLSBhdXRvIOaooeW8j++8muS9v+eUqCBCcmVha3BvaW50T2JzZXJ2ZXIg55uj6IG96Kit5YKZ5pa55ZCR6K6K5YyWXG4gICAgICovXG4gICAgcHJpdmF0ZSBzZXR1cEJyZWFrcG9pbnRPYnNlcnZlcigpIHtcbiAgICAgICAgLy8gbGFuZHNjYXBlIOaIliBsYW5kc2NhcGVMb2NrUm90YXRpb246IOmOluWumueCuuapq+WQkVxuICAgICAgICBpZiAodGhpcy5vcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZScgfHwgdGhpcy5vcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZUxvY2tSb3RhdGlvbicpIHtcbiAgICAgICAgICAgIHRoaXMuYm91bmRhcnlDb250ZXh0LnNldElzTGFuZHNjYXBlKHRydWUpO1xuICAgICAgICAgICAgdGhpcy5vcmllbnRhdGlvbkNoYW5nZS5lbWl0KHRydWUpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gcG9ydHJhaXQg5oiWIHBvcnRyYWl0TG9ja1JvdGF0aW9uOiDpjpblrprngrrnm7TlkJFcbiAgICAgICAgaWYgKHRoaXMub3JpZW50YXRpb24gPT09ICdwb3J0cmFpdCcgfHwgdGhpcy5vcmllbnRhdGlvbiA9PT0gJ3BvcnRyYWl0TG9ja1JvdGF0aW9uJykge1xuICAgICAgICAgICAgdGhpcy5ib3VuZGFyeUNvbnRleHQuc2V0SXNMYW5kc2NhcGUoZmFsc2UpO1xuICAgICAgICAgICAgdGhpcy5vcmllbnRhdGlvbkNoYW5nZS5lbWl0KGZhbHNlKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGF1dG8g5qih5byP77ya55uj6IG95a+m6Zqb55qE6Kit5YKZ5pa55ZCR77yI55So5pa8IG9yaWVudGF0aW9uQ2hhbmdlIOS6i+S7tu+8iVxuICAgICAgICB0aGlzLmJyZWFrcG9pbnRPYnNlcnZlclxuICAgICAgICAgICAgLm9ic2VydmUoJyhvcmllbnRhdGlvbjogbGFuZHNjYXBlKScpXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKHJlc3VsdCA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5ib3VuZGFyeUNvbnRleHQuc2V0SXNMYW5kc2NhcGUocmVzdWx0Lm1hdGNoZXMpO1xuICAgICAgICAgICAgICAgIHRoaXMub3JpZW50YXRpb25DaGFuZ2UuZW1pdChyZXN1bHQubWF0Y2hlcyk7XG4gICAgICAgICAgICAgICAgLy8g5pa55ZCR6K6K5YyW5pmC6YeN5paw6KiI566X6YKK55WMXG4gICAgICAgICAgICAgICAgdGhpcy51cGRhdGVCb3VuZGFyeUNhbGN1bGF0aW9ucygpO1xuICAgICAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICog6Kit5a6a6KaW56qX5aSn5bCP6K6K5YyW55uj5o6nXG4gICAgICpcbiAgICAgKiDlip/og73vvJpcbiAgICAgKiAtIOebo+iBvSB3aW5kb3cgcmVzaXplIOS6i+S7tlxuICAgICAqIC0g5pu05paw5a+m6Zqb6Kit5YKZ5pa55ZCRXG4gICAgICogLSDph43mlrDoqIjnrpfpgornlYzlsLrlr7hcbiAgICAgKi9cbiAgICBwcml2YXRlIHNldHVwUmVzaXplT2JzZXJ2ZXIoKSB7XG4gICAgICAgIGZyb21FdmVudCh3aW5kb3csICdyZXNpemUnKVxuICAgICAgICAgICAgLnBpcGUoXG4gICAgICAgICAgICAgICAgLy8gZGVib3VuY2VUaW1lKDE1MCksIC8vIOWPr+mBuO+8mumYsuaKluWEquWMluaAp+iDvVxuICAgICAgICAgICAgICAgIHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy51cGRhdGVBY3R1YWxPcmllbnRhdGlvbigpO1xuICAgICAgICAgICAgICAgIHRoaXMudXBkYXRlQm91bmRhcnlDYWxjdWxhdGlvbnMoKTtcbiAgICAgICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIENhbGN1bGF0aW9uIE1ldGhvZHMgLSDoqIjnrpfmlrnms5VcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIC8qKlxuICAgICAqIOabtOaWsOWvpumam+ioreWCmeaWueWQke+8iOWfuuaWvOimluWPo+WwuuWvuO+8iVxuICAgICAqXG4gICAgICog5bel5L2c5Y6f55CG77yaXG4gICAgICogLSDlj6rlnKggbG9ja1JvdGF0aW9uIOaooeW8j+S4i+mcgOimgeaqoua4rOWvpumam+aWueWQkVxuICAgICAqIC0g6YCa6YGO5q+U6LyDIHdpbmRvdy5pbm5lckhlaWdodCDlkowgd2luZG93LmlubmVyV2lkdGgg5Yik5pa3XG4gICAgICogLSDntZDmnpznlKjmlrzop7jnmbwgQ1NTIOaXi+i9ieiuiuaPm1xuICAgICAqL1xuICAgIHByaXZhdGUgdXBkYXRlQWN0dWFsT3JpZW50YXRpb24oKSB7XG4gICAgICAgIC8vIOWPquacieWcqCBsb2NrUm90YXRpb24g5qih5byP5LiL5omN6ZyA6KaB5qqi5ris5a+m6Zqb5pa55ZCRXG4gICAgICAgIGlmICh0aGlzLm9yaWVudGF0aW9uICE9PSAnbGFuZHNjYXBlTG9ja1JvdGF0aW9uJyAmJiB0aGlzLm9yaWVudGF0aW9uICE9PSAncG9ydHJhaXRMb2NrUm90YXRpb24nKSB7XG4gICAgICAgICAgICB0aGlzLmFjdHVhbElzUG9ydHJhaXQgPSBmYWxzZTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIOaqoua4rOWvpumam+imluWPo+aWueWQke+8mumrmOW6piA+IOWvrOW6piA9IOebtOWQkVxuICAgICAgICB0aGlzLmFjdHVhbElzUG9ydHJhaXQgPSB3aW5kb3cuaW5uZXJIZWlnaHQgPiB3aW5kb3cuaW5uZXJXaWR0aDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiDmm7TmlrDpgornlYzoqIjnrpfvvIjmlK/mj7QgQ2hyb21lIDc077yM5L2/55SoIEphdmFTY3JpcHQg5qih5pOsIG1pbiDlh73mlbjvvIlcbiAgICAgKlxuICAgICAqIOW3peS9nOWOn+eQhu+8mlxuICAgICAqIDEuIOiugOWPluWfuuekjiBDU1Mg6K6K5pW477yIdncxMDAsIHZoMTAwLCBkZXNpZ24td2lkdGgsIGRlc2lnbi1oZWlnaHTvvIlcbiAgICAgKiAyLiDomZXnkIYgbG9ja1JvdGF0aW9uIOaooeW8j+eahOimluWPo+WwuuWvuOS6pOaPm1xuICAgICAqIDMuIOioiOeul+mCiueVjOacgOWkp+WAvOWSjOWvpumam+mCiueVjOWwuuWvuFxuICAgICAqIDQuIOioiOeulyBweDJ2dyDovYnmj5vmr5TkvotcbiAgICAgKiA1LiDoqK3lrpogQ1NTIOiuiuaVuOS+m+aoo+W8j+S9v+eUqFxuICAgICAqXG4gICAgICog6Kit5a6a55qEIENTUyDorormlbjvvJpcbiAgICAgKiAtIC0tYm91bmRhcnktd2lkdGg6IOWvpumam+mCiueVjOWvrOW6pu+8iHB477yJXG4gICAgICogLSAtLWJvdW5kYXJ5LWhlaWdodDog5a+m6Zqb6YKK55WM6auY5bqm77yIcHjvvIlcbiAgICAgKiAtIC0tcHgydnctcmF0aW86IHB4IOi9iSB2dyDnmoTmr5Tkvovkv4LmlbhcbiAgICAgKi9cbiAgICBwcml2YXRlIHVwZGF0ZUJvdW5kYXJ5Q2FsY3VsYXRpb25zKCkge1xuICAgICAgICBjb25zdCBob3N0RWxlbWVudCA9IHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50IGFzIEhUTUxFbGVtZW50O1xuICAgICAgICBjb25zdCBjb21wdXRlZFN0eWxlID0gZ2V0Q29tcHV0ZWRTdHlsZShob3N0RWxlbWVudCk7XG5cbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyBTdGVwIDE6IOiugOWPluWfuuekjiBDU1Mg6K6K5pW4XG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgbGV0IHZ3MTAwID0gcGFyc2VGbG9hdChjb21wdXRlZFN0eWxlLmdldFByb3BlcnR5VmFsdWUoJy0tdncxMDAnKSkgfHwgd2luZG93LmlubmVyV2lkdGg7XG4gICAgICAgIGxldCB2aDEwMCA9IHBhcnNlRmxvYXQoY29tcHV0ZWRTdHlsZS5nZXRQcm9wZXJ0eVZhbHVlKCctLXZoMTAwJykpIHx8IHdpbmRvdy5pbm5lckhlaWdodDtcbiAgICAgICAgY29uc3QgZGVzaWduV2lkdGggPSBwYXJzZUZsb2F0KGNvbXB1dGVkU3R5bGUuZ2V0UHJvcGVydHlWYWx1ZSgnLS1kZXNpZ24td2lkdGgnKSk7XG4gICAgICAgIGNvbnN0IGRlc2lnbkhlaWdodCA9IHBhcnNlRmxvYXQoY29tcHV0ZWRTdHlsZS5nZXRQcm9wZXJ0eVZhbHVlKCctLWRlc2lnbi1oZWlnaHQnKSk7XG5cbiAgICAgICAgLy8g5qqi5p+l5Z+656SO6K6K5pW45piv5ZCm5rqW5YKZ5aW9XG4gICAgICAgIGlmIChpc05hTihkZXNpZ25XaWR0aCkpIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ1tCc0JvdW5kYXJ5XVtCUzAwMV0gQ1NTIHZhcmlhYmxlIC0tZGVzaWduLXdpZHRoIGlzIG5vdCBzZXQgb3IgaW52YWxpZCcpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmIChpc05hTihkZXNpZ25IZWlnaHQpKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdbQnNCb3VuZGFyeV1bQlMwMDJdIENTUyB2YXJpYWJsZSAtLWRlc2lnbi1oZWlnaHQgaXMgbm90IHNldCBvciBpbnZhbGlkJyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGRlc2lnbldpZHRoID09PSAwKSB7XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKCdbQnNCb3VuZGFyeV1bQlMwMDNdIENTUyB2YXJpYWJsZSAtLWRlc2lnbi13aWR0aCBjYW5ub3QgYmUgemVybycpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGlmIChkZXNpZ25IZWlnaHQgPT09IDApIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ1tCc0JvdW5kYXJ5XVtCUzAwNF0gQ1NTIHZhcmlhYmxlIC0tZGVzaWduLWhlaWdodCBjYW5ub3QgYmUgemVybycpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyBTdGVwIDI6IOiZleeQhiBsb2NrUm90YXRpb24g5qih5byP55qE6KaW5Y+j5bC65a+45Lqk5o+bXG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgLy8gbGFuZHNjYXBlTG9ja1JvdGF0aW9uICsg5a+m6Zqb55u05ZCR77ya5Lqk5o+b6KaW5Y+j5bC65a+4XG4gICAgICAgIC8vIOWOn+WboO+8muaXi+i9ieW+jO+8jOWFp+WuueeahOWvrOW6puWwjeaHieioreWCmeeahOmrmOW6pu+8jOWFp+WuueeahOmrmOW6puWwjeaHieioreWCmeeahOWvrOW6plxuICAgICAgICBpZiAodGhpcy5vcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZUxvY2tSb3RhdGlvbicgJiYgdGhpcy5hY3R1YWxJc1BvcnRyYWl0KSB7XG4gICAgICAgICAgICBbdncxMDAsIHZoMTAwXSA9IFt2aDEwMCwgdncxMDBdO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gcG9ydHJhaXRMb2NrUm90YXRpb24gKyDlr6bpmpvmqavlkJHvvJrkuqTmj5voppblj6PlsLrlr7hcbiAgICAgICAgaWYgKHRoaXMub3JpZW50YXRpb24gPT09ICdwb3J0cmFpdExvY2tSb3RhdGlvbicgJiYgIXRoaXMuYWN0dWFsSXNQb3J0cmFpdCkge1xuICAgICAgICAgICAgW3Z3MTAwLCB2aDEwMF0gPSBbdmgxMDAsIHZ3MTAwXTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgLy8gU3RlcCAzOiDoqIjnrpfmr5TkvovlkozpgornlYzmnIDlpKflgLxcbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICBjb25zdCBsYW5kc2NhcGVSYXRpbyA9IGRlc2lnbldpZHRoIC8gZGVzaWduSGVpZ2h0O1xuICAgICAgICBjb25zdCBwb3J0cmFpdFJhdGlvID0gZGVzaWduSGVpZ2h0IC8gZGVzaWduV2lkdGg7XG5cbiAgICAgICAgLy8g5qC55pOa5pa55ZCR5qih5byP6KiI566X6YKK55WM5pyA5aSn5YC8XG4gICAgICAgIGNvbnN0IGlzTGFuZHNjYXBlID0gdGhpcy5pc0xhbmRzY2FwZTtcbiAgICAgICAgY29uc3QgYm91bmRhcnlNYXhXaWR0aCA9IGlzTGFuZHNjYXBlXG4gICAgICAgICAgICA/IHZoMTAwICogcG9ydHJhaXRSYXRpbyAgLy8g5qmr5ZCR77ya6auY5bqmICog6auY5a+s5q+UXG4gICAgICAgICAgICA6IHZoMTAwICogbGFuZHNjYXBlUmF0aW87IC8vIOebtOWQke+8mumrmOW6piAqIOWvrOmrmOavlFxuICAgICAgICBjb25zdCBib3VuZGFyeU1heEhlaWdodCA9IGlzTGFuZHNjYXBlXG4gICAgICAgICAgICA/IHZ3MTAwICogbGFuZHNjYXBlUmF0aW8gIC8vIOapq+WQke+8muWvrOW6piAqIOWvrOmrmOavlFxuICAgICAgICAgICAgOiB2dzEwMCAqIHBvcnRyYWl0UmF0aW87ICAvLyDnm7TlkJHvvJrlr6zluqYgKiDpq5jlr6zmr5RcblxuICAgICAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgICAgIC8vIFN0ZXAgNDog6KiI566X5a+m6Zqb6YKK55WM5bC65a+477yI5L2/55SoIE1hdGgubWluIOaooeaTrCBDU1MgbWluKCnvvIlcbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyBDaHJvbWUgNzkg5omN5pSv5o+0IENTUyBtaW4oKSDlh73mlbjvvIzmiYDku6XlnKggSmF2YVNjcmlwdCDkuK3oqIjnrpdcbiAgICAgICAgY29uc3QgYm91bmRhcnlXaWR0aCA9IE1hdGgubWluKHZ3MTAwLCBib3VuZGFyeU1heFdpZHRoKTtcbiAgICAgICAgY29uc3QgYm91bmRhcnlIZWlnaHQgPSBNYXRoLm1pbih2aDEwMCwgYm91bmRhcnlNYXhIZWlnaHQpO1xuXG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgLy8gU3RlcCA1OiDoqIjnrpcgcHgydncg6L2J5o+b5q+U5L6LXG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgLy8g5qmr5ZCR5qih5byP5L2/55SoIGRlc2lnbi1oZWlnaHQg5L2c54K65Z+65rqW77yM55u05ZCR5qih5byP5L2/55SoIGRlc2lnbi13aWR0aCDkvZzngrrln7rmupZcbiAgICAgICAgY29uc3QgZGVzaWduQmFzZVdpZHRoID0gaXNMYW5kc2NhcGUgPyBkZXNpZ25IZWlnaHQgOiBkZXNpZ25XaWR0aDtcbiAgICAgICAgY29uc3QgcHgydndSYXRpbyA9IGJvdW5kYXJ5V2lkdGggLyBkZXNpZ25CYXNlV2lkdGg7XG5cbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyBTdGVwIDY6IOioreWumiBDU1Mg6K6K5pW4XG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgaG9zdEVsZW1lbnQuc3R5bGUuc2V0UHJvcGVydHkoJy0tYm91bmRhcnktd2lkdGgnLCBgJHtib3VuZGFyeVdpZHRofXB4YCk7XG4gICAgICAgIGhvc3RFbGVtZW50LnN0eWxlLnNldFByb3BlcnR5KCctLWJvdW5kYXJ5LWhlaWdodCcsIGAke2JvdW5kYXJ5SGVpZ2h0fXB4YCk7XG4gICAgICAgIGhvc3RFbGVtZW50LnN0eWxlLnNldFByb3BlcnR5KCctLXB4MnZ3LXJhdGlvJywgYCR7cHgydndSYXRpb31gKTtcbiAgICB9XG59XG4iLCI8ZGl2XG4gIGNsYXNzPVwiYm91bmRhcnktd3JhcHBlclwiXG4gIFtjbGFzcy5maXhlZC1jZW50ZXJdPVwiaXNGaXhlZENlbnRlclwiXG4gIFtjbGFzcy5wb2ludGVyRXZlbnRzTm9uZV09XCJwb2ludGVyRXZlbnRzTm9uZVwiXG4gIFtzdHlsZV09XCJmb3J3YXJkU3R5bGVcIlxuPlxuICAgIDxuZy1jb250ZW50PjwvbmctY29udGVudD5cbjwvZGl2PlxuIl19
|
|
302
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnMtYm91bmRhcnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9wcm9qZWN0cy9iaXRzdGFjay1uZy1ib3VuZGFyeS9zcmMvbGliL2JzLWJvdW5kYXJ5LnRzIiwiLi4vLi4vLi4vLi4vcHJvamVjdHMvYml0c3RhY2stbmctYm91bmRhcnkvc3JjL2xpYi90ZW1wbGF0ZS5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBQyxrQkFBa0IsRUFBQyxNQUFNLHFCQUFxQixDQUFDO0FBQ3ZELE9BQU8sRUFFSCxTQUFTLEVBQ1QsVUFBVSxFQUNWLFlBQVksRUFDWixNQUFNLEVBQ04sS0FBSyxFQUVMLE1BQU0sR0FDVCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQWUsU0FBUyxFQUFFLE9BQU8sRUFBRSxTQUFTLEVBQUMsTUFBTSxNQUFNLENBQUM7QUFFakUsT0FBTyxFQUFDLHdCQUF3QixFQUFDLE1BQU0sK0JBQStCLENBQUM7O0FBR3ZFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBeUJHO0FBaUJILE1BQU0sT0FBTyxVQUFVO0lBaEJ2QjtRQWtCSSwrREFBK0Q7UUFDL0QsbUJBQW1CO1FBQ25CLCtEQUErRDtRQUUvRDs7O1dBR0c7UUFDTSxrQkFBYSxHQUFhLEtBQUssQ0FBQztRQUV6Qzs7O1dBR0c7UUFDTSxzQkFBaUIsR0FBYSxLQUFLLENBQUM7UUFFN0M7Ozs7Ozs7O1dBUUc7UUFDTSxnQkFBVyxHQUFzQixNQUFNLENBQUM7UUFtQmpEOzs7O1dBSUc7UUFDTyxzQkFBaUIsR0FBRyxJQUFJLFlBQVksRUFBVyxDQUFDO1FBRTFELCtEQUErRDtRQUMvRCx1QkFBdUI7UUFDdkIsK0RBQStEO1FBRXZELHVCQUFrQixHQUFHLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1FBQ2hELG9CQUFlLEdBQUcsTUFBTSxDQUFDLHdCQUF3QixDQUFDLENBQUM7UUFDbkQsZUFBVSxHQUFHLE1BQU0sQ0FBQyxVQUFV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QkFNRyxpQkFBaUI7c0JBQXpCLEtBQUs7Z0JBV0csV0FBVztzQkFBbkIsS0FBSztnQkFLRyxZQUFZO3NCQUFwQixLQUFLO2dCQU1HLFdBQVc7c0JBQW5CLEtBQUs7Z0JBTUcsWUFBWTtzQkFBcEIsS0FBSztnQkFPSSxpQkFBaUI7c0JBQTFCLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge0JyZWFrcG9pbnRPYnNlcnZlcn0gZnJvbSAnQGFuZ3VsYXIvY2RrL2xheW91dCc7XG5pbXBvcnQge1xuICAgIEFmdGVyVmlld0luaXQsXG4gICAgQ29tcG9uZW50LFxuICAgIEVsZW1lbnRSZWYsXG4gICAgRXZlbnRFbWl0dGVyLFxuICAgIGluamVjdCxcbiAgICBJbnB1dCwgT25EZXN0cm95LFxuICAgIE9uSW5pdCxcbiAgICBPdXRwdXQsXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtkZWJvdW5jZVRpbWUsIGZyb21FdmVudCwgU3ViamVjdCwgdGFrZVVudGlsfSBmcm9tICdyeGpzJztcblxuaW1wb3J0IHtCc0JvdW5kYXJ5Q29udGV4dFNlcnZpY2V9IGZyb20gJy4vYnMtYm91bmRhcnktY29udGV4dC5zZXJ2aWNlJztcbmltcG9ydCB7VE9yaWVudGF0aW9uTW9kZX0gZnJvbSAnLi9tb2RlbCc7XG5cbi8qKlxuICogPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gKiBCc0JvdW5kYXJ5IENvbXBvbmVudCAtIEFXRCDoh6rpganmh4npgornlYzlrrnlmahcbiAqID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICpcbiAqIOWKn+iDveiqquaYju+8mlxuICogMS4g5o+Q5L6b6Z+/5oeJ5byP6YKK55WM5a655Zmo77yM6Ieq5YuV6YGp5oeJ5LiN5ZCM6Kit5YKZ5bC65a+4XG4gKiAyLiDmlK/mj7QgNSDnqK7mlrnlkJHmqKHlvI/vvJphdXRvLCBwb3J0cmFpdCwgbGFuZHNjYXBlLCBwb3J0cmFpdExvY2tSb3RhdGlvbiwgbGFuZHNjYXBlTG9ja1JvdGF0aW9uXG4gKiAzLiDkvb/nlKggQ1NTIOiuiuaVuOWvpuePvuWLleaFi+ioiOeul++8jOaUr+aPtCBweDJ2dygpIOWHveaVuFxuICogNC4g55u45a65IENocm9tZSA3NCvvvIzkvb/nlKggSmF2YVNjcmlwdCDoqIjnrpfpgornlYzlsLrlr7hcbiAqXG4gKiDkvb/nlKjnr4TkvovvvJpcbiAqIGBgYGh0bWxcbiAqIDxicy1ib3VuZGFyeSBbb3JpZW50YXRpb25dPVwiJ2F1dG8nXCIgW2lzRml4ZWRDZW50ZXJdPVwidHJ1ZVwiPlxuICogICA8ZGl2IGNsYXNzPVwiY29udGVudFwiPi4uLjwvZGl2PlxuICogPC9icy1ib3VuZGFyeT5cbiAqIGBgYFxuICpcbiAqIENTUyDorormlbjoqK3lrprvvJpcbiAqIGBgYHNjc3NcbiAqIGJzLWJvdW5kYXJ5IHtcbiAqICAgLS1kZXNpZ24td2lkdGg6IDM3NTsgICAvLyDoqK3oqIjnqL/lr6zluqZcbiAqICAgLS1kZXNpZ24taGVpZ2h0OiA2Njc7ICAvLyDoqK3oqIjnqL/pq5jluqZcbiAqIH1cbiAqIGBgYFxuICovXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ2JzLWJvdW5kYXJ5JyxcbiAgICB0ZW1wbGF0ZVVybDogJy4vdGVtcGxhdGUuaHRtbCcsXG4gICAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgICBzdHlsZVVybHM6IFsnLi9zdHlsZXMuc2NzcyddLFxuICAgIHByb3ZpZGVyczogW0JzQm91bmRhcnlDb250ZXh0U2VydmljZV0sXG4gICAgaG9zdDoge1xuICAgICAgICAnW2NsYXNzLmF1dG9dJzogJ29yaWVudGF0aW9uID09PSBcImF1dG9cIicsXG4gICAgICAgICdbY2xhc3MubGFuZHNjYXBlXSc6ICdpc0xhbmRzY2FwZSAmJiBvcmllbnRhdGlvbiA9PT0gXCJsYW5kc2NhcGVcIicsXG4gICAgICAgICdbY2xhc3MucG9ydHJhaXRdJzogJyFpc0xhbmRzY2FwZSAmJiBvcmllbnRhdGlvbiA9PT0gXCJwb3J0cmFpdFwiJyxcbiAgICAgICAgJ1tjbGFzcy5sYW5kc2NhcGUtbG9jay1yb3RhdGlvbl0nOiAnb3JpZW50YXRpb24gPT09IFwibGFuZHNjYXBlTG9ja1JvdGF0aW9uXCInLFxuICAgICAgICAnW2NsYXNzLnBvcnRyYWl0LWxvY2stcm90YXRpb25dJzogJ29yaWVudGF0aW9uID09PSBcInBvcnRyYWl0TG9ja1JvdGF0aW9uXCInLFxuICAgICAgICAnW2NsYXNzLmFjdHVhbC1wb3J0cmFpdF0nOiAnYWN0dWFsSXNQb3J0cmFpdCcsXG4gICAgICAgICdbY2xhc3MuYWN0dWFsLWxhbmRzY2FwZV0nOiAnIWFjdHVhbElzUG9ydHJhaXQnXG4gICAgfVxufSlcbmV4cG9ydCBjbGFzcyBCc0JvdW5kYXJ5IGltcGxlbWVudHMgT25Jbml0LCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3kge1xuXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLy8gSW5wdXRzICYgT3V0cHV0c1xuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gICAgLyoqXG4gICAgICog5piv5ZCm5bCH5YWn5a655Zu65a6a5Zyo6KaW56qX5Lit5aSuXG4gICAgICogQGRlZmF1bHQgZmFsc2VcbiAgICAgKi9cbiAgICBASW5wdXQoKSBpc0ZpeGVkQ2VudGVyPzogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgLyoqXG4gICAgICog5piv5ZCm56aB55So5oyH5qiZ5LqL5Lu2XG4gICAgICogQGRlZmF1bHQgZmFsc2VcbiAgICAgKi9cbiAgICBASW5wdXQoKSBwb2ludGVyRXZlbnRzTm9uZT86IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKlxuICAgICAqIOaWueWQkeaooeW8j1xuICAgICAqIC0gYXV0bzog6Ieq5YuV5qqi5ris5pa55ZCRXG4gICAgICogLSBwb3J0cmFpdDog6Y6W5a6a55u05ZCRXG4gICAgICogLSBsYW5kc2NhcGU6IOmOluWumuapq+WQkVxuICAgICAqIC0gcG9ydHJhaXRMb2NrUm90YXRpb246IOW8t+WItuebtOWQke+8iOapq+WQkeioreWCmeaZguaXi+i9iSAtOTDCsO+8iVxuICAgICAqIC0gbGFuZHNjYXBlTG9ja1JvdGF0aW9uOiDlvLfliLbmqavlkJHvvIjnm7TlkJHoqK3lgpnmmYLml4vovYkgOTDCsO+8iVxuICAgICAqIEBkZWZhdWx0ICdhdXRvJ1xuICAgICAqL1xuICAgIEBJbnB1dCgpIG9yaWVudGF0aW9uPzogVE9yaWVudGF0aW9uTW9kZSA9ICdhdXRvJztcblxuICAgIC8qKlxuICAgICAqIOWQkeWFp+WxpOWuueWZqOi9ieeZvOeahOiHquioguaoo+W8j1xuICAgICAqL1xuICAgIEBJbnB1dCgpIGZvcndhcmRTdHlsZT86IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG5cbiAgICAvKipcbiAgICAgKiDoqK3oqIjnqL/lr6zluqbvvIjllq7kvY3vvJpweO+8iVxuICAgICAqIOW/heWhq+WPg+aVuFxuICAgICAqL1xuICAgIEBJbnB1dCgpIGRlc2lnbldpZHRoITogbnVtYmVyO1xuXG4gICAgLyoqXG4gICAgICog6Kit6KiI56i/6auY5bqm77yI5Zau5L2N77yacHjvvIlcbiAgICAgKiDlv4Xloavlj4PmlbhcbiAgICAgKi9cbiAgICBASW5wdXQoKSBkZXNpZ25IZWlnaHQhOiBudW1iZXI7XG5cbiAgICAvKipcbiAgICAgKiDmlrnlkJHororljJbkuovku7ZcbiAgICAgKiBAZW1pdHMgdHJ1ZTogbGFuZHNjYXBl77yI5qmr5bGP77yJXG4gICAgICogQGVtaXRzIGZhbHNlOiBwb3J0cmFpdO+8iOebtOWxj++8iVxuICAgICAqL1xuICAgIEBPdXRwdXQoKSBvcmllbnRhdGlvbkNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8Ym9vbGVhbj4oKTtcblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIFByaXZhdGUgRGVwZW5kZW5jaWVzXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiAgICBwcml2YXRlIGJyZWFrcG9pbnRPYnNlcnZlciA9IGluamVjdChCcmVha3BvaW50T2JzZXJ2ZXIpO1xuICAgIHByaXZhdGUgYm91bmRhcnlDb250ZXh0ID0gaW5qZWN0KEJzQm91bmRhcnlDb250ZXh0U2VydmljZSk7XG4gICAgcHJpdmF0ZSBlbGVtZW50UmVmID0gaW5qZWN0KEVsZW1lbnRSZWYpO1xuICAgIHByaXZhdGUgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgLy8gUHVibGljIFByb3BlcnRpZXNcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIC8qKlxuICAgICAqIOWvpumam+ioreWCmeaWueWQke+8iOWfuuaWvOimluWPo+WwuuWvuO+8iVxuICAgICAqIHRydWU6IOebtOWQkSAoaGVpZ2h0ID4gd2lkdGgpXG4gICAgICogZmFsc2U6IOapq+WQkSAod2lkdGggPj0gaGVpZ2h0KVxuICAgICAqL1xuICAgIGFjdHVhbElzUG9ydHJhaXQgPSBmYWxzZTtcblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIEdldHRlcnNcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIC8qKlxuICAgICAqIOWIpOaWt+eVtuWJjeaYr+WQpueCuuapq+WQkeaooeW8j1xuICAgICAqIOagueaTmiBvcmllbnRhdGlvbiDoqK3lrprlkozlr6bpmpvoqK3lgpnmlrnlkJHliKTmlrdcbiAgICAgKi9cbiAgICBnZXQgaXNMYW5kc2NhcGUoKTogYm9vbGVhbiB7XG4gICAgICAgIC8vIGxhbmRzY2FwZUxvY2tSb3RhdGlvbjog5by35Yi25qmr5bGP5qih5byPXG4gICAgICAgIGlmICh0aGlzLm9yaWVudGF0aW9uID09PSAnbGFuZHNjYXBlTG9ja1JvdGF0aW9uJykge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gcG9ydHJhaXRMb2NrUm90YXRpb246IOW8t+WItuebtOWxj+aooeW8j1xuICAgICAgICBpZiAodGhpcy5vcmllbnRhdGlvbiA9PT0gJ3BvcnRyYWl0TG9ja1JvdGF0aW9uJykge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIC8vIGxhbmRzY2FwZTog6Y6W5a6a5qmr5ZCRXG4gICAgICAgIGlmICh0aGlzLm9yaWVudGF0aW9uID09PSAnbGFuZHNjYXBlJykge1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgLy8gcG9ydHJhaXQ6IOmOluWumuebtOWQkVxuICAgICAgICBpZiAodGhpcy5vcmllbnRhdGlvbiA9PT0gJ3BvcnRyYWl0Jykge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIC8vIGF1dG86IOiHquWLleaqoua4rFxuICAgICAgICByZXR1cm4gdGhpcy5ib3VuZGFyeUNvbnRleHQuZ2V0SXNMYW5kc2NhcGUoKTtcbiAgICB9XG5cbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAvLyBMaWZlY3ljbGUgSG9va3NcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIG5nT25Jbml0KCk6IHZvaWQge1xuICAgICAgICB0aGlzLnVwZGF0ZUFjdHVhbE9yaWVudGF0aW9uKCk7XG4gICAgICAgIHRoaXMuc2V0dXBCcmVha3BvaW50T2JzZXJ2ZXIoKTtcbiAgICAgICAgdGhpcy5zZXR1cFJlc2l6ZU9ic2VydmVyKCk7XG4gICAgfVxuXG4gICAgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuICAgICAgICAvLyDkvb/nlKggQElucHV0IOWPg+aVuO+8jOWPr+S7peeri+WNs+ioiOeul++8iOeEoemcgOetieW+hSBDU1PvvIlcbiAgICAgICAgdGhpcy51cGRhdGVCb3VuZGFyeUNhbGN1bGF0aW9ucygpO1xuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICB0aGlzLmRlc3Ryb3kkLm5leHQoKTtcbiAgICAgICAgdGhpcy5kZXN0cm95JC5jb21wbGV0ZSgpO1xuICAgIH1cblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIE9ic2VydmVycyBTZXR1cCAtIOebo+iBveWZqOioreWumlxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gICAgLyoqXG4gICAgICog6Kit5a6a5pa55ZCR6K6K5YyW55uj5o6nXG4gICAgICpcbiAgICAgKiDlt6XkvZzljp/nkIbvvJpcbiAgICAgKiAtIGxvY2sg5qih5byP77yIcG9ydHJhaXQvbGFuZHNjYXBl77yJ77ya55u05o6l6Kit5a6a5Zu65a6a5pa55ZCR77yM5LiN55uj6IG96K6K5YyWXG4gICAgICogLSBsb2NrUm90YXRpb24g5qih5byP77ya6Kit5a6a5Zu65a6a5pa55ZCR77yM5L2G6ZyA55uj6IG95a+m6Zqb6Kit5YKZ5pa55ZCR5Lul6Ke455m85peL6L2JXG4gICAgICogLSBhdXRvIOaooeW8j++8muS9v+eUqCBCcmVha3BvaW50T2JzZXJ2ZXIg55uj6IG96Kit5YKZ5pa55ZCR6K6K5YyWXG4gICAgICovXG4gICAgcHJpdmF0ZSBzZXR1cEJyZWFrcG9pbnRPYnNlcnZlcigpIHtcbiAgICAgICAgLy8gbGFuZHNjYXBlIOaIliBsYW5kc2NhcGVMb2NrUm90YXRpb246IOmOluWumueCuuapq+WQkVxuICAgICAgICBpZiAodGhpcy5vcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZScgfHwgdGhpcy5vcmllbnRhdGlvbiA9PT0gJ2xhbmRzY2FwZUxvY2tSb3RhdGlvbicpIHtcbiAgICAgICAgICAgIHRoaXMuYm91bmRhcnlDb250ZXh0LnNldElzTGFuZHNjYXBlKHRydWUpO1xuICAgICAgICAgICAgdGhpcy5vcmllbnRhdGlvbkNoYW5nZS5lbWl0KHRydWUpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gcG9ydHJhaXQg5oiWIHBvcnRyYWl0TG9ja1JvdGF0aW9uOiDpjpblrprngrrnm7TlkJFcbiAgICAgICAgaWYgKHRoaXMub3JpZW50YXRpb24gPT09ICdwb3J0cmFpdCcgfHwgdGhpcy5vcmllbnRhdGlvbiA9PT0gJ3BvcnRyYWl0TG9ja1JvdGF0aW9uJykge1xuICAgICAgICAgICAgdGhpcy5ib3VuZGFyeUNvbnRleHQuc2V0SXNMYW5kc2NhcGUoZmFsc2UpO1xuICAgICAgICAgICAgdGhpcy5vcmllbnRhdGlvbkNoYW5nZS5lbWl0KGZhbHNlKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGF1dG8g5qih5byP77ya55uj6IG95a+m6Zqb55qE6Kit5YKZ5pa55ZCR77yI55So5pa8IG9yaWVudGF0aW9uQ2hhbmdlIOS6i+S7tu+8iVxuICAgICAgICB0aGlzLmJyZWFrcG9pbnRPYnNlcnZlclxuICAgICAgICAgICAgLm9ic2VydmUoJyhvcmllbnRhdGlvbjogbGFuZHNjYXBlKScpXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKHJlc3VsdCA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5ib3VuZGFyeUNvbnRleHQuc2V0SXNMYW5kc2NhcGUocmVzdWx0Lm1hdGNoZXMpO1xuICAgICAgICAgICAgICAgIHRoaXMub3JpZW50YXRpb25DaGFuZ2UuZW1pdChyZXN1bHQubWF0Y2hlcyk7XG4gICAgICAgICAgICAgICAgLy8g5pa55ZCR6K6K5YyW5pmC6YeN5paw6KiI566X6YKK55WMXG4gICAgICAgICAgICAgICAgdGhpcy51cGRhdGVCb3VuZGFyeUNhbGN1bGF0aW9ucygpO1xuICAgICAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICog6Kit5a6a6KaW56qX5aSn5bCP6K6K5YyW55uj5o6nXG4gICAgICpcbiAgICAgKiDlip/og73vvJpcbiAgICAgKiAtIOebo+iBvSB3aW5kb3cgcmVzaXplIOS6i+S7tlxuICAgICAqIC0g5pu05paw5a+m6Zqb6Kit5YKZ5pa55ZCRXG4gICAgICogLSDph43mlrDoqIjnrpfpgornlYzlsLrlr7hcbiAgICAgKi9cbiAgICBwcml2YXRlIHNldHVwUmVzaXplT2JzZXJ2ZXIoKSB7XG4gICAgICAgIGZyb21FdmVudCh3aW5kb3csICdyZXNpemUnKVxuICAgICAgICAgICAgLnBpcGUoXG4gICAgICAgICAgICAgICAgLy8gZGVib3VuY2VUaW1lKDE1MCksIC8vIOWPr+mBuO+8mumYsuaKluWEquWMluaAp+iDvVxuICAgICAgICAgICAgICAgIHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy51cGRhdGVBY3R1YWxPcmllbnRhdGlvbigpO1xuICAgICAgICAgICAgICAgIHRoaXMudXBkYXRlQm91bmRhcnlDYWxjdWxhdGlvbnMoKTtcbiAgICAgICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgIC8vIENhbGN1bGF0aW9uIE1ldGhvZHMgLSDoqIjnrpfmlrnms5VcbiAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuICAgIC8qKlxuICAgICAqIOabtOaWsOWvpumam+ioreWCmeaWueWQke+8iOWfuuaWvOimluWPo+WwuuWvuO+8iVxuICAgICAqXG4gICAgICog5bel5L2c5Y6f55CG77yaXG4gICAgICogLSDlj6rlnKggbG9ja1JvdGF0aW9uIOaooeW8j+S4i+mcgOimgeaqoua4rOWvpumam+aWueWQkVxuICAgICAqIC0g6YCa6YGO5q+U6LyDIHdpbmRvdy5pbm5lckhlaWdodCDlkowgd2luZG93LmlubmVyV2lkdGgg5Yik5pa3XG4gICAgICogLSDntZDmnpznlKjmlrzop7jnmbwgQ1NTIOaXi+i9ieiuiuaPm1xuICAgICAqL1xuICAgIHByaXZhdGUgdXBkYXRlQWN0dWFsT3JpZW50YXRpb24oKSB7XG4gICAgICAgIC8vIOWPquacieWcqCBsb2NrUm90YXRpb24g5qih5byP5LiL5omN6ZyA6KaB5qqi5ris5a+m6Zqb5pa55ZCRXG4gICAgICAgIGlmICh0aGlzLm9yaWVudGF0aW9uICE9PSAnbGFuZHNjYXBlTG9ja1JvdGF0aW9uJyAmJiB0aGlzLm9yaWVudGF0aW9uICE9PSAncG9ydHJhaXRMb2NrUm90YXRpb24nKSB7XG4gICAgICAgICAgICB0aGlzLmFjdHVhbElzUG9ydHJhaXQgPSBmYWxzZTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIOaqoua4rOWvpumam+imluWPo+aWueWQke+8mumrmOW6piA+IOWvrOW6piA9IOebtOWQkVxuICAgICAgICB0aGlzLmFjdHVhbElzUG9ydHJhaXQgPSB3aW5kb3cuaW5uZXJIZWlnaHQgPiB3aW5kb3cuaW5uZXJXaWR0aDtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiDmm7TmlrDpgornlYzoqIjnrpfvvIjmlK/mj7QgQ2hyb21lIDc077yM5L2/55SoIEphdmFTY3JpcHQg5qih5pOsIG1pbiDlh73mlbjvvIlcbiAgICAgKlxuICAgICAqIOW3peS9nOWOn+eQhu+8mlxuICAgICAqIDEuIOW+niBASW5wdXQg5Y+D5pW46K6A5Y+W6Kit6KiI5bC65a+4XG4gICAgICogMi4g6K6A5Y+W6KaW5Y+j5bC65a+477yIdncxMDAsIHZoMTAw77yJXG4gICAgICogMy4g6JmV55CGIGxvY2tSb3RhdGlvbiDmqKHlvI/nmoToppblj6PlsLrlr7jkuqTmj5tcbiAgICAgKiA0LiDoqIjnrpfpgornlYzmnIDlpKflgLzlkozlr6bpmpvpgornlYzlsLrlr7hcbiAgICAgKiA1LiDoqIjnrpcgcHgydncg6L2J5o+b5q+U5L6LXG4gICAgICogNi4g6Kit5a6aIENTUyDorormlbjkvpvmqKPlvI/kvb/nlKhcbiAgICAgKlxuICAgICAqIOioreWumueahCBDU1Mg6K6K5pW477yaXG4gICAgICogLSAtLWRlc2lnbi13aWR0aDog6Kit6KiI56i/5a+s5bqmXG4gICAgICogLSAtLWRlc2lnbi1oZWlnaHQ6IOioreioiOeov+mrmOW6plxuICAgICAqIC0gLS1ib3VuZGFyeS13aWR0aDog5a+m6Zqb6YKK55WM5a+s5bqm77yIcHjvvIlcbiAgICAgKiAtIC0tYm91bmRhcnktaGVpZ2h0OiDlr6bpmpvpgornlYzpq5jluqbvvIhweO+8iVxuICAgICAqIC0gLS1weDJ2dy1yYXRpbzogcHgg6L2JIHZ3IOeahOavlOS+i+S/guaVuFxuICAgICAqL1xuICAgIHByaXZhdGUgdXBkYXRlQm91bmRhcnlDYWxjdWxhdGlvbnMoKSB7XG4gICAgICAgIGNvbnN0IGhvc3RFbGVtZW50ID0gdGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQgYXMgSFRNTEVsZW1lbnQ7XG5cbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyBTdGVwIDE6IOiugOWPluioreioiOWwuuWvuOS4puioreWumiBDU1Mg6K6K5pW4XG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgY29uc3QgZGVzaWduV2lkdGggPSB0aGlzLmRlc2lnbldpZHRoO1xuICAgICAgICBjb25zdCBkZXNpZ25IZWlnaHQgPSB0aGlzLmRlc2lnbkhlaWdodDtcblxuICAgICAgICAvLyDoqK3lrpogQ1NTIOiuiuaVuO+8jOS+m+aoo+W8j+S9v+eUqO+8iOS+i+WmgiBweDJ2dygpIOWHveaVuO+8iVxuICAgICAgICBob3N0RWxlbWVudC5zdHlsZS5zZXRQcm9wZXJ0eSgnLS1kZXNpZ24td2lkdGgnLCBgJHtkZXNpZ25XaWR0aH1gKTtcbiAgICAgICAgaG9zdEVsZW1lbnQuc3R5bGUuc2V0UHJvcGVydHkoJy0tZGVzaWduLWhlaWdodCcsIGAke2Rlc2lnbkhlaWdodH1gKTtcblxuICAgICAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgICAgIC8vIFN0ZXAgMjog6K6A5Y+W6KaW5Y+j5bC65a+477yI55u05o6l5L2/55SoIHdpbmRvdy5pbm5lciog56K65L+d5LiA6Ie05oCn77yJXG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgbGV0IHZ3MTAwID0gd2luZG93LmlubmVyV2lkdGg7XG4gICAgICAgIGxldCB2aDEwMCA9IHdpbmRvdy5pbm5lckhlaWdodDtcblxuICAgICAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgICAgIC8vIFN0ZXAgMjog6JmV55CGIGxvY2tSb3RhdGlvbiDmqKHlvI/nmoToppblj6PlsLrlr7jkuqTmj5tcbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyBsYW5kc2NhcGVMb2NrUm90YXRpb24gKyDlr6bpmpvnm7TlkJHvvJrkuqTmj5voppblj6PlsLrlr7hcbiAgICAgICAgLy8g5Y6f5Zug77ya5peL6L2J5b6M77yM5YWn5a6555qE5a+s5bqm5bCN5oeJ6Kit5YKZ55qE6auY5bqm77yM5YWn5a6555qE6auY5bqm5bCN5oeJ6Kit5YKZ55qE5a+s5bqmXG4gICAgICAgIGlmICh0aGlzLm9yaWVudGF0aW9uID09PSAnbGFuZHNjYXBlTG9ja1JvdGF0aW9uJyAmJiB0aGlzLmFjdHVhbElzUG9ydHJhaXQpIHtcbiAgICAgICAgICAgIFt2dzEwMCwgdmgxMDBdID0gW3ZoMTAwLCB2dzEwMF07XG4gICAgICAgIH1cblxuICAgICAgICAvLyBwb3J0cmFpdExvY2tSb3RhdGlvbiArIOWvpumam+apq+WQke+8muS6pOaPm+imluWPo+WwuuWvuFxuICAgICAgICBpZiAodGhpcy5vcmllbnRhdGlvbiA9PT0gJ3BvcnRyYWl0TG9ja1JvdGF0aW9uJyAmJiAhdGhpcy5hY3R1YWxJc1BvcnRyYWl0KSB7XG4gICAgICAgICAgICBbdncxMDAsIHZoMTAwXSA9IFt2aDEwMCwgdncxMDBdO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyBTdGVwIDM6IOioiOeul+avlOS+i+WSjOmCiueVjOacgOWkp+WAvFxuICAgICAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgICAgIGNvbnN0IGxhbmRzY2FwZVJhdGlvID0gZGVzaWduV2lkdGggLyBkZXNpZ25IZWlnaHQ7XG4gICAgICAgIGNvbnN0IHBvcnRyYWl0UmF0aW8gPSBkZXNpZ25IZWlnaHQgLyBkZXNpZ25XaWR0aDtcblxuICAgICAgICAvLyDmoLnmk5rmlrnlkJHmqKHlvI/oqIjnrpfpgornlYzmnIDlpKflgLxcbiAgICAgICAgY29uc3QgaXNMYW5kc2NhcGUgPSB0aGlzLmlzTGFuZHNjYXBlO1xuICAgICAgICBjb25zdCBib3VuZGFyeU1heFdpZHRoID0gaXNMYW5kc2NhcGVcbiAgICAgICAgICAgID8gdmgxMDAgKiBwb3J0cmFpdFJhdGlvICAvLyDmqavlkJHvvJrpq5jluqYgKiDpq5jlr6zmr5RcbiAgICAgICAgICAgIDogdmgxMDAgKiBsYW5kc2NhcGVSYXRpbzsgLy8g55u05ZCR77ya6auY5bqmICog5a+s6auY5q+UXG4gICAgICAgIGNvbnN0IGJvdW5kYXJ5TWF4SGVpZ2h0ID0gaXNMYW5kc2NhcGVcbiAgICAgICAgICAgID8gdncxMDAgKiBsYW5kc2NhcGVSYXRpbyAgLy8g5qmr5ZCR77ya5a+s5bqmICog5a+s6auY5q+UXG4gICAgICAgICAgICA6IHZ3MTAwICogcG9ydHJhaXRSYXRpbzsgIC8vIOebtOWQke+8muWvrOW6piAqIOmrmOWvrOavlFxuXG4gICAgICAgIC8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgICAgICAgLy8gU3RlcCA0OiDoqIjnrpflr6bpmpvpgornlYzlsLrlr7jvvIjkvb/nlKggTWF0aC5taW4g5qih5pOsIENTUyBtaW4oKe+8iVxuICAgICAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgICAgIC8vIENocm9tZSA3OSDmiY3mlK/mj7QgQ1NTIG1pbigpIOWHveaVuO+8jOaJgOS7peWcqCBKYXZhU2NyaXB0IOS4reioiOeul1xuICAgICAgICBjb25zdCBib3VuZGFyeVdpZHRoID0gTWF0aC5taW4odncxMDAsIGJvdW5kYXJ5TWF4V2lkdGgpO1xuICAgICAgICBjb25zdCBib3VuZGFyeUhlaWdodCA9IE1hdGgubWluKHZoMTAwLCBib3VuZGFyeU1heEhlaWdodCk7XG5cbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyBTdGVwIDU6IOioiOeulyBweDJ2dyDovYnmj5vmr5TkvotcbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICAvLyDmqavlkJHmqKHlvI/kvb/nlKggZGVzaWduLWhlaWdodCDkvZzngrrln7rmupbvvIznm7TlkJHmqKHlvI/kvb/nlKggZGVzaWduLXdpZHRoIOS9nOeCuuWfuua6llxuICAgICAgICBjb25zdCBkZXNpZ25CYXNlV2lkdGggPSBpc0xhbmRzY2FwZSA/IGRlc2lnbkhlaWdodCA6IGRlc2lnbldpZHRoO1xuICAgICAgICBjb25zdCBweDJ2d1JhdGlvID0gYm91bmRhcnlXaWR0aCAvIGRlc2lnbkJhc2VXaWR0aDtcblxuICAgICAgICAvLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4gICAgICAgIC8vIFN0ZXAgNjog6Kit5a6aIENTUyDorormlbhcbiAgICAgICAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuICAgICAgICBob3N0RWxlbWVudC5zdHlsZS5zZXRQcm9wZXJ0eSgnLS1ib3VuZGFyeS13aWR0aCcsIGAke2JvdW5kYXJ5V2lkdGh9cHhgKTtcbiAgICAgICAgaG9zdEVsZW1lbnQuc3R5bGUuc2V0UHJvcGVydHkoJy0tYm91bmRhcnktaGVpZ2h0JywgYCR7Ym91bmRhcnlIZWlnaHR9cHhgKTtcbiAgICAgICAgaG9zdEVsZW1lbnQuc3R5bGUuc2V0UHJvcGVydHkoJy0tcHgydnctcmF0aW8nLCBgJHtweDJ2d1JhdGlvfWApO1xuICAgIH1cbn1cbiIsIjxkaXZcbiAgY2xhc3M9XCJib3VuZGFyeS13cmFwcGVyXCJcbiAgW2NsYXNzLmZpeGVkLWNlbnRlcl09XCJpc0ZpeGVkQ2VudGVyXCJcbiAgW2NsYXNzLnBvaW50ZXJFdmVudHNOb25lXT1cInBvaW50ZXJFdmVudHNOb25lXCJcbiAgW3N0eWxlXT1cImZvcndhcmRTdHlsZVwiXG4+XG4gICAgPG5nLWNvbnRlbnQ+PC9uZy1jb250ZW50PlxuPC9kaXY+XG4iXX0=
|
|
@@ -131,7 +131,7 @@ class BsBoundary {
|
|
|
131
131
|
this.setupResizeObserver();
|
|
132
132
|
}
|
|
133
133
|
ngAfterViewInit() {
|
|
134
|
-
//
|
|
134
|
+
// 使用 @Input 參數,可以立即計算(無需等待 CSS)
|
|
135
135
|
this.updateBoundaryCalculations();
|
|
136
136
|
}
|
|
137
137
|
ngOnDestroy() {
|
|
@@ -215,44 +215,35 @@ class BsBoundary {
|
|
|
215
215
|
* 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)
|
|
216
216
|
*
|
|
217
217
|
* 工作原理:
|
|
218
|
-
* 1.
|
|
219
|
-
* 2.
|
|
220
|
-
* 3.
|
|
221
|
-
* 4.
|
|
222
|
-
* 5.
|
|
218
|
+
* 1. 從 @Input 參數讀取設計尺寸
|
|
219
|
+
* 2. 讀取視口尺寸(vw100, vh100)
|
|
220
|
+
* 3. 處理 lockRotation 模式的視口尺寸交換
|
|
221
|
+
* 4. 計算邊界最大值和實際邊界尺寸
|
|
222
|
+
* 5. 計算 px2vw 轉換比例
|
|
223
|
+
* 6. 設定 CSS 變數供樣式使用
|
|
223
224
|
*
|
|
224
225
|
* 設定的 CSS 變數:
|
|
226
|
+
* - --design-width: 設計稿寬度
|
|
227
|
+
* - --design-height: 設計稿高度
|
|
225
228
|
* - --boundary-width: 實際邊界寬度(px)
|
|
226
229
|
* - --boundary-height: 實際邊界高度(px)
|
|
227
230
|
* - --px2vw-ratio: px 轉 vw 的比例係數
|
|
228
231
|
*/
|
|
229
232
|
updateBoundaryCalculations() {
|
|
230
233
|
const hostElement = this.elementRef.nativeElement;
|
|
231
|
-
const computedStyle = getComputedStyle(hostElement);
|
|
232
234
|
// ========================================
|
|
233
|
-
// Step 1:
|
|
235
|
+
// Step 1: 讀取設計尺寸並設定 CSS 變數
|
|
234
236
|
// ========================================
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
console.error('[BsBoundary][BS002] CSS variable --design-height is not set or invalid');
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
if (designWidth === 0) {
|
|
249
|
-
console.error('[BsBoundary][BS003] CSS variable --design-width cannot be zero');
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (designHeight === 0) {
|
|
253
|
-
console.error('[BsBoundary][BS004] CSS variable --design-height cannot be zero');
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
237
|
+
const designWidth = this.designWidth;
|
|
238
|
+
const designHeight = this.designHeight;
|
|
239
|
+
// 設定 CSS 變數,供樣式使用(例如 px2vw() 函數)
|
|
240
|
+
hostElement.style.setProperty('--design-width', `${designWidth}`);
|
|
241
|
+
hostElement.style.setProperty('--design-height', `${designHeight}`);
|
|
242
|
+
// ========================================
|
|
243
|
+
// Step 2: 讀取視口尺寸(直接使用 window.inner* 確保一致性)
|
|
244
|
+
// ========================================
|
|
245
|
+
let vw100 = window.innerWidth;
|
|
246
|
+
let vh100 = window.innerHeight;
|
|
256
247
|
// ========================================
|
|
257
248
|
// Step 2: 處理 lockRotation 模式的視口尺寸交換
|
|
258
249
|
// ========================================
|
|
@@ -299,17 +290,18 @@ class BsBoundary {
|
|
|
299
290
|
}
|
|
300
291
|
}
|
|
301
292
|
BsBoundary.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
302
|
-
BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", orientation: "orientation", forwardStyle: "forwardStyle" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.landscape": "isLandscape && orientation === \"landscape\"", "class.portrait": "!isLandscape && orientation === \"portrait\"", "class.landscape-lock-rotation": "orientation === \"landscapeLockRotation\"", "class.portrait-lock-rotation": "orientation === \"portraitLockRotation\"", "class.actual-portrait": "actualIsPortrait", "class.actual-landscape": "!actualIsPortrait" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))
|
|
293
|
+
BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", orientation: "orientation", forwardStyle: "forwardStyle", designWidth: "designWidth", designHeight: "designHeight" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.auto": "orientation === \"auto\"", "class.landscape": "isLandscape && orientation === \"landscape\"", "class.portrait": "!isLandscape && orientation === \"portrait\"", "class.landscape-lock-rotation": "orientation === \"landscapeLockRotation\"", "class.portrait-lock-rotation": "orientation === \"portraitLockRotation\"", "class.actual-portrait": "actualIsPortrait", "class.actual-landscape": "!actualIsPortrait" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.portrait{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}@media (orientation: landscape){:host.auto{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}:host.landscape-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}:host.landscape-lock-rotation.actual-portrait .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}:host.portrait-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.portrait-lock-rotation.actual-landscape .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}\n"] });
|
|
303
294
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, decorators: [{
|
|
304
295
|
type: Component,
|
|
305
296
|
args: [{ selector: 'bs-boundary', standalone: true, providers: [BsBoundaryContextService], host: {
|
|
297
|
+
'[class.auto]': 'orientation === "auto"',
|
|
306
298
|
'[class.landscape]': 'isLandscape && orientation === "landscape"',
|
|
307
299
|
'[class.portrait]': '!isLandscape && orientation === "portrait"',
|
|
308
300
|
'[class.landscape-lock-rotation]': 'orientation === "landscapeLockRotation"',
|
|
309
301
|
'[class.portrait-lock-rotation]': 'orientation === "portraitLockRotation"',
|
|
310
302
|
'[class.actual-portrait]': 'actualIsPortrait',
|
|
311
303
|
'[class.actual-landscape]': '!actualIsPortrait'
|
|
312
|
-
}, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))
|
|
304
|
+
}, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.portrait{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}@media (orientation: landscape){:host.auto{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}:host.landscape-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}:host.landscape-lock-rotation.actual-portrait .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}:host.portrait-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.portrait-lock-rotation.actual-landscape .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}\n"] }]
|
|
313
305
|
}], propDecorators: { isFixedCenter: [{
|
|
314
306
|
type: Input
|
|
315
307
|
}], pointerEventsNone: [{
|
|
@@ -318,6 +310,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
318
310
|
type: Input
|
|
319
311
|
}], forwardStyle: [{
|
|
320
312
|
type: Input
|
|
313
|
+
}], designWidth: [{
|
|
314
|
+
type: Input
|
|
315
|
+
}], designHeight: [{
|
|
316
|
+
type: Input
|
|
321
317
|
}], orientationChange: [{
|
|
322
318
|
type: Output
|
|
323
319
|
}] } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bitstack-ng-boundary.mjs","sources":["../../../projects/bitstack-ng-boundary/src/lib/bs-boundary-context.service.ts","../../../projects/bitstack-ng-boundary/src/lib/bs-boundary.ts","../../../projects/bitstack-ng-boundary/src/lib/template.html","../../../projects/bitstack-ng-boundary/src/public-api.ts","../../../projects/bitstack-ng-boundary/src/bitstack-ng-boundary.ts"],"sourcesContent":["import {Injectable} from '@angular/core';\nimport {BehaviorSubject} from 'rxjs';\n\n@Injectable()\nexport class BsBoundaryContextService {\n private _isLandscape = new BehaviorSubject<boolean>(false);\n\n readonly isLandscape$ = this._isLandscape.asObservable();\n\n setIsLandscape(value: boolean): void {\n this._isLandscape.next(value);\n }\n\n getIsLandscape(): boolean {\n return this._isLandscape.value;\n }\n}\n","import {BreakpointObserver} from '@angular/cdk/layout';\nimport {\n AfterViewInit,\n Component,\n ElementRef,\n EventEmitter,\n inject,\n Input, OnDestroy,\n OnInit,\n Output,\n} from '@angular/core';\nimport {debounceTime, fromEvent, Subject, takeUntil} from 'rxjs';\n\nimport {BsBoundaryContextService} from './bs-boundary-context.service';\nimport {TOrientationMode} from './model';\n\n/**\n * ============================================================\n * BsBoundary Component - AWD 自適應邊界容器\n * ============================================================\n *\n * 功能說明:\n * 1. 提供響應式邊界容器,自動適應不同設備尺寸\n * 2. 支援 5 種方向模式:auto, portrait, landscape, portraitLockRotation, landscapeLockRotation\n * 3. 使用 CSS 變數實現動態計算,支援 px2vw() 函數\n * 4. 相容 Chrome 74+,使用 JavaScript 計算邊界尺寸\n *\n * 使用範例:\n * ```html\n * <bs-boundary [orientation]=\"'auto'\" [isFixedCenter]=\"true\">\n * <div class=\"content\">...</div>\n * </bs-boundary>\n * ```\n *\n * CSS 變數設定:\n * ```scss\n * bs-boundary {\n * --design-width: 375; // 設計稿寬度\n * --design-height: 667; // 設計稿高度\n * }\n * ```\n */\n@Component({\n selector: 'bs-boundary',\n templateUrl: './template.html',\n standalone: true,\n styleUrls: ['./styles.scss'],\n providers: [BsBoundaryContextService],\n host: {\n '[class.landscape]': 'isLandscape && orientation === \"landscape\"',\n '[class.portrait]': '!isLandscape && orientation === \"portrait\"',\n '[class.landscape-lock-rotation]': 'orientation === \"landscapeLockRotation\"',\n '[class.portrait-lock-rotation]': 'orientation === \"portraitLockRotation\"',\n '[class.actual-portrait]': 'actualIsPortrait',\n '[class.actual-landscape]': '!actualIsPortrait'\n }\n})\nexport class BsBoundary implements OnInit, AfterViewInit, OnDestroy {\n\n // ============================================================\n // Inputs & Outputs\n // ============================================================\n\n /**\n * 是否將內容固定在視窗中央\n * @default false\n */\n @Input() isFixedCenter?: boolean = false;\n\n /**\n * 是否禁用指標事件\n * @default false\n */\n @Input() pointerEventsNone?: boolean = false;\n\n /**\n * 方向模式\n * - auto: 自動檢測方向\n * - portrait: 鎖定直向\n * - landscape: 鎖定橫向\n * - portraitLockRotation: 強制直向(橫向設備時旋轉 -90°)\n * - landscapeLockRotation: 強制橫向(直向設備時旋轉 90°)\n * @default 'auto'\n */\n @Input() orientation?: TOrientationMode = 'auto';\n\n /**\n * 向內層容器轉發的自訂樣式\n */\n @Input() forwardStyle?: Record<string, string>;\n\n /**\n * 方向變化事件\n * @emits true: landscape(橫屏)\n * @emits false: portrait(直屏)\n */\n @Output() orientationChange = new EventEmitter<boolean>();\n\n // ============================================================\n // Private Dependencies\n // ============================================================\n\n private breakpointObserver = inject(BreakpointObserver);\n private boundaryContext = inject(BsBoundaryContextService);\n private elementRef = inject(ElementRef);\n private destroy$ = new Subject<void>();\n\n // ============================================================\n // Public Properties\n // ============================================================\n\n /**\n * 實際設備方向(基於視口尺寸)\n * true: 直向 (height > width)\n * false: 橫向 (width >= height)\n */\n actualIsPortrait = false;\n\n // ============================================================\n // Getters\n // ============================================================\n\n /**\n * 判斷當前是否為橫向模式\n * 根據 orientation 設定和實際設備方向判斷\n */\n get isLandscape(): boolean {\n // landscapeLockRotation: 強制橫屏模式\n if (this.orientation === 'landscapeLockRotation') {\n return true;\n }\n // portraitLockRotation: 強制直屏模式\n if (this.orientation === 'portraitLockRotation') {\n return false;\n }\n // landscape: 鎖定橫向\n if (this.orientation === 'landscape') {\n return true;\n }\n // portrait: 鎖定直向\n if (this.orientation === 'portrait') {\n return false;\n }\n // auto: 自動檢測\n return this.boundaryContext.getIsLandscape();\n }\n\n // ============================================================\n // Lifecycle Hooks\n // ============================================================\n\n ngOnInit(): void {\n this.updateActualOrientation();\n this.setupBreakpointObserver();\n this.setupResizeObserver();\n }\n\n ngAfterViewInit(): void {\n // 在 view 初始化後計算(確保 CSS 已完全應用)\n this.updateBoundaryCalculations();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // ============================================================\n // Observers Setup - 監聽器設定\n // ============================================================\n\n /**\n * 設定方向變化監控\n *\n * 工作原理:\n * - lock 模式(portrait/landscape):直接設定固定方向,不監聽變化\n * - lockRotation 模式:設定固定方向,但需監聽實際設備方向以觸發旋轉\n * - auto 模式:使用 BreakpointObserver 監聽設備方向變化\n */\n private setupBreakpointObserver() {\n // landscape 或 landscapeLockRotation: 鎖定為橫向\n if (this.orientation === 'landscape' || this.orientation === 'landscapeLockRotation') {\n this.boundaryContext.setIsLandscape(true);\n this.orientationChange.emit(true);\n return;\n }\n\n // portrait 或 portraitLockRotation: 鎖定為直向\n if (this.orientation === 'portrait' || this.orientation === 'portraitLockRotation') {\n this.boundaryContext.setIsLandscape(false);\n this.orientationChange.emit(false);\n return;\n }\n\n // auto 模式:監聽實際的設備方向(用於 orientationChange 事件)\n this.breakpointObserver\n .observe('(orientation: landscape)')\n .pipe(takeUntil(this.destroy$))\n .subscribe(result => {\n this.boundaryContext.setIsLandscape(result.matches);\n this.orientationChange.emit(result.matches);\n // 方向變化時重新計算邊界\n this.updateBoundaryCalculations();\n });\n }\n\n /**\n * 設定視窗大小變化監控\n *\n * 功能:\n * - 監聽 window resize 事件\n * - 更新實際設備方向\n * - 重新計算邊界尺寸\n */\n private setupResizeObserver() {\n fromEvent(window, 'resize')\n .pipe(\n // debounceTime(150), // 可選:防抖優化性能\n takeUntil(this.destroy$)\n )\n .subscribe(() => {\n this.updateActualOrientation();\n this.updateBoundaryCalculations();\n });\n }\n\n // ============================================================\n // Calculation Methods - 計算方法\n // ============================================================\n\n /**\n * 更新實際設備方向(基於視口尺寸)\n *\n * 工作原理:\n * - 只在 lockRotation 模式下需要檢測實際方向\n * - 通過比較 window.innerHeight 和 window.innerWidth 判斷\n * - 結果用於觸發 CSS 旋轉變換\n */\n private updateActualOrientation() {\n // 只有在 lockRotation 模式下才需要檢測實際方向\n if (this.orientation !== 'landscapeLockRotation' && this.orientation !== 'portraitLockRotation') {\n this.actualIsPortrait = false;\n return;\n }\n\n // 檢測實際視口方向:高度 > 寬度 = 直向\n this.actualIsPortrait = window.innerHeight > window.innerWidth;\n }\n\n /**\n * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)\n *\n * 工作原理:\n * 1. 讀取基礎 CSS 變數(vw100, vh100, design-width, design-height)\n * 2. 處理 lockRotation 模式的視口尺寸交換\n * 3. 計算邊界最大值和實際邊界尺寸\n * 4. 計算 px2vw 轉換比例\n * 5. 設定 CSS 變數供樣式使用\n *\n * 設定的 CSS 變數:\n * - --boundary-width: 實際邊界寬度(px)\n * - --boundary-height: 實際邊界高度(px)\n * - --px2vw-ratio: px 轉 vw 的比例係數\n */\n private updateBoundaryCalculations() {\n const hostElement = this.elementRef.nativeElement as HTMLElement;\n const computedStyle = getComputedStyle(hostElement);\n\n // ========================================\n // Step 1: 讀取基礎 CSS 變數\n // ========================================\n let vw100 = parseFloat(computedStyle.getPropertyValue('--vw100')) || window.innerWidth;\n let vh100 = parseFloat(computedStyle.getPropertyValue('--vh100')) || window.innerHeight;\n const designWidth = parseFloat(computedStyle.getPropertyValue('--design-width'));\n const designHeight = parseFloat(computedStyle.getPropertyValue('--design-height'));\n\n // 檢查基礎變數是否準備好\n if (isNaN(designWidth)) {\n console.error('[BsBoundary][BS001] CSS variable --design-width is not set or invalid');\n return;\n }\n if (isNaN(designHeight)) {\n console.error('[BsBoundary][BS002] CSS variable --design-height is not set or invalid');\n return;\n }\n if (designWidth === 0) {\n console.error('[BsBoundary][BS003] CSS variable --design-width cannot be zero');\n return;\n }\n if (designHeight === 0) {\n console.error('[BsBoundary][BS004] CSS variable --design-height cannot be zero');\n return;\n }\n\n // ========================================\n // Step 2: 處理 lockRotation 模式的視口尺寸交換\n // ========================================\n // landscapeLockRotation + 實際直向:交換視口尺寸\n // 原因:旋轉後,內容的寬度對應設備的高度,內容的高度對應設備的寬度\n if (this.orientation === 'landscapeLockRotation' && this.actualIsPortrait) {\n [vw100, vh100] = [vh100, vw100];\n }\n\n // portraitLockRotation + 實際橫向:交換視口尺寸\n if (this.orientation === 'portraitLockRotation' && !this.actualIsPortrait) {\n [vw100, vh100] = [vh100, vw100];\n }\n\n // ========================================\n // Step 3: 計算比例和邊界最大值\n // ========================================\n const landscapeRatio = designWidth / designHeight;\n const portraitRatio = designHeight / designWidth;\n\n // 根據方向模式計算邊界最大值\n const isLandscape = this.isLandscape;\n const boundaryMaxWidth = isLandscape\n ? vh100 * portraitRatio // 橫向:高度 * 高寬比\n : vh100 * landscapeRatio; // 直向:高度 * 寬高比\n const boundaryMaxHeight = isLandscape\n ? vw100 * landscapeRatio // 橫向:寬度 * 寬高比\n : vw100 * portraitRatio; // 直向:寬度 * 高寬比\n\n // ========================================\n // Step 4: 計算實際邊界尺寸(使用 Math.min 模擬 CSS min())\n // ========================================\n // Chrome 79 才支援 CSS min() 函數,所以在 JavaScript 中計算\n const boundaryWidth = Math.min(vw100, boundaryMaxWidth);\n const boundaryHeight = Math.min(vh100, boundaryMaxHeight);\n\n // ========================================\n // Step 5: 計算 px2vw 轉換比例\n // ========================================\n // 橫向模式使用 design-height 作為基準,直向模式使用 design-width 作為基準\n const designBaseWidth = isLandscape ? designHeight : designWidth;\n const px2vwRatio = boundaryWidth / designBaseWidth;\n\n // ========================================\n // Step 6: 設定 CSS 變數\n // ========================================\n hostElement.style.setProperty('--boundary-width', `${boundaryWidth}px`);\n hostElement.style.setProperty('--boundary-height', `${boundaryHeight}px`);\n hostElement.style.setProperty('--px2vw-ratio', `${px2vwRatio}`);\n }\n}\n","<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n","/*\n * Public API Surface of @bitstack/ng-boundary\n */\n\nexport * from './lib/bs-boundary';\nexport * from './lib/bs-boundary-context.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bitstack-ng-boundary.mjs","sources":["../../../projects/bitstack-ng-boundary/src/lib/bs-boundary-context.service.ts","../../../projects/bitstack-ng-boundary/src/lib/bs-boundary.ts","../../../projects/bitstack-ng-boundary/src/lib/template.html","../../../projects/bitstack-ng-boundary/src/public-api.ts","../../../projects/bitstack-ng-boundary/src/bitstack-ng-boundary.ts"],"sourcesContent":["import {Injectable} from '@angular/core';\nimport {BehaviorSubject} from 'rxjs';\n\n@Injectable()\nexport class BsBoundaryContextService {\n private _isLandscape = new BehaviorSubject<boolean>(false);\n\n readonly isLandscape$ = this._isLandscape.asObservable();\n\n setIsLandscape(value: boolean): void {\n this._isLandscape.next(value);\n }\n\n getIsLandscape(): boolean {\n return this._isLandscape.value;\n }\n}\n","import {BreakpointObserver} from '@angular/cdk/layout';\nimport {\n AfterViewInit,\n Component,\n ElementRef,\n EventEmitter,\n inject,\n Input, OnDestroy,\n OnInit,\n Output,\n} from '@angular/core';\nimport {debounceTime, fromEvent, Subject, takeUntil} from 'rxjs';\n\nimport {BsBoundaryContextService} from './bs-boundary-context.service';\nimport {TOrientationMode} from './model';\n\n/**\n * ============================================================\n * BsBoundary Component - AWD 自適應邊界容器\n * ============================================================\n *\n * 功能說明:\n * 1. 提供響應式邊界容器,自動適應不同設備尺寸\n * 2. 支援 5 種方向模式:auto, portrait, landscape, portraitLockRotation, landscapeLockRotation\n * 3. 使用 CSS 變數實現動態計算,支援 px2vw() 函數\n * 4. 相容 Chrome 74+,使用 JavaScript 計算邊界尺寸\n *\n * 使用範例:\n * ```html\n * <bs-boundary [orientation]=\"'auto'\" [isFixedCenter]=\"true\">\n * <div class=\"content\">...</div>\n * </bs-boundary>\n * ```\n *\n * CSS 變數設定:\n * ```scss\n * bs-boundary {\n * --design-width: 375; // 設計稿寬度\n * --design-height: 667; // 設計稿高度\n * }\n * ```\n */\n@Component({\n selector: 'bs-boundary',\n templateUrl: './template.html',\n standalone: true,\n styleUrls: ['./styles.scss'],\n providers: [BsBoundaryContextService],\n host: {\n '[class.auto]': 'orientation === \"auto\"',\n '[class.landscape]': 'isLandscape && orientation === \"landscape\"',\n '[class.portrait]': '!isLandscape && orientation === \"portrait\"',\n '[class.landscape-lock-rotation]': 'orientation === \"landscapeLockRotation\"',\n '[class.portrait-lock-rotation]': 'orientation === \"portraitLockRotation\"',\n '[class.actual-portrait]': 'actualIsPortrait',\n '[class.actual-landscape]': '!actualIsPortrait'\n }\n})\nexport class BsBoundary implements OnInit, AfterViewInit, OnDestroy {\n\n // ============================================================\n // Inputs & Outputs\n // ============================================================\n\n /**\n * 是否將內容固定在視窗中央\n * @default false\n */\n @Input() isFixedCenter?: boolean = false;\n\n /**\n * 是否禁用指標事件\n * @default false\n */\n @Input() pointerEventsNone?: boolean = false;\n\n /**\n * 方向模式\n * - auto: 自動檢測方向\n * - portrait: 鎖定直向\n * - landscape: 鎖定橫向\n * - portraitLockRotation: 強制直向(橫向設備時旋轉 -90°)\n * - landscapeLockRotation: 強制橫向(直向設備時旋轉 90°)\n * @default 'auto'\n */\n @Input() orientation?: TOrientationMode = 'auto';\n\n /**\n * 向內層容器轉發的自訂樣式\n */\n @Input() forwardStyle?: Record<string, string>;\n\n /**\n * 設計稿寬度(單位:px)\n * 必填參數\n */\n @Input() designWidth!: number;\n\n /**\n * 設計稿高度(單位:px)\n * 必填參數\n */\n @Input() designHeight!: number;\n\n /**\n * 方向變化事件\n * @emits true: landscape(橫屏)\n * @emits false: portrait(直屏)\n */\n @Output() orientationChange = new EventEmitter<boolean>();\n\n // ============================================================\n // Private Dependencies\n // ============================================================\n\n private breakpointObserver = inject(BreakpointObserver);\n private boundaryContext = inject(BsBoundaryContextService);\n private elementRef = inject(ElementRef);\n private destroy$ = new Subject<void>();\n\n // ============================================================\n // Public Properties\n // ============================================================\n\n /**\n * 實際設備方向(基於視口尺寸)\n * true: 直向 (height > width)\n * false: 橫向 (width >= height)\n */\n actualIsPortrait = false;\n\n // ============================================================\n // Getters\n // ============================================================\n\n /**\n * 判斷當前是否為橫向模式\n * 根據 orientation 設定和實際設備方向判斷\n */\n get isLandscape(): boolean {\n // landscapeLockRotation: 強制橫屏模式\n if (this.orientation === 'landscapeLockRotation') {\n return true;\n }\n // portraitLockRotation: 強制直屏模式\n if (this.orientation === 'portraitLockRotation') {\n return false;\n }\n // landscape: 鎖定橫向\n if (this.orientation === 'landscape') {\n return true;\n }\n // portrait: 鎖定直向\n if (this.orientation === 'portrait') {\n return false;\n }\n // auto: 自動檢測\n return this.boundaryContext.getIsLandscape();\n }\n\n // ============================================================\n // Lifecycle Hooks\n // ============================================================\n\n ngOnInit(): void {\n this.updateActualOrientation();\n this.setupBreakpointObserver();\n this.setupResizeObserver();\n }\n\n ngAfterViewInit(): void {\n // 使用 @Input 參數,可以立即計算(無需等待 CSS)\n this.updateBoundaryCalculations();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // ============================================================\n // Observers Setup - 監聽器設定\n // ============================================================\n\n /**\n * 設定方向變化監控\n *\n * 工作原理:\n * - lock 模式(portrait/landscape):直接設定固定方向,不監聽變化\n * - lockRotation 模式:設定固定方向,但需監聽實際設備方向以觸發旋轉\n * - auto 模式:使用 BreakpointObserver 監聽設備方向變化\n */\n private setupBreakpointObserver() {\n // landscape 或 landscapeLockRotation: 鎖定為橫向\n if (this.orientation === 'landscape' || this.orientation === 'landscapeLockRotation') {\n this.boundaryContext.setIsLandscape(true);\n this.orientationChange.emit(true);\n return;\n }\n\n // portrait 或 portraitLockRotation: 鎖定為直向\n if (this.orientation === 'portrait' || this.orientation === 'portraitLockRotation') {\n this.boundaryContext.setIsLandscape(false);\n this.orientationChange.emit(false);\n return;\n }\n\n // auto 模式:監聽實際的設備方向(用於 orientationChange 事件)\n this.breakpointObserver\n .observe('(orientation: landscape)')\n .pipe(takeUntil(this.destroy$))\n .subscribe(result => {\n this.boundaryContext.setIsLandscape(result.matches);\n this.orientationChange.emit(result.matches);\n // 方向變化時重新計算邊界\n this.updateBoundaryCalculations();\n });\n }\n\n /**\n * 設定視窗大小變化監控\n *\n * 功能:\n * - 監聽 window resize 事件\n * - 更新實際設備方向\n * - 重新計算邊界尺寸\n */\n private setupResizeObserver() {\n fromEvent(window, 'resize')\n .pipe(\n // debounceTime(150), // 可選:防抖優化性能\n takeUntil(this.destroy$)\n )\n .subscribe(() => {\n this.updateActualOrientation();\n this.updateBoundaryCalculations();\n });\n }\n\n // ============================================================\n // Calculation Methods - 計算方法\n // ============================================================\n\n /**\n * 更新實際設備方向(基於視口尺寸)\n *\n * 工作原理:\n * - 只在 lockRotation 模式下需要檢測實際方向\n * - 通過比較 window.innerHeight 和 window.innerWidth 判斷\n * - 結果用於觸發 CSS 旋轉變換\n */\n private updateActualOrientation() {\n // 只有在 lockRotation 模式下才需要檢測實際方向\n if (this.orientation !== 'landscapeLockRotation' && this.orientation !== 'portraitLockRotation') {\n this.actualIsPortrait = false;\n return;\n }\n\n // 檢測實際視口方向:高度 > 寬度 = 直向\n this.actualIsPortrait = window.innerHeight > window.innerWidth;\n }\n\n /**\n * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)\n *\n * 工作原理:\n * 1. 從 @Input 參數讀取設計尺寸\n * 2. 讀取視口尺寸(vw100, vh100)\n * 3. 處理 lockRotation 模式的視口尺寸交換\n * 4. 計算邊界最大值和實際邊界尺寸\n * 5. 計算 px2vw 轉換比例\n * 6. 設定 CSS 變數供樣式使用\n *\n * 設定的 CSS 變數:\n * - --design-width: 設計稿寬度\n * - --design-height: 設計稿高度\n * - --boundary-width: 實際邊界寬度(px)\n * - --boundary-height: 實際邊界高度(px)\n * - --px2vw-ratio: px 轉 vw 的比例係數\n */\n private updateBoundaryCalculations() {\n const hostElement = this.elementRef.nativeElement as HTMLElement;\n\n // ========================================\n // Step 1: 讀取設計尺寸並設定 CSS 變數\n // ========================================\n const designWidth = this.designWidth;\n const designHeight = this.designHeight;\n\n // 設定 CSS 變數,供樣式使用(例如 px2vw() 函數)\n hostElement.style.setProperty('--design-width', `${designWidth}`);\n hostElement.style.setProperty('--design-height', `${designHeight}`);\n\n // ========================================\n // Step 2: 讀取視口尺寸(直接使用 window.inner* 確保一致性)\n // ========================================\n let vw100 = window.innerWidth;\n let vh100 = window.innerHeight;\n\n // ========================================\n // Step 2: 處理 lockRotation 模式的視口尺寸交換\n // ========================================\n // landscapeLockRotation + 實際直向:交換視口尺寸\n // 原因:旋轉後,內容的寬度對應設備的高度,內容的高度對應設備的寬度\n if (this.orientation === 'landscapeLockRotation' && this.actualIsPortrait) {\n [vw100, vh100] = [vh100, vw100];\n }\n\n // portraitLockRotation + 實際橫向:交換視口尺寸\n if (this.orientation === 'portraitLockRotation' && !this.actualIsPortrait) {\n [vw100, vh100] = [vh100, vw100];\n }\n\n // ========================================\n // Step 3: 計算比例和邊界最大值\n // ========================================\n const landscapeRatio = designWidth / designHeight;\n const portraitRatio = designHeight / designWidth;\n\n // 根據方向模式計算邊界最大值\n const isLandscape = this.isLandscape;\n const boundaryMaxWidth = isLandscape\n ? vh100 * portraitRatio // 橫向:高度 * 高寬比\n : vh100 * landscapeRatio; // 直向:高度 * 寬高比\n const boundaryMaxHeight = isLandscape\n ? vw100 * landscapeRatio // 橫向:寬度 * 寬高比\n : vw100 * portraitRatio; // 直向:寬度 * 高寬比\n\n // ========================================\n // Step 4: 計算實際邊界尺寸(使用 Math.min 模擬 CSS min())\n // ========================================\n // Chrome 79 才支援 CSS min() 函數,所以在 JavaScript 中計算\n const boundaryWidth = Math.min(vw100, boundaryMaxWidth);\n const boundaryHeight = Math.min(vh100, boundaryMaxHeight);\n\n // ========================================\n // Step 5: 計算 px2vw 轉換比例\n // ========================================\n // 橫向模式使用 design-height 作為基準,直向模式使用 design-width 作為基準\n const designBaseWidth = isLandscape ? designHeight : designWidth;\n const px2vwRatio = boundaryWidth / designBaseWidth;\n\n // ========================================\n // Step 6: 設定 CSS 變數\n // ========================================\n hostElement.style.setProperty('--boundary-width', `${boundaryWidth}px`);\n hostElement.style.setProperty('--boundary-height', `${boundaryHeight}px`);\n hostElement.style.setProperty('--px2vw-ratio', `${px2vwRatio}`);\n }\n}\n","<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n","/*\n * Public API Surface of @bitstack/ng-boundary\n */\n\nexport * from './lib/bs-boundary';\nexport * from './lib/bs-boundary-context.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -131,7 +131,7 @@ class BsBoundary {
|
|
|
131
131
|
this.setupResizeObserver();
|
|
132
132
|
}
|
|
133
133
|
ngAfterViewInit() {
|
|
134
|
-
//
|
|
134
|
+
// 使用 @Input 參數,可以立即計算(無需等待 CSS)
|
|
135
135
|
this.updateBoundaryCalculations();
|
|
136
136
|
}
|
|
137
137
|
ngOnDestroy() {
|
|
@@ -215,44 +215,35 @@ class BsBoundary {
|
|
|
215
215
|
* 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)
|
|
216
216
|
*
|
|
217
217
|
* 工作原理:
|
|
218
|
-
* 1.
|
|
219
|
-
* 2.
|
|
220
|
-
* 3.
|
|
221
|
-
* 4.
|
|
222
|
-
* 5.
|
|
218
|
+
* 1. 從 @Input 參數讀取設計尺寸
|
|
219
|
+
* 2. 讀取視口尺寸(vw100, vh100)
|
|
220
|
+
* 3. 處理 lockRotation 模式的視口尺寸交換
|
|
221
|
+
* 4. 計算邊界最大值和實際邊界尺寸
|
|
222
|
+
* 5. 計算 px2vw 轉換比例
|
|
223
|
+
* 6. 設定 CSS 變數供樣式使用
|
|
223
224
|
*
|
|
224
225
|
* 設定的 CSS 變數:
|
|
226
|
+
* - --design-width: 設計稿寬度
|
|
227
|
+
* - --design-height: 設計稿高度
|
|
225
228
|
* - --boundary-width: 實際邊界寬度(px)
|
|
226
229
|
* - --boundary-height: 實際邊界高度(px)
|
|
227
230
|
* - --px2vw-ratio: px 轉 vw 的比例係數
|
|
228
231
|
*/
|
|
229
232
|
updateBoundaryCalculations() {
|
|
230
233
|
const hostElement = this.elementRef.nativeElement;
|
|
231
|
-
const computedStyle = getComputedStyle(hostElement);
|
|
232
234
|
// ========================================
|
|
233
|
-
// Step 1:
|
|
235
|
+
// Step 1: 讀取設計尺寸並設定 CSS 變數
|
|
234
236
|
// ========================================
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
console.error('[BsBoundary][BS002] CSS variable --design-height is not set or invalid');
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
if (designWidth === 0) {
|
|
249
|
-
console.error('[BsBoundary][BS003] CSS variable --design-width cannot be zero');
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (designHeight === 0) {
|
|
253
|
-
console.error('[BsBoundary][BS004] CSS variable --design-height cannot be zero');
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
237
|
+
const designWidth = this.designWidth;
|
|
238
|
+
const designHeight = this.designHeight;
|
|
239
|
+
// 設定 CSS 變數,供樣式使用(例如 px2vw() 函數)
|
|
240
|
+
hostElement.style.setProperty('--design-width', `${designWidth}`);
|
|
241
|
+
hostElement.style.setProperty('--design-height', `${designHeight}`);
|
|
242
|
+
// ========================================
|
|
243
|
+
// Step 2: 讀取視口尺寸(直接使用 window.inner* 確保一致性)
|
|
244
|
+
// ========================================
|
|
245
|
+
let vw100 = window.innerWidth;
|
|
246
|
+
let vh100 = window.innerHeight;
|
|
256
247
|
// ========================================
|
|
257
248
|
// Step 2: 處理 lockRotation 模式的視口尺寸交換
|
|
258
249
|
// ========================================
|
|
@@ -299,17 +290,18 @@ class BsBoundary {
|
|
|
299
290
|
}
|
|
300
291
|
}
|
|
301
292
|
BsBoundary.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
302
|
-
BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", orientation: "orientation", forwardStyle: "forwardStyle" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.landscape": "isLandscape && orientation === \"landscape\"", "class.portrait": "!isLandscape && orientation === \"portrait\"", "class.landscape-lock-rotation": "orientation === \"landscapeLockRotation\"", "class.portrait-lock-rotation": "orientation === \"portraitLockRotation\"", "class.actual-portrait": "actualIsPortrait", "class.actual-landscape": "!actualIsPortrait" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))
|
|
293
|
+
BsBoundary.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: BsBoundary, isStandalone: true, selector: "bs-boundary", inputs: { isFixedCenter: "isFixedCenter", pointerEventsNone: "pointerEventsNone", orientation: "orientation", forwardStyle: "forwardStyle", designWidth: "designWidth", designHeight: "designHeight" }, outputs: { orientationChange: "orientationChange" }, host: { properties: { "class.auto": "orientation === \"auto\"", "class.landscape": "isLandscape && orientation === \"landscape\"", "class.portrait": "!isLandscape && orientation === \"portrait\"", "class.landscape-lock-rotation": "orientation === \"landscapeLockRotation\"", "class.portrait-lock-rotation": "orientation === \"portraitLockRotation\"", "class.actual-portrait": "actualIsPortrait", "class.actual-landscape": "!actualIsPortrait" } }, providers: [BsBoundaryContextService], ngImport: i0, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.portrait{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}@media (orientation: landscape){:host.auto{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}:host.landscape-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}:host.landscape-lock-rotation.actual-portrait .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}:host.portrait-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.portrait-lock-rotation.actual-landscape .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}\n"] });
|
|
303
294
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BsBoundary, decorators: [{
|
|
304
295
|
type: Component,
|
|
305
296
|
args: [{ selector: 'bs-boundary', standalone: true, providers: [BsBoundaryContextService], host: {
|
|
297
|
+
'[class.auto]': 'orientation === "auto"',
|
|
306
298
|
'[class.landscape]': 'isLandscape && orientation === "landscape"',
|
|
307
299
|
'[class.portrait]': '!isLandscape && orientation === "portrait"',
|
|
308
300
|
'[class.landscape-lock-rotation]': 'orientation === "landscapeLockRotation"',
|
|
309
301
|
'[class.portrait-lock-rotation]': 'orientation === "portraitLockRotation"',
|
|
310
302
|
'[class.actual-portrait]': 'actualIsPortrait',
|
|
311
303
|
'[class.actual-landscape]': '!actualIsPortrait'
|
|
312
|
-
}, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))
|
|
304
|
+
}, template: "<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n", styles: [":host{--landscape-ratio: calc(var(--design-width) / var(--design-height));--portrait-ratio: calc(var(--design-height) / var(--design-width));--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}.boundary-wrapper{width:var(--vw100);height:var(--vh100);max-width:var(--boundary-max-width);max-height:var(--boundary-max-height)}.boundary-wrapper.pointerEventsNone{pointer-events:none}:host.portrait{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.landscape{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}@media (orientation: landscape){:host.auto{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}}.fixed-center{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;z-index:99}:host.landscape-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--portrait-ratio));--boundary-max-height: calc(var(--vw100) * var(--landscape-ratio))}:host.landscape-lock-rotation.actual-portrait .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}:host.portrait-lock-rotation{--boundary-max-width: calc(var(--vh100) * var(--landscape-ratio));--boundary-max-height: calc(var(--vw100) * var(--portrait-ratio))}:host.portrait-lock-rotation.actual-landscape .boundary-wrapper{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%) rotate(-90deg);transform-origin:center;width:var(--boundary-width);height:var(--boundary-height);max-width:none;max-height:none}\n"] }]
|
|
313
305
|
}], propDecorators: { isFixedCenter: [{
|
|
314
306
|
type: Input
|
|
315
307
|
}], pointerEventsNone: [{
|
|
@@ -318,6 +310,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImpor
|
|
|
318
310
|
type: Input
|
|
319
311
|
}], forwardStyle: [{
|
|
320
312
|
type: Input
|
|
313
|
+
}], designWidth: [{
|
|
314
|
+
type: Input
|
|
315
|
+
}], designHeight: [{
|
|
316
|
+
type: Input
|
|
321
317
|
}], orientationChange: [{
|
|
322
318
|
type: Output
|
|
323
319
|
}] } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bitstack-ng-boundary.mjs","sources":["../../../projects/bitstack-ng-boundary/src/lib/bs-boundary-context.service.ts","../../../projects/bitstack-ng-boundary/src/lib/bs-boundary.ts","../../../projects/bitstack-ng-boundary/src/lib/template.html","../../../projects/bitstack-ng-boundary/src/public-api.ts","../../../projects/bitstack-ng-boundary/src/bitstack-ng-boundary.ts"],"sourcesContent":["import {Injectable} from '@angular/core';\nimport {BehaviorSubject} from 'rxjs';\n\n@Injectable()\nexport class BsBoundaryContextService {\n private _isLandscape = new BehaviorSubject<boolean>(false);\n\n readonly isLandscape$ = this._isLandscape.asObservable();\n\n setIsLandscape(value: boolean): void {\n this._isLandscape.next(value);\n }\n\n getIsLandscape(): boolean {\n return this._isLandscape.value;\n }\n}\n","import {BreakpointObserver} from '@angular/cdk/layout';\nimport {\n AfterViewInit,\n Component,\n ElementRef,\n EventEmitter,\n inject,\n Input, OnDestroy,\n OnInit,\n Output,\n} from '@angular/core';\nimport {debounceTime, fromEvent, Subject, takeUntil} from 'rxjs';\n\nimport {BsBoundaryContextService} from './bs-boundary-context.service';\nimport {TOrientationMode} from './model';\n\n/**\n * ============================================================\n * BsBoundary Component - AWD 自適應邊界容器\n * ============================================================\n *\n * 功能說明:\n * 1. 提供響應式邊界容器,自動適應不同設備尺寸\n * 2. 支援 5 種方向模式:auto, portrait, landscape, portraitLockRotation, landscapeLockRotation\n * 3. 使用 CSS 變數實現動態計算,支援 px2vw() 函數\n * 4. 相容 Chrome 74+,使用 JavaScript 計算邊界尺寸\n *\n * 使用範例:\n * ```html\n * <bs-boundary [orientation]=\"'auto'\" [isFixedCenter]=\"true\">\n * <div class=\"content\">...</div>\n * </bs-boundary>\n * ```\n *\n * CSS 變數設定:\n * ```scss\n * bs-boundary {\n * --design-width: 375; // 設計稿寬度\n * --design-height: 667; // 設計稿高度\n * }\n * ```\n */\n@Component({\n selector: 'bs-boundary',\n templateUrl: './template.html',\n standalone: true,\n styleUrls: ['./styles.scss'],\n providers: [BsBoundaryContextService],\n host: {\n '[class.landscape]': 'isLandscape && orientation === \"landscape\"',\n '[class.portrait]': '!isLandscape && orientation === \"portrait\"',\n '[class.landscape-lock-rotation]': 'orientation === \"landscapeLockRotation\"',\n '[class.portrait-lock-rotation]': 'orientation === \"portraitLockRotation\"',\n '[class.actual-portrait]': 'actualIsPortrait',\n '[class.actual-landscape]': '!actualIsPortrait'\n }\n})\nexport class BsBoundary implements OnInit, AfterViewInit, OnDestroy {\n\n // ============================================================\n // Inputs & Outputs\n // ============================================================\n\n /**\n * 是否將內容固定在視窗中央\n * @default false\n */\n @Input() isFixedCenter?: boolean = false;\n\n /**\n * 是否禁用指標事件\n * @default false\n */\n @Input() pointerEventsNone?: boolean = false;\n\n /**\n * 方向模式\n * - auto: 自動檢測方向\n * - portrait: 鎖定直向\n * - landscape: 鎖定橫向\n * - portraitLockRotation: 強制直向(橫向設備時旋轉 -90°)\n * - landscapeLockRotation: 強制橫向(直向設備時旋轉 90°)\n * @default 'auto'\n */\n @Input() orientation?: TOrientationMode = 'auto';\n\n /**\n * 向內層容器轉發的自訂樣式\n */\n @Input() forwardStyle?: Record<string, string>;\n\n /**\n * 方向變化事件\n * @emits true: landscape(橫屏)\n * @emits false: portrait(直屏)\n */\n @Output() orientationChange = new EventEmitter<boolean>();\n\n // ============================================================\n // Private Dependencies\n // ============================================================\n\n private breakpointObserver = inject(BreakpointObserver);\n private boundaryContext = inject(BsBoundaryContextService);\n private elementRef = inject(ElementRef);\n private destroy$ = new Subject<void>();\n\n // ============================================================\n // Public Properties\n // ============================================================\n\n /**\n * 實際設備方向(基於視口尺寸)\n * true: 直向 (height > width)\n * false: 橫向 (width >= height)\n */\n actualIsPortrait = false;\n\n // ============================================================\n // Getters\n // ============================================================\n\n /**\n * 判斷當前是否為橫向模式\n * 根據 orientation 設定和實際設備方向判斷\n */\n get isLandscape(): boolean {\n // landscapeLockRotation: 強制橫屏模式\n if (this.orientation === 'landscapeLockRotation') {\n return true;\n }\n // portraitLockRotation: 強制直屏模式\n if (this.orientation === 'portraitLockRotation') {\n return false;\n }\n // landscape: 鎖定橫向\n if (this.orientation === 'landscape') {\n return true;\n }\n // portrait: 鎖定直向\n if (this.orientation === 'portrait') {\n return false;\n }\n // auto: 自動檢測\n return this.boundaryContext.getIsLandscape();\n }\n\n // ============================================================\n // Lifecycle Hooks\n // ============================================================\n\n ngOnInit(): void {\n this.updateActualOrientation();\n this.setupBreakpointObserver();\n this.setupResizeObserver();\n }\n\n ngAfterViewInit(): void {\n // 在 view 初始化後計算(確保 CSS 已完全應用)\n this.updateBoundaryCalculations();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // ============================================================\n // Observers Setup - 監聽器設定\n // ============================================================\n\n /**\n * 設定方向變化監控\n *\n * 工作原理:\n * - lock 模式(portrait/landscape):直接設定固定方向,不監聽變化\n * - lockRotation 模式:設定固定方向,但需監聽實際設備方向以觸發旋轉\n * - auto 模式:使用 BreakpointObserver 監聽設備方向變化\n */\n private setupBreakpointObserver() {\n // landscape 或 landscapeLockRotation: 鎖定為橫向\n if (this.orientation === 'landscape' || this.orientation === 'landscapeLockRotation') {\n this.boundaryContext.setIsLandscape(true);\n this.orientationChange.emit(true);\n return;\n }\n\n // portrait 或 portraitLockRotation: 鎖定為直向\n if (this.orientation === 'portrait' || this.orientation === 'portraitLockRotation') {\n this.boundaryContext.setIsLandscape(false);\n this.orientationChange.emit(false);\n return;\n }\n\n // auto 模式:監聽實際的設備方向(用於 orientationChange 事件)\n this.breakpointObserver\n .observe('(orientation: landscape)')\n .pipe(takeUntil(this.destroy$))\n .subscribe(result => {\n this.boundaryContext.setIsLandscape(result.matches);\n this.orientationChange.emit(result.matches);\n // 方向變化時重新計算邊界\n this.updateBoundaryCalculations();\n });\n }\n\n /**\n * 設定視窗大小變化監控\n *\n * 功能:\n * - 監聽 window resize 事件\n * - 更新實際設備方向\n * - 重新計算邊界尺寸\n */\n private setupResizeObserver() {\n fromEvent(window, 'resize')\n .pipe(\n // debounceTime(150), // 可選:防抖優化性能\n takeUntil(this.destroy$)\n )\n .subscribe(() => {\n this.updateActualOrientation();\n this.updateBoundaryCalculations();\n });\n }\n\n // ============================================================\n // Calculation Methods - 計算方法\n // ============================================================\n\n /**\n * 更新實際設備方向(基於視口尺寸)\n *\n * 工作原理:\n * - 只在 lockRotation 模式下需要檢測實際方向\n * - 通過比較 window.innerHeight 和 window.innerWidth 判斷\n * - 結果用於觸發 CSS 旋轉變換\n */\n private updateActualOrientation() {\n // 只有在 lockRotation 模式下才需要檢測實際方向\n if (this.orientation !== 'landscapeLockRotation' && this.orientation !== 'portraitLockRotation') {\n this.actualIsPortrait = false;\n return;\n }\n\n // 檢測實際視口方向:高度 > 寬度 = 直向\n this.actualIsPortrait = window.innerHeight > window.innerWidth;\n }\n\n /**\n * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)\n *\n * 工作原理:\n * 1. 讀取基礎 CSS 變數(vw100, vh100, design-width, design-height)\n * 2. 處理 lockRotation 模式的視口尺寸交換\n * 3. 計算邊界最大值和實際邊界尺寸\n * 4. 計算 px2vw 轉換比例\n * 5. 設定 CSS 變數供樣式使用\n *\n * 設定的 CSS 變數:\n * - --boundary-width: 實際邊界寬度(px)\n * - --boundary-height: 實際邊界高度(px)\n * - --px2vw-ratio: px 轉 vw 的比例係數\n */\n private updateBoundaryCalculations() {\n const hostElement = this.elementRef.nativeElement as HTMLElement;\n const computedStyle = getComputedStyle(hostElement);\n\n // ========================================\n // Step 1: 讀取基礎 CSS 變數\n // ========================================\n let vw100 = parseFloat(computedStyle.getPropertyValue('--vw100')) || window.innerWidth;\n let vh100 = parseFloat(computedStyle.getPropertyValue('--vh100')) || window.innerHeight;\n const designWidth = parseFloat(computedStyle.getPropertyValue('--design-width'));\n const designHeight = parseFloat(computedStyle.getPropertyValue('--design-height'));\n\n // 檢查基礎變數是否準備好\n if (isNaN(designWidth)) {\n console.error('[BsBoundary][BS001] CSS variable --design-width is not set or invalid');\n return;\n }\n if (isNaN(designHeight)) {\n console.error('[BsBoundary][BS002] CSS variable --design-height is not set or invalid');\n return;\n }\n if (designWidth === 0) {\n console.error('[BsBoundary][BS003] CSS variable --design-width cannot be zero');\n return;\n }\n if (designHeight === 0) {\n console.error('[BsBoundary][BS004] CSS variable --design-height cannot be zero');\n return;\n }\n\n // ========================================\n // Step 2: 處理 lockRotation 模式的視口尺寸交換\n // ========================================\n // landscapeLockRotation + 實際直向:交換視口尺寸\n // 原因:旋轉後,內容的寬度對應設備的高度,內容的高度對應設備的寬度\n if (this.orientation === 'landscapeLockRotation' && this.actualIsPortrait) {\n [vw100, vh100] = [vh100, vw100];\n }\n\n // portraitLockRotation + 實際橫向:交換視口尺寸\n if (this.orientation === 'portraitLockRotation' && !this.actualIsPortrait) {\n [vw100, vh100] = [vh100, vw100];\n }\n\n // ========================================\n // Step 3: 計算比例和邊界最大值\n // ========================================\n const landscapeRatio = designWidth / designHeight;\n const portraitRatio = designHeight / designWidth;\n\n // 根據方向模式計算邊界最大值\n const isLandscape = this.isLandscape;\n const boundaryMaxWidth = isLandscape\n ? vh100 * portraitRatio // 橫向:高度 * 高寬比\n : vh100 * landscapeRatio; // 直向:高度 * 寬高比\n const boundaryMaxHeight = isLandscape\n ? vw100 * landscapeRatio // 橫向:寬度 * 寬高比\n : vw100 * portraitRatio; // 直向:寬度 * 高寬比\n\n // ========================================\n // Step 4: 計算實際邊界尺寸(使用 Math.min 模擬 CSS min())\n // ========================================\n // Chrome 79 才支援 CSS min() 函數,所以在 JavaScript 中計算\n const boundaryWidth = Math.min(vw100, boundaryMaxWidth);\n const boundaryHeight = Math.min(vh100, boundaryMaxHeight);\n\n // ========================================\n // Step 5: 計算 px2vw 轉換比例\n // ========================================\n // 橫向模式使用 design-height 作為基準,直向模式使用 design-width 作為基準\n const designBaseWidth = isLandscape ? designHeight : designWidth;\n const px2vwRatio = boundaryWidth / designBaseWidth;\n\n // ========================================\n // Step 6: 設定 CSS 變數\n // ========================================\n hostElement.style.setProperty('--boundary-width', `${boundaryWidth}px`);\n hostElement.style.setProperty('--boundary-height', `${boundaryHeight}px`);\n hostElement.style.setProperty('--px2vw-ratio', `${px2vwRatio}`);\n }\n}\n","<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n","/*\n * Public API Surface of @bitstack/ng-boundary\n */\n\nexport * from './lib/bs-boundary';\nexport * from './lib/bs-boundary-context.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bitstack-ng-boundary.mjs","sources":["../../../projects/bitstack-ng-boundary/src/lib/bs-boundary-context.service.ts","../../../projects/bitstack-ng-boundary/src/lib/bs-boundary.ts","../../../projects/bitstack-ng-boundary/src/lib/template.html","../../../projects/bitstack-ng-boundary/src/public-api.ts","../../../projects/bitstack-ng-boundary/src/bitstack-ng-boundary.ts"],"sourcesContent":["import {Injectable} from '@angular/core';\nimport {BehaviorSubject} from 'rxjs';\n\n@Injectable()\nexport class BsBoundaryContextService {\n private _isLandscape = new BehaviorSubject<boolean>(false);\n\n readonly isLandscape$ = this._isLandscape.asObservable();\n\n setIsLandscape(value: boolean): void {\n this._isLandscape.next(value);\n }\n\n getIsLandscape(): boolean {\n return this._isLandscape.value;\n }\n}\n","import {BreakpointObserver} from '@angular/cdk/layout';\nimport {\n AfterViewInit,\n Component,\n ElementRef,\n EventEmitter,\n inject,\n Input, OnDestroy,\n OnInit,\n Output,\n} from '@angular/core';\nimport {debounceTime, fromEvent, Subject, takeUntil} from 'rxjs';\n\nimport {BsBoundaryContextService} from './bs-boundary-context.service';\nimport {TOrientationMode} from './model';\n\n/**\n * ============================================================\n * BsBoundary Component - AWD 自適應邊界容器\n * ============================================================\n *\n * 功能說明:\n * 1. 提供響應式邊界容器,自動適應不同設備尺寸\n * 2. 支援 5 種方向模式:auto, portrait, landscape, portraitLockRotation, landscapeLockRotation\n * 3. 使用 CSS 變數實現動態計算,支援 px2vw() 函數\n * 4. 相容 Chrome 74+,使用 JavaScript 計算邊界尺寸\n *\n * 使用範例:\n * ```html\n * <bs-boundary [orientation]=\"'auto'\" [isFixedCenter]=\"true\">\n * <div class=\"content\">...</div>\n * </bs-boundary>\n * ```\n *\n * CSS 變數設定:\n * ```scss\n * bs-boundary {\n * --design-width: 375; // 設計稿寬度\n * --design-height: 667; // 設計稿高度\n * }\n * ```\n */\n@Component({\n selector: 'bs-boundary',\n templateUrl: './template.html',\n standalone: true,\n styleUrls: ['./styles.scss'],\n providers: [BsBoundaryContextService],\n host: {\n '[class.auto]': 'orientation === \"auto\"',\n '[class.landscape]': 'isLandscape && orientation === \"landscape\"',\n '[class.portrait]': '!isLandscape && orientation === \"portrait\"',\n '[class.landscape-lock-rotation]': 'orientation === \"landscapeLockRotation\"',\n '[class.portrait-lock-rotation]': 'orientation === \"portraitLockRotation\"',\n '[class.actual-portrait]': 'actualIsPortrait',\n '[class.actual-landscape]': '!actualIsPortrait'\n }\n})\nexport class BsBoundary implements OnInit, AfterViewInit, OnDestroy {\n\n // ============================================================\n // Inputs & Outputs\n // ============================================================\n\n /**\n * 是否將內容固定在視窗中央\n * @default false\n */\n @Input() isFixedCenter?: boolean = false;\n\n /**\n * 是否禁用指標事件\n * @default false\n */\n @Input() pointerEventsNone?: boolean = false;\n\n /**\n * 方向模式\n * - auto: 自動檢測方向\n * - portrait: 鎖定直向\n * - landscape: 鎖定橫向\n * - portraitLockRotation: 強制直向(橫向設備時旋轉 -90°)\n * - landscapeLockRotation: 強制橫向(直向設備時旋轉 90°)\n * @default 'auto'\n */\n @Input() orientation?: TOrientationMode = 'auto';\n\n /**\n * 向內層容器轉發的自訂樣式\n */\n @Input() forwardStyle?: Record<string, string>;\n\n /**\n * 設計稿寬度(單位:px)\n * 必填參數\n */\n @Input() designWidth!: number;\n\n /**\n * 設計稿高度(單位:px)\n * 必填參數\n */\n @Input() designHeight!: number;\n\n /**\n * 方向變化事件\n * @emits true: landscape(橫屏)\n * @emits false: portrait(直屏)\n */\n @Output() orientationChange = new EventEmitter<boolean>();\n\n // ============================================================\n // Private Dependencies\n // ============================================================\n\n private breakpointObserver = inject(BreakpointObserver);\n private boundaryContext = inject(BsBoundaryContextService);\n private elementRef = inject(ElementRef);\n private destroy$ = new Subject<void>();\n\n // ============================================================\n // Public Properties\n // ============================================================\n\n /**\n * 實際設備方向(基於視口尺寸)\n * true: 直向 (height > width)\n * false: 橫向 (width >= height)\n */\n actualIsPortrait = false;\n\n // ============================================================\n // Getters\n // ============================================================\n\n /**\n * 判斷當前是否為橫向模式\n * 根據 orientation 設定和實際設備方向判斷\n */\n get isLandscape(): boolean {\n // landscapeLockRotation: 強制橫屏模式\n if (this.orientation === 'landscapeLockRotation') {\n return true;\n }\n // portraitLockRotation: 強制直屏模式\n if (this.orientation === 'portraitLockRotation') {\n return false;\n }\n // landscape: 鎖定橫向\n if (this.orientation === 'landscape') {\n return true;\n }\n // portrait: 鎖定直向\n if (this.orientation === 'portrait') {\n return false;\n }\n // auto: 自動檢測\n return this.boundaryContext.getIsLandscape();\n }\n\n // ============================================================\n // Lifecycle Hooks\n // ============================================================\n\n ngOnInit(): void {\n this.updateActualOrientation();\n this.setupBreakpointObserver();\n this.setupResizeObserver();\n }\n\n ngAfterViewInit(): void {\n // 使用 @Input 參數,可以立即計算(無需等待 CSS)\n this.updateBoundaryCalculations();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // ============================================================\n // Observers Setup - 監聽器設定\n // ============================================================\n\n /**\n * 設定方向變化監控\n *\n * 工作原理:\n * - lock 模式(portrait/landscape):直接設定固定方向,不監聽變化\n * - lockRotation 模式:設定固定方向,但需監聽實際設備方向以觸發旋轉\n * - auto 模式:使用 BreakpointObserver 監聽設備方向變化\n */\n private setupBreakpointObserver() {\n // landscape 或 landscapeLockRotation: 鎖定為橫向\n if (this.orientation === 'landscape' || this.orientation === 'landscapeLockRotation') {\n this.boundaryContext.setIsLandscape(true);\n this.orientationChange.emit(true);\n return;\n }\n\n // portrait 或 portraitLockRotation: 鎖定為直向\n if (this.orientation === 'portrait' || this.orientation === 'portraitLockRotation') {\n this.boundaryContext.setIsLandscape(false);\n this.orientationChange.emit(false);\n return;\n }\n\n // auto 模式:監聽實際的設備方向(用於 orientationChange 事件)\n this.breakpointObserver\n .observe('(orientation: landscape)')\n .pipe(takeUntil(this.destroy$))\n .subscribe(result => {\n this.boundaryContext.setIsLandscape(result.matches);\n this.orientationChange.emit(result.matches);\n // 方向變化時重新計算邊界\n this.updateBoundaryCalculations();\n });\n }\n\n /**\n * 設定視窗大小變化監控\n *\n * 功能:\n * - 監聽 window resize 事件\n * - 更新實際設備方向\n * - 重新計算邊界尺寸\n */\n private setupResizeObserver() {\n fromEvent(window, 'resize')\n .pipe(\n // debounceTime(150), // 可選:防抖優化性能\n takeUntil(this.destroy$)\n )\n .subscribe(() => {\n this.updateActualOrientation();\n this.updateBoundaryCalculations();\n });\n }\n\n // ============================================================\n // Calculation Methods - 計算方法\n // ============================================================\n\n /**\n * 更新實際設備方向(基於視口尺寸)\n *\n * 工作原理:\n * - 只在 lockRotation 模式下需要檢測實際方向\n * - 通過比較 window.innerHeight 和 window.innerWidth 判斷\n * - 結果用於觸發 CSS 旋轉變換\n */\n private updateActualOrientation() {\n // 只有在 lockRotation 模式下才需要檢測實際方向\n if (this.orientation !== 'landscapeLockRotation' && this.orientation !== 'portraitLockRotation') {\n this.actualIsPortrait = false;\n return;\n }\n\n // 檢測實際視口方向:高度 > 寬度 = 直向\n this.actualIsPortrait = window.innerHeight > window.innerWidth;\n }\n\n /**\n * 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)\n *\n * 工作原理:\n * 1. 從 @Input 參數讀取設計尺寸\n * 2. 讀取視口尺寸(vw100, vh100)\n * 3. 處理 lockRotation 模式的視口尺寸交換\n * 4. 計算邊界最大值和實際邊界尺寸\n * 5. 計算 px2vw 轉換比例\n * 6. 設定 CSS 變數供樣式使用\n *\n * 設定的 CSS 變數:\n * - --design-width: 設計稿寬度\n * - --design-height: 設計稿高度\n * - --boundary-width: 實際邊界寬度(px)\n * - --boundary-height: 實際邊界高度(px)\n * - --px2vw-ratio: px 轉 vw 的比例係數\n */\n private updateBoundaryCalculations() {\n const hostElement = this.elementRef.nativeElement as HTMLElement;\n\n // ========================================\n // Step 1: 讀取設計尺寸並設定 CSS 變數\n // ========================================\n const designWidth = this.designWidth;\n const designHeight = this.designHeight;\n\n // 設定 CSS 變數,供樣式使用(例如 px2vw() 函數)\n hostElement.style.setProperty('--design-width', `${designWidth}`);\n hostElement.style.setProperty('--design-height', `${designHeight}`);\n\n // ========================================\n // Step 2: 讀取視口尺寸(直接使用 window.inner* 確保一致性)\n // ========================================\n let vw100 = window.innerWidth;\n let vh100 = window.innerHeight;\n\n // ========================================\n // Step 2: 處理 lockRotation 模式的視口尺寸交換\n // ========================================\n // landscapeLockRotation + 實際直向:交換視口尺寸\n // 原因:旋轉後,內容的寬度對應設備的高度,內容的高度對應設備的寬度\n if (this.orientation === 'landscapeLockRotation' && this.actualIsPortrait) {\n [vw100, vh100] = [vh100, vw100];\n }\n\n // portraitLockRotation + 實際橫向:交換視口尺寸\n if (this.orientation === 'portraitLockRotation' && !this.actualIsPortrait) {\n [vw100, vh100] = [vh100, vw100];\n }\n\n // ========================================\n // Step 3: 計算比例和邊界最大值\n // ========================================\n const landscapeRatio = designWidth / designHeight;\n const portraitRatio = designHeight / designWidth;\n\n // 根據方向模式計算邊界最大值\n const isLandscape = this.isLandscape;\n const boundaryMaxWidth = isLandscape\n ? vh100 * portraitRatio // 橫向:高度 * 高寬比\n : vh100 * landscapeRatio; // 直向:高度 * 寬高比\n const boundaryMaxHeight = isLandscape\n ? vw100 * landscapeRatio // 橫向:寬度 * 寬高比\n : vw100 * portraitRatio; // 直向:寬度 * 高寬比\n\n // ========================================\n // Step 4: 計算實際邊界尺寸(使用 Math.min 模擬 CSS min())\n // ========================================\n // Chrome 79 才支援 CSS min() 函數,所以在 JavaScript 中計算\n const boundaryWidth = Math.min(vw100, boundaryMaxWidth);\n const boundaryHeight = Math.min(vh100, boundaryMaxHeight);\n\n // ========================================\n // Step 5: 計算 px2vw 轉換比例\n // ========================================\n // 橫向模式使用 design-height 作為基準,直向模式使用 design-width 作為基準\n const designBaseWidth = isLandscape ? designHeight : designWidth;\n const px2vwRatio = boundaryWidth / designBaseWidth;\n\n // ========================================\n // Step 6: 設定 CSS 變數\n // ========================================\n hostElement.style.setProperty('--boundary-width', `${boundaryWidth}px`);\n hostElement.style.setProperty('--boundary-height', `${boundaryHeight}px`);\n hostElement.style.setProperty('--px2vw-ratio', `${px2vwRatio}`);\n }\n}\n","<div\n class=\"boundary-wrapper\"\n [class.fixed-center]=\"isFixedCenter\"\n [class.pointerEventsNone]=\"pointerEventsNone\"\n [style]=\"forwardStyle\"\n>\n <ng-content></ng-content>\n</div>\n","/*\n * Public API Surface of @bitstack/ng-boundary\n */\n\nexport * from './lib/bs-boundary';\nexport * from './lib/bs-boundary-context.service';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/lib/bs-boundary.d.ts
CHANGED
|
@@ -52,6 +52,16 @@ export declare class BsBoundary implements OnInit, AfterViewInit, OnDestroy {
|
|
|
52
52
|
* 向內層容器轉發的自訂樣式
|
|
53
53
|
*/
|
|
54
54
|
forwardStyle?: Record<string, string>;
|
|
55
|
+
/**
|
|
56
|
+
* 設計稿寬度(單位:px)
|
|
57
|
+
* 必填參數
|
|
58
|
+
*/
|
|
59
|
+
designWidth: number;
|
|
60
|
+
/**
|
|
61
|
+
* 設計稿高度(單位:px)
|
|
62
|
+
* 必填參數
|
|
63
|
+
*/
|
|
64
|
+
designHeight: number;
|
|
55
65
|
/**
|
|
56
66
|
* 方向變化事件
|
|
57
67
|
* @emits true: landscape(橫屏)
|
|
@@ -107,18 +117,21 @@ export declare class BsBoundary implements OnInit, AfterViewInit, OnDestroy {
|
|
|
107
117
|
* 更新邊界計算(支援 Chrome 74,使用 JavaScript 模擬 min 函數)
|
|
108
118
|
*
|
|
109
119
|
* 工作原理:
|
|
110
|
-
* 1.
|
|
111
|
-
* 2.
|
|
112
|
-
* 3.
|
|
113
|
-
* 4.
|
|
114
|
-
* 5.
|
|
120
|
+
* 1. 從 @Input 參數讀取設計尺寸
|
|
121
|
+
* 2. 讀取視口尺寸(vw100, vh100)
|
|
122
|
+
* 3. 處理 lockRotation 模式的視口尺寸交換
|
|
123
|
+
* 4. 計算邊界最大值和實際邊界尺寸
|
|
124
|
+
* 5. 計算 px2vw 轉換比例
|
|
125
|
+
* 6. 設定 CSS 變數供樣式使用
|
|
115
126
|
*
|
|
116
127
|
* 設定的 CSS 變數:
|
|
128
|
+
* - --design-width: 設計稿寬度
|
|
129
|
+
* - --design-height: 設計稿高度
|
|
117
130
|
* - --boundary-width: 實際邊界寬度(px)
|
|
118
131
|
* - --boundary-height: 實際邊界高度(px)
|
|
119
132
|
* - --px2vw-ratio: px 轉 vw 的比例係數
|
|
120
133
|
*/
|
|
121
134
|
private updateBoundaryCalculations;
|
|
122
135
|
static ɵfac: i0.ɵɵFactoryDeclaration<BsBoundary, never>;
|
|
123
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<BsBoundary, "bs-boundary", never, { "isFixedCenter": "isFixedCenter"; "pointerEventsNone": "pointerEventsNone"; "orientation": "orientation"; "forwardStyle": "forwardStyle"; }, { "orientationChange": "orientationChange"; }, never, ["*"], true>;
|
|
136
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<BsBoundary, "bs-boundary", never, { "isFixedCenter": "isFixedCenter"; "pointerEventsNone": "pointerEventsNone"; "orientation": "orientation"; "forwardStyle": "forwardStyle"; "designWidth": "designWidth"; "designHeight": "designHeight"; }, { "orientationChange": "orientationChange"; }, never, ["*"], true>;
|
|
124
137
|
}
|