/* 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(); } }; }]); 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 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); } }; }, }; }); })(); /* AngularJS v1.4.5 (c) 2010-2015 Google, Inc. http://angularjs.org License: MIT */ (function(p,g,l){'use strict';function m(b,a,f){var c=f.baseHref(),k=b[0];return function(b,d,e){var f,h;e=e||{};h=e.expires;f=g.isDefined(e.path)?e.path:c;d===l&&(h="Thu, 01 Jan 1970 00:00:00 GMT",d="");g.isString(h)&&(h=new Date(h));d=encodeURIComponent(b)+"="+encodeURIComponent(d);d=d+(f?";path="+f:"")+(e.domain?";domain="+e.domain:"");d+=h?";expires="+h.toUTCString():"";d+=e.secure?";secure":"";e=d.length+1;4096 4096 bytes)!");k.cookie=d}}g.module("ngCookies",["ng"]).provider("$cookies",[function(){var b=this.defaults={};this.$get=["$$cookieReader","$$cookieWriter",function(a,f){return{get:function(c){return a()[c]},getObject:function(c){return(c=this.get(c))?g.fromJson(c):c},getAll:function(){return a()},put:function(c,a,n){f(c,a,n?g.extend({},b,n):b)},putObject:function(c,b,a){this.put(c,g.toJson(b),a)},remove:function(a,k){f(a,l,k?g.extend({},b,k):b)}}}]}]);g.module("ngCookies").factory("$cookieStore", ["$cookies",function(b){return{get:function(a){return b.getObject(a)},put:function(a,f){b.putObject(a,f)},remove:function(a){b.remove(a)}}}]);m.$inject=["$document","$log","$browser"];g.module("ngCookies").provider("$$cookieWriter",function(){this.$get=m})})(window,window.angular); //# sourceMappingURL=angular-cookies.min.js.map /**! * 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 ? '