/*** GLobal Directives ***/ // Route State Load Spinner(used on page or content load) MetronicApp.directive('ngSpinnerBar', ['$rootScope', '$state', function ($rootScope, $state) { return { link: function(scope, element, attrs) { // by defult hide the spinner bar element.addClass('hide'); // hide spinner bar by default // display the spinner bar whenever the route changes(the content part started loading) $rootScope.$on('$stateChangeStart', function() { element.removeClass('hide'); // show spinner bar }); // hide the spinner bar on rounte change success(after the content loaded) $rootScope.$on('$stateChangeSuccess', function() { element.addClass('hide'); // hide spinner bar $('body').removeClass('page-on-load'); // remove page loading indicator Layout.setSidebarMenuActiveLink('match'); // activate selected link in the sidebar menu // auto scorll to page top, except on certain states. if (!$state.includes('WorkOrderDetail')) { setTimeout(function () { Metronic.scrollTop(); // scroll to the top on content load }, $rootScope.settings.layout.pageAutoScrollOnLoad); } }); // handle errors $rootScope.$on('$stateNotFound', function() { element.addClass('hide'); // hide spinner bar }); // handle errors $rootScope.$on('$stateChangeError', function() { element.addClass('hide'); // hide spinner bar }); } }; } ]) // Handle global LINK click MetronicApp.directive('a', function () { return { restrict: 'E', link: function (scope, elem, attrs) { if (attrs.ngClick || attrs.href === '' || attrs.href === '#') { elem.on('click', function (e) { e.preventDefault(); // prevent link click for above criteria }); } } }; }); // Handle Dropdown Hover Plugin Integration MetronicApp.directive('dropdownMenuHover', function () { return { link: function (scope, elem) { elem.dropdownHover(); } }; }); // Secure Image Directive - loads images with bearer token authentication MetronicApp.directive('secureImage', ['appConfig', '$cookies', '$http', '$rootScope', function(appConfig, $cookies, $http, $rootScope) { return { restrict: 'E', scope: { imageUrl: '@', fallbackSrc: '@', alt: '@', ngClass: '@', refreshEvent: '@' // Optional event name to listen for refresh }, template: '{{alt}}', link: function(scope, element, attrs) { scope.loadedImageUrl = ''; // Watch for changes in imageUrl scope.$watch('imageUrl', function(newValue) { if (newValue) { loadSecureImage(newValue); } else if (scope.fallbackSrc) { scope.loadedImageUrl = scope.fallbackSrc; } }); // Listen for refresh events (e.g., CompanyLogoChanged) if (scope.refreshEvent) { scope.$on(scope.refreshEvent, function(event, data) { if (scope.imageUrl && data.HasLogo) { // Add timestamp to force refresh var refreshUrl = addCacheBuster(scope.imageUrl); loadSecureImage(refreshUrl); } else if (!data.HasLogo) { scope.loadedImageUrl = scope.fallbackSrc || ''; } }); } // Listen for CompanyLogoChanged by default scope.$on('CompanyLogoChanged', function(event, data) { if (scope.imageUrl && data.HasLogo) { // Add timestamp to force refresh var refreshUrl = addCacheBuster(scope.imageUrl); loadSecureImage(refreshUrl); } else if (!data.HasLogo) { scope.loadedImageUrl = scope.fallbackSrc || ''; } }); function addCacheBuster(url) { var separator = url.indexOf('?') !== -1 ? '&' : '?'; return url + separator + '_t=' + new Date().getTime(); } function loadSecureImage(apiUrl) { const bearerToken = $cookies.get('SessionToken'); if (!apiUrl) { scope.loadedImageUrl = scope.fallbackSrc || ''; return; } // Clean up previous blob URL before creating new one if (scope.loadedImageUrl && scope.loadedImageUrl.startsWith('blob:')) { URL.revokeObjectURL(scope.loadedImageUrl); } // Fetch the image with the bearer token $http({ method: 'GET', url: apiUrl, responseType: 'blob', headers: { 'Authorization': `Bearer ${bearerToken}` } }).then(function (response) { // Convert the Blob to an object URL const blob = new Blob([response.data], { type: response.headers('Content-Type') }); scope.loadedImageUrl = URL.createObjectURL(blob); }).catch(function (error) { console.error('Error fetching secure image:', error); scope.loadedImageUrl = scope.fallbackSrc || ''; }); } // Clean up object URLs when the directive is destroyed scope.$on('$destroy', function() { if (scope.loadedImageUrl && scope.loadedImageUrl.startsWith('blob:')) { URL.revokeObjectURL(scope.loadedImageUrl); } }); } }; }]);