function get_ip() { return "/fagineum_coupon/"; } function get_imageurl() { return "/assets/files/bezahlung/"; } function get_agblink() { return "https://bergwerk.berlin/info/agb.html"; } function get_datenschutztext() { return "Datenschutzklausel"; } function get_datenschutzlink() { return "https://bergwerk.berlin/info/datenschutzklausel.html"; } function get_coupon_text() { var array_coupon_text = []; array_coupon_text.push({amount:20, text: 'ein Kind bis 3-5 Jahre inkl. 1,5 Std. Klettern im BergWerk.mini, Kletter-Handschuhe, Durstlöscher & Snack'}); array_coupon_text.push({amount:25, text: 'ein Kind bis 5-7 Jahre (bis 1,30 m) inkl. 1,5 Std. Klettern im BigJunior, KletterHandschuhe, Durstlöscher oder Snack'}); array_coupon_text.push({amount:30, text: 'ein Kind (5-11 Jahre) inkl. 2,5 Std. Klettern, Kletter-Handschuhe, Durstlöscher & frisches Popcorn'}); array_coupon_text.push({amount:35, text: 'ein Kletterer ab 12 Jahre inkl. 2,5 Std. Klettern, Kletter-Handschuhe & Durstlöscher'}); array_coupon_text.push({amount:40, text: 'ein Kletterer ab 12 Jahre inkl. 2,5 Std. Klettern, 12-m-Freifall, Kletter-Handschuhe & Durstlöscher'}); array_coupon_text.push({amount:45, text: 'ein Kletterer ab 12 Jahre inkl. 2,5 Std. Klettern, 12-m-Freifall, Kletter-Handschuhe, Action-Foto & Durstlöscher'}); array_coupon_text.push({amount:50, text: '1 Erw. & 1 Kind (5-7 Jahre) inkl. 1,5 Std. Klettern im BigJunior, Kletter-Handschuhe & Durstlöscher'}); array_coupon_text.push({amount:65, text: '1 Erw. & 1 Kind (8-11 Jahre) inkl. 2,5 Std. Klettern, Kletter-Handschuhe, Snack & Durstlöscher'}); return array_coupon_text; } /*! angularjs-slider - v5.8.7 - (c) Rafal Zajac , Valentin Hervieu , Jussi Saarivirta , Angelin Sirbu - https://github.com/angular-slider/angularjs-slider - 2016-11-09 */ /*jslint unparam: true */ /*global angular: false, console: false, define, module */ (function(root, factory) { 'use strict'; /* istanbul ignore next */ if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['angular'], factory); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. // to support bundler like browserify var angularObj = require('angular'); if ((!angularObj || !angularObj.module) && typeof angular != 'undefined') { angularObj = angular; } module.exports = factory(angularObj); } else { // Browser globals (root is window) factory(root.angular); } }(this, function(angular) { 'use strict'; var module = angular.module('rzModule', []) .factory('RzSliderOptions', function() { var defaultOptions = { floor: 0, ceil: null, //defaults to rz-slider-model step: 1, precision: 0, minRange: null, maxRange: null, pushRange: false, minLimit: null, maxLimit: null, id: null, translate: null, getLegend: null, stepsArray: null, bindIndexForStepsArray: false, draggableRange: false, draggableRangeOnly: false, showSelectionBar: false, showSelectionBarEnd: false, showSelectionBarFromValue: null, hidePointerLabels: false, hideLimitLabels: false, autoHideLimitLabels: true, readOnly: false, disabled: false, interval: 350, showTicks: false, showTicksValues: false, ticksArray: null, ticksTooltip: null, ticksValuesTooltip: null, vertical: false, getSelectionBarColor: null, getTickColor: null, getPointerColor: null, keyboardSupport: true, scale: 1, enforceStep: true, enforceRange: false, noSwitching: false, onlyBindHandles: false, onStart: null, onChange: null, onEnd: null, rightToLeft: false, boundPointerLabels: true, mergeRangeLabelsIfSame: false, customTemplateScope: null, logScale: false, customValueToPosition: null, customPositionToValue: null }; var globalOptions = {}; var factory = {}; /** * `options({})` allows global configuration of all sliders in the * application. * * var app = angular.module( 'App', ['rzModule'], function( RzSliderOptions ) { * // show ticks for all sliders * RzSliderOptions.options( { showTicks: true } ); * }); */ factory.options = function(value) { angular.extend(globalOptions, value); }; factory.getOptions = function(options) { return angular.extend({}, defaultOptions, globalOptions, options); }; return factory; }) .factory('rzThrottle', ['$timeout', function($timeout) { /** * rzThrottle * * Taken from underscore project * * @param {Function} func * @param {number} wait * @param {ThrottleOptions} options * @returns {Function} */ return function(func, wait, options) { 'use strict'; /* istanbul ignore next */ var getTime = (Date.now || function() { return new Date().getTime(); }); var context, args, result; var timeout = null; var previous = 0; options = options || {}; var later = function() { previous = getTime(); timeout = null; result = func.apply(context, args); context = args = null; }; return function() { var now = getTime(); var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0) { $timeout.cancel(timeout); timeout = null; previous = now; result = func.apply(context, args); context = args = null; } else if (!timeout && options.trailing !== false) { timeout = $timeout(later, remaining); } return result; }; } }]) .factory('RzSlider', ['$timeout', '$document', '$window', '$compile', 'RzSliderOptions', 'rzThrottle', function($timeout, $document, $window, $compile, RzSliderOptions, rzThrottle) { 'use strict'; /** * Slider * * @param {ngScope} scope The AngularJS scope * @param {Element} sliderElem The slider directive element wrapped in jqLite * @constructor */ var Slider = function(scope, sliderElem) { /** * The slider's scope * * @type {ngScope} */ this.scope = scope; /** * The slider inner low value (linked to rzSliderModel) * @type {number} */ this.lowValue = 0; /** * The slider inner high value (linked to rzSliderHigh) * @type {number} */ this.highValue = 0; /** * Slider element wrapped in jqLite * * @type {jqLite} */ this.sliderElem = sliderElem; /** * Slider type * * @type {boolean} Set to true for range slider */ this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined; /** * Values recorded when first dragging the bar * * @type {Object} */ this.dragging = { active: false, value: 0, difference: 0, position: 0, lowLimit: 0, highLimit: 0 }; /** * property that handle position (defaults to left for horizontal) * @type {string} */ this.positionProperty = 'left'; /** * property that handle dimension (defaults to width for horizontal) * @type {string} */ this.dimensionProperty = 'width'; /** * Half of the width or height of the slider handles * * @type {number} */ this.handleHalfDim = 0; /** * Maximum position the slider handle can have * * @type {number} */ this.maxPos = 0; /** * Precision * * @type {number} */ this.precision = 0; /** * Step * * @type {number} */ this.step = 1; /** * The name of the handle we are currently tracking * * @type {string} */ this.tracking = ''; /** * Minimum value (floor) of the model * * @type {number} */ this.minValue = 0; /** * Maximum value (ceiling) of the model * * @type {number} */ this.maxValue = 0; /** * The delta between min and max value * * @type {number} */ this.valueRange = 0; /** * If showTicks/showTicksValues options are number. * In this case, ticks values should be displayed below the slider. * @type {boolean} */ this.intermediateTicks = false; /** * Set to true if init method already executed * * @type {boolean} */ this.initHasRun = false; /** * Used to call onStart on the first keydown event * * @type {boolean} */ this.firstKeyDown = false; /** * Internal flag to prevent watchers to be called when the sliders value are modified internally. * @type {boolean} */ this.internalChange = false; /** * Internal flag to keep track of the visibility of combo label * @type {boolean} */ this.cmbLabelShown = false; /** * Internal variable to keep track of the focus element */ this.currentFocusElement = null; // Slider DOM elements wrapped in jqLite this.fullBar = null; // The whole slider bar this.selBar = null; // Highlight between two handles this.minH = null; // Left slider handle this.maxH = null; // Right slider handle this.flrLab = null; // Floor label this.ceilLab = null; // Ceiling label this.minLab = null; // Label above the low value this.maxLab = null; // Label above the high value this.cmbLab = null; // Combined label this.ticks = null; // The ticks // Initialize slider this.init(); }; // Add instance methods Slider.prototype = { /** * Initialize slider * * @returns {undefined} */ init: function() { var thrLow, thrHigh, self = this; var calcDimFn = function() { self.calcViewDimensions(); }; this.applyOptions(); this.syncLowValue(); if (this.range) this.syncHighValue(); this.initElemHandles(); this.manageElementsStyle(); this.setDisabledState(); this.calcViewDimensions(); this.setMinAndMax(); this.addAccessibility(); this.updateCeilLab(); this.updateFloorLab(); this.initHandles(); this.manageEventsBindings(); // Recalculate slider view dimensions this.scope.$on('reCalcViewDimensions', calcDimFn); // Recalculate stuff if view port dimensions have changed angular.element($window).on('resize', calcDimFn); this.initHasRun = true; // Watch for changes to the model thrLow = rzThrottle(function() { self.onLowHandleChange(); }, self.options.interval); thrHigh = rzThrottle(function() { self.onHighHandleChange(); }, self.options.interval); this.scope.$on('rzSliderForceRender', function() { self.resetLabelsValue(); thrLow(); if (self.range) { thrHigh(); } self.resetSlider(); }); // Watchers (order is important because in case of simultaneous change, // watchers will be called in the same order) this.scope.$watch('rzSliderOptions()', function(newValue, oldValue) { if (newValue === oldValue) return; self.applyOptions(); // need to be called before synchronizing the values self.syncLowValue(); if (self.range) self.syncHighValue(); self.resetSlider(); }, true); this.scope.$watch('rzSliderModel', function(newValue, oldValue) { if (self.internalChange) return; if (newValue === oldValue) return; thrLow(); }); this.scope.$watch('rzSliderHigh', function(newValue, oldValue) { if (self.internalChange) return; if (newValue === oldValue) return; if (newValue != null) thrHigh(); if (self.range && newValue == null || !self.range && newValue != null) { self.applyOptions(); self.resetSlider(); } }); this.scope.$on('$destroy', function() { self.unbindEvents(); angular.element($window).off('resize', calcDimFn); self.currentFocusElement = null; }); }, findStepIndex: function(modelValue) { var index = 0; for (var i = 0; i < this.options.stepsArray.length; i++) { var step = this.options.stepsArray[i]; if (step === modelValue) { index = i; break; } else if (angular.isDate(step)) { if (step.getTime() === modelValue.getTime()) { index = i; break; } } else if (angular.isObject(step)) { if (angular.isDate(step.value) && step.value.getTime() === modelValue.getTime() || step.value === modelValue) { index = i; break; } } } return index; }, syncLowValue: function() { if (this.options.stepsArray) { if (!this.options.bindIndexForStepsArray) this.lowValue = this.findStepIndex(this.scope.rzSliderModel); else this.lowValue = this.scope.rzSliderModel } else this.lowValue = this.scope.rzSliderModel; }, syncHighValue: function() { if (this.options.stepsArray) { if (!this.options.bindIndexForStepsArray) this.highValue = this.findStepIndex(this.scope.rzSliderHigh); else this.highValue = this.scope.rzSliderHigh } else this.highValue = this.scope.rzSliderHigh; }, getStepValue: function(sliderValue) { var step = this.options.stepsArray[sliderValue]; if (angular.isDate(step)) return step; if (angular.isObject(step)) return step.value; return step; }, applyLowValue: function() { if (this.options.stepsArray) { if (!this.options.bindIndexForStepsArray) this.scope.rzSliderModel = this.getStepValue(this.lowValue); else this.scope.rzSliderModel = this.lowValue } else this.scope.rzSliderModel = this.lowValue; }, applyHighValue: function() { if (this.options.stepsArray) { if (!this.options.bindIndexForStepsArray) this.scope.rzSliderHigh = this.getStepValue(this.highValue); else this.scope.rzSliderHigh = this.highValue } else this.scope.rzSliderHigh = this.highValue; }, /* * Reflow the slider when the low handle changes (called with throttle) */ onLowHandleChange: function() { this.syncLowValue(); if (this.range) this.syncHighValue(); this.setMinAndMax(); this.updateLowHandle(this.valueToPosition(this.lowValue)); this.updateSelectionBar(); this.updateTicksScale(); this.updateAriaAttributes(); if (this.range) { this.updateCmbLabel(); } }, /* * Reflow the slider when the high handle changes (called with throttle) */ onHighHandleChange: function() { this.syncLowValue(); this.syncHighValue(); this.setMinAndMax(); this.updateHighHandle(this.valueToPosition(this.highValue)); this.updateSelectionBar(); this.updateTicksScale(); this.updateCmbLabel(); this.updateAriaAttributes(); }, /** * Read the user options and apply them to the slider model */ applyOptions: function() { var sliderOptions; if (this.scope.rzSliderOptions) sliderOptions = this.scope.rzSliderOptions(); else sliderOptions = {}; this.options = RzSliderOptions.getOptions(sliderOptions); if (this.options.step <= 0) this.options.step = 1; this.range = this.scope.rzSliderModel !== undefined && this.scope.rzSliderHigh !== undefined; this.options.draggableRange = this.range && this.options.draggableRange; this.options.draggableRangeOnly = this.range && this.options.draggableRangeOnly; if (this.options.draggableRangeOnly) { this.options.draggableRange = true; } this.options.showTicks = this.options.showTicks || this.options.showTicksValues || !!this.options.ticksArray; this.scope.showTicks = this.options.showTicks; //scope is used in the template if (angular.isNumber(this.options.showTicks) || this.options.ticksArray) this.intermediateTicks = true; this.options.showSelectionBar = this.options.showSelectionBar || this.options.showSelectionBarEnd || this.options.showSelectionBarFromValue !== null; if (this.options.stepsArray) { this.parseStepsArray(); } else { if (this.options.translate) this.customTrFn = this.options.translate; else this.customTrFn = function(value) { return String(value); }; this.getLegend = this.options.getLegend; } if (this.options.vertical) { this.positionProperty = 'bottom'; this.dimensionProperty = 'height'; } if (this.options.customTemplateScope) this.scope.custom = this.options.customTemplateScope; }, parseStepsArray: function() { this.options.floor = 0; this.options.ceil = this.options.stepsArray.length - 1; this.options.step = 1; if (this.options.translate) { this.customTrFn = this.options.translate; } else { this.customTrFn = function(modelValue) { if (this.options.bindIndexForStepsArray) return this.getStepValue(modelValue); return modelValue; }; } this.getLegend = function(index) { var step = this.options.stepsArray[index]; if (angular.isObject(step)) return step.legend; return null; }; }, /** * Resets slider * * @returns {undefined} */ resetSlider: function() { this.manageElementsStyle(); this.addAccessibility(); this.setMinAndMax(); this.updateCeilLab(); this.updateFloorLab(); this.unbindEvents(); this.manageEventsBindings(); this.setDisabledState(); this.calcViewDimensions(); this.refocusPointerIfNeeded(); }, refocusPointerIfNeeded: function() { if (this.currentFocusElement) { this.onPointerFocus(this.currentFocusElement.pointer, this.currentFocusElement.ref); this.focusElement(this.currentFocusElement.pointer) } }, /** * Set the slider children to variables for easy access * * Run only once during initialization * * @returns {undefined} */ initElemHandles: function() { // Assign all slider elements to object properties for easy access angular.forEach(this.sliderElem.children(), function(elem, index) { var jElem = angular.element(elem); switch (index) { case 0: this.fullBar = jElem; break; case 1: this.selBar = jElem; break; case 2: this.minH = jElem; break; case 3: this.maxH = jElem; break; case 4: this.flrLab = jElem; break; case 5: this.ceilLab = jElem; break; case 6: this.minLab = jElem; break; case 7: this.maxLab = jElem; break; case 8: this.cmbLab = jElem; break; case 9: this.ticks = jElem; break; } }, this); // Initialize position cache properties this.selBar.rzsp = 0; this.minH.rzsp = 0; this.maxH.rzsp = 0; this.flrLab.rzsp = 0; this.ceilLab.rzsp = 0; this.minLab.rzsp = 0; this.maxLab.rzsp = 0; this.cmbLab.rzsp = 0; }, /** * Update each elements style based on options */ manageElementsStyle: function() { if (!this.range) this.maxH.css('display', 'none'); else this.maxH.css('display', ''); this.alwaysHide(this.flrLab, this.options.showTicksValues || this.options.hideLimitLabels); this.alwaysHide(this.ceilLab, this.options.showTicksValues || this.options.hideLimitLabels); var hideLabelsForTicks = this.options.showTicksValues && !this.intermediateTicks; this.alwaysHide(this.minLab, hideLabelsForTicks || this.options.hidePointerLabels); this.alwaysHide(this.maxLab, hideLabelsForTicks || !this.range || this.options.hidePointerLabels); this.alwaysHide(this.cmbLab, hideLabelsForTicks || !this.range || this.options.hidePointerLabels); this.alwaysHide(this.selBar, !this.range && !this.options.showSelectionBar); if (this.options.vertical) this.sliderElem.addClass('rz-vertical'); if (this.options.draggableRange) this.selBar.addClass('rz-draggable'); else this.selBar.removeClass('rz-draggable'); if (this.intermediateTicks && this.options.showTicksValues) this.ticks.addClass('rz-ticks-values-under'); }, alwaysHide: function(el, hide) { el.rzAlwaysHide = hide; if (hide) this.hideEl(el); else this.showEl(el) }, /** * Manage the events bindings based on readOnly and disabled options * * @returns {undefined} */ manageEventsBindings: function() { if (this.options.disabled || this.options.readOnly) this.unbindEvents(); else this.bindEvents(); }, /** * Set the disabled state based on rzSliderDisabled * * @returns {undefined} */ setDisabledState: function() { if (this.options.disabled) { this.sliderElem.attr('disabled', 'disabled'); } else { this.sliderElem.attr('disabled', null); } }, /** * Reset label values * * @return {undefined} */ resetLabelsValue: function() { this.minLab.rzsv = undefined; this.maxLab.rzsv = undefined; }, /** * Initialize slider handles positions and labels * * Run only once during initialization and every time view port changes size * * @returns {undefined} */ initHandles: function() { this.updateLowHandle(this.valueToPosition(this.lowValue)); /* the order here is important since the selection bar should be updated after the high handle but before the combined label */ if (this.range) this.updateHighHandle(this.valueToPosition(this.highValue)); this.updateSelectionBar(); if (this.range) this.updateCmbLabel(); this.updateTicksScale(); }, /** * Translate value to human readable format * * @param {number|string} value * @param {jqLite} label * @param {String} which * @param {boolean} [useCustomTr] * @returns {undefined} */ translateFn: function(value, label, which, useCustomTr) { useCustomTr = useCustomTr === undefined ? true : useCustomTr; var valStr = '', getDimension = false, noLabelInjection = label.hasClass('no-label-injection'); if (useCustomTr) { if (this.options.stepsArray && !this.options.bindIndexForStepsArray) value = this.getStepValue(value); valStr = String(this.customTrFn(value, this.options.id, which)); } else { valStr = String(value) } if (label.rzsv === undefined || label.rzsv.length !== valStr.length || (label.rzsv.length > 0 && label.rzsd === 0)) { getDimension = true; label.rzsv = valStr; } if (!noLabelInjection) { label.html(valStr); } ; this.scope[which + 'Label'] = valStr; // Update width only when length of the label have changed if (getDimension) { this.getDimension(label); } }, /** * Set maximum and minimum values for the slider and ensure the model and high * value match these limits * @returns {undefined} */ setMinAndMax: function() { this.step = +this.options.step; this.precision = +this.options.precision; this.minValue = this.options.floor; if (this.options.logScale && this.minValue === 0) throw Error("Can't use floor=0 with logarithmic scale"); if (this.options.enforceStep) { this.lowValue = this.roundStep(this.lowValue); if (this.range) this.highValue = this.roundStep(this.highValue); } if (this.options.ceil != null) this.maxValue = this.options.ceil; else this.maxValue = this.options.ceil = this.range ? this.highValue : this.lowValue; if (this.options.enforceRange) { this.lowValue = this.sanitizeValue(this.lowValue); if (this.range) this.highValue = this.sanitizeValue(this.highValue); } this.applyLowValue(); if (this.range) this.applyHighValue(); this.valueRange = this.maxValue - this.minValue; }, /** * Adds accessibility attributes * * Run only once during initialization * * @returns {undefined} */ addAccessibility: function() { this.minH.attr('role', 'slider'); this.updateAriaAttributes(); if (this.options.keyboardSupport && !(this.options.readOnly || this.options.disabled)) this.minH.attr('tabindex', '0'); else this.minH.attr('tabindex', ''); if (this.options.vertical) this.minH.attr('aria-orientation', 'vertical'); if (this.range) { this.maxH.attr('role', 'slider'); if (this.options.keyboardSupport && !(this.options.readOnly || this.options.disabled)) this.maxH.attr('tabindex', '0'); else this.maxH.attr('tabindex', ''); if (this.options.vertical) this.maxH.attr('aria-orientation', 'vertical'); } }, /** * Updates aria attributes according to current values */ updateAriaAttributes: function() { this.minH.attr({ 'aria-valuenow': this.scope.rzSliderModel, 'aria-valuetext': this.customTrFn(this.scope.rzSliderModel, this.options.id, 'model'), 'aria-valuemin': this.minValue, 'aria-valuemax': this.maxValue }); if (this.range) { this.maxH.attr({ 'aria-valuenow': this.scope.rzSliderHigh, 'aria-valuetext': this.customTrFn(this.scope.rzSliderHigh, this.options.id, 'high'), 'aria-valuemin': this.minValue, 'aria-valuemax': this.maxValue }); } }, /** * Calculate dimensions that are dependent on view port size * * Run once during initialization and every time view port changes size. * * @returns {undefined} */ calcViewDimensions: function() { var handleWidth = this.getDimension(this.minH); this.handleHalfDim = handleWidth / 2; this.barDimension = this.getDimension(this.fullBar); this.maxPos = this.barDimension - handleWidth; this.getDimension(this.sliderElem); this.sliderElem.rzsp = this.sliderElem[0].getBoundingClientRect()[this.positionProperty]; if (this.initHasRun) { this.updateFloorLab(); this.updateCeilLab(); this.initHandles(); var self = this; $timeout(function() { self.updateTicksScale(); }); } }, /** * Update the ticks position * * @returns {undefined} */ updateTicksScale: function() { if (!this.options.showTicks) return; var ticksArray = this.options.ticksArray || this.getTicksArray(), translate = this.options.vertical ? 'translateY' : 'translateX', self = this; if (this.options.rightToLeft) ticksArray.reverse(); this.scope.ticks = ticksArray.map(function(value) { var position = self.valueToPosition(value); if (self.options.vertical) position = self.maxPos - position; var tick = { selected: self.isTickSelected(value), style: { transform: translate + '(' + Math.round(position) + 'px)' } }; if (tick.selected && self.options.getSelectionBarColor) { tick.style['background-color'] = self.getSelectionBarColor(); } if (!tick.selected && self.options.getTickColor) { tick.style['background-color'] = self.getTickColor(value); } if (self.options.ticksTooltip) { tick.tooltip = self.options.ticksTooltip(value); tick.tooltipPlacement = self.options.vertical ? 'right' : 'top'; } if (self.options.showTicksValues) { tick.value = self.getDisplayValue(value, 'tick-value'); if (self.options.ticksValuesTooltip) { tick.valueTooltip = self.options.ticksValuesTooltip(value); tick.valueTooltipPlacement = self.options.vertical ? 'right' : 'top'; } } if (self.getLegend) { var legend = self.getLegend(value, self.options.id); if (legend) tick.legend = legend; } return tick; }); }, getTicksArray: function() { var step = this.step, ticksArray = []; if (this.intermediateTicks) step = this.options.showTicks; for (var value = this.minValue; value <= this.maxValue; value += step) { ticksArray.push(value); } return ticksArray; }, isTickSelected: function(value) { if (!this.range) { if (this.options.showSelectionBarFromValue !== null) { var center = this.options.showSelectionBarFromValue; if (this.lowValue > center && value >= center && value <= this.lowValue) return true; else if (this.lowValue < center && value <= center && value >= this.lowValue) return true; } else if (this.options.showSelectionBarEnd) { if (value >= this.lowValue) return true; } else if (this.options.showSelectionBar && value <= this.lowValue) return true; } if (this.range && value >= this.lowValue && value <= this.highValue) return true; return false; }, /** * Update position of the floor label * * @returns {undefined} */ updateFloorLab: function() { this.translateFn(this.minValue, this.flrLab, 'floor'); this.getDimension(this.flrLab); var position = this.options.rightToLeft ? this.barDimension - this.flrLab.rzsd : 0; this.setPosition(this.flrLab, position); }, /** * Update position of the ceiling label * * @returns {undefined} */ updateCeilLab: function() { this.translateFn(this.maxValue, this.ceilLab, 'ceil'); this.getDimension(this.ceilLab); var position = this.options.rightToLeft ? 0 : this.barDimension - this.ceilLab.rzsd; this.setPosition(this.ceilLab, position); }, /** * Update slider handles and label positions * * @param {string} which * @param {number} newPos */ updateHandles: function(which, newPos) { if (which === 'lowValue') this.updateLowHandle(newPos); else this.updateHighHandle(newPos); this.updateSelectionBar(); this.updateTicksScale(); if (this.range) this.updateCmbLabel(); }, /** * Helper function to work out the position for handle labels depending on RTL or not * * @param {string} labelName maxLab or minLab * @param newPos * * @returns {number} */ getHandleLabelPos: function(labelName, newPos) { var labelRzsd = this[labelName].rzsd, nearHandlePos = newPos - labelRzsd / 2 + this.handleHalfDim, endOfBarPos = this.barDimension - labelRzsd; if (!this.options.boundPointerLabels) return nearHandlePos; if (this.options.rightToLeft && labelName === 'minLab' || !this.options.rightToLeft && labelName === 'maxLab') { return Math.min(nearHandlePos, endOfBarPos); } else { return Math.min(Math.max(nearHandlePos, 0), endOfBarPos); } }, /** * Update low slider handle position and label * * @param {number} newPos * @returns {undefined} */ updateLowHandle: function(newPos) { this.setPosition(this.minH, newPos); this.translateFn(this.lowValue, this.minLab, 'model'); this.setPosition(this.minLab, this.getHandleLabelPos('minLab', newPos)); if (this.options.getPointerColor) { var pointercolor = this.getPointerColor('min'); this.scope.minPointerStyle = { backgroundColor: pointercolor }; } if (this.options.autoHideLimitLabels) { this.shFloorCeil(); } }, /** * Update high slider handle position and label * * @param {number} newPos * @returns {undefined} */ updateHighHandle: function(newPos) { this.setPosition(this.maxH, newPos); this.translateFn(this.highValue, this.maxLab, 'high'); this.setPosition(this.maxLab, this.getHandleLabelPos('maxLab', newPos)); if (this.options.getPointerColor) { var pointercolor = this.getPointerColor('max'); this.scope.maxPointerStyle = { backgroundColor: pointercolor }; } if (this.options.autoHideLimitLabels) { this.shFloorCeil(); } }, /** * Show/hide floor/ceiling label * * @returns {undefined} */ shFloorCeil: function() { // Show based only on hideLimitLabels if pointer labels are hidden if (this.options.hidePointerLabels) { return; } var flHidden = false, clHidden = false, isMinLabAtFloor = this.isLabelBelowFloorLab(this.minLab), isMinLabAtCeil = this.isLabelAboveCeilLab(this.minLab), isMaxLabAtCeil = this.isLabelAboveCeilLab(this.maxLab), isCmbLabAtFloor = this.isLabelBelowFloorLab(this.cmbLab), isCmbLabAtCeil = this.isLabelAboveCeilLab(this.cmbLab); if (isMinLabAtFloor) { flHidden = true; this.hideEl(this.flrLab); } else { flHidden = false; this.showEl(this.flrLab); } if (isMinLabAtCeil) { clHidden = true; this.hideEl(this.ceilLab); } else { clHidden = false; this.showEl(this.ceilLab); } if (this.range) { var hideCeil = this.cmbLabelShown ? isCmbLabAtCeil : isMaxLabAtCeil; var hideFloor = this.cmbLabelShown ? isCmbLabAtFloor : isMinLabAtFloor; if (hideCeil) { this.hideEl(this.ceilLab); } else if (!clHidden) { this.showEl(this.ceilLab); } // Hide or show floor label if (hideFloor) { this.hideEl(this.flrLab); } else if (!flHidden) { this.showEl(this.flrLab); } } }, isLabelBelowFloorLab: function(label) { var isRTL = this.options.rightToLeft, pos = label.rzsp, dim = label.rzsd, floorPos = this.flrLab.rzsp, floorDim = this.flrLab.rzsd; return isRTL ? pos + dim >= floorPos - 2 : pos <= floorPos + floorDim + 2; }, isLabelAboveCeilLab: function(label) { var isRTL = this.options.rightToLeft, pos = label.rzsp, dim = label.rzsd, ceilPos = this.ceilLab.rzsp, ceilDim = this.ceilLab.rzsd; return isRTL ? pos <= ceilPos + ceilDim + 2 : pos + dim >= ceilPos - 2; }, /** * Update slider selection bar, combined label and range label * * @returns {undefined} */ updateSelectionBar: function() { var position = 0, dimension = 0, isSelectionBarFromRight = this.options.rightToLeft ? !this.options.showSelectionBarEnd : this.options.showSelectionBarEnd, positionForRange = this.options.rightToLeft ? this.maxH.rzsp + this.handleHalfDim : this.minH.rzsp + this.handleHalfDim; if (this.range) { dimension = Math.abs(this.maxH.rzsp - this.minH.rzsp); position = positionForRange; } else { if (this.options.showSelectionBarFromValue !== null) { var center = this.options.showSelectionBarFromValue, centerPosition = this.valueToPosition(center), isModelGreaterThanCenter = this.options.rightToLeft ? this.lowValue <= center : this.lowValue > center; if (isModelGreaterThanCenter) { dimension = this.minH.rzsp - centerPosition; position = centerPosition + this.handleHalfDim; } else { dimension = centerPosition - this.minH.rzsp; position = this.minH.rzsp + this.handleHalfDim; } } else if (isSelectionBarFromRight) { dimension = Math.abs(this.maxPos - this.minH.rzsp) + this.handleHalfDim; position = this.minH.rzsp + this.handleHalfDim; } else { dimension = Math.abs(this.maxH.rzsp - this.minH.rzsp) + this.handleHalfDim; position = 0; } } this.setDimension(this.selBar, dimension); this.setPosition(this.selBar, position); if (this.options.getSelectionBarColor) { var color = this.getSelectionBarColor(); this.scope.barStyle = { backgroundColor: color }; } }, /** * Wrapper around the getSelectionBarColor of the user to pass to * correct parameters */ getSelectionBarColor: function() { if (this.range) return this.options.getSelectionBarColor(this.scope.rzSliderModel, this.scope.rzSliderHigh); return this.options.getSelectionBarColor(this.scope.rzSliderModel); }, /** * Wrapper around the getPointerColor of the user to pass to * correct parameters */ getPointerColor: function(pointerType) { if (pointerType === 'max') { return this.options.getPointerColor(this.scope.rzSliderHigh, pointerType); } return this.options.getPointerColor(this.scope.rzSliderModel, pointerType); }, /** * Wrapper around the getTickColor of the user to pass to * correct parameters */ getTickColor: function(value) { return this.options.getTickColor(value); }, /** * Update combined label position and value * * @returns {undefined} */ updateCmbLabel: function() { var isLabelOverlap = null; if (this.options.rightToLeft) { isLabelOverlap = this.minLab.rzsp - this.minLab.rzsd - 10 <= this.maxLab.rzsp; } else { isLabelOverlap = this.minLab.rzsp + this.minLab.rzsd + 10 >= this.maxLab.rzsp; } if (isLabelOverlap) { var lowTr = this.getDisplayValue(this.lowValue, 'model'), highTr = this.getDisplayValue(this.highValue, 'high'), labelVal = ''; if (this.options.mergeRangeLabelsIfSame && lowTr === highTr) { labelVal = lowTr; } else { labelVal = this.options.rightToLeft ? highTr + ' - ' + lowTr : lowTr + ' - ' + highTr; } this.translateFn(labelVal, this.cmbLab, 'cmb', false); var pos = this.options.boundPointerLabels ? Math.min( Math.max( this.selBar.rzsp + this.selBar.rzsd / 2 - this.cmbLab.rzsd / 2, 0 ), this.barDimension - this.cmbLab.rzsd ) : this.selBar.rzsp + this.selBar.rzsd / 2 - this.cmbLab.rzsd / 2; this.setPosition(this.cmbLab, pos); this.cmbLabelShown = true; this.hideEl(this.minLab); this.hideEl(this.maxLab); this.showEl(this.cmbLab); } else { this.cmbLabelShown = false; this.showEl(this.maxLab); this.showEl(this.minLab); this.hideEl(this.cmbLab); } if (this.options.autoHideLimitLabels) { this.shFloorCeil(); } }, /** * Return the translated value if a translate function is provided else the original value * @param value * @param which if it's min or max handle * @returns {*} */ getDisplayValue: function(value, which) { if (this.options.stepsArray && !this.options.bindIndexForStepsArray) { value = this.getStepValue(value); } return this.customTrFn(value, this.options.id, which); }, /** * Round value to step and precision based on minValue * * @param {number} value * @param {number} customStep a custom step to override the defined step * @returns {number} */ roundStep: function(value, customStep) { var step = customStep ? customStep : this.step, steppedDifference = parseFloat((value - this.minValue) / step).toPrecision(12); steppedDifference = Math.round(+steppedDifference) * step; var newValue = (this.minValue + steppedDifference).toFixed(this.precision); return +newValue; }, /** * Hide element * * @param element * @returns {jqLite} The jqLite wrapped DOM element */ hideEl: function(element) { return element.css({ visibility: 'hidden' }); }, /** * Show element * * @param element The jqLite wrapped DOM element * @returns {jqLite} The jqLite */ showEl: function(element) { if (!!element.rzAlwaysHide) { return element; } return element.css({ visibility: 'visible' }); }, /** * Set element left/top position depending on whether slider is horizontal or vertical * * @param {jqLite} elem The jqLite wrapped DOM element * @param {number} pos * @returns {number} */ setPosition: function(elem, pos) { elem.rzsp = pos; var css = {}; css[this.positionProperty] = Math.round(pos) + 'px'; elem.css(css); return pos; }, /** * Get element width/height depending on whether slider is horizontal or vertical * * @param {jqLite} elem The jqLite wrapped DOM element * @returns {number} */ getDimension: function(elem) { var val = elem[0].getBoundingClientRect(); if (this.options.vertical) elem.rzsd = (val.bottom - val.top) * this.options.scale; else elem.rzsd = (val.right - val.left) * this.options.scale; return elem.rzsd; }, /** * Set element width/height depending on whether slider is horizontal or vertical * * @param {jqLite} elem The jqLite wrapped DOM element * @param {number} dim * @returns {number} */ setDimension: function(elem, dim) { elem.rzsd = dim; var css = {}; css[this.dimensionProperty] = Math.round(dim) + 'px'; elem.css(css); return dim; }, /** * Returns a value that is within slider range * * @param {number} val * @returns {number} */ sanitizeValue: function(val) { return Math.min(Math.max(val, this.minValue), this.maxValue); }, /** * Translate value to pixel position * * @param {number} val * @returns {number} */ valueToPosition: function(val) { var fn = this.linearValueToPosition; if (this.options.customValueToPosition) fn = this.options.customValueToPosition; else if (this.options.logScale) fn = this.logValueToPosition; val = this.sanitizeValue(val); var percent = fn(val, this.minValue, this.maxValue) || 0; if (this.options.rightToLeft) percent = 1 - percent; return percent * this.maxPos; }, linearValueToPosition: function(val, minVal, maxVal) { var range = maxVal - minVal; return (val - minVal) / range; }, logValueToPosition: function(val, minVal, maxVal) { val = Math.log(val); minVal = Math.log(minVal); maxVal = Math.log(maxVal); var range = maxVal - minVal; return (val - minVal) / range; }, /** * Translate position to model value * * @param {number} position * @returns {number} */ positionToValue: function(position) { var percent = position / this.maxPos; if (this.options.rightToLeft) percent = 1 - percent; var fn = this.linearPositionToValue; if (this.options.customPositionToValue) fn = this.options.customPositionToValue; else if (this.options.logScale) fn = this.logPositionToValue; return fn(percent, this.minValue, this.maxValue) || 0; }, linearPositionToValue: function(percent, minVal, maxVal) { return percent * (maxVal - minVal) + minVal; }, logPositionToValue: function(percent, minVal, maxVal) { minVal = Math.log(minVal); maxVal = Math.log(maxVal); var value = percent * (maxVal - minVal) + minVal; return Math.exp(value); }, // Events /** * Get the X-coordinate or Y-coordinate of an event * * @param {Object} event The event * @returns {number} */ getEventXY: function(event) { /* http://stackoverflow.com/a/12336075/282882 */ //noinspection JSLint var clientXY = this.options.vertical ? 'clientY' : 'clientX'; if (event[clientXY] !== undefined) { return event[clientXY]; } return event.originalEvent === undefined ? event.touches[0][clientXY] : event.originalEvent.touches[0][clientXY]; }, /** * Compute the event position depending on whether the slider is horizontal or vertical * @param event * @returns {number} */ getEventPosition: function(event) { var sliderPos = this.sliderElem.rzsp, eventPos = 0; if (this.options.vertical) eventPos = -this.getEventXY(event) + sliderPos; else eventPos = this.getEventXY(event) - sliderPos; return eventPos * this.options.scale - this.handleHalfDim; // #346 handleHalfDim is already scaled }, /** * Get event names for move and event end * * @param {Event} event The event * * @return {{moveEvent: string, endEvent: string}} */ getEventNames: function(event) { var eventNames = { moveEvent: '', endEvent: '' }; if (event.touches || (event.originalEvent !== undefined && event.originalEvent.touches)) { eventNames.moveEvent = 'touchmove'; eventNames.endEvent = 'touchend'; } else { eventNames.moveEvent = 'mousemove'; eventNames.endEvent = 'mouseup'; } return eventNames; }, /** * Get the handle closest to an event. * * @param event {Event} The event * @returns {jqLite} The handle closest to the event. */ getNearestHandle: function(event) { if (!this.range) { return this.minH; } var position = this.getEventPosition(event), distanceMin = Math.abs(position - this.minH.rzsp), distanceMax = Math.abs(position - this.maxH.rzsp); if (distanceMin < distanceMax) return this.minH; else if (distanceMin > distanceMax) return this.maxH; else if (!this.options.rightToLeft) //if event is at the same distance from min/max then if it's at left of minH, we return minH else maxH return position < this.minH.rzsp ? this.minH : this.maxH; else //reverse in rtl return position > this.minH.rzsp ? this.minH : this.maxH; }, /** * Wrapper function to focus an angular element * * @param el {AngularElement} the element to focus */ focusElement: function(el) { var DOM_ELEMENT = 0; el[DOM_ELEMENT].focus(); }, /** * Bind mouse and touch events to slider handles * * @returns {undefined} */ bindEvents: function() { var barTracking, barStart, barMove; if (this.options.draggableRange) { barTracking = 'rzSliderDrag'; barStart = this.onDragStart; barMove = this.onDragMove; } else { barTracking = 'lowValue'; barStart = this.onStart; barMove = this.onMove; } if (!this.options.onlyBindHandles) { this.selBar.on('mousedown', angular.bind(this, barStart, null, barTracking)); this.selBar.on('mousedown', angular.bind(this, barMove, this.selBar)); } if (this.options.draggableRangeOnly) { this.minH.on('mousedown', angular.bind(this, barStart, null, barTracking)); this.maxH.on('mousedown', angular.bind(this, barStart, null, barTracking)); } else { this.minH.on('mousedown', angular.bind(this, this.onStart, this.minH, 'lowValue')); if (this.range) { this.maxH.on('mousedown', angular.bind(this, this.onStart, this.maxH, 'highValue')); } if (!this.options.onlyBindHandles) { this.fullBar.on('mousedown', angular.bind(this, this.onStart, null, null)); this.fullBar.on('mousedown', angular.bind(this, this.onMove, this.fullBar)); this.ticks.on('mousedown', angular.bind(this, this.onStart, null, null)); this.ticks.on('mousedown', angular.bind(this, this.onTickClick, this.ticks)); } } if (!this.options.onlyBindHandles) { this.selBar.on('touchstart', angular.bind(this, barStart, null, barTracking)); this.selBar.on('touchstart', angular.bind(this, barMove, this.selBar)); } if (this.options.draggableRangeOnly) { this.minH.on('touchstart', angular.bind(this, barStart, null, barTracking)); this.maxH.on('touchstart', angular.bind(this, barStart, null, barTracking)); } else { this.minH.on('touchstart', angular.bind(this, this.onStart, this.minH, 'lowValue')); if (this.range) { this.maxH.on('touchstart', angular.bind(this, this.onStart, this.maxH, 'highValue')); } if (!this.options.onlyBindHandles) { this.fullBar.on('touchstart', angular.bind(this, this.onStart, null, null)); this.fullBar.on('touchstart', angular.bind(this, this.onMove, this.fullBar)); this.ticks.on('touchstart', angular.bind(this, this.onStart, null, null)); this.ticks.on('touchstart', angular.bind(this, this.onTickClick, this.ticks)); } } if (this.options.keyboardSupport) { this.minH.on('focus', angular.bind(this, this.onPointerFocus, this.minH, 'lowValue')); if (this.range) { this.maxH.on('focus', angular.bind(this, this.onPointerFocus, this.maxH, 'highValue')); } } }, /** * Unbind mouse and touch events to slider handles * * @returns {undefined} */ unbindEvents: function() { this.minH.off(); this.maxH.off(); this.fullBar.off(); this.selBar.off(); this.ticks.off(); }, /** * onStart event handler * * @param {?Object} pointer The jqLite wrapped DOM element; if null, the closest handle is used * @param {?string} ref The name of the handle being changed; if null, the closest handle's value is modified * @param {Event} event The event * @returns {undefined} */ onStart: function(pointer, ref, event) { var ehMove, ehEnd, eventNames = this.getEventNames(event); event.stopPropagation(); event.preventDefault(); // We have to do this in case the HTML where the sliders are on // have been animated into view. this.calcViewDimensions(); if (pointer) { this.tracking = ref; } else { pointer = this.getNearestHandle(event); this.tracking = pointer === this.minH ? 'lowValue' : 'highValue'; } pointer.addClass('rz-active'); if (this.options.keyboardSupport) this.focusElement(pointer); ehMove = angular.bind(this, this.dragging.active ? this.onDragMove : this.onMove, pointer); ehEnd = angular.bind(this, this.onEnd, ehMove); $document.on(eventNames.moveEvent, ehMove); $document.one(eventNames.endEvent, ehEnd); this.callOnStart(); }, /** * onMove event handler * * @param {jqLite} pointer * @param {Event} event The event * @param {boolean} fromTick if the event occured on a tick or not * @returns {undefined} */ onMove: function(pointer, event, fromTick) { var newPos = this.getEventPosition(event), newValue, ceilValue = this.options.rightToLeft ? this.minValue : this.maxValue, flrValue = this.options.rightToLeft ? this.maxValue : this.minValue; if (newPos <= 0) { newValue = flrValue; } else if (newPos >= this.maxPos) { newValue = ceilValue; } else { newValue = this.positionToValue(newPos); if (fromTick && angular.isNumber(this.options.showTicks)) newValue = this.roundStep(newValue, this.options.showTicks); else newValue = this.roundStep(newValue); } this.positionTrackingHandle(newValue); }, /** * onEnd event handler * * @param {Event} event The event * @param {Function} ehMove The the bound move event handler * @returns {undefined} */ onEnd: function(ehMove, event) { var moveEventName = this.getEventNames(event).moveEvent; if (!this.options.keyboardSupport) { this.minH.removeClass('rz-active'); this.maxH.removeClass('rz-active'); this.tracking = ''; } this.dragging.active = false; $document.off(moveEventName, ehMove); this.callOnEnd(); }, onTickClick: function(pointer, event) { this.onMove(pointer, event, true); }, onPointerFocus: function(pointer, ref) { this.tracking = ref; pointer.one('blur', angular.bind(this, this.onPointerBlur, pointer)); pointer.on('keydown', angular.bind(this, this.onKeyboardEvent)); pointer.on('keyup', angular.bind(this, this.onKeyUp)); this.firstKeyDown = true; pointer.addClass('rz-active'); this.currentFocusElement = { pointer: pointer, ref: ref }; }, onKeyUp: function() { this.firstKeyDown = true; this.callOnEnd(); }, onPointerBlur: function(pointer) { pointer.off('keydown'); pointer.off('keyup'); this.tracking = ''; pointer.removeClass('rz-active'); this.currentFocusElement = null }, /** * Key actions helper function * * @param {number} currentValue value of the slider * * @returns {?Object} action value mappings */ getKeyActions: function(currentValue) { var increaseStep = currentValue + this.step, decreaseStep = currentValue - this.step, increasePage = currentValue + this.valueRange / 10, decreasePage = currentValue - this.valueRange / 10; //Left to right default actions var actions = { 'UP': increaseStep, 'DOWN': decreaseStep, 'LEFT': decreaseStep, 'RIGHT': increaseStep, 'PAGEUP': increasePage, 'PAGEDOWN': decreasePage, 'HOME': this.minValue, 'END': this.maxValue }; //right to left means swapping right and left arrows if (this.options.rightToLeft) { actions.LEFT = increaseStep; actions.RIGHT = decreaseStep; // right to left and vertical means we also swap up and down if (this.options.vertical) { actions.UP = decreaseStep; actions.DOWN = increaseStep; } } return actions; }, onKeyboardEvent: function(event) { var currentValue = this[this.tracking], keyCode = event.keyCode || event.which, keys = { 38: 'UP', 40: 'DOWN', 37: 'LEFT', 39: 'RIGHT', 33: 'PAGEUP', 34: 'PAGEDOWN', 36: 'HOME', 35: 'END' }, actions = this.getKeyActions(currentValue), key = keys[keyCode], action = actions[key]; if (action == null || this.tracking === '') return; event.preventDefault(); if (this.firstKeyDown) { this.firstKeyDown = false; this.callOnStart(); } var self = this; $timeout(function() { var newValue = self.roundStep(self.sanitizeValue(action)); if (!self.options.draggableRangeOnly) { self.positionTrackingHandle(newValue); } else { var difference = self.highValue - self.lowValue, newMinValue, newMaxValue; if (self.tracking === 'lowValue') { newMinValue = newValue; newMaxValue = newValue + difference; if (newMaxValue > self.maxValue) { newMaxValue = self.maxValue; newMinValue = newMaxValue - difference; } } else { newMaxValue = newValue; newMinValue = newValue - difference; if (newMinValue < self.minValue) { newMinValue = self.minValue; newMaxValue = newMinValue + difference; } } self.positionTrackingBar(newMinValue, newMaxValue); } }); }, /** * onDragStart event handler * * Handles dragging of the middle bar. * * @param {Object} pointer The jqLite wrapped DOM element * @param {string} ref One of the refLow, refHigh values * @param {Event} event The event * @returns {undefined} */ onDragStart: function(pointer, ref, event) { var position = this.getEventPosition(event); this.dragging = { active: true, value: this.positionToValue(position), difference: this.highValue - this.lowValue, lowLimit: this.options.rightToLeft ? this.minH.rzsp - position : position - this.minH.rzsp, highLimit: this.options.rightToLeft ? position - this.maxH.rzsp : this.maxH.rzsp - position }; this.onStart(pointer, ref, event); }, /** * getValue helper function * * gets max or min value depending on whether the newPos is outOfBounds above or below the bar and rightToLeft * * @param {string} type 'max' || 'min' The value we are calculating * @param {number} newPos The new position * @param {boolean} outOfBounds Is the new position above or below the max/min? * @param {boolean} isAbove Is the new position above the bar if out of bounds? * * @returns {number} */ getValue: function(type, newPos, outOfBounds, isAbove) { var isRTL = this.options.rightToLeft, value = null; if (type === 'min') { if (outOfBounds) { if (isAbove) { value = isRTL ? this.minValue : this.maxValue - this.dragging.difference; } else { value = isRTL ? this.maxValue - this.dragging.difference : this.minValue; } } else { value = isRTL ? this.positionToValue(newPos + this.dragging.lowLimit) : this.positionToValue(newPos - this.dragging.lowLimit) } } else { if (outOfBounds) { if (isAbove) { value = isRTL ? this.minValue + this.dragging.difference : this.maxValue; } else { value = isRTL ? this.maxValue : this.minValue + this.dragging.difference; } } else { if (isRTL) { value = this.positionToValue(newPos + this.dragging.lowLimit) + this.dragging.difference } else { value = this.positionToValue(newPos - this.dragging.lowLimit) + this.dragging.difference; } } } return this.roundStep(value); }, /** * onDragMove event handler * * Handles dragging of the middle bar. * * @param {jqLite} pointer * @param {Event} event The event * @returns {undefined} */ onDragMove: function(pointer, event) { var newPos = this.getEventPosition(event), newMinValue, newMaxValue, ceilLimit, flrLimit, isUnderFlrLimit, isOverCeilLimit, flrH, ceilH; if (this.options.rightToLeft) { ceilLimit = this.dragging.lowLimit; flrLimit = this.dragging.highLimit; flrH = this.maxH; ceilH = this.minH; } else { ceilLimit = this.dragging.highLimit; flrLimit = this.dragging.lowLimit; flrH = this.minH; ceilH = this.maxH; } isUnderFlrLimit = newPos <= flrLimit; isOverCeilLimit = newPos >= this.maxPos - ceilLimit; if (isUnderFlrLimit) { if (flrH.rzsp === 0) return; newMinValue = this.getValue('min', newPos, true, false); newMaxValue = this.getValue('max', newPos, true, false); } else if (isOverCeilLimit) { if (ceilH.rzsp === this.maxPos) return; newMaxValue = this.getValue('max', newPos, true, true); newMinValue = this.getValue('min', newPos, true, true); } else { newMinValue = this.getValue('min', newPos, false); newMaxValue = this.getValue('max', newPos, false); } this.positionTrackingBar(newMinValue, newMaxValue); }, /** * Set the new value and position for the entire bar * * @param {number} newMinValue the new minimum value * @param {number} newMaxValue the new maximum value */ positionTrackingBar: function(newMinValue, newMaxValue) { if (this.options.minLimit != null && newMinValue < this.options.minLimit) { newMinValue = this.options.minLimit; newMaxValue = newMinValue + this.dragging.difference; } if (this.options.maxLimit != null && newMaxValue > this.options.maxLimit) { newMaxValue = this.options.maxLimit; newMinValue = newMaxValue - this.dragging.difference; } this.lowValue = newMinValue; this.highValue = newMaxValue; this.applyLowValue(); if (this.range) this.applyHighValue(); this.applyModel(); this.updateHandles('lowValue', this.valueToPosition(newMinValue)); this.updateHandles('highValue', this.valueToPosition(newMaxValue)); }, /** * Set the new value and position to the current tracking handle * * @param {number} newValue new model value */ positionTrackingHandle: function(newValue) { var valueChanged = false; newValue = this.applyMinMaxLimit(newValue); if (this.range) { if (this.options.pushRange) { newValue = this.applyPushRange(newValue); valueChanged = true; } else { if (this.options.noSwitching) { if (this.tracking === 'lowValue' && newValue > this.highValue) newValue = this.applyMinMaxRange(this.highValue); else if (this.tracking === 'highValue' && newValue < this.lowValue) newValue = this.applyMinMaxRange(this.lowValue); } newValue = this.applyMinMaxRange(newValue); /* This is to check if we need to switch the min and max handles */ if (this.tracking === 'lowValue' && newValue > this.highValue) { this.lowValue = this.highValue; this.applyLowValue(); this.updateHandles(this.tracking, this.maxH.rzsp); this.updateAriaAttributes(); this.tracking = 'highValue'; this.minH.removeClass('rz-active'); this.maxH.addClass('rz-active'); if (this.options.keyboardSupport) this.focusElement(this.maxH); valueChanged = true; } else if (this.tracking === 'highValue' && newValue < this.lowValue) { this.highValue = this.lowValue; this.applyHighValue(); this.updateHandles(this.tracking, this.minH.rzsp); this.updateAriaAttributes(); this.tracking = 'lowValue'; this.maxH.removeClass('rz-active'); this.minH.addClass('rz-active'); if (this.options.keyboardSupport) this.focusElement(this.minH); valueChanged = true; } } } if (this[this.tracking] !== newValue) { this[this.tracking] = newValue; if (this.tracking === 'lowValue') this.applyLowValue(); else this.applyHighValue(); this.updateHandles(this.tracking, this.valueToPosition(newValue)); this.updateAriaAttributes(); valueChanged = true; } if (valueChanged) this.applyModel(); }, applyMinMaxLimit: function(newValue) { if (this.options.minLimit != null && newValue < this.options.minLimit) return this.options.minLimit; if (this.options.maxLimit != null && newValue > this.options.maxLimit) return this.options.maxLimit; return newValue; }, applyMinMaxRange: function(newValue) { var oppositeValue = this.tracking === 'lowValue' ? this.highValue : this.lowValue, difference = Math.abs(newValue - oppositeValue); if (this.options.minRange != null) { if (difference < this.options.minRange) { if (this.tracking === 'lowValue') return this.highValue - this.options.minRange; else return this.lowValue + this.options.minRange; } } if (this.options.maxRange != null) { if (difference > this.options.maxRange) { if (this.tracking === 'lowValue') return this.highValue - this.options.maxRange; else return this.lowValue + this.options.maxRange; } } return newValue; }, applyPushRange: function(newValue) { var difference = this.tracking === 'lowValue' ? this.highValue - newValue : newValue - this.lowValue, minRange = this.options.minRange !== null ? this.options.minRange : this.options.step, maxRange = this.options.maxRange; // if smaller than minRange if (difference < minRange) { if (this.tracking === 'lowValue') { this.highValue = Math.min(newValue + minRange, this.maxValue); newValue = this.highValue - minRange; this.applyHighValue(); this.updateHandles('highValue', this.valueToPosition(this.highValue)); } else { this.lowValue = Math.max(newValue - minRange, this.minValue); newValue = this.lowValue + minRange; this.applyLowValue(); this.updateHandles('lowValue', this.valueToPosition(this.lowValue)); } this.updateAriaAttributes(); } // if greater than maxRange else if (maxRange !== null && difference > maxRange) { if (this.tracking === 'lowValue') { this.highValue = newValue + maxRange; this.applyHighValue(); this.updateHandles('highValue', this.valueToPosition(this.highValue)); } else { this.lowValue = newValue - maxRange; this.applyLowValue(); this.updateHandles('lowValue', this.valueToPosition(this.lowValue)); } this.updateAriaAttributes(); } return newValue; }, /** * Apply the model values using scope.$apply. * We wrap it with the internalChange flag to avoid the watchers to be called */ applyModel: function() { this.internalChange = true; this.scope.$apply(); this.callOnChange(); this.internalChange = false; }, /** * Call the onStart callback if defined * The callback call is wrapped in a $evalAsync to ensure that its result will be applied to the scope. * * @returns {undefined} */ callOnStart: function() { if (this.options.onStart) { var self = this, pointerType = this.tracking === 'lowValue' ? 'min' : 'max'; this.scope.$evalAsync(function() { self.options.onStart(self.options.id, self.scope.rzSliderModel, self.scope.rzSliderHigh, pointerType); }); } }, /** * Call the onChange callback if defined * The callback call is wrapped in a $evalAsync to ensure that its result will be applied to the scope. * * @returns {undefined} */ callOnChange: function() { if (this.options.onChange) { var self = this, pointerType = this.tracking === 'lowValue' ? 'min' : 'max'; this.scope.$evalAsync(function() { self.options.onChange(self.options.id, self.scope.rzSliderModel, self.scope.rzSliderHigh, pointerType); }); } }, /** * Call the onEnd callback if defined * The callback call is wrapped in a $evalAsync to ensure that its result will be applied to the scope. * * @returns {undefined} */ callOnEnd: function() { if (this.options.onEnd) { var self = this, pointerType = this.tracking === 'lowValue' ? 'min' : 'max'; this.scope.$evalAsync(function() { self.options.onEnd(self.options.id, self.scope.rzSliderModel, self.scope.rzSliderHigh, pointerType); }); } this.scope.$emit('slideEnded'); } }; return Slider; }]) .directive('rzslider', ['RzSlider', function(RzSlider) { 'use strict'; return { restrict: 'AE', replace: true, scope: { rzSliderModel: '=?', rzSliderHigh: '=?', rzSliderOptions: '&?', rzSliderTplUrl: '@' }, /** * Return template URL * * @param {jqLite} elem * @param {Object} attrs * @return {string} */ templateUrl: function(elem, attrs) { //noinspection JSUnresolvedVariable return attrs.rzSliderTplUrl || 'rzSliderTpl.html'; }, link: function(scope, elem) { scope.slider = new RzSlider(scope, elem); //attach on scope so we can test it } }; }]); // IDE assist /** * @name ngScope * * @property {number} rzSliderModel * @property {number} rzSliderHigh * @property {Object} rzSliderOptions */ /** * @name jqLite * * @property {number|undefined} rzsp rzslider label position position * @property {number|undefined} rzsd rzslider element dimension * @property {string|undefined} rzsv rzslider label value/text * @property {Function} css * @property {Function} text */ /** * @name Event * @property {Array} touches * @property {Event} originalEvent */ /** * @name ThrottleOptions * * @property {boolean} leading * @property {boolean} trailing */ module.run(['$templateCache', function($templateCache) { 'use strict'; $templateCache.put('rzSliderTpl.html', "
  • {{ t.value }} {{ t.legend }}
" ); }]); return module.name })); var fagineum_ticket_client = angular.module("fagineum_ticket_client", ["ngRoute", "ngAnimate", "ui.bootstrap"]); var fagineum_coupon = angular.module("fagineum_coupon.coupon", ["ngRoute", "ngAnimate", "ui.bootstrap","rzModule"]); fagineum_coupon.config( function ($routeProvider, $sceProvider, $logProvider) { $logProvider.debugEnabled(false); $sceProvider.enabled(false); $routeProvider.when("/fagineum_coupon", { templateUrl: get_ip() + "/html/get_html.php?content=coupon.html", controller: "fagineum_coupon_controller" }); $routeProvider.otherwise({ redirectTo: "/fagineum_coupon" }); } ); webservice_get_coupon_template_data = function($scope, $http) { $http.get(get_ip() + '/services/get_public_coupon.php').success(function (response) { if (response.success == "true" || response.success === true) { $scope.coupon_template_id = response.data.id; $scope.coupon_amount_min = response.data.amount_min; $scope.coupon_amount_max = response.data.amount_max; $scope.slide_options.floor = response.data.amount_min*1; $scope.slide_options.ceil = response.data.amount_max*1; $scope.slide_options.show = true; $scope.services.coupon_list[0].value = response.data.amount_min*1; $scope.services.update_text(); } else { console.log("Fehlerr"); console.log(response); } }); } webservice_do_coupon_order = function ($scope, $http) { var fs = $scope.formservice; var cs = $scope.services; var obj = new Object(); obj.c_salutation = fs.salutation; obj.c_organisation = fs.organisation; obj.c_first_name = fs.first_name; obj.c_last_name = fs.last_name; obj.c_street = fs.street; obj.c_zipcode = fs.zipcode; obj.c_city = fs.city; obj.c_telefon = fs.telefon; obj.c_email = fs.email; obj.c_type = fs.customertyp; obj.coupon_count = cs.coupon_list.length; for (var i = 0; i < cs.coupon_list.length; i++) { obj["coupon_" + (i + 1) + "_template_id"] = $scope.coupon_template_id; obj["coupon_" + (i + 1) + "_amount"] = cs.coupon_list[i].value; } $http.post(get_ip() + '/services/create_coupon.php', obj).success(function (response) { if (response.success == "true") { $scope.coupon_id = response.data.id; webservice_do_payment($scope, $http, [], []); try { if (typeof ga === 'function') { //ga('send', 'pageview', 'coupon/step' + $scope.step); gtag('event', 'screen_view', { 'app_name': 'fagineum/couponclient', 'screen_name': 'coupon/step' + $scope.step }); var amount = $scope.services.coupon_list.reduce((prev, val) => prev + val.value * 1, 0); gtag( "event", "purchase", { transaction_id: $scope.bookingnr, value: amount, tax: 0, shipping: 0, currency: "EUR" } ); } } catch (err) { console.log(err.message); } } else { $scope.step = 8; try { if (typeof ga === 'function') { //ga('send', 'pageview', 'coupon/step' + $scope.step); gtag('event', 'screen_view', { 'app_name': 'fagineum/couponclient', 'screen_name': 'coupon/step' + $scope.step }); } } catch (err) { console.log(err.message); } } }); } webservice_do_payment = function ($scope, $http, keys, values) { $scope.loading = true; var obj = {}; obj.type = 2; obj.type_id = $scope.coupon_id; obj.key = $scope.paymentkey; for (var i = 0; i < keys.length; i++) { obj[keys[i]] = values[i]; } $http.post(get_ip() + '/services/create_payment.php', obj).success(function (response) { if (response.success == "true") { var data = response.data; $scope.paymentkey = data.key; if (data.action == "select") { $scope.payment_provider_list = []; for (var i = 0; i < data.parameter.length; i++) { if (data.parameter[i].key1.lastIndexOf("hint_", 0) === -1) { $scope.payment_provider_list.push(data.parameter[i]); } } for (var i = 0; i < data.parameter.length; i++) { if (data.parameter[i].key1.lastIndexOf("hint_", 0) === 0) { for (var j = 0; j < $scope.payment_provider_list.length; j++) { if (data.parameter[i].key1 == "hint_" + $scope.payment_provider_list[j].value) { $scope.payment_provider_list[j].hint = data.parameter[i].value; } } } } $scope.step = 5; } if (data.action == "link") { $scope.step = 6; var load_external_content = false; var url = []; var js_preload = []; var js_afterload = []; for (var i = 0; i < data.parameter.length; i++) { if (data.parameter[i].key1 == "forward") { if (data.parameter[i].value == "noforward") { $scope.forward = false; } else { $scope.forward = true; $scope.forward_url = data.parameter[i].value; if ($scope.forward_url.indexOf("javascript:") === -1 && $scope.forward_url.indexOf("javascripts:") === -1) { window.setTimeout(function () { window.location.href = $scope.forward_url; }, 5000); } else { url = $scope.forward_url.replace("javascript:", "").split("::::"); $scope.forward_url = ""; load_external_content = true; } } } if (data.parameter[i].key1 == "hint") { $scope.payment_hint = data.parameter[i].value; } if (data.parameter[i].key1 == "name") { $scope.payment_name = data.parameter[i].value; } if (data.parameter[i].key1 == "payment_provider_id") { $scope.payment_provider_id = data.parameter[i].value; } if (data.parameter[i].key1 == "html") { $scope.payment_html = data.parameter[i].value; } if (data.parameter[i].key1 == "js_preload") { js_preload = data.parameter[i].value.split("::::"); } if (data.parameter[i].key1 == "js_afterload") { js_afterload = data.parameter[i].value.split("::::"); } } if (load_external_content === true) { for (var i = 0; i < js_preload.length; i++) { var script = document.createElement('script'); script.text = js_preload[i]; document.body.appendChild(script); console.log("preload" + (i + 1)); } window.setTimeout(function () { for (var i = 0; i < url.length; i++) { var script = document.createElement("script"); script.src = url[i]; document.body.appendChild(script); console.log("url" + (i + 1)); } window.setTimeout(function () { for (var i = 0; i < js_afterload.length; i++) { var script = document.createElement('script'); script.text = js_afterload[i]; document.body.appendChild(script); console.log("afterload" + (i + 1)); } }, 2000); }, 2000); } } try { if (typeof ga === 'function') { //ga('send', 'pageview', 'coupon/step' + $scope.step); gtag('event', 'screen_view', { 'app_name': 'fagineum/couponclient', 'screen_name': 'coupon/step' + $scope.step }); } } catch (err) { console.log(err.message); } } else { $scope.step = 8; try { if (typeof ga === 'function') { //ga('send', 'pageview', 'coupon/step' + $scope.step); gtag('event', 'screen_view', { 'app_name': 'fagineum/couponclient', 'screen_name': 'coupon/step' + $scope.step }); } } catch (err) { console.log(err.message); } } $scope.loading = false; }); } fagineum_coupon.factory('fagineum_coupon_service', function () { var services = { coupon_list: [ { value: 50, text: "" } ], add_coupon: function () { this.coupon_list.push( { value: 50, text: "" } ); this.update_text(); }, remove_coupon: function (index) { if (index > -1) { this.coupon_list.splice(index, 1); } }, get_total_price: function () { var val = 0; for (var i = 0; i < this.coupon_list.length; i++) { val += this.coupon_list[i].value * 1; } return val; }, update_text: function () { var coupon_text_array = get_coupon_text(); for (i = 0; i < this.coupon_list.length; i++) { var set_text = false; for (j = 0; j < coupon_text_array.length; j++) { if (coupon_text_array[j].amount * 1 === this.coupon_list[i].value * 1) { this.coupon_list[i].text = coupon_text_array[j].text; j = coupon_text_array.length; set_text = true; } } if (set_text === false) { this.coupon_list[i].text = ""; } } } }; return services; }); fagineum_coupon.factory('fagineum_coupon_form_service', function() { var services = { customertyp: "1", has_error: false, error_text: "", salutation: "0", salutation_error: false, organisation: "", organisation_error: false, first_name: "", first_name_error: false, last_name: "", last_name_error: false, street: "", street_error: false, zipcode: "", zipcode_error: false, city: "", city_error: false, telefon: "", telefon_error: false, email: "", email_error: false, email1: "", email1_error: false, check1: false, check1_error: false, check2: false, check2_error: false, init_error : function() { this.has_error = false; this.error_text = ""; this.salutation_error = false; if(this.customertyp*1 === 1) { this.organisation = ""; } this.organisation_error = false; this.first_name_error= false; this.last_name_error= false; this.street_error = false; this.zipcode_error= false; this.city_error= false; this.telefon_error= false; this.email_error= false; this.email1_error= false; this.email_equals_error = false; this.check1_error= false; this.check2_error= false; }, validateEmail : function(email) { var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(email); }, check_error : function() { this.init_error(); if(this.salutation*1 === 0) { this.salutation_error = true; this.has_error = true; } if(this.customertyp*1 === 2 && this.organisation.length === 0) { this.organisation_error = true; this.has_error = true; } if(this.first_name.length === 0) { this.first_name_error = true; this.has_error = true; } if(this.last_name.length === 0) { this.last_name_error = true; this.has_error = true; } if(this.street.length === 0) { this.street_error = true; this.has_error = true; } if(this.zipcode.length === 0) { this.zipcode_error = true; this.has_error = true; } if(this.city.length === 0) { this.city_error = true; this.has_error = true; } if(this.telefon.length === 0) { this.telefon_error = true; this.has_error = true; } if(this.email.length === 0 || this.validateEmail(this.email) === false) { this.email_error = true; this.has_error = true; } if(this.email1.length === 0 || this.validateEmail(this.email1) === false) { this.email1_error = true; this.has_error = true; } if(this.email.length > 0 && this.email1.length > 0 && this.email != this.email1) { this.email_equals_error = true; this.has_error = true; } if(this.check1 !== true) { this.check1_error = true; this.has_error = true; } if(this.check2 !== true) { this.check2_error = true; this.has_error = true; } return this.has_error; } }; return services; }); fagineum_coupon.controller("fagineum_coupon_controller", function ($scope, $http, $location, $sce, $uibModal, $log, fagineum_coupon_service, fagineum_coupon_form_service) { $scope.coupon_template_id = 0; $scope.coupon_amount_min = 0; $scope.coupon_amount_max = 0; webservice_get_coupon_template_data($scope, $http); $scope.services = fagineum_coupon_service; $scope.services.update_text(); $scope.formservice = fagineum_coupon_form_service; $scope.step = 1; $scope.coupon_id = 0; $scope.payment_id = 0; $scope.paymentkey = ""; $scope.forward = false; $scope.payment_hint = ""; $scope.forward_url = ""; $scope.payment_name = ""; $scope.payment_provider_id = ""; $scope.loading = false; $scope.payment_html = ""; $scope.next = function () { if ($scope.step * 1 === 1) { $scope.step++; try { if (typeof ga === 'function') { //ga('send', 'pageview', 'coupon/step' + $scope.step); gtag('event', 'screen_view', { 'app_name': 'fagineum/couponclient', 'screen_name': 'coupon/step' + $scope.step }); } } catch (err) { console.log(err.message); } } else if ($scope.step * 1 === 2) { $scope.step++; try { if (typeof ga === 'function') { //ga('send', 'pageview', 'coupon/step' + $scope.step); gtag('event', 'screen_view', { 'app_name': 'fagineum/couponclient', 'screen_name': 'coupon/step' + $scope.step }); } } catch (err) { console.log(err.message); } } else if ($scope.step === 3) { $scope.formservice.init_error(); if ($scope.formservice.check_error() === false) { $scope.step = 4; webservice_do_coupon_order($scope, $http); } } } $scope.back = function () { $scope.step--; } $scope.choose_payment = function () { webservice_do_payment($scope, $http, ["payment_provider_id"], [$scope.payment_provider_id]); } $scope.get_slider_options = function () { return $scope.slide_options; } $scope.slide_options = { floor: $scope.coupon_amount_min, ceil: $scope.coupon_amount_max, show: false, step: 5, translate: function (value) { return '€ ' + value + ""; }, showTicks: true, onChange: function (id) { $scope.services.update_text(); } } $scope.get_datenschutztext = function () { return get_datenschutztext(); } $scope.get_datenschutzlink = function () { return get_datenschutzlink(); } $scope.get_agb_link = function () { return get_agblink(); } $scope.get_imageurl = function() { return get_imageurl(); } $scope.hide_background = function() { if(window["fagineum_coupon_custom_hide"] !== undefined) { window["fagineum_coupon_custom_hide"](); } } $scope.show_background = function() { if(window["fagineum_coupon_custom_show"] !== undefined) { window["fagineum_coupon_custom_show"](); } } }); angular.element(document).ready(function() { angular.bootstrap(angular.element(document.getElementById("fagineum_coupon")), ['fagineum_coupon.coupon']); });