var ngSharedApi = angular.module("ngSharedApi", []); (function () { ngSharedApi.factory('shApi', ['$timeout', '$rootScope', '$window', '$location', 'CanonicalUrl', 'Translator', function ($timeout, $rootScope, $window, $location, CanonicalUrl, Translator) { function internalScrollToTop(offset) { if (typeof offset === 'undefined') offset = 0; $("html, body").animate({ scrollTop: offset }, "slow"); } function showMessageAlerts(model, status, message) { if (typeof model.showAlerts === 'undefined' || model.showAlerts == null) return; if (status === 251 || status === 254) { model.showAlerts.showSuccess(message); } else if (status === 252) { model.showAlerts.showWarning(message); } else if (status === 253) { model.showAlerts.showError(message); } else if (status === 231) { model.showAlerts.showError(message); } } function redirectWithDialog(model, url, error) { model.nextUrl = url; model.handled = true; model.errorResult = function () { model.error = false; model.errorResult = null; model.handled = false; model.navigating = true; $window.location.href = model.nextUrl; } model.working = false; model.errorMsg = error; model.error = true; } function redirect(model, url) { model.handled = true; model.navigating = true; model.working = false; $window.location.href = url; } function redirectNewTab(model, url) { model.handled = true; model.navigating = true; model.working = false; window.open(url, "_blank"); } return { scrollToError: function (topoffset) { if (typeof topoffset === 'undeed') topoffset = 0; if ($('.check-element.show').length > 0) { var offset = ($('.check-element.show').first().offset().top - 50) + topoffset; $("html, body").animate({ scrollTop: offset }, "slow"); } }, scrollTo: function (element, time, topoffset) { if (topoffset == null) { topoffset = 0; } if (typeof element != undefined && element != null) { if (typeof $(element).offset() != "undefined") { if (typeof time != "undefined" && time != null) { $('html, body').animate({ scrollTop: ($(element).offset().top - 50) + topoffset }, time); } else { $('html, body').animate({ scrollTop: ($(element).offset().top - 50) + topoffset }, "slow"); } } } }, scrollToInModalDialog: function(element, time) { var container = $('body'), scrollToEl = $(element); if (typeof element != undefined && element != null) { if (typeof scrollToEl.offset() != "undefined") { if (typeof time != "undefined" && time != null) { scrollToEl.animate({ scrollTop: scrollToEl.offset().top - container.offset().top + container.scrollTop() }, time); } else { scrollToEl.animate({ scrollTop: scrollToEl.offset().top - container.offset().top + container.scrollTop() }, "slow"); } } } }, scrollToTop: function (topoffset) { if (typeof topoffset === 'undefined') topoffset = 0; internalScrollToTop(topoffset); }, preventNavigate: function (e, model) { if (e.stopPropagation) { e.stopPropagation(); } if (e.preventDefault) { e.preventDefault(); } e.cancelBubble = true; e.returnValue = false; if (typeof model != 'undefined' && model != null) { if (typeof model.error != 'undefined') model.error = false; //clear errors if (typeof model.uploadings != 'undefined' && model.uploadings != 0) return true; } return false; }, uploadUsingUpload: function (Upload, uploadUrl, guidToken, file, fileType, model, beforeCallback, afterCallback, objectState, antiForgeryToken) { var headtoken = {}; if (antiForgeryToken != null) headtoken = { '__RequestVerificationToken': antiForgeryToken }; file.uploading = true; if (typeof model != 'undefined' && model != null && typeof model.uploadings != 'undefined') model.uploadings++; if (typeof beforeCallback != 'undefined' && beforeCallback != null) beforeCallback(objectState, file); file.upload = Upload.upload({ url: uploadUrl, method: 'POST', fields: { guid: guidToken, type: fileType }, headers: headtoken, sendFieldsAs: 'form', file: file }); file.upload.then(function (response) { $timeout(function () { file.uploading = false; if (typeof model != 'undefined' && typeof model.uploadings != 'undefined') model.uploadings--; if (typeof afterCallback != 'undefined' && afterCallback != null) afterCallback(objectState, response.data !== 'true', file); if (response.data !== 'true') { if (typeof model != 'undefined') { model.error = true; model.errorMsg = response.data; } } else file.result = response.data; }); }, function (response) { file.uploading = false; if (typeof model != 'undefined' && typeof model.uploadings != 'undefined') model.uploadings--; file.progress = 0; if (response.status > 0 && typeof model != 'undefined') { if (typeof afterCallback != 'undefined' && afterCallback != null) afterCallback(objectState, true, file); model.error = true; model.errorMsg = response.data; } }); file.upload.progress(function (evt) { // Math.min is to fix IE which reports 200% sometimes file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total)); }); file.upload.xhr(function (xhr) { // xhr.upload.addEventListener('abort', function(){console.log('abort complete')}, false); }); }, convertDataUrlToBlob: function (Upload, dataUrl, name) { var file = Upload.dataUrltoBlob(dataUrl, name); return file; }, getFileUrl: function (file) { var URL = window.URL || window.webkitURL; var srcTmp = URL.createObjectURL(file); return srcTmp; }, parseStringToDate: function (dateString, format) { var dateReturn = null; dateString = dateString.replaceAll('/', '-'); format = format.replaceAll('/', '-'); var dateSplitted = dateString.split('-'); var formatSplitted = format.toUpperCase().split('-'); if (dateSplitted.length === 3 && formatSplitted.length === 3 && formatSplitted.includes('DD') && formatSplitted.includes('MM') && formatSplitted.includes('YYYY')) { var day = dateSplitted[formatSplitted.indexOf('DD')]; var month = parseInt(dateSplitted[formatSplitted.indexOf('MM')], 10) - 1; var year = dateSplitted[formatSplitted.indexOf('YYYY')]; if (day > 0 && 31 >= day && month >= 0 && 11 >= month) dateReturn = new Date(year, month, day); } return dateReturn; }, navigationStateControl: function (model) { model.navigating = false; model.errorMsg = null; model.error = false; var dataLayer = $window.dataLayer = $window.dataLayer || []; //overlay loading animation $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) { if (toParams != undefined && toParams.avoidLoading) { model.navigating = false; } else { model.navigating = true; } }); $rootScope.$on('$stateNotFound', function (event, toState, toParams, fromState, fromParams) { model.navigating = false; model.errorMsg = 'Page not found'; model.error = true; internalScrollToTop(); }); $rootScope.$on('$stateChangeError', function (event, toState, toParams, fromState, fromParams) { model.navigating = false; model.errorMsg = 'Error loading page'; model.error = true; internalScrollToTop(); }); $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) { model.navigating = false; if (typeof $window.__gaTracker === 'function') $window.__gaTracker('send', 'pageview', { page: $location.url(), title: toState.name }); dataLayer.push({ event: 'ngRouteChange', attributes: { route: $location.path(), title: toState.name } }); internalScrollToTop(); }); }, defineRequired: function (scope, model) { model.continue = false; scope.timeError = function (fld) { //se pone solo para controlar errores en timepicker hasta que se actualice if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return true; return fld.$error.time; } scope.wrongEmailError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.wrongEmail; } scope.customRequiredError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$viewValue === ''; } scope.customError = function (fld, prop) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || fld.$dirty || model.continue) && fld.$error[prop]; } scope.requiredError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.required; } scope.fromlistError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.fromlist; } scope.patternError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.pattern; } scope.uniqueError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.unique; } scope.equalsToError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.equalsTo; } scope.duplicatedError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.duplicated; } scope.dateError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.date; } scope.mindateError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.mindate; } scope.maxdateError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.maxdate; } scope.minlengthError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.minlength; } scope.maxlengthError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.maxlength; } scope.minError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.min; } scope.maxError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.max; } scope.integerError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.integer; } scope.numberError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.number; } scope.urlError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.url; } scope.maxdistanceError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.maxdistance; } scope.pointError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.point; } scope.formError = function (form) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (model.continue && form.$invalid); } scope.equalError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$error.equal; } scope.hasError = function (fld) { if (typeof fld == 'undefined' || typeof fld.$touched == 'undefined') return false; return (fld.$touched || model.continue) && fld.$invalid; } }, minDateCheck: function (scope, fld) { scope.$watch(fld, function (date) { var name = $("[ng-model='" + fld + "']").closest("form").attr("name") + "." + $("[ng-model='" + fld + "']").attr("name"); var mindate = $("[ng-model='" + fld + "']").attr("min-date"); if (typeof date != 'undefined' && date != null && date != '' && date < scope.$eval(mindate)) { scope.$eval(name).$setValidity('mindate', false); } else { scope.$eval(name).$setValidity('mindate', true); } }); }, maxDateCheck: function (scope, fld) { scope.$watch(fld, function (date) { var name = $("[ng-model='" + fld + "']").closest("form").attr("name") + "." + $("[ng-model='" + fld + "']").attr("name"); var maxdate = $("[ng-model='" + fld + "']").attr("max-date"); if (typeof date != 'undefined' && date != null && date != '' && date > scope.$eval(maxdate)) { scope.$eval(name).$setValidity('maxdate', false); } else { scope.$eval(name).$setValidity('maxdate', true); } }); }, getUTCDateFromTime: function (time) { return new Date(time + ((new Date(time)).getTimezoneOffset() * 60000)); }, serverUTCDateFromTime: function (time) { return new Date(time - ((new Date(time)).getTimezoneOffset() * 60000)); }, getUTCDateFromString: function (dates) { return this.getUTCDateFromTime((new Date(dates)).getTime()); }, getPrefix: function (culture) { var pre = CanonicalUrl.getFolderPrefix(culture); var prefix = ''; if (typeof pre !== 'undefined' && pre !== '') prefix = '/' + pre; return prefix; }, interceptor25x: function (scope, model) { model.showAlerts = {}; scope.$on('interceptor25x', function (event, argument) { if (argument.status === 250) { redirectWithDialog(model, argument.url, argument.error); } if (argument.status === 251 || argument.status === 252 || argument.status === 253 || argument.status === 254) { showMessageAlerts(model, argument.status, argument.msg); } }); }, interceptor23x: function (scope, model) { scope.$on('interceptor23x', function (event, argument) { if (argument.status === 231) { model.handled = true; model.errorResult = function () { model.error = false; model.errorResult = null; model.handled = false; model.navigating = true; $window.location.reload(); } model.working = false; model.errorMsg = Translator.getTranslation('error_server_connection'); model.error = true; } }); }, interceptor40x: function (scope, model) { scope.$on('interceptor40x', function (event, argument) { if (argument.status == 401) { redirectWithDialog(model, argument.url, Translator.getTranslation('modal_login_session_error')); } else { redirect(model, argument.url); } }); }, interceptor55x: function (scope, model) { scope.$on('interceptor55x', function (event, argument) { redirect(model, argument.url); }); }, timeout: function (scope) { $timeout(function () { var version = '4.2.0'; scope.capture = localStorage.getItem('capture' + version) || 'camera'; scope.accept = localStorage.getItem('accept' + version) || 'image/*,audio/*,video/*'; scope.acceptSelect = localStorage.getItem('acceptSelect' + version) || 'image/*,audio/*,video/*'; scope.disabled = localStorage.getItem('disabled' + version) == 'true' || false; scope.multiple = localStorage.getItem('multiple' + version) == 'true' || false; scope.allowDir = localStorage.getItem('allowDir' + version) == 'true' || true; scope.$watch('capture+accept+acceptSelect+disabled+capture+multiple+allowDir', function () { localStorage.setItem('capture' + version, scope.capture); localStorage.setItem('accept' + version, scope.accept); localStorage.setItem('acceptSelect' + version, scope.acceptSelect); localStorage.setItem('disabled' + version, scope.disabled); localStorage.setItem('multiple' + version, scope.multiple); localStorage.setItem('allowDir' + version, scope.allowDir); }); }); }, handleError: function (model, msg, inpage, blockClose) { model.errorAvoidClose = false; if (!model.handled) { if (typeof msg === 'undefined' || msg === null) msg = Translator.getTranslation('error_server_connection'); if (typeof inpage === 'undefined') inpage = false; model.working = false; if (inpage) { model.pageError = msg; } else { model.error = true; model.errorMsg = msg; } } //Avoid close modal if (blockClose) { model.errorAvoidClose = true; } }, copyToClipboard: function (elem) { // create hidden text element, if it doesn't already exist var targetId = "_hiddenCopyText_"; var isInput = elem.tagName === "INPUT" || elem.tagName === "TEXTAREA"; var origSelectionStart, origSelectionEnd; if (isInput) { // can just use the original source element for the selection and copy target = elem; origSelectionStart = elem.selectionStart; origSelectionEnd = elem.selectionEnd; } else { // must use a temporary form element for the selection and copy target = document.getElementById(targetId); if (!target) { var target = document.createElement("textarea"); target.style.position = "absolute"; target.style.left = "-9999px"; target.style.top = "0"; target.id = targetId; document.body.appendChild(target); } target.textContent = elem.textContent; } // select the content var currentFocus = document.activeElement; target.focus(); target.setSelectionRange(0, target.value.length); // copy the selection var succeed; try { succeed = document.execCommand("copy"); } catch (e) { succeed = false; } // restore original focus if (currentFocus && typeof currentFocus.focus === "function") { currentFocus.focus(); } if (isInput) { // restore prior selection elem.setSelectionRange(origSelectionStart, origSelectionEnd); } else { // clear temporary content target.textContent = ""; } return succeed; }, getPdfFile: function (data, urlName) { var characters = atob(data); var numbers = new Array(characters.length); for (let i = 0; i < characters.length; i++) { numbers[i] = characters.charCodeAt(i); } var arr = new Uint8Array(numbers); var blob = new Blob([arr], { type: "application/pdf" }); if (window.navigator.msSaveOrOpenBlob) // IE10+ window.navigator.msSaveOrOpenBlob(blob, urlName + ".pdf"); else { // Others var a = document.createElement("a"), url = URL.createObjectURL(blob); a.href = url; a.download = urlName + ".pdf"; document.body.appendChild(a); a.click(); setTimeout(function () { document.body.removeChild(a); window.URL.revokeObjectURL(url); }, 0); } }, createPath: function (obj, path) { try { return eval("obj." + path); } catch (e) { return undefined; } }, checkIfContains: function (array, input) { var retval = false; if (typeof array != 'undefined' && array != null) { $.each(array, function (value, key) { if (key == input) { retval = true; return false; } }); } return retval; }, returnByProperty: function (array, keyValue, valueToFind) { var retval = null; if (typeof array != 'undefined' && array != null) { $.each(array, function (value, key) { if (key[keyValue] == valueToFind) { retval = array[value]; return; } }); } return retval; }, containsAnyElement: function (haystack, arr) { return arr.some(function (v) { return haystack.indexOf(v) >= 0; }); }, datePickerOptions: function (culture) { var startingday = (culture == 'en-GB' ? 0 : 1); return { formatYear: 'yyyy', showWeeks: false, startingDay: startingday }; }, getCompleteDateHour: function (date, hour, addMinutes) { if (typeof date != 'undefined' && date != null && typeof hour != 'undefined' && hour != null) { var dt = new Date(date); dt.setHours(hour.getHours(), hour.getMinutes(), 0, 0); if (typeof addMinutes != 'undefined') { dt = new Date(dt.getTime() + addMinutes * 60000); } return dt; } return null; }, getTimezoneOffset: function () { var o = -(new Date().getTimezoneOffset() / 60); if (o >= 0) o = '+' + o; return o; }, generateGuid: function () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }, //field: object property //reverse: reverse sort by //primer: function like 'parseInt' sortBy: function (field, reverse, primer) { var key = primer ? function (x) { return primer(x[field]) } : function (x) { return x[field] }; reverse = !reverse ? 1 : -1; return function (a, b) { return a = key(a), b = key(b), reverse * ((a > b) - (b > a)); } }, replaceAt: function (value, index, replacement) { return value.substr(0, index) + replacement + value.substr(index + replacement.length); }, fullWindow: function (isFullWindow) { if (isFullWindow) //Disable scroll at body $('body').addClass("full-selector"); else $('body').removeClass("full-selector"); }, } }]); ngSharedApi.factory('httpResponseInterceptor', ['$q', '$location', '$injector', '$rootScope', 'CanonicalUrl', function ($q, $location, $injector, $rootScope, CanonicalUrl) { var httpResponseInterceptor = { response: function (response) { if (response.status === 250) { var resp = angular.fromJson(response.data); $rootScope.$broadcast('interceptor25x', { error: resp.message, url: resp.redirecturl, status: 250 }); } else if (response.status === 251 || response.status === 252 || response.status === 253 || response.status === 254) { var resp = angular.fromJson(response.data); $rootScope.$broadcast('interceptor25x', { msg: resp.message, data: resp.data, status: response.status, }); } else if (response.status === 231) { $rootScope.$broadcast('interceptor23x', { msg: response.data, status: response.status, }); } else if (response.status === 200) { var headers = response.headers("X-Responded-JSON"); if (headers != null && headers != "") { var objheaders = angular.fromJson(headers); var resp = angular.fromJson(response); var redirectUrlTmp; if (typeof objheaders.status !== 'undefined' && objheaders.status === 401 && typeof objheaders.headers !== 'undefined') { if (typeof resp.headers().location != 'undefined') { //webapi viene por headers redirectUrlTmp = resp.headers().location; } else { redirectUrlTmp = resp.data.redirecturl; } $rootScope.$broadcast('interceptor40x', { url: redirectUrlTmp, status: 401 }); } } } return response || $q.when(response); }, responseError: function (rejection) { var resp = angular.fromJson(rejection); if (rejection.status === 550) { var redirectUrlTmp; if (typeof resp.headers().location != 'undefined') { //webapi viene por headers redirectUrlTmp = resp.headers().location; } else { redirectUrlTmp = resp.data.redirecturl; } $rootScope.$broadcast('interceptor55x', { url: redirectUrlTmp, status: 550 }); } else if (rejection.status === 404) { $rootScope.$broadcast('interceptor40x', { url: CanonicalUrl.getNotFoundUrl(), status: 404 }); } else if (rejection.status === 403) { var redirectUrlTmp; if (typeof resp.headers().location != 'undefined') { //webapi viene por headers redirectUrlTmp = resp.headers().location; } else { redirectUrlTmp = resp.data.redirecturl; } $rootScope.$broadcast('interceptor40x', { url: redirectUrlTmp, status: 403 }); } else if (rejection.status === 231) { $rootScope.$broadcast('interceptor23x', { msg: resp.message, status: rejection.status, }); } return $q.reject(rejection); }, }; return httpResponseInterceptor; }]); })(); var ngSharedDirectives = angular.module("ngSharedDirectives", ['ngTranslator', 'ngFileUpload', 'ngPurchasesService', 'ngLookupsService', 'ngSharedApi', 'ngResource', 'ui.bootstrap']); (function (Translator, Purchases, Lookups, shApi, $timeout, $compile, $document, $http) { ngSharedDirectives.directive('buttonsRadioAdvanced', function () { return { restrict: 'E', scope: { model: '=', options: '=', id: '@', name: '@', btclass: '@', controlname: '@', notactive: '@', extrainfo: '@', showextrainfo: '=', mainClass: '@' }, link: function (scope, element, attrs, ctrls) { if (attrs.required !== undefined) { // If attribute required exists // ng-required takes a boolean scope.required = true; } }, controller: function ($scope) { $scope.isActive = function (option) { if (typeof $scope.id == 'undefined' || $scope.id == null) return option == $scope.model; else return option[$scope.id] == $scope.model; }; //this option is clickable (button not disabled) $scope.isDisabled = function (option) { if (typeof $scope.notactive == 'undefined' || $scope.notactive == null) return false; else return option[$scope.notactive]; }; $scope.getName = function (option) { if (typeof $scope.name == 'undefined' || $scope.name == null) return option; else return option[$scope.name]; } $scope.getValue = function (option) { if (typeof $scope.id == 'undefined' || $scope.id == null) return option; else return option[$scope.id]; } $scope.getAdditionalInfo = function (option) { if (typeof $scope.extrainfo == 'undefined' || $scope.extrainfo == null) return ''; else return option[$scope.extrainfo]; } }, template: " " + " " + "" }; }); /* -------------------------------------------------- Transfer filters ------------------------------------------------------------------ */ ngSharedDirectives.directive('transportationFilters', function ($timeout, Purchases, shApi) { //required vbles for all the fields (this way it will be highly configurable for validations depending on the "injected" business logic) var isOriginRequired = false; var isDestinationRequired = false; var isDateRequired = false; var isHourRequired = false; var isReturnDateRequired = false; var isReturnHourRequired = false; var avoidSpecificPointValidation = false; //Avoid specific point validation (for internal purposes) var showRoundTrip = false; var peopleShowOptions = false; var parentForm; return { restrict: 'E', //prefix for the urls like .com/ar (que no tienen dominio propio) scope: { origin: '=', destination: '=', prefix: '=', people: '=', date: '=', transportationDatepickerOptions: '=', minDate: '=', maxDate: '=', culture: '=', roundTrip: '=', returnDate: '=', maxReturnDate: '=', returnDateGap: '=', arrayTranslations: '=', isValidForm: '=', errorsList: '=', searchTrigger: '=', inputEvent: '=', blockForm: '=', isPlaceAmendable: '=', showTransferFilters: '=', callbackCustomAction: '=', resetScreen: '=', serviceTypes: '=', serviceOptions: '=', providerOptions: '=', providers: '=', totalCount: '=', showOnScreenLimit: '=', transferSortType: '=', transferSortFilterOptions: '=' }, link: function (scope, elem, attrs) { shApi.defineRequired(scope, scope); //load the booleans for the required fields //the required fields depend on the business logic, it's not the directive business if (attrs.originRequired !== undefined) isOriginRequired = (attrs.originRequired === 'true' || attrs.originRequired === ''); if (attrs.destinationRequired !== undefined) isDestinationRequired = (attrs.destinationRequired === 'true' || attrs.destinationRequired === ''); if (attrs.isDateRequired !== undefined) isDateRequired = (attrs.isDateRequired === 'true' || attrs.isDateRequired === ''); if (attrs.isHourRequired !== undefined) isHourRequired = (attrs.isHourRequired === 'true' || attrs.isHourRequired === ''); if (attrs.isReturnDateRequired !== undefined) isReturnDateRequired = (attrs.isReturnDateRequired === 'true' || attrs.isReturnDateRequired === ''); if (attrs.isReturnHourRequired !== undefined) isReturnHourRequired = (attrs.isReturnHourRequired === 'true' || attrs.isReturnHourRequired === ''); if (attrs.mydateformat !== undefined) scope.mydateformat = attrs.mydateformat; if (attrs.showPeopleOptions !== undefined) scope.showPeopleOptions = (attrs.showPeopleOptions == "true" || attrs.showPeopleOptions == ''); if (attrs.avoidSpecificPointValidation !== undefined) avoidSpecificPointValidation = (attrs.avoidSpecificPointValidation == "true" || attrs.avoidSpecificPointValidation == ''); if (attrs.showRoundTrip !== undefined) scope.showRoundTrip = (attrs.showRoundTrip == 'true' || attrs.showRoundTrip == ''); if (attrs.showLangugageSelection !== undefined) scope.showLangugageSelection = (attrs.showLangugageSelection == 'true' || attrs.showLangugageSelection == ''); //initializations scope.errorsList = []; scope.selectedOrigin = ''; scope.selectedDestination = ''; scope.destinationChangedByCode = false; scope.originChangedByCode = false; //initialize vbles used in validation scope.originNoResults = false; scope.destinationNoResults = false; scope.orgGuid = shApi.generateGuid(); scope.destGuid = shApi.generateGuid(); //set dates if (scope.minDate === undefined || scope.minDate === null) { scope.minDate = new Date(); } if (scope.maxDate === undefined || scope.maxDate === null) { scope.maxDate = new Date(); scope.maxDate.setFullYear(2025); } if (scope.date === undefined || scope.date === null) { scope.date = shApi.getCompleteDateHour(scope.minDate, scope.minDate, 60);//initialize selectedDate } scope.selectedDate = scope.date; scope.selectedHour = scope.date; scope.returnDateChangedByCode = false; scope.minReturnDate = shApi.getCompleteDateHour(scope.date, scope.date, scope.returnDateGap == undefined ? 1 : scope.returnDateGap); scope.returnDate = scope.minReturnDate; scope.selectedReturnDate = scope.returnDate; scope.selectedReturnHour = scope.returnDate; //getting the form (required to mark validations on it) parentForm = scope.parentForm = elem.parent().controller('form'); //translation method scope.translate = function (key) { return scope.arrayTranslations[key]; } //load tranlations for internal directives or attributesi if (!scope.translationsLoaded) { //people selector scope.inputmsg = scope.translate("_bookservice_travelers"); scope.personmsg = scope.translate("_bookservice_person"); scope.peoplemsg = scope.translate("_bookservice_people"); scope.adultmsg = scope.translate("_bookservice_adult"); scope.adultsmsg = scope.translate("_bookservice_adults"); scope.childmsg = scope.translate("_bookservice_child"); scope.childrenmsg = scope.translate("_bookservice_children"); scope.infantmsg = scope.translate("_bookservice_infant"); scope.infantsmsg = scope.translate("_bookservice_infants"); scope.undertwelvemsg = scope.translate("_bookservice_childrenundertwelve"); scope.underfivemsg = scope.translate("_bookservice_childrenunderfive"); scope.closemsg = scope.translate("_bookservice_apply"); scope.bookserviceprovider = scope.translate("_sales_viewer_table_providers"); scope.bookserviceproviderselected = scope.translate("_bookservice_provider_selected"); scope.childseatmessage = scope.translate("_child_seat_message"); //date picker scope.dateOriginalTitle = scope.translate("_bookservice_dateservice_input_placeholder"); //hour control scope.hourTranslation = scope.translate("_bookservice_hourservice_input_placeholder"); scope.returnHourTranslation = scope.translate("_roundtrip_selector_hour"); scope.translationsLoaded = true; //place holders origin/destination scope.originPlaceHolder = scope.translate("_bookservice_addresssearch_placeholder"); scope.destinationPlaceHolder = scope.translate("_bookservice_addresssearch_placeholder"); //roundtrip scope.mainoptionmsg = scope.translate("_roundtrip_selector_oneway"); scope.alternativemsg = scope.translate("_roundtrip_selector_return"); scope.reestablishtext = scope.translate("_bookservice_reestablish_text"); //Filter scope.searchresultstext = scope.translate("_bookservice_searchresults_text"); scope.noresults = scope.translate("_bookservice_foundresults_none"); scope.applyall = scope.translate("_bookservice_apply_all"); scope.dateTranslation = scope.translate("_bookservice_dateservice_title"); scope.typeoftransfer = scope.translate("_cartdetail_service_apartmentcategory"); scope.more = scope.translate("_bookservice_more"); scope.deleteall = scope.translate("_bookservice_delete_all"); scope.less = scope.translate("_bookservice_less"); scope.traveller = scope.translate("_booking_additionalconfiguration_traveller"); scope.travellers = scope.translate("_booking_additionalconfiguration_travellers_step_title"); scope.returndatetext = scope.translate("_roundtrip_selector_date"); scope.serviceconfiguration = scope.translate("_roundtrip_service_configuration"); scope.onewaydata = scope.translate("_roundtrip_one_way_data"); scope.addreturn = scope.translate("_roundtrip_add_return"); scope.enterdatetimeerror = scope.translate("_roundtrip_enter_date_time"); scope.clearfilters = scope.translate("_roundtrip_clear_filters"); scope.onewaydatehourtag = scope.translate("_roundtrip_one_way_date_hour"); scope.returndatehourtag = scope.translate("_roundtrip_return_date_hour"); scope.indicatepeople = scope.translate("_roundtrip_indicate_people"); scope.noadulterror = scope.translate("_roundtrip_error_no_adult"); scope.errordatetext = scope.translate("_orderslist_dates_error"); scope.searchservicetext = scope.translate("_roundtrip_error_search_services"); scope.clearsearch = scope.translate("_clear_search"); } $(document).ready(function (e) { scope.Init(); }); }, controller: function ($scope) { $scope.Init = function () { //initialization origin/destination setSelectedOrigin(); setSelectedDestination(); //run validations validate("origin"); validate("destination"); } //initializeOrigin function setSelectedOrigin() { if ($scope.origin != null && $scope.origin.placeId != null) { if ($scope.selectedOrigin == '') $scope.selectedOrigin = {}; //TODO: Resolve by placeId, a method is needed to fill all properties of $scope.selectedOrigin //check that watch event is not caused due to selectedOriginChanged, because the origin data is different from selectedOrigin if ($scope.selectedOrigin.PlaceId != $scope.origin.placeId) { $scope.selectedOrigin.PlaceId = $scope.origin.placeId; $scope.selectedOrigin.AutoCompleteText = $scope.origin.displayName; } $scope.modalForSearch(); } else $scope.selectedOrigin = ''; } function setSelectedDestination() { if ($scope.destination != null && $scope.destination.placeId != null) { if ($scope.selectedDestination == '') $scope.selectedDestination = {}; //TODO: Resolve by placeId, a method is needed to fill all properties of $scope.selectedDestination //check that watch event is not caused due to selectedOriginChanged, because the origin data is different from selectedOrigin if ($scope.selectedDestination.PlaceId != $scope.destination.placeId) { $scope.selectedDestination.PlaceId = $scope.destination.placeId; $scope.selectedDestination.AutoCompleteText = $scope.destination.displayName; } $scope.modalForSearch(); } else $scope.selectedDestination = ''; } //-- Validations ---------------------------- function validate(which) { if ($scope.errorsList == undefined) $scope.errorsList = []; switch (which) { case 'origin': var isOriginLoaded = parentForm.origin !== undefined; var isOriginInvalid = false; if (isOriginLoaded) { if (isOriginRequired && !$scope.originNoResults && ($scope.selectedOrigin.length == 0 && $scope.selectedOrigin.PlaceId == undefined)) { isOriginInvalid = true; if (parentForm.origin.$touched) parentForm.origin.$setValidity('required', false); //reset point validity parentForm.origin.$setValidity('point', true); } else { parentForm.origin.$setValidity('required', true); } if ((!$scope.originNoResults || parentForm.origin.$touched) && $scope.selectedOrigin != '' && $scope.selectedOrigin.PlaceId == undefined) { isOriginInvalid = true; parentForm.origin.$setValidity('fromlist', false); //reset point validity parentForm.origin.$setValidity('point', true); } else { parentForm.origin.$setValidity('fromlist', true); } //if origin is not airport or it's not specific custom error if (!$scope.originNoResults && !($scope.selectedOrigin.PlaceId == undefined)) { if (avoidSpecificPointValidation) parentForm.origin.$setValidity('point', true); else { if ($scope.selectedOrigin.IsSpecific || $scope.selectedOrigin.IsAirport || $scope.selectedOrigin.IsStation) { parentForm.origin.$setValidity('point', true); } else { isOriginInvalid = true; parentForm.origin.$setValidity('point', false); } } } } else isOriginInvalid = true; if (isOriginRequired && isOriginInvalid) { if (!$scope.errorsList.includes('origin')) $scope.errorsList.push('origin'); } else { var index = $scope.errorsList.indexOf('origin'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); setTimeout(function () { document.getElementById('destinationTransportation').focus(); }, 100); } if ($scope.errorsList.indexOf('destination') == -1 && $scope.errorsList.indexOf('origin') == -1) { $scope.showTransferFilters = true; } break; case 'destination': var isDestinationLoaded = parentForm.destination !== undefined; var isDestinationInvalid = false; if (isDestinationLoaded) { if (isDestinationRequired && $scope.selectedDestination.length == 0 && $scope.selectedDestination.PlaceId == undefined) { isDestinationInvalid = true; if (parentForm.destination !== undefined && parentForm.destination.$touched) parentForm.destination.$setValidity('required', false); } else { parentForm.destination.$setValidity('required', true); } if ((!$scope.destinationNoResults || parentForm.destination.$touched) && $scope.selectedDestination != '' && $scope.selectedDestination.PlaceId == undefined) { //destination = name attribute isDestinationInvalid = true; parentForm.destination.$setValidity('fromlist', false); } else { parentForm.destination.$setValidity('fromlist', true); } //if destination is not airport or it's not specific custom error if ($scope.roundTrip && !$scope.destinationNoResults && $scope.selectedDestination != '' && !($scope.selectedDestination.PlaceId == undefined)) { if (avoidSpecificPointValidation) { parentForm.destination.$setValidity('point', true); } else { if ($scope.selectedDestination.IsSpecific || $scope.selectedDestination.IsAirport || $scope.selectedDestination.IsStation) { parentForm.destination.$setValidity('point', true); } else { isDestinationInvalid = true; parentForm.destination.$setValidity('point', false); } } } else { parentForm.destination.$setValidity('point', true); } } else isDestinationInvalid = true; if (isDestinationRequired && isDestinationInvalid) { if (!$scope.errorsList.includes('destination')) $scope.errorsList.push('destination'); } else { var index = $scope.errorsList.indexOf('destination'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } if ($scope.errorsList.indexOf('destination') == -1 && $scope.errorsList.indexOf('origin') == -1) { $scope.showTransferFilters = true; } break; case 'date': var isDateInvalid = false; //TODO: Different if is required or not if ($scope.date == null) { if (parentForm.goDate.$touched) parentForm.goDate.$setValidity('required', false); isDateInvalid = true; } else { parentForm.goDate.$setValidity('required', true); if ($scope.date > $scope.maxDate) { parentForm.goDate.$setValidity('maxdate', false); isDateInvalid = true; } else { parentForm.goDate.$setValidity('maxdate', true); } if ($scope.date < $scope.minDate) { parentForm.goDate.$setValidity('mindate', false); isDateInvalid = true; } else { parentForm.goDate.$setValidity('mindate', true); } } if (isDateInvalid) { if (!$scope.errorsList.includes('date')) $scope.errorsList.push('date'); } else { var index = $scope.errorsList.indexOf('date'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; case 'returnDate': var isReturnDateInvalid = false; if ($scope.roundTrip) { if ($scope.returnDate == null) { parentForm.returnDate.$setValidity('required', false); isReturnDateInvalid = true; } else { parentForm.returnDate.$setValidity('required', true); if ($scope.returnDate > $scope.maxReturnDate) { parentForm.returnDate.$setValidity('maxdate', false); isReturnDateInvalid = true; } else { parentForm.returnDate.$setValidity('maxdate', true); } if ($scope.returnDate < $scope.minReturnDate) { parentForm.returnDate.$setValidity('mindate', false); isReturnDateInvalid = true; } else { parentForm.returnDate.$setValidity('mindate', true); } } } else { parentForm.returnDate.$setValidity('required', true); parentForm.returnDate.$setValidity('mindate', true); parentForm.returnDate.$setValidity('maxdate', true); } if (isReturnDateInvalid) { if (!$scope.errorsList.includes('returnDate')) $scope.errorsList.push('returnDate'); } else { var index = $scope.errorsList.indexOf('returnDate'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; } $scope.isValidForm = ($scope.errorsList.length == 0); } function setMinReturnDateHour() { //Update min return date if (!$scope.errorsList.includes('date')) { $scope.minReturnDate = shApi.getCompleteDateHour($scope.date, $scope.date, $scope.returnDateGap == undefined ? 1 : $scope.returnDateGap); validate("returnDate"); } } function setReturnDateHour() { if (!$scope.errorsList.includes('date')) { $scope.minReturnDate = shApi.getCompleteDateHour($scope.date, $scope.date, $scope.returnDateGap == undefined ? 1 : $scope.returnDateGap); $scope.returnDate = $scope.minReturnDate; $scope.selectedReturnDate = $scope.selectedReturnHour = $scope.returnDate; validate("returnDate"); } } //vbles used to check the validations (only checked in watch in the state of focus lost) $scope.originFocusLost = function () { $scope.originHasFocus = false; $scope.orgGuid = shApi.generateGuid(); $scope.inputEvent = 'blur'; //if selectedOrigin is null it means the field has been just initialized or just cleared by the purchase process if ($scope.selectedOrigin != null) { $timeout(function () { //if (!$scope.errorsList.includes("origin")) { //we put this here because the on-blur of the $touched event goes after the on-blur (focus lost) //check if routeTab is still selected if (parentForm.origin != undefined) { parentForm.origin.$setTouched(); validate('origin'); } //} }, 1000); } } $scope.originFocusGained = function () { $scope.originHasFocus = true; $scope.inputEvent = 'focus'; } $scope.clearOrigin = function () { $scope.selectedOrigin = ''; } $scope.clearDestination = function () { $scope.selectedDestination = ''; } //aim to check if the user has left the input field (focus lost) without selecting a search item result $scope.destinationFocusLost = function () {//timeout to ensure the destinationChanged has been executed $scope.destinationHasFocus = false; $scope.destGuid = shApi.generateGuid(); $scope.inputEvent = 'blur'; //if selectedDestination is null it means the field has been just initialized or just cleared by the purchase process if ($scope.selectedDestination != null) { $timeout(function () { //if (!$scope.errorsList.includes("destination")) { //we put this here because the on-blur of the $touched event goes after the on-blur (focus lost) //check if routeTab is still selected if (parentForm.destination != undefined) { parentForm.destination.$setTouched(); validate('destination'); } //} }, 1000); } } $scope.destinationFocusGained = function () { $scope.inputEvent = 'focus'; $scope.destinationHasFocus = true; } //--End Validations ---------------------------- // ---------------------------------------------------------------------------------------------------- $scope.newSortType = function (val) { $scope.transferSortType = val; } $scope.isModalOpen = false; $scope.showBackdrop = false; $scope.showCountTotal = false; $scope.filtersSelected = 0; $scope.callApply = false; $scope.tagClear = false; $scope.isFirstSearch = true; $scope.errorReturnsDate = false; $scope.ignoreFirst = false; $scope.searchTriggered = false; $scope.showDateTag = false; $scope.dataIsTheSame = false; $scope.animationFilters = function (option) { if (($scope.selectedOrigin.PlaceId != undefined && $scope.selectedDestination.PlaceId != undefined) || (option == true)) { $scope.showBackdrop = !$scope.showBackdrop; $scope.isModalOpen = !$scope.isModalOpen; if (!$scope.isModalOpen) { setTimeout(function () { $scope.showBackdrop = false; $("html").css("overflow", ""); $("body").css("overflow", ""); }, 300); } else { $scope.showBackdrop = true; $("html").css("overflow", "hidden"); $("body").css("overflow", "hidden"); } if ($scope.isModalOpen == false) { setTimeout(function () { $scope.showBackdrop = false; }, 300); } } } $scope.totalOverflowing = 0; $scope.overflow = function () { setTimeout(function () { $(window).on('resize', function () { var element = document.querySelector('#overflowbar'); if (element != undefined && element != null) { if ((element.offsetHeight < element.scrollHeight) || (element.offsetWidth < element.scrollWidth)) { // your element have overflow $("#button-overflow").css("display", ""); $(".filter-style__tag-separator").css("display", ""); if ($(document).width() > 1366) { var parentContainerWidth = $(".filter-style").width() - 680; } else { var parentContainerWidth = $("#overflowbar").width() - 50; } $(".tag-box__tag-box").children().css("visibility", ""); var elementCount = $('.tag-box__tag-selected').filter(function () { var hidden = $(this).offset().left >= parentContainerWidth; if (hidden == true) { $(this).css("visibility", "hidden"); } else { $(this).css("visibility", ""); } return hidden; }).length; $scope.totalOverflowing = elementCount; } else { //your element don't have overflow $("#button-overflow").css("display", "none"); $(".filter-style__tag-separator").css("display", "none"); $scope.totalOverflowing = 0; $(".tag-box__tag-box").children().css("visibility", ""); } } }).resize(); }, 200); } $(window).on('resize', function () { var element = document.querySelector('#overflowbar'); if (element != undefined && element != null) { if ((element.offsetHeight < element.scrollHeight) || (element.offsetWidth < element.scrollWidth)) { // your element have overflow $("#button-overflow").css("display", ""); $(".filter-style__tag-separator").css("display", ""); if ($(document).width() > 1366) { var parentContainerWidth = $(".filter-style").width() - 680; } else { var parentContainerWidth = $("#overflowbar").width() - 50; } $(".tag-box__tag-box").children().css("visibility", ""); var elementCount = $('.tag-box__tag-selected').filter(function () { var hidden = $(this).offset().left >= parentContainerWidth; if (hidden == true) { $(this).css("visibility", "hidden"); } else { $(this).css("visibility", ""); } return hidden; }).length; $scope.totalOverflowing = elementCount; } else { //your element don't have overflow $("#button-overflow").css("display", "none"); $(".filter-style__tag-separator").css("display", "none"); $scope.totalOverflowing = 0; $(".tag-box__tag-box").children().css("visibility", ""); } } }).resize(); $(".tag-box__tag-selected").css("visibility", ""); $scope.closeModal = function () { if ($scope.searchTriggered == false || $scope.selectedDestination == "" || $scope.selectedDestination.PlaceId == "undefined" || ($scope.isFirstSearch == true && $scope.tagClear == false) ) { $scope.clearDestination(); $timeout(function () { $("#destinationTransportation").focus(); $scope.parentForm.destination.$setPristine(); $scope.parentForm.destination.$setUntouched(); }, 0); $scope.animationFilters(true); } else { $scope.restoreLastData(); $scope.showDateTag = true; $scope.showPeopleTag = true; $scope.isFirstSearch = false; $scope.tagClear = false; $scope.animationFilters(); } } $scope.applyTransferFilters = function (option) { var error = false; $scope.isFirstSearch = false; $scope.tagClear = false; $scope.checkDates(); if ($scope.date == null || ($scope.people.numPeople == 0 || $scope.people.detailedPeople.adults == 0) || ($scope.roundTrip == true && $scope.returnDate == null) || ($scope.roundTrip == true && $scope.errorReturnsDate == true) ) { error = true; } if (error == false) { $scope.checkIfSame(); if ($scope.dataIsTheSame == false) { $scope.fillTags(); $scope.overflow(); $scope.searchTriggered = true; if (option != "noModal") { $scope.animationFilters(); validate('destination'); } $scope.searchTrigger = "filterApply"; } else { if (option == "noModal") { $scope.fillTags(); if ($scope.searchTrigger == "switchLRDirectionButton") { $scope.searchTrigger = "switchRLDirectionButton"; } else { $scope.searchTrigger = "switchLRDirectionButton"; } } else { $scope.animationFilters(); validate('destination'); } } } } $scope.clearDateHourTag = function () { $scope.isFirstSearch = true; $scope.showDateTag = false; $scope.tagClear = true; $scope.date = null; $scope.selectedDate = null; $scope.selectedHour = null; $scope.animationFilters(); } $scope.clearReturnDateHourTag = function () { $scope.isFirstSearch = false; $scope.returnDate = null; $scope.selectedReturnDate = null; $scope.selectedReturnHour = null; $scope.selectedReturnDateTag = null; $scope.selectedReturnHourTag = null; $scope.showReturn = false; $scope.roundTrip = false; $scope.applyTransferFilters("noModal"); } $scope.clearPeopleTag = function () { $scope.isFirstSearch = true; $scope.showPeopleTag = false; $scope.tagClear = true; $scope.people = {}; $scope.people.numPeople = 0; $scope.people.detailedPeople = {}; $scope.people.detailedPeople.adults = 0; $scope.people.detailedPeople.childrenUnderFive = 0; $scope.people.detailedPeople.childrenUnderTwelve = 0; $scope.animationFilters(); } $scope.modalForSearch = function () { if ( ($scope.searchTrigger != "switchLRDirectionButton" && $scope.searchTrigger != "switchRLDirectionButton") && $scope.selectedOrigin != null && $scope.selectedOrigin != "" && $scope.selectedOrigin.PlaceId != undefined && $scope.selectedDestination != null && $scope.selectedDestination != "" && $scope.selectedDestination.PlaceId != undefined && $scope.ignoreFirst == false ) { $scope.clearFilters(); $scope.isFirstSearch = true; $scope.animationFilters(); } if ($scope.ignoreFirst == true) { $scope.ignoreFirst = false; } } $scope.clearFilters = function () { $scope.date = null; $scope.returnDate = null; $scope.selectedDate = null; $scope.selectedHour = null; $scope.selectedReturnDate = null; $scope.selectedReturnHour = null; $scope.showReturn = false; $scope.roundTrip = false; $scope.people = {}; $scope.people.numPeople = 0; $scope.people.detailedPeople = {}; $scope.people.detailedPeople.adults = 0; $scope.people.detailedPeople.childrenUnderFive = 0; $scope.people.detailedPeople.childrenUnderTwelve = 0; $scope.selectedServiceTypes = []; $scope.tagClear = true; $scope.isFirstSearch = true; } $scope.checkDates = function () { $scope.errorReturnsDate = false; var oneWayDateComp = null; var returnDateComp = null; if ($scope.date != null) oneWayDateComp = new Date($scope.date); if ($scope.returnDate != null) returnDateComp = new Date($scope.returnDate); if ( (oneWayDateComp != null && returnDateComp != null) && ( oneWayDateComp.getTime() >= returnDateComp.getTime() ) ) { $scope.errorReturnsDate = true; } } $scope.fillTags = function () { $scope.selectedDateTag = $scope.selectedDate; $scope.selectedHourTag = $scope.selectedHour; $scope.showReturnTag = $scope.showReturn; $scope.roundTripTag = $scope.roundTrip; $scope.selectedReturnDateTag = $scope.selectedReturnDate; $scope.selectedReturnHourTag = $scope.selectedReturnHour; $scope.selectedFullDateTag = $scope.date; $scope.selectedFullReturnDateTag = $scope.returnDate; $scope.peopleTag = {}; //Tag people $scope.peopleTag.numPeople = $scope.people.numPeople; $scope.peopleTag.detailedPeople = {}; $scope.peopleTag.detailedPeople.adults = $scope.people.detailedPeople.adults; $scope.peopleTag.detailedPeople.childrenUnderFive = $scope.people.detailedPeople.childrenUnderFive; $scope.peopleTag.detailedPeople.childrenUnderTwelve = $scope.people.detailedPeople.childrenUnderTwelve; $scope.selectedServiceTypesAlone = []; //Tag type of transfer angular.forEach($scope.selectedServiceTypes, function (checkTags, key) { var pushToRepeat = []; pushToRepeat = checkTags; pushToRepeat.filled = true; $scope.selectedServiceTypesAlone.push(pushToRepeat); }); $scope.showDateTag = true; $scope.showPeopleTag = true; } $scope.restoreLastData = function () { $scope.selectedDate = $scope.selectedDateTag; $scope.selectedHour = $scope.selectedHourTag; $scope.showReturn = $scope.showReturnTag; $scope.roundTrip = $scope.roundTripTag; $scope.selectedReturnDate = $scope.selectedReturnDateTag; $scope.selectedReturnHour = $scope.selectedReturnHourTag; $scope.people = {}; //Tag people $scope.people.numPeople = $scope.peopleTag.numPeople; $scope.people.detailedPeople = {}; $scope.people.detailedPeople.adults = $scope.peopleTag.detailedPeople.adults; $scope.people.detailedPeople.childrenUnderFive = $scope.peopleTag.detailedPeople.childrenUnderFive; $scope.people.detailedPeople.childrenUnderTwelve = $scope.peopleTag.detailedPeople.childrenUnderTwelve; $scope.selectedServiceTypes = $scope.selectedServiceTypesAlone; $scope.selectedServiceTypesAlone = []; //Tag type of transfer angular.forEach($scope.selectedServiceTypes, function (checkTags, key) { var pushToRepeat = []; pushToRepeat = checkTags; pushToRepeat.filled = true; $scope.selectedServiceTypesAlone.push(pushToRepeat); }); } $scope.$watch('callApply', function (newVal, oldVal) { if (newVal !== oldVal) { $scope.applyTransferFilters("noModal"); } }, true); $scope.checkIfSame = function () { if ($scope.searchTriggered == true) { $scope.dataIsTheSame = true; var checkFullDate = new Date($scope.date); var checkSelectedFullDateTag = new Date($scope.selectedFullDateTag); //Date var dateIsDifferent = Math.abs(checkFullDate.getTime() - checkSelectedFullDateTag.getTime()); if (dateIsDifferent > 0) return $scope.dataIsTheSame = false; if ($scope.showReturnTag !== $scope.showReturn) return $scope.dataIsTheSame = false; if ($scope.roundTripTag !== $scope.roundTrip) return $scope.dataIsTheSame = false; var checkFullReturnDate = new Date($scope.returnDate); var checkSelectedFullReturnDateTag = new Date($scope.selectedFullReturnDateTag); //Return date var dateIsDifferent = Math.abs(checkFullReturnDate.getTime() - checkSelectedFullReturnDateTag.getTime()); if (dateIsDifferent > 0) return $scope.dataIsTheSame = false; if ($scope.peopleTag.detailedPeople.adults !== $scope.people.detailedPeople.adults) return $scope.dataIsTheSame = false; if ($scope.peopleTag.detailedPeople.childrenUnderFive !== $scope.people.detailedPeople.childrenUnderFive) return $scope.dataIsTheSame = false; if ($scope.peopleTag.detailedPeople.childrenUnderTwelve !== $scope.people.detailedPeople.childrenUnderTwelve) return $scope.dataIsTheSame = false; let privateType = $scope.selectedServiceTypes.find(o => o.key === 'Private'); let privateTypeTag = $scope.selectedServiceTypesAlone.find(o => o.key === 'Private'); if (privateType !== privateTypeTag) return $scope.dataIsTheSame = false; let sharedType = $scope.selectedServiceTypes.find(o => o.key === 'Shared'); let sharedTypeTag = $scope.selectedServiceTypesAlone.find(o => o.key === 'Shared'); if (sharedType !== sharedTypeTag) return $scope.dataIsTheSame = false; } } $scope.backToSearch = function () { if ($scope.selectedOrigin != null && $scope.selectedOrigin != "" && $scope.selectedOrigin.PlaceId != undefined && $scope.selectedDestination != null && $scope.selectedDestination != "" && $scope.selectedDestination.PlaceId != undefined) { $scope.clearFilters(); $scope.fillTags(); $scope.searchTriggered = false; $scope.showDateTag = false; $scope.showPeopleTag = false; $scope.searchTrigger = ""; $scope.dataIsTheSame = false; if ($scope.resetScreen == "resetS") { $scope.resetScreen = "reset"; } else { $scope.resetScreen = "resetS"; } } $timeout(function () { if ($scope.selectedOrigin == "") $("#originTransportation").focus(); else if ($scope.selectedDestination == "") $("#destinationTransportation").focus(); }, 0); $scope.parentForm.$setPristine(); $scope.parentForm.$setUntouched(); $scope.parentForm.origin.$setPristine(); $scope.parentForm.origin.$setUntouched(); $scope.parentForm.destination.$setPristine(); $scope.parentForm.destination.$setUntouched(); } // ----------------------------------------------------------------------------------- $scope.getAddressesPredictionForOrigin = function (filter) { return getAddressesPrediction(filter, true); }; $scope.getAddressesPredictionForDestination = function (filter) { return getAddressesPrediction(filter, false); }; //function that is loaded in the template function getAddressesPrediction(filter, isOrigin) { var placeId = null; var guid = null; if (isOrigin && parentForm.destination.$valid && $scope.selectedDestination != '' && $scope.selectedDestination.PlaceId != undefined) { placeId = $scope.selectedDestination.PlaceId; } else if (!isOrigin && parentForm.origin.$valid & $scope.selectedOrigin != '' && $scope.selectedOrigin.PlaceId != undefined) { placeId = $scope.selectedOrigin.PlaceId; } if (isOrigin) guid = $scope.orgGuid; else guid = $scope.destGuid; if (guid == null) guid = shApi.generateGuid(); return Purchases.getAddressesPrediction(filter, placeId, $scope.prefix, guid) .then(function (response) { return response; }); }; $scope.switchPoints = function () { //set errors on both before switching, later it will be validated if (!$scope.errorsList.includes('destination')) $scope.errorsList.push('destination'); if (!$scope.errorsList.includes('origin')) $scope.errorsList.push('origin'); var selectedDestination = $scope.selectedDestination; $scope.selectedDestination = $scope.selectedOrigin; $scope.selectedOrigin = selectedDestination; //validations after change.. var auxOriginNoResults = $scope.originNoResults; $scope.originNoResults = $scope.destinationNoResults; $scope.destinationNoResults = auxOriginNoResults; if ($scope.searchTriggered == true) { $scope.applyTransferFilters("noModal"); } else { $scope.ignoreFirst = true; validate("origin"); validate("destination"); } }; //In case origin is updated programatically $scope.$watch('origin', function (newOrg, oldOrg) { if (newOrg !== oldOrg) { setSelectedOrigin(); } }, true);//true as third parameter to check internal properties //In case origin is updated programatically $scope.$watch('destination', function (newOrg, oldOrg) { if (newOrg !== oldOrg) { setSelectedDestination(); } }, true);//true as third parameter to check internal properties ///model change events // ///We use watchs insted of ng-change because... ///on element selected with keyboard enter ng-change doesn't work // ng-change is executed before the binding is updated which is problematic when communicating changes to the outer controller through the callback function $scope.$watch('selectedOrigin', function (newOrg, oldOrg) { if (newOrg !== oldOrg) { if ($scope.selectedOrigin != null && $scope.selectedOrigin.PlaceId != null) { var sameOrigin = false; if (($scope.origin != null && $scope.origin != undefined) && $scope.origin.placeId == $scope.selectedOrigin.PlaceId) { sameOrigin = true; } //initialize only if it's undefined... to not remove data on the controller if ($scope.origin == undefined) $scope.origin = {}; $scope.origin.placeId = $scope.selectedOrigin.PlaceId; $scope.origin.displayName = $scope.selectedOrigin.AutoCompleteText; $scope.origin.isAirport = $scope.selectedOrigin.IsAirport; $scope.origin.isStation = $scope.selectedOrigin.IsStation; if ($scope.isPlaceAmendable == true) { $scope.$parent.$parent.originIATAToChange = null; } if (sameOrigin == true) { setSelectedOrigin(); } } //execute the validations first validate("origin"); } }, true); $scope.$watch('selectedDestination', function (newOrg, oldOrg) { if (newOrg !== oldOrg) { if ($scope.selectedDestination != null && $scope.selectedDestination.PlaceId != null) { var sameDestination = false; if (($scope.destination != null && $scope.destination != undefined) && $scope.destination.placeId == $scope.selectedDestination.PlaceId) { sameDestination = true; } //initialize only if it's undefined... to not remove data on the controller if ($scope.destination == undefined) $scope.destination = {}; $scope.destination.placeId = $scope.selectedDestination.PlaceId; $scope.destination.displayName = $scope.selectedDestination.AutoCompleteText; $scope.destination.isAirport = $scope.selectedDestination.IsAirport; $scope.destination.isStation = $scope.selectedDestination.IsStation; if ($scope.isPlaceAmendable == true) { $scope.$parent.$parent.destinationIATAToChange = null; } if (sameDestination == true) { setSelectedDestination(); } } //update validations validate("destination"); } }, true); $scope.$watch('date', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var selectedDate = ($scope.selectedDate != undefined && $scope.selectedDate != null) ? $scope.selectedDate.getTime() : $scope.selectedDate; if (newValDate !== oldValDate && newValDate !== selectedDate) { if (newVal != null) { $scope.selectedDate = $scope.selectedHour = newVal; } else { $scope.selectedDate = $scope.selectedHour = null; } } });//true as third parameter to check internal properties $scope.$watch('returnDate', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; if (newValDate !== oldValDate && $scope.selectedReturnDate != null && newValDate !== $scope.selectedReturnDate.getTime()) { if (newVal != null) { $scope.selectedReturnDate = $scope.selectedReturnHour = newVal; } else { $scope.selectedReturnDate = $scope.selectedReturnHour = null; } } });//true as third parameter to check internal properties //end- model change events $scope.$watch('selectedDate', function (newVal, oldVal) { // Watcher One way date filter var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var date = angular.isDate($scope.date) ? $scope.date.getTime() : null; if (newValDate !== oldValDate && (date !== newValDate || newValDate == null)) { $scope.date = $scope.selectedHour = shApi.getCompleteDateHour($scope.selectedDate, $scope.selectedHour, 0); validate("date"); setMinReturnDateHour(); if ($scope.errorsList.includes('returnDate')) { setReturnDateHour(); } if (newValDate != null && $scope.selectedHour == null) { $timeout(function () { $("[name=hour]").trigger('click'); }, 0); } $scope.checkDates(); } }); $scope.$watch('selectedHour', function (newVal, oldVal) { // Watcher One way hour filter var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var date = angular.isDate($scope.date) ? $scope.date.getTime() : null; if (newValDate !== oldValDate && (date !== newValDate || newValDate == null)) { $scope.date = $scope.selectedDate = shApi.getCompleteDateHour($scope.selectedDate, $scope.selectedHour, 0); validate("date"); setMinReturnDateHour(); if ($scope.errorsList.includes('returnDate')) { setReturnDateHour(); } $scope.checkDates(); } }); $scope.$watch('selectedReturnDate', function (newVal, oldVal) { // Watcher return date filter var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; if (newValDate !== oldValDate && $scope.returnDate != null && $scope.returnDate.getTime() !== newValDate) { $scope.returnDate = $scope.selectedReturnHour = shApi.getCompleteDateHour($scope.selectedReturnDate, $scope.selectedReturnHour, 0); validate("returnDate"); if (newValDate != null && $scope.selectedReturnHour == null) { $timeout(function () { $("[name=returnHour]").trigger('click'); }, 0); } $scope.checkDates(); } }); $scope.$watch('selectedReturnHour', function (newVal, oldVal) { // Watcher return hour filter var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; if (newValDate !== oldValDate && $scope.returnDate != null && $scope.returnDate.getTime() != newValDate) { $scope.returnDate = $scope.selectedReturnDate = shApi.getCompleteDateHour($scope.selectedReturnDate, $scope.selectedReturnHour, 0); validate("returnDate"); $scope.checkDates(); } }); $scope.$watch('roundTrip', function (newVal, oldVal) { // Watcher for return if (newVal !== oldVal) { validate("destination"); if (newVal) { setReturnDateHour(); } } }); // Watchers for service type filter $scope.$watch('serviceTypes', function (newVal, oldVal) { if (newVal !== oldVal) { setServiceTypesInternalValues(newVal); } }, true); $scope.$watch('selectedServiceTypes', function (newVal, oldVal) { if (newVal !== oldVal) { $scope.serviceTypeSelector(); } }, true); // ------------------------------------------------------------ $scope.serviceTypeSelector = function () { try { if (JSON.stringify($scope.serviceTypes) != JSON.stringify($scope.selectedServiceTypes.map(function (j) { return j.key; }))) { $scope.serviceTypes = []; for (var index in $scope.selectedServiceTypes) { $scope.serviceTypes.push($scope.selectedServiceTypes[index].key); } } } catch (e) { log.error("Error in event serviceTypeSelector:" + e); } } function setServiceTypesInternalValues(newVal) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.selectedServiceTypes = []; } else { $scope.selectedServiceTypes = []; //Search in list for (var index in $scope.serviceTypes) { var srv = shApi.returnByProperty($scope.serviceOptions, 'key', $scope.serviceTypes[index]); if (srv != null) $scope.selectedServiceTypes.push(srv); } } } // ------------------------------------------------------------ Provider ------------------------------------------------------------ $scope.$watch('providers', function (newVal, oldVal) { if (newVal !== oldVal) { setProviderOptionsInternalValues(newVal); } }, true); $scope.providersListSelectorOnClose = function () { try { //Lanzamos la query sólo cuando se haya cerrado el popup y haya cambiado la selección if (JSON.stringify($scope.providers) != JSON.stringify($scope.selectedProviders.map(function (j) { return j.UniqueId; }))) { $scope.providers = []; for (var index in $scope.selectedProviders) { $scope.providers.push($scope.selectedProviders[index].UniqueId); } $scope.searchTrigger = "providerFilters"; $scope.$apply(); } } catch (e) { log.error("Error in event providersListSelectorOnClose:" + e); } } function setProviderOptionsInternalValues(newVal) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.selectedProviders = []; } else { $scope.selectedProviders = []; //Search in list for (var index in $scope.providers) { var provid = shApi.returnByProperty($scope.providerOptions, 'UniqueId', $scope.providers[index]); if (provid != null) $scope.selectedProviders.push(provid); } } } // -------------------------------------------------------------------------------------------------------------------------------------------- $scope.onBookingDirectivesTemplateKeyDown = function ($event) { if ($event.keyCode == 13) { $event.preventDefault(); } }; $scope.focusElement = function (el) { $timeout(function () { document.getElementById(el).focus(); }, 500); }; $scope.callbackAction = function (e) { if ($scope.callbackCustomAction != undefined) { $scope.callbackCustomAction(e); } } }, templateUrl: function (elem, attrs) { //custom url var url = '/Scripts/app/Modules/transportationLateralFiltersTemplate.html'; if (angular.isDefined(attrs.templateUrl)) { url = attrs.templateUrl; } return url; }, }; }); /* ------------------------------------------------------------------------------------------------------------------------------------------- */ ///errorsList is the binding to the outside form $scope.errors, so we can communicate errors from the directive to the container element ///isError to indicate clearly that there is a validation error in the element ///transportationDatePickerPopup -> used to bind the date format ///origin and destination will be null in case there is nothing selected from the typeahead. This helps in validations. /* -------------------------------------------------- Original for internal ------------------------------------------------------------------ */ ngSharedDirectives.directive('transportationFiltersOriginal', function ($timeout, Purchases, shApi) { //required vbles for all the fields (this way it will be highly configurable for validations depending on the "injected" business logic) var isOriginRequired = false; var isDestinationRequired = false; var isDateRequired = false; var isHourRequired = false; var isReturnDateRequired = false; var isReturnHourRequired = false; var avoidSpecificPointValidation = false; //Avoid specific point validation (for internal purposes) var showRoundTrip = false; var peopleShowOptions = false; var parentForm; return { restrict: 'E', //prefix for the urls like .com/ar (que no tienen dominio propio) scope: { origin: '=', destination: '=', prefix: '=', people: '=', date: '=', transportationDatepickerOptions: '=', minDate: '=', maxDate: '=', culture: '=', roundTrip: '=', returnDate: '=', maxReturnDate: '=', returnDateGap: '=', arrayTranslations: '=', isValidForm: '=', errorsList: '=', searchTrigger: '=', inputEvent: '=', blockForm: '=', isPlaceAmendable: '=', showTransferFilters: '=', callbackCustomAction: '=', serviceTypes: '=', serviceOptions: '=', providerOptions: '=', providers: '=', totalCount: '=' }, link: function (scope, elem, attrs) { shApi.defineRequired(scope, scope); //load the booleans for the required fields //the required fields depend on the business logic, it's not the directive business if (attrs.originRequired !== undefined) isOriginRequired = (attrs.originRequired === 'true' || attrs.originRequired === ''); if (attrs.destinationRequired !== undefined) isDestinationRequired = (attrs.destinationRequired === 'true' || attrs.destinationRequired === ''); if (attrs.isDateRequired !== undefined) isDateRequired = (attrs.isDateRequired === 'true' || attrs.isDateRequired === ''); if (attrs.isHourRequired !== undefined) isHourRequired = (attrs.isHourRequired === 'true' || attrs.isHourRequired === ''); if (attrs.isReturnDateRequired !== undefined) isReturnDateRequired = (attrs.isReturnDateRequired === 'true' || attrs.isReturnDateRequired === ''); if (attrs.isReturnHourRequired !== undefined) isReturnHourRequired = (attrs.isReturnHourRequired === 'true' || attrs.isReturnHourRequired === ''); if (attrs.mydateformat !== undefined) scope.mydateformat = attrs.mydateformat; if (attrs.showPeopleOptions !== undefined) scope.showPeopleOptions = (attrs.showPeopleOptions == "true" || attrs.showPeopleOptions == ''); if (attrs.avoidSpecificPointValidation !== undefined) avoidSpecificPointValidation = (attrs.avoidSpecificPointValidation == "true" || attrs.avoidSpecificPointValidation == ''); if (attrs.showRoundTrip !== undefined) scope.showRoundTrip = (attrs.showRoundTrip == 'true' || attrs.showRoundTrip == ''); if (attrs.showLangugageSelection !== undefined) scope.showLangugageSelection = (attrs.showLangugageSelection == 'true' || attrs.showLangugageSelection == ''); //initializations scope.errorsList = []; scope.selectedOrigin = ''; scope.selectedDestination = ''; scope.destinationChangedByCode = false; scope.originChangedByCode = false; //initialize vbles used in validation scope.originNoResults = false; scope.destinationNoResults = false; scope.orgGuid = shApi.generateGuid(); scope.destGuid = shApi.generateGuid(); //set dates if (scope.minDate === undefined || scope.minDate === null) { scope.minDate = new Date(); } if (scope.maxDate === undefined || scope.maxDate === null) { scope.maxDate = new Date(); scope.maxDate.setFullYear(2025); } if (scope.date === undefined || scope.date === null) { scope.date = shApi.getCompleteDateHour(scope.minDate, scope.minDate, 60);//initialize selectedDate } scope.selectedDate = scope.date; scope.selectedHour = scope.date; scope.returnDateChangedByCode = false; scope.minReturnDate = shApi.getCompleteDateHour(scope.date, scope.date, scope.returnDateGap == undefined ? 1 : scope.returnDateGap); scope.returnDate = scope.minReturnDate; scope.selectedReturnDate = scope.returnDate; scope.selectedReturnHour = scope.returnDate; //getting the form (required to mark validations on it) parentForm = scope.parentForm = elem.parent().controller('form'); //translation method scope.translate = function (key) { return scope.arrayTranslations[key]; } //load tranlations for internal directives or attributesi if (!scope.translationsLoaded) { //people selector scope.inputmsg = scope.translate("_bookservice_travelers"); scope.personmsg = scope.translate("_bookservice_person"); scope.peoplemsg = scope.translate("_bookservice_people"); scope.adultmsg = scope.translate("_bookservice_adult"); scope.adultsmsg = scope.translate("_bookservice_adults"); scope.childmsg = scope.translate("_bookservice_child"); scope.childrenmsg = scope.translate("_bookservice_children"); scope.infantmsg = scope.translate("_bookservice_infant"); scope.infantsmsg = scope.translate("_bookservice_infants"); scope.undertwelvemsg = scope.translate("_bookservice_childrenundertwelve"); scope.underfivemsg = scope.translate("_bookservice_childrenunderfive"); scope.closemsg = scope.translate("_bookservice_apply"); scope.bookserviceprovider = scope.translate("_sales_viewer_table_providers"); scope.bookserviceproviderselected = scope.translate("_bookservice_provider_selected"); scope.childseatmessage = scope.translate("_child_seat_message"); //date picker scope.dateOriginalTitle = scope.translate("_bookservice_dateservice_input_placeholder"); //hour control scope.hourTranslation = scope.translate("_bookservice_hourservice_input_placeholder"); scope.returnHourTranslation = scope.translate("_roundtrip_selector_hour"); scope.translationsLoaded = true; //place holders origin/destination scope.originPlaceHolder = scope.translate("_bookservice_addresssearch_placeholder"); scope.destinationPlaceHolder = scope.translate("_bookservice_addresssearch_placeholder"); scope.originPlaceholderText = scope.translate("_bookservice_origin_text"); scope.destinationPlaceholderText = scope.translate("_bookservice_destination_text"); //roundtrip scope.mainoptionmsg = scope.translate("_roundtrip_selector_oneway"); scope.alternativemsg = scope.translate("_roundtrip_selector_return"); scope.reestablishtext = scope.translate("_bookservice_reestablish_text"); //results info scope.searchresultstext = scope.translate("_bookservice_searchresults_text"); } $(document).ready(function (e) { scope.Init(); }); }, controller: function ($scope) { $scope.Init = function () { //initialization origin/destination setSelectedOrigin(); setSelectedDestination(); //run validations validate("origin"); validate("destination"); } //initializeOrigin function setSelectedOrigin() { if ($scope.origin != null && $scope.origin.placeId != null) { if ($scope.selectedOrigin == '') $scope.selectedOrigin = {}; //TODO: Resolve by placeId, a method is needed to fill all properties of $scope.selectedOrigin //check that watch event is not caused due to selectedOriginChanged, because the origin data is different from selectedOrigin if ($scope.selectedOrigin.PlaceId != $scope.origin.placeId) { $scope.selectedOrigin.PlaceId = $scope.origin.placeId; $scope.selectedOrigin.AutoCompleteText = $scope.origin.displayName; } } else $scope.selectedOrigin = ''; } function setSelectedDestination() { if ($scope.destination != null && $scope.destination.placeId != null) { if ($scope.selectedDestination == '') $scope.selectedDestination = {}; //TODO: Resolve by placeId, a method is needed to fill all properties of $scope.selectedDestination //check that watch event is not caused due to selectedOriginChanged, because the origin data is different from selectedOrigin if ($scope.selectedDestination.PlaceId != $scope.destination.placeId) { $scope.selectedDestination.PlaceId = $scope.destination.placeId; $scope.selectedDestination.AutoCompleteText = $scope.destination.displayName; } } else $scope.selectedDestination = ''; } //-- Validations ---------------------------- function validate(which) { if ($scope.errorsList == undefined) $scope.errorsList = []; switch (which) { case 'origin': var isOriginLoaded = parentForm.origin !== undefined; var isOriginInvalid = false; if (isOriginLoaded) { if (isOriginRequired && !$scope.originNoResults && ($scope.selectedOrigin.length == 0 && $scope.selectedOrigin.PlaceId == undefined)) { isOriginInvalid = true; if (parentForm.origin.$touched) parentForm.origin.$setValidity('required', false); //reset point validity parentForm.origin.$setValidity('point', true); } else { parentForm.origin.$setValidity('required', true); } if ((!$scope.originNoResults || parentForm.origin.$touched) && $scope.selectedOrigin != '' && $scope.selectedOrigin.PlaceId == undefined) { isOriginInvalid = true; parentForm.origin.$setValidity('fromlist', false); //reset point validity parentForm.origin.$setValidity('point', true); } else { parentForm.origin.$setValidity('fromlist', true); } //if origin is not airport or it's not specific custom error if (!$scope.originNoResults && !($scope.selectedOrigin.PlaceId == undefined)) { if (avoidSpecificPointValidation) parentForm.origin.$setValidity('point', true); else { if ($scope.selectedOrigin.IsSpecific || $scope.selectedOrigin.IsAirport || $scope.selectedOrigin.IsStation) { parentForm.origin.$setValidity('point', true); } else { isOriginInvalid = true; parentForm.origin.$setValidity('point', false); } } } } else isOriginInvalid = true; if (isOriginRequired && isOriginInvalid) { if (!$scope.errorsList.includes('origin')) $scope.errorsList.push('origin'); } else { var index = $scope.errorsList.indexOf('origin'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); setTimeout(function () { document.getElementById('destinationTransportation').focus(); }, 100); } if ($scope.errorsList.indexOf('destination') == -1 && $scope.errorsList.indexOf('origin') == -1) { $scope.showTransferFilters = true; } break; case 'destination': var isDestinationLoaded = parentForm.destination !== undefined; var isDestinationInvalid = false; if (isDestinationLoaded) { if (isDestinationRequired && $scope.selectedDestination.length == 0 && $scope.selectedDestination.PlaceId == undefined) { isDestinationInvalid = true; if (parentForm.destination !== undefined && parentForm.destination.$touched) parentForm.destination.$setValidity('required', false); } else { parentForm.destination.$setValidity('required', true); } if ((!$scope.destinationNoResults || parentForm.destination.$touched) && $scope.selectedDestination != '' && $scope.selectedDestination.PlaceId == undefined) { //destination = name attribute isDestinationInvalid = true; parentForm.destination.$setValidity('fromlist', false); } else { parentForm.destination.$setValidity('fromlist', true); } //if destination is not airport or it's not specific custom error if ($scope.roundTrip && !$scope.destinationNoResults && $scope.selectedDestination != '' && !($scope.selectedDestination.PlaceId == undefined)) { if (avoidSpecificPointValidation) { parentForm.destination.$setValidity('point', true); } else { if ($scope.selectedDestination.IsSpecific || $scope.selectedDestination.IsAirport || $scope.selectedDestination.IsStation) { parentForm.destination.$setValidity('point', true); } else { isDestinationInvalid = true; parentForm.destination.$setValidity('point', false); } } } else { parentForm.destination.$setValidity('point', true); } } else isDestinationInvalid = true; if (isDestinationRequired && isDestinationInvalid) { if (!$scope.errorsList.includes('destination')) $scope.errorsList.push('destination'); } else { var index = $scope.errorsList.indexOf('destination'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } if ($scope.errorsList.indexOf('destination') == -1 && $scope.errorsList.indexOf('origin') == -1) { $scope.showTransferFilters = true; } break; case 'date': var isDateInvalid = false; //TODO: Different if is required or not if ($scope.date == null) { if (parentForm.goDate.$touched) parentForm.goDate.$setValidity('required', false); isDateInvalid = true; } else { parentForm.goDate.$setValidity('required', true); if ($scope.date > $scope.maxDate) { parentForm.goDate.$setValidity('maxdate', false); isDateInvalid = true; } else { parentForm.goDate.$setValidity('maxdate', true); } if ($scope.date < $scope.minDate) { parentForm.goDate.$setValidity('mindate', false); isDateInvalid = true; } else { parentForm.goDate.$setValidity('mindate', true); } } if (isDateInvalid) { if (!$scope.errorsList.includes('date')) $scope.errorsList.push('date'); } else { var index = $scope.errorsList.indexOf('date'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; case 'returnDate': var isReturnDateInvalid = false; if ($scope.roundTrip) { if ($scope.returnDate == null) { parentForm.returnDate.$setValidity('required', false); isReturnDateInvalid = true; } else { parentForm.returnDate.$setValidity('required', true); if ($scope.returnDate > $scope.maxReturnDate) { parentForm.returnDate.$setValidity('maxdate', false); isReturnDateInvalid = true; } else { parentForm.returnDate.$setValidity('maxdate', true); } if ($scope.returnDate < $scope.minReturnDate) { parentForm.returnDate.$setValidity('mindate', false); isReturnDateInvalid = true; } else { parentForm.returnDate.$setValidity('mindate', true); } } } else { parentForm.returnDate.$setValidity('required', true); parentForm.returnDate.$setValidity('mindate', true); parentForm.returnDate.$setValidity('maxdate', true); } if (isReturnDateInvalid) { if (!$scope.errorsList.includes('returnDate')) $scope.errorsList.push('returnDate'); } else { var index = $scope.errorsList.indexOf('returnDate'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; } $scope.isValidForm = ($scope.errorsList.length == 0); } function setMinReturnDateHour() { //Update min return date if (!$scope.errorsList.includes('date')) { $scope.minReturnDate = shApi.getCompleteDateHour($scope.date, $scope.date, $scope.returnDateGap == undefined ? 1 : $scope.returnDateGap); validate("returnDate"); } } function setReturnDateHour() { if (!$scope.errorsList.includes('date')) { $scope.minReturnDate = shApi.getCompleteDateHour($scope.date, $scope.date, $scope.returnDateGap == undefined ? 1 : $scope.returnDateGap); $scope.returnDate = $scope.minReturnDate; $scope.selectedReturnDate = $scope.selectedReturnHour = $scope.returnDate; validate("returnDate"); } } //vbles used to check the validations (only checked in watch in the state of focus lost) $scope.originFocusLost = function () { $scope.originHasFocus = false; $scope.orgGuid = shApi.generateGuid(); $scope.inputEvent = 'blur'; //if selectedOrigin is null it means the field has been just initialized or just cleared by the purchase process if ($scope.selectedOrigin != null) { $timeout(function () { //if (!$scope.errorsList.includes("origin")) { //we put this here because the on-blur of the $touched event goes after the on-blur (focus lost) //check if routeTab is still selected if (parentForm.origin != undefined) { parentForm.origin.$setTouched(); validate('origin'); } //} }, 1000); } } $scope.originFocusGained = function () { $scope.originHasFocus = true; $scope.inputEvent = 'focus'; } $scope.clearOrigin = function () { $scope.selectedOrigin = ''; } $scope.clearDestination = function () { $scope.selectedDestination = ''; } //aim to check if the user has left the input field (focus lost) without selecting a search item result $scope.destinationFocusLost = function () {//timeout to ensure the destinationChanged has been executed $scope.destinationHasFocus = false; $scope.destGuid = shApi.generateGuid(); $scope.inputEvent = 'blur'; //if selectedDestination is null it means the field has been just initialized or just cleared by the purchase process if ($scope.selectedDestination != null) { $timeout(function () { //if (!$scope.errorsList.includes("destination")) { //we put this here because the on-blur of the $touched event goes after the on-blur (focus lost) //check if routeTab is still selected if (parentForm.destination != undefined) { parentForm.destination.$setTouched(); validate('destination'); } //} }, 1000); } } $scope.destinationFocusGained = function () { $scope.inputEvent = 'focus'; $scope.destinationHasFocus = true; } //--End Validations ---------------------------- $scope.getAddressesPredictionForOrigin = function (filter) { return getAddressesPrediction(filter, true); }; $scope.getAddressesPredictionForDestination = function (filter) { return getAddressesPrediction(filter, false); }; //function that is loaded in the template function getAddressesPrediction(filter, isOrigin) { var placeId = null; var guid = null; if (isOrigin && parentForm.destination.$valid && $scope.selectedDestination != '' && $scope.selectedDestination.PlaceId != undefined) { placeId = $scope.selectedDestination.PlaceId; } else if (!isOrigin && parentForm.origin.$valid & $scope.selectedOrigin != '' && $scope.selectedOrigin.PlaceId != undefined) { placeId = $scope.selectedOrigin.PlaceId; } if (isOrigin) guid = $scope.orgGuid; else guid = $scope.destGuid; if (guid == null) guid = shApi.generateGuid(); return Purchases.getAddressesPrediction(filter, placeId, $scope.prefix, guid) .then(function (response) { return response; }); }; $scope.switchPoints = function () { //set errors on both before switching, later it will be validated if (!$scope.errorsList.includes('destination')) $scope.errorsList.push('destination'); if (!$scope.errorsList.includes('origin')) $scope.errorsList.push('origin'); var selectedDestination = $scope.selectedDestination; $scope.selectedDestination = $scope.selectedOrigin; $scope.selectedOrigin = selectedDestination; //validations after change.. var auxOriginNoResults = $scope.originNoResults; $scope.originNoResults = $scope.destinationNoResults; $scope.destinationNoResults = auxOriginNoResults; validate("origin"); validate("destination"); }; //In case origin is updated programatically $scope.$watch('origin', function (newOrg, oldOrg) { if (newOrg !== oldOrg) { setSelectedOrigin(); } }, true);//true as third parameter to check internal properties //In case origin is updated programatically $scope.$watch('destination', function (newOrg, oldOrg) { if (newOrg !== oldOrg) { setSelectedDestination(); } }, true);//true as third parameter to check internal properties ///model change events // ///We use watchs insted of ng-change because... ///on element selected with keyboard enter ng-change doesn't work // ng-change is executed before the binding is updated which is problematic when communicating changes to the outer controller through the callback function $scope.$watch('selectedOrigin', function (newOrg, oldOrg) { if (newOrg !== oldOrg) { if ($scope.selectedOrigin != null && $scope.selectedOrigin.PlaceId != null) { //initialize only if it's undefined... to not remove data on the controller if ($scope.origin == undefined) $scope.origin = {}; $scope.origin.placeId = $scope.selectedOrigin.PlaceId; $scope.origin.displayName = $scope.selectedOrigin.AutoCompleteText; $scope.origin.isAirport = $scope.selectedOrigin.IsAirport; $scope.origin.isStation = $scope.selectedOrigin.IsStation; if ($scope.isPlaceAmendable == true) $scope.$parent.$parent.originIATAToChange = null; } //execute the validations first validate("origin"); //notifies changes to the controller $scope.searchTrigger = "origin"; } }, true); $scope.$watch('selectedDestination', function (newOrg, oldOrg) { if (newOrg !== oldOrg) { if ($scope.selectedDestination != null && $scope.selectedDestination.PlaceId != null) { //initialize only if it's undefined... to not remove data on the controller if ($scope.destination == undefined) $scope.destination = {}; $scope.destination.placeId = $scope.selectedDestination.PlaceId; $scope.destination.displayName = $scope.selectedDestination.AutoCompleteText; $scope.destination.isAirport = $scope.selectedDestination.IsAirport; $scope.destination.isStation = $scope.selectedDestination.IsStation; if ($scope.isPlaceAmendable == true) $scope.$parent.$parent.destinationIATAToChange = null; } //update validations validate("destination"); //notify changes to the controller $scope.searchTrigger = "destination"; } }, true); $scope.$watch('date', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var selectedDate = ($scope.selectedDate != undefined && $scope.selectedDate != null) ? $scope.selectedDate.getTime() : $scope.selectedDate; if (newValDate !== oldValDate && newValDate !== selectedDate) { if (newVal != null) { $scope.selectedDate = $scope.selectedHour = newVal; } else { $scope.selectedDate = $scope.selectedHour = null; } } });//true as third parameter to check internal properties $scope.$watch('returnDate', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; if (newValDate !== oldValDate && newValDate !== $scope.selectedReturnDate.getTime()) { if (newVal != null) { $scope.selectedReturnDate = $scope.selectedReturnHour = newVal; } else { $scope.selectedReturnDate = $scope.selectedReturnHour = null; } } });//true as third parameter to check internal properties //people on close handler (callback for communication is in the child directive stPeopleSelector) $scope.peopleSelectorOnClose = function () { //notifies changes to the controller if values different from original $scope.searchTrigger = "detailedPeople"; $scope.$apply(); } //end- model change events $scope.$watch('selectedDate', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var date = angular.isDate($scope.date) ? $scope.date.getTime() : null; if (newValDate !== oldValDate && (date !== newValDate || newValDate == null)) { $scope.date = $scope.selectedHour = shApi.getCompleteDateHour($scope.selectedDate, $scope.selectedHour, 0); validate("date"); setMinReturnDateHour(); if ($scope.errorsList.includes('returnDate')) { setReturnDateHour(); } //notifies changes to the controller $scope.searchTrigger = "date"; } }); $scope.$watch('selectedHour', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var date = angular.isDate($scope.date) ? $scope.date.getTime() : null; if (newValDate !== oldValDate && (date !== newValDate || newValDate == null)) { $scope.date = $scope.selectedDate = shApi.getCompleteDateHour($scope.selectedDate, $scope.selectedHour, 0); validate("date"); setMinReturnDateHour(); //notifies changes to the controller $scope.searchTrigger = "date"; if ($scope.errorsList.includes('returnDate')) { setReturnDateHour(); } //notifies changes to the controller $scope.searchTrigger = "date"; } }); $scope.$watch('selectedReturnDate', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; if (newValDate !== oldValDate && $scope.returnDate.getTime() !== newValDate) { $scope.returnDate = $scope.selectedReturnHour = shApi.getCompleteDateHour($scope.selectedReturnDate, $scope.selectedReturnHour, 0); validate("returnDate"); //notifies changes to the controller $scope.searchTrigger = "returnDate"; } }); $scope.$watch('selectedReturnHour', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; if (newValDate !== oldValDate && $scope.returnDate.getTime() != newValDate) { $scope.returnDate = $scope.selectedReturnDate = shApi.getCompleteDateHour($scope.selectedReturnDate, $scope.selectedReturnHour, 0); validate("returnDate"); //notifies changes to the controller $scope.searchTrigger = "returnDate"; } }); $scope.$watch('roundTrip', function (newVal, oldVal) { if (newVal !== oldVal) { validate("destination"); if (newVal) { setReturnDateHour(); } //notifies changes to the controller $scope.searchTrigger = "roundTrip"; } }); //Service type filter $scope.$watch('serviceTypes', function (newVal, oldVal) { if (newVal !== oldVal) { setServiceTypesInternalValues(newVal); } }, true); $scope.serviceTypeSelectorOnClose = function () { try { //Lanzamos la query sólo cuando se haya cerrado el popup y haya cambiado la selección if (JSON.stringify($scope.serviceTypes) != JSON.stringify($scope.selectedServiceTypes.map(function (j) { return j.key; }))) { $scope.serviceTypes = []; for (var index in $scope.selectedServiceTypes) { $scope.serviceTypes.push($scope.selectedServiceTypes[index].key); } $scope.searchTrigger = "serviceTypeFilters"; $scope.$apply(); } } catch (e) { log.error("Error in event serviceTypeSelectorOnClose:" + e); } } function setServiceTypesInternalValues(newVal) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.selectedServiceTypes = []; } else { $scope.selectedServiceTypes = []; //Search in list for (var index in $scope.serviceTypes) { var srv = shApi.returnByProperty($scope.serviceOptions, 'key', $scope.serviceTypes[index]); if (srv != null) $scope.selectedServiceTypes.push(srv); } } } // ------------------------------------------------------------ Provider ------------------------------------------------------------ $scope.$watch('providers', function (newVal, oldVal) { if (newVal !== oldVal) { setProviderOptionsInternalValues(newVal); } }, true); $scope.providersListSelectorOnClose = function () { try { //Lanzamos la query sólo cuando se haya cerrado el popup y haya cambiado la selección if (JSON.stringify($scope.providers) != JSON.stringify($scope.selectedProviders.map(function (j) { return j.UniqueId; }))) { $scope.providers = []; for (var index in $scope.selectedProviders) { $scope.providers.push($scope.selectedProviders[index].UniqueId); } $scope.searchTrigger = "providerFilters"; $scope.$apply(); } } catch (e) { log.error("Error in event providersListSelectorOnClose:" + e); } } function setProviderOptionsInternalValues(newVal) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.selectedProviders = []; } else { $scope.selectedProviders = []; //Search in list for (var index in $scope.providers) { var provid = shApi.returnByProperty($scope.providerOptions, 'UniqueId', $scope.providers[index]); if (provid != null) $scope.selectedProviders.push(provid); } } } // -------------------------------------------------------------------------------------------------------------------------------------------- $scope.onBookingDirectivesTemplateKeyDown = function ($event) { if ($event.keyCode == 13) { $event.preventDefault(); } }; $scope.focusElement = function (el) { $timeout(function () { document.getElementById(el).focus(); }, 500); }; $scope.callbackAction = function (e) { if ($scope.callbackCustomAction != undefined) { $scope.callbackCustomAction(e); } } }, templateUrl: function (elem, attrs) { //custom url var url = '/Scripts/app/Modules/transportationFiltersTemplate.html'; if (angular.isDefined(attrs.templateUrl)) { url = attrs.templateUrl; } return url; }, }; }); /* ------------------------------------------------------------------------------------------------------------------------------------------- */ //we use selected fields in directive to distinguish the changes outside the directive. The watches for selectedOject changed, update the binded object //and notify the outside controller by calling the searchTrigger method. ngSharedDirectives.directive('bookingPerHoursFilters', function ($timeout, Purchases, shApi) { //required vbles for all the fields (this way it will be highly configurable for validations depending on the "injected" business logic) var isDateRequired = false; var isHourRequired = false; var peopleShowOptions = false; var areDurationHoursRequired = false; var isDestinationRequired = false; var parentForm; return { restrict: 'E', //prefix for the urls like .com/ar (que no tienen dominio propio) scope: { destination: '=', prefix: '=', people: '=', date: '=', datepickerOptions: '=', minDate: '=', maxDate: '=', languageFilters: '=', getLanguagesMethod: '=', culture: '=', arrayTranslations: '=', isValidForm: '=', errorsList: '=', searchTrigger: '=', showPeopleOptions: '=', inputEvent: '=', durationOptions: '=', durationRange: '=', blockForm: '=', showDriverFilters: '=', callbackCustomAction: '=', }, link: function (scope, elem, attrs) { shApi.defineRequired(scope, scope); //load the booleans for the required fields //the required fields depend on the business logic, it's not the directive business if (attrs.isDateRequired !== undefined) isDateRequired = (attrs.isDateRequired === 'true' || attrs.isDateRequired === '') if (attrs.isHourRequired !== undefined) isHourRequired = (attrs.isHourRequired === 'true' || attrs.isHourRequired === '') if (attrs.mydateformat !== undefined) scope.mydateformat = attrs.mydateformat if (attrs.areDurationHoursRequired !== undefined) areDurationHoursRequired = (attrs.areDurationHoursRequired === "true" || attrs.areDurationHoursRequired === '') if (attrs.destinationVisible !== undefined) scope.destinationVisible = (attrs.destinationVisible === "true" || attrs.destinationVisible === '') if (attrs.destinationRequired !== undefined) isDestinationRequired = (attrs.destinationRequired === 'true' || attrs.destinationRequired === '') //initializations scope.errorsList = []; //set dates if (scope.date === undefined || scope.date === null) { scope.date = scope.minDate;//initialize selectedDate } scope.selectedDate = scope.date; scope.selectedHour = scope.date; //getting the form (required to mark validations on it) parentForm = scope.parentForm = elem.parent().controller('form'); //improvement in attributes binding: https://gist.github.com/CMCDragonkai/6282750 //load tranlations for internal directives or attributes if (!scope.translationsLoaded) { //people selector scope.inputmsg = scope.translate("_bookservice_travelers"); scope.personmsg = scope.translate("_bookservice_person"); scope.peoplemsg = scope.translate("_bookservice_people"); scope.adultmsg = scope.translate("_bookservice_adult"); scope.adultsmsg = scope.translate("_bookservice_adults"); scope.childmsg = scope.translate("_bookservice_child"); scope.childrenmsg = scope.translate("_bookservice_children"); scope.infantmsg = scope.translate("_bookservice_infant"); scope.infantsmsg = scope.translate("_bookservice_infants"); scope.undertwelvemsg = scope.translate("_bookservice_childrenundertwelve"); scope.underfivemsg = scope.translate("_bookservice_childrenunderfive"); scope.closemsg = scope.translate("_bookservice_apply"); //date picker scope.dateOriginalTitle = scope.translate("_bookservice_dateservice_input_placeholder"); //hour control scope.hourTranslation = scope.translate("_bookservice_hourservice_input_placeholder"); scope.destinationPlaceHolder = scope.translate("_bookservice_destination_placeholder"); //duration control scope.durationtext = scope.translate("_bookservice_duration_text"); scope.rangetext = scope.translate("_bookservice_duration_range"); scope.hourstext = scope.translate("_bookservice_duration_hours"); scope.reestablishtext = scope.translate("_bookservice_reestablish_text"); scope.translationsLoaded = true; } //event on close of stPeopleSelector $(document).ready(function (e) { scope.Init(); }); } , controller: function ($scope) { $scope.Init = function () { setSelectedDestination(); //run validations validate("destination"); //validate("durationHours"); } //-- Validations ---------------------------- function validate(which) { if ($scope.errorsList == undefined) $scope.errorsList = []; switch (which) { case 'destination': //if destination if not shown avoid validation if ($scope.destinationVisible) { var isDestinationInvalid = false; if (isDestinationRequired && $scope.selectedDestination.length == 0 && $scope.selectedDestination.PlaceId == undefined) { isDestinationInvalid = true; if (parentForm.destination.$touched) parentForm.destination.$setValidity('required', false); } else { parentForm.destination.$setValidity('required', true); } if (!$scope.destinationNoResults && $scope.selectedDestination != '' && $scope.selectedDestination.PlaceId == undefined) { //destination = name attribute isDestinationInvalid = true; parentForm.destination.$setValidity('fromlist', false); } else { parentForm.destination.$setValidity('fromlist', true); } if (isDestinationRequired && isDestinationInvalid) { if (!$scope.errorsList.includes('destination')) $scope.errorsList.push('destination'); } else { var index = $scope.errorsList.indexOf('destination'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } } if (!isDestinationInvalid) { $scope.showDriverFilters = true; } break; case 'date': var isDateInvalid = false; if ($scope.date == null) { if (parentForm.dateSrv.$touched) parentForm.dateSrv.$setValidity('required', false); isDateInvalid = true; } else { parentForm.dateSrv.$setValidity('required', true); if ($scope.date > $scope.maxDate) { parentForm.dateSrv.$setValidity('maxdate', false); isDateInvalid = true; } else { parentForm.dateSrv.$setValidity('maxdate', true); } if ($scope.date < $scope.minDate) { parentForm.dateSrv.$setValidity('mindate', false); isDateInvalid = true; } else { parentForm.dateSrv.$setValidity('mindate', true); } } if (isDateInvalid) { if (!$scope.errorsList.includes('date')) $scope.errorsList.push('date'); } else { var index = $scope.errorsList.indexOf('date'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; } $scope.isValidForm = ($scope.errorsList.length == 0); } //translation method $scope.translate = function (key) { return $scope.arrayTranslations[key]; } //used in dropdown of hours $scope.selectFormat = function (i, custom) { return i; }; //destination code //aim to check if the user has left the input field (focus lost) without selecting a search item result $scope.destinationFocusLost = function () {//timeout to ensure the destinationChanged has been executed $scope.destinationHasFocus = false; $scope.inputEvent = 'blur'; //if selectedDestination is null it means the field has been just initialized or just cleared by the purchase process if ($scope.selectedDestination != null) { $timeout(function () { //if (!$scope.errorsList.includes("destination")) { //we put this here because the on-blur of the $touched event goes after the on-blur (focus lost) //check if routeTab is still selected if (parentForm.destination != undefined) { parentForm.destination.$setTouched(); validate('destination'); } //} }, 1000); } } $scope.destinationFocusGained = function () { $scope.destinationHasFocus = true; $scope.inputEvent = 'focus'; } //--End Validations ---------------------------- //function that is loaded in the template $scope.getAddressesPrediction = function (filter) { return Purchases.getDestinationPrediction(filter, $scope.prefix) .then(function (response) { return response; }); }; function setSelectedDestination() { if ($scope.destination != null && $scope.destination.placeId != null) { if ($scope.selectedDestination == '') $scope.selectedDestination = {}; //TODO: Resolve by placeId, a method is needed to fill all properties of $scope.selectedDestination //check that watch event is not caused due to selectedOriginChanged, because the origin data is different from selectedOrigin if ($scope.selectedDestination.PlaceId != $scope.destination.placeId) { $scope.selectedDestination.PlaceId = $scope.destination.placeId; $scope.selectedDestination.AutoCompleteText = $scope.destination.displayName; } } else $scope.selectedDestination = ''; } //In case origin is updated programatically $scope.$watch('destination', function (newOrg, oldOrg) { if (newOrg !== oldOrg) { setSelectedDestination(); } }, true);//true as third parameter to check internal properties $scope.$watch('selectedDestination', function (newOrg, oldOrg) { if (newOrg !== oldOrg) { $scope.destinationString = $scope.selectedDestination != null && $scope.selectedDestination.AutoCompleteText != null ? $scope.selectedDestination.AutoCompleteText : $scope.selectedDestination; if ($scope.selectedDestination != null && $scope.selectedDestination.PlaceId != null) { //initialize only if it's undefined... to not remove data on the controller if ($scope.destination == undefined) $scope.destination = {}; $scope.destination.placeId = $scope.selectedDestination.PlaceId; $scope.destination.displayName = $scope.selectedDestination.AutoCompleteText; } //update validations validate("destination"); //notify changes to the controller $scope.searchTrigger = "destination"; } }, true); //end destination code// $scope.clearDestination = function () { $scope.selectedDestination = ''; } $scope.$watch('date', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var selectedDate = ($scope.selectedDate != undefined && $scope.selectedDate != null) ? $scope.selectedDate.getTime() : $scope.selectedDate; if (newValDate !== oldValDate && newValDate !== selectedDate) { if (newVal != null) { $scope.selectedDate = $scope.selectedHour = newVal; } else { $scope.selectedDate = $scope.selectedHour = null; } validate("date"); } }); //true as third parameter to check internal properties $scope.$watch('selectedDate', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var date = angular.isDate($scope.date) ? $scope.date.getTime() : null; if (newValDate !== oldValDate && (date !== newValDate || newValDate == null)) { $scope.date = $scope.selectedHour = shApi.getCompleteDateHour($scope.selectedDate, $scope.selectedHour, 0); validate("date"); //notifies changes to the controller $scope.searchTrigger = "date"; } }); //hour is binded using the date, when the initilization is done $scope.$watch('selectedHour', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var date = angular.isDate($scope.date) ? $scope.date.getTime() : null; if (newValDate !== oldValDate && (date !== newValDate || newValDate == null)) { //setting hour in datetime. $scope.date = $scope.selectedDate = shApi.getCompleteDateHour($scope.selectedDate, $scope.selectedHour, 0); validate("date"); //notifies changes to the controller $scope.searchTrigger = "date"; } }); //people on close handler (callback for communication is in the child directive stPeopleSelector) $scope.peopleSelectorOnClose = function () { //notifies changes to the controller $scope.searchTrigger = "detailedPeople"; $scope.$apply(); } //duration range on close handler $scope.durationSelectorOnClose = function () { $scope.searchTrigger = "durationRange"; } $scope.$watch('languageFilters', function (newVal, oldVal) { if (newVal !== oldVal) { //notifies changes to the controller $scope.searchTrigger = "languageFilters"; } }, true); $scope.onBookingDirectivesTemplateKeyDown = function ($event) { if ($event.keyCode == 13) { $event.preventDefault(); } }; $scope.callbackAction = function (e) { if ($scope.callbackCustomAction != undefined) { $scope.callbackCustomAction(e); } } }, templateUrl: function (elem, attrs) { //custom url var url = '/Scripts/app/Modules/bookingPerHoursTemplate.html'; if (angular.isDefined(attrs.templateUrl)) { url = attrs.templateUrl; } return url; }, }; }); /* ---------------------------------------- Activity filter ------------------------------------------------------------------------------ */ ngSharedDirectives.directive('activityFilters', function ($timeout, Purchases, Lookups, shApi, $window) { var isDateRequired = false; var parentForm; return { restrict: 'E', scope: { destinationFilters: '=', date: '=', datepickerOptions: '=', minDate: '=', maxDate: '=', defaultDate: '=', verticals: '=', verticalOptions: '=', searchText: '=', language: '=', languageOptions: '=', arrayTranslations: '=', searchTrigger: '=', isValidForm: '=', errorsList: '=', freeTours: '=', prefix: '=', culture: '=', blockForm: '=', showActivitiesFilters: '=', callbackCustomAction: '=', quickFilters: '=', quickFilter: '=', totalCount: '=', showOnScreenLimit: '=', activitySortType: '=', activitySortFilterOptions: '=', loadMaterializedNewPage: '=', providerOptions: '=', providers: '=', filtersSelected: '=' }, link: function (scope, elem, attrs) { shApi.defineRequired(scope, scope); if (attrs.mydateformat !== undefined) scope.mydateformat = attrs.mydateformat; parentForm = scope.parentForm = elem.parent().controller('form'); if (scope.date === undefined) scope.date = null; scope.selectedDate = scope.date; scope.defaultDate = null; scope.destinationString = ''; scope.errorsList = []; if (scope.destinationFilters !== undefined && scope.destinationFilters !== null && scope.destinationFilters.length != 0) { for (var index in scope.destinationFilters) { var dest = scope.destinationFilters[index]; if (index > 0) scope.destinationString = scope.destinationString + ',' + dest.displayName; else scope.destinationString = dest.displayName; } } if (!scope.translationsLoaded) { scope.closemsg = scope.translate("_bookservice_apply"); scope.durationtext = scope.translate("_bookservice_duration_text"); scope.rangetext = scope.translate("_bookservice_duration_range"); scope.hourstext = scope.translate("_bookservice_duration_hours"); scope.verticaltext = scope.translate("_bookservice_vertical_input_text"); scope.reestablishtext = scope.translate("_bookservice_reestablish_text"); scope.alltext = scope.translate("_bookservice_all_text"); scope.categoryselectedtextnew = scope.translate("_bookservice_category_selected_new"); scope.categorylabeltext = scope.translate("_bookservice_vertical_input_text"); scope.languageslabeltext = scope.translate("_bookservice_label_languages"); scope.languagestext = scope.translate("_bookingservice_language_input_fromlist"); scope.languagesselectedtextnew = scope.translate("_bookservice_languages_selected_new"); scope.textfiltertext = scope.translate("_bookservice_activities_textfilter_text"); scope.filter = scope.translate("_bookservice_filter"); scope.filters = scope.translate("_bookservice_filters"); scope.applyall = scope.translate("_bookservice_apply_all"); scope.searchinputtext = scope.translate("_bookservice_search_text"); scope.clearfilter = scope.translate("_bookservice_clear_filters"); scope.more = scope.translate("_bookservice_more"); scope.deleteall = scope.translate("_bookservice_delete_all"); scope.less = scope.translate("_bookservice_less"); scope.recommended = scope.translate("_bookservice_recommended"); scope.activities = scope.translate("_bookservice_activities"); scope.dateselectortitle = scope.translate("_bookseervice_dateselector_title"); scope.dateselectorplaceholder = scope.translate("_bookseervice_dateselector_placeholder"); scope.quickfiltersdesc = scope.translate("_bookservice_quickfilters_desc"); scope.resultslesstext = scope.translate("_bookservice_foundresults_lesstext"); scope.resultstext = scope.translate("_bookservice_foundresults_text"); scope.resultsnone = scope.translate("_bookservice_foundresults_none"); scope.inputsearchtitle = scope.translate("_bookseervice_inputsearch_title"); scope.providerlabeltext = scope.translate("_bookservice_provider"); scope.searchresultstext = scope.translate("_bookservice_searchresults_text"); scope.showall = scope.translate("_bookservice_show_all"); scope.translationsLoaded = true; } scope.Init(); }, controller: function ($scope) { $scope.Init = function () { setSelectedDestinations(); setSelectedVerticals(); setSelectedLanguages(); setSelectedProviders(); setTextFilter(); //Quickfilter? validate("destinations"); } function setSelectedDestinations() { setInternalDestinationFilters($scope.destinationFilters); } function setSelectedVerticals() { setVerticalInternalValues($scope.verticals); } function setSelectedLanguages() { setLanguageInternalValues($scope.language); } function setSelectedProviders() { setProviderInternalValues($scope.providers); } function setTextFilter() { setSearchTextInternalValue($scope.searchText); } //-- Validations ---------------------------- function validate(which) { if ($scope.errorsList == undefined) $scope.errorsList = []; var destinationNedeed = $scope.quickFilter == undefined || $scope.quickFilter == null || $scope.quickFilter == ''; switch (which) { case 'destinations': var isDestinationsInvalid = false; if (($scope.destinationFilters == undefined || $scope.destinationFilters == null || $scope.destinationFilters.length < 1) && destinationNedeed) { //Si tiene filtro rápido no es necesario el destino isDestinationsInvalid = true; } if (isDestinationsInvalid) { if (!$scope.errorsList.includes('destinations')) $scope.errorsList.push('destinations'); } else { var index = $scope.errorsList.indexOf('destinations'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; case 'date': var isDateInvalid = false; if ($scope.date == null) { isDateInvalid = false; } else { if ($scope.date > $scope.maxDate) { parentForm.activityDate.$setValidity('maxdate', false); isDateInvalid = true; } else { parentForm.activityDate.$setValidity('maxdate', true); } if ($scope.date < $scope.minDate) { parentForm.activityDate.$setValidity('mindate', false); isDateInvalid = true; } else { parentForm.activityDate.$setValidity('mindate', true); } } if (isDateInvalid) { if (!$scope.errorsList.includes('date')) $scope.errorsList.push('date'); } else { var index = $scope.errorsList.indexOf('date'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; } $scope.isValidForm = ($scope.errorsList.length == 0); } $scope.isModalOpen = false; $scope.showBackdrop = false; $scope.showCountTotal = false; $scope.filtersSelected = 0; $scope.animationFilters = function () { $scope.showBackdrop = !$scope.showBackdrop; $scope.isModalOpen = !$scope.isModalOpen; if (!$scope.isModalOpen) { setTimeout(function () { $scope.showBackdrop = false; $("html").css("overflow", ""); $("body").css("overflow", ""); }, 300); } else { $scope.showBackdrop = true; $("html").css("overflow", "hidden"); $("body").css("overflow", "hidden"); } if ($scope.isModalOpen == false) { setTimeout(function () { $scope.showBackdrop = false; }, 300); } } $scope.totalOverflowing = 0; $scope.overflow = function () { setTimeout(function () { $(window).on('resize', function () { var element = document.querySelector('#overflowbar'); if (element != undefined && element != null) { if ((element.offsetHeight < element.scrollHeight) || (element.offsetWidth < element.scrollWidth)) { // your element have overflow $("#button-overflow").css("display", ""); $(".filter-style__tag-separator").css("display", ""); if ($(document).width() > 1366) { var parentContainerWidth = $(".filter-style").width() - 680; } else { var parentContainerWidth = $("#overflowbar").width() - 50; } $(".tag-box__tag-box").children().css("visibility", ""); var elementCount = $('.tag-box__tag-selected').filter(function () { var hidden = $(this).offset().left >= parentContainerWidth; if (hidden == true) { $(this).css("visibility", "hidden"); } else { $(this).css("visibility", ""); } return hidden; }).length; $scope.totalOverflowing = elementCount; } else { //your element don't have overflow $("#button-overflow").css("display", "none"); $(".filter-style__tag-separator").css("display", "none"); $scope.totalOverflowing = 0; $(".tag-box__tag-box").children().css("visibility", ""); } } }).resize(); }, 200); } $(window).on('resize', function () { var element = document.querySelector('#overflowbar'); if (element != undefined && element != null) { if ((element.offsetHeight < element.scrollHeight) || (element.offsetWidth < element.scrollWidth)) { // your element have overflow $("#button-overflow").css("display", ""); $(".filter-style__tag-separator").css("display", ""); if ($(document).width() > 1366) { var parentContainerWidth = $(".filter-style").width() - 680; } else { var parentContainerWidth = $("#overflowbar").width() - 50; } $(".tag-box__tag-box").children().css("visibility", ""); var elementCount = $('.tag-box__tag-selected').filter(function () { var hidden = $(this).offset().left >= parentContainerWidth; if (hidden == true) { $(this).css("visibility", "hidden"); } else { $(this).css("visibility", ""); } return hidden; }).length; $scope.totalOverflowing = elementCount; } else { //your element don't have overflow $("#button-overflow").css("display", "none"); $(".filter-style__tag-separator").css("display", "none"); $scope.totalOverflowing = 0; $(".tag-box__tag-box").children().css("visibility", ""); } } }).resize(); //translation method $scope.translate = function (key) { return $scope.arrayTranslations[key]; } $scope.clearSearchTag = function () { $scope.selectedText = null; setTimeout(function () { $scope.overflow(); }, 200); } $scope.clearQuickFilter = function () { $scope.selectedQuickFilter = null; $scope.overflow(); //$window.location.href = '/agent-booking-configuration'; } $(".tag-box__tag-selected").css("visibility", ""); $scope.clearDateTag = function () { $scope.date = null; setTimeout(function () { $scope.overflow(); }, 200); } $scope.openCloseModal = function () { $scope.animationFilters(); } $scope.applyFilters = function () { // Boton aplicar $scope.overflow(); $scope.showBackdrop = !$scope.showBackdrop; $scope.animationFilters(); if ($scope.filtersSelected > 0) { $scope.showCountTotal = true; } else { $scope.showCountTotal = false; } } $scope.$watch('blockForm', function (newVal, oldVal) { if (newVal !== oldVal) { var countverticals = $scope.verticals != undefined && $scope.verticals != null ? $scope.verticals.length : 0; var countlanguage = $scope.language != undefined && $scope.language != null ? $scope.language.length : 0; var countproviders = $scope.providers != undefined && $scope.providers != null ? $scope.providers.length : 0; var dateSelected = 0; var textTyped = 0; var freeToursSelected = 0; var quickFilterSelected = 0; if ($scope.date != undefined && $scope.date != null) { dateSelected = 1; } if ($scope.selectedText != undefined && $scope.selectedText != null && $scope.selectedText != "") { textTyped = 1; } if ($scope.freeTours == true) { freeToursSelected = 1; } if ($scope.quickFilter != null && $scope.quickFilter != undefined) { quickFilterSelected = 1; } $scope.filtersSelected = countverticals + countlanguage + countproviders + dateSelected + textTyped + freeToursSelected + quickFilterSelected; } }); $scope.clearAll = function () { //Para quitar todo $scope.selectedQuickFilter = null; $scope.clearSelectorFilters(); } $scope.freeToursButton = function () { if ($scope.selectedFreeTours == false) { $scope.selectedFreeTours = true; } else { $scope.selectedFreeTours = false; } } $scope.newSortType = function (val) { $scope.activitySortType = val; } $scope.clearSelectorFilters = function () { $scope.selectedVerticals = []; $scope.selectedLanguages = []; $scope.selectedProviders = []; $scope.selectedText = null; $scope.selectedFreeTours = false; $scope.selectedDate = null; $scope.filtersSelected = 0; $("#button-overflow").css("display", "none"); $(".filter-style__tag-separator").css("display", "none"); $scope.totalOverflowing = 0; } $scope.$watch('freeTours', function (newVal, oldVal) { if (newVal !== oldVal) { $scope.selectedFreeTours = newVal; } }); $scope.$watch('selectedFreeTours', function (newVal, oldVal) { if (newVal !== oldVal) { //clearAllFacetFilters(); $scope.freeTours = newVal; $scope.searchTrigger = "freeTours"; } }); $scope.$watch('date', function (newVal, oldVal) { var newValDate = (newVal != undefined && newVal instanceof Date) ? newVal.getTime() : newVal; var oldValDate = (oldVal != undefined && oldVal instanceof Date) ? oldVal.getTime() : oldVal; var selectedDate = ($scope.selectedDate != undefined && $scope.selectedDate != null) ? $scope.selectedDate.getTime() : $scope.selectedDate; var datePicked = $scope.selectedDate; if (datePicked != null) { $scope.datePicked = datePicked.getDate() + "/" + (datePicked.getMonth() + 1) + "/" + datePicked.getFullYear(); } if (newValDate !== oldValDate && newValDate !== selectedDate) { if (newVal != null) $scope.selectedDate = newVal; else $scope.selectedDate = null; validate("date"); } }); $scope.$watch('selectedDate', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var date = angular.isDate($scope.date) ? $scope.date.getTime() : null; if (newValDate !== oldValDate && (date !== newValDate || newValDate == null)) { $scope.date = $scope.selectedDate; //clearAllFacetFilters(); validate("date"); $scope.searchTrigger = "date"; } }); $scope.$watch('destinationFilters', function (newVal, oldVal) { if (newVal != oldVal) { setInternalDestinationFilters(newVal); } }, true); $scope.$watch('selectedDestinationFilters', function (newVal, oldVal) { if (newVal != oldVal) { $scope.destinationString = ''; if (newVal != null && newVal.length > 0) { $scope.destinationFilters = []; for (var index in newVal) { var destination = newVal[index]; var dest = {}; dest.placeId = destination.PlaceId; dest.displayName = destination.AutoCompleteText; $scope.showActivitiesFilters = true; $scope.destinationFilters.push(dest); if (index > 0) $scope.destinationString = $scope.destinationString + ',' + dest.displayName; else $scope.destinationString = dest.displayName; } $scope.clearAll(); } else { $scope.destinationFilters = []; $scope.destinationString = ''; if ($scope.selectedQuickFilter != undefined && $scope.selectedQuickFilter != null) $scope.clearSelectorFilters(); else $scope.clearAll(); } validate("destinations"); $scope.searchTrigger = "destinationFilters"; } }, true); function setInternalDestinationFilters(newVal) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.selectedDestinationFilters = []; } else { var list = []; for (var index in newVal) { var elem = {}; elem.PlaceId = newVal[index].placeId; elem.AutoCompleteText = newVal[index].displayName; list.push(elem); } $scope.selectedDestinationFilters = list; } } $scope.getActivityDestinations = function (filter) { var placeId = null; if ($scope.destinationFilters != undefined && $scope.destinationFilters != null && $scope.destinationFilters.length > 0) placeId = $scope.destinationFilters[0].placeId; return Purchases.getActivitiesDestinationPrediction(filter, $scope.prefix, placeId) .then(function (response) { return response; }); }; $scope.$watch('verticalOptions', function (newVal, oldVal) { if (newVal !== oldVal) { if ($scope.verticals != undefined && $scope.verticals != null && $scope.verticals.length > 0) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.verticals = $scope.selectedVerticals = []; } else { var newList = []; angular.forEach(newVal, function (item, index) { if (shApi.checkIfContains($scope.verticals, item.Key)) { newList.push(item.Key); } }); $scope.verticals = newList; } } } }, true); $scope.$watch('verticals', function (newVal, oldVal) { if (newVal !== oldVal) { setVerticalInternalValues(newVal); } }, true); $scope.$watch('selectedVerticals', function (newVal, oldVal) { if (newVal !== oldVal) { if (newVal == null || newVal == '') { if ($scope.verticals != undefined && $scope.verticals != null && $scope.verticals.length > 0) { $scope.verticals = []; $scope.searchTrigger = "verticalFilters"; } else { $scope.verticals = []; } } else if (JSON.stringify($scope.verticals) != JSON.stringify($scope.selectedVerticals.map(function (j) { return j.Key; }))) { var verticalList = []; for (var index in $scope.selectedVerticals) { verticalList.push($scope.selectedVerticals[index].Key); } $scope.verticals = verticalList; $scope.searchTrigger = "verticalFilters"; //$scope.$apply(); } } }, true); function setVerticalInternalValues(newVal) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.selectedVerticals = []; } else { //Search in list var verticalList = []; for (var index in $scope.verticals) { var vertical = shApi.returnByProperty($scope.verticalOptions, 'Key', $scope.verticals[index]); if (vertical != null) verticalList.push(vertical); } $scope.selectedVerticals = verticalList; } } // ------------------------------------------------------------ Provider ------------------------------------------------------------ $scope.$watch('providerOptions', function (newVal, oldVal) { if (newVal !== oldVal) { if ($scope.providers != undefined && $scope.providers != null && $scope.providers.length > 0) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.providers = $scope.selectedProviders = []; } else { var newList = []; angular.forEach(newVal, function (item, index) { if (shApi.checkIfContains($scope.providers, item.UniqueId)) { newList.push(item.UniqueId); } }); $scope.providers = newList; } } } }, true); $scope.$watch('providers', function (newVal, oldVal) { if (newVal !== oldVal) { setProviderInternalValues(newVal); } }, true); $scope.$watch('selectedProviders', function (newVal, oldVal) { if (newVal !== oldVal) { if (newVal == null || newVal == '') { if ($scope.providers != undefined && $scope.providers != null && $scope.providers.length > 0) { $scope.providers = []; $scope.searchTrigger = "providerFilters"; } else { $scope.providers = []; } } else if (JSON.stringify($scope.providers) != JSON.stringify($scope.selectedProviders.map(function (j) { return j.UniqueId; }))) { var providerList = []; for (var index in $scope.selectedProviders) { providerList.push($scope.selectedProviders[index].UniqueId); } $scope.providers = providerList; $scope.searchTrigger = "providerFilters"; //$scope.$apply(); } } }, true); function setProviderInternalValues(newVal) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.selectedProviders = []; } else { //Search in list var providerList = []; for (var index in $scope.providers) { var provid = shApi.returnByProperty($scope.providerOptions, 'UniqueId', $scope.providers[index]); if (provid != null) providerList.push(provid); } $scope.selectedProviders = providerList; } } // -------------------------------------------------------------------------------------------------------------------------------------------- $scope.$watch('searchText', function (newVal, oldVal) { if (newVal !== oldVal) { setSearchTextInternalValue(newVal); } }); var $qtimer = null; $scope.$watch('selectedText', function (newVal, oldVal) { if (newVal !== oldVal) { if ($qtimer != null) $timeout.cancel($qtimer); $qtimer = $timeout(function () { if (newVal == null || newVal == '') $scope.searchText = null; else $scope.searchText = newVal; $scope.searchTrigger = "freeText"; $qtimer = null; }, 900); } }); function setSearchTextInternalValue(newVal) { if (newVal == undefined || newVal == null || newVal == '') { $scope.selectedText = null; } else $scope.selectedText = $scope.searchText; } $scope.$watch('languageOptions', function (newVal, oldVal) { if (newVal !== oldVal) { if ($scope.language != undefined && $scope.language != null && $scope.language.length > 0) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.language = $scope.selectedLanguages = []; } else { var newList = []; angular.forEach(newVal, function (item, index) { if (shApi.checkIfContains($scope.language, item.Code)) { newList.push(item.Code); } }); $scope.language = newList; } } } }, true); $scope.$watch('language', function (newVal, oldVal) { if (newVal !== oldVal) { setLanguageInternalValues(newVal); } }); //Only one language by the moment $scope.$watch('selectedLanguages', function (newVal, oldVal) { if (newVal !== oldVal) { if (newVal == null || newVal == '') { if ($scope.language != undefined && $scope.language != null && $scope.language.length > 0) { $scope.language = []; $scope.searchTrigger = "language"; } else { $scope.language = []; } } else if (JSON.stringify($scope.language) != JSON.stringify($scope.selectedLanguages.map(function (j) { return j.Code; }))) { var languageList = []; for (var index in $scope.selectedLanguages) { languageList.push($scope.selectedLanguages[index].Code); } $scope.language = languageList; $scope.searchTrigger = "language"; } } }, true); function setLanguageInternalValues(newVal) { if (newVal == null || newVal == '') { $scope.selectedLanguages = []; } else { var langList = []; //Search in list for (var index in $scope.language) { var lang = shApi.returnByProperty($scope.languageOptions, 'Code', $scope.language[index]); if (lang != null) langList.push(lang); } $scope.selectedLanguages = langList; } } $scope.getLanguages = function (filter) { return Lookups.getPurchaseLanguages(filter, true, $scope.prefix) .then(function (response) { return response; }); }; $scope.clearLanguage = function () { $scope.selectedLanguage = null; }; $scope.durationSelectorOnClose = function () { $scope.searchTrigger = "durationRange"; }; $scope.selectQuickFilter = function (code) { if ($scope.selectedQuickFilter != undefined && $scope.selectedQuickFilter != null && $scope.selectedQuickFilter.Code == code) { $scope.selectedQuickFilter = null; } else { //Search in list setSelectedQuickFilterByCode(code); } }; function setSelectedQuickFilterByCode(code) { var quickFilter = shApi.returnByProperty($scope.quickFilters, 'Code', code); if (quickFilter != null) $scope.selectedQuickFilter = quickFilter; } //Quick filter $scope.$watch('selectedQuickFilter', function (newVal, oldVal) { if (newVal != oldVal) { if (newVal != undefined && newVal != null && newVal.Code != undefined && newVal.Code != null) { $scope.showActivitiesFilters = true; $scope.quickFilter = newVal.Code; $scope.destinationFilters = []; } else { $scope.quickFilter = null; } $scope.clearSelectorFilters(); //Search timeout $timeout(function () { validate("destinations"); $scope.searchTrigger = "quickFilter"; }, 500); } }); $scope.$watch('quickFilter', function (newVal, oldVal) { if (newVal != oldVal) { if (newVal != undefined && newVal != null) { setSelectedQuickFilterByCode(newVal); } else $scope.selectedQuickFilter = null; } }); ///// $scope.onBookingDirectivesTemplateKeyDown = function ($event) { if ($event.keyCode == 13) { $event.preventDefault(); } }; $scope.callbackAction = function (e) { if ($scope.callbackCustomAction != undefined) { $scope.callbackCustomAction(e); } } $scope.hideMobile = function () { $scope.mobileClick = false; } $scope.focusElement = function (el) { $timeout(function () { document.getElementById(el).focus(); }, 500); }; $scope.getRoundedTotal = function (totalCount) { if (totalCount == undefined || totalCount == null) return ''; return Math.floor((totalCount + 1) / 100) * 100; } }, templateUrl: function (elem, attrs) { //custom url var url = '/Scripts/app/Modules/bookingActivitiesTemplateFilters.html'; if (angular.isDefined(attrs.templateUrl)) { url = attrs.templateUrl; } return url; }, }; }); /* --------------------------------------------- Dropdown ------------------------------------------------------------------------------ */ ngSharedDirectives.directive('stMultiselectDropdownFilter', function ($filter, shApi) { return { restrict: 'E', scope: { model: '=', options: '=', defaultmsg: '@', name: '@', topBoolName: '@', id: '@', count: '@', multiselectClass: '@', controlName: '@', disabled: '=', right: '@', randomtext: '@', totalOverflowing:'@', more: '@', less: '@', mainClass: '@', loading: '=', }, templateUrl: '/Scripts/app/Modules/stMultiselectDropdownFilter.html', link: function (scope, elem, attrs, ctrls) { if (scope.fixed === undefined) scope.fixed = false; if (scope.disabled === undefined) scope.disabled = false; scope.hasIcon = false; scope.tags = angular.isDefined(attrs.tags); scope.filteroverflow = angular.isDefined(attrs.filteroverflow); if (attrs.iconClass !== undefined) { scope.hasIcon = true; scope.iconClass = attrs.iconClass; } var dropStyle = $(elem[0]).find('.dropdown-verticals'); scope.dropdownclass = function () { if (!scope.disabled) { if (dropStyle.hasClass('hidden')) { dropStyle.removeClass('hidden'); } else { dropStyle.addClass('hidden'); } } } }, controller: function ($scope) { $scope.init = function () { if ($scope.model == undefined) $scope.model = []; //if ($scope.totalOverflowing == undefined) $scope.totalOverflowing = 0; //else loadInternalValues($scope.model); } //$scope.init(); $scope.totalOverflowing = 0; $(".tag-box__tag-selected").css("visibility", ""); //Styles for watchers $scope.filterstyles = function () { //overflow setTimeout(function () { $(window).on('resize', function () { var element = document.querySelector('#overflowbar'); if (element != undefined && element != null) { if ((element.offsetHeight < element.scrollHeight) || (element.offsetWidth < element.scrollWidth)) { // your element have overflow $("#button-overflow").css("display", ""); $(".filter-style__tag-separator").css("display", ""); if ($(document).width() > 1366) { var parentContainerWidth = $(".filter-style").width() - 680; } else { var parentContainerWidth = $("#overflowbar").width() - 50; } $(".tag-box__tag-box").children().css("visibility", ""); var elementCount = $('.tag-box__tag-selected').filter(function () { var hidden = $(this).offset().left >= parentContainerWidth; if (hidden == true) { $(this).css("visibility", "hidden"); } else { $(this).css("visibility", ""); } return hidden; }).length; $scope.totalOverflowing = elementCount; } else { //your element don't have overflow $("#button-overflow").css("display", "none"); $(".filter-style__tag-separator").css("display", "none"); $scope.totalOverflowing = 0; $(".tag-box__tag-box").children().css("visibility", ""); } } }).resize(); }, 200); } $scope.toggleSelectedItem = function (option) { var intIndex = -1; for (var idx in $scope.model) { if ($scope.getValue(option) == $scope.getValue($scope.model[idx])) { intIndex = idx; break; } }; if (intIndex >= 0) { $scope.model.splice(intIndex, 1); $scope.filterstyles(); } else { $scope.model.push(option); $scope.filterstyles(); } }; //open $scope.initialValues = function () { $scope.initialList = angular.copy($scope.model); } //only mobile $scope.return = function () { $scope.model = angular.copy($scope.initialList); } //Default use as dictionary $scope.getName = function (option) { if (typeof $scope.name == 'undefined' || $scope.name == null) return option["value"]; else return option[$scope.name]; } $scope.getValue = function (option) { if (typeof $scope.id == 'undefined' || $scope.id == null) return option["key"]; else return option[$scope.id]; } $scope.resumeName = $scope.defaultmsg; $scope.toggleSelectItem = function (option) { var intIndex = -1; for (var idx in $scope.model) { if ($scope.getValue(option) == $scope.getValue($scope.model[idx])) { intIndex = idx; break; } }; if (intIndex >= 0) { $scope.model.splice(intIndex, 1); $scope.filterstyles(); } else { $scope.model.push(option); $scope.filterstyles(); } }; $scope.getChecked = function (option) { var varSelected = ''; for (var idx in $scope.model) { if ($scope.getValue(option) == $scope.getValue($scope.model[idx])) { varSelected = true; break; } } return (varSelected); }; } } }); /* --------------------------------------------- --- ------------------------------------------------------------------------------------ */ /*Only for internal process*/ ngSharedDirectives.directive('activityFiltersOriginal', function ($timeout, Purchases, Lookups, shApi) { var isDateRequired = false; var parentForm; return { restrict: 'E', scope: { destinationFilters: '=', date: '=', datepickerOptions: '=', minDate: '=', maxDate: '=', defaultDate: '=', verticals: '=', verticalOptions: '=', searchText: '=', language: '=', languageOptions: '=', durationOptions: '=', durationRange: '=', arrayTranslations: '=', searchTrigger: '=', isValidForm: '=', errorsList: '=', freeTours: '=', prefix: '=', culture: '=', blockForm: '=', showActivitiesFilters: '=', callbackCustomAction: '=', totalCount: '=', }, link: function (scope, elem, attrs) { shApi.defineRequired(scope, scope); scope.useLanguageMultipleSelector = false; if (attrs.isDateRequired !== undefined) isDateRequired = (attrs.isDateRequired === 'true' || attrs.isDateRequired === ''); if (attrs.mydateformat !== undefined) scope.mydateformat = attrs.mydateformat; if (attrs.languageMultipleSelector !== undefined) scope.useLanguageMultipleSelector = (attrs.languageMultipleSelector || attrs.languageMultipleSelector === ''); parentForm = scope.parentForm = elem.parent().controller('form'); if (scope.date === undefined || scope.date === null) { scope.date = scope.minDate; } scope.selectedDate = scope.date; var inicialDate = new Date(Date.now()); inicialDate.setDate(inicialDate.getDate() + 7); scope.defaultDate = inicialDate; scope.destinationString = ''; scope.errorsList = []; if (scope.destinationFilters !== undefined && scope.destinationFilters !== null && scope.destinationFilters.length != 0) { for (var index in scope.destinationFilters) { var dest = scope.destinationFilters[index]; if (index > 0) scope.destinationString = scope.destinationString + ',' + dest.displayName; else scope.destinationString = dest.displayName; } } if (!scope.translationsLoaded) { scope.closemsg = scope.translate("_bookservice_apply"); scope.durationtext = scope.translate("_bookservice_duration_text"); scope.rangetext = scope.translate("_bookservice_duration_range"); scope.hourstext = scope.translate("_bookservice_duration_hours"); scope.verticaltext = scope.translate("_bookservice_vertical_input_text"); scope.reestablishtext = scope.translate("_bookservice_reestablish_text"); scope.alltext = scope.translate("_bookservice_all_text"); scope.categoryselectedtext = scope.translate("_bookservice_category_selected"); scope.categorylabeltext = scope.translate("_bookservice_vertical_input_text"); scope.languageslabeltext = scope.translate("_bookservice_label_languages"); scope.languagestext = scope.translate("_bookingservice_language_input_fromlist"); scope.languagesselectedtext = scope.translate("_bookservice_languages_selected"); scope.textfiltertext = scope.translate("_bookservice_activities_textfilter_text"); scope.translationsLoaded = true; } scope.Init(); }, controller: function ($scope) { $scope.Init = function () { setSelectedDestinations(); setSelectedVerticals(); setSelectedLanguages(); setTextFilter(); validate("destinations"); } function setSelectedDestinations() { setInternalDestinationFilters($scope.destinationFilters); } function setSelectedVerticals() { setVerticalInternalValues($scope.verticals); } function setSelectedLanguages() { setLanguageInternalValues($scope.language); } function setTextFilter() { setSearchTextInternalValue($scope.searchText); } //-- Validations ---------------------------- function validate(which) { if ($scope.errorsList == undefined) $scope.errorsList = []; switch (which) { case 'destinations': var isDestinationsInvalid = false; if ($scope.destinationFilters == undefined || $scope.destinationFilters == null || $scope.destinationFilters.length < 1) { isDestinationsInvalid = true; } if (isDestinationsInvalid) { if (!$scope.errorsList.includes('destinations')) $scope.errorsList.push('destinations'); } else { var index = $scope.errorsList.indexOf('destinations'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; case 'date': var isDateInvalid = false; if ($scope.date == null) { if (parentForm.activityDate.$touched) parentForm.activityDate.$setValidity('required', false); isDateInvalid = true; } else { parentForm.activityDate.$setValidity('required', true); if ($scope.date > $scope.maxDate) { parentForm.activityDate.$setValidity('maxdate', false); isDateInvalid = true; } else { parentForm.activityDate.$setValidity('maxdate', true); } if ($scope.date < $scope.minDate) { parentForm.activityDate.$setValidity('mindate', false); isDateInvalid = true; } else { parentForm.activityDate.$setValidity('mindate', true); } } if (isDateInvalid) { if (!$scope.errorsList.includes('date')) $scope.errorsList.push('date'); } else { var index = $scope.errorsList.indexOf('date'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; //case 'language': // var isLanguageInvalid = false; // if ($scope.selectedLanguage != null && $scope.selectedLanguage != '' && $scope.selectedLanguage.CodeSimplified == undefined) { // isLanguageInvalid = true; // parentForm.language.$setValidity('fromlist', false); // } // else { // parentForm.language.$setValidity('fromlist', true); // } // if (isLanguageInvalid) { // if (!$scope.errorsList.includes('language')) $scope.errorsList.push('language'); // } // else { // var index = $scope.errorsList.indexOf('language'); // if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); // } // break; } $scope.isValidForm = ($scope.errorsList.length == 0); } //translation method $scope.translate = function (key) { return $scope.arrayTranslations[key]; } $scope.$watch('freeTours', function (newVal, oldVal) { if (newVal !== oldVal) { $scope.selectedFreeTours = newVal; } }); $scope.$watch('selectedFreeTours', function (newVal, oldVal) { if (newVal !== oldVal) { $scope.freeTours = newVal; $scope.searchTrigger = "freeTours"; } }); $scope.$watch('date', function (newVal, oldVal) { var newValDate = (newVal != undefined && newVal instanceof Date) ? newVal.getTime() : newVal; var oldValDate = (oldVal != undefined && oldVal instanceof Date) ? oldVal.getTime() : oldVal; var selectedDate = ($scope.selectedDate != undefined && $scope.selectedDate != null) ? $scope.selectedDate.getTime() : $scope.selectedDate; if (newValDate !== oldValDate && newValDate !== selectedDate) { if (newVal != null) $scope.selectedDate = newVal; else $scope.selectedDate = null; validate("date"); } }); $scope.$watch('selectedDate', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var date = angular.isDate($scope.date) ? $scope.date.getTime() : null; if (newValDate !== oldValDate && (date !== newValDate || newValDate == null)) { //Internal process: allow select date minimun date (same day) instead of throw error if ($scope.selectedDate < $scope.minDate) { $scope.date = $scope.minDate; } else $scope.date = $scope.selectedDate; validate("date"); $scope.searchTrigger = "date"; } }); //TODO: De momento solo para limpiar valores en la inicialización $scope.$watch('destinationFilters', function (newVal, oldVal) { if (newVal != oldVal) { setInternalDestinationFilters(newVal); } }, true); //TODO: Convertir y validar! $scope.$watch('selectedDestinationFilters', function (newVal, oldVal) { if (newVal != oldVal) { $scope.destinationString = ''; if (newVal != null && newVal.length > 0) { $scope.destinationFilters = []; for (var index in newVal) { var destination = newVal[index]; var dest = {}; dest.placeId = destination.PlaceId; dest.displayName = destination.AutoCompleteText; $scope.showActivitiesFilters = true; $scope.destinationFilters.push(dest); if (index > 0) $scope.destinationString = $scope.destinationString + ',' + dest.displayName; else $scope.destinationString = dest.displayName; } } else { $scope.destinationFilters = []; $scope.selectedText = null; $scope.searchText = null; $scope.destinationString = ''; } validate("destinations"); $scope.searchTrigger = "destinationFilters"; } }, true); function setInternalDestinationFilters(newVal) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.selectedDestinationFilters = []; } else { var list = []; for (var index in newVal) { var elem = {}; elem.PlaceId = newVal[index].placeId; elem.AutoCompleteText = newVal[index].displayName; list.push(elem); } $scope.selectedDestinationFilters = list; } } $scope.getActivityDestinations = function (filter) { var placeId = null; if ($scope.destinationFilters != undefined && $scope.destinationFilters != null && $scope.destinationFilters.length > 0) placeId = $scope.destinationFilters[0].placeId; return Purchases.getActivitiesDestinationPredictionWhitoutRegions(filter, $scope.prefix, placeId) .then(function (response) { return response; }); }; $scope.$watch('verticals', function (newVal, oldVal) { if (newVal !== oldVal) { setVerticalInternalValues(newVal); } }, true); $scope.verticalsSelectorOnClose = function () { try { //Lanzamos la query sólo cuando se haya cerrado el popup y haya cambiado la selección if (JSON.stringify($scope.verticals) != JSON.stringify($scope.selectedVerticals.map(function (j) { return j.key; }))) { $scope.verticals = []; for (var index in $scope.selectedVerticals) { $scope.verticals.push($scope.selectedVerticals[index].key); } $scope.searchTrigger = "verticalFilters"; $scope.$apply(); } } catch (e) { log.error("Error in event verticalsSelectorOnClose:" + e); } } function setVerticalInternalValues(newVal) { if (newVal == undefined || newVal == null || newVal.length == 0) { $scope.selectedVerticals = []; } else { $scope.selectedVerticals = []; //Search in list for (var index in $scope.verticals) { var vertical = shApi.returnByProperty($scope.verticalOptions, 'key', $scope.verticals[index]); if (vertical != null) $scope.selectedVerticals.push(vertical); } } } $scope.searchOnClose = function () { $scope.searchText = $scope.selectedText; $scope.searchTrigger = "freeText"; }; $scope.$watch('searchText', function (newVal, oldVal) { if (newVal !== oldVal) { setSearchTextInternalValue(newVal); } }); function setSearchTextInternalValue(newVal) { if (newVal == undefined || newVal == null || newVal == '') { $scope.selectedText = null; } else $scope.selectedText = $scope.searchText; } $scope.$watch('language', function (newVal, oldVal) { if (newVal !== oldVal) { setLanguageInternalValues(newVal); } }); ////If language list selector: $scope.languagesSelectorOnClose = function () { try { if ($scope.language === null) $scope.language = []; //Lanzamos la query sólo cuando se haya cerrado el popup y haya cambiado la selección if (JSON.stringify($scope.language) != JSON.stringify($scope.selectedLanguages.map(function (j) { return j.CodeSimplified; }))) { $scope.language = []; for (var index in $scope.selectedLanguages) { $scope.language.push($scope.selectedLanguages[index].CodeSimplified); } $scope.searchTrigger = "language"; $scope.$apply(); } } catch (e) { log.error("Error in event languagesSelectorOnClose:" + e); } } //Only one language by the moment $scope.$watch('selectedLanguages', function (newVal, oldVal) { if (!$scope.useLanguageMultipleSelector && newVal !== oldVal) { if (newVal != null && newVal.length > 0 && newVal[0].CodeSimplified != undefined) { $scope.language = $scope.selectedLanguages[0].CodeSimplified; } else { $scope.language = null; } $scope.searchTrigger = "language"; } }, true); function setLanguageInternalValues(newVal) { if (newVal == null || newVal == '') { $scope.selectedLanguages = []; } else { $scope.selectedLanguages = []; //Search in list for (var index in $scope.language) { var lang = shApi.returnByProperty($scope.languageOptions, 'CodeSimplified', $scope.language[index]); if (lang != null) $scope.selectedLanguages.push(lang); } } } $scope.getLanguages = function (filter) { return Lookups.getPurchaseLanguages(filter, true, $scope.prefix) .then(function (response) { return response; }); }; $scope.clearLanguage = function () { $scope.selectedLanguage = null; }; $scope.durationSelectorOnClose = function () { $scope.searchTrigger = "durationRange"; }; $scope.onBookingDirectivesTemplateKeyDown = function ($event) { if ($event.keyCode == 13) { $event.preventDefault(); } }; $scope.callbackAction = function (e) { if ($scope.callbackCustomAction != undefined) { $scope.callbackCustomAction(e); } } $scope.hideMobile = function () { $scope.mobileClick = false; } $scope.focusElement = function (el) { $timeout(function () { document.getElementById(el).focus(); }, 500); }; }, templateUrl: function (elem, attrs) { //custom url var url = '/Scripts/app/Modules/bookingActivitiesTemplate.html'; if (angular.isDefined(attrs.templateUrl)) { url = attrs.templateUrl; } return url; }, }; }); /* ------------------------------------------------------------------------------------------------------------------------------------------- */ ngSharedDirectives.directive('apartmentFilters', function ($timeout, Purchases, Lookups, shApi) { var isDateRequired = false; var parentForm; return { restrict: 'E', scope: { destinationFilters: '=', fromDate: '=', toDate: '=', datepickerOptions: '=', minDateCheckIn: '=', maxDateCheckIn: '=', minDateCheckout: '=', maxDateCheckout: '=', defaultDate: '=', arrayTranslations: '=', searchTrigger: '=', isValidForm: '=', errorsList: '=', prefix: '=', culture: '=', blockForm: '=', people: '=', showPeopleOptions: '=', callbackCustomAction: '=' }, link: function (scope, elem, attrs) { shApi.defineRequired(scope, scope); if (attrs.isDateRequired !== undefined) isDateRequired = (attrs.isDateRequired === 'true' || attrs.isDateRequired === '') if (attrs.mydateformat !== undefined) scope.mydateformat = attrs.mydateformat; if (attrs.showPeopleOptions !== undefined) scope.showPeopleOptions = (attrs.showPeopleOptions == "true" || attrs.showPeopleOptions == ''); parentForm = scope.parentForm = elem.parent().controller('form'); if (scope.date === undefined || scope.date === null) { scope.date = scope.minDate; } scope.toSelectedDate = scope.toDate; scope.fromSelectedDate = scope.fromDate; var inicialDate = new Date(Date.now()); inicialDate.setDate(inicialDate.getDate() + 7); scope.defaultDate = inicialDate; scope.errorsList = []; if (!scope.translationsLoaded) { scope.closemsg = scope.translate("_bookservice_apply"); scope.reestablishtext = scope.translate("_bookservice_reestablish_text"); scope.alltext = scope.translate("_bookservice_all_text"); scope.inputmsg = scope.translate("_bookservice_travelers"); scope.personmsg = scope.translate("_bookservice_person"); scope.peoplemsg = scope.translate("_bookservice_people"); scope.adultmsg = scope.translate("_bookservice_adult"); scope.adultsmsg = scope.translate("_bookservice_adults"); scope.childmsg = scope.translate("_bookservice_child"); scope.childrenmsg = scope.translate("_bookservice_children"); scope.infantmsg = scope.translate("_bookservice_infant"); scope.infantsmsg = scope.translate("_bookservice_infants"); scope.undertwelvemsg = scope.translate("_bookservice_childrenundertwelve"); scope.underfivemsg = scope.translate("_bookservice_childrenunderfive"); scope.guestmsg = scope.translate("_bookservice_apartment_guest"); scope.guestsmsg = scope.translate("_cartdetail_service_guests_selector"); scope.undertwelveapartmentmsg = scope.translate("_bookservice_childrenundertwelve_apartment"); scope.undertwomsg = scope.translate("_bookservice_childrenundertwo"); scope.babymsg = scope.translate("_bookservice_baby"); scope.babiesmsg = scope.translate("_bookservice_babies"); scope.translationsLoaded = true; } $(document).ready(function (e) { scope.Init(); }); }, controller: function ($scope) { $scope.Init = function () { setSelectedDestinations(); validate("destinations"); } function setSelectedDestinations() { $scope.selectedDestinationFilters = angular.copy($scope.destinationFilters); } //-- Validations ---------------------------- function validate(which) { if ($scope.errorsList == undefined) $scope.errorsList = []; switch (which) { case 'destinations': var isDestinationsInvalid = false; if ($scope.destinationFilters == undefined || $scope.destinationFilters == null || $scope.destinationFilters.length < 1) { isDestinationsInvalid = true; } if (isDestinationsInvalid) { if (!$scope.errorsList.includes('destinations')) $scope.errorsList.push('destinations'); } else { var index = $scope.errorsList.indexOf('destinations'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; case 'fromDate': var isDateInvalid = false; if ($scope.fromDate == null) { if (parentForm.fromApartmentDate.$touched) parentForm.fromApartmentDate.$setValidity('required', false); isDateInvalid = true; } else { parentForm.fromApartmentDate.$setValidity('required', true); if ($scope.fromDate > $scope.maxDate) { parentForm.fromApartmentDate.$setValidity('maxdate', false); isDateInvalid = true; } else { parentForm.fromApartmentDate.$setValidity('maxdate', true); } if ($scope.fromDate < $scope.minDate) { parentForm.fromApartmentDate.$setValidity('mindate', false); isDateInvalid = true; } else { parentForm.fromApartmentDate.$setValidity('mindate', true); } if ($scope.fromDate >= $scope.toDate) { var aux = new Date(); aux.setFullYear($scope.fromDate.getFullYear()) aux.setMonth($scope.fromDate.getMonth()) aux.setDate($scope.fromDate.getDate() + 1) $scope.toDate = aux; } } if (isDateInvalid) { if (!$scope.errorsList.includes('fromDate')) $scope.errorsList.push('fromDate'); } else { var index = $scope.errorsList.indexOf('fromDate'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); var aux = new Date(); aux.setFullYear($scope.fromDate.getFullYear()) aux.setMonth($scope.fromDate.getMonth()) aux.setDate($scope.fromDate.getDate() + 1) $scope.minDateCheckout = aux; } break; case 'toDate': var isDateInvalid = false; if ($scope.toDate == null) { if (parentForm.toApartmentDate.$touched) parentForm.toApartmentDate.$setValidity('required', false); isDateInvalid = true; } else { parentForm.toApartmentDate.$setValidity('required', true); if ($scope.toDate > $scope.maxDate) { parentForm.toApartmentDate.$setValidity('maxdate', false); isDateInvalid = true; } else { parentForm.toApartmentDate.$setValidity('maxdate', true); } if ($scope.toDate < $scope.minDate) { parentForm.toApartmentDate.$setValidity('mindate', false); isDateInvalid = true; } else { parentForm.toApartmentDate.$setValidity('mindate', true); } if ($scope.fromDate >= $scope.toDate) { parentForm.fromApartmentDate.$setValidity('maxdate', false); isDateInvalid = true; } else { parentForm.fromApartmentDate.$setValidity('maxdate', true); } } if (isDateInvalid) { if (!$scope.errorsList.includes('toDate')) $scope.errorsList.push('toDate'); } else { var index = $scope.errorsList.indexOf('toDate'); if (index != undefined && index != -1) $scope.errorsList.splice(index, 1); } break; } $scope.isValidForm = ($scope.errorsList.length == 0); } //translation method----- $scope.translate = function (key) { return $scope.arrayTranslations[key]; } $scope.$watch('toDate', function (newVal, oldVal) { var newValDate = (newVal != undefined && newVal instanceof Date) ? newVal.getTime() : newVal; var oldValDate = (oldVal != undefined && oldVal instanceof Date) ? oldVal.getTime() : oldVal; var selectedDate = ($scope.toSelectedDate != undefined && $scope.toSelectedDate != null) ? $scope.toSelectedDate.getTime() : $scope.toSelectedDate; if (newValDate !== oldValDate && newValDate !== selectedDate) { if (newVal != null) $scope.toSelectedDate = newVal; else $scope.toSelectedDate = null; validate("toDate"); } }); $scope.$watch('toSelectedDate', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var date = angular.isDate($scope.toDate) ? $scope.toDate.getTime() : null; if (newValDate !== oldValDate && (date !== newValDate || newValDate == null)) { $scope.toDate = $scope.toSelectedDate; validate("toDate"); $scope.searchTrigger = "toDate"; } }); $scope.$watch('fromDate', function (newVal, oldVal) { var newValDate = (newVal != undefined && newVal instanceof Date) ? newVal.getTime() : newVal; var oldValDate = (oldVal != undefined && oldVal instanceof Date) ? oldVal.getTime() : oldVal; var selectedDate = ($scope.fromSelectedDate != undefined && $scope.fromSelectedDate != null) ? $scope.fromSelectedDate.getTime() : $scope.fromSelectedDate; if (newValDate !== oldValDate && newValDate !== selectedDate) { if (newVal != null) $scope.fromSelectedDate = newVal; else $scope.fromSelectedDate = null; validate("fromDate"); } }); $scope.$watch('fromSelectedDate', function (newVal, oldVal) { var newValDate = newVal != undefined ? newVal.getTime() : newVal; var oldValDate = oldVal != undefined ? oldVal.getTime() : oldVal; var date = angular.isDate($scope.fromDate) ? $scope.fromDate.getTime() : null; if (newValDate !== oldValDate && (date !== newValDate || newValDate == null)) { $scope.fromDate = $scope.fromSelectedDate; //Guardamos la toDate antes de validar, para no hacer la busqueda en caso de haber cambiado (la busqueda la lanzará el watcher del toDate) var unchangedToDate = $scope.toDate; validate("fromDate"); if (unchangedToDate == $scope.toDate) $scope.searchTrigger = "fromDate"; } }); //TODO: De momento solo para limpiar valores en la inicialización $scope.$watch('destinationFilters', function (newVal, oldVal) { if (newVal !== oldVal && (newVal == null || newVal.length == 0)) { $scope.selectedDestinationFilters = []; } }, true); //TODO: Convertir y validar! $scope.$watch('selectedDestinationFilters', function (newVal, oldVal) { if (newVal !== oldVal) { if (newVal != null && newVal.length > 0) { $scope.destinationFilters = []; for (var index in newVal) { var destination = newVal[index]; var dest = {}; dest.placeId = destination.PlaceId; dest.displayName = destination.AutoCompleteText; $scope.showApartmentFilters = true; $scope.destinationFilters.push(dest); } } else { $scope.destinationFilters = []; $scope.showApartmentFilters = false; $scope.selectedDate = $scope.defaultDate; } validate("destinations"); $scope.searchTrigger = "destinationFilters"; } }, true); $scope.getApartmentDestinations = function (filter) { var placeId = null; var apartment = null; if ($scope.destinationFilters != undefined && $scope.destinationFilters != null && $scope.destinationFilters.length > 0) placeId = $scope.destinationFilters[0].placeId; apartment = shApi.generateGuid(); return Purchases.getApartmentsDestinationPrediction(filter, placeId, $scope.prefix, apartment) .then(function (response) { return response; }); }; $scope.onBookingDirectivesTemplateKeyDown = function ($event) { if ($event.keyCode == 13) { $event.preventDefault(); } }; $scope.apartmentPeopleSelectorOnClose = function () { $scope.searchTrigger = "detailedPeople"; $scope.$apply(); }; $scope.callbackAction = function (e) { if ($scope.callbackCustomAction != undefined) { $scope.callbackCustomAction(e); } } }, templateUrl: function (elem, attrs) { //custom url var url = '/Scripts/app/Modules/bookingApartmentsTemplate.html'; if (angular.isDefined(attrs.templateUrl)) { url = attrs.templateUrl; } return url; }, }; }); var INTEGER_REGEXP = /^\-?\d+$/; ngSharedDirectives.directive('integer', function () { return { require: 'ngModel', link: function (scope, elm, attrs, ctrl) { ctrl.$validators.integer = function (modelValue, viewValue) { if (ctrl.$isEmpty(modelValue)) { // consider empty models to be valid return true; } if (INTEGER_REGEXP.test(viewValue)) { // it is valid return true; } // it is invalid return false; }; } }; }); ngSharedDirectives.directive('modalDialog', function (Translator, $compile, $timeout) { var cuantos = 0; return { restrict: 'E', transclude: true, replace: true, scope: { show: '=', type: '@', message: '=', showclose: '&', contentid: '@', resultmethod: '=', title: '=', defaultbtn: '@', image: '=', uploadmethod: '=', attachtype: '=', maxsize: '=', disallowscroll: '=', closeonclick: '=', callback: '&', cssclass: "=" }, template: '
' + ' ' + '
', link: function (scope, element, attrs, transclude) { scope.showed = false; var name = attrs.contentid != undefined ? attrs.contentid : 'modalDirective'; scope.id = name + (++cuantos); scope.$watch('show', function (showVal) { if (typeof showVal == 'undefined') return; if (showVal && attrs.cssclass != undefined && angular.isString(attrs.cssclass)) { $("#content-" + scope.id).addClass(attrs.cssclass); } if (showVal && !scope.showed) { scope.showed = true; if (scope.disallowscroll == true) { $('html').css('overflow-y', 'hidden'); } if (!scope.isTranscluded()) { $("#" + scope.id + " div.modal-header").html(scope.getHeader()); $('#' + scope.id + " div.modal-body").html(""); $("#" + scope.id + " div.modal-body").html(scope.getBody()); $("#" + scope.id + " div.modal-footer").html(scope.getFooter()); $compile($("#" + scope.id)[0])(scope); } $("#" + scope.id).data("backdrop", scope.isCloseable().toString()); $("#" + scope.id).modal('show'); } else if (!showVal && scope.showed) { $("#" + scope.id).modal('hide'); scope.callback(); scope.showed = false; if (scope.disallowscroll == true) { $('html').css('overflow-y', 'auto'); } } }); if (scope.closeonclick == true) { $timeout(function () { $("#" + scope.id).on('hidden.bs.modal', function (e) { $timeout(function () { scope.show = false; //$("#" + scope.id).find("iframe").each(function () { // $(this).attr('src', '_blank'); //}); }); }); }); } }, controller: function ($scope, $compile) { //Attach $scope.$watch('attach', function (files) { if (files != null && files.name != null && files.name != undefined) { if (typeof $scope.uploadmethod !== 'undefined' && $scope.uploadmethod !== null) { $scope.uploadmethod(files, $scope.attachtype); } } else { $scope.attach = null; } }); $scope.isCloseable = function () { if ($scope.closeonclick == true) { return true; } if (typeof $scope.type != 'undefined' && ((( $scope.type.toLowerCase() === 'warning') && (typeof $scope.resultmethod === 'undefined' || $scope.resultmethod === null)) )) { return true; } return 'static'; } $scope.getBody = function () { var strret = ''; if (typeof $scope.contentid != 'undefined' && $scope.contentid != '' && (typeof $scope.type === 'undefined')) strret = $('#' + $scope.contentid).clone().removeClass('hide')[0].outerHTML; else if (typeof $scope.type === 'undefined' || $scope.type === 'loading') { var loadingMsg = null; if ($scope.message != undefined && $scope.message != null) loadingMsg = $scope.message; else loadingMsg = Translator.getTranslation('modal_loading'); strret = '
' + '
' /*'

' + loadingMsg + '...

'*/; } else if ($scope.type === 'jswindow') { strret = '
' + '' + '

' + $scope.title + '

' + '' + $scope.message + '' + '
' } else if ($scope.type === "warning") { strret = '
' + '

' + '' + $scope.message + '' + '
'; } else if ($scope.type.toLowerCase() === 'question') { strret = '' + $scope.message + ''; } else if ($scope.type.toLowerCase() === 'error') { strret = (typeof $scope.showclose != 'undefined' && $scope.showclose() ? '
' + '' + '
' : "") + ''; } else if ($scope.type.toLowerCase() === 'attach') { strret = '' + '
' + '
' + $scope.message + '
' + '
' + '' + '
' + '
' + '
' + '
' + '
' + Translator.getTranslation('modal_attach_message') + '
' + '
' + '
' + Translator.getTranslation('modal_attach_error_notavailable') + '
' + '
' + '' + Translator.getTranslation('modal_attach_error_size') + '' + '' + Translator.getTranslation('modal_attach_error_format') + '' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + Translator.getTranslation('modal_loading') + ': {{attach.progress}}%
' + '' ; } return strret; } $scope.getHeader = function () { var strret = ''; if (typeof $scope.type !== 'undefined' && $scope.type.toLowerCase() === 'error') { strret = (typeof $scope.showclose != 'undefined' && $scope.showclose() ? '' : '') + '' + Translator.getTranslation('modal_error') + ''; } else if (typeof $scope.type !== 'undefined' && $scope.type.toLowerCase() === 'warning') { strret = (typeof $scope.showclose != 'undefined' && $scope.showclose() ? '' : ''); } else if (typeof $scope.type !== 'undefined' && ($scope.type.toLowerCase() === 'question')) { strret = (typeof $scope.showclose != 'undefined' && $scope.showclose() ? '' : '') + '' + $scope.title + ''; } return strret; } $scope.getFooter = function () { var strret = ''; if (typeof $scope.type !== 'undefined' && ($scope.type.toLowerCase() === 'error')) { strret = '
'; } if (typeof $scope.type !== 'undefined' && ($scope.type.toLowerCase() === 'warning')) { strret = '
'; } if (typeof $scope.type !== 'undefined' && $scope.type.toLowerCase() === 'question') { strret = '
'; } return strret; } $scope.isFirst = function () { return $scope.defaultbtn === '1'; } $scope.isSecond = function () { return $scope.defaultbtn === '2'; } $scope.isError = function () { return (typeof $scope.type !== 'undefined' && $scope.type.toLowerCase() === 'error'); } $scope.isLoading = function () { return ((typeof $scope.type === 'undefined' || $scope.type.toLowerCase() === 'loading') && (typeof $scope.contentid === 'undefined' || $scope.contentid === '')); } $scope.isWarning = function () { return (typeof $scope.type !== 'undefined' && $scope.type.toLowerCase() === 'warning'); } $scope.isTranscluded = function () { return (typeof $scope.type !== 'undefined' && $scope.type.toLowerCase() === 'transcluded'); } $scope.isQuestion = function () { return (typeof $scope.type !== 'undefined' && $scope.type.toLowerCase() === 'question'); } $scope.isInfo = function () { return (typeof $scope.type !== 'undefined' && $scope.type.toLowerCase() === 'info'); } $scope.isDrop = function () { return (typeof $scope.type !== 'undefined' && $scope.type.toLowerCase() === 'attach'); } $scope.errorResult = function () { if (typeof $scope.resultmethod !== 'undefined' && $scope.resultmethod !== null) { $scope.resultmethod(); } else { $scope.show = false; } } $scope.closeAttach = function () { $scope.show = false; if ($scope.attach !== 'undefined' && $scope.attach !== null) { $scope.attach = null; } } $scope.disableKeyBoard = function () { return (typeof $scope.type !== 'undefined' && ($scope.type.toLowerCase() === 'error' || $scope.type.toLowerCase() === 'warning')); } $scope.questionResult = function (result) { if (typeof $scope.resultmethod !== 'undefined' && $scope.resultmethod !== null) { $scope.resultmethod(result); } } } }; }); //directiva para evitar que se marque un formulario como $dirty ngSharedDirectives.directive('noDirty', function () { return { require: 'ngModel', link: function (scope, element, attrs, ngModelCtrl) { ngModelCtrl.$setDirty = angular.noop; } } }); // html filter (render text as html) ngSharedDirectives.filter('trusted', ['$sce', function ($sce) { return function (text) { return $sce.trustAsHtml(text); }; }]); ngSharedDirectives.filter('formatLineEndings', ['$sce', function ($sce) { return function (text) { if (typeof text !== "undefined" && text != null) { text = '

' + text; text = text.replace(/--/g, ""); text = text.replace(/\\r?\\n|\\r|\\n/g, "

"); text = text.replace(/

<\/p>/g, ""); text = text + '

'; return $sce.trustAsHtml(text); } return text; }; }]); //Foco en campo de formulario ngSharedDirectives.directive('focusMe', function ($timeout) { return { scope: { trigger: '=focusMe' }, link: function (scope, element) { scope.$watch('trigger', function (value) { if (value === true) { //console.log('trigger',value); $timeout(function() { element[0].focus(); }, 200); } }); } }; }); ngSharedDirectives.directive('selectOnClick', ['$window', function ($window) { return { restrict: 'A', link: function (scope, element, attrs) { element.on('click', function () { if (!$window.getSelection().toString()) { // Required for mobile Safari this.setSelectionRange(0, this.value.length) } }); } }; }]); ngSharedDirectives.filter('range', function () { return function (input, from, to) { to = parseInt(to, 10); from = parseInt(from, 10); for (var i = from; i < to; i++) { input.push(i); } return input; }; }); ngSharedDirectives.filter('notEmpty', function () { return function (input) { return typeof input !== 'undefined' && input !== null && input !== ''; }; }); ngSharedDirectives.filter('isEmpty', function () { return function (input) { return typeof input === 'undefined' || input === null || input === ''; }; }); ngSharedDirectives.filter('trim', function () { return function (value) { if (!angular.isString(value)) { return value; } return value.replace(/^\s+|\s+$/g, ''); // you could use .trim, but it's not going to work in IE<9 }; }); ngSharedDirectives.filter('UTC', function ($filter) { return function (date, format) { var tmp = new Date(date); var dt = new Date(tmp.getTime() + (tmp.getTimezoneOffset() * 60000)); if (typeof format !== 'undefined' && format !== null && format !== '') return $filter('date')(dt, format); return dt; }; }); ngSharedDirectives.filter('dateLE', function ($filter) { return function (date1, date2, onlyDate) { var r = false; if (date1 != undefined && date2 != undefined) { if (typeof onlyDate == undefined) onlyDate = false; var d1 = new Date(date1.getTime()); var d2 = new Date(date2.getTime()); if (onlyDate) { d1.setHours(0, 0, 0, 0); d2.setHours(0, 0, 0, 0); } r = d1.getTime() <= d2.getTime(); } return r; }; }); ngSharedDirectives.filter('replace', function ($filter) { return function (str) { if (typeof str != 'undefined' && str != null) { var newstr = str; if (arguments.length <= 1) return newstr; var replaceElems = Array.prototype.slice.call(arguments); replaceElems.splice(0, 1); for (i in replaceElems) { newstr = newstr.replace(RegExp('\\{' + i + '\\}', 'gi'), replaceElems[i]); } return newstr; } }; }); ngSharedDirectives.filter("formatCurrency", function ($filter) { return function (amount, mask) { if (typeof mask != 'undefined' && mask != null && typeof amount != 'undefined' && amount != null) { return mask.replace(/\s/g, "").replace(/\{(\d+)\}/g, function (match, capture) { return $filter('number')(amount, 2); }); }; }; }); ngSharedDirectives.directive('stCalendar', function (shApi, $filter) { return { restrict: 'E', require: ['^?form'], scope: { 'date': '=', 'dateInputName': '@', 'toDate': '=', 'toDateInputName': '@', 'dateCalendarCenter': '=', 'culture': '@', 'dateFormat': '@', 'minDate': '=', 'maxDate': '=', 'placeholder': '@', 'scheduling': '=', 'isInline': '=', 'isReadOnly': '=', 'clear': '=', 'range': '=', 'rangeSeparator': '@', 'mainClass': '@', 'iconClass': '@', 'label': '@', 'headerTitle': '@', 'isOpen': '=', 'disabled': '=', 'viewMode': '@', 'right': '@', 'left': '=', 'rightIconIfLeftTrue': '@', }, link: function (scope, elem, attrs, ctrls) { var hasForm = false; if (ctrls[0] != null) { hasForm = true; scope.form = ctrls[0]; } var defaultCulture = "en"; var defaultViewMode = "days"; var fullScheduling = null; scope.autoClose = true; scope.isOpen = false; scope.errorList = []; scope.required = angular.isDefined(attrs.required); scope.deleteBtn = angular.isDefined(attrs.deleteBtn); if (scope.disabled === undefined) scope.disabled = false; if (scope.isInline === undefined) scope.isInline = false; if (scope.dateFormat === undefined) scope.dateFormat = ""; if (scope.placeholder === undefined) scope.placeholder = ""; if (scope.deleteBtn === undefined) scope.deleteBtn = false; if (scope.minDate === undefined || scope.minDate === null) scope.minDate = ""; else if (!angular.isDate(scope.minDate)) scope.minDate = new Date(scope.minDate); if (scope.maxDate === undefined || scope.maxDate === null) scope.maxDate = ""; else if (!angular.isDate(scope.maxDate)) scope.maxDate = new Date(scope.maxDate); if (scope.range === undefined) scope.range = false; if (scope.rangeSeparator === undefined) scope.rangeSeparator = '-'; if (scope.isReadOnly === undefined) scope.isReadOnly = false; if (scope.clear === undefined) scope.clear = false; if (scope.scheduling !== undefined) fullScheduling = scope.scheduling; if (scope.viewMode === undefined) scope.viewMode = defaultViewMode; if (scope.culture === undefined || scope.culture.length < 2) scope.culture = defaultCulture; else scope.culture = scope.culture.substr(0, 2); var divCalendar = $(elem).find(".calendar-div")[0]; var onRender = function (date, cellType) { if (cellType == 'day') { var day = date.getDate(); if (fullScheduling !== null) { var currentDate = formatDate(date.getDate(), date.getMonth(), date.getFullYear()); if (fullScheduling[currentDate] != undefined) { if (!fullScheduling[currentDate].IsSoldOut) { var priceFormatted = $filter('formatCurrency')(fullScheduling[currentDate].FromPrice, fullScheduling[currentDate].CurrencyMask); return { html: '
' + day + '
' + priceFormatted + '
' } } else { return { disabled: true, html: '
' + day + '
' } } } else { return { disabled: true, html: '
' + day + '
', } } } else { //Standard format: return { html: '
' + day + '
', } } } }; var formatDate = function (day, month, year) { if (day < 10) { day = "0" + day; } if ((month + 1) < 10) { month = "0" + (month + 1); } else { month = month + 1; } date = year + "-" + month + "-" + day + "T00:00:00Z"; return date; }; //Load datepicker var calendar = $(divCalendar).datepicker({ inline: scope.isInline, range: scope.range, language: scope.culture, autoClose: scope.autoClose, classes: 'new-datepicker', dateFormat: scope.dateFormat, minDate: scope.minDate, maxDate: scope.maxDate, view: scope.viewMode, onRenderCell: function (date, cellType) { return onRender(date, cellType); }, onSelect: function (formattedDate, date, inst) { if (scope.range) { if (date.length == 2) { var datesFormatted = formattedDate.split(","); scope.date = date[0]; scope.toDate = date[1]; scope.dateText = datesFormatted[0]; scope.toDateText = datesFormatted[1]; if (hasForm) { scope.form[scope.dateInputName].$setDirty(); } close.trigger('click'); } } else { if (date != "" && !isNaN(date.getTime()) ) { lastDate = date; scope.date = lastDate; scope.dateText = formattedDate; if (hasForm) { scope.form[scope.dateInputName].$setValidity('pattern', true); scope.form[scope.dateInputName].$setDirty(); } close.trigger('click'); } } }, }); var calendarData = calendar.data('datepicker'); calendar.hide(); if (scope.dateCalendarCenter !== undefined && scope.dateCalendarCenter !== null) { var dateCalendarCenter = new Date(scope.dateCalendarCenter) if (angular.isDate(dateCalendarCenter)) { calendarData.date = dateCalendarCenter; } } scope.$watch('minDate', function (newVal, oldVal) { if (newVal != oldVal) { var changeMinDate = ''; if (newVal !== undefined && newVal != null) { var daux; if (angular.isDate(newVal)) { daux = newVal; } else { daux = new Date(newVal); } changeMinDate = daux; } calendarData.update('minDate', changeMinDate); } }); scope.$watch('maxDate', function (newVal, oldVal) { if (newVal != oldVal) { var changeMaxDate = ''; if (newVal !== undefined && newVal != null) { var daux; if (angular.isDate(newVal)) { daux = newVal; } else { daux = new Date(newVal); } changeMaxDate = daux; } calendarData.update('maxDate', changeMaxDate); } }); scope.$watch('scheduling', function (newVal, oldVal) { if (newVal != oldVal) { if (angular.isObject(newVal) && newVal !== undefined && newVal != null) { fullScheduling = newVal; } else fullScheduling = null; //Reload template calendarData.update({}); } }); var close = $('
'); var button = $(elem[0]).find('.js-button-selector'); var container = $(elem[0]).find('.js-dropdown-container'); var returnbutton = $(elem[0]).find('.js-return'); $(document).mouseup(function (e) { if (!container.is(e.target) && container.has(e.target).length === 0 && !button.is(e.target) && button.has(e.target).length === 0) { //only trigger event on close if the container was active, then, hidden it if (scope.isOpen) { e.preventDefault(); close.trigger('click'); } } }); $(document).ready(function (e) { close.click(function (e) { calendar.hide(); scope.isOpen = false; shApi.fullWindow(false); //TODO: esto?? scope.$applyAsync(); }); button.click(function (e) { if (!scope.isOpen) { if (!scope.disabled) { calendar.show(); scope.$apply(function () { scope.isOpen = true; }); shApi.fullWindow(true); } } else { close.trigger('click'); } }); returnbutton.click(function (e) { close.trigger('click'); }); if (scope.date !== null && scope.date !== undefined) { if (angular.isDate(scope.date)) calendarData.selectDate(scope.date); else calendarData.selectDate(new Date(scope.date)); } if (scope.range && scope.toDate !== null && scope.toDate !== undefined) { if (angular.isDate(scope.toDate)) calendarData.selectDate(scope.toDate); else calendarData.selectDate(new Date(scope.toDate)); } scope.$watch('date', function (newVal, oldVal) { if (newVal === undefined || newVal === null) { calendarData.clear(); scope.dateText = ""; } else if (angular.isDate(newVal) && angular.isDate(oldVal)) { if (newVal.getTime() !== oldVal.getTime() && (calendarData.selectedDates[0] === undefined || newVal.getTime() !== calendarData.selectedDates[0].getTime())) calendarData.selectDate(newVal); } else if (newVal !== oldVal && (calendarData.selectedDates[0] === undefined || newVal.getTime() !== calendarData.selectedDates[0].getTime())) { if (angular.isDate(newVal)) calendarData.selectDate(newVal); else calendarData.selectDate(new Date(newVal)); } }); if (scope.range) { scope.$watch('toDate', function (newVal, oldVal) { if (newVal === undefined || newVal === null) { calendarData.clear(); scope.toDateText = ""; } else if (angular.isDate(newVal) && angular.isDate(oldVal)) { if (newVal.getTime() !== oldVal.getTime() && (calendarData.selectedDates[1] === undefined || newVal.getTime() !== calendarData.selectedDates[1].getTime())) calendarData.selectDate(newVal); } else if (newVal !== oldVal && (calendarData.selectedDates[1] === undefined || newVal.getTime() !== calendarData.selectedDates[1].getTime())) { calendarData.selectDate(newVal); } }); } if (!scope.isReadOnly) { var inputDate = $(elem).find('input')[0]; $(inputDate).on("change", function (event) { var newDate = shApi.parseStringToDate(inputDate.value, scope.dateFormat); if (newDate != null && angular.isDate(newDate) && !isNaN(newDate.getTime())) { if (hasForm) scope.form[scope.dateInputName].$setValidity('pattern', true); if (newDate > calendarData.minDate && calendarData.maxDate > newDate) { calendarData.selectDate(newDate); calendarData.date = newDate; } else if (calendarData.minDate > newDate) { calendarData.selectDate(calendarData.minDate); calendarData.date = calendarData.minDate; } else if (newDate > calendarData.maxDate) { calendarData.selectDate(calendarData.maxDate); calendarData.date = calendarData.maxDate; } } else { if (hasForm) scope.form[scope.dateInputName].$setValidity('pattern', false); //TODO: esto?? scope.$applyAsync(); } }); } if (scope.deleteBtn) { scope.deleteDate = function (e) { e.preventDefault(); scope.date = null; if (scope.range) { scope.toDate = null; } } } }) }, controller: function ($scope) { }, templateUrl: '/Scripts/app/Modules/stCalendarTemplate.html' }; }); ngSharedDirectives.directive('stTimepicker', function (shApi) { return { restrict: 'E', require: ['^?form'], scope: { 'time': '=', 'timeInputName': '@', 'minTime': '=', 'maxTime': '=', 'stepMinutes': '@', 'placeholder': '@', 'mainClass': '@', 'iconClass': '@', 'label': '@', 'headerTitle': '@', 'applymsg': '@', 'isOpen': '=', 'disabled': '=', 'allowClear': '=', 'clearmsg': '@', 'right': '@', 'alone': '=', }, link: function (scope, elem, attrs, ctrls) { var hasForm = false; if (ctrls[0] != null) { hasForm = true; scope.form = ctrls[0]; } scope.isOpen = false; scope.required = angular.isDefined(attrs.required) var normalizeMinutes = function () { var minutes = scope.time.getMinutes(); if (minutes != 0) { var minutesMultiplier = Math.round(minutes / scope.stepMinutes); scope.time.setMinutes(minutesMultiplier * scope.stepMinutes); } }; if (scope.disabled === undefined) scope.disabled = false; if (scope.allowClear == undefined) scope.allowClear = false; if (scope.placeholder === undefined) scope.placeholder = ""; if (scope.headerTitle === undefined) scope.headerTitle = ""; if (scope.applymsg === undefined) scope.applymsg = ""; if (scope.stepMinutes === undefined) scope.stepMinutes = 1; if (scope.time !== undefined && angular.isDate(scope.time)) normalizeMinutes(); var container = $(elem).find('.js-dropdown-container'); var inputElement = $(elem).find("input")[0]; var button = $(elem).find('.js-button-selector'); var applyBtn = $(elem).find('.js-save-button'); var returnBtn = $(elem).find('.js-return'); var clearBtn = $(elem).find('.js-clear-button'); returnBtn.click(function (e) { closeAndSaveTimePicker(); }); applyBtn.click(function (e) { closeAndSaveTimePicker(); }); clearBtn.click(function (e) { if (scope.allowClear) { closeAndClearTimePicker(); } }); var formatTime = function (date) { var hour = date.getHours().toString(); if (hour.length == 1) hour = "0" + hour; var minute = date.getMinutes().toString(); if (minute.length == 1) minute = "0" + minute; var timeFormatted = hour + ":" + minute; return timeFormatted; }; var closeAndSaveTimePicker = function () { $(inputElement).closeTimepicker(); container.addClass('hidden'); scope.isOpen = false; var newTime; if (scope.time !== undefined && angular.isDate(scope.time)) newTime = new Date(scope.time.getTime()); else newTime = new Date(); var timeSplitted = inputElement.value.split(':'); newTime.setHours(parseInt(timeSplitted[0]), parseInt(timeSplitted[1])); scope.time = newTime; scope.$apply(); }; var closeAndClearTimePicker = function () { $(inputElement).closeTimepicker(); container.addClass('hidden'); scope.isOpen = false; scope.time = null; scope.timeFormatted = null; scope.$apply(); } var openTimePicker = function () { if (!scope.disabled) { $(inputElement).timepicker({ minuteStep: parseInt(scope.stepMinutes), headerText: scope.headerTitle, applyText: scope.applymsg }); if (scope.alone == true) { var newTime; if (scope.time !== undefined && angular.isDate(scope.time)) newTime = new Date(scope.time.getTime()); else newTime = new Date(); scope.time = newTime; scope.$apply(); var timeSplitted = inputElement.value.split(':'); newTime.setHours(parseInt(timeSplitted[0]), parseInt(timeSplitted[1])); scope.$apply(); } $(inputElement).showTimepicker(); scope.$apply(function () { scope.isOpen = true; }); var timepicker = $(elem).find('.timepicker'); var timepickerContainer = $(elem).find('#timepickerContainer'); $(timepicker).appendTo(timepickerContainer); container.removeClass('hidden'); } }; $(document).mouseup(function (e) { if (scope.isOpen) { if (!container.is(e.target) && container.has(e.target).length === 0 && !button.is(e.target) && button.has(e.target).length === 0) { //only trigger event on close if the container was active, then, hidden it e.preventDefault(); closeAndSaveTimePicker(); } } }); $(document).ready(function (e) { button.click(function (e) { if (!scope.isOpen) { openTimePicker(); } else { closeAndSaveTimePicker(); } }); }); scope.$watch('time', function (newVal, oldVal) { if (angular.isDate(newVal)) { normalizeMinutes(); var timeFormatted = formatTime(newVal); if (timeFormatted !== scope.timeFormatted) scope.timeFormatted = timeFormatted; } else { scope.timeFormatted = null; } }); }, controller: function ($scope) { }, templateUrl: '/Scripts/app/Modules/stTimepickerTemplate.html' }; }); ngSharedDirectives.directive('stAlerts', function ($timeout, $compile) { var count = 0; return { replace: false, scope: { stAlerts: '=' }, link: function (scope, element, attrs, ctrls) { var elem = element; scope.internalControl = scope.stAlerts || {} scope.internalControl.showSuccess = function (msg) { alertstyle = "alerts alerts-style-success"; addAlert(msg, alertstyle); }; scope.internalControl.showWarning = function (msg) { alertstyle = "alerts alerts-style-warning"; addAlert(msg, alertstyle); }; scope.internalControl.showError = function (msg) { alertstyle = "alerts alerts-style-error"; addAlert(msg, alertstyle); }; function addAlert(msg, alertstyle) { var id = "alert" + (++count); elem.append($compile('
' + msg + '
')(scope)); var elemdiv = elem.children().last(); $timeout(function () { deleteAlert(elemdiv); }, 5000); }; function deleteAlert(elemdiv) { elemdiv.remove(); }; scope.deleteFromClick = function (e) { deleteAlert(e.target.parentElement); }; }, }; }); ngSharedDirectives.directive('stSlider', ['$parse', function ($parse) { return { replace: true, require: ['ngModel'], template: '
', link: function (scope, element, attrs, ctrls) { var ctrl = ctrls[0]; var title = "Slider"; var key = null; var val = null; var compareField = null; var initIsDefined = false; var parentForm = element.parent().controller('form'); var modelGetter = $parse(attrs['ngModel']); // This returns a function that lets us set the value of the ng-model binding expression: var modelSetter = modelGetter.assign; if (angular.isDefined(attrs.stSliderTitle)) { var auxt = scope.$eval(attrs.stSliderTitle); if (auxt === undefined) title = attrs.stSliderTitle; else title = auxt; } if (angular.isDefined(attrs.stSliderValue)) { var auxv = scope.$eval(attrs.stSliderValue); if (auxv === undefined) val = attrs.stSliderValue; else val = auxv; } if (angular.isDefined(attrs.stSliderCompareField)) { var auxc = attrs.stSliderCompareField; if (auxc === undefined) compareField = attrs.stCompareField; else compareField = auxc; } if (angular.isDefined(attrs.stSliderKey)) { var auxk = scope.$eval(attrs.stSliderKey); if (auxk === undefined) key = attrs.stSliderKey; else key = auxk; } var auxs = scope.$eval(attrs.stSliderSource); if (auxs === undefined) src = attrs.stSliderSource; else src = auxs; scope.$watch(attrs.stSliderSource, function (newValue, oldValue) { if (newValue != oldValue) { src = newValue; var parsed = parseItems(newValue); element.stslider('setSource', parsed); if (pendingToSet != null) { var aux = pendingToSet; pendingToSet = null; setValueInSlider(aux); } } }); var parseItems = function (src) { var itemsval = []; if (val != null) { var tempval = ""; for (var i = 0; i < src.length; i++) { tempval = src[i][val]; itemsval.push(tempval); } } else { itemsval = src; } return itemsval; } element.stslider({ source: parseItems(src), messages: { Title: title, }, change: function (event, ui) { if (isUpdating) return; var getval = element.stslider('getValue'); var newviewval = ""; if (val != null && key != null) { for (var i = 0; i < src.length; i++) { if (getval == src[i][val]) { newviewval = src[i][key]; break; } } } else if (val != null && key == null) { for (var i = 0; i < src.length; i++) { if (getval == src[i][val]) { newviewval = src[i]; break; } } } else { newviewval = getval; } ctrl.$setViewValue(newviewval); ctrl.$setTouched(); } }); var pendingToSet = null; var isUpdating = false; ctrl.$formatters.unshift(function (keyvalue) { if (src.length == 0 && keyvalue != undefined) pendingToSet = keyvalue; else { setValueInSlider(keyvalue); } }); var setValueInSlider = function (keyvalue) { var newvalue = 0; var isPristine = true; if (ctrl.$dirty === true) isPristine = false; if (compareField != null) { for (var i = 0; i < src.length; i++) { if (keyvalue[compareField] == src[i][compareField]) { newvalue = src[i][val]; break; } } } else if (key != null) { for (var i = 0; i < src.length; i++) { if (keyvalue == src[i][key]) { newvalue = src[i][val]; break; } } } else { newvalue = keyvalue; } isUpdating = true; element.stslider('setValue', newvalue); isUpdating = false; if (isPristine === true) { ctrl.$setPristine(); ctrl.$setUntouched(); parentForm.$setPristine(); } }; } }; }]); //TODO: Revisar ngSharedDirectives.directive('stPhone', [function () { return { require: ['ngModel'], restrict: 'A', scope: { 'ngModel': '=', 'stPhoneCode': '=', 'stCountryCode': '=', }, link: function (scope, elem, attrs, ctrls) { var opened; var myCtrls = ctrls[0]; var initialCtry = ''; var settingCode = false; if (angular.isDefined(attrs.stPhoneCode)) { var auxv = scope.$eval(attrs.stPhoneCode); if (auxv === undefined) initialCtry = scope.stPhoneCode; else initialCtry = auxv; } $(document).ready(function () { //si ya está cargado eliminamos el anterior porque no funciona muy bien el plugin //de js cuando hay más de uno en pantalla y cuando se clona. if (elem.attr('yetloaded') == 'true') { elem.parent().find('.flag-container').remove(); elem.unwrap(); } if (initialCtry == undefined || initialCtry == null || initialCtry == '') { elem.intlTelInput({ initialCountry: '' }); } else { elem.intlTelInput({ initialCountry: initialCtry }); } scope.stPhoneCode = elem.intlTelInput("getSelectedCountryData").dialCode; initialCtry = scope.stPhoneCode; //inicialización por defecto if (elem.attr('yetloaded') == undefined) elem.attr('yetloaded', 'true'); }); elem.on('countrychange', function (e, countryData) { if (!settingCode) { scope.$apply(function () { settingCode = true; scope.stPhoneCode = countryData.dialCode; scope.stCountryCode = countryData.iso2; }); settingCode = false; //elem.focus(); //elem.blur(); } }); elem.on('dropDown', function (e) { if (!opened) { scope.$apply(function () { scope.ngModel = null; }); } opened = !opened; }); scope.$watch('stPhoneCode', function (newVal, oldVal) { if (newVal != oldVal && !settingCode) { //Si no está definido establecer el valor por defecto settingCode = true; if (newVal == undefined || newVal == null || newVal == '') { elem.intlTelInput("setCountry", initialCtry); } else { elem.intlTelInput("setCountry", newVal); } settingCode = false; scope.stPhoneCode = elem.intlTelInput("getSelectedCountryData").dialCode; scope.stCountryCode = elem.intlTelInput("getSelectedCountryData").iso2; } }); myCtrls.$parsers.unshift(function (inputValue) { if (!opened) { return inputValue; } else { myCtrls.$setViewValue(null); return null; } }); } } } ]); ngSharedDirectives.directive('stCurrencySelector', ['$cookies', function ($cookies) { return { require: ['ngModel'], link: function (scope, element, attrs, ctrls) { var modelCtrl = ctrls[0]; var key = null; if (angular.isDefined(attrs.stCurrencySelectorKey)) { var auxv = scope.$eval(attrs.stCurrencySelectorKey); if (auxv === undefined) key = attrs.stCurrencySelectorKey; else key = auxv; } var val = null; if (angular.isDefined(attrs.stCurrencySelectorValue)) { var auxw = scope.$eval(attrs.stCurrencySelectorValue); if (auxw === undefined) val = attrs.stCurrencySelectorValue; else val = auxw; } var imp = null; if (angular.isDefined(attrs.stCurrencySelectorImportance)) { var auxt = scope.$eval(attrs.stCurrencySelectorImportance); if (auxt === undefined) imp = attrs.stCurrencySelectorImportance; else imp = auxt; } var cha = null; if (angular.isDefined(attrs.stCurrencySelectorChange)) { var auxy = scope.$eval(attrs.stCurrencySelectorChange); if (auxy === undefined) cha = attrs.stCurrencySelectorChange; else cha = auxy; } var opt1 = null; if (angular.isDefined(attrs.stCurrencySelectorOptions1)) { opt1 = attrs.stCurrencySelectorOptions1; } var opt2 = null; if (angular.isDefined(attrs.stCurrencySelectorOptions2)) { opt2 = attrs.stCurrencySelectorOptions2; } var nam = null; if (angular.isDefined(attrs.stCurrencySelectorName)) { var aux3 = scope.$eval(attrs.stCurrencySelectorName); if (aux3 === undefined) nam = attrs.stCurrencySelectorName; else nam = aux3; } var src = null; var auxs = scope.$eval(attrs.stCurrencySelectorSource); if (auxs === undefined) src = attrs.stCurrencySelectorSource; else src = auxs; var mainCurrenciesLabel = opt1; element.append($('')); $.each(src, function (index, object) { if (object[imp]) { element.append($('')); $.each(src, function (index, object) { if (!object[imp]) { element.append($('')); modelCtrl.$parsers.unshift(function (inputValue) { if (!imp) { return inputValue; } else { for (var i = 0; i < src.length; i++) { if (src[i][key] == inputValue) { return src[i]; } } } }); modelCtrl.$formatters.unshift(function (keyvalue) { var newvalue = null; if (key != null) //Con campo key { if (keyvalue != undefined) { for (var i = 0; i < src.length; i++) { if (keyvalue[key] == src[i][key]) { newvalue = src[i][key]; } } } } else { newvalue = keyvalue; } modelCtrl.$setViewValue(newvalue); element.val(newvalue); return newvalue; }); }, }; }]); ngSharedDirectives.directive('ngEsc', function ($document) { return { restrict: 'A', link: function (scope, element, attr) { var handler = function (evt) { var which = evt.which; if (which == 27) { scope.$apply(attr.ngEsc); evt.preventDefault(); evt.stopPropagation(); } }; //var cleanup = function () { // $document.off('keydown keypress keyup', handler); //}; $document.on('keydown keypress keyup', handler); //element.on('$destroy', cleanup); } } }); ngSharedDirectives.value('THROTTLE_MILLISECONDS', null); ngSharedDirectives.directive('infiniteScroll', [ '$rootScope', '$window', '$timeout', 'THROTTLE_MILLISECONDS', function ($rootScope, $window, $timeout, THROTTLE_MILLISECONDS) { return { scope: { infiniteScroll: '&', infiniteScrollContainer: '=', infiniteScrollDistance: '=', infiniteScrollDisabled: '=' }, link: function (scope, elem, attrs) { var changeContainer, checkWhenEnabled, container, handleInfiniteScrollContainer, handleInfiniteScrollDisabled, handleInfiniteScrollDistance, handler, immediateCheck, scrollDistance, scrollEnabled, throttle; $window = angular.element($window); scrollDistance = null; scrollEnabled = null; checkWhenEnabled = null; container = null; immediateCheck = true; handler = function () { var containerBottom, elementBottom, remaining, shouldScroll; if (container === $window) { containerBottom = container.height() + container.scrollTop(); elementBottom = elem.offset().top + elem.height(); } else { containerBottom = container.height(); elementBottom = elem.offset().top - container.offset().top + elem.height(); } remaining = elementBottom - containerBottom; shouldScroll = remaining <= container.height() * scrollDistance + 1; if (shouldScroll && scrollEnabled) { return scope.infiniteScroll(); } else if (shouldScroll) { return checkWhenEnabled = true; } }; throttle = function (func, wait) { var later, previous, timeout; timeout = null; previous = 0; later = function () { var context; previous = new Date().getTime(); $timeout.cancel(timeout); timeout = null; func.call(); return context = null; }; return function () { var now, remaining; now = new Date().getTime(); remaining = wait - (now - previous); if (remaining <= 0) { clearTimeout(timeout); $timeout.cancel(timeout); timeout = null; previous = now; return func.call(); } else { if (!timeout) { return timeout = $timeout(later, remaining); } } }; }; if (THROTTLE_MILLISECONDS != null) { handler = throttle(handler, THROTTLE_MILLISECONDS); } scope.$on('$destroy', function () { return container.off('scroll', handler); }); handleInfiniteScrollDistance = function (v) { return scrollDistance = parseInt(v, 10) || 0; }; scope.$watch('infiniteScrollDistance', handleInfiniteScrollDistance); handleInfiniteScrollDistance(scope.infiniteScrollDistance); handleInfiniteScrollDisabled = function (v) { scrollEnabled = !v; if (scrollEnabled && checkWhenEnabled) { checkWhenEnabled = false; return handler(); } }; scope.$watch('infiniteScrollDisabled', handleInfiniteScrollDisabled); handleInfiniteScrollDisabled(scope.infiniteScrollDisabled); changeContainer = function (newContainer) { if (container != null) { container.off('scroll', handler); } container = newContainer; if (newContainer != null) { return container.on('scroll', handler); } }; changeContainer($window); handleInfiniteScrollContainer = function (newContainer) { if ((!(newContainer != null)) || newContainer.length === 0) { return; } newContainer = angular.element(newContainer); if (newContainer != null) { return changeContainer(newContainer); } else { throw new Exception("invalid infinite-scroll-container attribute."); } }; scope.$watch('infiniteScrollContainer', handleInfiniteScrollContainer); handleInfiniteScrollContainer(scope.infiniteScrollContainer || []); if (attrs.infiniteScrollParent != null) { changeContainer(angular.element(elem.parent())); } if (attrs.infiniteScrollImmediateCheck != null) { immediateCheck = scope.$eval(attrs.infiniteScrollImmediateCheck); } return $timeout((function () { if (immediateCheck) { return handler(); } }), 0); } }; } ]); ngSharedDirectives.directive('stRadiobuttonSelector', function () { return { restrict: 'E', scope: { model: '=', options: '=', mainoption: '@', firstoption: '@', alternativeoption: '@', optionValue: '=', mainClass: '@', isOpen: '=', disabled: '=', }, link: function (scope, elem, attrs, ctrls) { if (scope.fixed === undefined) scope.fixed = false; if (scope.disabled === undefined) scope.disabled = false; scope.isOpen = false; var button = $(elem[0]).find('.js-button-selector'); var container = $(elem[0]).find('.js-dropdown-container'); var close = $(elem[0]).find('.js-save-button'); $(document).mouseup(function (e) { if (!scope.fixed) { if (!container.is(e.target) && container.has(e.target).length === 0 && !button.is(e.target) && button.has(e.target).length === 0) { //only trigger event on close if the container was active, then, hidden it if (!container.hasClass('hidden')) { e.preventDefault(); close.trigger('click'); } } } }); $(document).ready(function (e) { button.click(function (e) { if (container.hasClass('hidden')) { if (!scope.disabled) { container.removeClass('hidden'); scope.$apply(function () { scope.isOpen = true; }); } } else { e.preventDefault(); close.trigger('click'); } }) close.click(function (e) { container.addClass('hidden'); scope.$apply(function () { scope.isOpen = false; }); }); }); /*TODO:Esto debe ser generico*/ scope.$watch('optionValue', function (newVal, oldVal) { if (newVal != undefined && newVal != null) { if (newVal) { scope.selectedOption = 'option2'; scope.mainoption = scope.alternativeoption; } else { scope.selectedOption = 'option1'; scope.mainoption = scope.firstoption; } } else { scope.mainoption = null; scope.selectedOption = null; } }); scope.$watch('selectedOption', function (newVal, oldVal) { if (newVal != oldVal) { if (newVal == 'option2') { scope.mainoption = scope.alternativeoption; scope.optionValue = true; } else if (newVal == 'option1') { scope.mainoption = scope.firstoption; scope.optionValue = false; } container.addClass('hidden'); scope.isOpen = false; } }); }, templateUrl: '/Scripts/app/Modules/stRadiobuttonSelector.html' }; }); window.requestAnimFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })(); ngSharedDirectives.directive('keyEnter', function () { return function (scope, element, attrs) { element.bind("keydown keypress", function (event) { if (event.which === 13) { scope.$apply(function () { scope.$eval(attrs.keyEnter, { 'event': event }); }); event.preventDefault(); } }); }; }); ngSharedDirectives.directive('starRating', [function () { return { restrict: 'A', replace: true, scope: { starRating: '=' }, link: function (scope, element, attrs) { scope.$watch("starRating", function (newValue, oldValue) { var val = parseFloat(newValue); var size = Math.max(0, (Math.min(5, val))) * 16; element[0].style.width = size + 'px'; element[0].style.backgroundPositionY = '0px'; }); } } }]); ngSharedDirectives.directive('bsTooltip', function () { return { link: function (scope, element, attrs) { $(element).hover(function () { // on mouseenter $(element).tooltip('show'); }, function () { // on mouseleave $(element).tooltip('hide'); }); } }; }); ngSharedDirectives.directive('labelField', function () { return { restrict: 'AE', link: function ($scope, element, attrs) { $scope.$watch(attrs['ngModel'], function (newValue) { if (newValue && typeof newValue[attrs['labelField']] != 'undefined') { element[0].value = newValue[attrs['labelField']]; } }) } } }); ngSharedDirectives.directive('bindHtmlCompile', ['$compile', function ($compile) { return { restrict: 'A', link: function (scope, element, attrs) { scope.$watch(function () { return scope.$eval(attrs.bindHtmlCompile); }, function (value) { // In case value is a TrustedValueHolderType, sometimes it // needs to be explicitly called into a string in order to // get the HTML string. element.html(value && value.toString()); // If scope is provided use it, otherwise use parent scope var compileScope = scope; if (attrs.bindHtmlScope) { compileScope = scope.$eval(attrs.bindHtmlScope); } $compile(element.contents())(compileScope); }); } }; }]); ngSharedDirectives.factory('Cards', [function () { var defaultFormat = /(\d{1,4})/g; var defaultInputFormat = /(?:^|\s)(\d{4})$/; var cards = [ { type: 'maestro', pattern: /^(5018|5020|5038|6304|6759|676[1-3])/, format: defaultFormat, inputFormat: defaultInputFormat, length: [12, 13, 14, 15, 16, 17, 18, 19], cvcLength: [3], luhn: true }, { type: 'dinersclub', pattern: /^(36|38|30[0-5])/, format: defaultFormat, inputFormat: defaultInputFormat, length: [14], cvcLength: [3], luhn: true }, { type: 'laser', pattern: /^(6706|6771|6709)/, format: defaultFormat, inputFormat: defaultInputFormat, length: [16, 17, 18, 19], cvcLength: [3], luhn: true }, { type: 'jcb', pattern: /^35/, format: defaultFormat, inputFormat: defaultInputFormat, length: [16], cvcLength: [3], luhn: true }, { type: 'unionpay', pattern: /^62/, format: defaultFormat, inputFormat: defaultInputFormat, length: [16, 17, 18, 19], cvcLength: [3], luhn: false }, { type: 'discover', pattern: /^(6011|65|64[4-9]|622)/, format: defaultFormat, inputFormat: defaultInputFormat, length: [16], cvcLength: [3], luhn: true }, { type: 'mastercard', pattern: /^5[1-5]/, format: defaultFormat, inputFormat: defaultInputFormat, length: [16], cvcLength: [3], luhn: true }, { type: 'amex', pattern: /^3[47]/, format: /(\d{1,4})(\d{1,6})?(\d{1,5})?/, inputFormat: /^(\d{4}|\d{4}\s\d{6})$/, length: [15], cvcLength: [3, 4], luhn: true }, { type: 'visa', pattern: /^4/, format: defaultFormat, inputFormat: defaultInputFormat, length: [13, 14, 15, 16], cvcLength: [3], luhn: true } ]; var _fromNumber = function (num) { var card, i, len; num = (num + '').replace(/\D/g, ''); for (i = 0, len = cards.length; i < len; i++) { card = cards[i]; if (card.pattern.test(num)) { return card; } } } var _fromType = function (type) { var card, i, len; for (i = 0, len = cards.length; i < len; i++) { card = cards[i]; if (card.type === type) { return card; } } }; return { fromNumber: function (val) { return _fromNumber(val) }, fromType: function (val) { return _fromType(val) }, defaultFormat: function () { return defaultFormat; }, defaultInputFormat: function () { return defaultInputFormat; } } }]) .factory('_Format', ['Cards', '$filter', function (Cards, $filter) { var _formats = {} var _hasTextSelected = function ($target) { var ref; if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== $target.prop('selectionEnd')) { return true; } if (typeof document !== "undefined" && document !== null ? (ref = document.selection) != null ? typeof ref.createRange === "function" ? ref.createRange().text : void 0 : void 0 : void 0) { return true; } return false; }; // card formatting var _formatCardNumber = function (e) { var $target, card, digit, length, re, upperLength, value; digit = String.fromCharCode(e.which); $target = angular.element(e.currentTarget); value = $target.val(); card = Cards.fromNumber(value + digit); length = (value.replace(/\D/g, '') + digit).length; upperLength = 16; if (card) { upperLength = card.length[card.length.length - 1]; } if (length >= upperLength) { return; } if (!/^\d+$/.test(digit) && !e.meta && e.keyCode >= 46) { e.preventDefault(); return; } if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) { return; } re = Cards.defaultInputFormat(); if (card) { re = card.inputFormat; } if (re.test(value)) { e.preventDefault(); return $target.val(value + ' ' + digit); } else if (re.test(value + digit)) { e.preventDefault(); return $target.val(value + digit + ' '); } }; var _restrictCardNumber = function (e) { var $target, card, digit, value; $target = angular.element(e.currentTarget); digit = String.fromCharCode(e.which); if (!/^\d+$/.test(digit)) { return; } if (_hasTextSelected($target)) { return; } value = ($target.val() + digit).replace(/\D/g, ''); card = Cards.fromNumber(value); if (card) { if (!(value.length <= card.length[card.length.length - 1])) { e.preventDefault(); } } else { if (!(value.length <= 16)) { e.preventDefault(); } } }; var _formatBackCardNumber = function (e) { var $target, value; $target = angular.element(e.currentTarget); value = $target.val(); if (e.meta) { return; } if (e.which !== 8) { return; } if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) { return; } if (/\d\s$/.test(value) && !e.meta && e.keyCode >= 46) { e.preventDefault(); return $target.val(value.replace(/\d\s$/, '')); } else if (/\s\d?$/.test(value)) { e.preventDefault(); return $target.val(value.replace(/\s\d?$/, '')); } }; var _getFormattedCardNumber = function (num) { var card, groups, upperLength, ref; card = Cards.fromNumber(num); if (!card) { return num; } upperLength = card.length[card.length.length - 1]; num = num.replace(/\D/g, ''); num = num.slice(0, +upperLength + 1 || 9e9); if (card.format.global) { return (ref = num.match(card.format)) != null ? ref.join(' ') : void 0; } else { groups = card.format.exec(num); if (groups != null) { groups.shift(); } return groups != null ? groups.join(' ') : void 0; } }; var _reFormatCardNumber = function (e) { return setTimeout(function () { var $target, value; $target = angular.element(e.target); value = $target.val(); value = _getFormattedCardNumber(value); return $target.val(value); }); }; var _parseCardNumber = function (value) { return value != null ? value.replace(/\s/g, '') : value; }; _formats['card'] = function (elem, ctrl) { elem.bind('keypress', _restrictCardNumber); elem.bind('keypress', _formatCardNumber); elem.bind('keydown', _formatBackCardNumber); elem.bind('paste', _reFormatCardNumber); ctrl.$parsers.push(_parseCardNumber); ctrl.$formatters.push(_getFormattedCardNumber); } // cvc _formatCVC = function (e) { $target = angular.element(e.currentTarget); digit = String.fromCharCode(e.which); if (!/^\d+$/.test(digit) && !e.meta && e.keyCode >= 46) { e.preventDefault(); return; } val = $target.val() + digit; if (val.length <= 4) { return; } else { e.preventDefault(); return; } } _formats['cvc'] = function (elem) { elem.bind('keypress', _formatCVC) } return function (type, elem, ctrl) { if (!_formats[type]) { types = Object.keys(_formats); errstr = 'Unknown type for formatting: "' + type + '". '; errstr += 'Should be one of: "' + types.join('", "') + '"'; throw errstr; } return _formats[type](elem, ctrl); } }]) .directive('paymentsFormat', ['$window', '_Format', function ($window, _Format) { return { restrict: 'A', require: 'ngModel', link: function (scope, elem, attr, ctrl) { _Format(attr.paymentsFormat, elem, ctrl); } } }]) .factory('_Validate', ['Cards', '$parse', function (Cards, $parse) { var __indexOf = [].indexOf || function (item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; } var _luhnCheck = function (num) { var digit, digits, odd, sum, i, len; odd = true; sum = 0; digits = (num + '').split('').reverse(); for (i = 0, len = digits.length; i < len; i++) { digit = digits[i]; digit = parseInt(digit, 10); if ((odd = !odd)) { digit *= 2; } if (digit > 9) { digit -= 9; } sum += digit; } return sum % 10 === 0; }; var _validators = {} _validators['cvc'] = function (cvc, ctrl, scope, attr) { var ref, ref1; // valid if empty - let ng-required handle empty if (cvc == null || cvc.length == 0) return true; if (!/^\d+$/.test(cvc)) { return false; } var type; if (attr.paymentsTypeModel) { var typeModel = $parse(attr.paymentsTypeModel); type = typeModel(scope); } if (type) { return ref = cvc.length, __indexOf.call((ref1 = Cards.fromType(type)) != null ? ref1.cvcLength : void 0, ref) >= 0; } else { return cvc.length >= 3 && cvc.length <= 4; } } _validators['card'] = function (num, ctrl, scope, attr) { var card, ref, typeModel; if (attr.paymentsTypeModel) { typeModel = $parse(attr.paymentsTypeModel); } var clearCard = function () { if (typeModel) { typeModel.assign(scope, null); } ctrl.$card = null; }; // valid if empty - let ng-required handle empty if (num == null || num.length == 0) { clearCard(); return true; } num = (num + '').replace(/\s+|-/g, ''); if (!/^\d+$/.test(num)) { clearCard(); return false; } card = Cards.fromNumber(num); if (!card) { clearCard(); return false; } ctrl.$card = angular.copy(card); if (typeModel) { typeModel.assign(scope, card.type); } ret = (ref = num.length, __indexOf.call(card.length, ref) >= 0) && (card.luhn === false || _luhnCheck(num)); return ret; } return function (type, val, ctrl, scope, attr) { if (!_validators[type]) { types = Object.keys(_validators); errstr = 'Unknown type for validation: "' + type + '". '; errstr += 'Should be one of: "' + types.join('", "') + '"'; throw errstr; } return _validators[type](val, ctrl, scope, attr); } }]) .factory('_ValidateWatch', ['_Validate', function (_Validate) { var _validatorWatches = {} _validatorWatches['cvc'] = function (type, ctrl, scope, attr) { if (attr.paymentsTypeModel) { scope.$watch(attr.paymentsTypeModel, function (newVal, oldVal) { if (newVal != oldVal) { var valid = _Validate(type, ctrl.$modelValue, ctrl, scope, attr); ctrl.$setValidity(type, valid); } }); } } return function (type, ctrl, scope, attr) { if (_validatorWatches[type]) { return _validatorWatches[type](type, ctrl, scope, attr); } } }]) .directive('paymentsValidate', ['$window', '_Validate', '_ValidateWatch', function ($window, _Validate, _ValidateWatch) { return { restrict: 'A', require: 'ngModel', link: function (scope, elem, attr, ctrl) { var type = attr.paymentsValidate; _ValidateWatch(type, ctrl, scope, attr); var validateFn = function (val) { var valid = _Validate(type, val, ctrl, scope, attr); ctrl.$setValidity(type, valid); return valid ? val : undefined; }; ctrl.$formatters.push(validateFn); ctrl.$parsers.push(validateFn); } } }]); ngSharedDirectives.directive('stPeopleSelector', function (shApi) { return { restrict: 'E', scope: { 'people': '=', 'maxpeople': '=', 'adults': '=', 'childrenundertwelve': '=', 'childrenunderfive': '=', 'showoptions': '=', 'inputname': '@', 'inputmsg': '@', 'personmsg': '@', 'peoplemsg': '@', 'adultmsg': '@', 'adultsmsg': '@', 'childmsg': '@', 'childrenmsg': '@', 'babymsg': '@', 'babiesmsg': '@', 'infantmsg': "@", 'infantsmsg': "@", 'underfivemsg': '@', 'undertwelvemsg': '@', 'closemsg': '@', 'callbackOnClose': '=', 'mainClass': '@', 'isOpen': '=', 'allowrestablish': '=', 'originalpeople': '=', 'originaladults': '=', 'originalundertwelve': '=', 'originalunderfive': '=', 'unselectmsg': '@', 'disabled': '=', 'childseatalert': '=', 'childseatmessage': '@', 'filteralone': '=', 'required': '=', 'indicatepeople': '@', 'noadulterror': '@', 'isfirstsearch': '=', 'isclosed': '=', }, link: function (scope, elem, attrs, ctrls) { scope.isOpen = false; if (scope.disabled === undefined) scope.disabled = false; var close = $(elem[0]).find('.js-save-button'); var button = $(elem[0]).find('.js-button-selector'); var container = $(elem[0]).find('.js-dropdown-container'); var returnbutton = $(elem[0]).find('.js-return'); if (scope.filteralone == undefined || scope.filteralone == false) { $(document).mouseup(function (e) { if (!container.is(e.target) && container.has(e.target).length === 0 && !button.is(e.target) && button.has(e.target).length === 0) { //only trigger event on close if the container was active, then, hidden it if (!container.hasClass('hidden')) { e.preventDefault(); close.trigger('click'); } } }); $(document).ready(function (e) { close.click(function (e) { container.addClass('hidden'); scope.isOpen = false; shApi.fullWindow(false); //callback execution to notify the directives that contain this directive if (scope.callbackOnClose != undefined && scope.checkForChanges()) scope.callbackOnClose(); }); button.click(function (e) { if (container.hasClass('hidden')) { if (!scope.disabled) { container.removeClass('hidden'); scope.$apply(function () { scope.isOpen = true; }); shApi.fullWindow(true); //init default: scope.initialValues(); } } else { //e.preventDefault(); close.trigger('click'); } }); returnbutton.click(function (e) { scope.return(); close.trigger('click'); }); }) } }, controller: function ($scope) { $scope.initialValues = function () { $scope.initAdults = $scope.adults; $scope.initChildrenUnderFive = $scope.originalunderfive; $scope.initChildrenUnderTwelve = $scope.originalundertwelve; $scope.initPeople = $scope.people; } //Restablish to before open step (reutrn button mobile only) $scope.return = function () { $scope.adults = $scope.initAdults; $scope.childrenunderfive = $scope.initChildrenUnderFive; $scope.childrenundertwelve = $scope.initChildrenUnderTwelve; $scope.people = $scope.initPeople; } //Restablish to 0 step $scope.restablish = function () { if ($scope.allowrestablish) { $scope.adults = $scope.originaladults; $scope.childrenunderfive = $scope.originalunderfive; $scope.childrenundertwelve = $scope.originalundertwelve; $scope.people = $scope.originalpeople; } } $scope.checkForChanges = function () { var changes = false; if ($scope.adults !== $scope.initAdults || $scope.childrenunderfive !== $scope.initChildrenUnderFive || $scope.childrenundertwelve !== $scope.initChildrenUnderTwelve) changes = true; return changes; } $scope.addPeople = function (adult, undertwelve, underfive) { if (adult != 0) { $scope.adults += adult; $scope.people += adult; } if (undertwelve != 0) { $scope.childrenundertwelve += undertwelve; $scope.people += undertwelve; } if (underfive != 0) { $scope.childrenunderfive += underfive; $scope.people += underfive; } } $scope.showHideDropdown = function () { $scope.isclosed = !$scope.isclosed; } }, templateUrl: '/Scripts/app/Modules/stPeopleSelectorTemplate.html' } }); ngSharedDirectives.directive('stDoubleSelector', function (Translator, shApi) { var mandatoryData = false; return { restrict: 'E', scope: { 'className': '@', 'mainClass': '@', 'mainItem': '=', 'mainItemKey': '=', 'mainItemName': '@', 'mainOptionList': '=', 'numItemsKey': '=', 'allowSecond': '=', 'secondItem': '=', 'secondItemKey': '=', 'secondItemName': '@', 'secondOptionList': '=', 'actionTrigger': '=', 'firstItemTitle': '@', 'secondItemTitle': '@', 'defaultMsg': '@', 'icon': '@', 'isOpen': '=', 'dropdownAfterElementId': '@', 'leftIcon': '@', }, link: function (scope, elem, attrs, ctrls) { scope.isOpen = false; if (attrs.mandatoryData !== undefined) mandatoryData = (attrs.mandatoryData === 'true' || attrs.mandatoryData === ''); //load invariant translations scope.acceptMsg = Translator.getTranslation('apply_text'); scope.closeMsg = Translator.getTranslation('cancel_text'); var close = $(elem[0]).find('.js-cancel-button'); var save = $(elem[0]).find('.js-save-button'); var button = $(elem[0]).find('.js-button-selector'); var container = $(elem[0]).find('.js-dropdown-container'); var returnbutton = $(elem[0]).find('.js-return'); if (scope.dropdownAfterElementId !== undefined) { container = container.insertAfter($("#" + scope.dropdownAfterElementId)); } $(document).mouseup(function (e) { if (!container.is(e.target) && container.has(e.target).length === 0 && !button.is(e.target) && button.has(e.target).length === 0) { //only trigger event on close if the container was active, then, hidden it if (!container.hasClass('hidden')) { e.preventDefault(); close.trigger('click'); } } }); $(document).ready(function (e) { // Safari 3.0+ "[object HTMLElementConstructor]" var isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification)); save.click(function (e) { container.addClass('hidden'); $("#modalcontent").removeClass("modal-responsive"); if (isSafari == true) { $(".base-selector__footer-mobile").removeClass("base-selector__footer-mobile--safari"); } $("html").removeClass("fix-footer"); $("html").removeClass("fix-footer__html"); $("body").removeClass("fix-footer__body"); scope.isOpen = false; shApi.fullWindow(false); }); close.click(function (e) { container.addClass('hidden'); $("#modalcontent").removeClass("modal-responsive"); if (isSafari == true) { $(".base-selector__footer-mobile").removeClass("base-selector__footer-mobile--safari"); } $("html").removeClass("fix-footer"); $("html").removeClass("fix-footer__html"); $("body").removeClass("fix-footer__body"); scope.isOpen = false; shApi.fullWindow(false); //callback execution scope.onCancel(); }); button.click(function (e) { if (container.hasClass('hidden')) { container.removeClass('hidden'); $("#modalcontent").addClass("modal-responsive"); $("html").addClass("fix-footer"); $("html").addClass("fix-footer__html"); $("body").addClass("fix-footer__body"); if (isSafari == true) { $(".base-selector__footer-mobile").addClass("base-selector__footer-mobile--safari"); } scope.$apply(function () { scope.isOpen = true; }); shApi.fullWindow(true); //init default: scope.initialValues(); } else { //e.preventDefault(); close.trigger('click'); } }); returnbutton.click(function (e) { close.trigger('click'); }); }) }, controller: function ($scope) { //init when open $scope.initialValues = function () { $scope.isOpen = true; if ($scope.allowSecond) { if ($scope.secondItem != undefined && $scope.secondItem != null) $scope.lastSecondSelectedKeyValue = $scope.secondItem[$scope.secondItemKey]; } //Initialize first list if ($scope.mainOptionList != undefined && $scope.mainOptionList != null) { var finded = null; if ($scope.mainItem != undefined && $scope.mainItem != null) { finded = $.grep($scope.mainOptionList, function (e) { if (e[$scope.mainItemKey] === $scope.mainItem[$scope.mainItemKey]) return e; }); } if (finded != null && finded.length > 0) { $scope.mainItemSelected = finded[0]; $scope.$apply(); } else if (mandatoryData) { $scope.mainItemSelected = $scope.mainOptionList[0]; $scope.$apply(); } } } $scope.$watch('mainItemSelected', function (newVal, oldVal) { if (newVal !== oldVal) { if ($scope.allowSecond && $scope.actionTrigger != undefined && newVal != undefined && newVal != null) { $scope.actionTrigger = 'loadSecondaryValues:' + newVal[$scope.mainItemKey]; } } }); $scope.$watch('secondItemSelected', function (newVal, oldVal) { if (newVal !== oldVal) { if (newVal != undefined && newVal != null) { $scope.lastSecondSelectedKeyValue = newVal[$scope.secondItemKey]; } else $scope.lastSecondSelectedKeyValue = null; } }); $scope.$watch('secondOptionList', function (newValues, oldValues) { if (newValues != oldValues) { if ($scope.allowSecond) { $scope.secondItemSelected = undefined; if ($scope.secondOptionList != undefined && $scope.secondOptionList != null) { var finded = null; if ($scope.lastSecondSelectedKeyValue != null) { finded = $.grep($scope.secondOptionList, function (e) { if (e[$scope.secondItemKey] === $scope.lastSecondSelectedKeyValue) return e; }); } if (finded != null && finded.length > 0) $scope.secondItemSelected = finded[0]; else if (mandatoryData) $scope.secondItemSelected = $scope.secondOptionList[0]; } } } }, true); $scope.checkDisabled = function () { if (($scope.mainItemSelected == undefined || $scope.mainItemSelected == null) || ($scope.allowSecond && ($scope.secondItemSelected == undefined || $scope.secondItemSelected == null))) return true; return false; }; $scope.onCancel = function () { //if ($scope.actionTrigger != undefined) $scope.actionTrigger = 'apply'; //clear internal values closeAndClearInternalValues(); }; $scope.onSave = function () { $scope.mainItem = $scope.mainItemSelected; if ($scope.allowSecond) $scope.secondItem = $scope.secondItemSelected; if ($scope.actionTrigger != undefined) $scope.actionTrigger = 'apply'; closeAndClearInternalValues(); }; function closeAndClearInternalValues() { $scope.mainItemSelected = $scope.secondItemSelected = undefined; $scope.initialMainValue = $scope.initialSecondValue = undefined; $scope.lastSecondSelectedKeyValue = null; $scope.secondOptionList = undefined; $scope.isOpen = false; }; }, templateUrl: '/Scripts/app/Modules/stDoubleSelectorTemplate.html' } }); ngSharedDirectives.directive('stBaggageSelector', function (Translator, shApi) { return { restrict: 'E', scope: { 'totalpackages': '=', 'mediumsuitcase': '=', 'specialbaggage': '=', 'blocksuitcasegroup': '=', 'blockspecificgroup': '=', 'blockmediumsuitcase': '=', 'blocklargesuitcase': '=', 'alternativeoption': '=', 'intromsg': '@', 'specialbaggagedescription': '=', 'suitcaserulemsg': '@', 'specificrulemsg': '@', 'defaultbaggage': '=', 'uptomsg': '@', 'inputmsg': '@', 'mainClass': '@', 'showLabel': '=', 'showIcon': '=', 'closemsg': '@', 'isOpen': '=', 'infomsg': '@', 'titlemsg': '@', 'mediumBaggage': '@', 'mediumBaggageInfo': '@', 'allowrestablish': '=', 'defaulttotalpackages': '=', 'defaultmediumsuitcase': '=', 'unselectmsg': '@', }, link: function (scope, elem, attrs, ctrls) { scope.isOpen = false; var button = $(elem[0]).find('.js-button-selector'); var container = $(elem[0]).find('.js-dropdown-container'); var close = $(elem[0]).find('.js-save-button'); var returnbutton = $(elem[0]).find('.js-return'); $(document).mouseup(function (e) { if (!container.is(e.target) && container.has(e.target).length === 0 && !button.is(e.target) && button.has(e.target).length === 0) { if (!container.hasClass('hidden')) { e.preventDefault(); close.trigger('click'); } } }); $(document).ready(function (e) { close.click(function () { container.addClass('hidden'); scope.isOpen = false; shApi.fullWindow(false); }); button.click(function () { if (container.hasClass('hidden')) { container.removeClass('hidden'); scope.initialValues(); scope.$apply(function () { scope.isOpen = true; }); shApi.fullWindow(true); } else { close.trigger('click'); } }); returnbutton.click(function (e) { scope.return(); close.trigger('click'); }); }) }, controller: function ($scope) { $scope.addBaggage = function (mediumsuitcase) { if (mediumsuitcase != 0) { $scope.mediumsuitcase += mediumsuitcase; $scope.totalpackages += mediumsuitcase; } //Cambia el por defecto if ($scope.defaultbaggage != undefined) $scope.defaultbaggage = false; } $scope.getTranslation = function (str) { return Translator.getTranslation(str); } $scope.initialValues = function () { $scope.inittotalpackages = $scope.totalpackages; $scope.initmediumsuitcase = $scope.mediumsuitcase; } $scope.return = function () { $scope.totalpackages = $scope.inittotalpackages; $scope.mediumsuitcase = $scope.initmediumsuitcase; } $scope.restablish = function () { if ($scope.allowrestablish) { $scope.totalpackages = $scope.defaulttotalpackages; $scope.mediumsuitcase = $scope.defaultmediumsuitcase; } } }, templateUrl: '/Scripts/app/Modules/stBaggageSelectorTemplate.html' } } ); ngSharedDirectives.directive('stRangeSelector', function (shApi) { return { restrict: 'E', scope: { 'inputmsg': '@', 'callbackOnClose': '=', 'minvalue': '=', 'maxvalue': '=', 'stepnum': '=', 'range': '=', 'submsg': '@', 'closemsg': '@', 'units': '@', 'mainClass': '@', 'isOpen': '=', 'disabled': '=', }, link: function (scope, elem, attrs, ctrls) { scope.isOpen = false; if (scope.disabled === undefined) scope.disabled = false; var close = $(elem[0]).find('.js-save-button'); var button = $(elem[0]).find('.js-button-selector'); var container = $(elem[0]).find('.js-dropdown-container'); $(document).mouseup(function (e) { if (!scope.fixed) { if (!container.is(e.target) && container.has(e.target).length === 0 && !button.is(e.target) && button.has(e.target).length === 0) { //only trigger event on close if the container was active, then, hidden it if (!container.hasClass('hidden')) { e.preventDefault(); close.trigger('click'); } } } }); $(document).ready(function (e) { close.click(function (e) { container.addClass('hidden'); scope.isOpen = false; shApi.fullWindow(false); //callback execution to notify the directives that contain this directive if (scope.callbackOnClose != undefined && scope.checkForChanges()) scope.callbackOnClose(); }); button.click(function (e) { if (container.hasClass('hidden')) { if (!scope.disabled) { container.removeClass('hidden'); scope.$apply(function () { scope.isOpen = true; }); shApi.fullWindow(true); scope.initialValues(); } } else { //e.preventDefault(); close.trigger('click'); } }) }) }, controller: function ($scope) { $scope.initialValues = function () { $scope.minInitial = $scope.range[0]; $scope.maxInitial = $scope.range[1]; } $scope.checkForChanges = function () { var changes = false; if ($scope.minInitial !== $scope.range[0] || $scope.maxInitial !== $scope.range[1]) changes = true; return changes; } }, templateUrl: '/Scripts/app/Modules/stRangeSelectorTemplate.html' } }); ngSharedDirectives.directive('inputFocused', function ($timeout, $document) { return { scope: { trigger: '@inputFocused' }, link: function (scope, element) { scope.$watch('trigger', function (value) { if (value.substr(0, 4) === "true") { $timeout(function () { element[0].focus(); element[0].blur(); }); } }); } }; }); ngSharedDirectives.directive('stMultiselectDropdown', function ($filter, shApi) { return { restrict: 'E', scope: { model: '=', selectedmsg: '@', options: '=', titlemsg: '@', defaultmsg: '@', unselectmsg: '@', closemsg: '@', name: '@', topBoolName: '@', callbackOnClose: '=', id: '@', multiselectClass: '@', controlName: '@', isOpen: '=', disabled: '=', mainClass: '@', bookserviceprovider: '@', bookserviceproviderselected: '@', showtags: '=', filteralone: '=', isclosed: '=', filled: '@', }, templateUrl: '/Scripts/app/Modules/stMultiselectDropdown.html', link: function (scope, elem, attrs, ctrls) { if (scope.fixed === undefined) scope.fixed = false; if (scope.disabled === undefined) scope.disabled = false; scope.hasIcon = false; scope.isOpen = false; scope.hasTopOptions = false; if (attrs.iconClass !== undefined) { scope.hasIcon = true; scope.iconClass = attrs.iconClass; } var button = $(elem[0]).find('.js-button-selector'); var container = $(elem[0]).find('.js-dropdown-container'); var close = $(elem[0]).find('.js-save-button'); var returnbutton = $(elem[0]).find('.js-return'); if (scope.filteralone == undefined || scope.filteralone == false) { $(document).mouseup(function (e) { if (!container.is(e.target) && container.has(e.target).length === 0 && !button.is(e.target) && button.has(e.target).length === 0) { //only trigger event on close if the container was active, then, hidden it if (!container.hasClass('hidden')) { e.preventDefault(); close.trigger('click'); } } }); $(document).ready(function (e) { close.click(function (e) { container.addClass('hidden'); scope.isOpen = false; shApi.fullWindow(false); if (scope.callbackOnClose != undefined) scope.callbackOnClose(); }); button.click(function () { if (container.hasClass('hidden')) { if (!scope.disabled) { container.removeClass('hidden'); scope.initialValues(); scope.$apply(function () { scope.isOpen = true; }); shApi.fullWindow(true); } } else { container.addClass('hidden'); close.trigger('click'); } }); returnbutton.click(function (e) { scope.return(); close.trigger('click'); }); scope.init(); }) } else { $(document).ready(function (e) { scope.init(); }) } }, controller: function ($scope) { $scope.init = function () { if ($scope.model == undefined) $scope.model = []; else loadInternalValues($scope.model); } //open $scope.initialValues = function () { $scope.initialList = angular.copy($scope.model); } //only mobile $scope.return = function () { $scope.model = angular.copy($scope.initialList); } //Default use as dictionary $scope.getName = function (option) { if (typeof $scope.name == 'undefined' || $scope.name == null) return option["value"]; else return option[$scope.name]; } $scope.getValue = function (option) { if (typeof $scope.id == 'undefined' || $scope.id == null) return option["key"]; else return option[$scope.id]; } $scope.getIsTopOption = function (option) { var isTopOption = false; if (typeof $scope.topBoolName != 'undefined' && $scope.topBoolName != null) isTopOption = option[$scope.topBoolName]; $scope.hasTopOptions |= isTopOption; return isTopOption; } $scope.getIsFilled = function (option) { if (typeof $scope.filled == 'undefined' || $scope.filled == null) return option["filled"]; else return option[$scope.filled]; } $scope.resumeName = $scope.defaultmsg; $scope.selectAll = function () { $scope.model = []; angular.forEach($scope.options, function (item, index) { $scope.model.push(item); }); }; $scope.deselectAll = function () { $scope.model = []; }; $scope.toggleSelectItem = function (option) { var intIndex = -1; for (var idx in $scope.model) { if ($scope.getValue(option) == $scope.getValue($scope.model[idx])) { intIndex = idx; break; } }; if (intIndex >= 0) { $scope.model.splice(intIndex, 1); } else { $scope.model.push(option); } }; $scope.getChecked = function (option) { var varSelected = ''; for (var idx in $scope.model) { if ($scope.getValue(option) == $scope.getValue($scope.model[idx])) { varSelected = true; break; } } return (varSelected); }; $scope.$watchCollection('model', function (newValue, oldValue) { if (newValue != undefined && newValue != null) { loadInternalValues(newValue); } }); function loadInternalValues(newValue) { if ($scope.filteralone != true) { if (newValue.length > 0) { if (newValue.length == $scope.options.length) { $scope.resumeName = $scope.defaultmsg; } else { if ($scope.selectedmsg != undefined && $scope.selectedmsg != null) { $scope.resumeName = $filter('replace')($scope.selectedmsg, newValue.length); } else { $scope.resumeName = newValue.length; } } } else { $scope.resumeName = $scope.defaultmsg; } } } $scope.showHideDropdown = function () { $scope.isclosed = !$scope.isclosed; } $scope.callApplyTransferFilters = function () { if ($scope.$parent.callApply == false) { $scope.$parent.callApply = true; } else { $scope.$parent.callApply = false; } } } } }); ngSharedDirectives.directive('stSearcherByText', function ($filter, shApi) { return { restrict: 'E', scope: { model: '=', unselectmsg: '@', closemsg: '@', defaultmsg: '@', isOpen: '=', callbackOnClose: '=', disabled: '=', mainClass: '@' }, templateUrl: '/Scripts/app/Modules/stSearcherByText.html', link: function (scope, elem, attrs, ctrls) { scope.isOpen = false; if (scope.fixed === undefined) scope.fixed = false; if (scope.disabled === undefined) scope.disabled = false; scope.hasIcon = false; if (attrs.iconClass !== undefined) { scope.hasIcon = true; scope.iconClass = attrs.iconClass; } scope.initValueOpen(); var button = $(elem[0]).find('.js-button-selector'); var container = $(elem[0]).find('.js-dropdown-container'); var close = $(elem[0]).find('.js-save-button'); var returnbutton = $(elem[0]).find('.js-return'); $(document).mouseup(function (e) { if (!scope.fixed) { if (!container.is(e.target) && container.has(e.target).length === 0 && !button.is(e.target) && button.has(e.target).length === 0) { //only trigger event on close if the container was active, then, hidden it if (!container.hasClass('hidden')) { e.preventDefault(); close.trigger('click'); } } } }); $(document).ready(function (e) { close.click(function (e) { scope.closeClick(); }); button.click(function () { if (container.hasClass('hidden')) { if (!scope.disabled) { scope.initValueOpen(); container.removeClass('hidden'); container.find("input").first().focus(); scope.$apply(function () { scope.isOpen = true; }); shApi.fullWindow(true); } } else { container.addClass('hidden'); close.trigger('click'); } }) returnbutton.click(function (e) { scope.return(); close.trigger('click'); }); }); scope.closeClick = function () { container.addClass('hidden'); scope.isOpen = false; shApi.fullWindow(false); if (scope.callbackOnClose != undefined && scope.checkForChanges()) scope.callbackOnClose(); }; }, controller: function ($scope) { $scope.initValueOpen = function () { $scope.initialValue = $scope.model; } $scope.return = function () { $scope.model = $scope.initialValue; } $scope.checkForChanges = function () { var changes = false; if ($scope.initialValue !== $scope.model) changes = true; return changes; } $scope.$watchCollection('model', function (newValue, oldValue) { $scope.resumeName = $scope.defaultmsg; if (newValue != undefined && newValue != null) { if (newValue.length > 0) { $scope.resumeName = newValue; } } }); $scope.deselectAll = function () { $scope.model = null; }; } } }); ngSharedDirectives.directive('stMultiselectPickup', function ($timeout, Purchases, shApi) { return { restrict: 'AE', scope: { displayAttr: '@', mainClass: '@', inputClass: '@', selectedItems: '=', allItems: '=', readOnly: '=', addItem: '&', culture: '=', pickupplaceholdermsg: '@', pickupsearchmsg: '@', removeItem: '&', titlemsg: '@', }, link: function (scope, elem, attrs) { var button = $(elem[0]).find('.js-button-selector'); var container = $(elem[0]).find('.js-container'); var returnbutton = $(elem[0]).find('.js-return'); $(document).mouseup(function (e) { if (!container.is(e.target) && container.has(e.target).length === 0 && !button.is(e.target) && button.has(e.target).length === 0) { //only trigger event on close if the container was active, then, hidden it if (!container.hasClass('hidden')) { e.preventDefault(); closeSelector(); } } }); $(document).ready(function (e) { button.click(function () { if (container.hasClass('hidden')) { container.removeClass('hidden'); shApi.fullWindow(true); } else { closeSelector(); } }); returnbutton.click(function (e) { closeSelector(); }); }); scope.updateSelectedItems = function (obj) { var selectedObj; for (i = 0; typeof scope.selectedItems !== 'undefined' && i < scope.selectedItems.length; i++) { if (scope.selectedItems[i][scope.displayAttr].toUpperCase() === obj[scope.displayAttr].toUpperCase()) { selectedObj = scope.selectedItems[i]; break; } } if (typeof selectedObj === 'undefined') { scope.addItem({ item: obj }); } else { scope.removeItem({ item: selectedObj }); } closeSelector(); } scope.isItemSelected = function (item) { if (typeof scope.selectedItems === 'undefined') return false; var tmpItem; for (i = 0; i < scope.selectedItems.length; i++) { tmpItem = scope.selectedItems[i]; if (typeof tmpItem !== 'undefined' && typeof tmpItem[scope.displayAttr] !== 'undefined' && typeof item[scope.displayAttr] !== 'undefined' && tmpItem[scope.displayAttr].toUpperCase() === item[scope.displayAttr].toUpperCase()) { return true; } } return false; } scope.commaDelimitedSelected = function () { var list = ""; angular.forEach(scope.selectedItems, function (item, index) { list += item[scope.displayAttr]; if (index < scope.selectedItems.length - 1) list += ', '; }); return list.length ? list : scope.pickupplaceholdermsg; } function closeSelector() { container.addClass('hidden'); shApi.fullWindow(false); } }, templateUrl: '/Scripts/app/Modules/stMultiselectPickup.html' } }); ngSharedDirectives.directive('customTagsinput', function ($timeout, shApi) { var tagsAfter = false;//by default tags appear before the input element (as in the checked examples) var maxTags = 0; //$timeout(function () { console.log('dd'); }, 2000); return { restrict: 'EA', scope: { inputTagText: '@', inputTagId: '@', inputTagDescription: '@', tagsInputArray: '=', placeHolderContent: '@', typeaheadTooltip: '@', typeaheadCall: "=", typeaheadLabel: '@', maxTags: "=", changesTrigger: '=', searchTemplate: '@', selectCallback: '=', filters: '=' }, link: function (scope, element, attrs) { scope.searchDone = false; $('.tagscontainer').click(function () { $(this).find('input[type="text"]').focus(); }); //set configuation attrs //bool tagsAfter to set the position of input elements with regards to tags collection if (attrs.tagsAfter !== undefined) tagsAfter = (attrs.tagsAfter === 'true' || attrs.tagsAfter === ''); scope.iconClass = attrs.iconClass !== undefined ? attrs.iconClass : 'st-search fa-fw font-up-1'; scope.inputId = attrs.inputId !== undefined ? attrs.inputId : 'tags'; if (scope.filters === undefined) scope.filters = false; //draws the new tag element where corresponds to configuration above scope.drawInputTag = function (object) { //we save the id of the object in the name to have an identification of the object in order to remove it, we do it this way to not rely on text just in case in the future //we have two texts that are the same but we also have a different description(could be a tooltip) var htmlSpan = '
'; if (tagsAfter) $(element).find(".div-search-input").append(htmlSpan); else $(element).find(".content-tags").append(htmlSpan); //attaching handler when on click event is fired in a $(element).find("a[name='" + object[scope.inputTagId] + "']").click(function (event) { var controlToFocus = $($(this).closest('.text-addon')).find('.controlToFocus')[0]; var id = $(this)[0].name; scope.removeItem(id); $(this).parent().parent().remove();//remove text tag if (scope.filters) { $('#search-text-tag').val(""); } var aElement = $(this); $timeout(function () { if (controlToFocus) { $(controlToFocus).focus(); } }); }); } scope.clearUnavailableTags = function (newVal, oldVal) { if (newVal == undefined || newVal === null || newVal.length == 0) { $(element).find(".tag").remove(); } else if (newVal != undefined && newVal != null && newVal.length > 0) { var tagKeys = []; $.each(newVal, function (idx, item) { tagKeys.push(item[scope.inputTagId]); }); var existingTags = $(element).find(".content-tags .tag .removeDiv a"); $.each(existingTags, function (idx, item) { if (!shApi.checkIfContains(tagKeys, item.name)) { $(element).find("a[name='" + item.name + "']").parent().parent().remove(); } }); } } scope.readOnly = function (val) { if (val) { $(element).find("input").attr("disabled", true); scope.searchDone = true; } else { $(element).find("input").removeAttr("disabled"); scope.searchDone = false; } $(element).find("input").attr("placeholder", attrs.placeHolderContent); } scope.addTagFromModel = function (object) { //Clear input $(element).find("input").val(''); //Si no existe ya un elemento en la lista var elem = $(element).find("a[name='" + object[scope.inputTagId] + "']"); if (elem.length === 0) scope.drawInputTag(object); } scope.init(); }, controller: function ($scope) { $scope.selectedObject = ""; //vbles initializations $scope.init = function () { if ($scope.tagsInputArray === undefined) $scope.tagsInputArray = []; else loadInternalValues($scope.tagsInputArray); //end initializations } //typeahead method $scope.getArrayData = function (filter) { return $scope.typeaheadCall($scope.selectedObject); } //add new item $scope.newSelectedItem = function () { //check if the object exists var obj = $.grep($scope.tagsInputArray, function (e) { return e[$scope.inputTagId] == $scope.selectedObject[$scope.inputTagId]; }); if (obj.length == 0) { $scope.tagsInputArray.push($scope.selectedObject); $scope.drawInputTag($scope.selectedObject); } $scope.checkMaxTags(); //clean input $scope.resetSelectedObject(); if ($scope.selectCallback != undefined) $scope.selectCallback(); } $scope.removeItem = function (id) { var index = -1; $.each($scope.tagsInputArray, function (idx, item) { if (item[$scope.inputTagId] == id) { index = idx; return false; } }); if (index > -1) $scope.tagsInputArray.splice(index, 1); $scope.checkMaxTags(); } $scope.checkMaxTags = function () { if ($scope.tagsInputArray != undefined && $scope.tagsInputArray != null) { if ($scope.tagsInputArray.length >= $scope.maxTags) { $scope.readOnly(true); } else { $scope.readOnly(false); } } } //clean input after adding $scope.resetSelectedObject = function () { $scope.selectedObject = ""; //var obj = document.getElementById(activitieSearcher); //obj.removeClass('open-mobile'); $('.div-input-v2').removeClass('open-mobile'); } //if language filters is reset from outside controller or service... directive has to reset the DOM $scope.$watch('tagsInputArray', function (newVal, oldVal) { $scope.changesTrigger = "tagsChange"; loadInternalValues(newVal); }, true); function loadInternalValues(newVal, oldVal) { $scope.checkMaxTags(); //clean dom tags.. $scope.clearUnavailableTags(newVal, oldVal); //If the value is reset outside controller if (newVal != undefined && newVal != null && newVal.length > 0) { $.each(newVal, function (idx, elem) { $scope.addTagFromModel(newVal[idx]); }); } } }, templateUrl: "/Scripts/app/Modules/tagsInputTemplate.html" }; }); ngSharedDirectives.filter('startFrom', function () { return function (input, start) { start = +start; //parse to int return input.slice(start); } }); ngSharedDirectives.directive('popoverElem', function () { return { link: function (scope, element, attrs) { element.on('click', function () { element.addClass('trigger'); }); } } }); ngSharedDirectives.directive('popoverClose', function ($timeout) { return { scope: { excludeClass: '@' }, link: function (scope, element, attrs) { var trigger = document.getElementsByClassName('trigger'); function closeTrigger(i) { $timeout(function () { angular.element(trigger[0]).click(); }); } element.on('click', function (event) { var etarget = angular.element(event.target); var tlength = trigger.length; if (!etarget.hasClass('trigger') && !etarget.hasClass(scope.excludeClass)) { for (var i = 0; i < tlength; i++) { closeTrigger(i); } } }); } }; }); //Directiva para que funcionen los popovers en un ngRepeat ngSharedDirectives.directive('bsPopover', function () { return function (scope, element, attrs) { element.find("a[rel=popover]").popover({ placement: 'right', html: 'true' }); $('body').on('click', function (e) { $('a[rel="popover"]').each(function () { //the 'is' for buttons that trigger popups //the 'has' for icons within a button that triggers a popup if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) { $(this).popover('hide'); } }); }); }; }); ngSharedDirectives.directive('focusOnElementChange', function ($timeout) { return { scope: { containerToWatch: '=', elementToFocus: '@', }, link: function (scope, element, attrs) { // Focus en el primer control con la clase .controlToFocus en el elemento scope.$watch('containerToWatch', function (newValue, oldValue) { if (newValue != oldValue) { $timeout(function () { var tab = $($('.' + newValue)[0]); if (tab) { var first = tab.find(scope.elementToFocus)[0]; if (first) { first.focus(); } } }); } }); } } }); /*2 decimal places*/ ngSharedDirectives.directive('decimalPlaces', function () { return { link: function (scope, ele, attrs) { ele.bind('keyup keypress', function (e) { var newVal = $(this).val() + (e.charCode !== 0 ? String.fromCharCode(e.charCode) : ''); if ($(this).val().search(/(.*)\.[0-9][0-9]/) === 0 && newVal.length > $(this).val().length) { e.preventDefault(); } }); } }; }); ngSharedDirectives.directive('focusClass', function () { return { restrict: 'A', priority: 1, require: 'ngModel', link: function (scope, element, attrs, ctrl) { var parentClass = null; if (attrs.focusClassname !== undefined) parentClass = attrs.focusClassname; else parentClass = "div-input-v2"; //default parent class var parents = element.parents('.' + parentClass); var parent = parents.first(); if (parent != undefined && parent != null) { element.bind('focus', function () { parent.addClass('focus'); }).bind('blur', function () { parent.removeClass('focus'); }); } } }; }); ngSharedDirectives.directive('stAutocomplete', function ($timeout, shApi) { return { restrict: 'E', require: ['^form'], scope: { 'extraClasses': '@', 'iconClass': '@', 'tamt': '@', 'typeaheadTemplate': '@', 'headerTitle': '@', 'inputId': '@', 'inputName': '@', 'textProperty': '@', 'autocomplete': '=', 'noResultsControl': '=', 'disabled': '=', 'arrayTranslations': '=', 'placeholder': '@', 'fromlistErrorMsg': '@', 'requiredErrorMsg': '@', 'pointErrorMsg': '@', 'tooltipError': '@', 'functionData': '=', 'required': '=', 'classInput': '@', 'clearTags': '=', 'hideOldx': '=', 'showNewx': '=', 'onlyRigth': '=', 'otherInput': '=', 'clearsearchtext': '@', }, link: function (scope, elem, attrs, ctrls) { shApi.defineRequired(scope, scope); scope.form = ctrls[0]; scope.placeholderText = ""; scope.tooltipErrorMode = scope.tooltipError === 'true'; if (scope.disabled === undefined) scope.disabled = false; if (scope.extraClasses === undefined) scope.extraClasses = ""; if (scope.classInput === undefined) scope.classInput = ""; if (scope.iconClass === undefined) scope.iconClass = ""; if (scope.headerTitle === undefined) scope.headerTitle = ""; if (scope.inputId === undefined) scope.inputId = ""; if (scope.inputName === undefined) scope.inputName = ""; if (scope.textProperty === undefined) scope.textProperty = ""; if (scope.fromlistErrorMsg === undefined) scope.fromlistErrorMsg = ""; if (scope.requiredErrorMsg === undefined) scope.requiredErrorMsg = ""; if (scope.pointErrorMsg === undefined) scope.pointErrorMsg = ""; if (scope.placeholder !== undefined) { if (scope.arrayTranslations !== undefined) var text = scope.arrayTranslations[scope.placeholder]; if (text !== undefined) scope.placeholderText = text; else scope.placeholderText = scope.placeholder; } }, controller: function ($scope) { $scope.getTypeaheadData = function (filter) { return $scope.functionData(filter); }; $scope.focusElement = function (id) { $timeout(function () { document.getElementById(id).focus(); }, 500); }; $scope.clearInput = function () { $scope.autocomplete = ''; if ($scope.clearTags == true) { $scope.otherInput = ''; $scope.$parent.backToSearch(); } } }, templateUrl: '/Scripts/app/Modules/stAutocompleteTemplate.html' }; }); ngSharedDirectives.directive('overlayElement', function () { return { restrict: 'A', scope: { 'overlayElementControl': '=', 'overlayElementExtraId': '@', 'callbackOnClose': '&' }, link: function (scope, element, attrs, ctrl) { var extraElement = null; var overlayElementClass = "overlay-element"; var overlayOpened = false; var divBackDrop = $('
').addClass("overlay-backdrop"); function setOverlay() { overlayOpened = true $(divBackDrop).hide().appendTo('body').fadeIn(50); $(element).addClass(overlayElementClass); if (extraElement !== null) $(extraElement).addClass(overlayElementClass); } function removeOverlay() { overlayOpened = false divBackDrop.fadeOut(50, function () { divBackDrop.remove(); $(element).removeClass(overlayElementClass); if (extraElement !== null) $(extraElement).removeClass(overlayElementClass); }); if (scope.callbackOnClose !== undefined) { scope.$applyAsync(function () { scope.callbackOnClose(); }); } } if (scope.overlayElementControl) setOverlay(); $(document).ready(function (e) { if (scope.overlayElementExtraId !== undefined) extraElement = $("#" + scope.overlayElementExtraId); scope.$watch('overlayElementControl', function (newValue, oldValue) { if (newValue != oldValue) { if (newValue && !overlayOpened) setOverlay(); else if (!newValue && overlayOpened) removeOverlay(); } }); }); $(document).mouseup(function (e) { if (overlayOpened && !element.is(e.target) && element.has(e.target).length === 0) { e.preventDefault(); removeOverlay(); } }); } }; }); } )(); var ngSharedPurchase = angular.module("ngSharedPurchase", []); (function () { ngSharedPurchase.factory('shPurchase', ['$timeout', '$rootScope', '$window', '$location', 'CanonicalUrl', 'Translator', '$cookies', 'shApi', '$http', '$state', 'Purchases', function ($timeout, $rootScope, $window, $location, CanonicalUrl, Translator, $cookies, shApi, $http, $state, Purchases) { function loadTimesForLanguage(model, lngCode) { var language = $.grep(model.availableLanguages, function (e) { if (e.languageCode === lngCode) return e; }); var currentTimeList = []; if (model.materializedData.AskTime) { $.each(model.availableTimes, function (idx, val) { if (shApi.containsAnyElement(language[0].productIds, val.productIds)) { currentTimeList.push(val); } }); } return currentTimeList; }; function initBaggageOptions(model, g) { //Initialize baggage opts model.edition.numMaxSeats = g.InfoBag.NumServices * g.InfoBag.NumPeople; model.edition.numBaggageItems = g.InfoBag.NumBaggageItems; model.edition.allowSpecialBaggage = g.InfoBag.AllowSpecialBaggage; model.edition.blockSuitcaseGroup = false; model.edition.blockMediumSuitcase = false; model.edition.blockLargeSuitcase = false; model.edition.blockSpecificGroup = false; model.edition.totalBaggage = 0; model.edition.mediumSuitcase = 0; model.edition.largeSuitcase = 0; model.edition.skis = 0; model.edition.bicycle = 0; model.edition.golfSticks = 0; model.edition.hasSpecialBaggage = false; model.edition.specialBaggageDescription = null; if (g.DetailedBaggage != null && g.DetailedBaggage.Baggage != null) { model.edition.mediumSuitcase = g.DetailedBaggage.Baggage.MediumSuitcase; model.edition.largeSuitcase = g.DetailedBaggage.Baggage.LargeSuitcase; model.edition.skis = g.DetailedBaggage.Baggage.Skis; model.edition.bicycle = g.DetailedBaggage.Baggage.Bicycle; model.edition.golfSticks = g.DetailedBaggage.Baggage.GolfSticks; if (g.DetailedBaggage.Baggage.SpecialBaggage != null) { model.edition.hasSpecialBaggage = g.DetailedBaggage.Baggage.SpecialBaggage.HasSpecialBaggage; model.edition.specialBaggageDescription = g.DetailedBaggage.Baggage.SpecialBaggage.Description; } model.edition.totalBaggage = model.edition.mediumSuitcase + model.edition.largeSuitcase + model.edition.skis + model.edition.bicycle + model.edition.golfSticks; } }; function areDatesEqual(date1, date2) { return date1.setHours(0, 0, 0, 0) === date2.setHours(0, 0, 0, 0) }; function getSeasons(apartmentData, dateFrom, dateTo, numPeople, model) { var seasons = {}; var currentApartment = apartmentData; seasons.Error = true; seasons.Filtered = []; if (currentApartment.detail.Seasons != null && currentApartment.detail.Seasons.length > 0) { //var minAvailabilityDate = shApi.getUTCDateFromString(currentApartment.detail.Seasons[0].AvailabilityPeriod.Min); //var maxAvailabilityDate = shApi.getUTCDateFromString(currentApartment.detail.Seasons[0].AvailabilityPeriod.Max); $.each(currentApartment.detail.Seasons, function (seasonKey, seasonValue) { var validSeason = false; var periodsList = []; if (seasonValue.AvailabilityPeriod != null) { //var minDate = minAvailabilityDate.getTime(); //var toDate = dateTo.getTime(); //var diff1 = minDate - toDate; //var maxDate = maxAvailabilityDate.getTime(); //var fromDate = dateFrom.getTime(); //var diff2 = maxDate - fromDate; //var fDays = Math.abs(Math.round(diff1 / (1000 * 60 * 60 * 24))); //var mDays = Math.abs(Math.round(diff2 / (1000 * 60 * 60 * 24))); var dateMin = shApi.getUTCDateFromString(seasonValue.AvailabilityPeriod.Min); var dateMax = shApi.getUTCDateFromString(seasonValue.AvailabilityPeriod.Max); if ((dateFrom <= dateMin && dateTo >= dateMin) || (dateFrom >= dateMin && dateFrom <= dateMax)) { $.each(seasonValue.FromPrices, function (periodKey, periodValue) { $.each(periodValue, function (peoplePriceKey, peoplePriceValue) { if (peoplePriceValue.People.Min <= numPeople && peoplePriceValue.People.Max >= numPeople) { var period = {}; period.Key = periodKey; period.Name = peoplePriceValue.Name; period.FromPrice = peoplePriceValue.FromPrice; if (period.FromPrice != null || period.FromPrice != undefined) { validSeason = true; } periodsList.push(period); return true; } }) }); } //else if ((dateFrom < dateMin) && (dateTo < dateMin)) { // var dMin = dateMin.getDate(); // var dTo = dateTo.getDate(); // var diffewer = dMin - dTo; // var fewerDays = Math.abs(Math.round(diffewer / (1000 * 60 * 60 * 24))); //} //else if ((dateFrom > dateMax) && (dateTo > dateMax)) { // var dFrom = dateFrom.getDate(); // var dMax= dateMax.getDate(); // var diffmore = dFrom - dMax; // var moreDays = Math.abs(Math.round(diffmore / (1000 * 60 * 60 * 24))); //} } if (validSeason) { //seasons = {}; //seasons.Filtered = []; var season = {}; season.Key = seasonKey; season.Periods = periodsList; seasons.Filtered.push(season); seasons.Error = false; model.showNoLodgingAvailability = false; model.seeNextSeason = false; //return false; } //else { // model.showNoLodgingAvailability = true; // if ((fewerDays <= fDays) || (moreDays <= mDays)) { // fDays = fewerDays; // mDays = moreDays; // $.each(seasonValue.FromPrices, function (periodKey, periodValue) { // $.each(periodValue, function (peoplePriceKey, peoplePriceValue) { // if (peoplePriceValue.People.Min <= numPeople && peoplePriceValue.People.Max >= numPeople) { // var period = {}; // period.Key = periodKey; // period.Name = peoplePriceValue.Name; // period.FromPrice = peoplePriceValue.FromPrice; // periodsList.push(period); // return true; // } // }) // }); // seasons = {}; // seasons.Filtered = []; // var season = {}; // season.Key = seasonKey; // season.Periods = periodsList; // seasons.Filtered.push(season); // } //} }); } return seasons; }; return { checkBaggageValidity: function (model, totalServices, baggageItems) { model.blockSuitcaseGroup = false; var totalItems = totalServices * baggageItems; if (model.mediumSuitcase >= totalItems) model.blockSuitcaseGroup = true; }, initializeMaterializedDataGroundRoute: function (model, g) { model.edition.numServices = g.InfoBag.NumServices; $.each(model.materializedData.Details, function (idx, item) { if (item["StageNumber"] == 1) { model.orgStage = item; return false; } }); $.each(model.materializedData.Details, function (idx, item) { if (item["StageNumber"] == 2) { model.destStage = item; return false; } }); initBaggageOptions(model, g); //model.minServiceDate = shApi.getUTCDateFromTime(g.InfoBag.MinJSServiceDate); //model.maxServiceDate = shApi.getUTCDateFromTime(g.InfoBag.MaxJSServiceDate); if (model.orgStage != null) { model.edition.providerDate = shApi.getUTCDateFromString(model.orgStage.ProviderDateService); if (model.orgStage.FlightNumberOrigin) { model.edition.flightNumberOrigin1 = model.orgStage.StageInfo.Origin.FlightNumber; if (model.orgStage.StageInfo.Origin.FlightDate != undefined && model.orgStage.StageInfo.Origin.FlightDate != null) { model.edition.flightDateOrigin1 = model.edition.flightHourOrigin1 = shApi.getUTCDateFromString(model.orgStage.StageInfo.Origin.FlightDate); } } if (model.orgStage.FlightNumberDestination) { model.edition.flightNumberDestination1 = model.orgStage.StageInfo.Destination.FlightNumber; if (model.orgStage.StageInfo.Destination.FlightDate != undefined && model.orgStage.StageInfo.Destination.FlightDate != null) { model.edition.flightDateDestination1 = model.edition.flightHourDestination1 = shApi.getUTCDateFromString(model.orgStage.StageInfo.Destination.FlightDate); } } model.edition.date = model.edition.dateService = model.edition.hourService = shApi.getUTCDateFromString(model.orgStage.DateHour); } if (model.destStage != null) { //La fecha de vuelta tiene que ser al menos un día posterior a la de ida (por el momento) model.minReturnServiceDate = shApi.getCompleteDateHour(model.edition.dateService, model.edition.hourService, 1440); model.maxReturnServiceDate = model.maxServiceDate; model.edition.returnProviderDate = shApi.getUTCDateFromString(model.destStage.ProviderDateService); if (model.destStage.FlightNumberOrigin) { model.edition.flightNumberOrigin2 = model.destStage.StageInfo.Origin.FlightNumber; if (model.destStage.StageInfo.Origin.FlightDate != undefined && model.destStage.StageInfo.Origin.FlightDate != null) { model.edition.flightDateOrigin2 = model.edition.flightHourOrigin2 = shApi.getUTCDateFromString(model.destStage.StageInfo.Origin.FlightDate); } } if (model.destStage.FlightNumberDestination) { model.edition.flightNumberDestination2 = model.destStage.StageInfo.Destination.FlightNumber; if (model.destStage.StageInfo.Destination.FlightDate != undefined && model.destStage.StageInfo.Destination.FlightDate != null) { model.edition.flightDateDestination2 = model.edition.flightHourDestination2 = shApi.getUTCDateFromString(model.destStage.StageInfo.Destination.FlightDate); } } model.edition.returnDate = model.edition.returnDateService = model.edition.returnHourService = shApi.getUTCDateFromString(model.destStage.DateHour); } }, initializeMaterializedDataGroundRouteShared: function (model, g) { $.each(model.materializedData.Details, function (idx, item) { if (item["StageNumber"] == 1) { model.orgStage = item; return false; } }); $.each(model.materializedData.Details, function (idx, item) { if (item["StageNumber"] == 2) { model.destStage = item; return false; } }); if (model.orgStage != null) { model.edition.providerDate = shApi.getUTCDateFromString(model.orgStage.ProviderDateService); if (model.orgStage.FlightNumberOrigin) { model.edition.flightNumberOrigin1 = model.orgStage.StageInfo.Origin.FlightNumber; if (model.orgStage.StageInfo.Origin.FlightDate != undefined && model.orgStage.StageInfo.Origin.FlightDate != null) { model.edition.flightDateOrigin1 = model.edition.flightHourOrigin1 = shApi.getUTCDateFromString(model.orgStage.StageInfo.Origin.FlightDate); } } if (model.orgStage.FlightNumberDestination) { model.edition.flightNumberDestination1 = model.orgStage.StageInfo.Destination.FlightNumber; if (model.orgStage.StageInfo.Destination.FlightDate != undefined && model.orgStage.StageInfo.Destination.FlightDate != null) { model.edition.flightDateDestination1 = model.edition.flightHourDestination1 = shApi.getUTCDateFromString(model.orgStage.StageInfo.Destination.FlightDate); } } model.edition.date = model.edition.dateService = model.edition.hourService = shApi.getUTCDateFromString(model.orgStage.DateHour); } if (model.destStage != null) { model.edition.returnProviderDate = shApi.getUTCDateFromString(model.destStage.ProviderDateService); if (model.destStage.FlightNumberOrigin) { model.edition.flightNumberOrigin2 = model.destStage.StageInfo.Origin.FlightNumber; if (model.destStage.StageInfo.Origin.FlightDate != undefined && model.destStage.StageInfo.Origin.FlightDate != null) { model.edition.flightDateOrigin2 = model.edition.flightHourOrigin2 = shApi.getUTCDateFromString(model.destStage.StageInfo.Origin.FlightDate); } } if (model.destStage.FlightNumberDestination) { model.edition.flightNumberDestination2 = model.destStage.StageInfo.Destination.FlightNumber; if (model.destStage.StageInfo.Destination.FlightDate != undefined && model.destStage.StageInfo.Destination.FlightDate != null) { model.edition.flightDateDestination2 = model.edition.flightHourDestination2 = shApi.getUTCDateFromString(model.destStage.StageInfo.Destination.FlightDate); } } model.edition.returnDate = model.edition.returnDateService = model.edition.returnHourService = shApi.getUTCDateFromString(model.destStage.DateHour); } }, initDefaultBaggage: function (model) { //Important: After call initBaggageOptions if (model.edition != undefined) { model.edition.defaultTotalBaggage = model.edition.totalBaggage; model.edition.defaultMediumSuitcase = model.edition.mediumSuitcase; model.edition.defaultLargeSuitcase = model.edition.largeSuitcase; model.edition.defaultSkis = model.edition.skis; model.edition.defaultBicycle = model.edition.bicycle; model.edition.defaultGolfSticks = model.edition.golfSticks; model.edition.defaultHasSpecialBaggage = model.edition.hasSpecialBaggage; model.edition.defaultSpecialBaggageDescription = model.edition.specialBaggageDescription; } }, initializeMaterializedDataGroundHourly: function (model, g) { model.edition.numServices = g.InfoBag.NumServices; }, fillAdditionalMaterializedData: function (editionData, baggageRevision, currency) { var p = null; var c = editionData; if (c != null) { p = {}; if (c.addNotes) { p.NotesForProvider = c.notesForProvider; } if (c.flightNumberOrigin1 != undefined && c.flightNumberOrigin1 != null) { p.OriginFlightNumber = c.flightNumberOrigin1; } if (c.flightNumberDestination1 != undefined && c.flightNumberDestination1 != null) { p.DestinationFlightNumber = c.flightNumberDestination1; } if (c.flightDateOrigin1 != undefined && c.flightDateOrigin1 != null) { p.OriginFlightDate = shApi.serverUTCDateFromTime(c.flightDateOrigin1.getTime()); } if (c.departuredateflightOrigin1 != undefined && c.departuredateflightOrigin1 != null) { p.OriginDepartureFlightDate = shApi.serverUTCDateFromTime(c.departuredateflightOrigin1.getTime()); } if (c.arrivaldateflightOrigin1 != undefined && c.arrivaldateflightOrigin1 != null) { p.OriginArrivalFlightDate = shApi.serverUTCDateFromTime(c.arrivaldateflightOrigin1.getTime()); } if (c.flightDateDestination1 != undefined && c.flightDateDestination1 != null) { p.DestinationFlightDate = shApi.serverUTCDateFromTime(c.flightDateDestination1.getTime()); } if (c.departuredateflightDestination1 != undefined && c.departuredateflightDestination1 != null) { p.DestinationDepartureFlightDate = shApi.serverUTCDateFromTime(c.departuredateflightDestination1.getTime()); } if (c.arrivaldateflightDestination1 != undefined && c.arrivaldateflightDestination1 != null) { p.DestinationArrivalFlightDate = shApi.serverUTCDateFromTime(c.arrivaldateflightDestination1.getTime()); } if (c.flightNumberOrigin2 != undefined && c.flightNumberOrigin2 != null) { p.ReturnOriginFlightNumber = c.flightNumberOrigin2; } if (c.flightNumberDestination2 != undefined && c.flightNumberDestination2 != null) { p.ReturnDestinationFlightNumber = c.flightNumberDestination2; } if (c.flightDateOrigin2 != undefined && c.flightDateOrigin2 != null) { p.ReturnOriginFlightDate = shApi.serverUTCDateFromTime(c.flightDateOrigin2.getTime()); } if (c.departuredateflightOrigin2 != undefined && c.departuredateflightOrigin2 != null) { p.ReturnOriginDepartureFlightDate = shApi.serverUTCDateFromTime(c.departuredateflightOrigin2.getTime()); } if (c.arrivaldateflightOrigin2 != undefined && c.arrivaldateflightOrigin2 != null) { p.ReturnOriginArrivalFlightDate = shApi.serverUTCDateFromTime(c.arrivaldateflightOrigin2.getTime()); } if (c.flightDateDestination2 != undefined && c.flightDateDestination2 != null) { p.ReturnDestinationFlightDate = shApi.serverUTCDateFromTime(c.flightDateDestination2.getTime()); } if (c.departuredateflightDestination2 != undefined && c.departuredateflightDestination2 != null) { p.ReturnDestinationDepartureFlightDate = shApi.serverUTCDateFromTime(c.departuredateflightDestination2.getTime()); } if (c.arrivaldateflightDestination2 != undefined && c.arrivaldateflightDestination2 != null) { p.ReturnDestinationArrivalFlightDate = shApi.serverUTCDateFromTime(c.arrivaldateflightDestination2.getTime()); } if (c.userDateMandatory != undefined) { p.UserDateMandatory = c.userDateMandatory; } if (c.returnUserDateMandatory != undefined) { p.ReturnUserDateMandatory = c.returnUserDateMandatory; } if (c.meetingPoint != null && typeof c.meetingPoint.PlaceId !== 'undefined') { p.MeetingPoint = {}; p.MeetingPoint.PlaceId = c.meetingPoint.PlaceId; p.MeetingPoint.RawAddress = c.meetingPoint.AutoCompleteText; } if (baggageRevision) { p.DetailedBaggage = {}; p.DetailedBaggage.Bicycle = c.bicycle; p.DetailedBaggage.GolfSticks = c.golfSticks; p.DetailedBaggage.LargeSuitcase = c.largeSuitcase; p.DetailedBaggage.MediumSuitcase = c.mediumSuitcase; p.DetailedBaggage.Skis = c.skis; p.DetailedBaggage.SpecialBaggage = {}; p.DetailedBaggage.SpecialBaggage.Description = c.specialBaggageDescription; p.DetailedBaggage.SpecialBaggage.HasSpecialBaggage = c.hasSpecialBaggage; } if (c.date != undefined && c.date != null) { p.Date = shApi.serverUTCDateFromTime(c.date.getTime()); } if (c.providerDate != undefined && c.providerDate != null) { p.providerDate = shApi.serverUTCDateFromTime(c.providerDate.getTime()); } if (c.returnDate != undefined && c.returnDate != null) { p.ReturnDate = shApi.serverUTCDateFromTime(c.returnDate.getTime()); } if (c.duration != undefined && c.duration != null && c.duration > 0) { p.DurationUnits = c.duration; } if (c.languageCode != undefined && c.languageCode != null) { p.LanguageCode = c.languageCode; } p.IsFromFlightManualEdition = c.isFromFlightManualEdition; p.Currency = currency; } return p; }, meetingPointMaterializedValidity: function (model, path, newVal, oldVal, materializedUid, serviceRequestType, partnerUserCode) { var stage = 1; var meetingPoint = newVal; if (meetingPoint != oldVal) { var form = shApi.createPath(model.materializedEdition, 'meetingPoint'); if (form != undefined) { var filled = meetingPoint != null && meetingPoint != undefined && meetingPoint != ''; if (filled) { if (typeof meetingPoint.PlaceId === 'undefined') { form.$setValidity('fromlist', false); form.$setValidity('point', true); model.meetingPointFromListError = true; } else { form.$setValidity('fromlist', true); model.meetingPointFromListError = false; model.blockEditButton = true; //Check meeting point validity $http.post(CanonicalUrl.getUrl(model.culture, path), { materializedUid: materializedUid, stageNumber: 1, placeId: meetingPoint.PlaceId, serviceRequestType: serviceRequestType, partnerUserCode: partnerUserCode }).success(function (data, status, headers, config) { if (data != null && typeof data.IsValid != 'undefined') { if (!data.IsValid) { form.$setValidity('point', false); model.meetingPointErrorMsg[stage] = data.ErrorMsg; model.meetingPointPointError = true; } else { form.$setValidity('point', true); model.meetingPointErrorMsg[stage] = ''; model.meetingPointPointError = false; } model.blockEditButton = false; } else { shApi.handleError(model, data); } }).error(function (data, status, headers, config) { shApi.handleError(model, data); }); } } else { form.$setValidity('fromlist', true); form.$setValidity('point', true); model.meetingPointFromListError = false; model.meetingPointPointError = false; } } } }, flightNumberValidity: function (newVal, stage) { var filled = newVal != null && newVal != undefined && newVal != ''; var errorRequired = false; var errorFromList = false; var errorCoincidence = false; if (filled) { if (typeof newVal.FlightCode === 'undefined') { errorFromList = true; } else { errorFromList = false; } if (!errorFromList) { //El número de vuelo se pide cuando el origen es aeropuerto, por tanto será el destino de donde viene var orgCode = stage.ChauffeurDetail.StageInfo.Origin.AirportCode; if (orgCode != null) { if (orgCode != newVal.IATADestination) { errorCoincidence = true; } else errorCoincidence = false; } } } else { errorRequired = true; errorFromList = false; errorCoincidence = false; } var errors = {}; errors.errorFromList = errorFromList; errors.errorCoincidence = errorCoincidence; errors.errorRequired = errorRequired; return errors; }, showGoogleRouteMap: function (orgLat, orgLon, destLat, destLon) { var directionsDisplay = new google.maps.DirectionsRenderer(); var directionsService = new google.maps.DirectionsService(); var center = new google.maps.LatLng(orgLat, orgLon); var map = new google.maps.Map(document.getElementById('map'), { zoom: 10, center: center, mapTypeId: google.maps.MapTypeId.ROADMAP, streetViewControl: false, mapTypeControl: false }); //Por place id: { placeId: "ChIJc1lGdwfP20YR3lGOMZD-GTM" } var start = new google.maps.LatLng(orgLat, orgLon); var end = new google.maps.LatLng(destLat, destLon); var bounds = new google.maps.LatLngBounds(); bounds.extend(start); bounds.extend(end); map.fitBounds(bounds); var request = { origin: start, destination: end, travelMode: google.maps.TravelMode.DRIVING }; //TODO: Servidor directionsService.route(request, function (response, status) { if (status == google.maps.DirectionsStatus.OK) { directionsDisplay.setDirections(response); directionsDisplay.setMap(map); } else { //alert("Directions Request from " + start.toUrlValue(6) + " to " + end.toUrlValue(6) + " failed: " + status); } }); }, showGooglePointMap: function (mapId, lat, lon) { var uluru = { lat: lat, lng: lon }; var map = new google.maps.Map( document.getElementById(mapId), { zoom: 4, center: uluru, zoomControl: true, mapTypeControl: false, scaleControl: false, streetViewControl: false, rotateControl: false, fullscreenControl: true }); var marker = new google.maps.Marker({ position: uluru, map: map }); map.setZoom(15); map.panTo(marker.position); }, showFloatingCart: function() { $(".all-shopping-cart").addClass('all-shopping-cart--open'); $(".sidebar-shopping-cart__overlay").addClass('sidebar-shopping-cart__overlay--show'); $(".sidebar-shopping-cart__overlay").addClass('sidebar-shopping-cart__overlay--open'); $('html').addClass('delete-scroll'); $('body').addClass('delete-scroll'); $('html').css('padding-right', '0px'); $('html').css('overflow-y', 'hidden'); $('.js-added-correctly').addClass('show'); window.setTimeout(function () { $(".js-added-correctly").fadeOut(200, function () { $('.js-added-correctly').removeClass('show'); $('.js-added-correctly').removeAttr('style'); }); }, 5000); }, //Element resize onElementHeightChange: function (elm, callback) { var lastHeight = elm.clientHeight, newHeight; (function run() { newHeight = elm.clientHeight; if (lastHeight != newHeight) callback(); lastHeight = newHeight; elm.onElementHeightChangeTimer = setTimeout(run, 200); })(); }, validateCheckFlightOneWay: function (model) { if ((model.materializedEdition.flightNumberOrigin1.$error.pattern == undefined) && (model.materializedEdition.flightNumberOrigin1.$error.required == undefined)) { if (model.materializedData.Details[0].FlightNumberOrigin && !model.showFlightConfirmationOrigin1) { model.materializedEdition.flightNumberOrigin1.$setValidity("valEmptyField", false); } else { model.materializedEdition.flightNumberOrigin1.$setValidity("valEmptyField", true); } } if ((model.materializedEdition.flightNumberDestination1.$error.pattern == undefined) && (model.materializedEdition.flightNumberDestination1.$error.required == undefined)) { if (model.materializedData.Details[0].FlightNumberDestination && !model.showFlightConfirmationDestination1) { model.materializedEdition.flightNumberDestination1.$setValidity("valEmptyField", false); } else { model.materializedEdition.flightNumberDestination1.$setValidity("valEmptyField", true); } } }, validateCheckFlightReturn: function (model) { if ((model.materializedEdition.flightNumberOrigin2.$error.pattern == undefined) && (model.materializedEdition.flightNumberOrigin2.$error.required == undefined)) { if (model.materializedData.Details[1] != undefined && model.materializedData.Details[1].FlightNumberOrigin && !model.showFlightConfirmationOrigin2) { model.materializedEdition.flightNumberOrigin2.$setValidity("valEmptyField", false); } else { model.materializedEdition.flightNumberOrigin2.$setValidity("valEmptyField", true); } } if ((model.materializedEdition.flightNumberDestination2.$error.pattern == undefined) && (model.materializedEdition.flightNumberDestination2.$error.required == undefined)) { if (model.materializedData.Details[1] != undefined && model.materializedData.Details[1].FlightNumberDestination && !model.showFlightConfirmationDestination2) { model.materializedEdition.flightNumberDestination2.$setValidity("valEmptyField", false); } else { model.materializedEdition.flightNumberDestination2.$setValidity("valEmptyField", true); } } }, cleanFlightFormErrors: function (model) { if (model.materializedEdition != undefined) { model.materializedEdition.$setPristine(); model.materializedEdition.$setUntouched(); if (model.materializedEdition.flightNumberOrigin1 != undefined) { model.materializedEdition.flightNumberOrigin1.$setValidity('valErrorCoincidence', true); model.materializedEdition.flightNumberOrigin1.$setValidity('valDateWrong', true); model.materializedEdition.flightNumberOrigin1.$setValidity('valEmptyField', true); } if (model.materializedEdition.flightNumberDestination1 != undefined) { model.materializedEdition.flightNumberDestination1.$setValidity('valDateWrong', true); model.materializedEdition.flightNumberDestination1.$setValidity('valErrorCoincidence', true); model.materializedEdition.flightNumberDestination1.$setValidity('valEmptyField', true); } if (model.materializedEdition.flightNumberOrigin2 != undefined) { model.materializedEdition.flightNumberOrigin2.$setValidity('valDateWrong', true); model.materializedEdition.flightNumberOrigin2.$setValidity('valErrorCoincidence', true); model.materializedEdition.flightNumberOrigin2.$setValidity('valEmptyField', true); } if (model.materializedEdition.flightNumberDestination2 != undefined) { model.materializedEdition.flightNumberDestination2.$setValidity('valDateWrong', true); model.materializedEdition.flightNumberDestination2.$setValidity('valErrorCoincidence', true); model.materializedEdition.flightNumberDestination2.$setValidity('valEmptyField', true); } } }, cleanMeetingPointFormErrors: function (model) { if (model.materializedEdition != undefined) { model.materializedEdition.$setPristine(); model.materializedEdition.$setUntouched(); if (model.materializedEdition.meetingPoint != undefined) { model.meetingPointPointError = false; model.meetingPointFromListError = false; } } }, establishFiltersOrder: function (orderList, newValue, fltrs) { var value = null; if (newValue == 'destinationFilters' || newValue == 'quickFilter') { orderList = []; //se reestablecen los valores de orden value = "FirstLevelFilter"; } else { if (fltrs.verticals == null || fltrs.verticals.length == 0) { var index = orderList.indexOf('CategoryFilter'); if (index != undefined && index != -1) orderList.splice(index, 1); } if (fltrs.language == null || fltrs.language.length == 0) { var index = orderList.indexOf('LanguageFilter'); if (index != undefined && index != -1) orderList.splice(index, 1); } if (newValue == 'freeText' || newValue == 'date' || newValue == 'freeTours' || newValue == 'providerFilters') { value = "SecondLevelFilter"; } else if (newValue == 'verticalFilters') { value = "CategoryFilter"; } else if (newValue == 'language') { value = "LanguageFilter"; } } if (value != null) { if (!shApi.checkIfContains(orderList, value)) { orderList.push(value); } } return orderList; }, initServiceSliderList: function (model, group) { var idGroup = model.materializedList[group].GroupingCode; var elem = {}; elem = {}; elem.list = []; elem.listkeys = []; //para pantalla elem.groupingCode = idGroup; elem.groupingIndex = group; elem.currentMaterializedUid = model.materializedList[group].MaterializedServices[0].MaterializedUid; $.each(model.materializedList[group].MaterializedServices, function (idx, item) { var materialized = {}; materialized.fullDataLoaded = false; materialized.detail = {}; materialized.detail.Title = item.Title; materialized.detail.MainPicture = item.ImgUrl; materialized.detail.ProviderLogoUrl = item.ProviderLogo; materialized.detail.Subtitle = item.SubTitle; materialized.detail.CategoryName = item.CategoryName; materialized.detail.IsFreeTour = item.IsFreeTour; materialized.detail.Modifiers = item.Modifiers; materialized.detail.Rating = item.RatingInfo; materialized.detail.MaterializedServiceType = item.MaterializedServiceType; elem.listkeys.push(item.MaterializedUid); elem.list[item.MaterializedUid] = materialized; }); model.currentSrvList.push(elem); }, loadActivityFullInfo: function (model, materializedUid, groupingCode, currencyCode, timeOut, alternativeProvider) { var currentRequestId = shApi.generateGuid(); model.lastActivityRequestUid = currentRequestId; //1. Bucar índice en el grupo para encontrar el elemento var found = model.currentSrvList.find(function (item) { return item.groupingCode == groupingCode; }); var idx = 0; if (found != null) { idx = model.currentSrvList.indexOf(found); } var totalItems = model.currentSrvList.length; var idListing = []; if (alternativeProvider != undefined && alternativeProvider) { idListing = [idx]; } else { //TODO: Improve if (idx - 2 >= 0) idListing.push(idx - 2); if (idx - 1 >= 0) idListing.push(idx - 1); idListing.push(idx); if (idx + 1 <= (totalItems - 1)) idListing.push(idx + 1); if (idx + 2 <= (totalItems - 1)) idListing.push(idx + 2); } var materializedIds = []; $.each(idListing, function (id, val) { var mainMaterialized = null; if (materializedUid != null && groupingCode == model.currentSrvList[val].groupingCode) { mainMaterialized = materializedUid; } else { mainMaterialized = model.currentSrvList[val].currentMaterializedUid; } model.currentSrvList[val].currentMaterializedUid = mainMaterialized; if (!model.currentSrvList[val].list[mainMaterialized].fullDataLoaded) { materializedIds.push(mainMaterialized); } }); $timeout(function () { if (materializedIds.length > 0 && currentRequestId == model.lastActivityRequestUid) { Purchases.getActivitiesFullInformation(JSON.stringify(materializedIds), currencyCode, model.prefix) .then(function (response) { model.lastResponse = response; $.each(idListing, function (id, val) { $.each(model.lastResponse, function (id, item) { if (model.currentSrvList[val].list[model.lastResponse[id].MaterializedUid] != undefined) { model.currentSrvList[val].list[model.lastResponse[id].MaterializedUid].error = model.lastResponse[id].Error; if (!model.lastResponse[id].Error) { //Hasta que vengan tipados var typeOfMat = model.currentSrvList[val].list[model.lastResponse[id].MaterializedUid].detail.MaterializedServiceType; model.currentSrvList[val].list[model.lastResponse[id].MaterializedUid].detail = angular.copy(model.lastResponse[id].Detail); model.currentSrvList[val].list[model.lastResponse[id].MaterializedUid].detail.MaterializedServiceType = typeOfMat; } model.currentSrvList[val].list[model.lastResponse[id].MaterializedUid].fullDataLoaded = true; } }); }); model.sliderShow = true; }); } }, timeOut); }, //Participants (Internal process): convertFormParticipantsToUpload: function(form) { var list = []; angular.forEach(form.participantsdata, function (value, key) { var iterator = value; var slotKey = key; var slotList = {}; slotList.slot_key = slotKey; slotList.slot_values = []; angular.forEach(iterator, function (value2, key2) { var aux = {}; aux.participant_key = value2.key; aux.formvalues = []; angular.forEach(value2.fields, function (f, key3) { var auxValue = {}; auxValue.key = f.key; auxValue.uservalue = f.uservalue; auxValue.datatype = f.datatype; aux.formvalues.push(auxValue); }); slotList.slot_values.push(aux); }); list.push(slotList); }); return list; }, //NEW: TravelManager chargeActivityOptions2: function (model) { model.availableTimes = []; model.availableLanguages = []; $.each(model.materializedData.Options, function (key, val) { if (model.materializedData.AskTime) { var addTo = true; if (model.availableTimes.length > 0) { var findTime = $.grep(model.availableTimes, function (e) { return e.index === val.Time.Index; }); if (findTime.length > 0) { addTo = false; } } if (addTo) { var time = {}; time.index = val.Time.Index; time.availableHour = val.Time.AvailableHour; time.hourFormatted = val.Time.HourFormatted; time.numLangs = val.Languages != null ? val.Languages.length : 0; time.productIds = []; time.productIds.push(key); model.availableTimes.push(time); } else { for (var currentTime in model.availableTimes) { if (model.availableTimes[currentTime].index == val.Time.Index) { model.availableTimes[currentTime].productIds.push(key); } } } } if (model.materializedData.AskLanguage) { if (val.Languages != null) { for (var idx in val.Languages) { var language = val.Languages[idx]; var addTo = true; if (model.availableLanguages.length > 0) { var findLanguage = $.grep(model.availableLanguages, function (e) { return e.languageCode === language.LanguageCode; }); if (findLanguage.length > 0) { addTo = false; } } if (addTo) { var lang = {}; lang.languageCode = language.LanguageCode; lang.languageName = language.LanguageName; lang.numTimes = 0; lang.productIds = []; lang.productIds.push(key); model.availableLanguages.push(lang); } else { for (var currentLanguage in model.availableLanguages) { if (model.availableLanguages[currentLanguage].languageCode == language.LanguageCode) { model.availableLanguages[currentLanguage].productIds.push(key); } } } } } } }); if (model.availableTimes.length > 0) { model.availableTimes = model.availableTimes.sort(shApi.sortBy('index', false, parseInt)); } if (model.availableLanguages.length > 0) { model.availableLanguages = model.availableLanguages.sort(shApi.sortBy('code', false)); if (model.availableTimes.length > 0) { $.each(model.availableLanguages, function (key, val) { model.availableLanguages[key].numTimes = loadTimesForLanguage(model, model.availableLanguages[key].languageCode).length; }); } } }, loadLanguagesForSelectedTime: function (model, idx) { var time = $.grep(model.availableTimes, function (e) { if (e.index === idx) return e; }); var currentLanguageList = []; $.each(model.availableLanguages, function (idx, val) { if (shApi.containsAnyElement(time[0].productIds, val.productIds)) { currentLanguageList.push(val); } }); return currentLanguageList; }, loadTimesForSelectedLanguage: function (model, lngCode) { return loadTimesForLanguage(model, lngCode); }, ////////////////////////////////// chargeActivityOptions: function (model) { model.availableTimes = []; model.availableLanguages = []; $.each(model.materializedData.Options, function (key, val) { if (model.materializedData.AskTime) { var addTo = true; if (model.availableTimes.length > 0) { var findTime = $.grep(model.availableTimes, function (e) { return e.index === val.Time.Index; }); if (findTime.length > 0) { addTo = false; } } if (addTo) { var time = {}; time.index = val.Time.Index; time.availableHour = val.Time.AvailableHour; time.isNotAvailable = false; /*First initization*/ time.hourFormatted = val.Time.HourFormatted; time.productIds = []; time.productIds.push(key); model.availableTimes.push(time); } else { for (var currentTime in model.availableTimes) { if (model.availableTimes[currentTime].index == val.Time.Index) { model.availableTimes[currentTime].productIds.push(key); } } } } if (model.materializedData.AskLanguage) { if (val.Languages != null) { for (var idx in val.Languages) { var language = val.Languages[idx]; var addTo = true; if (model.availableLanguages.length > 0) { var findLanguage = $.grep(model.availableLanguages, function (e) { return e.languageCode === language.LanguageCode; }); if (findLanguage.length > 0) { addTo = false; } } if (addTo) { var lang = {}; lang.languageCode = language.LanguageCode; lang.languageName = language.LanguageName; lang.isNotAvailable = false; /*First initialization*/ lang.productIds = []; lang.productIds.push(key); model.availableLanguages.push(lang); } else { for (var currentLanguage in model.availableLanguages) { if (model.availableLanguages[currentLanguage].languageCode == language.LanguageCode) { model.availableLanguages[currentLanguage].productIds.push(key); } } } } } } }); if (model.availableTimes.length > 0) model.availableTimes = model.availableTimes.sort(shApi.sortBy('index', false, parseInt)); if (model.availableLanguages.length > 0) model.availableLanguages = model.availableLanguages.sort(shApi.sortBy('code', false)); }, selectTimeForActivity: function (model, idx) { var time = $.grep(model.availableTimes, function (e) { if (e.index === idx) return e; }); var currentNotAvailable = false; if (model.materializedData.AskLanguage) { $.each(model.availableLanguages, function (idx, val) { var contains = shApi.containsAnyElement(time[0].productIds, val.productIds); if (contains) { model.availableLanguages[idx].isNotAvailable = false; } else model.availableLanguages[idx].isNotAvailable = true; if (model.selectedLanguage != undefined && model.selectedLanguage != null && model.selectedLanguage.languageCode == val.languageCode && model.availableLanguages[idx].isNotAvailable) { currentNotAvailable = true; } }); if (model.selectedLanguage == undefined || model.selectedLanguage == null || currentNotAvailable) { $.each(model.availableLanguages, function (idx, val) { if (!val.isNotAvailable) { model.selectedLanguage = model.availableLanguages[idx]; return false; } }); } } }, selectLanguageForActivity: function (model, lngCode) { var language = $.grep(model.availableLanguages, function (e) { if (e.languageCode === lngCode) return e; }); var currentNotAvailable = false; if (model.materializedData.AskTime) { $.each(model.availableTimes, function (idx, val) { var contains = shApi.containsAnyElement(language[0].productIds, val.productIds); if (contains) { model.availableTimes[idx].isNotAvailable = false; } else model.availableTimes[idx].isNotAvailable = true; if (model.selectedTime != undefined && model.selectedTime != null && model.selectedTime.index == val.index && model.availableTimes[idx].isNotAvailable) { currentNotAvailable = true; } }); if (model.selectedTime == undefined || model.selectedTime == null || currentNotAvailable) { $.each(model.availableTimes, function (idx, val) { if (!val.isNotAvailable) { model.selectedTime = model.availableTimes[idx]; return false; } }); } } }, getGroupsFilteredForActivity: function (model) { model.showProductGroups = false; var products = {}; model.productGroups = {}; model.currentOptions = []; if (model.materializedData.AskLanguage && model.materializedData.AskTime && model.selectedTime != null && model.selectedLanguage != null) { var productList = model.selectedTime.productIds.filter(element => model.selectedLanguage.productIds.indexOf(element) !== -1); $.each(productList, function (idx, val) { products[val] = model.materializedData.Options[val]; }); } else if (model.materializedData.AskTime && model.selectedTime != null) { $.each(model.selectedTime.productIds, function (idx, val) { products[val] = model.materializedData.Options[val]; }); } else if (model.materializedData.AskLanguage && model.selectedLanguage != null) { $.each(model.selectedLanguage.productIds, function (idx, val) { products[val] = model.materializedData.Options[val]; }) } else { products = model.materializedData.Options; } //Make groups $.each(products, function (idx, val) { var find = model.productGroups[val.FeatureIndex] != undefined; if (!find) { var grp = {}; grp.FeatureName = val.FeatureName; val.Num = 0; grp.ReferencePVP = val.PVP; grp.CurrencyMask = val.CurrencyMask; grp.Options = []; grp.Options[idx] = val; model.productGroups[val.FeatureIndex] = grp; } else { if (model.productGroups[val.FeatureIndex].ReferencePVP === 0) model.productGroups[val.FeatureIndex].ReferencePVP = val.PVP; val.Num = 0; model.productGroups[val.FeatureIndex].Options[idx] = val; } }); }, selectActivityGroup: function (model, index) { model.currentOptions = []; model.currentGroupTitle = model.productGroups[index].FeatureName; model.edition.minBookingTickets = 0; model.edition.maxBookingTickets = 0; model.edition.isGroup = false; model.edition.maxBookingGroupTickets = 0; var count = 0; for (var key in model.productGroups[index].Options) { var item = model.productGroups[index].Options[key]; var pricingMatrixOrdered = item.PricingMatrix.length > 1 ? item.PricingMatrix.sort(shApi.sortBy('MinTickets', false, parseInt)) : item.PricingMatrix; if (count == 0) { model.edition.minBookingTickets = item.MinBookingTickets; model.edition.maxBookingTickets = item.MaxBookingTickets; model.edition.isGroup = item.GroupType == 'Group'; if (model.edition.isGroup) { model.edition.maxBookingGroupTickets = Math.ceil(item.MaxBookingTickets / item.GroupSize); } } var minList = []; var maxList = []; for (let i = 0; i < item.PricingMatrix.length; i++) { minList.push(item.PricingMatrix[i].MinTickets); maxList.push(item.PricingMatrix[i].MaxTickets); } var minValue = Math.min.apply(null, minList); var maxValue = Math.max.apply(null, maxList); var opt = {}; opt.Name = item.Name; opt.MinValue = minValue; opt.MaxValue = maxValue; opt.AvailableAlone = item.AvailableAlone; opt.Code = key; opt.PVP = item.PVP; //TODO: price group opt.UnitaryPrice = pricingMatrixOrdered[0].PVP; opt.CurrencyMask = item.CurrencyMask; opt.Num = 0; // default value opt.IsSoldOut = item.IsSoldout; opt.PricingMatrix = item.PricingMatrix; opt.GroupType = item.GroupType; opt.IsGroup = item.GroupType == 'Group'; opt.GroupSize = item.GroupSize; opt.AgeBandInfo = item.AgeBandInfo; opt.HasPriceVariants = item.PricingMatrix.length > 1; count++; model.currentOptions.push(opt); } //$(".min-tickets-info").removeClass("min-tickets-info--timeout"); $(".action-list-selector__option-animation").removeClass("action-list-selector__option-animation--anim"); }, getActivityTotals: function (model) { model.edition.totalPrice = 0; model.edition.totalTickets = 0; model.edition.totalGroupTickets = 0; model.edition.hasAvailableAlone = false; model.edition.options = []; for (var idx in model.currentOptions) { var item = model.currentOptions[idx]; if (idx == 0) model.edition.currencyMask = item.CurrencyMask; var pricingMatrixOrdered = item.PricingMatrix.length > 1 ? item.PricingMatrix.sort(shApi.sortBy('MinTickets', false, parseInt)) : item.PricingMatrix; if (item.Num > 0) { //model.edition.totalTickets += item.Num; var numTickets = Math.trunc(((item.Num - 1) / item.GroupSize) + 1); var pm = pricingMatrixOrdered.find(function (pricingM) { return ((pricingM.MinTickets <= item.Num) && (pricingM.MaxTickets >= item.Num)); }); if (pm != undefined && pm != null) { model.edition.totalPrice += pm.PVP * numTickets; } //TODO: price group model.currentOptions[idx].UnitaryPrice = pm.PVP; model.edition.totalTickets += item.Num; if (model.edition.isGroup) { model.edition.totalGroupTickets = Math.ceil(model.edition.totalTickets / item.GroupSize); } var option = {}; option.Units = item.Num; option.Code = item.Code; option.GroupSize = item.GroupSize; option.LanguageCode = model.selectedLanguage != undefined && model.selectedLanguage != null ? model.selectedLanguage.languageCode : null; model.edition.options.push(option); if (item.AvailableAlone) model.edition.hasAvailableAlone = true; } else { //TODO: price group model.currentOptions[idx].UnitaryPrice = pricingMatrixOrdered[0].PVP; } }; }, deleteActivityOption: function (model, key) { $.each(model.currentOptions, function (idx, item) { if (item.Code == key) { if (item.Num == item.MinValue) item.Num = 0; else if (item.Num - 1 >= item.MinValue) item.Num--; return false; } }); }, addActivityOption: function (model, key) { $.each(model.currentOptions, function (idx, item) { if (item.Code == key) { if (item.Num == 0) item.Num = item.MinValue; else if (item.Num + 1 <= item.MaxValue) item.Num++; return false; } }); }, fillAdditionalMaterializedActivity: function (model) { var finalDate = null; if (model.edition.date != undefined && model.edition.date != null) { if (model.materializedData.AskTime) finalDate = shApi.getCompleteDateHour(model.edition.date, new Date(model.selectedTime.availableHour), 0); else finalDate = model.edition.date; } var p = {}; p.Options = model.edition.options; p.PickupSelected = this.fillAdditionalPickupActivity(model); //if (model.isFromList == true && model.materializedData.AskPickup == true) { // p.PickupSelected.DisplayName = model.materializedEdition.pickup.DisplayName; // p.PickupSelected.PlaceId = model.materializedEdition.pickup.PlaceId; // p.PickupSelected.FromOptionList = false; //} //else if (model.isFromList == false && model.materializedData.AskPickup == true) { // p.PickupSelected.DisplayName = model.materializedEdition.pickup.name; // p.PickupSelected.PlaceId = model.materializedEdition.pickup.id; // p.PickupSelectedFromOptionList = true; //} p.Date = finalDate != null ? shApi.serverUTCDateFromTime(finalDate.getTime()) : null; p.LanguageCode = model.selectedLanguage != undefined && model.selectedLanguage != null ? model.selectedLanguage.languageCode : null; p.PickupSelected = model.selectedPickup; p.Currency = model.currencyCode; return p; }, fillAdditionalPickupActivity: function (model) { var p = {}; if (model.showPickupToEnter == true && model.materializedData.AskPickup == true) { p.DisplayName = model.materializedEdition.pickup.DisplayName; p.PlaceId = model.materializedEdition.pickup.PlaceId; p.FromOptionList = false; } else if (model.showPickupToEnter == false && model.materializedData.AskPickup == true){ p.DisplayName = model.materializedEdition.pickup.name; p.PlaceId = model.materializedEdition.pickup.id; p.FromOptionList = true; } return p; }, toogleModalIframeDivs: function (showIframe) { if (showIframe == true) { $("#detailIframeLoading").fadeIn(1500).addClass("hidden"); $("#detailIframeDiv").css("visibility", "visible"); } else { $("#detailIframeLoading").removeClass("hidden"); $("#detailIframeDiv").css("visibility", "hidden"); } }, //Llamar en asíncrono on shown.bs.modal setIframeSourceAndHeight: function (src, listener, toogleModalIframeDivs ) { var iframeElement = document.getElementById("activityDetailIframeId"); // src = "https://localhost:44300/test.html"; //iframeElement.setAttribute('src', '_blank'); if (iframeElement.getAttribute('src') != src) { iframeElement.height = 50; window.CommunicationBridge.getInstance().attachMessageCalback(function (data) { //TODO: REMOVE When wordpress sends correct data //This function only should send broadcast try { if (data && data.messageType == window.MessageType.Resize) { console.log("adjust was fired by auto-adjust"); $rootScope.$broadcast("modalIframeResizeReady", true); return; } var increaseSize = 35; if (data == 1033) { increaseSize = 820; } var receivedHeight = 0; if ( data !== parseInt( data)) { if (data.collapse == true) { console.log("got collapse from event message "); increaseSize = 0; } receivedHeight = data.height; } else { receivedHeight = data; } receivedHeight = parseInt(receivedHeight); if (receivedHeight != NaN ) { var iframeElement = document.getElementById("activityDetailIframeId"); var height = receivedHeight + increaseSize; if (iframeElement) { iframeElement.height = height; } $rootScope.$broadcast("modalIframeResizeReady", true); } } catch(e) { console.error(e); } }); iframeElement.setAttribute("src", src); } else { toogleModalIframeDivs(true); } //OJO: Sólo peticiones en dominio , para otras realizar un post hacia el iframe //iframeElement.onload = function () { // asignamos el alto del iframe al alto de su contenido. // var iframe = this; // if (iframe) { // var iframeWin = iframe.contentWindow || iframe.contentDocument.parentWindow; // if (iframeWin.document.body) { // iframe.height = iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight; // var containerElement = document.getElementById('material-edition-modal'); // containerElement.style.height = iframe.height + "px"; // } // } //}; }, checkDisabledDates: function (date, mode, availableDates) { var isDisabled = false; if (availableDates != undefined && availableDates.length > 0) { if (mode === 'day') { var available = false; for (var i = 0; i < availableDates.length; i++) { if (areDatesEqual(availableDates[i], date)) { available = true; } } isDisabled = !available; } else isDisabled = false; } return isDisabled; }, //**Apartments**// initApartmentSliderList: function (model, group) { var idGroup = model.materializedList[group].GroupingCode; var elem = {}; elem = {}; elem.list = []; elem.listkeys = []; //para pantalla elem.groupingCode = idGroup; elem.groupingIndex = group; elem.currentMaterializedUid = model.materializedList[group].MaterializedServices[0].MaterializedUid; $.each(model.materializedList[group].MaterializedServices, function (idx, item) { var materialized = {}; materialized.fullDataLoaded = false; materialized.detail = {}; materialized.detail.Title = item.Title; materialized.detail.MainPicture = item.ImgUrl; materialized.detail.MainThumbnail = item.MainThumbnail; materialized.detail.ProviderLogoUrl = item.ProviderLogo; materialized.detail.DistanceToDetails = item.DistanceToDetails; materialized.detail.ApartmentCategory = item.ApartmentCategory; materialized.detail.Price = {}; materialized.detail.Price.PVP = item.Prices.FinalTotal; materialized.detail.Price.CurrencyMask = item.Prices.CurrencyMask; materialized.detail.AddressData = item.AddressData; elem.listkeys.push(item.MaterializedUid); elem.list[item.MaterializedUid] = materialized; }); model.currentApartmentList.push(elem); }, loadApartmentFullInfo: function (model, materializedUid, groupingCode, timeOut, alternativeProvider) { var currentRequestId = shApi.generateGuid(); model.lastApartmentRequestUid = currentRequestId; //1. Bucar índice en el grupo para encontrar el elemento var found = model.currentApartmentList.find(function (item) { return item.groupingCode == groupingCode; }); var idx = 0; if (found != null) { idx = model.currentApartmentList.indexOf(found); } var totalItems = model.currentApartmentList.length; var idListing = []; if (alternativeProvider != undefined && alternativeProvider) { idListing = [idx]; } else { //TODO: Improve if (idx - 2 >= 0) idListing.push(idx - 2); if (idx - 1 >= 0) idListing.push(idx - 1); idListing.push(idx); if (idx + 1 <= (totalItems - 1)) idListing.push(idx + 1); if (idx + 2 <= (totalItems - 1)) idListing.push(idx + 2); } var materializedIds = []; $.each(idListing, function (id, val) { var mainMaterialized = null; if (materializedUid != null && groupingCode == model.currentApartmentList[val].groupingCode) { mainMaterialized = materializedUid; } else { mainMaterialized = model.currentApartmentList[val].currentMaterializedUid; } model.currentApartmentList[val].currentMaterializedUid = mainMaterialized; if (!model.currentApartmentList[val].list[mainMaterialized].fullDataLoaded) { materializedIds.push(mainMaterialized); } }); var infoLoaded = false; if (model.currentApartmentList[model.currentIndexApartment].list[model.currentApartmentList[model.currentIndexApartment].currentMaterializedUid].fullDataLoaded) { model.currentSeasons = getSeasons(model.currentApartmentList[model.currentIndexApartment].list[model.currentApartmentList[model.currentIndexApartment].currentMaterializedUid], model.apartmentSlider.fromDate, model.apartmentSlider.toDate, model.apartmentSlider.people.numPeople, model); infoLoaded = true; } $timeout(function () { if (materializedIds.length > 0 && currentRequestId == model.lastApartmentRequestUid) { Purchases.getApartmentsFullInformation(JSON.stringify(materializedIds), model.prefix) .then(function (response) { model.lastResponse = response; $.each(idListing, function (id, val) { $.each(model.lastResponse, function (id, item) { if (model.currentApartmentList[val].list[model.lastResponse[id].MaterializedUid] != undefined) { model.currentApartmentList[val].list[model.lastResponse[id].MaterializedUid].error = model.lastResponse[id].Error; if (!model.lastResponse[id].Error) { model.currentApartmentList[val].list[model.lastResponse[id].MaterializedUid].detail = angular.copy(model.lastResponse[id].Detail); } model.currentApartmentList[val].list[model.lastResponse[id].MaterializedUid].fullDataLoaded = true; } }); }); //Filtro las seasons iniciales if (!infoLoaded) { model.currentSeasons = getSeasons(model.currentApartmentList[model.currentIndexApartment].list[model.currentApartmentList[model.currentIndexApartment].currentMaterializedUid], model.apartmentSlider.fromDate, model.apartmentSlider.toDate, model.apartmentSlider.people.numPeople,model); } model.sliderShow = true; }); } }, timeOut); }, fillAdditionalMaterializedApartment: function (model) { var p = {}; if (model.edition.fromDate != undefined && model.edition.fromDate != null) { p.FromDate = model.edition.fromDate; } if (model.edition.toDate != undefined && model.edition.toDate != null) { p.ToDate = model.edition.toDate; } if (model.edition.apartmentPrice != undefined && model.edition.apartmentPrice != null) { p.TargetPrice = model.edition.apartmentPrice; } p.Notes = model.edition.apartmentMsg; p.People = model.edition.people.numPeople; p.DetailedPeople ={}; p.DetailedPeople.Adults = model.edition.people.detailedPeople.adults; p.DetailedPeople.ChildrenUnderFive = model.edition.people.detailedPeople.childrenUnderFive; p.DetailedPeople.ChildrenUnderTwelve = model.edition.people.detailedPeople.childrenUnderTwelve; return p; }, getSeasonsFiltered: function (model, apartmentData, dateFrom, dateTo, numPeople) { return getSeasons(apartmentData, dateFrom, dateTo, numPeople, model); }, checkShowPickup: function (model, isReturn, isLoading) { if (!isReturn) { if ((model.materializedData.Details[0].FlightNumberOrigin && !model.showFlightConfirmationOrigin1) || (model.materializedData.Details[0].FlightNumberDestination && !model.showFlightConfirmationDestination1)) { model.showPîckup1 = false; } else { model.showPickup1 = true; } model.loadingPickup1 = isLoading; } //Vuelta else { if ((model.materializedData.Details[1].FlightNumberOrigin && !model.showFlightConfirmationOrigin2) || (model.materializedData.Details[1].FlightNumberDestination && !model.showFlightConfirmationDestination2)) { model.showPîckup2 = false; } else { model.showPickup2 = true; } model.loadingPickup2 = isLoading; } }, loadManualFlightInfo: function (model, current, number, departuredate, arrivaldate, iataDepartureDestination, iataArrivalDestination, iataDepartureOrigin, iataArrivalOrigin, isArrival) { if (current == "manualFlightInfoOrigin1" || current == "manualFlightInfoDestination1") { model.isManualOneWay = true; model.isManualReturn = false; } else if (current == "manualFlightInfoOrigin2" || current == "manualFlightInfoDestination2") { model.isManualOneWay = false; model.isManualReturn = true; } model.manualFlightInfo = {}; model.manualFlightInfo.current = current; model.manualFlightInfo.number = number != undefined ? number : null; model.manualFlightInfo.departuredateflight = model.manualFlightInfo.departuredate = model.manualFlightInfo.departurehour = departuredate; model.manualFlightInfo.arrivaldateflight = model.manualFlightInfo.arrivaldate = model.manualFlightInfo.arrivalhour = arrivaldate; model.manualFlightInfo.iataDepartureOrigin = iataDepartureOrigin != undefined ? iataDepartureOrigin : null; model.manualFlightInfo.iataArrivalOrigin = iataArrivalOrigin; model.manualFlightInfo.iataDepartureDestination = iataDepartureDestination; model.manualFlightInfo.iataArrivalDestination = iataArrivalDestination != undefined ? iataArrivalDestination : null; model.manualFlightInfo.isArrival = isArrival; }, } }]); })(); var ngSharedPurchase = angular.module("ngSharedCheckout", []); (function () { ngSharedPurchase.factory('shCheckout', ['$timeout', '$rootScope', '$window', '$location', 'CanonicalUrl', 'Translator', '$cookies', 'shApi', '$http', '$state', 'Purchases', function ($timeout, $rootScope, $window, $location, CanonicalUrl, Translator, $cookies, shApi, $http, $state, Purchases) { function addNewTraveller(scope, model, isMainTraveller, phoneCode) { var travellerId = shApi.generateGuid(); model.travellerInformation[travellerId] = {}; initTraveller(scope, model, travellerId, isMainTraveller, false, true, phoneCode); }; function initTraveller(scope, model, travellerId, isMainTraveller, isCustomer, isNew, phoneCode) { if (isMainTraveller) model.mainTravellerId = travellerId; model.travellerInformation[travellerId].name = null; model.travellerInformation[travellerId].surname = null; model.travellerInformation[travellerId].birthDate = null; model.travellerInformation[travellerId].age = null; model.travellerInformation[travellerId].mainTraveller = isMainTraveller; model.travellerInformation[travellerId].isCustomer = isCustomer; model.travellerInformation[travellerId].showAdditionalForm = false; model.travellerInformation[travellerId].showActivityForm = false; model.travellerInformation[travellerId].valid = false; if (isMainTraveller) { model.travellerInformation[travellerId].phoneCode = phoneCode != undefined ? phoneCode : null; model.travellerInformation[travellerId].phoneNumber = ''; model.travellerInformation[travellerId].email = null; //Init aditional data if (model.participantQuestions.mainTravellerQuestions != null) { if (model.participantQuestions.mainTravellerQuestions.fields != null && model.participantQuestions.mainTravellerQuestions.fields.length > 0) { model.travellerInformation[travellerId].travellerFields = angular.copy(model.participantQuestions.mainTravellerQuestions.fields); checkAndBindParticipantValues(scope, model, travellerId, isMainTraveller, true); } if (model.participantQuestions.mainTravellerQuestions.activityQuestions != null && model.participantQuestions.mainTravellerQuestions.activityQuestions.length > 0) { model.travellerInformation[travellerId].activityFields = angular.copy(model.participantQuestions.mainTravellerQuestions.activityQuestions); checkAndBindActivitiesParticipantValues(scope, model, travellerId); model.travellerInformation[travellerId].showActivityForm = true; } } } else { //Init aditional data if (model.participantQuestions.travellerQuestions != null) { if (model.participantQuestions.travellerQuestions.fields != null && model.participantQuestions.travellerQuestions.fields.length > 0) { model.travellerInformation[travellerId].travellerFields = angular.copy(model.participantQuestions.travellerQuestions.fields); checkAndBindParticipantValues(scope, model, travellerId, isMainTraveller, true); } if (model.participantQuestions.travellerQuestions.activityQuestions != null && model.participantQuestions.travellerQuestions.activityQuestions.length > 0) { model.travellerInformation[travellerId].activityFields = angular.copy(model.participantQuestions.travellerQuestions.activityQuestions); checkAndBindActivitiesParticipantValues(scope, model, travellerId); model.travellerInformation[travellerId].showActivityForm = true; } } } }; function checkAndBindParticipantValues(scope, model, travellerId, isMainTraveller) { var hidden = true; $.each(model.travellerInformation[travellerId].travellerFields, function (key, val) { model.travellerInformation[travellerId].travellerFields[key].hidden = false; model.travellerInformation[travellerId].travellerFields[key].required = true; switch (val.metatype) { case 'name': model.travellerInformation[travellerId].travellerFields[key].hidden = true; addNameWatcher(scope, model, travellerId, key); break; case 'surname': model.travellerInformation[travellerId].travellerFields[key].hidden = true; addSurnameWatcher(scope, model, travellerId, key); break; case 'leadtraveller': model.travellerInformation[travellerId].travellerFields[key].hidden = true; if (isMainTraveller) model.travellerInformation[travellerId].travellerFields[key].uservalue = true; else model.travellerInformation[travellerId].travellerFields[key].uservalue = false; break; case 'iscustomer': model.travellerInformation[travellerId].travellerFields[key].hidden = true; if (isMainTraveller) { //1st initialization if (model.mainTravellerCheck) { model.travellerInformation[travellerId].isCustomer = model.travellerInformation[travellerId].travellerFields[key].uservalue = true; model.customerId = travellerId; } else { model.travellerInformation[travellerId].isCustomer = model.travellerInformation[travellerId].travellerFields[key].uservalue = false; model.customerId = null; } } break; case 'email': if (isMainTraveller) { model.travellerInformation[travellerId].travellerFields[key].hidden = true; addEmailWatcher(scope, model, travellerId, key); } break; case 'phonenumber': if (isMainTraveller) { model.travellerInformation[travellerId].travellerFields[key].hidden = true; addPhoneWatcher(scope, model, travellerId, key); } break; case 'dateofbirth': model.travellerInformation[travellerId].travellerFields[key].hidden = true; addBirthdateWatcher(scope, model, travellerId, key); break; default: break; } if (hidden && !model.travellerInformation[travellerId].travellerFields[key].hidden) { hidden = false; } }); if (!hidden) { model.travellerInformation[travellerId].showAdditionalForm = true; } } function checkAndBindActivitiesParticipantValues(scope, model, travellerId) { $.each(model.travellerInformation[travellerId].activityFields, function (key, val) { $.each(val.fields, function (key2, val2) { model.travellerInformation[travellerId].activityFields[key].fields[key2].hidden = false; model.travellerInformation[travellerId].activityFields[key].fields[key2].required = true; }); }); } function addBirthdateWatcher(scope, model, travellerId, key) { scope.$watch('Model.travellerInformation["' + travellerId + '"].birthDate', function (newValue, oldValue) { if (newValue != oldValue) { if (newValue != undefined && newValue != null) { model.travellerInformation[travellerId].age = getAge(newValue); model.travellerInformation[travellerId].travellerFields[key].uservalue = newValue; } else { model.travellerInformation[travellerId].age = null; model.travellerInformation[travellerId].travellerFields[key].uservalue = null; } } }); } function addNameWatcher(scope, model, travellerId, key) { scope.$watch('Model.travellerInformation["' + travellerId + '"].name', function (newValue, oldValue) { if (newValue != oldValue) { if (newValue != undefined && newValue != null) { model.travellerInformation[travellerId].travellerFields[key].uservalue = newValue; } else { model.travellerInformation[travellerId].travellerFields[key].uservalue = null; } } }); } function addSurnameWatcher(scope, model, travellerId, key) { scope.$watch('Model.travellerInformation["' + travellerId + '"].surname', function (newValue, oldValue) { if (newValue != oldValue) { if(newValue != undefined && newValue != null) { model.travellerInformation[travellerId].travellerFields[key].uservalue = newValue; } else { model.travellerInformation[travellerId].travellerFields[key].uservalue = null; } } }); } function addEmailWatcher(scope, model, travellerId, key) { scope.$watch('Model.travellerInformation["' + travellerId + '"].email', function (newValue, oldValue) { if (newValue != oldValue) { if (newValue != undefined && newValue != null) { model.travellerInformation[travellerId].travellerFields[key].uservalue = newValue; } else { model.travellerInformation[travellerId].travellerFields[key].uservalue = null; } } }); } function addPhoneWatcher(scope, model, travellerId, key) { scope.$watch('Model.travellerInformation["' + travellerId + '"].phoneCode', function (newValue, oldValue) { if (newValue != oldValue) { if (newValue != undefined && newValue != null && model.travellerInformation[travellerId].phoneNumber != undefined && model.travellerInformation[travellerId].phoneNumber != null) { model.travellerInformation[travellerId].travellerFields[key].uservalue = model.travellerInformation[travellerId].phoneCode + '-' + model.travellerInformation[travellerId].phoneNumber; } else { model.travellerInformation[travellerId].travellerFields[key].uservalue = null; } } }); scope.$watch('Model.travellerInformation["' + travellerId + '"].phoneNumber', function (newValue, oldValue) { if (newValue != oldValue) { if (newValue != undefined && newValue != null && model.travellerInformation[travellerId].phoneCode != undefined && model.travellerInformation[travellerId].phoneCode != null) { model.travellerInformation[travellerId].travellerFields[key].uservalue = model.travellerInformation[travellerId].phoneCode + '-' + model.travellerInformation[travellerId].phoneNumber; } else { model.travellerInformation[travellerId].travellerFields[key].uservalue = null; } } }); } function getAge(dateString) { var today = new Date(); var birthDate = new Date(dateString); var age = today.getFullYear() - birthDate.getFullYear(); var m = today.getMonth() - birthDate.getMonth(); if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) { age--; } return age; }; function checkIfTravellerFormsAreInvalid(model, travellerId, index) { var error = false; var formMain = eval('model.travellerFormMain[' + index + ']'); if (formMain === undefined || !formMain.$valid) { error = true; } if (!error && model.travellerInformation[travellerId].showAdditionalForm) { var formAdditional = eval('model.travellerFormAdditional[' + index + ']'); if (formAdditional === undefined || !formAdditional.$valid) { error = true; } } if (!error && model.travellerInformation[travellerId].showActivityForm) { $.each(model.travellerInformation[travellerId].activityFields, function (idx, val) { var formActivity = eval('model.travellerFormActivity[' + index + '][' + idx + ']'); if (formActivity === undefined || !formActivity.$valid) { error = true; return; } }); } return error; } function goToStep(number, model) { switch (number) { case 1: model.showFirstStep = true; model.showFirstStepResume = false; model.showSecondStep = false; model.showSecondStepResume = false; model.showFinalButton = false; model.showAlertEditionModal = false; break; case 2: model.showSecondStep = true; model.showSecondStepResume = false; model.showFinalButton = false; break; } } function clearActivityTravellerSlotsAssociated(model, key) { Object.keys(model.activityList[key].options).forEach(function (key2) { $.each(model.activityList[key].options[key2].optionList, function (id, val) { model.activityList[key].options[key2].optionList[id].travellerKey = null; }); }); } return { initTravellers: function (scope, model, travelersNumber, phoneCode) { model.travellerInformation = {}; for (var i = 0; i < travelersNumber; i++) { addNewTraveller(scope, model, i == 0, phoneCode); } model.numberTravelersSelected = true; model.showTravellerErrors = false; }, addNewTraveller: function (scope, model, phoneCode) { addNewTraveller(scope, model, false, phoneCode); model.showTravellerData = Object.keys(model.travellerInformation).length - 1; //show last added }, getAge: function (dateString) { getAge(dateString); }, saveTravellerData: function(model, index, travellerId) { var error = checkIfTravellerFormsAreInvalid(model, travellerId, index); if (!error) { model.travellerInformation[travellerId].valid = true; model.showTravellerData = -1; model.continue = false; } else { model.travellerInformation[travellerId].valid = false; model.continue = true; window.setTimeout(function () { shApi.scrollToError(model.formClientData); }, 350); } }, clearTravellerData: function (scope, model, travellerId, phoneCode) { var isMainTraveller = model.travellerInformation[travellerId].mainTraveller; initTraveller(scope, model, travellerId, isMainTraveller, false, false, phoneCode); }, checkIfTravellerFormsAreInvalid: function (model, index, travellerId) { return checkIfTravellerFormsAreInvalid(model, index, travellerId); }, initializeActivityList: function (model, activityList) { model.activityList = {}; $.each(activityList, function (key, val) { model.activityList[val.groupingCode] = {}; model.activityList[val.groupingCode].title = val.title; model.activityList[val.groupingCode].imgUrl = val.imgUrl; model.activityList[val.groupingCode].totalSlots = 0; model.activityList[val.groupingCode].options = {}; var slotsCount = 0; $.each(val.options, function (key2, val2) { model.activityList[val.groupingCode].options[val2.code] = {}; model.activityList[val.groupingCode].options[val2.code].name = val2.name; model.activityList[val.groupingCode].options[val2.code].units = val2.units; model.activityList[val.groupingCode].options[val2.code].ageBandInfo = val2.ageBandInfo; model.activityList[val.groupingCode].options[val2.code].optionList = []; for (i = 0; i < val2.units; i++) { var opt = {}; opt.travellerKey = null; model.activityList[val.groupingCode].options[val2.code].optionList.push(opt); slotsCount = slotsCount + 1; } model.activityList[val.groupingCode].totalSlots = slotsCount; }); }); }, tryMatchTravellersWithSlotAuto: function (scope, model) { model.travellersTotalAdded = Object.keys(model.travellerInformation).length; //1. Clear all values: Object.keys(model.activityList).forEach(function (key) { clearActivityTravellerSlotsAssociated(model, key); }); //2. If travellers different from total calculated if (model.travellersTotalAdded == model.travellerNumberCalculated) { Object.keys(model.activityList).forEach(function (key) { if (model.activityList[key].totalSlots == model.travellersTotalAdded) { var ageBandRangeOptions = 0; // How many are of age band rg?? var totalOptions = 0; Object.keys(model.activityList[key].options).forEach(function (key2) { $.each(model.activityList[key].options[key2].optionList, function (id, val) { totalOptions++; var optionData = model.activityList[key].options[key2]; if (optionData.ageBandInfo != null && optionData.ageBandInfo.ageFrom != null && optionData.ageBandInfo.ageTo != null) { ageBandRangeOptions++; } }); }); var travellerOptionsAllowed = []; var allAgeBandOptions = ageBandRangeOptions == totalOptions; var allNoAgeBandOptions = ageBandRangeOptions == 0; if (allAgeBandOptions || (allNoAgeBandOptions && Object.keys(model.activityList[key].options).length == 1)) { Object.keys(model.activityList[key].options).forEach(function (key2) { Object.keys(model.travellerInformation).forEach(function (key3) { if (travellerOptionsAllowed[key3] == undefined || travellerOptionsAllowed[key3] == null) { travellerOptionsAllowed[key3] = []; } if (allAgeBandOptions) { var optionData = model.activityList[key].options[key2]; var ageTraveller = model.travellerInformation[key3].age; if (ageTraveller >= optionData.ageBandInfo.ageFrom && ageTraveller <= optionData.ageBandInfo.ageTo) { travellerOptionsAllowed[key3].push(key2); } } else { travellerOptionsAllowed[key3].push(key2); } }); }); var matched = 0; Object.keys(travellerOptionsAllowed).forEach(function (key4) { //Not allowed or allowed in more than one.. if (travellerOptionsAllowed[key4].length == 1) { var opt = travellerOptionsAllowed[key4][0]; $.each(model.activityList[key].options[opt].optionList, function (id, val) { if (model.activityList[key].options[opt].optionList[id].travellerKey == null) { model.activityList[key].options[opt].optionList[id].travellerKey = key4; matched++; return false; } }); } }); //Only fullfill if all options can be matched if (matched != totalOptions) { clearActivityTravellerSlotsAssociated(model, key); } } } }); } }, selectParticipant: function (model, activityKey, slotKey, participantIndex, travellerKey, btnId) { var participantData = model.activityList[activityKey].options[slotKey].optionList[participantIndex]; if (travellerKey !== participantData.travellerKey) { model.activityList[activityKey].options[slotKey].optionList[participantIndex].travellerKey = travellerKey; //Check duplicity and clear $.each(model.activityList[activityKey].options, function (slotKey2, slotValue2) { $.each(slotValue2.optionList, function (participantIndex2, participantValue2) { if (participantValue2.travellerKey == travellerKey && !(slotKey2 == slotKey && participantIndex2 == participantIndex)) { model.activityList[activityKey].options[slotKey2].optionList[participantIndex2].travellerKey = null; } }); }); } model.participantSelectorOpen[btnId] = false; }, setParticipantTravellerCustomer: function (model, isCustomer) { var travellerId = model.mainTravellerId; $.each(model.travellerInformation[travellerId].travellerFields, function (key, val) { if (val.metatype == "iscustomer") { if (isCustomer) { model.travellerInformation[travellerId].isCustomer = model.travellerInformation[travellerId].travellerFields[key].uservalue = true; model.customerId = travellerId; } else { model.travellerInformation[travellerId].isCustomer = model.travellerInformation[travellerId].travellerFields[key].uservalue = false; model.customerId = travellerId; } return; } }); }, fillBuyerDataWithTravellerData: function (model) { var mainTraveller = model.travellerInformation[model.mainTravellerId]; model.buyerData = {}; model.buyerData.email = mainTraveller.email; model.buyerData.name = mainTraveller.name; model.buyerData.surname = mainTraveller.surname; model.buyerData.phoneCode = mainTraveller.phoneCode; model.buyerData.phone = mainTraveller.phoneNumber; }, copyBuyerDataToParticipantAsClient: function (model) { var travellerId = model.customerId; model.travellerInformation[travellerId].name = model.buyerData.name; model.travellerInformation[travellerId].surname = model.buyerData.surname; //model.travellerInformation[travellerId].email = model.buyerData.email; //same as participant model.travellerInformation[travellerId].phoneCode = model.buyerData.phoneCode; model.travellerInformation[travellerId].phoneNumber = model.buyerData.phone; }, clearBuyerData: function (model, phoneCode) { model.buyerData = {}; model.buyerData.email = null; model.buyerData.name = null; model.buyerData.surname = null; model.buyerData.phoneCode = phoneCode != undefined ? phoneCode : null; model.buyerData.phone = null; }, saveParticipants: function (model) { model.showFinalButton = false; model.showParticipantsErrors = false; model.showAgeErrors = false; var totalNoValid = 0; var valid = true; $.each(model.activityList, function (activityKey, activityItem) { $.each(activityItem.options, function (slotKey, slotValue) { model.ageInfo = {}; model.ageInfo.ageFrom = slotValue.ageBandInfo.ageFrom; model.ageInfo.ageTo = slotValue.ageBandInfo.ageTo; $.each(slotValue.optionList, function (optionIndex, optionValue) { if (optionValue.travellerKey == undefined || optionValue.travellerKey == null || optionValue.travellerKey == '') { valid = false; model.showParticipantsErrors = true; model.showAgeErrors = true; totalNoValid += 1; } $.each(model.travellerInformation, function (travellerKey, travellerInfo) { if (optionValue.travellerKey == travellerKey) { if ((model.ageInfo.ageFrom == undefined || model.ageInfo.ageTo == undefined) || (travellerInfo.age >= model.ageInfo.ageFrom && travellerInfo.age <= model.ageInfo.ageTo)) { valid = true; } else { valid = false; model.showAgeErrors = true; model.showParticipantsErrors = true; totalNoValid += 1; } } }); }); }); }); if (totalNoValid == 0) { valid = true; model.showFinalButton = true; model.showSecondStep = false; model.showSecondStepResume = true; } else { valid = false; model.continue = true; } return valid; }, goToStep: function (number, model) { goToStep(number, model); }, editFirstStep: function (model) { if (model.participantsNeeded) { model.showAlertEditionModal = true; } else { goToStep(1, model); } }, getTravellersDataFormattedToUpload: function (model) { var travellersData = []; $.each(model.travellerInformation, function (key, val) { var traveller = {}; traveller.code = key; traveller.data = {}; traveller.data.code = key; traveller.data.name = val.name; traveller.data.surname = val.surname; traveller.data.birthDate = shApi.serverUTCDateFromTime(val.birthDate.getTime()); if (val.phoneCode != undefined && val.phoneCode != null && val.phoneNumber != undefined && val.phoneNumber != null) { traveller.data.phone = val.phoneCode + '-' + val.phoneNumber; } traveller.data.mainTraveller = val.mainTraveller; traveller.data.isCustomer = val.isCustomer; traveller.data.email = val.email; traveller.formValues = null; traveller.activityParticipantsData = null; if (val.travellerFields != undefined && val.travellerFields != null) { traveller.formValues = []; $.each(val.travellerFields, function (key3, val3) { var auxValue = {}; auxValue.key = key3; auxValue.datatype = val3.datatype; auxValue.metatype = val3.metatype; if (val3.datatype == 'date') auxValue.uservalue = shApi.serverUTCDateFromTime(val3.uservalue.getTime()); else auxValue.uservalue = val3.uservalue; traveller.formValues.push(auxValue); }); } if (val.activityFields != undefined && val.activityFields != null) { traveller.activityFormValues = []; $.each(val.activityFields, function (key2, val2) { var activity = {}; activity.groupingCode = val2.groupingCode; activity.formValues = []; $.each(val2.fields, function (key3, val3) { var auxValue = {}; auxValue.key = key3; auxValue.uservalue = val3.uservalue; auxValue.datatype = val3.datatype; auxValue.metatype = val3.metatype; activity.formValues.push(auxValue); }); traveller.activityFormValues.push(activity); }); } travellersData.push(traveller); }); return travellersData; }, getActivityTravellersMatchedFormattedToUpload: function (model) { var activityTravellers = []; $.each(model.activityList, function (key, val) { var activity = {}; activity.serviceGroup = key; activity.options = []; $.each(val.options, function (key2, val2) { var option = {}; option.code = key2; option.travellerKeys = []; for (i = 0; i < val2.optionList.length; i++) { option.travellerKeys.push(val2.optionList[i].travellerKey); } activity.options.push(option); }); activityTravellers.push(activity); }); return activityTravellers; }, } }]); })(); var ngDynamicFormRender = angular.module("ngDynamicFormRender", ['ngTranslator', 'ngSharedApi', 'ngResource', 'ui.bootstrap']); (function (Translator, shApi, $compile) { ngDynamicFormRender.directive('stDynamicFormRender', function ($parse, $compile, shApi) { var supportedTypes = ['string', 'boolean', 'integer', 'number', 'liststring', 'date', 'multioptionliststring', 'listinteger', 'multioptionlistinteger']; var parentForm = null; var newForm = null; var responsive = false; return { restrict: 'E', transclude: true, scope: { data: '=', formName: '=', culture: '=', continue: '=', arrayMessages: '=', }, link: function ($scope, element, attrs) { if (attrs.responsive !== undefined) responsive = (attrs.responsive === 'true' || attrs.responsive === ''); //Conversions $scope.internalModelDic = []; shApi.defineRequired($scope, $scope); newForm = angular.element('
'); //Build fields angular.forEach($scope.data, buildFields); $compile(newForm)($scope) element.append(newForm); function buildFields(field, idx) { if (shApi.checkIfContains(supportedTypes, field.datatype)) { var modelField = 'data[' + idx + ']'; var newElement = angular.element('
'); if (responsive) newElement.attr('class', 'field col-xs-12 col-md-6'); else newElement.attr('class', 'field'); newElement.attr('ng-if', '!' + modelField + '.hidden'); //Only show vald var fieldName = getFieldName(field); var fieldContainer = getFieldContainer(field, fieldName, modelField); var fieldDiv = getFieldDiv(field, fieldName); var errors = []; if (field.datatype === 'string') { var newFieldElem = ''; newElement.append(getLabelElem(field)); if (field.metatype === 'phonenumber') { $scope.internalModelDic[modelField] = {}; fieldDiv.addClass("content-input--phone"); //init (format 34-654354345) if ($scope.data[idx].uservalue != undefined && $scope.data[idx].uservalue != null) { var splitValue = $scope.data[idx].uservalue.split("-"); $scope.internalModelDic[modelField].uservalueaux = splitValue[0]; //phoneNumber $scope.internalModelDic[modelField].uservalue = splitValue[1]; //phoneCode } else { $scope.internalModelDic[modelField].uservalue = ''; $scope.internalModelDic[modelField].uservalueaux = null; } var phoneCodeSpan = angular.element('+{{internalModelDic[\'' + modelField + '\'].uservalueaux}}'); fieldDiv.prepend(phoneCodeSpan); newFieldElem = angular.element(''); addPhoneWatchers(modelField, fieldName); } //text label else if (field.metatype === 'starttime' || field.metatype === 'endtime' || field.metatype === 'inboundflighttime' || field.metatype === 'outboundflighttime') { fieldContainer.attr("ng-class", "{'focus': " + fieldName + "open }") $scope.internalModelDic[modelField] = {}; $scope.internalModelDic[modelField].uservalue = null; newFieldElem = angular.element(' ' ); if (field.required) { errors.push(getElementRequiredError(field, fieldName)); } //Convert dates if (field.range != null) { $scope.internalModelDic[modelField].range = {}; $scope.internalModelDic[modelField].range.Min = shApi.getUTCDateFromString(getMinRange(field)); $scope.internalModelDic[modelField].range.Max = shApi.getUTCDateFromString(getMaxRange(field)); newFieldElem.attr('min-date', 'internalModelDic["' + modelField + '"].range.Min'); newFieldElem.attr('max-date', 'internalModelDic["' + modelField + '"].range.Max'); errors.push(getElementMinDateError(field, fieldName)); errors.push(getElementMaxDateError(field, fieldName)); } if (field.uservalue != null) { $scope.internalModelDic[modelField].uservalue = shApi.getUTCDateFromString(field.uservalue); } fieldContainer.append(newFieldElem); //Add watchers (convert data format) addDateWatchers(modelField, fieldName); } else { newFieldElem = angular.element(''); newFieldElem.attr('name', fieldName); } addPatternValidationIfNeeded(newFieldElem, errors, field, fieldName); if (field.range != null) { newFieldElem.attr('ng-minlength', getMinRange(field)); newFieldElem.attr('ng-maxlength', getMaxRange(field)); errors.push(getElementMinLengthError(field, fieldName)); errors.push(getElementMaxLengthError(field, fieldName)); } if (field.required) { errors.push(getElementRequiredError(field, fieldName)); } fieldContainer.append(fieldDiv); fieldDiv.append(newFieldElem); } else if (field.datatype === 'integer' || field.datatype === 'number') { newElement.append(getLabelElem(field)); var newFieldElem = angular.element(''); newFieldElem.attr('name', fieldName); //Integer no decimals if (field.datatype === 'integer') { newFieldElem.attr('step', 1); newFieldElem.attr('ng-pattern', '^[1-9][0-9]*$'); } //Number -> default 2 decimals else { newFieldElem.attr('step', .01); newFieldElem.attr('ng-pattern', '/^[1-9][0-9]*(\.[0-9]{1,2})?$/') } errors.push(getElementPatternError(field, fieldName)); if (field.range != null) { newFieldElem.attr('min', getMinRange(field)); newFieldElem.attr('max', getMaxRange(field)); errors.push(getElementMinError(field, fieldName)); errors.push(getElementMaxError(field, fieldName)); } if (field.required) { errors.push(getElementRequiredError(field, fieldName)); } fieldContainer.append(fieldDiv); fieldDiv.append(newFieldElem); } else if (field.datatype === 'boolean') { var idcheckbox = 'checkbox-' + fieldName; var newFieldElem = angular.element(''); fieldContainer.append(fieldDiv); fieldDiv.append(newFieldElem); } else if (field.datatype === 'liststring' || field.datatype === 'listinteger') { newElement.append(getLabelElem(field)); //Only one selectionable option var defaultMsg = "--" + getMessage("list_default_option") + "--"; var newFieldElem = angular.element(''); if (field.required) { errors.push(getElementRequiredError(field, fieldName)); } fieldContainer.append(fieldDiv); fieldDiv.append(newFieldElem); } else if (field.datatype === 'date') { newElement.append(getLabelElem(field)); fieldContainer.attr("ng-class", "{'error': fieldHasError(" + getFormFieldRoute(fieldName) + "), 'focus': " + fieldName + "open }"); $scope.internalModelDic[modelField] = {}; $scope.internalModelDic[modelField].uservalue = null; var newFieldElem = angular.element(' ' ); if (field.required) { newFieldElem.attr('required', true); errors.push(getElementRequiredError(field, fieldName)); } //Convert dates if (field.range != null) { $scope.internalModelDic[modelField].range = {}; $scope.internalModelDic[modelField].range.Min = shApi.getUTCDateFromString(getMinRange(field)); $scope.internalModelDic[modelField].range.Max = shApi.getUTCDateFromString(getMaxRange(field)); newFieldElem.attr('min-date', 'internalModelDic["' + modelField + '"].range.Min'); newFieldElem.attr('max-date', 'internalModelDic["' + modelField + '"].range.Max'); errors.push(getElementMinDateError(field, fieldName)); errors.push(getElementMaxDateError(field, fieldName)); } if (field.uservalue != null) { $scope.internalModelDic[modelField].uservalue = shApi.getUTCDateFromString(field.uservalue); } fieldContainer.append(newFieldElem); //Add watchers (convert data format) addDateWatchers(modelField, fieldName); } else if (field.datatype == 'multioptionliststring' || field.datatype == 'multioptionlistinteger') { newElement.append(getLabelElem(field)); var divCheckbox = angular.element('
'); var contentCheckbox = angular.element('
'); var inputElement = angular.element(''); var labelElement = angular.element(''); contentCheckbox.append(inputElement); contentCheckbox.append(labelElement); divCheckbox.append(contentCheckbox); fieldContainer.append(fieldDiv); fieldDiv.append(divCheckbox); } if (fieldContainer != null) { newElement.append(fieldContainer); if (errors.length > 0) { newElement.append(errors); } newForm.append(newElement); } } }; //Messages and Translations function getMessage(key) { return $scope.arrayMessages[key]; }; //Create main element function getFieldName(field) { var name = field.key; return name; }; function getFieldContainer(field, fieldName) { var htmlcontainer = ''; if (field.datatype === 'boolean') { htmlcontainer = '
'; } else if (field.datatype === 'multioptionliststring' || field.datatype === 'multioptionlistinteger') { htmlcontainer = '
'; } else { htmlcontainer = '
'; } return angular.element(htmlcontainer); }; function getFieldDiv(field) { var htmlcontainer = ''; if (field.datatype === 'boolean') { htmlcontainer = '
'; } else if (field.datatype === 'multioptionliststring' || field.datatype === 'multioptionlistinteger') { htmlcontainer = '
'; } else { htmlcontainer = '
'; } return angular.element(htmlcontainer); }; function getLabelElem(field) { return angular.element('' + field.labelname + ''); }; function getFormFieldRoute(fieldName) { return 'formName.' + fieldName; } //Base error elem function getErrorElem(msg) { return angular.element('' + msg + ''); } function addPatternValidationIfNeeded(newFieldElem, errors, field, fieldName) { var patternError = null; var customMsg = null; switch (field.metatype) { case 'email': patternError = getMessage('email_pattern'); customMsg = getMessage('email_pattern_errormsg'); break; case 'phonenumber': patternError = getMessage('phone_pattern'); customMsg = getMessage('phone_pattern_errormsg'); break; } if (patternError != null) { newFieldElem.attr('ng-pattern', '/' + patternError + '/'); errors.push(getElementPatternError(field, fieldName, customMsg)); } } //////////////////////////////////////////////// //Watchers function addDateWatchers(modelField, fieldName) { $scope.$watch(modelField + '.uservalue', function (newValue, oldValue) { if (newValue !== oldValue) { if (newValue !== undefined && newValue !== null) { $scope.internalModelDic[modelField].uservalue = shApi.getUTCDateFromString(newValue); } else { $scope.internalModelDic[modelField].uservalue = null; } } }); $scope.$watch('internalModelDic[\'' + modelField + '\'].uservalue', function (newValue, oldValue) { var newValDate = newValue != undefined ? newValue.getTime() : newValue; var oldValDate = oldValue != undefined ? oldValue.getTime() : oldValue; var modelScope = $scope.$eval(modelField); if (newValDate !== oldValDate && modelScope !== undefined) { checkDateValidity(newValue, modelField, fieldName); if (newValDate !== undefined && newValDate !== null) { modelScope.uservalue = shApi.serverUTCDateFromTime(newValue); } else { modelScope.uservalue = null; } } }); } function addPhoneWatchers(modelField, fieldName) { $scope.$watch(modelField + '.uservalue', function (newValue, oldValue) { if (newValue !== oldValue) { if (newValue !== undefined && newValue !== null && newValue != '') { //Split values var splitValue = newValue.split("-"); $scope.internalModelDic[modelField].uservalueaux = splitValue[0]; $scope.internalModelDic[modelField].uservalue = splitValue[1]; } else { $scope.internalModelDic[modelField].uservalue = ''; $scope.internalModelDic[modelField].uservalueaux = undefined; } } }); $scope.$watch('internalModelDic[\'' + modelField + '\'].uservalue', function (newValue, oldValue) { if (newValue !== oldValue) { var modelScope = $scope.$eval(modelField); if (modelScope !== undefined) { var newVal = newValue == undefined || newValue == null ? '' : newValue; modelScope.uservalue = $scope.internalModelDic[modelField].uservalueaux + '-' + newVal; } } }); $scope.$watch('internalModelDic[\'' + modelField + '\'].uservalueaux', function (newValue, oldValue) { if (newValue !== oldValue) { var modelScope = $scope.$eval(modelField); if (modelScope !== undefined) { modelScope.uservalue = newValue + '-' + $scope.internalModelDic[modelField].uservalue; } } }); } ////////////////////////// //min-max validation function checkDateValidity(newDate, modelField, fieldName) { var route = $scope.$eval(getFormFieldRoute(fieldName)); if (route !== undefined && $scope.internalModelDic[modelField].range !== null) { if (newDate !== undefined && newDate !== null) { if (newDate > $scope.internalModelDic[modelField].range.Max) { route.$setValidity('maxdate', false); } else { route.$setValidity('maxdate', true); } if (newDate < $scope.internalModelDic[modelField].range.Min) { route.$setValidity('mindate', false); } else { route.$setValidity('mindate', true); } } else { route.$setValidity('maxdate', true); route.$setValidity('mindate', true); } } }; ////////////////////////////////////////////////// //Error elements function getElementRequiredError(field, fieldName) { var el = getErrorElem(getMessage('required_default_error')); return el.attr('ng-class', '{\'show\': fieldRequiredError(' + getFormFieldRoute(fieldName) + ')}'); }; function getElementPatternError(field, fieldName, customMsg) { var msg = customMsg !== undefined && customMsg != null ? customMsg : getMessage('pattern_default_error'); var el = getErrorElem(msg); return el.attr('ng-class', '{\'show\': fieldPatternError(' + getFormFieldRoute(fieldName) + ')}'); }; function getElementMinLengthError(field, fieldName) { var el = getErrorElem(getMessage('min_length_default_error') + ": " + getMinRange(field)); return el.attr('ng-class', '{\'show\': fieldMinLengthError(' + getFormFieldRoute(fieldName) + ')}'); }; function getElementMaxLengthError(field, fieldName) { var el = getErrorElem(getMessage('max_length_default_error') + ": " + getMaxRange(field)); return el.attr('ng-class', '{\'show\': fieldMaxLengthError(' + getFormFieldRoute(fieldName) + ')}'); }; function getElementMinError(field, fieldName) { var el = getErrorElem(getMessage('min_default_error') + ": " + getMinRange(field)); return el.attr('ng-class', '{\'show\': fieldMinError(' + getFormFieldRoute(fieldName) + ')}'); }; function getElementMaxError(field, fieldName) { var el = getErrorElem(getMessage('max_default_error') + ": " + getMaxRange(field)); return el.attr('ng-class', '{\'show\': fieldMaxError(' + getFormFieldRoute(fieldName) + ')}'); }; function getElementMinDateError(field, fieldName) { var el = getErrorElem(getMessage('min_date_default_error')); return el.attr('ng-class', '{\'show\': fieldDateMinError(' + getFormFieldRoute(fieldName) + ')}'); }; function getElementMaxDateError(field, fieldName) { var el = getErrorElem(getMessage('max_date_default_error')); return el.attr('ng-class', '{\'show\': fieldDateMaxError(' + getFormFieldRoute(fieldName) + ')}'); }; function getMinRange(field) { if (field.range.Min != undefined) { return field.range.Min; } if (field.range.min != undefined) { return field.range.min; } } function getMaxRange(field) { if (field.range.Max != undefined) { return field.range.Max; } if (field.range.max != undefined) { return field.range.max; } } //Validation errors $scope.fieldHasError = function (field) { if (field !== undefined) { return $scope.hasError(field); } }; $scope.fieldRequiredError = function (field) { if (field !== undefined) { return $scope.requiredError(field); } }; $scope.fieldPatternError = function (field) { if (field !== undefined) { return $scope.patternError(field); } }; $scope.fieldMinLengthError = function (field) { if (field !== undefined) { return $scope.minlengthError(field); } }; $scope.fieldMaxLengthError = function (field) { if (field !== undefined) { return $scope.maxlengthError(field); } }; $scope.fieldMinError = function (field) { if (field !== undefined) { return $scope.minError(field); } }; $scope.fieldMaxError = function (field) { if (field !== undefined) { return $scope.maxError(field); } }; $scope.fieldDateMinError = function (field) { if (field !== undefined) { return $scope.mindateError(field); } }; $scope.fieldDateMaxError = function (field) { if (field !== undefined) { return $scope.maxdateError(field); } }; }, }; }); })(); /* jQuery UI Slider plugin wrapper */ angular.module('ui.slider', []).value('uiSliderConfig', {}).directive('uiSlider', ['uiSliderConfig', '$timeout', function (uiSliderConfig, $timeout) { uiSliderConfig = uiSliderConfig || {}; return { require: 'ngModel', compile: function () { var preLink = function (scope, elm, attrs, ngModel) { function parseNumber(n, decimals) { return (decimals) ? parseFloat(n) : parseInt(n, 10); } var directiveOptions = angular.copy(scope.$eval(attrs.uiSlider)); var options = angular.extend(directiveOptions || {}, uiSliderConfig); // Object holding range values var prevRangeValues = { min: null, max: null }; // convenience properties var properties = ['min', 'max', 'step', 'lowerBound', 'upperBound']; var useDecimals = (!angular.isUndefined(attrs.useDecimals)) ? true : false; var updateOn = (angular.isDefined(options['updateOn'])) ? options['updateOn'] : 'slide' var init = function () { // When ngModel is assigned an array of values then range is expected to be true. // Warn user and change range to true else an error occurs when trying to drag handle if (angular.isArray(ngModel.$viewValue) && options.range !== true) { console.warn('Change your range option of ui-slider. When assigning ngModel an array of values then the range option should be set to true.'); options.range = true; } // Ensure the convenience properties are passed as options if they're defined // This avoids init ordering issues where the slider's initial state (eg handle // position) is calculated using widget defaults // Note the properties take precedence over any duplicates in options angular.forEach(properties, function (property) { if (angular.isDefined(attrs[property])) { options[property] = parseNumber(attrs[property], useDecimals); } }); elm.slider(options); init = angular.noop; }; // Find out if decimals are to be used for slider angular.forEach(properties, function (property) { // support {{}} and watch for updates attrs.$observe(property, function (newVal) { if (!!newVal) { init(); options[property] = parseNumber(newVal, useDecimals); elm.slider('option', property, parseNumber(newVal, useDecimals)); ngModel.$render(); } }); }); attrs.$observe('disabled', function (newVal) { init(); elm.slider('option', 'disabled', !!newVal); }); // Watch ui-slider (byVal) for changes and update scope.$watch(attrs.uiSlider, function (newVal) { init(); if (newVal !== undefined) { elm.slider('option', newVal); } }, true); // Late-bind to prevent compiler clobbering $timeout(init, 0, true); // Update model value from slider elm.bind(updateOn, function (event, ui) { var valuesChanged; if (ui.values) { var boundedValues = ui.values.slice(); if (options.lowerBound && boundedValues[0] < options.lowerBound) { boundedValues[0] = Math.max(boundedValues[0], options.lowerBound); } if (options.upperBound && boundedValues[1] > options.upperBound) { boundedValues[1] = Math.min(boundedValues[1], options.upperBound); } if (boundedValues[0] !== ui.values[0] || boundedValues[1] !== ui.values[1]) { valuesChanged = true; ui.values = boundedValues; } } else { var boundedValue = ui.value; if (options.lowerBound && boundedValue < options.lowerBound) { boundedValue = Math.max(boundedValue, options.lowerBound); } if (options.upperBound && boundedValue > options.upperBound) { boundedValue = Math.min(boundedValue, options.upperBound); } if (boundedValue !== ui.value) { valuesChanged = true; ui.value = boundedValue; } } ngModel.$setViewValue(ui.values || ui.value); $(ui.handle).find('.ui-slider-tip').text(ui.value); scope.$apply(); if (valuesChanged) { setTimeout(function () { elm.slider('value', ui.values || ui.value); }, 0); return false; } }); // Update slider from model value ngModel.$render = function () { init(); var method = options.range === true ? 'values' : 'value'; if (options.range !== true && isNaN(ngModel.$viewValue) && !(ngModel.$viewValue instanceof Array)) { ngModel.$viewValue = 0; } else if (options.range && !angular.isDefined(ngModel.$viewValue)) { ngModel.$viewValue = [0, 0]; } // Do some sanity check of range values if (options.range === true) { // previously, the model was a string b/c it was in a text input, need to convert to a array. // make sure input exists, comma exists once, and it is a string. if (ngModel.$viewValue && angular.isString(ngModel.$viewValue) && (ngModel.$viewValue.match(/,/g) || []).length === 1) { // transform string model into array. var valueArr = ngModel.$viewValue.split(','); ngModel.$viewValue = [Number(valueArr[0]), Number(valueArr[1])]; } // Check outer bounds for min and max values if (angular.isDefined(options.min) && options.min > ngModel.$viewValue[0]) { ngModel.$viewValue[0] = options.min; } if (angular.isDefined(options.max) && options.max < ngModel.$viewValue[1]) { ngModel.$viewValue[1] = options.max; } // Check min and max range values if (ngModel.$viewValue[0] > ngModel.$viewValue[1]) { // Min value should be less to equal to max value if (prevRangeValues.min >= ngModel.$viewValue[1]) { ngModel.$viewValue[1] = prevRangeValues.min; } // Max value should be less to equal to min value if (prevRangeValues.max <= ngModel.$viewValue[0]) { ngModel.$viewValue[0] = prevRangeValues.max; } } // Store values for later user prevRangeValues.min = ngModel.$viewValue[0]; prevRangeValues.max = ngModel.$viewValue[1]; } elm.slider(method, ngModel.$viewValue); }; scope.$watch(attrs.ngModel, function () { if (options.range === true) { ngModel.$render(); $(elm).find('.ui-slider-tip').each(function (i, tipElm) { $(tipElm).text(ngModel.$viewValue[i]); }); } else { $(elm).find('.ui-slider-tip').text(ngModel.$viewValue); } }, true); function destroy() { if (elm.hasClass('ui-slider')) { elm.slider('destroy'); } } scope.$on("$destroy", destroy); elm.one('$destroy', destroy); }; var postLink = function (scope, element, attrs, ngModel) { // Add tick marks if 'tick' and 'step' attributes have been setted on element. // Support horizontal slider bar so far. 'tick' and 'step' attributes are required. var options = angular.extend({}, scope.$eval(attrs.uiSlider)); var properties = ['min', 'max', 'step', 'tick', 'tip']; angular.forEach(properties, function (property) { if (angular.isDefined(attrs[property])) { options[property] = attrs[property]; } }); if (angular.isDefined(options['tick']) && angular.isDefined(options['step'])) { var total = parseInt((parseInt(options['max']) - parseInt(options['min'])) / parseInt(options['step'])); for (var i = total; i >= 0; i--) { var left = ((i / total) * 100) + '%'; $("
").addClass("ui-slider-tick").appendTo(element).css({ left: left }); }; } if (angular.isDefined(options['tip'])) { $timeout(function () { var handles = element.find('.ui-slider-handle'); if (handles && handles.length > 1 && ngModel.$viewValue && angular.isArray(ngModel.$viewValue)) { $(handles[0]).append('
' + ngModel.$viewValue[0] + '
'); $(handles[1]).append('
' + ngModel.$viewValue[1] + '
'); } else { element.find('.ui-slider-handle').append('
' + ngModel.$viewValue + '
'); } }, 10); } } return { pre: preLink, post: postLink }; } }; }]); angular.module('ngLazyImage', []); angular.module('ngLazyImage') .service('afklSrcSetService', ['$window', function ($window) { 'use strict'; /** * For other applications wanting the srccset/best image approach it is possible to use this module only * Loosely based on https://raw.github.com/borismus/srcset-polyfill/master/js/srcset-info.js */ var INT_REGEXP = /^[0-9]+$/; // SRCSET IMG OBJECT function ImageInfo(options) { this.src = options.src; this.w = options.w || Infinity; this.h = options.h || Infinity; this.x = options.x || 1; } /** * Parse srcset rules * @param {string} descString Containing all srcset rules * @return {object} Srcset rules */ var _parseDescriptors = function (descString) { var descriptors = descString.split(/\s/); var out = {}; for (var i = 0, l = descriptors.length; i < l; i++) { var desc = descriptors[i]; if (desc.length > 0) { var lastChar = desc.slice(-1); var value = desc.substring(0, desc.length - 1); var intVal = parseInt(value, 10); var floatVal = parseFloat(value); if (value.match(INT_REGEXP) && lastChar === 'w') { out[lastChar] = intVal; } else if (value.match(INT_REGEXP) && lastChar === 'h') { out[lastChar] = intVal; } else if (!isNaN(floatVal) && lastChar === 'x') { out[lastChar] = floatVal; } } } return out; }; /** * Returns best candidate under given circumstances * @param {object} images Candidate image * @param {function} criteriaFn Rule * @return {object} Returns best candidate under given criteria */ var _getBestCandidateIf = function (images, criteriaFn) { var bestCandidate = images[0]; for (var i = 0, l = images.length; i < l; i++) { var candidate = images[i]; if (criteriaFn(candidate, bestCandidate)) { bestCandidate = candidate; } } return bestCandidate; }; /** * Remove candidate under given circumstances * @param {object} images Candidate image * @param {function} criteriaFn Rule * @return {object} Removes images from global image collection (candidates) */ var _removeCandidatesIf = function (images, criteriaFn) { for (var i = images.length - 1; i >= 0; i--) { var candidate = images[i]; if (criteriaFn(candidate)) { images.splice(i, 1); // remove it } } return images; }; /** * Direct implementation of "processing the image candidates": * http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#processing-the-image-candidates * * @param {array} imageCandidates (required) * @param {object} view (optional) * @returns {ImageInfo} The best image of the possible candidates. */ var getBestImage = function (imageCandidates, view) { if (!imageCandidates) { return; } if (!view) { view = { 'w': $window.innerWidth || document.documentElement.clientWidth, 'h': $window.innerHeight || document.documentElement.clientHeight, 'x': $window.devicePixelRatio || 1 }; } var images = imageCandidates.slice(0); /* LARGEST */ // Width var largestWidth = _getBestCandidateIf(images, function (a, b) { return a.w > b.w; }); // Less than client width. _removeCandidatesIf(images, (function () { return function (a) { return a.w < view.w; }; })(this)); // If none are left, keep the one with largest width. if (images.length === 0) { images = [largestWidth]; } // Height var largestHeight = _getBestCandidateIf(images, function (a, b) { return a.h > b.h; }); // Less than client height. _removeCandidatesIf(images, (function () { return function (a) { return a.h < view.h; }; })(this)); // If none are left, keep one with largest height. if (images.length === 0) { images = [largestHeight]; } // Pixel density. var largestPxDensity = _getBestCandidateIf(images, function (a, b) { return a.x > b.x; }); // Remove all candidates with pxdensity less than client pxdensity. _removeCandidatesIf(images, (function () { return function (a) { return a.x < view.x; }; })(this)); // If none are left, keep one with largest pixel density. if (images.length === 0) { images = [largestPxDensity]; } /* SMALLEST */ // Width var smallestWidth = _getBestCandidateIf(images, function (a, b) { return a.w < b.w; }); // Remove all candidates with width greater than it. _removeCandidatesIf(images, function (a) { return a.w > smallestWidth.w; }); // Height var smallestHeight = _getBestCandidateIf(images, function (a, b) { return a.h < b.h; }); // Remove all candidates with height greater than it. _removeCandidatesIf(images, function (a) { return a.h > smallestHeight.h; }); // Pixel density var smallestPxDensity = _getBestCandidateIf(images, function (a, b) { return a.x < b.x; }); // Remove all candidates with pixel density less than smallest px density. _removeCandidatesIf(images, function (a) { return a.x > smallestPxDensity.x; }); return images[0]; }; // options {src: null/string, srcset: string} // options.src normal url or null // options.srcset 997-s.jpg 480w, 997-m.jpg 768w, 997-xl.jpg 1x var getSrcset = function (options) { var imageCandidates = []; var srcValue = options.src; var srcsetValue = options.srcset; if (!srcsetValue) { return; } /* PUSH CANDIDATE [{src: _, x: _, w: _, h:_}, ...] */ var _addCandidate = function (img) { for (var j = 0, ln = imageCandidates.length; j < ln; j++) { var existingCandidate = imageCandidates[j]; // DUPLICATE if (existingCandidate.x === img.x && existingCandidate.w === img.w && existingCandidate.h === img.h) { return; } } imageCandidates.push(img); }; var _parse = function () { var input = srcsetValue, position = 0, rawCandidates = [], url, descriptors; while (input !== '') { while (input.charAt(0) === ' ') { input = input.slice(1); } position = input.indexOf(' '); if (position !== -1) { url = input.slice(0, position); // if (url === '') { break; } input = input.slice(position + 1); position = input.indexOf(','); if (position === -1) { descriptors = input; input = ''; } else { descriptors = input.slice(0, position); input = input.slice(position + 1); } rawCandidates.push({ url: url, descriptors: descriptors }); } else { rawCandidates.push({ url: input, descriptors: '' }); input = ''; } } // FROM RAW CANDIDATES PUSH IMAGES TO COMPLETE SET for (var i = 0, l = rawCandidates.length; i < l; i++) { var candidate = rawCandidates[i], desc = _parseDescriptors(candidate.descriptors); _addCandidate(new ImageInfo({ src: candidate.url, x: desc.x, w: desc.w, h: desc.h })); } if (srcValue) { _addCandidate(new ImageInfo({ src: srcValue })); } }; _parse(); // Return best available image for current view based on our list of candidates var bestImage = getBestImage(imageCandidates); /** * Object returning best match at moment, and total collection of candidates (so 'image' API can be used by consumer) * @type {Object} */ var object = { 'best': bestImage, // IMAGE INFORMATION WHICH FITS BEST WHEN API IS REQUESTED 'candidates': imageCandidates // ALL IMAGE CANDIDATES BY GIVEN SRCSET ATTRIBUTES }; // empty collection imageCandidates = null; // pass best match and candidates return object; }; // throttle function to be used in directive function throttle(callback, delay) { var last, deferTimer; return function () { var now = +new Date(); if (last && now < last + delay) { clearTimeout(deferTimer); deferTimer = setTimeout(function () { last = now; callback(); }, delay + last - now); } else { last = now; callback(); } }; } /** * PUBLIC API */ return { get: getSrcset, // RETURNS BEST IMAGE AND IMAGE CANDIDATES image: getBestImage, // RETURNS BEST IMAGE WITH GIVEN CANDIDATES throttle: throttle // RETURNS A THROTTLER FUNCTION }; }]); angular.module('ngLazyImage') .directive('afklImageContainer', function () { 'use strict'; return { restrict: 'A', // We have to use controller instead of link here so that it will always run earlier than nested afklLazyImage directives controller: ['$scope', '$element', function ($scope, $element) { $element.data('afklImageContainer', $element); }] }; }) .directive('afklLazyImage', ['$rootScope', '$window', '$timeout', 'afklSrcSetService', '$parse', function ($rootScope, $window, $timeout, srcSetService, $parse) { 'use strict'; // Use srcSetService to find out our best available image var bestImage = function (images) { var image = srcSetService.get({ srcset: images }); var sourceUrl; if (image) { sourceUrl = image.best.src; } return sourceUrl; }; return { restrict: 'A', link: function (scope, element, attrs) { var _concatImgAttrs = function (imgAttrs) { var result = []; var CLASSNAME = 'afkl-lazy-image'; var setClass = false; if (!!options.imgAttrs) { result = Array.prototype.map.call(imgAttrs, function (item) { for (var key in item) { if (item.hasOwnProperty(key)) { // TODO: TITLE CAN COME LATER (FROM DATA MODEL) var value = item[key]; if (key === 'class') { setClass = true; value = value + ' ' + CLASSNAME; } return String.prototype.concat.call(key, '="', value, '"'); } } }); } if (!setClass) { result.push('class="' + CLASSNAME + '"'); } return result.join(' '); }; // CONFIGURATION VARS var $container = element.inheritedData('afklImageContainer'); if (!$container) { $container = angular.element(attrs.afklLazyImageContainer || $window); } var loaded = false; var timeout; var images = attrs.afklLazyImage; // srcset attributes var options = attrs.afklLazyImageOptions ? $parse(attrs.afklLazyImageOptions)(scope) : {}; // options (background, offset) var img = null; // Angular element to image which will be placed var currentImage = null; // current image url var offset = options.offset ? options.offset : 50; // default offset var imgAttrs = _concatImgAttrs(options.imgAttrs); // all image attributes like class, title, onerror var LOADING = 'afkl-lazy-image-loading'; attrs.afklLazyImageLoaded = false; var _containerScrollTop = function () { // See if we can use jQuery, with extra check // TODO: check if number is returned if ($container.scrollTop) { var scrollTopPosition = $container.scrollTop(); if (scrollTopPosition) { return scrollTopPosition; } } var c = $container[0]; if (c.pageYOffset !== undefined) { return c.pageYOffset; } else if (c.scrollTop !== undefined) { return c.scrollTop; } return document.documentElement.scrollTop || 0; }; var _containerInnerHeight = function () { if ($container.innerHeight) { return $container.innerHeight(); } var c = $container[0]; if (c.innerHeight !== undefined) { return c.innerHeight; } else if (c.clientHeight !== undefined) { return c.clientHeight; } return document.documentElement.clientHeight || 0; }; // Begin with offset and update on resize var _elementOffset = function () { if (element.offset) { return element.offset().top; } var box = element[0].getBoundingClientRect(); return box.top + _containerScrollTop() - document.documentElement.clientTop; }; var _elementOffsetContainer = function () { if (element.offset) { return element.offset().top - $container.offset().top; } return element[0].getBoundingClientRect().top - $container[0].getBoundingClientRect().top; }; // Update url of our image var _setImage = function () { if (options.background) { element[0].style.backgroundImage = 'url("' + currentImage + '")'; } else if (!!img) { img[0].src = currentImage; } }; // Append image to DOM var _placeImage = function () { loaded = true; // What is my best image available var hasImage = bestImage(images); if (hasImage) { // we have to make an image if background is false (default) if (!options.background) { if (!img) { element.addClass(LOADING); img = angular.element(''); img.one('load', _loaded); img.one('error', _error); // remove loading class when image is acually loaded element.append(img); } } // set correct src/url _checkIfNewImage(); } // Element is added to dom, no need to listen to scroll anymore $container.off('scroll', _onViewChange); }; // Check on resize if actually a new image is best fit, if so then apply it var _checkIfNewImage = function () { if (loaded) { var newImage = bestImage(images); if (newImage !== currentImage) { // update current url currentImage = newImage; // TODO: loading state... // update image url _setImage(); } } }; // First update our begin offset _checkIfNewImage(); var _loaded = function () { attrs.$set('afklLazyImageLoaded', 'done'); element.removeClass(LOADING); }; var _error = function () { attrs.$set('afklLazyImageLoaded', 'fail'); }; // Check if the container is in view for the first time. Utilized by the scroll and resize events. var _onViewChange = function () { // only do stuff when not set already if (!loaded) { // Config vars var remaining, shouldLoad, windowBottom; var height = _containerInnerHeight(); var scroll = _containerScrollTop(); var elOffset = $container[0] === $window ? _elementOffset() : _elementOffsetContainer(); windowBottom = $container[0] === $window ? height + scroll : height; remaining = elOffset - windowBottom; // Is our top of our image container in bottom of our viewport? //console.log($container[0].className, _elementOffset(), _elementPosition(), height, scroll, remaining, elOffset); shouldLoad = remaining <= offset; // Append image first time when it comes into our view, after that only resizing can have influence if (shouldLoad) { _placeImage(); } } }; var _onViewChangeThrottled = srcSetService.throttle(_onViewChange, 300); // EVENT: RESIZE THROTTLED var _onResize = function () { $timeout.cancel(timeout); timeout = $timeout(function () { _checkIfNewImage(); _onViewChange(); }, 300); }; // Remove events for total destroy var _eventsOff = function () { $timeout.cancel(timeout); angular.element($window).off('resize', _onResize); angular.element($window).off('scroll', _onViewChangeThrottled); if ($container[0] !== $window) { $container.off('resize', _onResize); $container.off('scroll', _onViewChangeThrottled); } // remove image being placed if (img) { img.remove(); } img = timeout = currentImage = undefined; }; // set events for scrolling and resizing on window // even if container is not window it is important // to cover two cases: // - when container size is bigger than window's size // - when container's side is out of initial window border angular.element($window).on('resize', _onResize); angular.element($window).on('scroll', _onViewChangeThrottled); // if container is not window, set events for container as well if ($container[0] !== $window) { $container.on('resize', _onResize); $container.on('scroll', _onViewChangeThrottled); } // events for image change attrs.$observe('afklLazyImage', function () { images = attrs.afklLazyImage; if (loaded) { _placeImage(); } }); // Image should be directly placed if (options.nolazy) { _placeImage(); } scope.$on('afkl.lazyImage.destroyed', _onResize); // Remove all events when destroy takes place scope.$on('$destroy', function () { // tell our other kids, i got removed $rootScope.$broadcast('afkl.lazyImage.destroyed'); // remove our events and image return _eventsOff(); }); return _onViewChange(); } }; }]); /* AngularJS v1.4.5 (c) 2010-2015 Google, Inc. http://angularjs.org License: MIT */ (function(x,s,y){'use strict';function t(f,k,p){n.directive(f,["$parse","$swipe",function(c,e){return function(l,m,g){function h(a){if(!b)return!1;var d=Math.abs(a.y-b.y);a=(a.x-b.x)*k;return r&&75>d&&0d/a}var d=c(g[f]),b,r,a=["touch"];s.isDefined(g.ngSwipeDisableMouse)||a.push("mouse");e.bind(m,{start:function(a,d){b=a;r=!0},cancel:function(a){r=!1},end:function(a,b){h(a)&&l.$apply(function(){m.triggerHandler(p);d(l,{$event:b})})}},a)}}])}var n=s.module("ngTouch",[]);n.factory("$swipe", [function(){function f(c){c=c.originalEvent||c;var e=c.touches&&c.touches.length?c.touches:[c];c=c.changedTouches&&c.changedTouches[0]||e[0];return{x:c.clientX,y:c.clientY}}function k(c,e){var l=[];s.forEach(c,function(c){(c=p[c][e])&&l.push(c)});return l.join(" ")}var p={mouse:{start:"mousedown",move:"mousemove",end:"mouseup"},touch:{start:"touchstart",move:"touchmove",end:"touchend",cancel:"touchcancel"}};return{bind:function(c,e,l){var m,g,h,d,b=!1;l=l||["mouse","touch"];c.on(k(l,"start"),function(a){h= f(a);b=!0;g=m=0;d=h;e.start&&e.start(h,a)});var r=k(l,"cancel");if(r)c.on(r,function(a){b=!1;e.cancel&&e.cancel(a)});c.on(k(l,"move"),function(a){if(b&&h){var c=f(a);m+=Math.abs(c.x-d.x);g+=Math.abs(c.y-d.y);d=c;10>m&&10>g||(g>m?(b=!1,e.cancel&&e.cancel(a)):(a.preventDefault(),e.move&&e.move(c,a)))}});c.on(k(l,"end"),function(a){b&&(b=!1,e.end&&e.end(f(a),a))})}}}]);n.config(["$provide",function(f){f.decorator("ngClickDirective",["$delegate",function(k){k.shift();return k}])}]);n.directive("ngClick", ["$parse","$timeout","$rootElement",function(f,k,p){function c(d,b,c){for(var a=0;aMath.abs(d[a]-b)&&25>Math.abs(e-g))return d.splice(a,a+2),!0}return!1}function e(d){if(!(2500e&&1>b||h&&h[0]===e&&h[1]===b)){h&&(h=null);var a=d.target;"label"===s.lowercase(a.nodeName||a[0]&&a[0].nodeName)&&(h=[e,b]);c(g,e,b)||(d.stopPropagation(),d.preventDefault(),d.target&& d.target.blur&&d.target.blur())}}}function l(d){d=d.touches&&d.touches.length?d.touches:[d];var b=d[0].clientX,c=d[0].clientY;g.push(b,c);k(function(){for(var a=0;ad&&12>w&&(g||(p[0].addEventListener("click",e,!0),p[0].addEventListener("touchstart",l,!0),g=[]),m=Date.now(),c(g,f,u),q&&q.blur(),s.isDefined(h.disabled)&& !1!==h.disabled||b.triggerHandler("click",[a]));k=!1;b.removeClass("ng-click-active")});b.onclick=function(a){};b.on("click",function(b,c){d.$apply(function(){a(d,{$event:c||b})})});b.on("mousedown",function(a){b.addClass("ng-click-active")});b.on("mousemove mouseup",function(a){b.removeClass("ng-click-active")})}}]);t("ngSwipeLeft",-1,"swipeleft");t("ngSwipeRight",1,"swiperight")})(window,window.angular); //# sourceMappingURL=angular-touch.min.js.map /*global angular */ /* Angular touch carousel with CSS GPU accel and slide buffering http://github.com/revolunet/angular-carousel */ angular.module('angular-carousel', [ 'ngTouch', 'angular-carousel.shifty' ]); angular.module('angular-carousel') .directive('rnCarouselAutoSlide', ['$interval', function($interval) { return { restrict: 'A', link: function (scope, element, attrs) { var stopAutoPlay = function() { if (scope.autoSlider) { $interval.cancel(scope.autoSlider); scope.autoSlider = null; } }; var restartTimer = function() { scope.autoSlide(); }; scope.$watch('carouselIndex', restartTimer); if (attrs.hasOwnProperty('rnCarouselPauseOnHover') && attrs.rnCarouselPauseOnHover !== 'false'){ element.on('mouseenter', stopAutoPlay); element.on('mouseleave', restartTimer); } scope.$on('$destroy', function(){ stopAutoPlay(); element.off('mouseenter', stopAutoPlay); element.off('mouseleave', restartTimer); }); } }; }]); angular.module('angular-carousel') .directive('rnCarouselIndicators', ['$parse', function($parse) { return { restrict: 'A', scope: { slides: '=', index: '=rnCarouselIndex', visibleItems: '=' }, templateUrl: 'carousel-indicators.html', link: function(scope, iElement, iAttributes) { var indexModel = $parse(iAttributes.rnCarouselIndex); scope.goToSlide = function(index) { indexModel.assign(scope.$parent.$parent, index); }; } }; }]); angular.module('angular-carousel').run(['$templateCache', function($templateCache) { $templateCache.put('carousel-indicators.html', '' ); }]); (function() { "use strict"; angular.module('angular-carousel') .service('DeviceCapabilities', function() { // TODO: merge in a single function // detect supported CSS property function detectTransformProperty() { var transformProperty = 'transform', safariPropertyHack = 'webkitTransform'; if (typeof document.body.style[transformProperty] !== 'undefined') { ['webkit', 'moz', 'o', 'ms'].every(function (prefix) { var e = '-' + prefix + '-transform'; if (typeof document.body.style[e] !== 'undefined') { transformProperty = e; return false; } return true; }); } else if (typeof document.body.style[safariPropertyHack] !== 'undefined') { transformProperty = '-webkit-transform'; } else { transformProperty = undefined; } return transformProperty; } //Detect support of translate3d function detect3dSupport() { var el = document.createElement('p'), has3d, transforms = { 'webkitTransform': '-webkit-transform', 'msTransform': '-ms-transform', 'transform': 'transform' }; // Add it to the body to get the computed style document.body.insertBefore(el, null); for (var t in transforms) { if (el.style[t] !== undefined) { el.style[t] = 'translate3d(1px,1px,1px)'; has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]); } } document.body.removeChild(el); return (has3d !== undefined && has3d.length > 0 && has3d !== "none"); } return { has3d: detect3dSupport(), transformProperty: detectTransformProperty() }; }) .service('computeCarouselSlideStyle', function(DeviceCapabilities) { // compute transition transform properties for a given slide and global offset return function(slideIndex, offset, transitionType) { var style = { display: 'inline-block' }, opacity, absoluteLeft = (slideIndex * 100) + offset, slideTransformValue = DeviceCapabilities.has3d ? 'translate3d(' + absoluteLeft + '%, 0, 0)' : 'translate3d(' + absoluteLeft + '%, 0)', distance = ((100 - Math.abs(absoluteLeft)) / 100); if (!DeviceCapabilities.transformProperty) { // fallback to default slide if transformProperty is not available style['margin-left'] = absoluteLeft + '%'; } else { if (transitionType == 'fadeAndSlide') { style[DeviceCapabilities.transformProperty] = slideTransformValue; opacity = 0; if (Math.abs(absoluteLeft) < 100) { opacity = 0.3 + distance * 0.7; } style.opacity = opacity; } else if (transitionType == 'hexagon') { var transformFrom = 100, degrees = 0, maxDegrees = 60 * (distance - 1); transformFrom = offset < (slideIndex * -100) ? 100 : 0; degrees = offset < (slideIndex * -100) ? maxDegrees : -maxDegrees; style[DeviceCapabilities.transformProperty] = slideTransformValue + ' ' + 'rotateY(' + degrees + 'deg)'; style[DeviceCapabilities.transformProperty + '-origin'] = transformFrom + '% 50%'; } else if (transitionType == 'zoom') { style[DeviceCapabilities.transformProperty] = slideTransformValue; var scale = 1; if (Math.abs(absoluteLeft) < 100) { scale = 1 + ((1 - distance) * 2); } style[DeviceCapabilities.transformProperty] += ' scale(' + scale + ')'; style[DeviceCapabilities.transformProperty + '-origin'] = '50% 50%'; opacity = 0; if (Math.abs(absoluteLeft) < 100) { opacity = 0.3 + distance * 0.7; } style.opacity = opacity; } else { style[DeviceCapabilities.transformProperty] = slideTransformValue; } } return style; }; }) .service('createStyleString', function() { return function(object) { var styles = []; angular.forEach(object, function(value, key) { styles.push(key + ':' + value); }); return styles.join(';'); }; }) .directive('rnCarousel', ['$swipe', '$window', '$document', '$parse', '$compile', '$timeout', '$interval', 'computeCarouselSlideStyle', 'createStyleString', 'Tweenable', function($swipe, $window, $document, $parse, $compile, $timeout, $interval, computeCarouselSlideStyle, createStyleString, Tweenable) { // internal ids to allow multiple instances var carouselId = 0, // in absolute pixels, at which distance the slide stick to the edge on release rubberTreshold = 3; var requestAnimationFrame = $window.requestAnimationFrame || $window.webkitRequestAnimationFrame || $window.mozRequestAnimationFrame; function getItemIndex(collection, target, defaultIndex) { var result = defaultIndex; collection.every(function(item, index) { if (angular.equals(item, target)) { result = index; return false; } return true; }); return result; } return { restrict: 'A', scope: true, compile: function(tElement, tAttributes) { // use the compile phase to customize the DOM var firstChild = tElement[0].querySelector('li'), firstChildAttributes = (firstChild) ? firstChild.attributes : [], isRepeatBased = false, isBuffered = false, repeatItem, repeatCollection; // try to find an ngRepeat expression // at this point, the attributes are not yet normalized so we need to try various syntax ['ng-repeat', 'data-ng-repeat', 'ng:repeat', 'x-ng-repeat'].every(function(attr) { var repeatAttribute = firstChildAttributes[attr]; if (angular.isDefined(repeatAttribute)) { // ngRepeat regexp extracted from angular 1.2.7 src var exprMatch = repeatAttribute.value.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/), trackProperty = exprMatch[3]; repeatItem = exprMatch[1]; repeatCollection = exprMatch[2]; if (repeatItem) { if (angular.isDefined(tAttributes['rnCarouselBuffered'])) { // update the current ngRepeat expression and add a slice operator if buffered isBuffered = true; repeatAttribute.value = repeatItem + ' in ' + repeatCollection + '|carouselSlice:carouselBufferIndex:carouselBufferSize'; if (trackProperty) { repeatAttribute.value += ' track by ' + trackProperty; } } isRepeatBased = true; return false; } } return true; }); return function(scope, iElement, iAttributes, containerCtrl) { carouselId++; var defaultOptions = { transitionType: iAttributes.rnCarouselTransition || 'slide', transitionEasing: iAttributes.rnCarouselEasing || 'easeTo', transitionDuration: parseInt(iAttributes.rnCarouselDuration, 10) || 300, isSequential: true, autoSlideDuration: 3, bufferSize: 5, /* in container % how much we need to drag to trigger the slide change */ moveTreshold: 0.1, defaultIndex: 0 }; // TODO var options = angular.extend({}, defaultOptions); var pressed, startX, isIndexBound = false, offset = 0, destination, swipeMoved = false, //animOnIndexChange = true, currentSlides = [], elWidth = null, elX = null, animateTransitions = true, intialState = true, animating = false, mouseUpBound = false, locked = false; //rn-swipe-disabled =true will only disable swipe events if(iAttributes.rnSwipeDisabled !== "true") { $swipe.bind(iElement, { start: swipeStart, move: swipeMove, end: swipeEnd, cancel: function(event) { swipeEnd({}, event); } }); } function getSlidesDOM() { var nodes = iElement[0].childNodes; var slides = []; for(var i=0; i currentSlides.length - 1) { index = 0; } if (!locked) { goToSlide(index, slideOptions); } }; scope.prevSlide = function(slideOptions) { var index = scope.carouselIndex - 1; if (index < 0) { index = currentSlides.length - 1; } goToSlide(index, slideOptions); }; function goToSlide(index, slideOptions) { //console.log('goToSlide', arguments); // move a to the given slide index if (index === undefined) { index = scope.carouselIndex; } slideOptions = slideOptions || {}; if (slideOptions.animate === false || options.transitionType === 'none') { locked = false; offset = index * -100; scope.carouselIndex = index; updateBufferIndex(); return; } locked = true; var tweenable = new Tweenable(); tweenable.tween({ from: { 'x': offset }, to: { 'x': index * -100 }, duration: options.transitionDuration, easing: options.transitionEasing, step: function(state) { if (isFinite(state.x)) { updateSlidesPosition(state.x); } }, finish: function() { scope.$apply(function() { scope.carouselIndex = index; offset = index * -100; updateBufferIndex(); $timeout(function () { locked = false; }, 0, false); }); } }); } scope.mouseLeave = function (selectControl) { $('.item.active .preview-' + selectControl).addClass("out"); $('.item.active .preview-' + selectControl).removeClass("in"); $('.item.active .preview-' + selectControl).css('display', 'none'); } scope.mouseEnter = function (selectControl) { $('.item.active .preview-' + selectControl).css('display', 'flex'); setTimeout(function () { $('.item.active .preview-' + selectControl).removeClass("out"); $('.item.active .preview-' + selectControl).addClass("in"); }, 10); } function getContainerWidth() { var rect = iElement[0].getBoundingClientRect(); return rect.width ? rect.width : rect.right - rect.left; } function updateContainerWidth() { elWidth = getContainerWidth(); } function bindMouseUpEvent() { if (!mouseUpBound) { mouseUpBound = true; $document.bind('mouseup', documentMouseUpEvent); } } function unbindMouseUpEvent() { if (mouseUpBound) { mouseUpBound = false; $document.unbind('mouseup', documentMouseUpEvent); } } function swipeStart(coords, event) { // console.log('swipeStart', coords, event); if (locked || currentSlides.length <= 1) { return; } updateContainerWidth(); elX = iElement[0].querySelector('li').getBoundingClientRect().left; pressed = true; startX = coords.x; return false; } function swipeMove(coords, event) { //console.log('swipeMove', coords, event); var x, delta; bindMouseUpEvent(); if (pressed) { x = coords.x; delta = startX - x; if (delta > 2 || delta < -2) { swipeMoved = true; var moveOffset = offset + (-delta * 100 / elWidth); updateSlidesPosition(moveOffset); } } return false; } var init = true; scope.carouselIndex = 0; if (!isRepeatBased) { // fake array when no ng-repeat currentSlides = []; angular.forEach(getSlidesDOM(), function(node, index) { currentSlides.push({id: index}); }); if (iAttributes.rnCarouselHtmlSlides) { var updateParentSlides = function(value) { slidesModel.assign(scope.$parent, value); }; var slidesModel = $parse(iAttributes.rnCarouselHtmlSlides); if (angular.isFunction(slidesModel.assign)) { /* check if this property is assignable then watch it */ scope.$watch('htmlSlides', function(newValue) { updateParentSlides(newValue); }); scope.$parent.$watch(slidesModel, function(newValue, oldValue) { if (newValue !== undefined && newValue !== null) { newValue = 0; updateParentIndex(newValue); } }); } scope.htmlSlides = currentSlides; } } if (iAttributes.rnCarouselControls !== undefined) { if (iAttributes.previewitems !== undefined) { var previewItemsPrev = 'ng-mouseleave="mouseLeave(\'prev\')" ng-mouseover="mouseEnter(\'prev\')"'; var previewItemsNext = 'ng-mouseleave="mouseLeave(\'next\')" ng-mouseover="mouseEnter(\'next\')"'; } else { var previewItemsPrev = ''; var previewItemsNext = ''; } if (iAttributes.visibleItems !== undefined) { var visibleItems = iAttributes.visibleItems; } else { var visibleItems = 1; } var textPrev = ''; var textNext = ''; var loadingControl = false; if (iAttributes.textPrev !== undefined) textPrev = iAttributes.textPrev; if (iAttributes.textNext != undefined) textNext = iAttributes.textNext; if (iAttributes.rnCarouselLoading != undefined) loadingControl = iAttributes.rnCarouselLoading; var canloop = ((isRepeatBased ? scope.$eval(repeatCollection.replace('::', '')).length : currentSlides.length) > 1) ? angular.isDefined(tAttributes['rnCarouselControlsAllowLoop']) : false; var nextSlideIndexCompareValue = isRepeatBased ? '(' + repeatCollection.replace('::', '') + ').length - ' + visibleItems : currentSlides.length - visibleItems; var tpl = ''; iElement.parent().append($compile(angular.element(tpl))(scope)); } if (iAttributes.rnCarouselAutoSlide!==undefined) { var duration = parseInt(iAttributes.rnCarouselAutoSlide, 10) || options.autoSlideDuration; scope.autoSlide = function() { if (scope.autoSlider) { $interval.cancel(scope.autoSlider); scope.autoSlider = null; } scope.autoSlider = $interval(function() { if (!locked && !pressed) { scope.nextSlide(); } }, duration * 1000); }; } if (iAttributes.rnCarouselDefaultIndex) { var defaultIndexModel = $parse(iAttributes.rnCarouselDefaultIndex); options.defaultIndex = defaultIndexModel(scope.$parent) || 0; } if (iAttributes.rnCarouselIndex) { var updateParentIndex = function(value) { indexModel.assign(scope.$parent, value); }; var indexModel = $parse(iAttributes.rnCarouselIndex); if (angular.isFunction(indexModel.assign)) { /* check if this property is assignable then watch it */ scope.$watch('carouselIndex', function(newValue) { updateParentIndex(newValue); }); scope.$parent.$watch(indexModel, function(newValue, oldValue) { if (newValue !== undefined && newValue !== null) { if (currentSlides && currentSlides.length > 0 && newValue >= currentSlides.length) { newValue = currentSlides.length - 1; updateParentIndex(newValue); } else if (currentSlides && newValue < 0) { newValue = 0; updateParentIndex(newValue); } if (!locked) { goToSlide(newValue, { animate: !init }); } init = false; } }); isIndexBound = true; if (options.defaultIndex) { goToSlide(options.defaultIndex, { animate: !init }); } } else if (!isNaN(iAttributes.rnCarouselIndex)) { /* if user just set an initial number, set it */ goToSlide(parseInt(iAttributes.rnCarouselIndex, 10), { animate: false }); } } else { goToSlide(options.defaultIndex, { animate: !init }); init = false; } if (iAttributes.rnCarouselLocked) { scope.$watch(iAttributes.rnCarouselLocked, function(newValue, oldValue) { // only bind swipe when it's not switched off if(newValue === true) { locked = true; } else { locked = false; } }); } if (isRepeatBased) { // use rn-carousel-deep-watch to fight the Angular $watchCollection weakness : https://github.com/angular/angular.js/issues/2621 // optional because it have some performance impacts (deep watch) var deepWatch = (iAttributes.rnCarouselDeepWatch!==undefined); scope[deepWatch?'$watch':'$watchCollection'](repeatCollection, function(newValue, oldValue) { //console.log('repeatCollection', currentSlides); currentSlides = newValue; // if deepWatch ON ,manually compare objects to guess the new position if (!angular.isArray(currentSlides)) { throw Error('the slides collection must be an Array'); } if (deepWatch && angular.isArray(newValue)) { var activeElement = oldValue[scope.carouselIndex]; var newIndex = getItemIndex(newValue, activeElement, scope.carouselIndex); goToSlide(newIndex, {animate: false}); } else { goToSlide(scope.carouselIndex, {animate: false}); } }, true); } function swipeEnd(coords, event, forceAnimation) { // console.log('swipeEnd', 'scope.carouselIndex', scope.carouselIndex); // Prevent clicks on buttons inside slider to trigger "swipeEnd" event on touchend/mouseup // console.log(iAttributes.rnCarouselOnInfiniteScroll); if (event && !swipeMoved) { return; } unbindMouseUpEvent(); pressed = false; swipeMoved = false; destination = startX - coords.x; if (destination===0) { return; } if (locked) { return; } offset += (-destination * 100 / elWidth); if (options.isSequential) { var minMove = options.moveTreshold * elWidth, absMove = -destination, slidesMove = -Math[absMove >= 0 ? 'ceil' : 'floor'](absMove / elWidth), shouldMove = Math.abs(absMove) > minMove; if (currentSlides && (slidesMove + scope.carouselIndex) >= currentSlides.length) { slidesMove = currentSlides.length - 1 - scope.carouselIndex; } if ((slidesMove + scope.carouselIndex) < 0) { slidesMove = -scope.carouselIndex; } var moveOffset = shouldMove ? slidesMove : 0; destination = (scope.carouselIndex + moveOffset); goToSlide(destination); if(iAttributes.rnCarouselOnInfiniteScrollRight!==undefined && slidesMove === 0 && scope.carouselIndex !== 0) { $parse(iAttributes.rnCarouselOnInfiniteScrollRight)(scope) goToSlide(0); } if(iAttributes.rnCarouselOnInfiniteScrollLeft!==undefined && slidesMove === 0 && scope.carouselIndex === 0 && moveOffset === 0) { $parse(iAttributes.rnCarouselOnInfiniteScrollLeft)(scope) goToSlide(currentSlides.length); } } else { scope.$apply(function() { scope.carouselIndex = parseInt(-offset / 100, 10); updateBufferIndex(); }); } } scope.$on('$destroy', function() { unbindMouseUpEvent(); }); scope.carouselBufferIndex = 0; scope.carouselBufferSize = options.bufferSize; function updateBufferIndex() { // update and cap te buffer index var bufferIndex = 0; var bufferEdgeSize = (scope.carouselBufferSize - 1) / 2; if (isBuffered) { if (scope.carouselIndex <= bufferEdgeSize) { // first buffer part bufferIndex = 0; } else if (currentSlides && currentSlides.length < scope.carouselBufferSize) { // smaller than buffer bufferIndex = 0; } else if (currentSlides && scope.carouselIndex > currentSlides.length - scope.carouselBufferSize) { // last buffer part bufferIndex = currentSlides.length - scope.carouselBufferSize; } else { // compute buffer start bufferIndex = scope.carouselIndex - bufferEdgeSize; } scope.carouselBufferIndex = bufferIndex; $timeout(function() { updateSlidesPosition(offset); }, 0, false); } else { $timeout(function() { updateSlidesPosition(offset); }, 0, false); } } function onOrientationChange() { updateContainerWidth(); goToSlide(); } // handle orientation change var winEl = angular.element($window); winEl.bind('orientationchange', onOrientationChange); winEl.bind('resize', onOrientationChange); scope.$on('$destroy', function() { unbindMouseUpEvent(); winEl.unbind('orientationchange', onOrientationChange); winEl.unbind('resize', onOrientationChange); }); }; } }; } ]); })(); angular.module('angular-carousel.shifty', []) .factory('Tweenable', function() { /*! shifty - v1.3.4 - 2014-10-29 - http://jeremyckahn.github.io/shifty */ ;(function (root) { /*! * Shifty Core * By Jeremy Kahn - jeremyckahn@gmail.com */ var Tweenable = (function () { 'use strict'; // Aliases that get defined later in this function var formula; // CONSTANTS var DEFAULT_SCHEDULE_FUNCTION; var DEFAULT_EASING = 'linear'; var DEFAULT_DURATION = 500; var UPDATE_TIME = 1000 / 60; var _now = Date.now ? Date.now : function () {return +new Date();}; var now = typeof SHIFTY_DEBUG_NOW !== 'undefined' ? SHIFTY_DEBUG_NOW : _now; if (typeof window !== 'undefined') { // requestAnimationFrame() shim by Paul Irish (modified for Shifty) // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ DEFAULT_SCHEDULE_FUNCTION = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || (window.mozCancelRequestAnimationFrame && window.mozRequestAnimationFrame) || setTimeout; } else { DEFAULT_SCHEDULE_FUNCTION = setTimeout; } function noop () { // NOOP! } /*! * Handy shortcut for doing a for-in loop. This is not a "normal" each * function, it is optimized for Shifty. The iterator function only receives * the property name, not the value. * @param {Object} obj * @param {Function(string)} fn */ function each (obj, fn) { var key; for (key in obj) { if (Object.hasOwnProperty.call(obj, key)) { fn(key); } } } /*! * Perform a shallow copy of Object properties. * @param {Object} targetObject The object to copy into * @param {Object} srcObject The object to copy from * @return {Object} A reference to the augmented `targetObj` Object */ function shallowCopy (targetObj, srcObj) { each(srcObj, function (prop) { targetObj[prop] = srcObj[prop]; }); return targetObj; } /*! * Copies each property from src onto target, but only if the property to * copy to target is undefined. * @param {Object} target Missing properties in this Object are filled in * @param {Object} src */ function defaults (target, src) { each(src, function (prop) { if (typeof target[prop] === 'undefined') { target[prop] = src[prop]; } }); } /*! * Calculates the interpolated tween values of an Object for a given * timestamp. * @param {Number} forPosition The position to compute the state for. * @param {Object} currentState Current state properties. * @param {Object} originalState: The original state properties the Object is * tweening from. * @param {Object} targetState: The destination state properties the Object * is tweening to. * @param {number} duration: The length of the tween in milliseconds. * @param {number} timestamp: The UNIX epoch time at which the tween began. * @param {Object} easing: This Object's keys must correspond to the keys in * targetState. */ function tweenProps (forPosition, currentState, originalState, targetState, duration, timestamp, easing) { var normalizedPosition = (forPosition - timestamp) / duration; var prop; for (prop in currentState) { if (currentState.hasOwnProperty(prop)) { currentState[prop] = tweenProp(originalState[prop], targetState[prop], formula[easing[prop]], normalizedPosition); } } return currentState; } /*! * Tweens a single property. * @param {number} start The value that the tween started from. * @param {number} end The value that the tween should end at. * @param {Function} easingFunc The easing curve to apply to the tween. * @param {number} position The normalized position (between 0.0 and 1.0) to * calculate the midpoint of 'start' and 'end' against. * @return {number} The tweened value. */ function tweenProp (start, end, easingFunc, position) { return start + (end - start) * easingFunc(position); } /*! * Applies a filter to Tweenable instance. * @param {Tweenable} tweenable The `Tweenable` instance to call the filter * upon. * @param {String} filterName The name of the filter to apply. */ function applyFilter (tweenable, filterName) { var filters = Tweenable.prototype.filter; var args = tweenable._filterArgs; each(filters, function (name) { if (typeof filters[name][filterName] !== 'undefined') { filters[name][filterName].apply(tweenable, args); } }); } var timeoutHandler_endTime; var timeoutHandler_currentTime; var timeoutHandler_isEnded; var timeoutHandler_offset; /*! * Handles the update logic for one step of a tween. * @param {Tweenable} tweenable * @param {number} timestamp * @param {number} duration * @param {Object} currentState * @param {Object} originalState * @param {Object} targetState * @param {Object} easing * @param {Function(Object, *, number)} step * @param {Function(Function,number)}} schedule */ function timeoutHandler (tweenable, timestamp, duration, currentState, originalState, targetState, easing, step, schedule) { timeoutHandler_endTime = timestamp + duration; timeoutHandler_currentTime = Math.min(now(), timeoutHandler_endTime); timeoutHandler_isEnded = timeoutHandler_currentTime >= timeoutHandler_endTime; timeoutHandler_offset = duration - ( timeoutHandler_endTime - timeoutHandler_currentTime); if (tweenable.isPlaying() && !timeoutHandler_isEnded) { tweenable._scheduleId = schedule(tweenable._timeoutHandler, UPDATE_TIME); applyFilter(tweenable, 'beforeTween'); tweenProps(timeoutHandler_currentTime, currentState, originalState, targetState, duration, timestamp, easing); applyFilter(tweenable, 'afterTween'); step(currentState, tweenable._attachment, timeoutHandler_offset); } else if (timeoutHandler_isEnded) { step(targetState, tweenable._attachment, timeoutHandler_offset); tweenable.stop(true); } } /*! * Creates a usable easing Object from either a string or another easing * Object. If `easing` is an Object, then this function clones it and fills * in the missing properties with "linear". * @param {Object} fromTweenParams * @param {Object|string} easing */ function composeEasingObject (fromTweenParams, easing) { var composedEasing = {}; if (typeof easing === 'string') { each(fromTweenParams, function (prop) { composedEasing[prop] = easing; }); } else { each(fromTweenParams, function (prop) { if (!composedEasing[prop]) { composedEasing[prop] = easing[prop] || DEFAULT_EASING; } }); } return composedEasing; } /** * Tweenable constructor. * @param {Object=} opt_initialState The values that the initial tween should start at if a "from" object is not provided to Tweenable#tween. * @param {Object=} opt_config See Tweenable.prototype.setConfig() * @constructor */ function Tweenable (opt_initialState, opt_config) { this._currentState = opt_initialState || {}; this._configured = false; this._scheduleFunction = DEFAULT_SCHEDULE_FUNCTION; // To prevent unnecessary calls to setConfig do not set default configuration here. // Only set default configuration immediately before tweening if none has been set. if (typeof opt_config !== 'undefined') { this.setConfig(opt_config); } } /** * Configure and start a tween. * @param {Object=} opt_config See Tweenable.prototype.setConfig() * @return {Tweenable} */ Tweenable.prototype.tween = function (opt_config) { if (this._isTweening) { return this; } // Only set default config if no configuration has been set previously and none is provided now. if (opt_config !== undefined || !this._configured) { this.setConfig(opt_config); } this._timestamp = now(); this._start(this.get(), this._attachment); return this.resume(); }; /** * Sets the tween configuration. `config` may have the following options: * * - __from__ (_Object=_): Starting position. If omitted, the current state is used. * - __to__ (_Object=_): Ending position. * - __duration__ (_number=_): How many milliseconds to animate for. * - __start__ (_Function(Object)_): Function to execute when the tween begins. Receives the state of the tween as the first parameter. Attachment is the second parameter. * - __step__ (_Function(Object, *, number)_): Function to execute on every tick. Receives the state of the tween as the first parameter. Attachment is the second parameter, and the time elapsed since the start of the tween is the third parameter. This function is not called on the final step of the animation, but `finish` is. * - __finish__ (_Function(Object, *)_): Function to execute upon tween completion. Receives the state of the tween as the first parameter. Attachment is the second parameter. * - __easing__ (_Object|string=_): Easing curve name(s) to use for the tween. * - __attachment__ (_Object|string|any=_): Value that is attached to this instance and passed on to the step/start/finish methods. * @param {Object} config * @return {Tweenable} */ Tweenable.prototype.setConfig = function (config) { config = config || {}; this._configured = true; // Attach something to this Tweenable instance (e.g.: a DOM element, an object, a string, etc.); this._attachment = config.attachment; // Init the internal state this._pausedAtTime = null; this._scheduleId = null; this._start = config.start || noop; this._step = config.step || noop; this._finish = config.finish || noop; this._duration = config.duration || DEFAULT_DURATION; this._currentState = config.from || this.get(); this._originalState = this.get(); this._targetState = config.to || this.get(); // Aliases used below var currentState = this._currentState; var targetState = this._targetState; // Ensure that there is always something to tween to. defaults(targetState, currentState); this._easing = composeEasingObject( currentState, config.easing || DEFAULT_EASING); this._filterArgs = [currentState, this._originalState, targetState, this._easing]; applyFilter(this, 'tweenCreated'); return this; }; /** * Gets the current state. * @return {Object} */ Tweenable.prototype.get = function () { return shallowCopy({}, this._currentState); }; /** * Sets the current state. * @param {Object} state */ Tweenable.prototype.set = function (state) { this._currentState = state; }; /** * Pauses a tween. Paused tweens can be resumed from the point at which they were paused. This is different than [`stop()`](#stop), as that method causes a tween to start over when it is resumed. * @return {Tweenable} */ Tweenable.prototype.pause = function () { this._pausedAtTime = now(); this._isPaused = true; return this; }; /** * Resumes a paused tween. * @return {Tweenable} */ Tweenable.prototype.resume = function () { if (this._isPaused) { this._timestamp += now() - this._pausedAtTime; } this._isPaused = false; this._isTweening = true; var self = this; this._timeoutHandler = function () { timeoutHandler(self, self._timestamp, self._duration, self._currentState, self._originalState, self._targetState, self._easing, self._step, self._scheduleFunction); }; this._timeoutHandler(); return this; }; /** * Move the state of the animation to a specific point in the tween's timeline. * If the animation is not running, this will cause the `step` handlers to be * called. * @param {millisecond} millisecond The millisecond of the animation to seek to. * @return {Tweenable} */ Tweenable.prototype.seek = function (millisecond) { this._timestamp = now() - millisecond; if (!this.isPlaying()) { this._isTweening = true; this._isPaused = false; // If the animation is not running, call timeoutHandler to make sure that // any step handlers are run. timeoutHandler(this, this._timestamp, this._duration, this._currentState, this._originalState, this._targetState, this._easing, this._step, this._scheduleFunction); this._timeoutHandler(); this.pause(); } return this; }; /** * Stops and cancels a tween. * @param {boolean=} gotoEnd If false or omitted, the tween just stops at its current state, and the "finish" handler is not invoked. If true, the tweened object's values are instantly set to the target values, and "finish" is invoked. * @return {Tweenable} */ Tweenable.prototype.stop = function (gotoEnd) { this._isTweening = false; this._isPaused = false; this._timeoutHandler = noop; (root.cancelAnimationFrame || root.webkitCancelAnimationFrame || root.oCancelAnimationFrame || root.msCancelAnimationFrame || root.mozCancelRequestAnimationFrame || root.clearTimeout)(this._scheduleId); if (gotoEnd) { shallowCopy(this._currentState, this._targetState); applyFilter(this, 'afterTweenEnd'); this._finish.call(this, this._currentState, this._attachment); } return this; }; /** * Returns whether or not a tween is running. * @return {boolean} */ Tweenable.prototype.isPlaying = function () { return this._isTweening && !this._isPaused; }; /** * Sets a custom schedule function. * * If a custom function is not set the default one is used [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame) if available, otherwise [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout)). * * @param {Function(Function,number)} scheduleFunction The function to be called to schedule the next frame to be rendered */ Tweenable.prototype.setScheduleFunction = function (scheduleFunction) { this._scheduleFunction = scheduleFunction; }; /** * `delete`s all "own" properties. Call this when the `Tweenable` instance is no longer needed to free memory. */ Tweenable.prototype.dispose = function () { var prop; for (prop in this) { if (this.hasOwnProperty(prop)) { delete this[prop]; } } }; /*! * Filters are used for transforming the properties of a tween at various * points in a Tweenable's life cycle. See the README for more info on this. */ Tweenable.prototype.filter = {}; /*! * This object contains all of the tweens available to Shifty. It is extendible - simply attach properties to the Tweenable.prototype.formula Object following the same format at linear. * * `pos` should be a normalized `number` (between 0 and 1). */ Tweenable.prototype.formula = { linear: function (pos) { return pos; } }; formula = Tweenable.prototype.formula; shallowCopy(Tweenable, { 'now': now ,'each': each ,'tweenProps': tweenProps ,'tweenProp': tweenProp ,'applyFilter': applyFilter ,'shallowCopy': shallowCopy ,'defaults': defaults ,'composeEasingObject': composeEasingObject }); root.Tweenable = Tweenable; return Tweenable; } ()); /*! * All equations are adapted from Thomas Fuchs' [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/penner.js). * * Based on Easing Equations (c) 2003 [Robert Penner](http://www.robertpenner.com/), all rights reserved. This work is [subject to terms](http://www.robertpenner.com/easing_terms_of_use.html). */ /*! * TERMS OF USE - EASING EQUATIONS * Open source under the BSD License. * Easing Equations (c) 2003 Robert Penner, all rights reserved. */ ;(function () { Tweenable.shallowCopy(Tweenable.prototype.formula, { easeInQuad: function (pos) { return Math.pow(pos, 2); }, easeOutQuad: function (pos) { return -(Math.pow((pos - 1), 2) - 1); }, easeInOutQuad: function (pos) { if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,2);} return -0.5 * ((pos -= 2) * pos - 2); }, easeInCubic: function (pos) { return Math.pow(pos, 3); }, easeOutCubic: function (pos) { return (Math.pow((pos - 1), 3) + 1); }, easeInOutCubic: function (pos) { if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,3);} return 0.5 * (Math.pow((pos - 2),3) + 2); }, easeInQuart: function (pos) { return Math.pow(pos, 4); }, easeOutQuart: function (pos) { return -(Math.pow((pos - 1), 4) - 1); }, easeInOutQuart: function (pos) { if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); }, easeInQuint: function (pos) { return Math.pow(pos, 5); }, easeOutQuint: function (pos) { return (Math.pow((pos - 1), 5) + 1); }, easeInOutQuint: function (pos) { if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,5);} return 0.5 * (Math.pow((pos - 2),5) + 2); }, easeInSine: function (pos) { return -Math.cos(pos * (Math.PI / 2)) + 1; }, easeOutSine: function (pos) { return Math.sin(pos * (Math.PI / 2)); }, easeInOutSine: function (pos) { return (-0.5 * (Math.cos(Math.PI * pos) - 1)); }, easeInExpo: function (pos) { return (pos === 0) ? 0 : Math.pow(2, 10 * (pos - 1)); }, easeOutExpo: function (pos) { return (pos === 1) ? 1 : -Math.pow(2, -10 * pos) + 1; }, easeInOutExpo: function (pos) { if (pos === 0) {return 0;} if (pos === 1) {return 1;} if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(2,10 * (pos - 1));} return 0.5 * (-Math.pow(2, -10 * --pos) + 2); }, easeInCirc: function (pos) { return -(Math.sqrt(1 - (pos * pos)) - 1); }, easeOutCirc: function (pos) { return Math.sqrt(1 - Math.pow((pos - 1), 2)); }, easeInOutCirc: function (pos) { if ((pos /= 0.5) < 1) {return -0.5 * (Math.sqrt(1 - pos * pos) - 1);} return 0.5 * (Math.sqrt(1 - (pos -= 2) * pos) + 1); }, easeOutBounce: function (pos) { if ((pos) < (1 / 2.75)) { return (7.5625 * pos * pos); } else if (pos < (2 / 2.75)) { return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); } else if (pos < (2.5 / 2.75)) { return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); } else { return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); } }, easeInBack: function (pos) { var s = 1.70158; return (pos) * pos * ((s + 1) * pos - s); }, easeOutBack: function (pos) { var s = 1.70158; return (pos = pos - 1) * pos * ((s + 1) * pos + s) + 1; }, easeInOutBack: function (pos) { var s = 1.70158; if ((pos /= 0.5) < 1) {return 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s));} return 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2); }, elastic: function (pos) { return -1 * Math.pow(4,-8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1; }, swingFromTo: function (pos) { var s = 1.70158; return ((pos /= 0.5) < 1) ? 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)) : 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2); }, swingFrom: function (pos) { var s = 1.70158; return pos * pos * ((s + 1) * pos - s); }, swingTo: function (pos) { var s = 1.70158; return (pos -= 1) * pos * ((s + 1) * pos + s) + 1; }, bounce: function (pos) { if (pos < (1 / 2.75)) { return (7.5625 * pos * pos); } else if (pos < (2 / 2.75)) { return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); } else if (pos < (2.5 / 2.75)) { return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); } else { return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); } }, bouncePast: function (pos) { if (pos < (1 / 2.75)) { return (7.5625 * pos * pos); } else if (pos < (2 / 2.75)) { return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); } else if (pos < (2.5 / 2.75)) { return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); } else { return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); } }, easeFromTo: function (pos) { if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); }, easeFrom: function (pos) { return Math.pow(pos,4); }, easeTo: function (pos) { return Math.pow(pos,0.25); } }); }()); /*! * The Bezier magic in this file is adapted/copied almost wholesale from * [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/cubic-bezier.js), * which was adapted from Apple code (which probably came from * [here](http://opensource.apple.com/source/WebCore/WebCore-955.66/platform/graphics/UnitBezier.h)). * Special thanks to Apple and Thomas Fuchs for much of this code. */ /*! * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder(s) nor the names of any * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ ;(function () { // port of webkit cubic bezier handling by http://www.netzgesta.de/dev/ function cubicBezierAtTime(t,p1x,p1y,p2x,p2y,duration) { var ax = 0,bx = 0,cx = 0,ay = 0,by = 0,cy = 0; function sampleCurveX(t) {return ((ax * t + bx) * t + cx) * t;} function sampleCurveY(t) {return ((ay * t + by) * t + cy) * t;} function sampleCurveDerivativeX(t) {return (3.0 * ax * t + 2.0 * bx) * t + cx;} function solveEpsilon(duration) {return 1.0 / (200.0 * duration);} function solve(x,epsilon) {return sampleCurveY(solveCurveX(x,epsilon));} function fabs(n) {if (n >= 0) {return n;}else {return 0 - n;}} function solveCurveX(x,epsilon) { var t0,t1,t2,x2,d2,i; for (t2 = x, i = 0; i < 8; i++) {x2 = sampleCurveX(t2) - x; if (fabs(x2) < epsilon) {return t2;} d2 = sampleCurveDerivativeX(t2); if (fabs(d2) < 1e-6) {break;} t2 = t2 - x2 / d2;} t0 = 0.0; t1 = 1.0; t2 = x; if (t2 < t0) {return t0;} if (t2 > t1) {return t1;} while (t0 < t1) {x2 = sampleCurveX(t2); if (fabs(x2 - x) < epsilon) {return t2;} if (x > x2) {t0 = t2;}else {t1 = t2;} t2 = (t1 - t0) * 0.5 + t0;} return t2; // Failure. } cx = 3.0 * p1x; bx = 3.0 * (p2x - p1x) - cx; ax = 1.0 - cx - bx; cy = 3.0 * p1y; by = 3.0 * (p2y - p1y) - cy; ay = 1.0 - cy - by; return solve(t, solveEpsilon(duration)); } /*! * getCubicBezierTransition(x1, y1, x2, y2) -> Function * * Generates a transition easing function that is compatible * with WebKit's CSS transitions `-webkit-transition-timing-function` * CSS property. * * The W3C has more information about * * CSS3 transition timing functions. * * @param {number} x1 * @param {number} y1 * @param {number} x2 * @param {number} y2 * @return {function} */ function getCubicBezierTransition (x1, y1, x2, y2) { return function (pos) { return cubicBezierAtTime(pos,x1,y1,x2,y2,1); }; } // End ported code /** * Creates a Bezier easing function and attaches it to `Tweenable.prototype.formula`. This function gives you total control over the easing curve. Matthew Lein's [Ceaser](http://matthewlein.com/ceaser/) is a useful tool for visualizing the curves you can make with this function. * * @param {string} name The name of the easing curve. Overwrites the old easing function on Tweenable.prototype.formula if it exists. * @param {number} x1 * @param {number} y1 * @param {number} x2 * @param {number} y2 * @return {function} The easing function that was attached to Tweenable.prototype.formula. */ Tweenable.setBezierFunction = function (name, x1, y1, x2, y2) { var cubicBezierTransition = getCubicBezierTransition(x1, y1, x2, y2); cubicBezierTransition.x1 = x1; cubicBezierTransition.y1 = y1; cubicBezierTransition.x2 = x2; cubicBezierTransition.y2 = y2; return Tweenable.prototype.formula[name] = cubicBezierTransition; }; /** * `delete`s an easing function from `Tweenable.prototype.formula`. Be careful with this method, as it `delete`s whatever easing formula matches `name` (which means you can delete default Shifty easing functions). * * @param {string} name The name of the easing function to delete. * @return {function} */ Tweenable.unsetBezierFunction = function (name) { delete Tweenable.prototype.formula[name]; }; })(); ;(function () { function getInterpolatedValues ( from, current, targetState, position, easing) { return Tweenable.tweenProps( position, current, from, targetState, 1, 0, easing); } // Fake a Tweenable and patch some internals. This approach allows us to // skip uneccessary processing and object recreation, cutting down on garbage // collection pauses. var mockTweenable = new Tweenable(); mockTweenable._filterArgs = []; /** * Compute the midpoint of two Objects. This method effectively calculates a specific frame of animation that [Tweenable#tween](shifty.core.js.html#tween) does many times over the course of a tween. * * Example: * * var interpolatedValues = Tweenable.interpolate({ * width: '100px', * opacity: 0, * color: '#fff' * }, { * width: '200px', * opacity: 1, * color: '#000' * }, 0.5); * * console.log(interpolatedValues); * // {opacity: 0.5, width: "150px", color: "rgb(127,127,127)"} * * @param {Object} from The starting values to tween from. * @param {Object} targetState The ending values to tween to. * @param {number} position The normalized position value (between 0.0 and 1.0) to interpolate the values between `from` and `to` for. `from` represents 0 and `to` represents `1`. * @param {string|Object} easing The easing curve(s) to calculate the midpoint against. You can reference any easing function attached to `Tweenable.prototype.formula`. If omitted, this defaults to "linear". * @return {Object} */ Tweenable.interpolate = function (from, targetState, position, easing) { var current = Tweenable.shallowCopy({}, from); var easingObject = Tweenable.composeEasingObject( from, easing || 'linear'); mockTweenable.set({}); // Alias and reuse the _filterArgs array instead of recreating it. var filterArgs = mockTweenable._filterArgs; filterArgs.length = 0; filterArgs[0] = current; filterArgs[1] = from; filterArgs[2] = targetState; filterArgs[3] = easingObject; // Any defined value transformation must be applied Tweenable.applyFilter(mockTweenable, 'tweenCreated'); Tweenable.applyFilter(mockTweenable, 'beforeTween'); var interpolatedValues = getInterpolatedValues( from, current, targetState, position, easingObject); // Transform values back into their original format Tweenable.applyFilter(mockTweenable, 'afterTween'); return interpolatedValues; }; }()); /** * Adds string interpolation support to Shifty. * * The Token extension allows Shifty to tween numbers inside of strings. Among * other things, this allows you to animate CSS properties. For example, you * can do this: * * var tweenable = new Tweenable(); * tweenable.tween({ * from: { transform: 'translateX(45px)'}, * to: { transform: 'translateX(90xp)'} * }); * * ` ` * `translateX(45)` will be tweened to `translateX(90)`. To demonstrate: * * var tweenable = new Tweenable(); * tweenable.tween({ * from: { transform: 'translateX(45px)'}, * to: { transform: 'translateX(90px)'}, * step: function (state) { * console.log(state.transform); * } * }); * * ` ` * The above snippet will log something like this in the console: * * translateX(60.3px) * ... * translateX(76.05px) * ... * translateX(90px) * * ` ` * Another use for this is animating colors: * * var tweenable = new Tweenable(); * tweenable.tween({ * from: { color: 'rgb(0,255,0)'}, * to: { color: 'rgb(255,0,255)'}, * step: function (state) { * console.log(state.color); * } * }); * * ` ` * The above snippet will log something like this: * * rgb(84,170,84) * ... * rgb(170,84,170) * ... * rgb(255,0,255) * * ` ` * This extension also supports hexadecimal colors, in both long (`#ff00ff`) * and short (`#f0f`) forms. Be aware that hexadecimal input values will be * converted into the equivalent RGB output values. This is done to optimize * for performance. * * var tweenable = new Tweenable(); * tweenable.tween({ * from: { color: '#0f0'}, * to: { color: '#f0f'}, * step: function (state) { * console.log(state.color); * } * }); * * ` ` * This snippet will generate the same output as the one before it because * equivalent values were supplied (just in hexadecimal form rather than RGB): * * rgb(84,170,84) * ... * rgb(170,84,170) * ... * rgb(255,0,255) * * ` ` * ` ` * ## Easing support * * Easing works somewhat differently in the Token extension. This is because * some CSS properties have multiple values in them, and you might need to * tween each value along its own easing curve. A basic example: * * var tweenable = new Tweenable(); * tweenable.tween({ * from: { transform: 'translateX(0px) translateY(0px)'}, * to: { transform: 'translateX(100px) translateY(100px)'}, * easing: { transform: 'easeInQuad' }, * step: function (state) { * console.log(state.transform); * } * }); * * ` ` * The above snippet create values like this: * * translateX(11.560000000000002px) translateY(11.560000000000002px) * ... * translateX(46.24000000000001px) translateY(46.24000000000001px) * ... * translateX(100px) translateY(100px) * * ` ` * In this case, the values for `translateX` and `translateY` are always the * same for each step of the tween, because they have the same start and end * points and both use the same easing curve. We can also tween `translateX` * and `translateY` along independent curves: * * var tweenable = new Tweenable(); * tweenable.tween({ * from: { transform: 'translateX(0px) translateY(0px)'}, * to: { transform: 'translateX(100px) translateY(100px)'}, * easing: { transform: 'easeInQuad bounce' }, * step: function (state) { * console.log(state.transform); * } * }); * * ` ` * The above snippet create values like this: * * translateX(10.89px) translateY(82.355625px) * ... * translateX(44.89000000000001px) translateY(86.73062500000002px) * ... * translateX(100px) translateY(100px) * * ` ` * `translateX` and `translateY` are not in sync anymore, because `easeInQuad` * was specified for `translateX` and `bounce` for `translateY`. Mixing and * matching easing curves can make for some interesting motion in your * animations. * * The order of the space-separated easing curves correspond the token values * they apply to. If there are more token values than easing curves listed, * the last easing curve listed is used. */ function token () { // Functionality for this extension runs implicitly if it is loaded. } /*!*/ // token function is defined above only so that dox-foundation sees it as // documentation and renders it. It is never used, and is optimized away at // build time. ;(function (Tweenable) { /*! * @typedef {{ * formatString: string * chunkNames: Array. * }} */ var formatManifest; // CONSTANTS var R_NUMBER_COMPONENT = /(\d|\-|\.)/; var R_FORMAT_CHUNKS = /([^\-0-9\.]+)/g; var R_UNFORMATTED_VALUES = /[0-9.\-]+/g; var R_RGB = new RegExp( 'rgb\\(' + R_UNFORMATTED_VALUES.source + (/,\s*/.source) + R_UNFORMATTED_VALUES.source + (/,\s*/.source) + R_UNFORMATTED_VALUES.source + '\\)', 'g'); var R_RGB_PREFIX = /^.*\(/; var R_HEX = /#([0-9]|[a-f]){3,6}/gi; var VALUE_PLACEHOLDER = 'VAL'; // HELPERS var getFormatChunksFrom_accumulator = []; /*! * @param {Array.number} rawValues * @param {string} prefix * * @return {Array.} */ function getFormatChunksFrom (rawValues, prefix) { getFormatChunksFrom_accumulator.length = 0; var rawValuesLength = rawValues.length; var i; for (i = 0; i < rawValuesLength; i++) { getFormatChunksFrom_accumulator.push('_' + prefix + '_' + i); } return getFormatChunksFrom_accumulator; } /*! * @param {string} formattedString * * @return {string} */ function getFormatStringFrom (formattedString) { var chunks = formattedString.match(R_FORMAT_CHUNKS); if (!chunks) { // chunks will be null if there were no tokens to parse in // formattedString (for example, if formattedString is '2'). Coerce // chunks to be useful here. chunks = ['', '']; // If there is only one chunk, assume that the string is a number // followed by a token... // NOTE: This may be an unwise assumption. } else if (chunks.length === 1 || // ...or if the string starts with a number component (".", "-", or a // digit)... formattedString[0].match(R_NUMBER_COMPONENT)) { // ...prepend an empty string here to make sure that the formatted number // is properly replaced by VALUE_PLACEHOLDER chunks.unshift(''); } return chunks.join(VALUE_PLACEHOLDER); } /*! * Convert all hex color values within a string to an rgb string. * * @param {Object} stateObject * * @return {Object} The modified obj */ function sanitizeObjectForHexProps (stateObject) { Tweenable.each(stateObject, function (prop) { var currentProp = stateObject[prop]; if (typeof currentProp === 'string' && currentProp.match(R_HEX)) { stateObject[prop] = sanitizeHexChunksToRGB(currentProp); } }); } /*! * @param {string} str * * @return {string} */ function sanitizeHexChunksToRGB (str) { return filterStringChunks(R_HEX, str, convertHexToRGB); } /*! * @param {string} hexString * * @return {string} */ function convertHexToRGB (hexString) { var rgbArr = hexToRGBArray(hexString); return 'rgb(' + rgbArr[0] + ',' + rgbArr[1] + ',' + rgbArr[2] + ')'; } var hexToRGBArray_returnArray = []; /*! * Convert a hexadecimal string to an array with three items, one each for * the red, blue, and green decimal values. * * @param {string} hex A hexadecimal string. * * @returns {Array.} The converted Array of RGB values if `hex` is a * valid string, or an Array of three 0's. */ function hexToRGBArray (hex) { hex = hex.replace(/#/, ''); // If the string is a shorthand three digit hex notation, normalize it to // the standard six digit notation if (hex.length === 3) { hex = hex.split(''); hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } hexToRGBArray_returnArray[0] = hexToDec(hex.substr(0, 2)); hexToRGBArray_returnArray[1] = hexToDec(hex.substr(2, 2)); hexToRGBArray_returnArray[2] = hexToDec(hex.substr(4, 2)); return hexToRGBArray_returnArray; } /*! * Convert a base-16 number to base-10. * * @param {Number|String} hex The value to convert * * @returns {Number} The base-10 equivalent of `hex`. */ function hexToDec (hex) { return parseInt(hex, 16); } /*! * Runs a filter operation on all chunks of a string that match a RegExp * * @param {RegExp} pattern * @param {string} unfilteredString * @param {function(string)} filter * * @return {string} */ function filterStringChunks (pattern, unfilteredString, filter) { var pattenMatches = unfilteredString.match(pattern); var filteredString = unfilteredString.replace(pattern, VALUE_PLACEHOLDER); if (pattenMatches) { var pattenMatchesLength = pattenMatches.length; var currentChunk; for (var i = 0; i < pattenMatchesLength; i++) { currentChunk = pattenMatches.shift(); filteredString = filteredString.replace( VALUE_PLACEHOLDER, filter(currentChunk)); } } return filteredString; } /*! * Check for floating point values within rgb strings and rounds them. * * @param {string} formattedString * * @return {string} */ function sanitizeRGBChunks (formattedString) { return filterStringChunks(R_RGB, formattedString, sanitizeRGBChunk); } /*! * @param {string} rgbChunk * * @return {string} */ function sanitizeRGBChunk (rgbChunk) { var numbers = rgbChunk.match(R_UNFORMATTED_VALUES); var numbersLength = numbers.length; var sanitizedString = rgbChunk.match(R_RGB_PREFIX)[0]; for (var i = 0; i < numbersLength; i++) { sanitizedString += parseInt(numbers[i], 10) + ','; } sanitizedString = sanitizedString.slice(0, -1) + ')'; return sanitizedString; } /*! * @param {Object} stateObject * * @return {Object} An Object of formatManifests that correspond to * the string properties of stateObject */ function getFormatManifests (stateObject) { var manifestAccumulator = {}; Tweenable.each(stateObject, function (prop) { var currentProp = stateObject[prop]; if (typeof currentProp === 'string') { var rawValues = getValuesFrom(currentProp); manifestAccumulator[prop] = { 'formatString': getFormatStringFrom(currentProp) ,'chunkNames': getFormatChunksFrom(rawValues, prop) }; } }); return manifestAccumulator; } /*! * @param {Object} stateObject * @param {Object} formatManifests */ function expandFormattedProperties (stateObject, formatManifests) { Tweenable.each(formatManifests, function (prop) { var currentProp = stateObject[prop]; var rawValues = getValuesFrom(currentProp); var rawValuesLength = rawValues.length; for (var i = 0; i < rawValuesLength; i++) { stateObject[formatManifests[prop].chunkNames[i]] = +rawValues[i]; } delete stateObject[prop]; }); } /*! * @param {Object} stateObject * @param {Object} formatManifests */ function collapseFormattedProperties (stateObject, formatManifests) { Tweenable.each(formatManifests, function (prop) { var currentProp = stateObject[prop]; var formatChunks = extractPropertyChunks( stateObject, formatManifests[prop].chunkNames); var valuesList = getValuesList( formatChunks, formatManifests[prop].chunkNames); currentProp = getFormattedValues( formatManifests[prop].formatString, valuesList); stateObject[prop] = sanitizeRGBChunks(currentProp); }); } /*! * @param {Object} stateObject * @param {Array.} chunkNames * * @return {Object} The extracted value chunks. */ function extractPropertyChunks (stateObject, chunkNames) { var extractedValues = {}; var currentChunkName, chunkNamesLength = chunkNames.length; for (var i = 0; i < chunkNamesLength; i++) { currentChunkName = chunkNames[i]; extractedValues[currentChunkName] = stateObject[currentChunkName]; delete stateObject[currentChunkName]; } return extractedValues; } var getValuesList_accumulator = []; /*! * @param {Object} stateObject * @param {Array.} chunkNames * * @return {Array.} */ function getValuesList (stateObject, chunkNames) { getValuesList_accumulator.length = 0; var chunkNamesLength = chunkNames.length; for (var i = 0; i < chunkNamesLength; i++) { getValuesList_accumulator.push(stateObject[chunkNames[i]]); } return getValuesList_accumulator; } /*! * @param {string} formatString * @param {Array.} rawValues * * @return {string} */ function getFormattedValues (formatString, rawValues) { var formattedValueString = formatString; var rawValuesLength = rawValues.length; for (var i = 0; i < rawValuesLength; i++) { formattedValueString = formattedValueString.replace( VALUE_PLACEHOLDER, +rawValues[i].toFixed(4)); } return formattedValueString; } /*! * Note: It's the duty of the caller to convert the Array elements of the * return value into numbers. This is a performance optimization. * * @param {string} formattedString * * @return {Array.|null} */ function getValuesFrom (formattedString) { return formattedString.match(R_UNFORMATTED_VALUES); } /*! * @param {Object} easingObject * @param {Object} tokenData */ function expandEasingObject (easingObject, tokenData) { Tweenable.each(tokenData, function (prop) { var currentProp = tokenData[prop]; var chunkNames = currentProp.chunkNames; var chunkLength = chunkNames.length; var easingChunks = easingObject[prop].split(' '); var lastEasingChunk = easingChunks[easingChunks.length - 1]; for (var i = 0; i < chunkLength; i++) { easingObject[chunkNames[i]] = easingChunks[i] || lastEasingChunk; } delete easingObject[prop]; }); } /*! * @param {Object} easingObject * @param {Object} tokenData */ function collapseEasingObject (easingObject, tokenData) { Tweenable.each(tokenData, function (prop) { var currentProp = tokenData[prop]; var chunkNames = currentProp.chunkNames; var chunkLength = chunkNames.length; var composedEasingString = ''; for (var i = 0; i < chunkLength; i++) { composedEasingString += ' ' + easingObject[chunkNames[i]]; delete easingObject[chunkNames[i]]; } easingObject[prop] = composedEasingString.substr(1); }); } Tweenable.prototype.filter.token = { 'tweenCreated': function (currentState, fromState, toState, easingObject) { sanitizeObjectForHexProps(currentState); sanitizeObjectForHexProps(fromState); sanitizeObjectForHexProps(toState); this._tokenData = getFormatManifests(currentState); }, 'beforeTween': function (currentState, fromState, toState, easingObject) { expandEasingObject(easingObject, this._tokenData); expandFormattedProperties(currentState, this._tokenData); expandFormattedProperties(fromState, this._tokenData); expandFormattedProperties(toState, this._tokenData); }, 'afterTween': function (currentState, fromState, toState, easingObject) { collapseFormattedProperties(currentState, this._tokenData); collapseFormattedProperties(fromState, this._tokenData); collapseFormattedProperties(toState, this._tokenData); collapseEasingObject(easingObject, this._tokenData); } }; } (Tweenable)); }(window)); return window.Tweenable; }); (function() { "use strict"; angular.module('angular-carousel') .filter('carouselSlice', function() { return function(collection, start, size) { if (angular.isArray(collection)) { return collection.slice(start, start + size); } else if (angular.isObject(collection)) { // dont try to slice collections :) return collection; } }; }); })(); (function () { 'use strict'; var ngAntiForgeryService = angular.module('ngAntiForgeryService', ['ngResource']); ngAntiForgeryService.service('AntiForgery', ['$resource', function ($resource) { this.getAntiForgery = function (prefix) { // Initiates the AJAX call var req = $.ajax("/acs-refresh-forgery-token") , chained = req.then( function(html) { return $('
').html(html).find('input[type="hidden"]').val(); }); return chained; } }]); })(); (function () { 'use strict'; var ngChartsService = angular.module('ngChartsService', ['ngResource']); ngChartsService.service('Charts', ['$resource', '$http', function ($resource) { this.getClientChartData = function (url, dataaux, prefix) { return $resource(prefix + '/webapi/clientdashboard/getchartdata?chartName=' + url, {}, { query: { method: 'POST', params: { data: dataaux }, isArray: false } }).query().$promise; }; this.getInternalChartData = function (url, dataaux, prefix) { return $resource(prefix + '/webapi/internaldashboard/getchartdata?chartName=' + url, {}, { query: { method: 'POST', params: { data: dataaux }, isArray: false } }).query().$promise; }; this.drawLineDoubleChart = function (labels, values, div) { var table = []; var aux = ['void']; aux = aux.concat(labels); table.push(aux); for (var i = 0; i < values.length; i++) { var aux1 = []; aux1.push(values[i].label); aux1 = aux1.concat(values[i].data) table.push(aux1); } var rows = this.transpose(table); var data = google.visualization.arrayToDataTable( rows ); var columns = []; var series = {}; for (var i = 0; i < data.getNumberOfColumns(); i++) { columns.push(i); if (i > 0) { series[i - 1] = {}; } } var options = { legend: { position: 'top' }, //chartArea: { 'width': '85%', 'height': '80%' }, series: { 0: { targetAxisIndex: 0 }, 1: { targetAxisIndex: 1 }, 2: { targetAxisIndex: 0 }, 3: { targetAxisIndex: 1 } }, vAxes: { 0: { }, 1: { }, }, }; var chart = new google.visualization.LineChart( document.getElementById(div)); chart.draw(data, options); google.visualization.events.addListener(chart, 'select', function () { var sel = chart.getSelection(); // if selection length is 0, we deselected an element if (sel.length > 0) { // if row is undefined, we clicked on the legend if (typeof sel[0].row === 'undefined' || sel[0].row == null) { var col = sel[0].column; if (columns[col] == col) { // hide the data series columns[col] = { label: data.getColumnLabel(col), type: data.getColumnType(col), calc: function () { return null; } }; // grey out the legend entry series[col - 1].color = '#CCCCCC'; } else { // show the data series columns[col] = col; series[col - 1].color = null; } var view = new google.visualization.DataView(data); view.setColumns(columns); chart.draw(view, options); } } }); } this.drawGauges = function (data, div) { var dat = google.visualization.arrayToDataTable(data ); var options = { width: 400, height: 120, redFrom: 90, redTo: 100, yellowFrom: 75, yellowTo: 90, minorTicks: 5 }; var chart = new google.visualization.Gauge(document.getElementById(div)); chart.draw(dat, options); } this.drawLineSimpleChart = function (labels, values, div) { var table = []; var aux = ['void']; aux = aux.concat(labels); table.push(aux); for (var i = 0; i < values.length; i++) { var aux1 = []; aux1.push(values[i].label); aux1 = aux1.concat(values[i].data) table.push(aux1); } var rows = this.transpose(table); var data = google.visualization.arrayToDataTable( rows ); var columns = []; var series = {}; for (var i = 0; i < data.getNumberOfColumns(); i++) { columns.push(i); if (i > 0) { series[i - 1] = {}; } } var options = { legend: { position: 'top' }, //chartArea: { 'width': '85%', 'height': '80%' }, series: { 0: { targetAxisIndex: 0 }, 1: { targetAxisIndex: 1 } }, vAxes: { 0: { }, 1: { }, }, }; var chart = new google.visualization.LineChart( document.getElementById(div)); chart.draw(data, options); google.visualization.events.addListener(chart, 'select', function () { var sel = chart.getSelection(); // if selection length is 0, we deselected an element if (sel.length > 0) { // if row is undefined, we clicked on the legend if (typeof sel[0].row === 'undefined' || sel[0].row == null) { var col = sel[0].column; if (columns[col] == col) { // hide the data series columns[col] = { label: data.getColumnLabel(col), type: data.getColumnType(col), calc: function () { return null; } }; // grey out the legend entry series[col - 1].color = '#CCCCCC'; } else { // show the data series columns[col] = col; series[col - 1].color = null; } var view = new google.visualization.DataView(data); view.setColumns(columns); chart.draw(view, options); } } }); }; this.drawLineSimpleChartNoAxis = function (labels, values, div, title) { var table = []; var aux = ['void']; aux = aux.concat(labels); table.push(aux); for (var i = 0; i < values.length; i++) { var aux1 = []; aux1.push(values[i].label.toLowerCase()); aux1 = aux1.concat(values[i].data) table.push(aux1); } var rows = this.transpose(table); var data = google.visualization.arrayToDataTable( rows ); var columns = []; var series = {}; for (var i = 0; i < data.getNumberOfColumns(); i++) { columns.push(i); if (i > 0) { series[i - 1] = {}; } } var options = { legend: { position: 'top' }, //chartArea: { 'width': '85%', 'height': '80%' }, series: { }, title: title, vAxes: { 0: { }, }, }; var chart = new google.visualization.LineChart( document.getElementById(div)); chart.draw(data, options); google.visualization.events.addListener(chart, 'select', function () { var sel = chart.getSelection(); // if selection length is 0, we deselected an element if (sel.length > 0) { // if row is undefined, we clicked on the legend if (typeof sel[0].row === 'undefined' || sel[0].row == null) { var col = sel[0].column; if (columns[col] == col) { // hide the data series columns[col] = { label: data.getColumnLabel(col), type: data.getColumnType(col), calc: function () { return null; } }; // grey out the legend entry series[col - 1].color = '#CCCCCC'; } else { // show the data series columns[col] = col; series[col - 1].color = null; } var view = new google.visualization.DataView(data); view.setColumns(columns); chart.draw(view, options); } } }); } this.drawLinePositiveChart = function (labels, values, div) { var table = []; var aux = ['void']; aux = aux.concat(labels); table.push(aux); for (var i = 0; i < values.length; i++) { var aux1 = []; aux1.push(values[i].label); aux1 = aux1.concat(values[i].data) table.push(aux1); } var rows = this.transpose(table); var data = google.visualization.arrayToDataTable( rows ); var columns = []; var series = {}; for (var i = 0; i < data.getNumberOfColumns(); i++) { columns.push(i); if (i > 0) { series[i - 1] = {}; } } var options = { pointSize: 5, colors: ['#1ABB9C'], legend: { position: 'top' }, //chartArea: { 'width': '85%', 'height': '80%' }, series: { 0: { targetAxisIndex: 0 }, }, vAxes: { 0: { viewWindowMode: "explicit", viewWindow: { min: 0 } }, }, }; var chart = new google.visualization.LineChart( document.getElementById(div)); chart.draw(data, options); google.visualization.events.addListener(chart, 'select', function () { var sel = chart.getSelection(); // if selection length is 0, we deselected an element if (sel.length > 0) { // if row is undefined, we clicked on the legend if (typeof sel[0].row === 'undefined' || sel[0].row == null) { var col = sel[0].column; if (columns[col] == col) { // hide the data series columns[col] = { label: data.getColumnLabel(col), type: data.getColumnType(col), calc: function () { return null; } }; // grey out the legend entry series[col - 1].color = '#CCCCCC'; } else { // show the data series columns[col] = col; series[col - 1].color = null; } var view = new google.visualization.DataView(data); view.setColumns(columns); chart.draw(view, options); } } }); } this.drawPieChart = function (values, div) { var table = []; var aux = ['void', 'Euros']; table.push(aux); for (var i = 0; i < values.length; i++) { var aux1 = []; aux1.push(values[i].label); aux1 = aux1.concat(values[i].value) table.push(aux1); } var data = google.visualization.arrayToDataTable( table ); var chart = new google.visualization.PieChart( document.getElementById(div)); var options = { hAxis: { title: '', }, vAxis: { title: '' }, legend: { position: 'top' }, //chartArea: { // 'width': '85%', 'height': '80%' //}, //sliceVisibilityThreshold: .15 }; chart.draw(data, options); } this.drawPieChartLib = function (values, div) { var table = []; var aux = ['void', 'Data']; table.push(aux); for (var i = 0; i < values.Labels.length; i++) { var aux1 = []; aux1.push(values.Labels[i]); aux1 = aux1.concat(values.Series[0].Values[i]) table.push(aux1); } var data = google.visualization.arrayToDataTable( table ); var chart = new google.visualization.PieChart( document.getElementById(div)); var options = values.Options; chart.draw(data, options); } this.getColor = function (index) { switch (index % 6) { case (0): return "rgba(031, 157, 212, 0.4)"; case (1): return "rgba(193, 02, 163, 0.4)"; case (2): return "rgba(163, 193, 02, 0.4)"; case (3): return "rgba(02, 68, 193, 0.4)"; case (4): return "rgba(02, 193, 128,0.4)"; case (5): return "rgba(193, 32, 02, 0.4)"; } } this.getSolidColor = function (index) { switch (index % 6) { case (0): return "rgba(031, 157, 212, 1)"; case (1): return "rgba(193, 02, 163, 1)"; case (2): return "rgba(163, 193, 02, 1)"; case (3): return "rgba(02, 68, 193, 1)"; case (4): return "rgba(02, 193, 128,1)"; case (5): return "rgba(193, 32, 02, 1)"; } } //Metodo de transpose para representas las gráficas this.transpose = function (table) { // Calculate the width and height of the Array var a = table, w = a.length ? a.length : 0, h = a[0] instanceof Array ? a[0].length : 0; // In case it is a zero matrix, no transpose routine needed. if (h === 0 || w === 0) { return []; } /** * @var {Number} i Counter * @var {Number} j Counter * @var {Array} t Transposed data is stored in this array. */ var i, j, t = []; // Loop through every item in the outer array (height) for (i = 0; i < h; i++) { // Insert a new row (array) t[i] = []; // Loop through every item per item in outer array (width) for (j = 0; j < w; j++) { // Save transposed data. t[i][j] = a[j][i]; } } return t; }; //Metodo para inicializar las graficas de google. //Recibe como parametro una función para pintar las graficas que se quieran this.initCharts = function (printCharts) { // Load the Visualization API and the corechart package. google.charts.load('current', { 'packages': ['corechart'] }); // Set a callback to run when the Google Visualization API is loaded. google.charts.setOnLoadCallback(printCharts); } this.formatDataToPrint = function (values) { var table = []; var aux = ['void']; aux = aux.concat(values.Labels); table.push(aux); for (var i = 0; i < values.Series.length; i++) { var aux1 = []; aux1.push(values.Series[i].Legend); aux1 = aux1.concat(values.Series[i].Values); table.push(aux1); } var rows = this.transpose(table); var data = google.visualization.arrayToDataTable( rows ); return data; } this.printMultiChart = function (div, values) { var data = this.formatDataToPrint(values); var options = values.Options; var chart = new google.visualization.ComboChart( document.getElementById(div)); var columns = []; var series = {}; for (var i = 0; i < data.getNumberOfColumns(); i++) { columns.push(i); if (i > 0) { series[i - 1] = {}; } } chart.draw(data, options); google.visualization.events.addListener(chart, 'select', function () { var sel = chart.getSelection(); // if selection length is 0, we deselected an element if (sel.length > 0) { // if row is undefined, we clicked on the legend if (typeof sel[0].row === 'undefined' || sel[0].row == null) { var col = sel[0].column; if (columns[col] == col) { // hide the data series columns[col] = { label: data.getColumnLabel(col), type: data.getColumnType(col), calc: function () { return null; } }; // grey out the legend entry series[col - 1].color = '#CCCCCC'; } else { // show the data series columns[col] = col; series[col - 1].color = null; } var view = new google.visualization.DataView(data); view.setColumns(columns); chart.draw(view, options); } } }); }; }]); })(); (function () { 'use strict'; var ngLookupsService = angular.module('ngLookupsService', ['ngResource']); ngLookupsService.service('Lookups', ['$resource', function ($resource) { // --- General Purpouses --- this.getAllCities = function (filter, prefix) { return $resource(prefix + '/webapi/cities/:flt', { flt: filter }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getAllAirports = function (filter, prefix) { return $resource(prefix + '/webapi/airport/:flt', { flt: filter }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getCityByName = function (filter, prefix) { return $resource(prefix + '/webapi/citybyname/:flt', { flt: filter }, { query: { method: 'GET', params: {}, isArray: false } }).query().$promise; }; this.getCountries = function (filter, prefix) { return $resource(prefix + '/webapi/countries/:flt', { flt: filter }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getPartners = function (filter, prefix) { return $resource(prefix + '/webapi/partners/:flt', { flt: filter }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getAgenciesByPartner = function (partnerUid, filter, withRef, prefix) { return $resource(prefix + '/webapi/agencies/:pe/:flt/:wr', { pe: partnerUid, flt: filter, wr: withRef }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getProviders = function (filter, prefix) { var filterBase64 = btoa(filter); return $resource(prefix + '/webapi/providers/:flt', { flt: filterBase64 }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getAllDomains = function (filter, prefix) { return $resource(prefix + '/webapi/domains/:flt', { flt: filter }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; // --- End General Purpouses --- // --- Professional Registration --- this.getRegistrationLanguages = function (filter, prefix, simplified) { return $resource(prefix + '/webapi/internaldashboard/languages/:sim/:flt', { sim: simplified, flt: filter }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; // --- End Professional Registration --- this.getUserPurchaseServices = function (cityName, countryCode, prefix) { return $resource(prefix + '/webapi/userpurchase/services/:city/:ctry', { city: cityName, ctry: countryCode }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getUserPurchaseAirports = function (cityName, countryCode, prefix) { return $resource(prefix + '/webapi/userpurchase/airports/:city/:ctry', { city: cityName, ctry: countryCode }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getPurchaseLanguages = function (filter, simplified, prefix) { return $resource(prefix + '/webapi/v2/purchases/availablelanguages?filter=:flt&simplified=:sim', { sim: simplified, flt: filter }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; // --- End User Purchase --- }]); })(); (function () { 'use strict'; var ngPurchasesService = angular.module('ngPurchasesService', ['ngResource']); ngPurchasesService.service('Purchases', ['$resource', function ($resource) { this.getMeetingPointAddressesCart = function (filter, prefix, latitude, longitude, radious) { return $resource(prefix + '/webapi/v2/purchases/meetingpointaddressescart?filter=:flt&latitude=:lat&longitude=:long&radious=:rad', { flt: filter, lat: latitude, long: longitude, rad: radious }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; // Services this.getMaterializedServices = function (filters, rqType, partner, rqId, prefix, skip, take, currencyCode, agentUid) { return $resource(prefix + '/webapi/v2/userpurchase/materializedservices', {}, { query: { method: 'POST', params: { filterData: filters, partnerCode: partner, currencyCode: currencyCode, rqType: rqType, rqId: rqId, skip: skip, take: take, agentUid: agentUid }, isArray: false } }).query().$promise; }; // Services this.getActivityMaterializedServicesByIds = function (ids, date, partner, rqId, prefix, mainId, sortType, currencyCode) { return $resource(prefix + '/webapi/v2/userpurchase/activitymaterializedservicesbylist', {}, { query: { method: 'POST', params: { ids: ids, mainId: mainId, date: date, rqId: rqId, partnerCode: partner, sortType: sortType, currencyCode: currencyCode }, isArray: false } }).query().$promise; }; this.getApartmentsFullInformation = function (materializedlist, prefix) { return $resource(prefix + '/webapi/v2/userpurchase/apartments', {}, { query: { method: 'POST', params: { materializeduids: materializedlist, }, isArray: true, } }).query().$promise; }; this.getActivitiesFullInformation = function (materializedlist, currencyCode, prefix) { return $resource(prefix + '/webapi/v2/userpurchase/activities', {}, { query: { method: 'POST', params: { materializeduids: materializedlist, currencyCode: currencyCode, }, isArray: true, } }).query().$promise; }; this.getActivityCoverData = function (prefix, partner, currencyCode) { return $resource(prefix + '/webapi/v2/userpurchase/activitycoverdata', {}, { query: { method: 'POST', params: { partnerCode: partner, currencyCode: currencyCode }, isArray: false } }).query().$promise; }; this.getActivityLanguages = function (prefix) { return $resource(prefix + '/webapi/v2/purchases/activityavailablelanguages', { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; //Only addresses, streets, stablishment this.getAddressesPrediction = function (filter, placeId, prefix, session) { return $resource(prefix + '/webapi/v2/userpurchase/addressesprediction?filter=:filter&placeId=:placeId&sessionToken=:session', { filter: filter, placeId: placeId, session: session }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getAddressesPickup = function (filter, prefix) { return $resource(prefix + '/webapi/v2/purchases/addressespickup?filter=:filter', { filter: filter}, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; //Only cities/regions this.getDestinationPrediction = function (filter, prefix) { return $resource(prefix + '/webapi/v2/userpurchase/destinationprediction?filter=:filter', { filter: filter }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; //Addresses and cities this.getUserPurchaseAddresses = function (filter, prefix, cityName, countryCode, serviceCode) { return $resource(prefix + '/webapi/v2/userpurchase/addresses?filter=:flt&cityName=:city&countryCode=:country&serviceCode=:srv', { flt: filter, city: cityName, country: countryCode, srv: serviceCode }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; //Airports this.getAirportsToComplete = function (filter) { return $resource('/webapi/v2/userpurchase/airport?filter=:filter', { filter: filter }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; //Cities/Venues this.getActivitiesDestinationPrediction = function (filter, prefix, placeId) { return $resource(prefix + '/webapi/v2/userpurchase/activitiesdestinationprediction?filter=:filter&placeId=:placeId', { filter: filter, placeId: placeId }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getActivitiesDestinationPredictionWhitoutRegions = function (filter, prefix, placeId) { return $resource(prefix + '/webapi/v2/userpurchase/activitiesdestinationpredictionwithoutregions?filter=:filter&placeId=:placeId', { filter: filter, placeId: placeId }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; //this.getApartmentsDestinationPrediction = function (filter, prefix, placeId) { // return $resource(prefix + '/webapi/v2/userpurchase/apartmentsdestinationprediction?filter=:filter&placeId=:placeId', { filter: filter, placeId: placeId }, // { // query: { method: 'GET', params: {}, isArray: true } // }).query().$promise; //}; this.getApartmentsDestinationPrediction = function (filter, placeId, prefix, session) { return $resource(prefix + '/webapi/v2/userpurchase/apartmentsdestinationprediction?filter=:filter&placeId=:placeId&sessionToken=:session', { filter: filter, placeId: placeId, session: session }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; //Internal purchase this.getPurchaseInternalAvailableServices = function (mainService, prefix) { return $resource(prefix + '/webapi/v2/internaldashboard/availableinternalservices?mainService=:srv', { srv: mainService }, { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getPurchaseInternalApartmentCategories = function (prefix) { return $resource(prefix + '/webapi/v2/internaldashboard/availableinternalapartmentcategories', { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getPuchaseInternalCurrencies = function (prefix) { return $resource(prefix + '/webapi/v2/internaldashboard/currencies', { query: { method: 'GET', params: {}, isArray: true } }).query().$promise; }; this.getFlightInfo = function (filter, date, dateArr, prefix, iataOrigin, iataDestination) { return $resource(prefix + '/webapi/v2/purchases/flightInfo', {}, { query: { method: 'POST', params: { filter: filter, departureDate: date, arrivalDate: dateArr, iataOrigin: iataOrigin, iataDestination: iataDestination }, isArray: false } }).query().$promise; }; ////// function transformGetToInt(json, headerGetter) { var fromJson = angular.fromJson({ raw: json }); return fromJson; } }]); })(); (function () { 'use strict'; var ngTabulatorHelperService = angular.module('ngTabulatorHelperService', ['ngResource']); ngTabulatorHelperService.service('TabulatorHelper', ['$resource', '$http', function ($resource) { this.setDefaultCell = function (cell) { //Put default params color for the cell. cell.addClass("tb-default-cell"); }; this.setModifiedCell = function (cell) { //Put modified color to the cell. cell.addClass("tb-modified-cell"); }; //custom header filter this.dateFilterEditor = function(cell, onRendered, success, cancel, editorParams) { var container = $("") //create and style input var selectfield = $(""); var start = $(""); var end = $(""); container.append(selectfield).append(start).append(end); var inputs = $("input", container); inputs.css({ "padding": "4px", "width": "50%", "box-sizing": "border-box", }) .val(cell.getValue()); function buildDateString() { var json = { type:selectfield.val(), start: start.val(), end: end.val() }; return JSON.stringify(json); } function buildTypeString() { var json = { type:selectfield.val()}; return JSON.stringify(json); } selectfield.change(function() { var selectvalue = selectfield.val(); if (selectvalue != "beetween_reservations") { start.val(""); end.val(""); start.hide(); end.hide(); success(buildTypeString()); } else { start.show(); end.show(); } }); //submit new value on blur inputs.on("change blur", function(e) { success(buildDateString()); }); //submit new value on enter inputs.on("keydown", function(e) { if (e.keyCode == 13) { success(buildDateString()); } if (e.keyCode == 27) { cancel(); } }); return container; }; //custom filter function this.dateFilterFunction=function(headerValue, rowValue, rowData, filterParams){ //headerValue - the value of the header filter element //rowValue - the value of the column in this row //rowData - the data for the row being filtered //filterParams - params object passed to the headerFilterFuncParams property var format = filterParams.format || "DD/MM/YYYY"; var start = moment(headerValue.start); var end = moment(headerValue.end); var value = moment(rowValue, format) if(rowValue){ if(start.isValid()){ if(end.isValid()){ return value >= start && value <= end; }else{ return value >= start; } }else{ if(end.isValid()){ return value <= end; } } } return false; //must return a boolean, true if it passes the filter. } }]); })(); (function () { 'use strict'; var ngValidationsService = angular.module('ngValidationsService', ['ngResource']); ngValidationsService.service('Validations', ['$resource', function ($resource) { this.existsUserEmail = function (email, prefix) { return $resource(prefix + '/webapi/registrationvalidation/email/:mail', { mail: email }, { get: { method: 'GET', transformResponse: transformGetToBoolean }, }).get().$promise; }; this.existsPartnerEmail = function (email, prefix) { return $resource(prefix + '/webapi/registrationpartnervalidation/email/:mail', { mail: email }, { get: { method: 'GET', transformResponse: transformGetToBoolean }, }).get().$promise; }; this.existsClientEmail = function (email, prefix) { if (email == undefined || email.length == 0) return; return $resource(prefix + '/webapi/clientregistrationvalidation/email/:mail/', { mail: email }, { get: { method: 'GET', transformResponse: transformGetToBoolean }, }).get().$promise; }; this.existsCompanyVat = function (vatnumber, prefix) { return $resource(prefix + '/webapi/registrationvalidation/vatnumber/:vatnumber', { vatnumber: vatnumber }, { get: { method: 'GET', transformResponse: transformGetToBoolean }, }).get().$promise; }; this.existsInternalUserEmail = function (email, prefix) { return $resource(prefix + '/webapi/internaluserregistrationvalidation/email/:mail', { mail: email }, { get: { method: 'GET', transformResponse: transformGetToBoolean }, }).get().$promise; }; this.existsUserEmailInternalDashboard = function (email, prefix) { return $resource(prefix + '/webapi/registerprofessionalvalidation/email/:mail', { mail: email }, { get: { method: 'GET', transformResponse: transformGetToBoolean }, }).get().$promise; }; this.existsDiscountCode = function (code, prefix) { return $resource(prefix + '/webapi/internaldiscountvalidation/discountCode/:discountCode', { discountCode: code }, { get: { method: 'GET', transformResponse: transformGetToBoolean }, }).get().$promise; }; }]); function transformGetToBoolean(json, headerGetter) { var fromJson = angular.fromJson({ raw: (json === "true") }); return fromJson; } })(); /**! * AngularJS file upload directives and services. Supports: file upload/drop/paste, resume, cancel/abort, * progress, resize, thumbnail, preview, validation and CORS * FileAPI Flash shim for old browsers not supporting FormData * @author Danial * @version 12.2.13 */ (function () { /** @namespace FileAPI.noContentTimeout */ function patchXHR(fnName, newFn) { window.XMLHttpRequest.prototype[fnName] = newFn(window.XMLHttpRequest.prototype[fnName]); } function redefineProp(xhr, prop, fn) { try { Object.defineProperty(xhr, prop, { get: fn }); } catch (e) {/*ignore*/ } } if (!window.FileAPI) { window.FileAPI = {}; } if (!window.XMLHttpRequest) { throw 'AJAX is not supported. XMLHttpRequest is not defined.'; } FileAPI.shouldLoad = !window.FormData || FileAPI.forceLoad; if (FileAPI.shouldLoad) { var initializeUploadListener = function (xhr) { if (!xhr.__listeners) { if (!xhr.upload) xhr.upload = {}; xhr.__listeners = []; var origAddEventListener = xhr.upload.addEventListener; xhr.upload.addEventListener = function (t, fn) { xhr.__listeners[t] = fn; if (origAddEventListener) origAddEventListener.apply(this, arguments); }; } }; patchXHR('open', function (orig) { return function (m, url, b) { initializeUploadListener(this); this.__url = url; try { orig.apply(this, [m, url, b]); } catch (e) { if (e.message.indexOf('Access is denied') > -1) { this.__origError = e; orig.apply(this, [m, '_fix_for_ie_crossdomain__', b]); } } }; }); patchXHR('getResponseHeader', function (orig) { return function (h) { return this.__fileApiXHR && this.__fileApiXHR.getResponseHeader ? this.__fileApiXHR.getResponseHeader(h) : (orig == null ? null : orig.apply(this, [h])); }; }); patchXHR('getAllResponseHeaders', function (orig) { return function () { return this.__fileApiXHR && this.__fileApiXHR.getAllResponseHeaders ? this.__fileApiXHR.getAllResponseHeaders() : (orig == null ? null : orig.apply(this)); }; }); patchXHR('abort', function (orig) { return function () { return this.__fileApiXHR && this.__fileApiXHR.abort ? this.__fileApiXHR.abort() : (orig == null ? null : orig.apply(this)); }; }); patchXHR('setRequestHeader', function (orig) { return function (header, value) { if (header === '__setXHR_') { initializeUploadListener(this); var val = value(this); // fix for angular < 1.2.0 if (val instanceof Function) { val(this); } } else { this.__requestHeaders = this.__requestHeaders || {}; this.__requestHeaders[header] = value; orig.apply(this, arguments); } }; }); patchXHR('send', function (orig) { return function () { var xhr = this; if (arguments[0] && arguments[0].__isFileAPIShim) { var formData = arguments[0]; var config = { url: xhr.__url, jsonp: false, //removes the callback form param cache: true, //removes the ?fileapiXXX in the url complete: function (err, fileApiXHR) { if (err && angular.isString(err) && err.indexOf('#2174') !== -1) { // this error seems to be fine the file is being uploaded properly. err = null; } xhr.__completed = true; if (!err && xhr.__listeners.load) xhr.__listeners.load({ type: 'load', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true }); if (!err && xhr.__listeners.loadend) xhr.__listeners.loadend({ type: 'loadend', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true }); if (err === 'abort' && xhr.__listeners.abort) xhr.__listeners.abort({ type: 'abort', loaded: xhr.__loaded, total: xhr.__total, target: xhr, lengthComputable: true }); if (fileApiXHR.status !== undefined) redefineProp(xhr, 'status', function () { return (fileApiXHR.status === 0 && err && err !== 'abort') ? 500 : fileApiXHR.status; }); if (fileApiXHR.statusText !== undefined) redefineProp(xhr, 'statusText', function () { return fileApiXHR.statusText; }); redefineProp(xhr, 'readyState', function () { return 4; }); if (fileApiXHR.response !== undefined) redefineProp(xhr, 'response', function () { return fileApiXHR.response; }); var resp = fileApiXHR.responseText || (err && fileApiXHR.status === 0 && err !== 'abort' ? err : undefined); redefineProp(xhr, 'responseText', function () { return resp; }); redefineProp(xhr, 'response', function () { return resp; }); if (err) redefineProp(xhr, 'err', function () { return err; }); xhr.__fileApiXHR = fileApiXHR; if (xhr.onreadystatechange) xhr.onreadystatechange(); if (xhr.onload) xhr.onload(); }, progress: function (e) { e.target = xhr; if (xhr.__listeners.progress) xhr.__listeners.progress(e); xhr.__total = e.total; xhr.__loaded = e.loaded; if (e.total === e.loaded) { // fix flash issue that doesn't call complete if there is no response text from the server var _this = this; setTimeout(function () { if (!xhr.__completed) { xhr.getAllResponseHeaders = function () { }; _this.complete(null, { status: 204, statusText: 'No Content' }); } }, FileAPI.noContentTimeout || 10000); } }, headers: xhr.__requestHeaders }; config.data = {}; config.files = {}; for (var i = 0; i < formData.data.length; i++) { var item = formData.data[i]; if (item.val != null && item.val.name != null && item.val.size != null && item.val.type != null) { config.files[item.key] = item.val; } else { config.data[item.key] = item.val; } } setTimeout(function () { if (!FileAPI.hasFlash) { throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"'; } xhr.__fileApiXHR = FileAPI.upload(config); }, 1); } else { if (this.__origError) { throw this.__origError; } orig.apply(xhr, arguments); } }; }); window.XMLHttpRequest.__isFileAPIShim = true; window.FormData = FormData = function () { return { append: function (key, val, name) { if (val.__isFileAPIBlobShim) { val = val.data[0]; } this.data.push({ key: key, val: val, name: name }); }, data: [], __isFileAPIShim: true }; }; window.Blob = Blob = function (b) { return { data: b, __isFileAPIBlobShim: true }; }; } })(); (function () { /** @namespace FileAPI.forceLoad */ /** @namespace window.FileAPI.jsUrl */ /** @namespace window.FileAPI.jsPath */ function isInputTypeFile(elem) { return elem[0].tagName.toLowerCase() === 'input' && elem.attr('type') && elem.attr('type').toLowerCase() === 'file'; } function hasFlash() { try { var fo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); if (fo) return true; } catch (e) { if (navigator.mimeTypes['application/x-shockwave-flash'] !== undefined) return true; } return false; } function getOffset(obj) { var left = 0, top = 0; if (window.jQuery) { return jQuery(obj).offset(); } if (obj.offsetParent) { do { left += (obj.offsetLeft - obj.scrollLeft); top += (obj.offsetTop - obj.scrollTop); obj = obj.offsetParent; } while (obj); } return { left: left, top: top }; } if (FileAPI.shouldLoad) { FileAPI.hasFlash = hasFlash(); //load FileAPI if (FileAPI.forceLoad) { FileAPI.html5 = false; } if (!FileAPI.upload) { var jsUrl, basePath, script = document.createElement('script'), allScripts = document.getElementsByTagName('script'), i, index, src; if (window.FileAPI.jsUrl) { jsUrl = window.FileAPI.jsUrl; } else if (window.FileAPI.jsPath) { basePath = window.FileAPI.jsPath; } else { for (i = 0; i < allScripts.length; i++) { src = allScripts[i].src; index = src.search(/\/ng\-file\-upload[\-a-zA-z0-9\.]*\.js/); if (index > -1) { basePath = src.substring(0, index + 1); break; } } } if (FileAPI.staticPath == null) FileAPI.staticPath = basePath; script.setAttribute('src', jsUrl || basePath + 'FileAPI.min.js'); document.getElementsByTagName('head')[0].appendChild(script); } FileAPI.ngfFixIE = function (elem, fileElem, changeFn) { if (!hasFlash()) { throw 'Adode Flash Player need to be installed. To check ahead use "FileAPI.hasFlash"'; } var fixInputStyle = function () { var label = fileElem.parent(); if (elem.attr('disabled')) { if (label) label.removeClass('js-fileapi-wrapper'); } else { if (!fileElem.attr('__ngf_flash_')) { fileElem.unbind('change'); fileElem.unbind('click'); fileElem.bind('change', function (evt) { fileApiChangeFn.apply(this, [evt]); changeFn.apply(this, [evt]); }); fileElem.attr('__ngf_flash_', 'true'); } label.addClass('js-fileapi-wrapper'); if (!isInputTypeFile(elem)) { label.css('position', 'absolute') .css('top', getOffset(elem[0]).top + 'px').css('left', getOffset(elem[0]).left + 'px') .css('width', elem[0].offsetWidth + 'px').css('height', elem[0].offsetHeight + 'px') .css('filter', 'alpha(opacity=0)').css('display', elem.css('display')) .css('overflow', 'hidden').css('z-index', '900000') .css('visibility', 'visible'); fileElem.css('width', elem[0].offsetWidth + 'px').css('height', elem[0].offsetHeight + 'px') .css('position', 'absolute').css('top', '0px').css('left', '0px'); } } }; elem.bind('mouseenter', fixInputStyle); var fileApiChangeFn = function (evt) { var files = FileAPI.getFiles(evt); //just a double check for #233 for (var i = 0; i < files.length; i++) { if (files[i].size === undefined) files[i].size = 0; if (files[i].name === undefined) files[i].name = 'file'; if (files[i].type === undefined) files[i].type = 'undefined'; } if (!evt.target) { evt.target = {}; } evt.target.files = files; // if evt.target.files is not writable use helper field if (evt.target.files !== files) { evt.__files_ = files; } (evt.__files_ || evt.target.files).item = function (i) { return (evt.__files_ || evt.target.files)[i] || null; }; }; }; FileAPI.disableFileInput = function (elem, disable) { if (disable) { elem.removeClass('js-fileapi-wrapper'); } else { elem.addClass('js-fileapi-wrapper'); } }; } })(); if (!window.FileReader) { window.FileReader = function () { var _this = this, loadStarted = false; this.listeners = {}; this.addEventListener = function (type, fn) { _this.listeners[type] = _this.listeners[type] || []; _this.listeners[type].push(fn); }; this.removeEventListener = function (type, fn) { if (_this.listeners[type]) _this.listeners[type].splice(_this.listeners[type].indexOf(fn), 1); }; this.dispatchEvent = function (evt) { var list = _this.listeners[evt.type]; if (list) { for (var i = 0; i < list.length; i++) { list[i].call(_this, evt); } } }; this.onabort = this.onerror = this.onload = this.onloadstart = this.onloadend = this.onprogress = null; var constructEvent = function (type, evt) { var e = { type: type, target: _this, loaded: evt.loaded, total: evt.total, error: evt.error }; if (evt.result != null) e.target.result = evt.result; return e; }; var listener = function (evt) { if (!loadStarted) { loadStarted = true; if (_this.onloadstart) _this.onloadstart(constructEvent('loadstart', evt)); } var e; if (evt.type === 'load') { if (_this.onloadend) _this.onloadend(constructEvent('loadend', evt)); e = constructEvent('load', evt); if (_this.onload) _this.onload(e); _this.dispatchEvent(e); } else if (evt.type === 'progress') { e = constructEvent('progress', evt); if (_this.onprogress) _this.onprogress(e); _this.dispatchEvent(e); } else { e = constructEvent('error', evt); if (_this.onerror) _this.onerror(e); _this.dispatchEvent(e); } }; this.readAsDataURL = function (file) { FileAPI.readAsDataURL(file, listener); }; this.readAsText = function (file) { FileAPI.readAsText(file, listener); }; }; } /**! * AngularJS file upload directives and services. Supoorts: file upload/drop/paste, resume, cancel/abort, * progress, resize, thumbnail, preview, validation and CORS * @author Danial * @version 12.2.13 */ if (window.XMLHttpRequest && !(window.FileAPI && FileAPI.shouldLoad)) { window.XMLHttpRequest.prototype.setRequestHeader = (function (orig) { return function (header, value) { if (header === '__setXHR_') { var val = value(this); // fix for angular < 1.2.0 if (val instanceof Function) { val(this); } } else { orig.apply(this, arguments); } }; })(window.XMLHttpRequest.prototype.setRequestHeader); } var ngFileUpload = angular.module('ngFileUpload', []); ngFileUpload.version = '12.2.13'; ngFileUpload.service('UploadBase', ['$http', '$q', '$timeout', function ($http, $q, $timeout) { var upload = this; upload.promisesCount = 0; this.isResumeSupported = function () { return window.Blob && window.Blob.prototype.slice; }; var resumeSupported = this.isResumeSupported(); function sendHttp(config) { config.method = config.method || 'POST'; config.headers = config.headers || {}; var deferred = config._deferred = config._deferred || $q.defer(); var promise = deferred.promise; function notifyProgress(e) { if (deferred.notify) { deferred.notify(e); } if (promise.progressFunc) { $timeout(function () { promise.progressFunc(e); }); } } function getNotifyEvent(n) { if (config._start != null && resumeSupported) { return { loaded: n.loaded + config._start, total: (config._file && config._file.size) || n.total, type: n.type, config: config, lengthComputable: true, target: n.target }; } else { return n; } } if (!config.disableProgress) { config.headers.__setXHR_ = function () { return function (xhr) { if (!xhr || !xhr.upload || !xhr.upload.addEventListener) return; config.__XHR = xhr; if (config.xhrFn) config.xhrFn(xhr); xhr.upload.addEventListener('progress', function (e) { e.config = config; notifyProgress(getNotifyEvent(e)); }, false); //fix for firefox not firing upload progress end, also IE8-9 xhr.upload.addEventListener('load', function (e) { if (e.lengthComputable) { e.config = config; notifyProgress(getNotifyEvent(e)); } }, false); }; }; } function uploadWithAngular() { $http(config).then(function (r) { if (resumeSupported && config._chunkSize && !config._finished && config._file) { var fileSize = config._file && config._file.size || 0; notifyProgress({ loaded: Math.min(config._end, fileSize), total: fileSize, config: config, type: 'progress' } ); upload.upload(config, true); } else { if (config._finished) delete config._finished; deferred.resolve(r); } }, function (e) { deferred.reject(e); }, function (n) { deferred.notify(n); } ); } if (!resumeSupported) { uploadWithAngular(); } else if (config._chunkSize && config._end && !config._finished) { config._start = config._end; config._end += config._chunkSize; uploadWithAngular(); } else if (config.resumeSizeUrl) { $http.get(config.resumeSizeUrl).then(function (resp) { if (config.resumeSizeResponseReader) { config._start = config.resumeSizeResponseReader(resp.data); } else { config._start = parseInt((resp.data.size == null ? resp.data : resp.data.size).toString()); } if (config._chunkSize) { config._end = config._start + config._chunkSize; } uploadWithAngular(); }, function (e) { throw e; }); } else if (config.resumeSize) { config.resumeSize().then(function (size) { config._start = size; if (config._chunkSize) { config._end = config._start + config._chunkSize; } uploadWithAngular(); }, function (e) { throw e; }); } else { if (config._chunkSize) { config._start = 0; config._end = config._start + config._chunkSize; } uploadWithAngular(); } promise.success = function (fn) { promise.then(function (response) { fn(response.data, response.status, response.headers, config); }); return promise; }; promise.error = function (fn) { promise.then(null, function (response) { fn(response.data, response.status, response.headers, config); }); return promise; }; promise.progress = function (fn) { promise.progressFunc = fn; promise.then(null, null, function (n) { fn(n); }); return promise; }; promise.abort = promise.pause = function () { if (config.__XHR) { $timeout(function () { config.__XHR.abort(); }); } return promise; }; promise.xhr = function (fn) { config.xhrFn = (function (origXhrFn) { return function () { if (origXhrFn) origXhrFn.apply(promise, arguments); fn.apply(promise, arguments); }; })(config.xhrFn); return promise; }; upload.promisesCount++; if (promise['finally'] && promise['finally'] instanceof Function) { promise['finally'](function () { upload.promisesCount--; }); } return promise; } this.isUploadInProgress = function () { return upload.promisesCount > 0; }; this.rename = function (file, name) { file.ngfName = name; return file; }; this.jsonBlob = function (val) { if (val != null && !angular.isString(val)) { val = JSON.stringify(val); } var blob = new window.Blob([val], { type: 'application/json' }); blob._ngfBlob = true; return blob; }; this.json = function (val) { return angular.toJson(val); }; function copy(obj) { var clone = {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = obj[key]; } } return clone; } this.isFile = function (file) { return file != null && (file instanceof window.Blob || (file.flashId && file.name && file.size)); }; this.upload = function (config, internal) { function toResumeFile(file, formData) { if (file._ngfBlob) return file; config._file = config._file || file; if (config._start != null && resumeSupported) { if (config._end && config._end >= file.size) { config._finished = true; config._end = file.size; } var slice = file.slice(config._start, config._end || file.size); slice.name = file.name; slice.ngfName = file.ngfName; if (config._chunkSize) { formData.append('_chunkSize', config._chunkSize); formData.append('_currentChunkSize', config._end - config._start); formData.append('_chunkNumber', Math.floor(config._start / config._chunkSize)); formData.append('_totalSize', config._file.size); } return slice; } return file; } function addFieldToFormData(formData, val, key) { if (val !== undefined) { if (angular.isDate(val)) { val = val.toISOString(); } if (angular.isString(val)) { formData.append(key, val); } else if (upload.isFile(val)) { var file = toResumeFile(val, formData); var split = key.split(','); if (split[1]) { file.ngfName = split[1].replace(/^\s+|\s+$/g, ''); key = split[0]; } config._fileKey = config._fileKey || key; formData.append(key, file, file.ngfName || file.name); } else { if (angular.isObject(val)) { if (val.$$ngfCircularDetection) throw 'ngFileUpload: Circular reference in config.data. Make sure specified data for Upload.upload() has no circular reference: ' + key; val.$$ngfCircularDetection = true; try { for (var k in val) { if (val.hasOwnProperty(k) && k !== '$$ngfCircularDetection') { var objectKey = config.objectKey == null ? '[i]' : config.objectKey; if (val.length && parseInt(k) > -1) { objectKey = config.arrayKey == null ? objectKey : config.arrayKey; } addFieldToFormData(formData, val[k], key + objectKey.replace(/[ik]/g, k)); } } } finally { delete val.$$ngfCircularDetection; } } else { formData.append(key, val); } } } } function digestConfig() { config._chunkSize = upload.translateScalars(config.resumeChunkSize); config._chunkSize = config._chunkSize ? parseInt(config._chunkSize.toString()) : null; config.headers = config.headers || {}; config.headers['Content-Type'] = undefined; config.transformRequest = config.transformRequest ? (angular.isArray(config.transformRequest) ? config.transformRequest : [config.transformRequest]) : []; config.transformRequest.push(function (data) { var formData = new window.FormData(), key; data = data || config.fields || {}; if (config.file) { data.file = config.file; } for (key in data) { if (data.hasOwnProperty(key)) { var val = data[key]; if (config.formDataAppender) { config.formDataAppender(formData, key, val); } else { addFieldToFormData(formData, val, key); } } } return formData; }); } if (!internal) config = copy(config); if (!config._isDigested) { config._isDigested = true; digestConfig(); } return sendHttp(config); }; this.http = function (config) { config = copy(config); config.transformRequest = config.transformRequest || function (data) { if ((window.ArrayBuffer && data instanceof window.ArrayBuffer) || data instanceof window.Blob) { return data; } return $http.defaults.transformRequest[0].apply(this, arguments); }; config._chunkSize = upload.translateScalars(config.resumeChunkSize); config._chunkSize = config._chunkSize ? parseInt(config._chunkSize.toString()) : null; return sendHttp(config); }; this.translateScalars = function (str) { if (angular.isString(str)) { if (str.search(/kb/i) === str.length - 2) { return parseFloat(str.substring(0, str.length - 2) * 1024); } else if (str.search(/mb/i) === str.length - 2) { return parseFloat(str.substring(0, str.length - 2) * 1048576); } else if (str.search(/gb/i) === str.length - 2) { return parseFloat(str.substring(0, str.length - 2) * 1073741824); } else if (str.search(/b/i) === str.length - 1) { return parseFloat(str.substring(0, str.length - 1)); } else if (str.search(/s/i) === str.length - 1) { return parseFloat(str.substring(0, str.length - 1)); } else if (str.search(/m/i) === str.length - 1) { return parseFloat(str.substring(0, str.length - 1) * 60); } else if (str.search(/h/i) === str.length - 1) { return parseFloat(str.substring(0, str.length - 1) * 3600); } } return str; }; this.urlToBlob = function (url) { var defer = $q.defer(); $http({ url: url, method: 'get', responseType: 'arraybuffer' }).then(function (resp) { var arrayBufferView = new Uint8Array(resp.data); var type = resp.headers('content-type') || 'image/WebP'; var blob = new window.Blob([arrayBufferView], { type: type }); var matches = url.match(/.*\/(.+?)(\?.*)?$/); if (matches.length > 1) { blob.name = matches[1]; } defer.resolve(blob); }, function (e) { defer.reject(e); }); return defer.promise; }; this.setDefaults = function (defaults) { this.defaults = defaults || {}; }; this.defaults = {}; this.version = ngFileUpload.version; } ]); ngFileUpload.service('Upload', ['$parse', '$timeout', '$compile', '$q', 'UploadExif', function ($parse, $timeout, $compile, $q, UploadExif) { var upload = UploadExif; upload.getAttrWithDefaults = function (attr, name) { if (attr[name] != null) return attr[name]; var def = upload.defaults[name]; return (def == null ? def : (angular.isString(def) ? def : JSON.stringify(def))); }; upload.attrGetter = function (name, attr, scope, params) { var attrVal = this.getAttrWithDefaults(attr, name); if (scope) { try { if (params) { return $parse(attrVal)(scope, params); } else { return $parse(attrVal)(scope); } } catch (e) { // hangle string value without single qoute if (name.search(/min|max|pattern/i)) { return attrVal; } else { throw e; } } } else { return attrVal; } }; upload.shouldUpdateOn = function (type, attr, scope) { var modelOptions = upload.attrGetter('ngfModelOptions', attr, scope); if (modelOptions && modelOptions.updateOn) { return modelOptions.updateOn.split(' ').indexOf(type) > -1; } return true; }; upload.emptyPromise = function () { var d = $q.defer(); var args = arguments; $timeout(function () { d.resolve.apply(d, args); }); return d.promise; }; upload.rejectPromise = function () { var d = $q.defer(); var args = arguments; $timeout(function () { d.reject.apply(d, args); }); return d.promise; }; upload.happyPromise = function (promise, data) { var d = $q.defer(); promise.then(function (result) { d.resolve(result); }, function (error) { $timeout(function () { throw error; }); d.resolve(data); }); return d.promise; }; function applyExifRotations(files, attr, scope) { var promises = [upload.emptyPromise()]; angular.forEach(files, function (f, i) { if (f.type.indexOf('image/jpeg') === 0 && upload.attrGetter('ngfFixOrientation', attr, scope, { $file: f })) { promises.push(upload.happyPromise(upload.applyExifRotation(f), f).then(function (fixedFile) { files.splice(i, 1, fixedFile); })); } }); return $q.all(promises); } function resizeFile(files, attr, scope, ngModel) { var resizeVal = upload.attrGetter('ngfResize', attr, scope); if (!resizeVal || !upload.isResizeSupported() || !files.length) return upload.emptyPromise(); if (resizeVal instanceof Function) { var defer = $q.defer(); return resizeVal(files).then(function (p) { resizeWithParams(p, files, attr, scope, ngModel).then(function (r) { defer.resolve(r); }, function (e) { defer.reject(e); }); }, function (e) { defer.reject(e); }); } else { return resizeWithParams(resizeVal, files, attr, scope, ngModel); } } function resizeWithParams(params, files, attr, scope, ngModel) { var promises = [upload.emptyPromise()]; function handleFile(f, i) { if (f.type.indexOf('image') === 0) { if (params.pattern && !upload.validatePattern(f, params.pattern)) return; params.resizeIf = function (width, height) { return upload.attrGetter('ngfResizeIf', attr, scope, { $width: width, $height: height, $file: f }); }; var promise = upload.resize(f, params); promises.push(promise); promise.then(function (resizedFile) { files.splice(i, 1, resizedFile); }, function (e) { f.$error = 'resize'; (f.$errorMessages = (f.$errorMessages || {})).resize = true; f.$errorParam = (e ? (e.message ? e.message : e) + ': ' : '') + (f && f.name); ngModel.$ngfValidations.push({ name: 'resize', valid: false }); upload.applyModelValidation(ngModel, files); }); } } for (var i = 0; i < files.length; i++) { handleFile(files[i], i); } return $q.all(promises); } upload.updateModel = function (ngModel, attr, scope, fileChange, files, evt, noDelay) { function update(files, invalidFiles, newFiles, dupFiles, isSingleModel) { attr.$$ngfPrevValidFiles = files; attr.$$ngfPrevInvalidFiles = invalidFiles; var file = files && files.length ? files[0] : null; var invalidFile = invalidFiles && invalidFiles.length ? invalidFiles[0] : null; if (ngModel) { upload.applyModelValidation(ngModel, files); ngModel.$setViewValue(isSingleModel ? file : files); } if (fileChange) { $parse(fileChange)(scope, { $files: files, $file: file, $newFiles: newFiles, $duplicateFiles: dupFiles, $invalidFiles: invalidFiles, $invalidFile: invalidFile, $event: evt }); } var invalidModel = upload.attrGetter('ngfModelInvalid', attr); if (invalidModel) { $timeout(function () { $parse(invalidModel).assign(scope, isSingleModel ? invalidFile : invalidFiles); }); } $timeout(function () { // scope apply changes }); } var allNewFiles, dupFiles = [], prevValidFiles, prevInvalidFiles, invalids = [], valids = []; function removeDuplicates() { function equals(f1, f2) { return f1.name === f2.name && (f1.$ngfOrigSize || f1.size) === (f2.$ngfOrigSize || f2.size) && f1.type === f2.type; } function isInPrevFiles(f) { var j; for (j = 0; j < prevValidFiles.length; j++) { if (equals(f, prevValidFiles[j])) { return true; } } for (j = 0; j < prevInvalidFiles.length; j++) { if (equals(f, prevInvalidFiles[j])) { return true; } } return false; } if (files) { allNewFiles = []; dupFiles = []; for (var i = 0; i < files.length; i++) { if (isInPrevFiles(files[i])) { dupFiles.push(files[i]); } else { allNewFiles.push(files[i]); } } } } function toArray(v) { return angular.isArray(v) ? v : [v]; } function resizeAndUpdate() { function updateModel() { $timeout(function () { update(keep ? prevValidFiles.concat(valids) : valids, keep ? prevInvalidFiles.concat(invalids) : invalids, files, dupFiles, isSingleModel); }, options && options.debounce ? options.debounce.change || options.debounce : 0); } var resizingFiles = validateAfterResize ? allNewFiles : valids; resizeFile(resizingFiles, attr, scope, ngModel).then(function () { if (validateAfterResize) { upload.validate(allNewFiles, keep ? prevValidFiles.length : 0, ngModel, attr, scope) .then(function (validationResult) { valids = validationResult.validsFiles; invalids = validationResult.invalidsFiles; updateModel(); }); } else { updateModel(); } }, function () { for (var i = 0; i < resizingFiles.length; i++) { var f = resizingFiles[i]; if (f.$error === 'resize') { var index = valids.indexOf(f); if (index > -1) { valids.splice(index, 1); invalids.push(f); } updateModel(); } } }); } prevValidFiles = attr.$$ngfPrevValidFiles || []; prevInvalidFiles = attr.$$ngfPrevInvalidFiles || []; if (ngModel && ngModel.$modelValue) { prevValidFiles = toArray(ngModel.$modelValue); } var keep = upload.attrGetter('ngfKeep', attr, scope); allNewFiles = (files || []).slice(0); if (keep === 'distinct' || upload.attrGetter('ngfKeepDistinct', attr, scope) === true) { removeDuplicates(attr, scope); } var isSingleModel = !keep && !upload.attrGetter('ngfMultiple', attr, scope) && !upload.attrGetter('multiple', attr); if (keep && !allNewFiles.length) return; upload.attrGetter('ngfBeforeModelChange', attr, scope, { $files: files, $file: files && files.length ? files[0] : null, $newFiles: allNewFiles, $duplicateFiles: dupFiles, $event: evt }); var validateAfterResize = upload.attrGetter('ngfValidateAfterResize', attr, scope); var options = upload.attrGetter('ngfModelOptions', attr, scope); upload.validate(allNewFiles, keep ? prevValidFiles.length : 0, ngModel, attr, scope) .then(function (validationResult) { if (noDelay) { update(allNewFiles, [], files, dupFiles, isSingleModel); } else { if ((!options || !options.allowInvalid) && !validateAfterResize) { valids = validationResult.validFiles; invalids = validationResult.invalidFiles; } else { valids = allNewFiles; } if (upload.attrGetter('ngfFixOrientation', attr, scope) && upload.isExifSupported()) { applyExifRotations(valids, attr, scope).then(function () { resizeAndUpdate(); }); } else { resizeAndUpdate(); } } }); }; return upload; }]); ngFileUpload.directive('ngfSelect', ['$parse', '$timeout', '$compile', 'Upload', function ($parse, $timeout, $compile, Upload) { var generatedElems = []; function isDelayedClickSupported(ua) { // fix for android native browser < 4.4 and safari windows var m = ua.match(/Android[^\d]*(\d+)\.(\d+)/); if (m && m.length > 2) { var v = Upload.defaults.androidFixMinorVersion || 4; return parseInt(m[1]) < 4 || (parseInt(m[1]) === v && parseInt(m[2]) < v); } // safari on windows return ua.indexOf('Chrome') === -1 && /.*Windows.*Safari.*/.test(ua); } function linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile, upload) { /** @namespace attr.ngfSelect */ /** @namespace attr.ngfChange */ /** @namespace attr.ngModel */ /** @namespace attr.ngfModelOptions */ /** @namespace attr.ngfMultiple */ /** @namespace attr.ngfCapture */ /** @namespace attr.ngfValidate */ /** @namespace attr.ngfKeep */ var attrGetter = function (name, scope) { return upload.attrGetter(name, attr, scope); }; function isInputTypeFile() { return elem[0].tagName.toLowerCase() === 'input' && attr.type && attr.type.toLowerCase() === 'file'; } function fileChangeAttr() { return attrGetter('ngfChange') || attrGetter('ngfSelect'); } function changeFn(evt) { if (upload.shouldUpdateOn('change', attr, scope)) { var fileList = evt.__files_ || (evt.target && evt.target.files), files = []; /* Handle duplicate call in IE11 */ if (!fileList) return; for (var i = 0; i < fileList.length; i++) { files.push(fileList[i]); } upload.updateModel(ngModel, attr, scope, fileChangeAttr(), files.length ? files : null, evt); } } upload.registerModelChangeValidator(ngModel, attr, scope); var unwatches = []; if (attrGetter('ngfMultiple')) { unwatches.push(scope.$watch(attrGetter('ngfMultiple'), function () { fileElem.attr('multiple', attrGetter('ngfMultiple', scope)); })); } if (attrGetter('ngfCapture')) { unwatches.push(scope.$watch(attrGetter('ngfCapture'), function () { fileElem.attr('capture', attrGetter('ngfCapture', scope)); })); } if (attrGetter('ngfAccept')) { unwatches.push(scope.$watch(attrGetter('ngfAccept'), function () { fileElem.attr('accept', attrGetter('ngfAccept', scope)); })); } unwatches.push(attr.$observe('accept', function () { fileElem.attr('accept', attrGetter('accept')); })); function bindAttrToFileInput(fileElem, label) { function updateId(val) { fileElem.attr('id', 'ngf-' + val); label.attr('id', 'ngf-label-' + val); } for (var i = 0; i < elem[0].attributes.length; i++) { var attribute = elem[0].attributes[i]; if (attribute.name !== 'type' && attribute.name !== 'class' && attribute.name !== 'style') { if (attribute.name === 'id') { updateId(attribute.value); unwatches.push(attr.$observe('id', updateId)); } else { fileElem.attr(attribute.name, (!attribute.value && (attribute.name === 'required' || attribute.name === 'multiple')) ? attribute.name : attribute.value); } } } } function createFileInput() { if (isInputTypeFile()) { return elem; } var fileElem = angular.element(''); var label = angular.element(''); label.css('visibility', 'hidden').css('position', 'absolute').css('overflow', 'hidden') .css('width', '0px').css('height', '0px').css('border', 'none') .css('margin', '0px').css('padding', '0px').attr('tabindex', '-1'); bindAttrToFileInput(fileElem, label); generatedElems.push({ el: elem, ref: label }); document.body.appendChild(label.append(fileElem)[0]); return fileElem; } function clickHandler(evt) { if (elem.attr('disabled')) return false; if (attrGetter('ngfSelectDisabled', scope)) return; var r = detectSwipe(evt); // prevent the click if it is a swipe if (r != null) return r; resetModel(evt); // fix for md when the element is removed from the DOM and added back #460 try { if (!isInputTypeFile() && !document.body.contains(fileElem[0])) { generatedElems.push({ el: elem, ref: fileElem.parent() }); document.body.appendChild(fileElem.parent()[0]); fileElem.bind('change', changeFn); } } catch (e) {/*ignore*/ } if (isDelayedClickSupported(navigator.userAgent)) { setTimeout(function () { fileElem[0].click(); }, 0); } else { fileElem[0].click(); } return false; } var initialTouchStartY = 0; var initialTouchStartX = 0; function detectSwipe(evt) { var touches = evt.changedTouches || (evt.originalEvent && evt.originalEvent.changedTouches); if (touches) { if (evt.type === 'touchstart') { initialTouchStartX = touches[0].clientX; initialTouchStartY = touches[0].clientY; return true; // don't block event default } else { // prevent scroll from triggering event if (evt.type === 'touchend') { var currentX = touches[0].clientX; var currentY = touches[0].clientY; if ((Math.abs(currentX - initialTouchStartX) > 20) || (Math.abs(currentY - initialTouchStartY) > 20)) { evt.stopPropagation(); evt.preventDefault(); return false; } } return true; } } } var fileElem = elem; function resetModel(evt) { if (upload.shouldUpdateOn('click', attr, scope) && fileElem.val()) { fileElem.val(null); upload.updateModel(ngModel, attr, scope, fileChangeAttr(), null, evt, true); } } if (!isInputTypeFile()) { fileElem = createFileInput(); } fileElem.bind('change', changeFn); if (!isInputTypeFile()) { elem.bind('click touchstart touchend', clickHandler); } else { elem.bind('click', resetModel); } function ie10SameFileSelectFix(evt) { if (fileElem && !fileElem.attr('__ngf_ie10_Fix_')) { if (!fileElem[0].parentNode) { fileElem = null; return; } evt.preventDefault(); evt.stopPropagation(); fileElem.unbind('click'); var clone = fileElem.clone(); fileElem.replaceWith(clone); fileElem = clone; fileElem.attr('__ngf_ie10_Fix_', 'true'); fileElem.bind('change', changeFn); fileElem.bind('click', ie10SameFileSelectFix); fileElem[0].click(); return false; } else { fileElem.removeAttr('__ngf_ie10_Fix_'); } } if (navigator.appVersion.indexOf('MSIE 10') !== -1) { fileElem.bind('click', ie10SameFileSelectFix); } if (ngModel) ngModel.$formatters.push(function (val) { if (val == null || val.length === 0) { if (fileElem.val()) { fileElem.val(null); } } return val; }); scope.$on('$destroy', function () { if (!isInputTypeFile()) fileElem.parent().remove(); angular.forEach(unwatches, function (unwatch) { unwatch(); }); }); $timeout(function () { for (var i = 0; i < generatedElems.length; i++) { var g = generatedElems[i]; if (!document.body.contains(g.el[0])) { generatedElems.splice(i, 1); g.ref.remove(); } } }); if (window.FileAPI && window.FileAPI.ngfFixIE) { window.FileAPI.ngfFixIE(elem, fileElem, changeFn); } } return { restrict: 'AEC', require: '?ngModel', link: function (scope, elem, attr, ngModel) { linkFileSelect(scope, elem, attr, ngModel, $parse, $timeout, $compile, Upload); } }; }]); (function () { ngFileUpload.service('UploadDataUrl', ['UploadBase', '$timeout', '$q', function (UploadBase, $timeout, $q) { var upload = UploadBase; upload.base64DataUrl = function (file) { if (angular.isArray(file)) { var d = $q.defer(), count = 0; angular.forEach(file, function (f) { upload.dataUrl(f, true)['finally'](function () { count++; if (count === file.length) { var urls = []; angular.forEach(file, function (ff) { urls.push(ff.$ngfDataUrl); }); d.resolve(urls, file); } }); }); return d.promise; } else { return upload.dataUrl(file, true); } }; upload.dataUrl = function (file, disallowObjectUrl) { if (!file) return upload.emptyPromise(file, file); if ((disallowObjectUrl && file.$ngfDataUrl != null) || (!disallowObjectUrl && file.$ngfBlobUrl != null)) { return upload.emptyPromise(disallowObjectUrl ? file.$ngfDataUrl : file.$ngfBlobUrl, file); } var p = disallowObjectUrl ? file.$$ngfDataUrlPromise : file.$$ngfBlobUrlPromise; if (p) return p; var deferred = $q.defer(); $timeout(function () { if (window.FileReader && file && (!window.FileAPI || navigator.userAgent.indexOf('MSIE 8') === -1 || file.size < 20000) && (!window.FileAPI || navigator.userAgent.indexOf('MSIE 9') === -1 || file.size < 4000000)) { //prefer URL.createObjectURL for handling refrences to files of all sizes //since it doesn´t build a large string in memory var URL = window.URL || window.webkitURL; if (URL && URL.createObjectURL && !disallowObjectUrl) { var url; try { url = URL.createObjectURL(file); } catch (e) { $timeout(function () { file.$ngfBlobUrl = ''; deferred.reject(); }); return; } $timeout(function () { file.$ngfBlobUrl = url; if (url) { deferred.resolve(url, file); upload.blobUrls = upload.blobUrls || []; upload.blobUrlsTotalSize = upload.blobUrlsTotalSize || 0; upload.blobUrls.push({ url: url, size: file.size }); upload.blobUrlsTotalSize += file.size || 0; var maxMemory = upload.defaults.blobUrlsMaxMemory || 268435456; var maxLength = upload.defaults.blobUrlsMaxQueueSize || 200; while ((upload.blobUrlsTotalSize > maxMemory || upload.blobUrls.length > maxLength) && upload.blobUrls.length > 1) { var obj = upload.blobUrls.splice(0, 1)[0]; URL.revokeObjectURL(obj.url); upload.blobUrlsTotalSize -= obj.size; } } }); } else { var fileReader = new FileReader(); fileReader.onload = function (e) { $timeout(function () { file.$ngfDataUrl = e.target.result; deferred.resolve(e.target.result, file); $timeout(function () { delete file.$ngfDataUrl; }, 1000); }); }; fileReader.onerror = function () { $timeout(function () { file.$ngfDataUrl = ''; deferred.reject(); }); }; fileReader.readAsDataURL(file); } } else { $timeout(function () { file[disallowObjectUrl ? '$ngfDataUrl' : '$ngfBlobUrl'] = ''; deferred.reject(); }); } }); if (disallowObjectUrl) { p = file.$$ngfDataUrlPromise = deferred.promise; } else { p = file.$$ngfBlobUrlPromise = deferred.promise; } p['finally'](function () { delete file[disallowObjectUrl ? '$$ngfDataUrlPromise' : '$$ngfBlobUrlPromise']; }); return p; }; return upload; }]); function getTagType(el) { if (el.tagName.toLowerCase() === 'img') return 'image'; if (el.tagName.toLowerCase() === 'audio') return 'audio'; if (el.tagName.toLowerCase() === 'video') return 'video'; return /./; } function linkFileDirective(Upload, $timeout, scope, elem, attr, directiveName, resizeParams, isBackground) { function constructDataUrl(file) { var disallowObjectUrl = Upload.attrGetter('ngfNoObjectUrl', attr, scope); Upload.dataUrl(file, disallowObjectUrl)['finally'](function () { $timeout(function () { var src = (disallowObjectUrl ? file.$ngfDataUrl : file.$ngfBlobUrl) || file.$ngfDataUrl; if (isBackground) { elem.css('background-image', 'url(\'' + (src || '') + '\')'); } else { elem.attr('src', src); } if (src) { elem.removeClass('ng-hide'); } else { elem.addClass('ng-hide'); } }); }); } $timeout(function () { var unwatch = scope.$watch(attr[directiveName], function (file) { var size = resizeParams; if (directiveName === 'ngfThumbnail') { if (!size) { size = { width: elem[0].naturalWidth || elem[0].clientWidth, height: elem[0].naturalHeight || elem[0].clientHeight }; } if (size.width === 0 && window.getComputedStyle) { var style = getComputedStyle(elem[0]); if (style.width && style.width.indexOf('px') > -1 && style.height && style.height.indexOf('px') > -1) { size = { width: parseInt(style.width.slice(0, -2)), height: parseInt(style.height.slice(0, -2)) }; } } } if (angular.isString(file)) { elem.removeClass('ng-hide'); if (isBackground) { return elem.css('background-image', 'url(\'' + file + '\')'); } else { return elem.attr('src', file); } } if (file && file.type && file.type.search(getTagType(elem[0])) === 0 && (!isBackground || file.type.indexOf('image') === 0)) { if (size && Upload.isResizeSupported()) { size.resizeIf = function (width, height) { return Upload.attrGetter('ngfResizeIf', attr, scope, { $width: width, $height: height, $file: file }); }; Upload.resize(file, size).then( function (f) { constructDataUrl(f); }, function (e) { throw e; } ); } else { constructDataUrl(file); } } else { elem.addClass('ng-hide'); } }); scope.$on('$destroy', function () { unwatch(); }); }); } /** @namespace attr.ngfSrc */ /** @namespace attr.ngfNoObjectUrl */ ngFileUpload.directive('ngfSrc', ['Upload', '$timeout', function (Upload, $timeout) { return { restrict: 'AE', link: function (scope, elem, attr) { linkFileDirective(Upload, $timeout, scope, elem, attr, 'ngfSrc', Upload.attrGetter('ngfResize', attr, scope), false); } }; }]); /** @namespace attr.ngfBackground */ /** @namespace attr.ngfNoObjectUrl */ ngFileUpload.directive('ngfBackground', ['Upload', '$timeout', function (Upload, $timeout) { return { restrict: 'AE', link: function (scope, elem, attr) { linkFileDirective(Upload, $timeout, scope, elem, attr, 'ngfBackground', Upload.attrGetter('ngfResize', attr, scope), true); } }; }]); /** @namespace attr.ngfThumbnail */ /** @namespace attr.ngfAsBackground */ /** @namespace attr.ngfSize */ /** @namespace attr.ngfNoObjectUrl */ ngFileUpload.directive('ngfThumbnail', ['Upload', '$timeout', function (Upload, $timeout) { return { restrict: 'AE', link: function (scope, elem, attr) { var size = Upload.attrGetter('ngfSize', attr, scope); linkFileDirective(Upload, $timeout, scope, elem, attr, 'ngfThumbnail', size, Upload.attrGetter('ngfAsBackground', attr, scope)); } }; }]); ngFileUpload.config(['$compileProvider', function ($compileProvider) { if ($compileProvider.imgSrcSanitizationWhitelist) $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|webcal|local|file|data|blob):/); if ($compileProvider.aHrefSanitizationWhitelist) $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|webcal|local|file|data|blob):/); }]); ngFileUpload.filter('ngfDataUrl', ['UploadDataUrl', '$sce', function (UploadDataUrl, $sce) { return function (file, disallowObjectUrl, trustedUrl) { if (angular.isString(file)) { return $sce.trustAsResourceUrl(file); } var src = file && ((disallowObjectUrl ? file.$ngfDataUrl : file.$ngfBlobUrl) || file.$ngfDataUrl); if (file && !src) { if (!file.$ngfDataUrlFilterInProgress && angular.isObject(file)) { file.$ngfDataUrlFilterInProgress = true; UploadDataUrl.dataUrl(file, disallowObjectUrl); } return ''; } if (file) delete file.$ngfDataUrlFilterInProgress; return (file && src ? (trustedUrl ? $sce.trustAsResourceUrl(src) : src) : file) || ''; }; }]); })(); ngFileUpload.service('UploadValidate', ['UploadDataUrl', '$q', '$timeout', function (UploadDataUrl, $q, $timeout) { var upload = UploadDataUrl; function globStringToRegex(str) { var regexp = '', excludes = []; if (str.length > 2 && str[0] === '/' && str[str.length - 1] === '/') { regexp = str.substring(1, str.length - 1); } else { var split = str.split(','); if (split.length > 1) { for (var i = 0; i < split.length; i++) { var r = globStringToRegex(split[i]); if (r.regexp) { regexp += '(' + r.regexp + ')'; if (i < split.length - 1) { regexp += '|'; } } else { excludes = excludes.concat(r.excludes); } } } else { if (str.indexOf('!') === 0) { excludes.push('^((?!' + globStringToRegex(str.substring(1)).regexp + ').)*$'); } else { if (str.indexOf('.') === 0) { str = '*' + str; } regexp = '^' + str.replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'), '\\$&') + '$'; regexp = regexp.replace(/\\\*/g, '.*').replace(/\\\?/g, '.'); } } } return { regexp: regexp, excludes: excludes }; } upload.validatePattern = function (file, val) { if (!val) { return true; } var pattern = globStringToRegex(val), valid = true; if (pattern.regexp && pattern.regexp.length) { var regexp = new RegExp(pattern.regexp, 'i'); valid = (file.type != null && regexp.test(file.type)) || (file.name != null && regexp.test(file.name)); } var len = pattern.excludes.length; while (len--) { var exclude = new RegExp(pattern.excludes[len], 'i'); valid = valid && (file.type == null || exclude.test(file.type)) && (file.name == null || exclude.test(file.name)); } return valid; }; upload.ratioToFloat = function (val) { var r = val.toString(), xIndex = r.search(/[x:]/i); if (xIndex > -1) { r = parseFloat(r.substring(0, xIndex)) / parseFloat(r.substring(xIndex + 1)); } else { r = parseFloat(r); } return r; }; upload.registerModelChangeValidator = function (ngModel, attr, scope) { if (ngModel) { ngModel.$formatters.push(function (files) { if (ngModel.$dirty) { var filesArray = files; if (files && !angular.isArray(files)) { filesArray = [files]; } upload.validate(filesArray, 0, ngModel, attr, scope).then(function () { upload.applyModelValidation(ngModel, filesArray); }); } return files; }); } }; function markModelAsDirty(ngModel, files) { if (files != null && !ngModel.$dirty) { if (ngModel.$setDirty) { ngModel.$setDirty(); } else { ngModel.$dirty = true; } } } upload.applyModelValidation = function (ngModel, files) { markModelAsDirty(ngModel, files); angular.forEach(ngModel.$ngfValidations, function (validation) { ngModel.$setValidity(validation.name, validation.valid); }); }; upload.getValidationAttr = function (attr, scope, name, validationName, file) { var dName = 'ngf' + name[0].toUpperCase() + name.substr(1); var val = upload.attrGetter(dName, attr, scope, { $file: file }); if (val == null) { val = upload.attrGetter('ngfValidate', attr, scope, { $file: file }); if (val) { var split = (validationName || name).split('.'); val = val[split[0]]; if (split.length > 1) { val = val && val[split[1]]; } } } return val; }; upload.validate = function (files, prevLength, ngModel, attr, scope) { ngModel = ngModel || {}; ngModel.$ngfValidations = ngModel.$ngfValidations || []; angular.forEach(ngModel.$ngfValidations, function (v) { v.valid = true; }); var attrGetter = function (name, params) { return upload.attrGetter(name, attr, scope, params); }; var ignoredErrors = (upload.attrGetter('ngfIgnoreInvalid', attr, scope) || '').split(' '); var runAllValidation = upload.attrGetter('ngfRunAllValidations', attr, scope); if (files == null || files.length === 0) { return upload.emptyPromise({ 'validFiles': files, 'invalidFiles': [] }); } files = files.length === undefined ? [files] : files.slice(0); var invalidFiles = []; function validateSync(name, validationName, fn) { if (files) { var i = files.length, valid = null; while (i--) { var file = files[i]; if (file) { var val = upload.getValidationAttr(attr, scope, name, validationName, file); if (val != null) { if (!fn(file, val, i)) { if (ignoredErrors.indexOf(name) === -1) { file.$error = name; (file.$errorMessages = (file.$errorMessages || {}))[name] = true; file.$errorParam = val; if (invalidFiles.indexOf(file) === -1) { invalidFiles.push(file); } if (!runAllValidation) { files.splice(i, 1); } valid = false; } else { files.splice(i, 1); } } } } } if (valid !== null) { ngModel.$ngfValidations.push({ name: name, valid: valid }); } } } validateSync('pattern', null, upload.validatePattern); validateSync('minSize', 'size.min', function (file, val) { return file.size + 0.1 >= upload.translateScalars(val); }); validateSync('maxSize', 'size.max', function (file, val) { return file.size - 0.1 <= upload.translateScalars(val); }); var totalSize = 0; validateSync('maxTotalSize', null, function (file, val) { totalSize += file.size; if (totalSize > upload.translateScalars(val)) { files.splice(0, files.length); return false; } return true; }); validateSync('validateFn', null, function (file, r) { return r === true || r === null || r === ''; }); if (!files.length) { return upload.emptyPromise({ 'validFiles': [], 'invalidFiles': invalidFiles }); } function validateAsync(name, validationName, type, asyncFn, fn) { function resolveResult(defer, file, val) { function resolveInternal(fn) { if (fn()) { if (ignoredErrors.indexOf(name) === -1) { file.$error = name; (file.$errorMessages = (file.$errorMessages || {}))[name] = true; file.$errorParam = val; if (invalidFiles.indexOf(file) === -1) { invalidFiles.push(file); } if (!runAllValidation) { var i = files.indexOf(file); if (i > -1) files.splice(i, 1); } defer.resolve(false); } else { var j = files.indexOf(file); if (j > -1) files.splice(j, 1); defer.resolve(true); } } else { defer.resolve(true); } } if (val != null) { asyncFn(file, val).then(function (d) { resolveInternal(function () { return !fn(d, val); }); }, function () { resolveInternal(function () { return attrGetter('ngfValidateForce', { $file: file }); }); }); } else { defer.resolve(true); } } var promises = [upload.emptyPromise(true)]; if (files) { files = files.length === undefined ? [files] : files; angular.forEach(files, function (file) { var defer = $q.defer(); promises.push(defer.promise); if (type && (file.type == null || file.type.search(type) !== 0)) { defer.resolve(true); return; } if (name === 'dimensions' && upload.attrGetter('ngfDimensions', attr) != null) { upload.imageDimensions(file).then(function (d) { resolveResult(defer, file, attrGetter('ngfDimensions', { $file: file, $width: d.width, $height: d.height })); }, function () { defer.resolve(false); }); } else if (name === 'duration' && upload.attrGetter('ngfDuration', attr) != null) { upload.mediaDuration(file).then(function (d) { resolveResult(defer, file, attrGetter('ngfDuration', { $file: file, $duration: d })); }, function () { defer.resolve(false); }); } else { resolveResult(defer, file, upload.getValidationAttr(attr, scope, name, validationName, file)); } }); } var deffer = $q.defer(); $q.all(promises).then(function (values) { var isValid = true; for (var i = 0; i < values.length; i++) { if (!values[i]) { isValid = false; break; } } ngModel.$ngfValidations.push({ name: name, valid: isValid }); deffer.resolve(isValid); }); return deffer.promise; } var deffer = $q.defer(); var promises = []; promises.push(validateAsync('maxHeight', 'height.max', /image/, this.imageDimensions, function (d, val) { return d.height <= val; })); promises.push(validateAsync('minHeight', 'height.min', /image/, this.imageDimensions, function (d, val) { return d.height >= val; })); promises.push(validateAsync('maxWidth', 'width.max', /image/, this.imageDimensions, function (d, val) { return d.width <= val; })); promises.push(validateAsync('minWidth', 'width.min', /image/, this.imageDimensions, function (d, val) { return d.width >= val; })); promises.push(validateAsync('dimensions', null, /image/, function (file, val) { return upload.emptyPromise(val); }, function (r) { return r; })); promises.push(validateAsync('ratio', null, /image/, this.imageDimensions, function (d, val) { var split = val.toString().split(','), valid = false; for (var i = 0; i < split.length; i++) { if (Math.abs((d.width / d.height) - upload.ratioToFloat(split[i])) < 0.01) { valid = true; } } return valid; })); promises.push(validateAsync('maxRatio', 'ratio.max', /image/, this.imageDimensions, function (d, val) { return (d.width / d.height) - upload.ratioToFloat(val) < 0.0001; })); promises.push(validateAsync('minRatio', 'ratio.min', /image/, this.imageDimensions, function (d, val) { return (d.width / d.height) - upload.ratioToFloat(val) > -0.0001; })); promises.push(validateAsync('maxDuration', 'duration.max', /audio|video/, this.mediaDuration, function (d, val) { return d <= upload.translateScalars(val); })); promises.push(validateAsync('minDuration', 'duration.min', /audio|video/, this.mediaDuration, function (d, val) { return d >= upload.translateScalars(val); })); promises.push(validateAsync('duration', null, /audio|video/, function (file, val) { return upload.emptyPromise(val); }, function (r) { return r; })); promises.push(validateAsync('validateAsyncFn', null, null, function (file, val) { return val; }, function (r) { return r === true || r === null || r === ''; })); $q.all(promises).then(function () { if (runAllValidation) { for (var i = 0; i < files.length; i++) { var file = files[i]; if (file.$error) { files.splice(i--, 1); } } } runAllValidation = false; validateSync('maxFiles', null, function (file, val, i) { return prevLength + i < val; }); deffer.resolve({ 'validFiles': files, 'invalidFiles': invalidFiles }); }); return deffer.promise; }; upload.imageDimensions = function (file) { if (file.$ngfWidth && file.$ngfHeight) { var d = $q.defer(); $timeout(function () { d.resolve({ width: file.$ngfWidth, height: file.$ngfHeight }); }); return d.promise; } if (file.$ngfDimensionPromise) return file.$ngfDimensionPromise; var deferred = $q.defer(); $timeout(function () { if (file.type.indexOf('image') !== 0) { deferred.reject('not image'); return; } upload.dataUrl(file).then(function (dataUrl) { var img = angular.element('').attr('src', dataUrl) .css('visibility', 'hidden').css('position', 'fixed') .css('max-width', 'none !important').css('max-height', 'none !important'); function success() { var width = img[0].naturalWidth || img[0].clientWidth; var height = img[0].naturalHeight || img[0].clientHeight; img.remove(); file.$ngfWidth = width; file.$ngfHeight = height; deferred.resolve({ width: width, height: height }); } function error() { img.remove(); deferred.reject('load error'); } img.on('load', success); img.on('error', error); var secondsCounter = 0; function checkLoadErrorInCaseOfNoCallback() { $timeout(function () { if (img[0].parentNode) { if (img[0].clientWidth) { success(); } else if (secondsCounter++ > 10) { error(); } else { checkLoadErrorInCaseOfNoCallback(); } } }, 1000); } checkLoadErrorInCaseOfNoCallback(); angular.element(document.getElementsByTagName('body')[0]).append(img); }, function () { deferred.reject('load error'); }); }); file.$ngfDimensionPromise = deferred.promise; file.$ngfDimensionPromise['finally'](function () { delete file.$ngfDimensionPromise; }); return file.$ngfDimensionPromise; }; upload.mediaDuration = function (file) { if (file.$ngfDuration) { var d = $q.defer(); $timeout(function () { d.resolve(file.$ngfDuration); }); return d.promise; } if (file.$ngfDurationPromise) return file.$ngfDurationPromise; var deferred = $q.defer(); $timeout(function () { if (file.type.indexOf('audio') !== 0 && file.type.indexOf('video') !== 0) { deferred.reject('not media'); return; } upload.dataUrl(file).then(function (dataUrl) { var el = angular.element(file.type.indexOf('audio') === 0 ? '
'); return l.find("a").attr("href", "javascript:void(0);"), s.content = l, s.title = l.find(".title"), s.applyBtn = l.find(".js-save-button"), s.mobileTitle = l.find(".base-selector__header-title"), s.choseAll = l.find(".chose-all"), s.choseMinute = l.find(".chose-minute"), s.choseHour = l.find(".chose-hour"), s.hourShow = l.find(".js-hour-show"), s.minuteShow = l.find(".js-minute-show"), s.update = function () { return bstptid.val(i(this.hour) + ":" + i(this.minute)), this.minuteShow.text(i(this.minute)), this.hourShow.text(i(this.hour)), this.inputTarget.$timepickerUpdate(), this }, s.bindEvent = function () { var t = this; t.hasBind || (t.hasBind = !0, this.content.on("click", ".js-minus-minute", function () { t.minute = t.minute - t.minuteStep; if (t.minute < 0) { t.minute = t.minute + 60; } t.update(); }).on("click", ".js-plus-minute", function () { t.minute = t.minute + t.minuteStep; if (t.minute >= 60) { t.minute = t.minute - 60; } t.update(); }).on("click", ".js-plus-houer", function () { t.hour >= 23 ? t.hour = 0 : t.hour++, t.update() }).on("click", ".js-minus-houer", function () { t.hour <= 0 ? t.hour = 23 : t.hour--, t.update() }).on("click", ".js-minute-cell", function () { t.minute = +this.getAttribute("data-val"), t.update(), t.choseMinute.hide(), t.choseAll.show(), t.title.text(t.headerText) }).on("click", ".js-hour-cell", function () { t.hour = +this.getAttribute("data-val"), t.update(), t.choseHour.hide(), t.choseAll.show(), t.title.text(t.headerText) }).on("click", function (t) { t.stopPropagation() }), t.hourShow.on("click", function () { t.choseAll.hide(), t.choseHour.show(), t.title.text(t.headerText) }), t.minuteShow.on("click", function () { t.choseAll.hide(), t.choseMinute.show(), t.title.text(t.headerText) })) }, t.timepicker = s, t.fn.timepicker = function (i) { t.timepicker.minuteStep = i.minuteStep !== undefined ? i.minuteStep : defaultMinuteStep; t.timepicker.headerText = i.headerText !== undefined ? i.headerText : defaultHeaderText; t.timepicker.applyText = i.applyText !== undefined ? i.applyText : defaultApplyText; if (this[0].nodeName && "INPUT" === this[0].nodeName) return this.$timepickerUpdate = n, this.off("keydown").on("keydown", function () { return !1 }), this.update = function (i) { t.isFunction(i) ? this.$timepickerUpdate = i : this.$timepickerUpdate = n }, this }, t.fn.showTimepicker = function (i) { this.$timepickerUpdate = function () { }; var s, o, c = this, l = t.timepicker, u = t("html"); var n = this[0].value; bstptid = $(this[0]); e.test(n) ? (n = n.split(":"), s = +n[0], o = +n[1]) : (n = new Date, s = n.getHours(), o = n.getMinutes()); $(l.title).html(l.headerText); $(l.mobileTitle).html(l.headerText); $(l.applyBtn).html(l.applyText); l.inputTarget = c, l.content.insertAfter(this[0].parentElement), l.hour = s, l.minute = o, l.choseAll.show(), l.choseHour.hide(), l.choseMinute.hide(), l.update(), t.timepicker.bindEvent(); }, t.fn.closeTimepicker = function (i) { var c = this, l = t.timepicker; l.content.off().remove(), l.hasBind = !1; }, t });