1 2 3/**************************************/ 4/*Application */ 5/**************************************/ 6 7extend(saffire, { 8 9 angular: { 10 11 //shared angular application 12 app: angular.module("SaffireAngularApp", ['ngAnimate', 'ngTouch', 'ngSanitize', 'ngCookies', 'ngMask']) 13 14 15 } 16 17} 18); 19 20saffire.angular.app.config(['$compileProvider', function ($compileProvider) { 21 $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|javascript):/); 22}]) 23 24saffire.angular.app.run(function ($rootScope) { 25 $rootScope.siteBaseURL = SITEBASEURL; 26 $rootScope.fileServerUploadURL = FILESERVERUPLOADURL; 27 $rootScope.fileServerImagesURL = FILESERVERIMAGESURL; 28 $rootScope.fileServerFilesURL = FILESERVERFILESURL; 29 30 if (PAGESSL != undefined && PAGESSL) { 31 $rootScope.siteBaseURL == SITEBASEURL.replace("http://","https://") 32 } 33}); 34 35saffire.angular.app.filter("sanitize", ['$sce', function ($sce) { 36 return function (htmlCode) { 37 return $sce.trustAsHtml(htmlCode); 38 } 39}]); 40 41/**************************************/ 42/*Services */ 43/**************************************/ 44 45saffire.angular.app.service('saffire.angular.webServices', ['$http', function webServices($http) { 46 47 //Public API 48 49 return ({ 50 getWebRequest: getWebRequest 51 }); 52 53 //Public Functions 54 55 function getWebRequest(url) { 56 57 var request = $http({ 58 method: "get", 59 url: url, 60 contentType: "application/json", 61 dataType: "json", 62 params: {}, 63 data: {} 64 }) 65 66 return (request.then(handleSuccess, handleError)) 67 68 } 69 70 //Private Functions 71 72 function handleSuccess(response) { 73 return response.data; 74 } 75 76 function handleError(response) { 77 return response.data; 78 } 79 80}]); 81 82saffire.angular.app.service('saffire.angular.webPoster', ['$http', function webPoster($http) { 83 84 //Public API 85 86 return ({ 87 postWebRequest: postWebRequest 88 }); 89 90 //Public Functions 91 92 function postWebRequest(url, postJsonString) { 93 94 var request = $http({ 95 method: "post", 96 url: url, 97 params: {}, 98 data: postJsonString 99 }) 100 101 return (request.then(handleSuccess, handleError)) 102 103 } 104 105 //Private Functions 106 107 function handleSuccess(response) { 108 return response.data; 109 } 110 111 function handleError(response) { 112 return response.data; 113 } 114 115}]); 116 117saffire.angular.app.service('saffire.angular.navigatorService', ['$window', function navigatorService($window) { 118 119 return ({ 120 getMediaDevices: getMediaDevices 121 }); 122 123 //Public Functions 124 125 function getMediaDevices() { 126 127 if (!$window.navigator.mediaDevices || !$window.navigator.mediaDevices.enumerateDevices) { 128 return new Promise(function (resolve, reject) { }); 129 } 130 131 var request = navigator.mediaDevices.enumerateDevices(); 132 133 return request.then(handleSuccess, handleError); 134 135 } 136 137 //Private Functions 138 139 function handleSuccess(response) { 140 return response; 141 } 142 143 function handleError(response) { 144 return response; 145 } 146 147}]); 148 149saffire.angular.app.service('saffire.angular.scriptLoaderService', ['$q', function scriptLoaderService($q) { 150 151 //Public API 152 153 return ({ 154 loadScript: loadScript 155 }) 156 157 //Public Functions 158 159 function loadScript(scriptPath) { 160 161 var deffered = $q.defer(); 162 163 var head = document.getElementsByTagName('head')[0]; 164 var script = document.createElement('script'); 165 166 script.setAttribute('type', 'text/javascript'); 167 script.src = scriptPath; 168 169 var onScriptLoaded = function () { 170 deffered.resolve() 171 }; 172 173 if (script.addEventListener) { // Standard browsers (including IE9+) 174 script.addEventListener('load', onScriptLoaded, false); 175 } 176 else { // IE8 and below 177 script.onreadystatechange = function () { 178 if (script.readyState === 'loaded' || script.readyState === 'complete') { 179 script.onreadystatechange = null; 180 onScriptLoaded(); 181 } 182 }; 183 } 184 185 head.appendChild(script); 186 187 return deffered.promise 188 }; 189 190}]); 191 192saffire.angular.app.service('saffire.angular.pdfPrinterService', ['$q', function pdfPrinterService($q) { 193 194 //Public API 195 196 return ({ 197 printPDF: printPDF 198 }) 199 200 //Public Functions 201 202 function printPDF(pdfURL) { 203 204 var deffered = $q.defer(); 205 206 if (document.getElementById("pdfIframe")) 207 document.getElementById("pdfIframe").remove(); 208 209 var ifrm = document.createElement("iframe"); 210 211 ifrm.setAttribute('id', 'pdfIframe'); 212 ifrm.setAttribute("src", pdfURL); 213 ifrm.style.visibility = 'hidden'; 214 ifrm.style.width = '1px'; 215 ifrm.style.height = '1px'; 216 217 document.body.appendChild(ifrm); 218 219 var onIFrameLoaded = function () { 220 var newWin = ifrm.contentWindow; 221 newWin.document.close(); 222 newWin.focus(); 223 newWin.print(); 224 225 deffered.resolve(); 226 }; 227 228 if (ifrm.addEventListener) { // Standard browsers (including IE9+) 229 ifrm.addEventListener('load', onIFrameLoaded, false); 230 } 231 232 return deffered.promise 233 }; 234 235}]); 236 237saffire.angular.app.service('saffire.angular.urlPrinterService', ['$q', function pdfPrinterService($q) { 238 239 //Public API 240 241 return ({ 242 printURL: printURL 243 }) 244 245 //Public Functions 246 247 function printURL(URL) { 248 249 var deffered = $q.defer(); 250 251 if (document.getElementById("printIframe")) 252 document.getElementById("printIframe").remove(); 253 254 var ifrm = document.createElement("iframe"); 255 256 ifrm.setAttribute('id', 'printIframe'); 257 ifrm.setAttribute("src", URL); 258 ifrm.style.visibility = 'hidden'; 259 ifrm.style.width = '1px'; 260 ifrm.style.height = '1px'; 261 262 document.body.appendChild(ifrm); 263 264 var onIFrameLoaded = function () { 265 var newWin = ifrm.contentWindow; 266 newWin.document.close(); 267 newWin.focus(); 268 newWin.print(); 269 270 deffered.resolve(); 271 }; 272 273 if (ifrm.addEventListener) { // Standard browsers (including IE9+) 274 ifrm.addEventListener('load', onIFrameLoaded, false); 275 } 276 277 return deffered.promise 278 }; 279 280}]); 281 282saffire.angular.app.service('saffire.angular.fileService', [function fileService() { 283 284 //Public API 285 286 return ({ 287 getFileExtension: getFileExtension, 288 });
289 290 //Public Functions 291 292 function getFileExtension(fileName) { 293 294 if (fileName) { 295 if (fileName.indexOf('.') > -1) { 296 return '.' + fileName.split('.').pop() 297 } 298 } 299 300 return '' 301 } 302 303}]); 304 305saffire.angular.app.service('saffire.angular.fileServerService', ['$http', '$rootScope', 'saffire.angular.fileService', function fileServerService($http, $rootScope, fileService) { 306 307 //Public API 308 309 return ({ 310 uploadFile: uploadFile, 311 getFileAbsolutePath: getFileAbsolutePath 312 }); 313 314 //Public Functions 315 316 function getFileAbsolutePath(fileName, isImage) { 317 318 if (isImage) { 319 return $rootScope.fileServerImagesURL + '&i=' + fileName 320 } 321 else { 322 return $rootScope.fileServerFilesURL + '&f=' + fileName 323 } 324 325 } 326 327 function uploadFile(fileName, fileData, mimeType, isImage) { 328 329 var url = $rootScope.fileServerUploadURL 330 var queryParams = []; 331 332 //change url to https if needed 333 if (document.location.protocol == 'https:' && url.indexOf('https:') < 0) { 334 url = url.replace('http:', 'https:') 335 } 336 337 //fileName 338 //remove illegal characters 339 fileName = fileName.replace(/[^\w\)\(\.]/gi, '') 340 fileExtension = fileService.getFileExtension(fileName); 341 342 //give a static filename just in case it was all illegal chars 343 if (fileName.replace(fileExtension, '') == '') { 344 fileName = 'file' + fileExtension 345 } 346 347 queryParams.push('filename=' + fileName) 348 349 //pathKey 350 var pathKey = 'ig'; 351 if (!isImage) { 352 pathKey = 'fg'; 353 } 354 355 queryParams.push('t=' + pathKey) 356 357 if (queryParams.length) { 358 url = url + '&' + queryParams.join('&') 359 } 360 361 var request = $http({ 362 method: "post", 363 url: url, 364 params: {}, 365 data: fileData, 366 transformRequest: [] 367 }) 368 369 return request 370 371 } 372 373}]); 374 375saffire.angular.app.factory('dialogService', ['$q', '$window', '$timeout', function ($q, $window, $timeout) { 376 377 var openDialog = function (scope, dialogOptions) { 378 379 var modalPromise = $q.defer(); 380 381 var defaultOptions = { 382 OnCloseCallbackFunction: function (args) { 383 scope.$apply( 384 modalPromise.resolve({ args: args }) 385 ) 386 }, 387 Parent: window, 388 ScrollFrame: true, 389 OnCloseTargetID: '', 390 OnCloseEventArg: '', 391 392 } 393 394 angular.extend(defaultOptions, dialogOptions); 395 396 $OpenModal(defaultOptions) 397 398 return modalPromise.promise; 399 400 } 401 402 var closeDialog = function (element) { 403 404 $timeout(function () { 405 406 var dialog = null; 407 if (jQuery.type(element) === "string") { 408 dialog = $('#' + element); 409 } else { 410 dialog = element; 411 } 412 $HideModal(dialog); 413 }) 414 415 } 416 417 return ({ 418 openDialog: openDialog, 419 closeDialog: closeDialog 420 }) 421}]) 422 423saffire.angular.app.factory('saffire.angular.scannerService', ['saffire.angular.scriptLoaderService', '$q', function (scriptLoaderService,$q) { 424 425 scriptLoaderService.loadScript(SITEBASEURL + 'JS/html5-qrcode.min.js?sv=' + SITEVERSION); 426 427 var permissionDenied = false; 428 var couldNotInitializeStream = false; 429 var hasAccess = false; 430 var myCamera = null; 431 var devices = []; 432 var cameraOn = false; 433 434 var getCameras = function () { 435 436 var getCamerasPromise = $q.defer(); 437 438 Html5Qrcode.getCameras() 439 .then(function (devices) { 440 hasAccess = true; 441 getCamerasPromise.resolve(devices) 442 }) 443 .catch(function (e) { 444 445 switch (e) { 446 case 'NotAllowedError : Permission denied': 447 permissionDenied = true; 448 break; 449 case 'NotReadableError : Could not start video source': 450 couldNotInitializeStream = true 451 break; 452 } 453 454 getCamerasPromise.resolve(devices) 455 }) 456 .then(function (devices) { 457 458 }) 459 460 return getCamerasPromise.promise 461 462 } 463 464 var start = function (scope,elementID,cameraID,onScanSucces,onScanFailed,config) { 465 466 myCamera = new Html5Qrcode(elementID); 467 468 var startPromise = $q.defer(); 469 470 var defaultConfig = { 471 fps: 10,// Optional, frame per seconds for qr code scanning 472 // qrbox: { width: 250, height: 250 } // Optional, if you want bounded box UI 473 }; 474 475 angular.extend(defaultConfig, config); 476 477 myCamera.start(cameraID, defaultConfig, 478 function (decodedText, decodedResult) { //successCallback 479 480 scope.$apply(function () { 481 onScanSucces(decodedText, decodedResult); 482 }); 483 484 }, onScanFailed).then(function () { 485 cameraOn = true; 486 startPromise.resolve() 487 }); 488 489 return startPromise.promise 490 491 } 492 493 var stop = function (scope) { 494 495 var stopPromise = $q.defer(); 496 497 if (myCamera) { 498 myCamera.stop().then(function () { 499 cameraOn = false; 500 stopPromise.resolve() 501 }); 502 } 503 else { 504 cameraOn = false; 505 stopPromise.resolve() 506 } 507 508 509 510 return stopPromise.promise 511 512 } 513 514 return ({ 515 getCameras: getCameras, 516 permissionDenied: function () { return permissionDenied; }, 517 couldNotInitializeStream: function () { return couldNotInitializeStream; }, 518 hasAccess: function () { return hasAccess }, 519 start: start, 520 stop: stop, 521 isScanning: function () { return cameraOn; } 522 }) 523}]) 524 525saffire.angular.app.factory('saffire.angular.editorService', ['$interval', function ($interval) { 526 527 var enabled = false; 528 var debug = false; 529 var showMessageFunction = null; 530 var closeMessageFunction = null; 531 var broadcastFunction = null; 532 var userIsSuperAdmin = false; 533 var hasChanges = false; 534 535 var setHasChanges = function (hasChangesFlag) { 536 hasChanges = hasChangesFlag; 537 } 538 539 var getHasChanges = function () { 540 return hasChanges; 541 } 542 543 var setShowMessageFunction = function (func) { 544 showMessageFunction = func; 545 } 546 547 var setCloseMessageFunction = function (func) { 548 closeMessageFunction = func 549 } 550 551 var showMessage = function (message) { 552 showMessageFunction(message); 553 } 554 555 var closeMessage = function () { 556 closeMessageFunction(); 557 } 558 559 var setBroadcastFunction = function (func) { 560 broadcastFunction = func 561 } 562 563 var broadcast = function (name, args) { 564 broadcastFunction(name, args); 565 } 566 567 var createValidationObject = function () { 568 return { 569 isValid: true, 570 messages: [], 571 flags: [], 572 } 573 } 574
575 var sumValidationObjects = function (validationObjects) { 576 577 var isValid = true; 578 var messages = []; 579 var flags = []; 580 581 if (validationObjects) { 582 $.each(validationObjects, function (voi) { 583 var vo = validationObjects[voi]; 584 if (!vo.isValid) { 585 isValid = false; 586 messages = messages.concat(vo.messages); 587 flags = flags.concat(vo.flags); 588 } 589 }) 590 } 591 592 var validationObject = createValidationObject() 593 validationObject.isValid = isValid; 594 validationObject.messages = messages; 595 validationObject.flags = flags; 596 597 return validationObject; 598 } 599 600 var sumValidationObjectsWithObject = function (validationObject, validationObjects) { 601 602 var tmp = sumValidationObjects(validationObjects); 603 604 validationObject.isValid = validationObject.isValid && tmp.isValid; 605 validationObject.messages = validationObject.messages.concat(tmp.messages); 606 validationObject.flags = validationObject.flags.concat(tmp.flags); 607 608 } 609 610 return ({ 611 enabled: enabled, 612 debug: debug, 613 showMessage: showMessage, 614 setShowMessageFunction: setShowMessageFunction, 615 closeMessage: closeMessage, 616 setCloseMessageFunction: setCloseMessageFunction, 617 setBroadcastFunction: setBroadcastFunction, 618 broadcast: broadcast, 619 createValidationObject: createValidationObject, 620 sumValidationObjects: sumValidationObjects, 621 sumValidationObjectsWithObject: sumValidationObjectsWithObject, 622 userIsSuperAdmin: userIsSuperAdmin, 623 setHasChanges: setHasChanges, 624 getHasChanges: getHasChanges, 625 }) 626}]) 627 628/**************************************/ 629/*directives */ 630/**************************************/ 631 632saffire.angular.app.directive('sfLoadingMask', ['$interval', function ($interval) { 633 634 635 function link(scope, element, attrs) { 636 637 if (!scope.loadingText) { 638 scope.loadingText = 'Please Wait...'; 639 } 640 641 scope.container = element.closest('.AngularAppContainer') 642 643 scope.$watch('appEnabled', function (newValue, oldValue) { 644 645 if (scope.container) { 646 if (!scope.appEnabled) { 647 scope.container.addClass('loading'); 648 } 649 else { 650 scope.container.removeClass('loading'); 651 } 652 } 653 654 }, true); 655 656 scope.$watch('suppressLoading', function (newValue, oldValue) { 657 658 if (scope.container) { 659 660 if (scope.suppressLoading) { 661 scope.container.addClass('loading-silent'); 662 } 663 else { 664 scope.container.removeClass('loading-silent'); 665 } 666 667 } 668 669 }, true); 670 }; 671 672 return { 673 restrict: 'E', 674 scope: { 675 appEnabled: '=', 676 suppressLoading: '=', 677 loadingText: '=?', 678 }, 679 templateUrl: SITEBASEURL + 'core/content/angular/loadingMask.html?sv=' + SITEVERSION, 680 link: link 681 682 } 683 684}]); 685 686saffire.angular.app.directive('sfMessenger', ['$interval', '$timeout', function ($interval, $timeout) { 687 688 function link(scope, element, attrs) { 689 690 scope.container = element.closest('.AngularAppContainer') 691 692 var lastActiveElement = null; 693 694 scope.showMessage = function (settings) { 695 696 var isShowMessage = true; 697 lastActiveElement = document.activeElement; 698 699 if (scope.messengerActive) { 700 if (settings.message.indexOf('Still here') != -1) { 701 scope.previousMessengerSettings = scope.messenger; 702 } 703 else if (scope.messenger.message.indexOf('Still here') != -1) { 704 isShowMessage = false; 705 scope.previousMessengerSettings = settings; 706 } 707 else { 708 scope.previousMessengerSettings = scope.messenger; 709 } 710 } 711 else { 712 scope.previousMessengerSettings = undefined; 713 } 714 715 if (isShowMessage) { 716 var messageSettings = { 717 message: '', 718 buttons: [], 719 autoHide: false, 720 timeoutInterval: 1000, 721 } 722 723 messageSettings = $.extend(messageSettings, settings); 724 725 scope.messenger = messageSettings; 726 scope.messengerActive = true; 727 728 if (scope.messenger.message.indexOf('Still here') != -1) { 729 scope.isInactivityTimerActive = true; 730 } 731 732 if (messageSettings.autoHide) { 733 734 if (scope.autoHideTimeOut) { 735 $timeout.cancel(scope.autoHideTimeOut); 736 } 737 738 scope.autoHideTimeOut = $timeout(function () { 739 scope.closeMessage(true); 740 741 if (scope.messenger.beforeClose) { 742 scope.messenger.beforeClose(); 743 } 744 745 }, messageSettings.timeoutInterval) 746 } 747 748 scope.container.addClass('messengerActive'); 749 750 (function() { 751 let fdelay = 200; 752 const isSeatSelector = $('#reservations').length > 0;
753 if (isSeatSelector) { 754 fdelay = 500; 755 } 756 setTimeout(() => { 757 setFocusInModal('.messenger'); 758 }, fdelay); 759 })(); 760 } 761 } 762 763 scope.closeMessage = function (isAutoHideTimeOut) { 764 765 if (scope.messenger) { 766 scope.messengerActive = false; 767 scope.messenger.countdown = 0; 768 769 if (scope.autoHideTimeOut) { 770 $timeout.cancel(scope.autoHideTimeOut); 771 } 772 773 if (scope.messenger.message.indexOf('Still here') != -1) 774 scope.isInactivityTimerActive = false; 775 776 scope.container.removeClass('messengerActive'); 777 778 // Return focus to last focused element or top of content 779 setTimeout(() => { 780 if (lastActiveElement && $(lastActiveElement).is(':visible')) { 781 lastActiveElement.focus(); 782 } else { 783 $('main').first().attr('tabindex', '-1').focus(); 784 } 785 }, 100); 786 787 if (isAutoHideTimeOut) { 788 scope.previousMessengerSettings = undefined; 789 } 790 else { 791 if (scope.previousMessengerSettings != undefined && scope.messenger.message != scope.previousMessengerSettings.message) { 792 scope.showMessage(scope.previousMessengerSettings); 793 scope.previousMessengerSettings = undefined; 794 } 795 } 796 } 797 } 798 }; 799 800 return { 801 restrict: 'E', 802 templateUrl: SITEBASEURL + 'core/content/angular/messenger.html?sv=' + SITEVERSION, 803 link: link 804 805 } 806 807}]); 808 809saffire.angular.app.directive('sfCountdown', ['$interval', '$timeout', function ($interval, $timeout) { 810 811 function link(scope, element, attrs) { 812 813 scope.$watch('startFrom', function (newValue, oldValue) { 814 815 if (scope.startFrom) { 816 817 scope.countdown = scope.startFrom; 818 819 scope.countdownTimeout = $interval(function () { 820 scope.countdown = scope.countdown - 1; 821 }, 1000, scope.startFrom); 822 } 823 824 }, true); 825 }; 826 827 return { 828 restrict: 'E', 829 scope: { 830 startFrom: '=' 831 }, 832 templateUrl: SITEBASEURL + 'core/content/angular/countdown.html?sv=' + SITEVERSION, 833 link: link 834 835 } 836 837}]); 838 839saffire.angular.app.directive('sfDetailsPanel', ['$timeout', function ($timeout) { 840 841 function link(scope, element, attrs) { 842 843 scope.hideShowDetails = function () { 844 scope.show = !scope.show; 845 846 $timeout(function () { 847 scope.onHideShowClick(); 848 }, 100); 849 } 850 }; 851 852 return { 853 restrict: 'E', 854 scope: { 855 onHideShowClick: '=' 856 }, 857 transclude: true, 858 templateUrl: SITEBASEURL + 'core/content/angular/detailsPanel.html', 859 link: link, 860 } 861}]); 862 863saffire.angular.app.directive('sfEnter', function () { 864 return function (scope, element, attrs) { 865 element.bind("keydown keypress", function (event) { 866 if (event.which === 13) { 867 scope.$apply(function () { 868 scope.$eval(attrs.sfEnter); 869 }); 870 871 event.preventDefault(); 872 } 873 }); 874 }; 875}); 876 877saffire.angular.app.directive('sfFileUpload', ['saffire.angular.fileServerService', 'saffire.angular.fileService', function (fileServerService, fileService) { 878 879 function link(scope, element, attrs) { 880 881 scope.fileUploadControl = element.find('#file')[0] 882 883 scope.fileName = '' 884 scope.mimeType = '' 885 886 if (scope.buttonText == '' || scope.buttonText == undefined) { 887 scope.buttonText = "Browse..."; 888 } 889 890 scope.clickUpload = function () { 891 scope.fileUploadControl.click(); 892 } 893 894 scope.fileUploadControl.onchange = function () { 895 var f = scope.fileUploadControl.files[0]; 896 var r = new FileReader(); 897 898 scope.fileName = scope.fileUploadControl.files[0].name; 899 scope.mimeType = scope.fileUploadControl.files[0].type; 900 901 r.onloadstart = function (e) { 902 903 //Check File Size 904 if (e.lengthComputable && scope.maxFileSize) { 905 if (e.total > scope.maxFileSize * 1000000) { 906 this.abort() 907 scope.fileUploadControl.value = '' 908 scope.$apply(function () { 909 scope.onValidationError({ validationMessage: 'File size is too large. File must be less than ' + scope.maxFileSize.toString() + 'MB' }) 910 }) 911 return; 912 } 913 } 914 915 //Check mime types 916 if (scope.allowFileTypes) { 917 var allowedTypes = scope.allowFileTypes.split(',') 918 var fileExtension = fileService.getFileExtension(scope.fileName); 919 var isValid = false; 920 921 if (allowedTypes.indexOf('audio') > -1) { 922 var mimeTypes = ["audio/basic", "audio/mpeg", "audio/mpeg", "audio/x-aiff", "audio/xwav", "audio/x-m4a"] 923 var fileExtensions = [".au", ".mp3", ".aif", ".m3u", ".wav", ".m4a"] 924 925 if (mimeTypes.indexOf(scope.mimeType.toLowerCase()) > -1 || fileExtensions.indexOf(fileExtension.toLowerCase()) > -1) { 926 isValid = true; 927 } 928 } 929 930 if (allowedTypes.indexOf('image') > -1) { 931 var mimeTypes = ["image/jpg", "image/jpeg", "image/gif", "image/png", "image/bmp"] 932 var fileExtensions = [".jpg", ".jpeg", ".gif", ".png", ".bmp", ".tiff", ".tif"] 933 934 if (mimeTypes.indexOf(scope.mimeType.toLowerCase()) > -1 || fileExtensions.indexOf(fileExtension.toLowerCase()) > -1) { 935 isValid = true; 936 } 937 } 938 939 if (allowedTypes.indexOf('pdf') > -1) { 940 var mimeTypes = ["application/pdf"] 941 var fileExtensions = [".pdf"] 942 943 if (mimeTypes.indexOf(scope.mimeType.toLowerCase()) > -1 || fileExtensions.indexOf(fileExtension.toLowerCase()) > -1) { 944 isValid = true; 945 } 946 } 947 948 if (!isValid) { 949 this.abort() 950 scope.fileUploadControl.value = '' 951 scope.$apply(function () { 952 scope.onValidationError({ validationMessage: 'Invalid file type selected' }) 953 }) 954 return; 955 } 956 } 957 958 959 scope.$apply(function () { 960 scope.onUploadStart(); 961 }) 962 963 } 964 965 r.onprogress = function (e) { 966 967 } 968 969 r.onloadend = function (e) { 970 if (e.total > 0 && e.total == e.loaded) { 971 var data = new Uint8Array(e.target.result) 972 scope.fileUploadControl.value = '' 973 scope.$apply(function () { 974 scope.onFileUpload({ 975 fileName: scope.fileName, 976 mimeType: scope.mimeType, 977 data: data 978 }) 979 }) 980 981 } 982 983 } 984 r.readAsArrayBuffer(f); 985 } 986 987 988 } 989 990 return { 991 restrict: 'E', 992 scope: { 993 onFileUpload: '&', 994 onUploadStart: '&', 995 onValidationError: '&', 996 maxFileSize: '@', 997 allowFileTypes: '@', 998 buttonClass: '@', 999 buttonText: '@', 1000 showMessage: '=', 1001 closeMessage: '=' 1002 }, 1003 templateUrl: SITEBASEURL + 'core/content/angular/templates/fileUpload.html',
1004 link: link 1005 }; 1006 1007}]); 1008 1009saffire.angular.app.directive('sfWebCamImageUpload', ['$interval', 'saffire.angular.navigatorService', function ($interval, navigatorService) { 1010 1011 function link(scope, element, attrs) { 1012 1013 if (valueIsNullOrUndefinedOrEmpty(scope.buttonText)) { 1014 scope.buttonText = "Take Picture"; 1015 } 1016 1017 scope.openCamera = function () { 1018 1019 //navigatorService.getMediaDevices().then(function (response) { 1020 // if (response.length > 0) { 1021 // scope.showSwitchCamera = $.grep(response, function (md) { 1022 // return md.kind == "videoinput" 1023 // }).length > 1 1024 // } 1025 //}) 1026 1027 scope.camOptions.constraints = { facingMode: scope.cameraSettings.facingMode }; 1028 1029 if (Webcam.live) { 1030 scope.showMessage({ 1031 message: "Camera is already active", 1032 buttons: [{ 1033 text: 'Ok', 1034 onClick: function () { 1035 scope.closeMessage(); 1036 } 1037 }], 1038 }) 1039 } else { 1040 Webcam.set(scope.camOptions); 1041 Webcam.attach(element.find("#camera")[0]); 1042 1043 if (Webcam.userMedia) { 1044 scope.cameraLoadedInterval = $interval(function () { 1045 if (Webcam.loaded) { 1046 $interval.cancel(scope.cameraLoadedInterval); 1047 scope.cameraOpened = true; 1048 } 1049 }, 500) 1050 } 1051 } 1052 } 1053 1054 scope.switchCamera = function () { 1055 if (scope.cameraSettings.facingMode == "environment") { 1056 scope.cameraSettings.facingMode = "user"; 1057 1058 } else if (scope.cameraSettings.facingMode == "user") { 1059 scope.cameraSettings.facingMode = "environment"; 1060 } 1061 1062 if (Webcam.live) { 1063 Webcam.reset(); 1064 } 1065 1066 scope.openCamera(); 1067 } 1068 1069 scope.takeSnapshot = function () { 1070 Webcam.snap(function (dataURI) { 1071 1072 var data = convertDataURIToBinary(dataURI); 1073 1074 var now = new Date(); 1075 scope.fileName = (now.getFullYear().toString() + (now.getMonth() + 1).toString() + now.getDate().toString() + now.getHours().toString() + now.getMinutes().toString() + now.getSeconds().toString() + now.getMilliseconds().toString() + ".jpg").toString(); 1076 1077 scope.takePicture({ 1078 fileName: scope.fileName, 1079 mimeType: "image/jpeg", 1080 data: data 1081 }); 1082 1083 scope.closeCamera(); 1084 }); 1085 } 1086 1087 scope.closeCamera = function () { 1088 scope.cameraOpened = false; 1089 Webcam.reset(); 1090 } 1091 } 1092 1093 return { 1094 restrict: 'E', 1095 scope: { 1096 camOptions: '=', 1097 takePicture: '&', 1098 buttonClass: '@', 1099 buttonText: '@', 1100 showMessage: '=', 1101 closeMessage: '=', 1102 cameraSettings: '=' 1103 }, 1104 templateUrl: SITEBASEURL + 'core/content/angular/templates/webCamImageUpload.html', 1105 link: link 1106 }; 1107}]); 1108 1109saffire.angular.app.directive('sfGoogleRecaptcha', ['$interval', 'saffire.angular.scriptLoaderService', function ($interval, scriptLoaderService) { 1110 1111 //This is currently written for a single use on a page 1112 //could be expanded to allow mutliple 1113 //The response token needs to be validated server side as well 1114 1115 function link(scope, element, attrs) { 1116 1117 scope.recaptchaContainer = element.find("#div_recaptcha"); 1118 scope.initialized = false; 1119 1120 scope.verifyCallback = function (response) { 1121 1122 scope.validation.submitRecaptchaValidationToken = response; 1123 1124 scope.$apply(function () { 1125 scope.validation.isValid = true; 1126 1127 }); 1128 1129 } 1130 1131 scope.expiredCallback = function () { 1132 1133 scope.$apply(function () { 1134 scope.validation.submitRecaptchaValidationToken = ''; 1135 scope.validation.isValid = false; 1136 }) 1137 1138 } 1139 1140 window.recaptchaCallBack = function () { 1141 1142 scope.recaptcha = grecaptcha.render(scope.recaptchaContainer[0], 1143 { 1144 sitekey: scope.siteKey, 1145 callback: scope.verifyCallback, 1146 'expired-callback': scope.expiredCallback, 1147 }) 1148 1149 } 1150 1151 scope.$watch('enabled', function (newVal, oldVal) { 1152 1153 if (scope.enabled && !scope.initialized) { 1154 1155 scope.initialized = true; 1156 scriptLoaderService.loadScript('https://www.google.com/recaptcha/api.js?onload=recaptchaCallBack&render=explicit'); 1157 1158 } 1159 1160 }, true) 1161 1162 scope.$watch('resetCounter', function (newVal, oldVal) { 1163 1164 if (scope.resetCounter && scope.resetCounter > 0 && scope.enabled && scope.initialized) { 1165 1166 grecaptcha.reset(); 1167 scope.validation.submitRecaptchaValidationToken = null; 1168 1169 } 1170 1171 }, true) 1172 1173 }; 1174 1175 return { 1176 restrict: 'E', 1177 scope: { 1178 siteKey: '@', 1179 validation: '=', 1180 enabled: '=', 1181 resetCounter: '=?', 1182 }, 1183 templateUrl: SITEBASEURL + 'core/content/angular/recaptcha/recaptcha.html?sv=' + SITEVERSION, 1184 link: link 1185 1186 } 1187 1188}]); 1189 1190saffire.angular.app.directive('sfPromptOnExit', [function () { 1191 1192 function link(scope, element, attrs) { 1193 1194 window.onbeforeunload = function () {
1195 if (scope.promptOnExit) { 1196 return "You have pending items in your cart, are you sure you want to leave this screen?"; 1197 } 1198 } 1199 1200 } 1201 1202 return { 1203 restrict: 'E', 1204 scope: { 1205 promptOnExit: '=', 1206 }, 1207 link: link 1208 }; 1209 1210}]); 1211 1212saffire.angular.app.directive('sfTicketItemInputText', function () { 1213 1214 function link(scope, element, attrs) { 1215 1216 scope.$watch('[value,isRequired,originalValue,validationObject]', function (newVal, oldVal) { 1217 1218 var isValid = true; 1219 var messages = []; 1220 1221 if (scope.isRequired && (valueIsNullOrUndefinedOrEmpty(scope.value))) { 1222 isValid = false; 1223 1224 if (scope.showLabelTextInValidationMessage) { 1225 messages.push((scope.labelText ? scope.labelText + ' is ' : '') + 'required'); 1226 } 1227 else { 1228 messages.push('required'); 1229 } 1230 } 1231 1232 //validation 1233 1234 if (scope.validationObject) { 1235 scope.validationObject.isValid = isValid; 1236 scope.validationObject.messages = messages; 1237 scope.validationObject.isValidated = true; 1238 } 1239 1240 }, true) 1241 1242 scope.$watch('autoComplete', function (newVal, oldVal) { 1243 if (scope.autoComplete && scope.autoComplete !== '') { 1244 element.find('input[type="text"]').attr('autocomplete', scope.autoComplete); 1245 } else { 1246 element.find('input[type="text"]').removeAttr('autocomplete'); 1247 } 1248 }, true) 1249 }; 1250 1251 return { 1252 scope: { 1253 value: '=', 1254 description: '=', 1255 isRequired: '=', 1256 validationObject: '=', 1257 labelText: '@', 1258 labelCssClass: '@', 1259 cssClass: '@', 1260 isCritical: '=', 1261 maxLen: '=', 1262 showValidationLabel: '=', 1263 showLabelTextInValidationMessage: '=', 1264 autoComplete: '@' 1265 }, 1266 templateUrl: SITEBASEURL + 'core/content/angular/Templates/ticketItemInputText.html', 1267 link: link 1268 } 1269}); 1270 1271saffire.angular.app.directive('sfTicketDetailsInputValidationLabel', function () { 1272 1273 function link(scope, element, attrs) { 1274 1275 }; 1276 1277 return { 1278 scope: { 1279 validationObject: '=' 1280 }, 1281 templateUrl: SITEBASEURL + 'core/content/angular/Templates/ticketsDetailsInputValidationLabel.html', 1282 link: link 1283 } 1284}); 1285 1286saffire.angular.app.directive('sfInputPhone', function () { 1287 1288 function link(scope, element, attrs) { 1289 1290 scope.$watch('[value,isRequired,validationObject]', function (newVal, oldVal) { 1291 1292 var isValid = true; 1293 var messages = []; 1294 1295 if (scope.isRequired && (valueIsNullOrUndefinedOrEmpty(scope.value))) { 1296 isValid = false; 1297 messages.push(scope.labelText + ' is required'); 1298 } 1299 1300 var rePhoneNumber = /^[0-9]{3}[0-9]{3}[0-9]{4}$/; 1301 1302 if (!stringIsNullOrEmpty(scope.value && scope.value.toString()) && !(rePhoneNumber.test(scope.value))) { 1303 isValid = false; 1304 messages.push(scope.labelText + ' is invalid'); 1305 } 1306 1307 if (scope.validationObject) { 1308 scope.validationObject.isValid = isValid; 1309 scope.validationObject.messages = messages; 1310 } 1311 1312 }, true) 1313 }; 1314 1315 return { 1316 scope: { 1317 value: '=', 1318 description: '=', 1319 isRequired: '=', 1320 readonly: '=?', 1321 validationObject: '=', 1322 validationEnabled: '=?', 1323 labelText: '@', 1324 cssClass: '@', 1325 placeholder: '@', 1326 isCritical: '=', 1327 maxLen: '=?', 1328 mask: '@' 1329 }, 1330 templateUrl: SITEBASEURL + 'core/content/angular/templates/inputPhone.html', 1331 link: link 1332 } 1333}); 1334 1335saffire.angular.app.directive('sfInputNumberKeypad', ['$rootScope', function ($rootScope) { 1336 1337 function link(scope, element, attrs) { 1338 scope.keyPressed = function (key) { 1339 1340 if (key == "Clear") { 1341 scope.input = ""; 1342 1343 } else if (key == "Clear Last") { 1344 scope.input = scope.input.slice(0, -1); 1345 1346 } else { 1347 if (scope.input.length < scope.maxLen) { 1348 scope.input += key; 1349 } 1350 } 1351 1352 $rootScope.$broadcast("sfInputNumberKeypadKeyPressed", key); 1353 } 1354 }; 1355 1356 return { 1357 scope: { 1358 input: '=', 1359 maxLen: '=' 1360 }, 1361 templateUrl: SITEBASEURL + 'core/content/angular/templates/inputNumberKeypad.html',
1362 link: link 1363 } 1364}]); 1365 1366saffire.angular.app.directive('sfValidationInputDropDown', function () { 1367 1368 function link(scope, element, attrs) { 1369 1370 scope.isInitialized = false; 1371 1372 if (!scope.selectLabel) 1373 scope.selectLabel = "Select"; 1374 1375 scope.getDisplayName = function (recordData) { 1376 if (scope.displayNameFunc) { 1377 return scope.displayNameFunc({ entity: recordData }); 1378 } 1379 1380 return recordData.Name; 1381 } 1382 1383 scope.getDisplayNameWithHTMLStripped = function (recordData) { 1384 1385 var displayName = scope.getDisplayName(recordData); 1386 1387 return displayName.replace(/<\/?[^>]+(>|$)/g, "") 1388 } 1389 1390 scope.isDisabled = function (recordData) { 1391 if (scope.isDisabledFunc) { 1392 return scope.isDisabledFunc({ entity: recordData }); 1393 } 1394 1395 return false; 1396 } 1397 1398 scope.$watch('[recordId, recordName, ddDataSource]', function (newVal, oldVal) { 1399 1400 if (scope.ddDataSource) { 1401 var template = []; 1402 1403 if (scope.nameSelection) { 1404 template = $.grep(scope.ddDataSource, function (tt) { 1405 return tt.Name == scope.recordId 1406 }) 1407 } else { 1408 template = $.grep(scope.ddDataSource, function (tt) { 1409 return tt.ID == scope.recordId 1410 }) 1411 } 1412 1413 if (template.length) { 1414 scope.selectedRecord = template[0]; 1415 } 1416 else 1417 scope.selectedRecord = null; 1418 1419 scope.isInitialized = true; 1420 } 1421 1422 }, true) 1423 1424 scope.$watch('selectedRecord', function (newVal, oldVal) { 1425 1426 if (scope.isInitialized) { 1427 1428 if (scope.nameSelection) 1429 scope.recordId = newVal ? scope.selectedRecord.Name : ''; 1430 else 1431 scope.recordId = newVal ? scope.selectedRecord.ID : null; 1432 1433 scope.selectedName = newVal ? scope.selectedRecord.Name : ''; 1434 } 1435 1436 }, true) 1437 1438 scope.$watch('[recordId,isRequired,validationObject,ids]', function (newVal, oldVal) { 1439 1440 var isValid = true; 1441 var messages = []; 1442 1443 if (scope.isRequired && valueIsNullOrUndefinedOrEmpty(scope.recordId)) { 1444 isValid = false; 1445 messages.push(scope.labelText + ' is required') 1446 } 1447 1448 if (scope.ids && scope.recordId) { 1449 var isDuplicate = $.grep(scope.ids, function (id) { 1450 return id == scope.recordId; 1451 }).length > 1; 1452 1453 if (isDuplicate) { 1454 isValid = false; 1455 messages.push('Duplicate ' + scope.labelText + ' selected') 1456 } 1457 } 1458 1459 if (scope.validationObject) { 1460 scope.validationObject.isValid = isValid; 1461 scope.validationObject.messages = messages; 1462 } 1463 }, true) 1464 }; 1465 1466 return { 1467 scope: { 1468 recordId: '=', 1469 selectedRecord: '=?', 1470 selectedName: '=?', 1471 nameSelection: '=?', 1472 ddDataSource: '=', 1473 showSelectLabel: '=', 1474 isRequired: '=', 1475 validationObject: '=', 1476 showValidationIcon: '=', 1477 selectLabel: '@', 1478 labelText: '@', 1479 disabled: '=', 1480 showLabelText: '=', 1481 ids: '=', 1482 displayNameFunc: '&?', 1483 isDisabledFunc: '&?' 1484 }, 1485 templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputDropDown.html', 1486 link: link 1487 1488 } 1489}); 1490saffire.angular.app.directive('sfValidationInputNumber', function () { 1491 1492 function link(scope, element, attrs, ctrl) { 1493 1494 scope.numberIsValid = true; 1495 1496 scope.$watch('[value, isRequired, numberIsValid, groupVar, min, max, validationEnabled]', function (newVal, oldVal) { 1497 1498 var isValid = true; 1499 var messages = [];
1500 var validationEnabled = scope.validationEnabled == undefined ? true : scope.validationEnabled; 1501 1502 if (scope.allowZero == undefined) { 1503 scope.allowZero = false; 1504 } 1505 1506 if (scope.allowNegative == undefined) { 1507 scope.allowNegative = false; 1508 } 1509 1510 if (validationEnabled) { 1511 scope.isValid = scope.numberIsValid; 1512 1513 if (scope.isRequired && valueIsNullOrUndefinedOrEmpty(scope.value)) { 1514 isValid = false; 1515 messages.push(scope.labelText + " is required") 1516 } 1517 else if (!valueIsNullOrUndefinedOrEmpty(scope.value) && scope.numberIsValid) { 1518 if (scope.min && scope.value < scope.min) { 1519 isValid = false; 1520 1521 if (scope.minValidationMessage) { 1522 messages.push(scope.minValidationMessage); 1523 1524 } else { 1525 messages.push("Please input a number greater than " + scope.min); 1526 } 1527 } 1528 1529 if (scope.max && scope.value > scope.max) { 1530 isValid = false; 1531 1532 if (scope.maxValidationMessage) { 1533 messages.push(scope.maxValidationMessage); 1534 1535 } else { 1536 messages.push("Please input a number no greater than " + scope.max); 1537 } 1538 } 1539 } 1540 else if (!scope.numberIsValid) { 1541 isValid = false; 1542 messages.push(scope.labelText + " is invalid"); 1543 } 1544 1545 if (scope.customValidateFunc) { 1546 1547 var customValidationObject = scope.customValidateFunc(); 1548 1549 if (!customValidationObject.isValid) { 1550 isValid = customValidationObject.isValid; 1551 1552 angular.forEach(customValidationObject.messages, function (m) { 1553 messages.push(m); 1554 }) 1555 } 1556 } 1557 } 1558 1559 if (scope.validationObject) { 1560 scope.validationObject.isValid = isValid; 1561 scope.validationObject.messages = messages; 1562 } 1563 }, true) 1564 }; 1565 1566 return { 1567 scope: { 1568 value: '=', 1569 isRequired: '=', 1570 validationObject: '=', 1571 validationEnabled: '=', 1572 showValidationIcon: '=', 1573 labelText: '@', 1574 showLabelText: '=', 1575 cssClass: '@', 1576 maxLen: '=', 1577 minLen: '=', 1578 min: '=?', 1579 minValidationMessage: '@', 1580 max: '=?', 1581 maxValidationMessage: '@', 1582 disabled: '=', 1583 placeholder: '@', 1584 integerOnly: '=?', 1585 allowZero: '=?', 1586 allowNegative: '=?', 1587 isValid: '=?', 1588 customValidateFunc: '=', 1589 groupVar: '=', 1590 width: '@', 1591 display: '@', 1592 isCvv: '=' 1593 }, 1594 templateUrl: SITEBASEURL + 'core/content/angular/Templates/validationInputNumber.html', 1595 link: link 1596 } 1597});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.