@smartsoft001-mobilems/angular 2.78.0 → 2.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { __decorate, __metadata } from 'tslib';
2
2
  import * as i0 from '@angular/core';
3
- import { Injectable, inject, PLATFORM_ID, signal, effect, computed, input, output, HostListener, Component, ApplicationRef, EnvironmentInjector, createComponent, InjectionToken, Directive, contentChild, Inject, viewChild, ElementRef, APP_INITIALIZER, NgModule } from '@angular/core';
3
+ import { Injectable, inject, PLATFORM_ID, signal, effect, computed, input, output, HostListener, Component, ApplicationRef, EnvironmentInjector, createComponent, InjectionToken, Directive, contentChild, Inject, NgZone, viewChild, model, ElementRef, APP_INITIALIZER, NgModule } from '@angular/core';
4
4
  import { Meta, Title, DomSanitizer } from '@angular/platform-browser';
5
5
  import { StyleService as StyleService$1, ReduxAction, AppBaseComponent, PageBaseComponent, BaseComponent } from '@smartsoft001/angular';
6
6
  import { HttpClient } from '@angular/common/http';
@@ -14,10 +14,12 @@ import { TranslateService } from '@ngx-translate/core';
14
14
  import { CrudSearchService, CrudService, CrudFacade } from '@smartsoft001/crud-shell-angular';
15
15
  import * as _ from 'lodash';
16
16
  import { Debounce } from 'lodash-decorators';
17
+ import Swiper from 'swiper';
18
+ import { Navigation, Autoplay } from 'swiper/modules';
19
+ import { toSignal } from '@angular/core/rxjs-interop';
17
20
  import { ConfigPageType, ArticleWidgetType } from '@smartsoft001-mobilems/models';
18
21
  import { Masonry } from '@thisissoon/angular-masonry';
19
22
  import { capitalize } from '@smartsoft001/utils';
20
- import { toSignal } from '@angular/core/rxjs-interop';
21
23
 
22
24
  function setTranslationsAndLang(service) {
23
25
  const map = {
@@ -272,7 +274,9 @@ class FileUrlService {
272
274
  if (path[0] === '/') {
273
275
  path = path.substring(1);
274
276
  }
275
- const shouldSkipExtensionReplacement = isStringPath || path.includes('media/dictionaries') || path.includes('media/articles');
277
+ const shouldSkipExtensionReplacement = isStringPath ||
278
+ path.includes('media/dictionaries') ||
279
+ path.includes('media/articles');
276
280
  if (!shouldSkipExtensionReplacement) {
277
281
  path = this.replaceExtensionIfCache(path, mode);
278
282
  }
@@ -1565,6 +1569,12 @@ class FiltersContext extends BaseComponent {
1565
1569
  get authors() {
1566
1570
  return this.filterGet('=', 'authors');
1567
1571
  }
1572
+ set formFeature(val) {
1573
+ this.filterSet(this.formFeature, val, 'formFeature', '=');
1574
+ }
1575
+ get formFeature() {
1576
+ return this.filterGet('=', 'formFeature');
1577
+ }
1568
1578
  set targetGroups(val) {
1569
1579
  this.filterSet(this.targetGroups, val, 'targetGroups', '=');
1570
1580
  }
@@ -1589,6 +1599,12 @@ class FiltersContext extends BaseComponent {
1589
1599
  get extraNumbersValue() {
1590
1600
  return this.filterGet('=', 'extraNumbersValue');
1591
1601
  }
1602
+ set inventoryNumber(val) {
1603
+ this.filterSet(this.inventoryNumber, val, 'inventoryNumber', '=');
1604
+ }
1605
+ get inventoryNumber() {
1606
+ return this.filterGet('=', 'inventoryNumber');
1607
+ }
1592
1608
  set filters(filters) {
1593
1609
  this.multipleFilterSet(filters?.map(({ val, key, type }) => ({
1594
1610
  currentValue: this.filterGet(type, key),
@@ -1745,6 +1761,790 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImpor
1745
1761
  args: ['window:scroll', []]
1746
1762
  }] } });
1747
1763
 
1764
+ class SliderBaseComponent {
1765
+ constructor() {
1766
+ this.fileUrlService = inject(FileUrlService);
1767
+ this.slides = input([], ...(ngDevMode ? [{ debugName: "slides" }] : []));
1768
+ this.autoplayEnabled = signal(true, ...(ngDevMode ? [{ debugName: "autoplayEnabled" }] : []));
1769
+ this.windowWidth = signal(typeof window !== 'undefined' ? window.innerWidth : 1024, ...(ngDevMode ? [{ debugName: "windowWidth" }] : []));
1770
+ // Computed signal that recalculates images when windowWidth or slides change
1771
+ this.slideImages = computed(() => {
1772
+ const width = this.windowWidth();
1773
+ const slides = this.slides();
1774
+ return slides.map((slide) => {
1775
+ let imageSource;
1776
+ if (width < 768) {
1777
+ imageSource =
1778
+ slide.imageMobile || slide.imageTablet || slide.imageDesktop;
1779
+ }
1780
+ else if (width < 1024) {
1781
+ imageSource =
1782
+ slide.imageTablet || slide.imageDesktop || slide.imageMobile;
1783
+ }
1784
+ else {
1785
+ imageSource =
1786
+ slide.imageDesktop || slide.imageTablet || slide.imageMobile;
1787
+ }
1788
+ if (!imageSource) {
1789
+ return '/gfx/no-image.png';
1790
+ }
1791
+ return this.fileUrlService.get(imageSource);
1792
+ });
1793
+ }, ...(ngDevMode ? [{ debugName: "slideImages" }] : []));
1794
+ this.ngZone = inject(NgZone);
1795
+ this.swiperContainer = viewChild('swiperContainer', ...(ngDevMode ? [{ debugName: "swiperContainer" }] : []));
1796
+ effect(() => {
1797
+ const slides = this.slides();
1798
+ if (this.swiper && slides.length > 0) {
1799
+ this.swiper.update();
1800
+ }
1801
+ });
1802
+ }
1803
+ ngOnInit() {
1804
+ this.initSwiper();
1805
+ // Listen to window resize to recreate swiper completely
1806
+ this.resizeListener = () => {
1807
+ this.ngZone.run(() => {
1808
+ this.windowWidth.set(window.innerWidth);
1809
+ this.recreateSwiper();
1810
+ });
1811
+ };
1812
+ window.addEventListener('resize', this.resizeListener);
1813
+ }
1814
+ ngOnDestroy() {
1815
+ if (this.resizeListener && typeof window !== 'undefined') {
1816
+ window.removeEventListener('resize', this.resizeListener);
1817
+ }
1818
+ if (this.swiper) {
1819
+ this.swiper.destroy(true, true);
1820
+ }
1821
+ }
1822
+ toggleAutoplay() {
1823
+ this.autoplayEnabled.update((current) => !current);
1824
+ if (this.swiper) {
1825
+ if (this.autoplayEnabled()) {
1826
+ this.swiper.params.autoplay = {
1827
+ delay: 5000,
1828
+ disableOnInteraction: false,
1829
+ };
1830
+ this.swiper.autoplay.start();
1831
+ }
1832
+ else {
1833
+ this.swiper.autoplay.stop();
1834
+ }
1835
+ }
1836
+ }
1837
+ getImage(slide) {
1838
+ let imageSource;
1839
+ if (typeof window !== 'undefined') {
1840
+ const width = window.innerWidth;
1841
+ if (width < 768) {
1842
+ imageSource =
1843
+ slide.imageMobile || slide.imageTablet || slide.imageDesktop;
1844
+ }
1845
+ else if (width < 1024) {
1846
+ imageSource =
1847
+ slide.imageTablet || slide.imageDesktop || slide.imageMobile;
1848
+ }
1849
+ else {
1850
+ imageSource =
1851
+ slide.imageDesktop || slide.imageTablet || slide.imageMobile;
1852
+ }
1853
+ }
1854
+ else {
1855
+ imageSource =
1856
+ slide.imageDesktop || slide.imageTablet || slide.imageMobile;
1857
+ }
1858
+ // Return the image source directly if it's a URL, otherwise use FileUrlService
1859
+ if (!imageSource) {
1860
+ return '/gfx/no-image.png';
1861
+ }
1862
+ return this.fileUrlService.get(imageSource);
1863
+ }
1864
+ initSwiper() {
1865
+ const container = this.swiperContainer();
1866
+ if (!container)
1867
+ return;
1868
+ // if (isPlatformServer(this.platformId)) return;
1869
+ this.swiper = new Swiper(container.nativeElement, {
1870
+ modules: [Navigation, Autoplay],
1871
+ loop: true,
1872
+ autoplay: {
1873
+ delay: 5000,
1874
+ disableOnInteraction: false,
1875
+ },
1876
+ navigation: {
1877
+ nextEl: '.swiper-button-next',
1878
+ prevEl: '.swiper-button-prev',
1879
+ },
1880
+ slidesPerView: 1,
1881
+ spaceBetween: 0,
1882
+ });
1883
+ }
1884
+ recreateSwiper() {
1885
+ const container = this.swiperContainer();
1886
+ if (!container)
1887
+ return;
1888
+ // Destroy existing swiper
1889
+ if (this.swiper) {
1890
+ this.swiper.destroy(true, true);
1891
+ this.swiper = undefined;
1892
+ }
1893
+ // Small delay to ensure DOM is updated after signal change
1894
+ setTimeout(() => {
1895
+ this.createSwiper(container.nativeElement);
1896
+ }, 0);
1897
+ }
1898
+ createSwiper(element) {
1899
+ this.swiper = new Swiper(element, {
1900
+ modules: [Navigation, Autoplay],
1901
+ loop: true,
1902
+ autoplay: {
1903
+ delay: 5000,
1904
+ disableOnInteraction: false,
1905
+ },
1906
+ navigation: {
1907
+ nextEl: '.swiper-button-next',
1908
+ prevEl: '.swiper-button-prev',
1909
+ },
1910
+ slidesPerView: 1,
1911
+ spaceBetween: 0,
1912
+ });
1913
+ }
1914
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: SliderBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1915
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "20.1.0", type: SliderBaseComponent, isStandalone: true, inputs: { slides: { classPropertyName: "slides", publicName: "slides", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "swiperContainer", first: true, predicate: ["swiperContainer"], descendants: true, isSignal: true }], ngImport: i0 }); }
1916
+ }
1917
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: SliderBaseComponent, decorators: [{
1918
+ type: Directive
1919
+ }], ctorParameters: () => [] });
1920
+
1921
+ class SearchBaseComponent {
1922
+ constructor() {
1923
+ this.route = inject(ActivatedRoute);
1924
+ this.router = inject(Router);
1925
+ this.service = inject(SearchService);
1926
+ this.params = toSignal(this.route.params);
1927
+ this.text = signal('', ...(ngDevMode ? [{ debugName: "text" }] : []));
1928
+ this.data = signal(null, ...(ngDevMode ? [{ debugName: "data" }] : []));
1929
+ this.tabs = signal([], ...(ngDevMode ? [{ debugName: "tabs" }] : []));
1930
+ this.searchMap = {
1931
+ fallbackId: 'obiekty',
1932
+ fallbackLabel: 'Obiekty',
1933
+ fallbackProperty: 'objects',
1934
+ searchTabs: [],
1935
+ };
1936
+ this.activeTab = signal(this.searchMap.fallbackId, ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
1937
+ effect(async () => {
1938
+ const { text } = this.params();
1939
+ this.text.set(text);
1940
+ const data = await firstValueFrom(this.service.search(text ? text : '_'));
1941
+ let path = this.searchMap.fallbackId;
1942
+ const tabs = [];
1943
+ for (const tab of this.searchMap.searchTabs) {
1944
+ if (data[tab.property]) {
1945
+ tabs.push({
1946
+ id: tab.id,
1947
+ label: `${tab.label} (${data[tab.property]})`,
1948
+ });
1949
+ }
1950
+ }
1951
+ if (!tabs.length) {
1952
+ tabs.push({
1953
+ id: this.searchMap.fallbackId,
1954
+ label: `${this.searchMap.fallbackLabel} (${data[this.searchMap.fallbackProperty]})`,
1955
+ });
1956
+ }
1957
+ for (const tab of this.searchMap.searchTabs) {
1958
+ if (data[tab.property]) {
1959
+ path = tab.id;
1960
+ break;
1961
+ }
1962
+ }
1963
+ this.data.set(data);
1964
+ this.tabs.set(tabs);
1965
+ this.activeTab.set(path);
1966
+ await this.router.navigate([`/wyszukaj/${text}/${path}`]);
1967
+ });
1968
+ effect(() => {
1969
+ const activeTab = this.activeTab();
1970
+ const text = this.text();
1971
+ this.router.navigate([`/wyszukaj/${text}/${activeTab}`]);
1972
+ });
1973
+ }
1974
+ ngOnInit() {
1975
+ this.service.setEnabled(true);
1976
+ }
1977
+ ngOnDestroy() {
1978
+ this.service.setEnabled(false);
1979
+ }
1980
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: SearchBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1981
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.1.0", type: SearchBaseComponent, isStandalone: true, ngImport: i0 }); }
1982
+ }
1983
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: SearchBaseComponent, decorators: [{
1984
+ type: Directive
1985
+ }], ctorParameters: () => [] });
1986
+
1987
+ class MediaTabBaseComponent {
1988
+ constructor() {
1989
+ this.item = input.required(...(ngDevMode ? [{ debugName: "item" }] : []));
1990
+ this.formFeatureTypes = input.required(...(ngDevMode ? [{ debugName: "formFeatureTypes" }] : []));
1991
+ this.models = computed(() => {
1992
+ const obj = this.item();
1993
+ let filtered = [];
1994
+ if (obj.additionalImages && Array.isArray(obj.additionalImages)) {
1995
+ filtered = obj.additionalImages
1996
+ .filter((file) => file?.formFeature
1997
+ ? this.formFeatureTypes().includes(file.formFeature)
1998
+ : false)
1999
+ .sort((a, b) => (a?.position ?? 0) - (b?.position ?? 0));
2000
+ }
2001
+ return filtered;
2002
+ }, ...(ngDevMode ? [{ debugName: "models" }] : []));
2003
+ }
2004
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: MediaTabBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2005
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.1.0", type: MediaTabBaseComponent, isStandalone: true, inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, formFeatureTypes: { classPropertyName: "formFeatureTypes", publicName: "formFeatureTypes", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 }); }
2006
+ }
2007
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: MediaTabBaseComponent, decorators: [{
2008
+ type: Directive
2009
+ }] });
2010
+
2011
+ class GalleryFullscreenBaseComponent {
2012
+ constructor() {
2013
+ this.fileUrlService = inject(FileUrlService);
2014
+ this.document = inject(DOCUMENT);
2015
+ this.ngZone = inject(NgZone);
2016
+ this.items = input(...(ngDevMode ? [undefined, { debugName: "items" }] : []));
2017
+ this.alt = input('', ...(ngDevMode ? [{ debugName: "alt" }] : []));
2018
+ this.id = input('imagePreview', ...(ngDevMode ? [{ debugName: "id" }] : []));
2019
+ this.selectedIndex = model(0, ...(ngDevMode ? [{ debugName: "selectedIndex" }] : []));
2020
+ this.closeChange = output();
2021
+ // Touch pinch gesture tracking
2022
+ this.touchStartDistance = 0;
2023
+ this.touchStartZoom = 1;
2024
+ // Pinch midpoint (client coords) and transform at start of pinch
2025
+ this.pinchStartClientX = 0;
2026
+ this.pinchStartClientY = 0;
2027
+ this.pinchStartTransformX = 0;
2028
+ this.pinchStartTransformY = 0;
2029
+ // Pan/drag gesture tracking
2030
+ this.isPanning = signal(false, ...(ngDevMode ? [{ debugName: "isPanning" }] : []));
2031
+ this.panStartX = 0;
2032
+ this.panStartY = 0;
2033
+ this.panStartTransformX = 0;
2034
+ this.panStartTransformY = 0;
2035
+ this.touchStartedOnButton = false;
2036
+ this.canGoPrevious = computed(() => this.fullscreenIndex() > 0, ...(ngDevMode ? [{ debugName: "canGoPrevious" }] : []));
2037
+ this.canGoNext = computed(() => {
2038
+ const items = this.items();
2039
+ return items && this.fullscreenIndex() < items.length - 1;
2040
+ }, ...(ngDevMode ? [{ debugName: "canGoNext" }] : []));
2041
+ // Fullscreen state
2042
+ this.fullscreenIndex = signal(0, ...(ngDevMode ? [{ debugName: "fullscreenIndex" }] : []));
2043
+ this.zoomLevel = signal(1, ...(ngDevMode ? [{ debugName: "zoomLevel" }] : []));
2044
+ this.rotation = signal(0, ...(ngDevMode ? [{ debugName: "rotation" }] : []));
2045
+ this.transform = computed(() => {
2046
+ const trans = this.imageTransform();
2047
+ return `
2048
+ translate(${trans.x}px, ${trans.y}px)
2049
+ scale(${this.zoomLevel()})
2050
+ rotate(${this.rotation()}deg)
2051
+ `;
2052
+ }, ...(ngDevMode ? [{ debugName: "transform" }] : []));
2053
+ this.imageTransform = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "imageTransform" }] : []));
2054
+ // Loading indicator for fullscreen image
2055
+ this.isFullscreenLoading = signal(true, ...(ngDevMode ? [{ debugName: "isFullscreenLoading" }] : []));
2056
+ this.fullscreenItem = computed(() => {
2057
+ const items = this.items();
2058
+ const index = this.fullscreenIndex();
2059
+ if (!items)
2060
+ return null;
2061
+ return items[index] || null;
2062
+ }, ...(ngDevMode ? [{ debugName: "fullscreenItem" }] : []));
2063
+ // OpenSeadragon viewer instance
2064
+ this.osdViewer = null;
2065
+ this.openseadragonModule = null;
2066
+ this.osdContainerId = computed(() => `osd-viewer-${this.id()}`, ...(ngDevMode ? [{ debugName: "osdContainerId" }] : []));
2067
+ // Reset fullscreen zoom when changing images
2068
+ effect(() => {
2069
+ this.fullscreenIndex();
2070
+ this.zoomLevel.set(1);
2071
+ this.imageTransform.set({ x: 0, y: 0 });
2072
+ this.rotation.set(0);
2073
+ // Load new image in OpenSeadragon if viewer exists
2074
+ if (this.osdViewer) {
2075
+ const item = this.fullscreenItem();
2076
+ if (item?.image) {
2077
+ this.loadOsdImage(item.image);
2078
+ }
2079
+ }
2080
+ });
2081
+ }
2082
+ ngAfterViewInit() {
2083
+ this.initFullscreen();
2084
+ }
2085
+ ngOnDestroy() {
2086
+ this.stopLoading();
2087
+ this.zoomLevel.set(1);
2088
+ this.imageTransform.set({ x: 0, y: 0 });
2089
+ this.document.body.classList.remove('smart-gallery-fullscreen-active');
2090
+ // Destroy OpenSeadragon viewer
2091
+ if (this.osdViewer) {
2092
+ this.ngZone.runOutsideAngular(() => {
2093
+ try {
2094
+ this.osdViewer.destroy();
2095
+ }
2096
+ catch (e) {
2097
+ // Ignore errors
2098
+ }
2099
+ });
2100
+ this.osdViewer = null;
2101
+ }
2102
+ }
2103
+ async initFullscreen() {
2104
+ this.fullscreenIndex.set(this.selectedIndex());
2105
+ this.document.body.classList.add('smart-gallery-fullscreen-active');
2106
+ const item = this.fullscreenItem();
2107
+ if (!item?.image)
2108
+ return;
2109
+ try {
2110
+ // Lazy load OpenSeadragon
2111
+ if (!this.openseadragonModule) {
2112
+ this.openseadragonModule = await import('openseadragon');
2113
+ }
2114
+ // Wait for template to render
2115
+ await new Promise((resolve) => setTimeout(resolve, 0));
2116
+ const container = this.document.getElementById(this.osdContainerId());
2117
+ if (!container) {
2118
+ this.startLoading();
2119
+ return;
2120
+ }
2121
+ // Create viewer outside Angular zone for better performance
2122
+ if (!this.osdViewer) {
2123
+ this.osdViewer = this.ngZone.runOutsideAngular(() => {
2124
+ const viewer = this.openseadragonModule.default({
2125
+ element: container,
2126
+ prefixUrl: 'https://openseadragon.github.io/openseadragon/images/',
2127
+ animationTime: 0.5,
2128
+ blendTime: 0.1,
2129
+ constrainDuringPan: true,
2130
+ maxZoomPixelRatio: 2,
2131
+ minZoomLevel: 0,
2132
+ visibilityRatio: 1,
2133
+ zoomPerScroll: 1.2,
2134
+ showNavigator: false,
2135
+ showNavigationControl: false,
2136
+ gestureSettingsMouse: {
2137
+ scrollToZoom: true,
2138
+ clickToZoom: false,
2139
+ dblClickToZoom: true,
2140
+ pinchToZoom: true,
2141
+ flickEnabled: true,
2142
+ flickMinSpeed: 0.5,
2143
+ flickMomentum: 0.25,
2144
+ },
2145
+ gestureSettingsTouch: {
2146
+ scrollToZoom: false,
2147
+ clickToZoom: false,
2148
+ dblClickToZoom: true,
2149
+ pinchToZoom: true,
2150
+ flickEnabled: true,
2151
+ flickMinSpeed: 0.5,
2152
+ flickMomentum: 0.25,
2153
+ },
2154
+ });
2155
+ // Sync zoom level with component state (run inside Angular zone)
2156
+ viewer.addHandler('open', () => {
2157
+ this.ngZone.run(() => {
2158
+ this.stopLoading();
2159
+ try {
2160
+ const viewport = viewer.viewport;
2161
+ viewport.setRotation(0);
2162
+ viewport.goHome(true);
2163
+ this.rotation.set(0);
2164
+ const homeZoom = viewer.viewport.getHomeZoom();
2165
+ // Dynamically set minZoomLevel to 50% of home zoom
2166
+ // This ensures all images can zoom out to exactly 50% regardless of aspect ratio
2167
+ viewer.viewport.minZoomLevel = homeZoom * 0.5;
2168
+ const currentZoom = viewer.viewport.getZoom();
2169
+ // Normalize: 100% = fit to screen (home zoom)
2170
+ const normalizedZoom = currentZoom / homeZoom;
2171
+ this.zoomLevel.set(normalizedZoom);
2172
+ }
2173
+ catch (e) {
2174
+ this.zoomLevel.set(1);
2175
+ this.rotation.set(0);
2176
+ }
2177
+ });
2178
+ });
2179
+ viewer.addHandler('zoom', () => {
2180
+ this.ngZone.run(() => {
2181
+ try {
2182
+ const homeZoom = viewer.viewport.getHomeZoom();
2183
+ const currentZoom = viewer.viewport.getZoom();
2184
+ const normalizedZoom = currentZoom / homeZoom;
2185
+ this.zoomLevel.set(normalizedZoom);
2186
+ }
2187
+ catch (e) {
2188
+ // Ignore errors
2189
+ }
2190
+ });
2191
+ });
2192
+ return viewer;
2193
+ });
2194
+ }
2195
+ // Load image into OpenSeadragon
2196
+ await this.loadOsdImage(item.image);
2197
+ }
2198
+ catch (error) {
2199
+ console.error('Failed to initialize OpenSeadragon:', error);
2200
+ this.startLoading();
2201
+ }
2202
+ }
2203
+ // Called from template when fullscreen image loads or fails
2204
+ onFullscreenImageLoad() {
2205
+ this.stopLoading();
2206
+ }
2207
+ startLoading() {
2208
+ this.isFullscreenLoading.set(true);
2209
+ }
2210
+ stopLoading() {
2211
+ this.isFullscreenLoading.set(false);
2212
+ }
2213
+ closeFullscreen() {
2214
+ this.closeChange.emit();
2215
+ }
2216
+ rotateRight() {
2217
+ this.rotation.update((r) => (r + 90) % 360);
2218
+ if (this.osdViewer) {
2219
+ this.ngZone.runOutsideAngular(() => {
2220
+ try {
2221
+ const viewport = this.osdViewer.viewport;
2222
+ viewport.setRotation(this.rotation());
2223
+ viewport.applyConstraints();
2224
+ }
2225
+ catch (e) {
2226
+ return;
2227
+ }
2228
+ });
2229
+ }
2230
+ }
2231
+ handleKeyboardEvent(event) {
2232
+ switch (event.key) {
2233
+ case 'Escape':
2234
+ this.closeFullscreen();
2235
+ event.preventDefault();
2236
+ break;
2237
+ case 'ArrowLeft':
2238
+ this.previousImage();
2239
+ event.preventDefault();
2240
+ break;
2241
+ case 'ArrowRight':
2242
+ this.nextImage();
2243
+ event.preventDefault();
2244
+ break;
2245
+ case '+':
2246
+ case '=':
2247
+ this.zoomIn();
2248
+ event.preventDefault();
2249
+ break;
2250
+ case '-':
2251
+ this.zoomOut();
2252
+ event.preventDefault();
2253
+ break;
2254
+ case '0':
2255
+ this.resetZoom();
2256
+ event.preventDefault();
2257
+ break;
2258
+ }
2259
+ }
2260
+ handleTouchStart(event) {
2261
+ // Check if touch started on a button
2262
+ const target = event.target;
2263
+ this.touchStartedOnButton = !!target.closest('button');
2264
+ if (this.touchStartedOnButton)
2265
+ return true;
2266
+ // If OpenSeadragon is active, let it handle gestures
2267
+ if (this.osdViewer)
2268
+ return false;
2269
+ event.preventDefault();
2270
+ event.stopPropagation();
2271
+ // Handle pinch gesture (two fingers)
2272
+ if (event.touches.length === 2) {
2273
+ const touch1 = event.touches[0];
2274
+ const touch2 = event.touches[1];
2275
+ this.touchStartDistance = this.getDistance(touch1, touch2);
2276
+ this.touchStartZoom = this.zoomLevel();
2277
+ this.isPanning.set(false);
2278
+ // store pinch midpoint (client coordinates) and current transform to keep the midpoint fixed while scaling
2279
+ const midX = (touch1.clientX + touch2.clientX) / 2;
2280
+ const midY = (touch1.clientY + touch2.clientY) / 2;
2281
+ this.pinchStartClientX = midX;
2282
+ this.pinchStartClientY = midY;
2283
+ this.pinchStartTransformX = this.imageTransform().x;
2284
+ this.pinchStartTransformY = this.imageTransform().y;
2285
+ }
2286
+ else if (event.touches.length === 1 && this.zoomLevel() > 1) {
2287
+ // Handle pan gesture only if zoomed in
2288
+ this.isPanning.set(true);
2289
+ this.panStartX = event.touches[0].clientX;
2290
+ this.panStartY = event.touches[0].clientY;
2291
+ this.panStartTransformX = this.imageTransform().x;
2292
+ this.panStartTransformY = this.imageTransform().y;
2293
+ }
2294
+ return false;
2295
+ }
2296
+ handleTouchMove(event) {
2297
+ if (this.touchStartedOnButton)
2298
+ return true;
2299
+ // If OpenSeadragon is active, let it handle gestures
2300
+ if (this.osdViewer)
2301
+ return false;
2302
+ event.preventDefault();
2303
+ event.stopPropagation();
2304
+ // Handle pinch zoom (two fingers)
2305
+ if (event.touches.length === 2 && this.touchStartDistance > 0) {
2306
+ const touch1 = event.touches[0];
2307
+ const touch2 = event.touches[1];
2308
+ const currentDistance = this.getDistance(touch1, touch2);
2309
+ const scale = currentDistance / this.touchStartDistance;
2310
+ const newZoom = Math.max(0.5, Math.min(5, this.touchStartZoom * scale));
2311
+ // adjust transform so the pinch midpoint remains under the same screen point
2312
+ const el = this.document.getElementById(this.id());
2313
+ if (el) {
2314
+ const rect = el.getBoundingClientRect();
2315
+ // relative point inside the element (in element pixels) at start of pinch
2316
+ const relX = (this.pinchStartClientX - rect.left - this.pinchStartTransformX) /
2317
+ this.touchStartZoom;
2318
+ const relY = (this.pinchStartClientY - rect.top - this.pinchStartTransformY) /
2319
+ this.touchStartZoom;
2320
+ const newTx = this.pinchStartClientX - rect.left - relX * newZoom;
2321
+ const newTy = this.pinchStartClientY - rect.top - relY * newZoom;
2322
+ this.imageTransform.set({ x: newTx, y: newTy });
2323
+ }
2324
+ this.zoomLevel.set(newZoom);
2325
+ }
2326
+ // Handle pan/drag (one finger while zoomed)
2327
+ else if (event.touches.length === 1 &&
2328
+ this.isPanning() &&
2329
+ this.zoomLevel() > 1) {
2330
+ const deltaX = event.touches[0].clientX - this.panStartX;
2331
+ const deltaY = event.touches[0].clientY - this.panStartY;
2332
+ this.imageTransform.set({
2333
+ x: this.panStartTransformX + deltaX,
2334
+ y: this.panStartTransformY + deltaY,
2335
+ });
2336
+ }
2337
+ return false;
2338
+ }
2339
+ handleTouchEnd() {
2340
+ this.touchStartDistance = 0;
2341
+ this.touchStartZoom = 1;
2342
+ this.isPanning.set(false);
2343
+ }
2344
+ handleMouseDown(event) {
2345
+ if (this.zoomLevel() <= 1)
2346
+ return false;
2347
+ event.preventDefault();
2348
+ this.isPanning.set(true);
2349
+ this.panStartX = event.clientX;
2350
+ this.panStartY = event.clientY;
2351
+ this.panStartTransformX = this.imageTransform().x;
2352
+ this.panStartTransformY = this.imageTransform().y;
2353
+ return false;
2354
+ }
2355
+ handleMouseMove(event) {
2356
+ if (!this.isPanning() || this.zoomLevel() <= 1)
2357
+ return false;
2358
+ event.preventDefault();
2359
+ const deltaX = event.clientX - this.panStartX;
2360
+ const deltaY = event.clientY - this.panStartY;
2361
+ requestAnimationFrame(() => {
2362
+ this.imageTransform.set({
2363
+ x: this.panStartTransformX + deltaX,
2364
+ y: this.panStartTransformY + deltaY,
2365
+ });
2366
+ });
2367
+ return false;
2368
+ }
2369
+ handleMouseUp() {
2370
+ this.isPanning.set(false);
2371
+ }
2372
+ handleWheel(event) {
2373
+ // If OpenSeadragon is active, let it handle wheel events
2374
+ if (this.osdViewer) {
2375
+ event.preventDefault();
2376
+ event.stopPropagation();
2377
+ return false;
2378
+ }
2379
+ return false;
2380
+ }
2381
+ previousImage() {
2382
+ if (this.canGoPrevious()) {
2383
+ // start loading when navigating
2384
+ this.startLoading();
2385
+ this.fullscreenIndex.update((i) => i - 1);
2386
+ }
2387
+ }
2388
+ nextImage() {
2389
+ if (this.canGoNext()) {
2390
+ // start loading when navigating
2391
+ this.startLoading();
2392
+ this.fullscreenIndex.update((i) => i + 1);
2393
+ }
2394
+ }
2395
+ selectThumbnail(index) {
2396
+ if (index === this.fullscreenIndex()) {
2397
+ // same index — no navigation, but ensure loading is false
2398
+ this.stopLoading();
2399
+ return;
2400
+ }
2401
+ // start loading when selecting thumbnail
2402
+ this.startLoading();
2403
+ this.fullscreenIndex.set(index);
2404
+ }
2405
+ zoomIn() {
2406
+ if (this.osdViewer) {
2407
+ this.ngZone.runOutsideAngular(() => {
2408
+ try {
2409
+ const viewport = this.osdViewer.viewport;
2410
+ viewport.zoomBy(1.2);
2411
+ viewport.applyConstraints();
2412
+ this.ngZone.run(() => {
2413
+ const homeZoom = viewport.getHomeZoom();
2414
+ const currentZoom = viewport.getZoom();
2415
+ this.zoomLevel.set(currentZoom / homeZoom);
2416
+ });
2417
+ }
2418
+ catch (e) {
2419
+ // Fallback to manual zoom
2420
+ this.ngZone.run(() => {
2421
+ this.zoomLevel.update((z) => Math.min(z + 0.5, 5));
2422
+ });
2423
+ }
2424
+ });
2425
+ return;
2426
+ }
2427
+ this.zoomLevel.update((z) => Math.min(z + 0.5, 5));
2428
+ }
2429
+ zoomOut() {
2430
+ if (this.osdViewer) {
2431
+ this.ngZone.runOutsideAngular(() => {
2432
+ try {
2433
+ const viewport = this.osdViewer.viewport;
2434
+ viewport.zoomBy(1 / 1.2);
2435
+ viewport.applyConstraints();
2436
+ this.ngZone.run(() => {
2437
+ const homeZoom = viewport.getHomeZoom();
2438
+ const currentZoom = viewport.getZoom();
2439
+ this.zoomLevel.set(currentZoom / homeZoom);
2440
+ });
2441
+ }
2442
+ catch (e) {
2443
+ // Fallback to manual zoom
2444
+ this.ngZone.run(() => {
2445
+ this.zoomLevel.update((z) => Math.max(z - 0.5, 0.5));
2446
+ });
2447
+ }
2448
+ });
2449
+ return;
2450
+ }
2451
+ this.zoomLevel.update((z) => Math.max(z - 0.5, 0.5));
2452
+ }
2453
+ resetZoom() {
2454
+ this.rotation.set(0);
2455
+ if (this.osdViewer) {
2456
+ this.ngZone.runOutsideAngular(() => {
2457
+ try {
2458
+ const viewport = this.osdViewer.viewport;
2459
+ // First reset rotation, then go home - order matters!
2460
+ viewport.setRotation(0);
2461
+ viewport.applyConstraints();
2462
+ viewport.goHome(true);
2463
+ viewport.applyConstraints();
2464
+ this.ngZone.run(() => {
2465
+ this.zoomLevel.set(1); // Home = 100%
2466
+ });
2467
+ }
2468
+ catch (e) {
2469
+ // Fallback to manual reset
2470
+ this.ngZone.run(() => {
2471
+ this.zoomLevel.set(1);
2472
+ this.imageTransform.set({ x: 0, y: 0 });
2473
+ });
2474
+ }
2475
+ });
2476
+ return;
2477
+ }
2478
+ this.zoomLevel.set(1);
2479
+ this.imageTransform.set({ x: 0, y: 0 });
2480
+ }
2481
+ async downloadImage() {
2482
+ const item = this.fullscreenItem();
2483
+ if (!item?.image)
2484
+ return;
2485
+ const url = this.fileUrlService.get(item.image, 'noCache');
2486
+ const filename = `${item.image.id || 'image'}.${item.image.extension || 'jpg'}`;
2487
+ try {
2488
+ const response = await fetch(url);
2489
+ const blob = await response.blob();
2490
+ const blobUrl = URL.createObjectURL(blob);
2491
+ const link = this.document.createElement('a');
2492
+ link.href = blobUrl;
2493
+ link.download = filename;
2494
+ link.click();
2495
+ URL.revokeObjectURL(blobUrl);
2496
+ }
2497
+ catch (error) {
2498
+ console.error('Failed to download image:', error);
2499
+ }
2500
+ }
2501
+ async loadOsdImage(image) {
2502
+ if (!this.osdViewer)
2503
+ return;
2504
+ const url = this.fileUrlService.get(image, 'noCache');
2505
+ this.ngZone.runOutsideAngular(() => {
2506
+ try {
2507
+ // Close any existing image
2508
+ try {
2509
+ this.osdViewer.close();
2510
+ }
2511
+ catch (e) {
2512
+ // Ignore errors
2513
+ }
2514
+ // Open the new image
2515
+ this.osdViewer.open({
2516
+ type: 'image',
2517
+ url: url,
2518
+ });
2519
+ }
2520
+ catch (error) {
2521
+ console.error('Failed to load image in OpenSeadragon:', error);
2522
+ this.ngZone.run(() => {
2523
+ this.stopLoading();
2524
+ });
2525
+ }
2526
+ });
2527
+ }
2528
+ getDistance(touch1, touch2) {
2529
+ const dx = touch1.clientX - touch2.clientX;
2530
+ const dy = touch1.clientY - touch2.clientY;
2531
+ return Math.sqrt(dx * dx + dy * dy);
2532
+ }
2533
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: GalleryFullscreenBaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2534
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.1.0", type: GalleryFullscreenBaseComponent, isStandalone: true, inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, selectedIndex: { classPropertyName: "selectedIndex", publicName: "selectedIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedIndex: "selectedIndexChange", closeChange: "closeChange" }, host: { listeners: { "document:keydown": "handleKeyboardEvent($event)", "mousedown": "handleMouseDown($event)", "mousemove": "handleMouseMove($event)", "mouseup": "handleMouseUp()" } }, ngImport: i0 }); }
2535
+ }
2536
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImport: i0, type: GalleryFullscreenBaseComponent, decorators: [{
2537
+ type: Directive,
2538
+ args: [{
2539
+ host: {
2540
+ '(document:keydown)': 'handleKeyboardEvent($event)',
2541
+ '(mousedown)': 'handleMouseDown($event)',
2542
+ '(mousemove)': 'handleMouseMove($event)',
2543
+ '(mouseup)': 'handleMouseUp()',
2544
+ },
2545
+ }]
2546
+ }], ctorParameters: () => [] });
2547
+
1748
2548
  class FooterComponent {
1749
2549
  constructor() {
1750
2550
  this.configsFacade = inject(ConfigsFacade);
@@ -2545,5 +3345,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.0", ngImpor
2545
3345
  * Generated bundle index. Do not edit.
2546
3346
  */
2547
3347
 
2548
- export { ARTICLES_WIDGET_COMPONENTS_TOKEN, AppComponent, AuthStorageService, COMPONENTS, ClickOutsideDirective, ConfigsFacade, ConfigsService, ContrastService, CrudBaseService, DIRECTIVES, DictionaryService, FileUrlService, FiltersBaseComponent, FiltersContext, FooterComponent, GameType, GlobalService, HeaderComponent, HomeComponent, HoverDirective, ImageBox, ListMode, MasonryGridComponent, MenuComponent, MetaService, ModalContainerComponent, ModalRef, ModalService, NotFoundComponent, PageComponent, PageStaticComponent, QueryFilterService, SEARCH_CONFIG_TOKEN, SERVICES, ScrollTopComponent, ScrollableDirective, SearchService, SeoService, SettingsService, SharedConfig, SharedModule, StyleService, TRANSLATE_DATA_PL, TranslationService, WcagService, WidgetBaseComponent, WidgetComponent, WidgetTextComponent, authenticationGuard, environment, getSearchFilterQuery, setTranslationsAndLang, unauthorizedGuard };
3348
+ export { ARTICLES_WIDGET_COMPONENTS_TOKEN, AppComponent, AuthStorageService, COMPONENTS, ClickOutsideDirective, ConfigsFacade, ConfigsService, ContrastService, CrudBaseService, DIRECTIVES, DictionaryService, FileUrlService, FiltersBaseComponent, FiltersContext, FooterComponent, GalleryFullscreenBaseComponent, GameType, GlobalService, HeaderComponent, HomeComponent, HoverDirective, ImageBox, ListMode, MasonryGridComponent, MediaTabBaseComponent, MenuComponent, MetaService, ModalContainerComponent, ModalRef, ModalService, NotFoundComponent, PageComponent, PageStaticComponent, QueryFilterService, SEARCH_CONFIG_TOKEN, SERVICES, ScrollTopComponent, ScrollableDirective, SearchBaseComponent, SearchService, SeoService, SettingsService, SharedConfig, SharedModule, SliderBaseComponent, StyleService, TRANSLATE_DATA_PL, TranslationService, WcagService, WidgetBaseComponent, WidgetComponent, WidgetTextComponent, authenticationGuard, environment, getSearchFilterQuery, setTranslationsAndLang, unauthorizedGuard };
2549
3349
  //# sourceMappingURL=smartsoft001-mobilems-angular.mjs.map