From 1945540ad08d2df11859a9bf2e9e347f1b69d69c Mon Sep 17 00:00:00 2001 From: Maximilian Lauterbach Date: Thu, 3 Dec 2015 14:06:12 +0100 Subject: [PATCH 01/17] missing version update --- tools/scripts.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/scripts.json b/tools/scripts.json index 400032d..cae103c 100644 --- a/tools/scripts.json +++ b/tools/scripts.json @@ -10,7 +10,7 @@ "libs/jquery-ui.min.js", "libs/jquery.ui.touch-punch.min.js", "plenty/plenty-2.js", - "plenty/plentymarketsCMStools-0.9.0.js" + "plenty/plentymarketsCMStools-1.0.1.js" ] }, "PageDesignContent": { From 3ebb9b5be498aa565641cd6c5cf6c1e7466445b1 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 4 Dec 2015 14:13:48 +0100 Subject: [PATCH 02/17] FEATURE add events 'check' & 'uncheck' for directives at checkboxes and radio buttons --- src/plentyFramework.js | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/plentyFramework.js b/src/plentyFramework.js index 8e71709..247636c 100644 --- a/src/plentyFramework.js +++ b/src/plentyFramework.js @@ -159,6 +159,8 @@ return; } + addCustomEvents( element ); + for ( var i = 0; i < directives.length; i++ ) { var directive = directives[i]; @@ -250,6 +252,49 @@ } ); } + function addCustomEvents( element ) + { + + var $elem = $( element ); + + if( $elem.is('input[type="checkbox"]') ) + { + $elem.on('change', function() { + + if( $elem.is(':checked') ) + { + $elem.trigger('check'); + } + else + { + $elem.trigger('uncheck'); + } + }); + } + + if( $elem.is('input[type="radio"]') ) + { + $elem.on('change', function() { + + var radioGroup = $elem.attr('name'); + + $( 'input[type="radio"][name="' + radioGroup + '"]' ).each(function( i, radio ) { + var $radio = $( radio ); + if( $radio.is(':checked') ) + { + $radio.trigger('check'); + } + else + { + $radio.trigger('uncheck'); + } + + }); + + }); + } + } + function parseDirectives( input, thisValue ) { var directivePattern = /^(([\w]+):)?([\w]+)\.([\w]+)(\((.*)\))?$/; From 5480b88198f5d64159cb37d0da424f511f658f42 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 4 Dec 2015 14:15:19 +0100 Subject: [PATCH 03/17] Add addClass() and removeClass() to UI directive, working like toggleClass() --- src/directives/UI.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/directives/UI.js b/src/directives/UI.js index 0e7b042..b608ae7 100644 --- a/src/directives/UI.js +++ b/src/directives/UI.js @@ -23,7 +23,9 @@ initSlideToggle : initSlideToggle, toggleHideShow : toggleHideShow, toggleSocialShare : toggleSocialShare, - toggleClass : toggleClass + toggleClass : toggleClass, + addClass : addClass, + removeClass : removeClass }; function initUIWindowEvents() @@ -346,8 +348,32 @@ { var e = pm.getRecentEvent(); if( !!e ) e.preventDefault(); - var $elem = $( target ); - $elem.toggleClass( cssClass ); + + $( target ).toggleClass( cssClass ); + return false; + } + } + + function addClass( cssClass, target, interval ) + { + if ( !!target && !!cssClass && ( !interval || MediaSizeService.isInterval( interval ) ) ) + { + var e = pm.getRecentEvent(); + if( !!e ) e.preventDefault(); + + $( target ).addClass( cssClass ); + return false; + } + } + + function removeClass( cssClass, target, interval ) + { + if ( !!target && !!cssClass && ( !interval || MediaSizeService.isInterval( interval ) ) ) + { + var e = pm.getRecentEvent(); + if( !!e ) e.preventDefault(); + + $( target ).removeClass( cssClass ); return false; } } From c04292a7205e6030c263cd7ceaad7286e4896571 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 4 Dec 2015 14:16:29 +0100 Subject: [PATCH 04/17] remove jquery-ui from scripts.json: Already included in plenty.shop.min.js --- tools/scripts.json | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/scripts.json b/tools/scripts.json index cae103c..286834d 100644 --- a/tools/scripts.json +++ b/tools/scripts.json @@ -7,7 +7,6 @@ "libs/owl.carousel.min.js", "libs/jquery.lazyload.js", "libs/jquery.touchSwipe.min.js", - "libs/jquery-ui.min.js", "libs/jquery.ui.touch-punch.min.js", "plenty/plenty-2.js", "plenty/plentymarketsCMStools-1.0.1.js" From 834ca6abdbd522b9d8b76730c6d210b271a9ba78 Mon Sep 17 00:00:00 2001 From: Felix Date: Fri, 4 Dec 2015 17:18:06 +0100 Subject: [PATCH 05/17] Add slide methods to UI directive --- src/directives/UI.js | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/directives/UI.js b/src/directives/UI.js index b608ae7..5154e2b 100644 --- a/src/directives/UI.js +++ b/src/directives/UI.js @@ -21,6 +21,9 @@ initToTop : initToTop, initLazyload : initLazyload, initSlideToggle : initSlideToggle, + slideDown : slideDown, + slideUp : slideUp, + slideToggle : slideToggle, toggleHideShow : toggleHideShow, toggleSocialShare : toggleSocialShare, toggleClass : toggleClass, @@ -263,6 +266,34 @@ } } + function slideDown( target, duration ) + { + duration = duration || 400; + $(target).parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); + $(target).slideDown( duration, function() { + fireEqualHeight(); + }); + } + + function slideUp( target, duration ) + { + duration = duration || 400; + $(target).parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); + $(target).slideUp( duration, function() { + fireEqualHeight(); + }); + } + + function slideToggle( target, duration ) + { + duration = duration || 400; + $(target).parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); + $(target).slideToggle( duration, function() { + fireEqualHeight(); + }); + } + + /** * TODO check comment * Social Share Activation From 7b84cbdede35145f164241edf42f5ceaa33182ae Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 9 Dec 2015 11:33:29 +0100 Subject: [PATCH 06/17] FEATURE introduce FeedbackService --- src/services/FeedbackService.js | 127 ++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 src/services/FeedbackService.js diff --git a/src/services/FeedbackService.js b/src/services/FeedbackService.js new file mode 100644 index 0000000..3054f0e --- /dev/null +++ b/src/services/FeedbackService.js @@ -0,0 +1,127 @@ +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Services + */ +(function($, pm) { + + pm.service('FeedbackService', function( API ) { + + return { + getFeedbacks: getFeedbacks, + addFeedback: addFeedback, + ArticleTypes: articleTypes(), + FeedbackTypes: feedbackTypes() + }; + + /* + FeedbackService + .getFeedbacks().between('2014-12-03', '2015-07-01') + .for( FeedbackService.ArticleTypes.ITEM, 2732, FeedbackService.FeedbackTypes.COMMENTS_ONLY ); + */ + function getFeedbacks() { + var feedbackInterval = { + dateStart: null, + dateEnd: null + }; + + return { + between: setFeedbackInterval, + for: listFeedbacks + }; + + function setFeedbackInterval( start, end ) { + feedbackInterval.dateStart = start; + feedbackInterval.dateEnd = end; + return this; + } + + function listFeedbacks( articleType, referenceId, feedbackType ) { + + var params = { + ReferenceId: referenceId, + FromDate: feedbackInterval.dateStart, + ToDate: feedbackInterval.dateEnd, + FeedbackType: feedbackType || feedbackTypes().COMMENTS_AND_RATINGS + }; + return API.get( '/rest/feedback/'+articleType+'/', params ); + + } + } + + /* + FeedbackService + .addFeedback() + .withRating( 5 ) + .withComment( 'Hallo' ) + .withAuthor( 'Felix', 'felix.dausch@plentymarkets.com', 123456 ) + .to( FeedbackService.ArticleTypes.ITEM, 2732 ); + */ + function addFeedback() { + + var params = { + Rating: 1.0, + Text: '', + Author: '', + Email: '', + CustomerId: 0 + }; + + return { + withRating: withRating, + withComment: withComment, + withAuthor: withAuthor, + to: sendFeedback + }; + + function withRating( rating ) { + params.Rating = rating; + return this; + } + + function withComment( comment ) { + params.Text = comment; + return this; + } + + function withAuthor( author, mail, customerID ) { + params.Author = author; + if( !!mail ) params.Email = mail; + if( !!customerID ) params.CustomerId = customerID; + return this; + } + + function sendFeedback( articleType, referenceId ) { + return API.post( '/rest/feedback/'+articleType+'/', params ); + + } + + } + + function feedbackTypes() { + return { + COMMENTS_ONLY: 'comments_only', + RATINGS_ONLY: 'ratings_only', + COMMENTS_AND_RATINGS: 'comments_with_ratings' + } + } + + function articleTypes() { + return { + ITEM: 'item', + CATEGORY: 'category', + BLOG: 'blog' + } + } + + + + }, ['APIFactory']); +}(jQuery, PlentyFramework)); \ No newline at end of file From 03dde143b1e9ab5177c7694cceb36b7b5847f2ab Mon Sep 17 00:00:00 2001 From: Maximilian Lauterbach Date: Wed, 9 Dec 2015 12:03:08 +0100 Subject: [PATCH 07/17] fixed variable name for PostFinder --- dist/plentymarketsCMStools-1.0.1.js | 6195 +++++++++++++++++++++++++++ src/services/CheckoutService.js | 2 +- 2 files changed, 6196 insertions(+), 1 deletion(-) create mode 100644 dist/plentymarketsCMStools-1.0.1.js diff --git a/dist/plentymarketsCMStools-1.0.1.js b/dist/plentymarketsCMStools-1.0.1.js new file mode 100644 index 0000000..85181f0 --- /dev/null +++ b/dist/plentymarketsCMStools-1.0.1.js @@ -0,0 +1,6195 @@ +(function defineMustache(global,factory){if(typeof exports==="object"&&exports&&typeof exports.nodeName!=="string"){factory(exports)}else if(typeof define==="function"&&define.amd){define(["exports"],factory)}else{global.Mustache={};factory(Mustache)}})(this,function mustacheFactory(mustache){var objectToString=Object.prototype.toString;var isArray=Array.isArray||function isArrayPolyfill(object){return objectToString.call(object)==="[object Array]"};function isFunction(object){return typeof object==="function"}function typeStr(obj){return isArray(obj)?"array":typeof obj}function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function hasProperty(obj,propName){return obj!=null&&typeof obj==="object"&&propName in obj}var regExpTest=RegExp.prototype.test;function testRegExp(re,string){return regExpTest.call(re,string)}var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string)}var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};function escapeHtml(string){return String(string).replace(/[&<>"'\/]/g,function fromEntityMap(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tagsToCompile){if(typeof tagsToCompile==="string")tagsToCompile=tagsToCompile.split(spaceRe,2);if(!isArray(tagsToCompile)||tagsToCompile.length!==2)throw new Error("Invalid tags: "+tagsToCompile);openingTagRe=new RegExp(escapeRegExp(tagsToCompile[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tagsToCompile[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tagsToCompile[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function eos(){return this.tail===""};Scanner.prototype.scan=function scan(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function scanUntil(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function push(view){return new Context(view,this)};Context.prototype.lookup=function lookup(name){var cache=this.cache;var value;if(cache.hasOwnProperty(name)){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index")value=this.renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this.unescapedValue(token,context);else if(symbol==="name")value=this.escapedValue(token,context);else if(symbol==="text")value=this.rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype.renderSection=function renderSection(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;j\n" + + " {{#values}}\n" + + "
  • \n" + + " \n" + + " {{.}}\n" + + " \n" + + "
  • \n" + + " {{/values}}\n" + + ""; + +TemplateCache["addressSuggestions/postFinder.html"] = "{{#addresses}}\n" + + "
    \n" + + "
    \n" + + " \n" + + "
    \n" + + "
    \n" + + "{{/addresses}}\n" + + ""; + +TemplateCache["error/errorMessage.html"] = "
    \n" + + " Code {{code}}:\n" + + " {{{message}}}\n" + + "
    \n" + + ""; + +TemplateCache["error/errorPopup.html"] = "
    \n" + + " \n" + + "
    \n" + + "
    \n" + + "
    \n" + + ""; + +TemplateCache["modal/modal.html"] = "
    \n" + + "
    \n" + + "
    \n" + + "\n" + + " {{#title}}\n" + + "
    \n" + + " \n" + + "

    {{{title}}}

    \n" + + "
    \n" + + " {{/title}}\n" + + "\n" + + "
    {{{content}}}
    \n" + + "\n" + + "
    \n" + + "\n" + + " {{#labelDismiss}}\n" + + " \n" + + " {{/labelDismiss}}\n" + + "\n" + + " \n" + + "
    \n" + + "
    \n" + + "
    \n" + + "
    \n" + + ""; + +TemplateCache["waitscreen/waitscreen.html"] = "
    "; + +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module PlentyFramework + */ +(function( $ ) +{ + + /** + * Collection of uncompiled registered factories & services. + * See {{#crossLink "PlentyFramework/compile:method"}}.compile(){{/crossLink}} + * @attribute components + * @static + * @type {{factories: {}, services: {}}} + */ + var components = { + factories : {}, + services : {}, + directives: {} + }; + + /** + * Framework providing client functions for plentymarkets Webshops. + * @class PlentyFramework + * @constructor + */ + PlentyFramework = function() + { + }; + + var instance = null; + PlentyFramework.getInstance = function() + { + instance = instance || new PlentyFramework(); + return instance; + }; + + /** + * Customizable controls for partials will be injected here. + * (e.g. Modal) + * @attribute + * @static + * @type {object} + */ + PlentyFramework.partials = {}; + + /** + * Collection of registered global variables + * @attribute + * @static + * @type {object} + */ + PlentyFramework.globals = {}; + + /** + * Set a global variable. + * @function setGlobal + * @static + * @param {string} identifier A unique identifier to reference this variable + * @param {*} value The value to set + * @return {*} The value + */ + PlentyFramework.setGlobal = function( identifier, value ) + { + if ( PlentyFramework.globals.hasOwnProperty( identifier ) ) + { + console.error( 'Global variable "' + identifier + '" already exists and cannot be overridden.' ); + return null; + } + + PlentyFramework.globals[identifier] = value; + + return PlentyFramework.globals[identifier]; + }; + + /** + * Get the value of a global variable or undefined if not exists + * @function getGlobal + * @static + * @param identifier The identifier of the requested variable + * @return {*} The value of the variable + */ + PlentyFramework.getGlobal = function( identifier ) + { + return PlentyFramework.globals[identifier]; + }; + + /** + * Collection of registered directives + * @type {Array} + * @static + */ + PlentyFramework.directives = {}; + + /** + * Register directive. Directives can be bound to dynamically added nodes by calling pm.bindPlentyFunctions(); + * @function directive + * @static + * @param {string} selector jQuery selector of the DOM-elements to bind the directive to + * @param {function} callback Function to add directives behaviour + * @param {Array} dependencies List of required services. Services will be passed to callback function + * @param {boolean} allowDuplicates Defines if a directive can be bound to the same element multiple times + * @return {object} The created directive + */ + PlentyFramework.directive = function( directiveName, directiveFunctions, dependencies ) + { + // Catch type mismatching for 'directiveName' + if ( typeof directiveName !== 'string' ) + { + console.error( "Type mismatch: Expect first parameter to be a 'string', '" + typeof directiveName + "' given." ); + return; + } + + // Catch type mismatching for 'serviceFunctions' + if ( typeof directiveFunctions !== 'function' ) + { + console.error( "Type mismatch: Expect second parameter to be a 'function', '" + typeof directiveFunctions + "' given." ); + return; + } + + dependencies = dependencies || []; + + components.directives[directiveName] = { + name : directiveName, + dependencies: dependencies, + compile : function() + { + var params = PlentyFramework.resolveServices( dependencies ); + PlentyFramework.directives[directiveName] = directiveFunctions.apply( null, params ); + } + }; + }; + + /** + * Bind registered directives. + * @function bindDirectives + * @param {string} [directiveSelector] restrict binding to elements matching this selector + */ + PlentyFramework.prototype.bindDirectives = function( rootElement ) + { + + rootElement = rootElement || 'html'; + + $( rootElement ).find( '[data-plenty]' ).each( function( i, element ) + { + + var directives = parseDirectives( $( element ).attr( 'data-plenty' ), $( element ) ); + + if ( directives.length <= 0 ) + { + // continue + return; + } + + addCustomEvents( element ); + + for ( var i = 0; i < directives.length; i++ ) + { + var directive = directives[i]; + if ( !!PlentyFramework.directives[directive.class] && PlentyFramework.directives.hasOwnProperty( directive.class ) ) + { + + var callback = PlentyFramework.directives[directive.class][directive.method]; + if ( !!callback && typeof callback == "function" ) + { + + if ( directive.event == "ready" ) + { + callback.apply( null, directive.params ); + } + else + { + bindEventCallback( $( element ), directive.event, callback, directive.params ); + /* + $( element ).on( directive.event, function( e ) + { + directive = injectEvent( directive, e ); + return callback.apply( null, directive.params ); + } ); + */ + } + + } + else + { + console.error( "Method not found: " + directive.method + " in " + directive.class ); + } + + } + else + { + console.error( "Directive not found: " + directive.class ); + } + } + } ); + + $( document ).trigger( 'initPartials', rootElement ); + }; + + var eventStack = []; + + PlentyFramework.getRecentEvent = function( eventType ) + { + var lastEventIdx = eventStack.length - 1; + if( !eventType ) + { + return eventStack[ lastEventIdx ]; + } + else + { + for( var i = lastEventIdx; i >= 0; i-- ) + { + if( eventType == eventStack[i].type ) + { + return eventStack[i]; + } + } + } + + return null; + + }; + + PlentyFramework.pushEvent = function( event ) + { + eventStack.push( event ); + }; + + + /** + * Bind event to element by eventType. + * If cms says "click:Foo.bar(this, event)" eventType is "click". + * + * @param $elem - jQuery object on which event was triggered + * @param eventType - type of event + * @param callback - callback function of directive [example: "bar(this, event)"] + * @param params - list of parameters for callback function. + */ + function bindEventCallback( $elem, eventType, callback, params ) + { + $elem.on( eventType, function( event ) + { + eventStack.push( event ); + return callback.apply( null, params ); + } ); + } + + function addCustomEvents( element ) + { + + var $elem = $( element ); + + if( $elem.is('input[type="checkbox"]') ) + { + $elem.on('change', function() { + + if( $elem.is(':checked') ) + { + $elem.trigger('check'); + } + else + { + $elem.trigger('uncheck'); + } + }); + } + + if( $elem.is('input[type="radio"]') ) + { + $elem.on('change', function() { + + var radioGroup = $elem.attr('name'); + + $( 'input[type="radio"][name="' + radioGroup + '"]' ).each(function( i, radio ) { + var $radio = $( radio ); + if( $radio.is(':checked') ) + { + $radio.trigger('check'); + } + else + { + $radio.trigger('uncheck'); + } + + }); + + }); + } + } + + function parseDirectives( input, thisValue ) + { + var directivePattern = /^(([\w]+):)?([\w]+)\.([\w]+)(\((.*)\))?$/; + var expressions = input.split( ';' ); + var directives = []; + + for ( var i = 0; i < expressions.length; i++ ) + { + var expression = expressions[i].trim(); + + if ( !expression ) + { + continue; + } + + if ( !directivePattern.test( expression ) ) + { + // console.warn( "Invalid directive: " + expression ); + continue; + } + + var match = expression.match( directivePattern ); + + if ( !match[3] || match[3].length <= 0 ) + { + console.error( "Cannot parse '" + expression + "': Class name not set." ); + continue; + } + + if ( !match[4] || match[4].length <= 0 ) + { + console.error( "Cannot parse '" + expression + "': Method not set." ); + continue; + } + + var directive = { + event : match[2] || 'ready', + class : match[3], + method: match[4], + params: [] + }; + + if ( !!match[6] && match[6].length > 0 ) + { + var params = match[6].match( /(['][^']+['])|([\w-]+)|(["][^"]+["])/g ); + for ( var j = 0; j < params.length; j++ ) + { + var param = params[j].trim(); + if ( !isNaN( parseFloat( param ) ) ) + { + directive.params.push( parseFloat( param ) ); + } + else if ( param.toLowerCase() == 'true' ) + { + directive.params.push( true ); + } + else if ( param.toLowerCase() == 'false' ) + { + directive.params.push( false ); + } + else if ( param.toLowerCase() == 'this' ) + { + directive.params.push( thisValue ); + } + else + { + directive.params.push( param.replace( /^['"]|['"]$/g, '' ) ); + } + } + } + + directives.push( directive ); + + } + return directives; + } + + /** + * Register a new service + * @function service + * @static + * @param {string} serviceName Unique identifier of the service to get/ create + * @param {function} serviceFunctions Callback containing all public functions of this service. + * @param {Array} [dependencies] Identifiers of required services to inject in serviceFunctions + * @return {object} The object described in serviceFunctions(). Can be received via + * PlentyFramework.[serviceName] + */ + PlentyFramework.service = function( serviceName, serviceFunctions, dependencies ) + { + + // Catch type mismatching for 'serviceName' + if ( typeof serviceName !== 'string' ) + { + console.error( "Type mismatch: Expect first parameter to be a 'string', '" + typeof serviceName + "' given." ); + return; + } + + // Catch type mismatching for 'serviceFunctions' + if ( typeof serviceFunctions !== 'function' ) + { + console.error( "Type mismatch: Expect second parameter to be a 'function', '" + typeof serviceFunctions + "' given." ); + return; + } + + dependencies = dependencies || []; + + components.services[serviceName] = { + name : serviceName, + dependencies: dependencies, + compile : function() + { + var params = PlentyFramework.resolveFactories( dependencies ); + PlentyFramework.prototype[serviceName] = serviceFunctions.apply( null, params ); + } + }; + + }; + + /** + * Returns an array containing required factories given by string identifier + * @function resolveServices + * @static + * @private + * @param {Array} dependencies Names of required factories + * @return {Array} Objects to apply to callback function + */ + PlentyFramework.resolveServices = function( dependencies ) + { + var compiledServices = []; + + $.each( dependencies, function( j, dependency ) + { + + // factory not found: try to compile dependent factory first + if ( !PlentyFramework.prototype.hasOwnProperty( dependency ) ) + { + if ( components.services.hasOwnProperty( dependency ) ) + { + components.services[dependency].compile(); + } + else + { + console.error( 'Cannot inject Service "' + dependency + '": Service not found.' ); + return false; + } + } + var service = PlentyFramework.prototype[dependency]; + compiledServices.push( service ); + } ); + + return compiledServices; + }; + + /** + * Collection of compiled factories + * @attribute factories + * @static + * @type {object} + */ + PlentyFramework.factories = {}; + + /** + * Register a new factory + * @function factory + * @static + * @param {string} factoryName A unique name of the new factory + * @param {function} factoryFunctions The function describing the factory + * @param {Array} dependencies List of required factories to inject + */ + PlentyFramework.factory = function( factoryName, factoryFunctions, dependencies ) + { + + // Catch type mismatching for 'serviceName' + if ( typeof factoryName !== 'string' ) + { + console.error( "Type mismatch: Expect first parameter to be a 'string', '" + typeof factoryName + "' given." ); + return; + } + + // Catch type mismatching for 'serviceFunctions' + if ( typeof factoryFunctions !== 'function' ) + { + console.error( "Type mismatch: Expect second parameter to be a 'function', '" + typeof factoryFunctions + "' given." ); + return; + } + + dependencies = dependencies || []; + components.factories[factoryName] = { + name : factoryName, + dependencies: dependencies, + compile : function() + { + var params = PlentyFramework.resolveFactories( dependencies ); + PlentyFramework.factories[factoryName] = factoryFunctions.apply( null, params ); + } + }; + + }; + + /** + * Returns an array containing required factories given by string identifier + * @function resolveFactories + * @static + * @private + * @param {Array} dependencies Names of required factories + * @return {Array} Objects to apply to callback function + */ + PlentyFramework.resolveFactories = function( dependencies ) + { + var compiledFactories = []; + + $.each( dependencies, function( j, dependency ) + { + + // factory not found: try to compile dependent factory first + if ( !PlentyFramework.factories.hasOwnProperty( dependency ) ) + { + if ( components.factories.hasOwnProperty( dependency ) ) + { + components.factories[dependency].compile(); + } + else + { + console.error( 'Cannot inject Factory "' + dependency + '": Factory not found.' ); + return false; + } + } + var factory = PlentyFramework.factories[dependency]; + compiledFactories.push( factory ); + } ); + + return compiledFactories; + }; + + /** + * Renders html template. Will provide given data to templates scope. + * Uses Mustache syntax for data-binding. + * @function compileTemplate + * @static + * @param {String} template relative path to partials template to load. Base path = '/src/partials/' + * @param {Object} data data to privide to templates scope. + * @returns {String} The rendered html string + */ + PlentyFramework.compileTemplate = function( template, data ) + { + data = data || {}; + data.translate = function() + { + return function( text, render ) + { + return render( PlentyFramework.translate( text ) ); + }; + }; + return Mustache.render( TemplateCache[template], data ); + }; + + /** + * The path on the server where the script is located in. + * @attribute + * @static + * @type {String} + */ + PlentyFramework.scriptPath = ''; + + /** + * Collection of locale strings will be injected here after reading language file. + * @attribute + * @static + * @type {Object} + */ + PlentyFramework.Strings = {}; + + /** + * Load language file containing translations of locale strings. + * @function loadLanguageFile + * @static + * @param fileName relative path to language file. + */ + PlentyFramework.loadLanguageFile = function( fileName ) + { + $.get( PlentyFramework.scriptPath + fileName ).done( function( response ) + { + PlentyFramework.Strings = response; + } ); + }; + + /** + * Try to get locale translation of given string. + * Render translated string using Mustache syntax + * if additional parameters are given. + * @function translate + * @static + * @param {String} string The string to translate + * @param {Object} [params] additional data for rendering + * @returns {String} The translation of the given string if found. Otherwise returns the original string. + */ + PlentyFramework.translate = function( string, params ) + { + var localeString; + if ( PlentyFramework.Strings.hasOwnProperty( string ) ) + { + localeString = PlentyFramework.Strings[string]; + } + else + { + localeString = string; + console.warn( 'No translation found for "' + localeString + '".' ); + } + + if ( !!params ) + { + localeString = Mustache.render( localeString, params ); + } + + return localeString; + + }; + + /** + * Compile registered factories & services + * @function compile + * @static + */ + PlentyFramework.compile = function() + { + + for ( var factory in components.factories ) + { + if ( !PlentyFramework.factories.hasOwnProperty( factory ) ) + { + components.factories[factory].compile(); + } + } + + for ( var service in components.services ) + { + if ( !PlentyFramework.prototype.hasOwnProperty( service ) ) + { + components.services[service].compile(); + } + } + + for ( var directive in components.directives ) + { + if ( !PlentyFramework.directives.hasOwnProperty( directive ) ) + { + components.directives[directive].compile(); + } + } + + var scripts = document.getElementsByTagName( 'SCRIPT' ); + if ( scripts.length > 0 ) + { + PlentyFramework.scriptPath = scripts[scripts.length - 1].src.match( /(.*)\/(.*)\.js(\?\S*)?$/ )[1]; + } + + }; + +}( jQuery )); + + + + +PlentyFramework.cssClasses = { + + active: "active" + +}; +(function( $, pm ) +{ + + pm.partials.Error = { + + /** + * Will be called, after the error popup was created and injected in DOM. + * @param {HTMLElement} popup The injected element of the popup + */ + init: function( popup ) + { + $( popup ).find( '.close' ).click( function() + { + popup.hide(); + popup.find( '.plentyErrorBoxInner' ).html( '' ); + } ); + }, + + /** + * Will be called for each thrown error. Has to be injected in DOM manually. + * @param {HTMLElement} popup The error popup element + * @param {HTMLElement} error The error message element + */ + addError: function( popup, error ) + { + var errorCode = $( error ).attr( 'data-plenty-error-code' ); + + if ( $( popup ).find( '[data-plenty-error-code="' + errorCode + '"]' ).length <= 0 ) + { + $( popup ).find( '.plentyErrorBoxInner' ).append( error ); + } + }, + + /** + * Will be called, after initialization and injection of all errors + * @param {HTMLElement} popup The error popup element + */ + show: function( popup ) + { + $( popup ).show(); + } + + } + +})( jQuery, PlentyFramework ); +(function( $, pm ) +{ + + pm.partials.Modal = { + + /** + * Will be called after a new modal was created and injected into DOM + * @param {HTMLElement} element The injected modal element + * @param {Modal} modal The instance of the current modal + */ + init: function( element, modal ) + { + element.on( 'hidden.bs.modal', function() + { + modal.hide(); + element.remove(); + } ); + + if ( modal.timeout > 0 ) + { + element.on( 'hide.bs.modal', modal.stopTimeout ); + element.find( '.modal-content' ).hover( function() + { + modal.pauseTimeout(); + }, function() + { + if ( element.is( '.in' ) ) + { + modal.continueTimeout(); + } + } ); + } + }, + + /** + * Will be called if a Modal requests to show. + * @param {HTMLElement} element The injected modal element + */ + show: function( element ) + { + element.modal( 'show' ); + }, + + /** + * Will be called if a Modal requests to hide. + * @param {HTMLElement} element The injected modal element + */ + hide: function( element ) + { + element.modal( 'hide' ); + }, + + /** + * Detect if a given HTML string contains a modal + * @param {HTMLElement} html the element to search a modal in. + * @returns {boolean} true if a modal was found + */ + isModal: function( html ) + { + return $( html ).filter( '.modal' ).length + $( html ).find( '.modal' ).length > 0; + }, + + /** + * Filter a modal from a given HTML string + * @param {HTMLElement} html the element to get a modal from. + * @returns {HTMLElement} the filtered modal element + */ + getModal: function( html ) + { + var modal = $( html ); + if ( modal.length > 1 ) + { + modal = $( html ).filter( '.modal' ) || $( html ).find( '.modal' ); + } + + return modal; + } + + }; + +}( jQuery, PlentyFramework )); +(function( $ ) +{ + + $( document ).on( 'initPartials', function( e, root ) + { + + $( root ).find( '[data-toggle="tooltip"]' ).tooltip( { + container: 'body' + } ); + + } ); + +})( jQuery ); +(function( $, pm ) +{ + + pm.partials.WaitScreen = { + + /** + * Will be called if the wait screen should be shown + * @param {HTMLElement} element The wait screen element + */ + show: function( element ) + { + element.addClass( 'in' ); + }, + + /** + * Will be called if the wait screen should be hidden + * @param {HTMLElement} element The wait screen element + */ + hide: function( element ) + { + element.removeClass( 'in' ); + } + + }; + +})( jQuery, PlentyFramework ); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Factories + */ +(function( $, pm ) +{ + + /** + * Handles requests to ReST API. Provides a {{#crossLink "APIFactory/handleError:method"}}default + * error-handling{{/crossLink}}. Request parameters will be parsed to json internally
    + * Requires: + *
      + *
    • {{#crossLink "UIFactory"}}UIFactory{{/crossLink}}
    • + *
    + * @class APIFactory + * @static + */ + pm.factory( 'APIFactory', function( UI ) + { + + return { + get : _get, + post : _post, + put : _put, + delete: _delete, + idle : _idle + }; + + /** + * Is called by default if a request failed.
    + * Can be prevented by setting the requests last parameter to false. + * + * @function handleError + * @private + * + * @param {object} jqXHR jQuery + * deferred Object + */ + function handleError( jqXHR ) + { + try + { + var responseText = $.parseJSON( jqXHR.responseText ); + UI.printErrors( responseText.error.error_stack ); + } + catch ( e ) + { + UI.throwError( jqXHR.status, jqXHR.statusText ); + } + } + + /** + * Sends a GET request to ReST-API + * + * @function get + * + * @param {string} url The URL to send the request to + * @param {object} params The data to append to requests body. Will be converted to JSON + * internally + * @param {boolean} [ignoreErrors=false] disable/ enable defaults error handling + * @param {boolean} [runInBackground=false] show wait screen while request is in progress. + * @return {object} jQuery + * deferred Object + */ + function _get( url, params, ignoreErrors, runInBackground, sync ) + { + + if ( !runInBackground ) + { + UI.showWaitScreen(); + } + + return $.ajax( + url, + { + type : 'GET', + data: params, + dataType: 'json', + async : !sync, + error : function( jqXHR ) + { + if ( !ignoreErrors ) + { + handleError( jqXHR ) + } + } + } + ).always( function() + { + if ( !runInBackground ) + { + UI.hideWaitScreen(); + } + } ); + + } + + /** + * Sends a POST request to ReST-API + * + * @function post + * + * @param {string} url The URL to send the request to + * @param {object} data The data to append to requests body. Will be converted to JSON + * internally + * @param {boolean} [ignoreErrors=false] disable/ enable defaults error handling + * @param {boolean} [runInBackground=false] show wait screen while request is in progress. + * @return {object} jQuery + * deferred Object + */ + function _post( url, data, ignoreErrors, runInBackground ) + { + + var params = { + type : 'POST', + dataType: 'json', + error : function( jqXHR ) + { + if ( !ignoreErrors ) + { + handleError( jqXHR ) + } + } + }; + + if ( !!data && data.isFile ) + { + params.cache = data.cache; + params.processData = data.processData; + params.data = data.data; + params.contentType = false; + } + else + { + params.data = JSON.stringify( data ); + params.contentType = 'application/json'; + } + + if ( !runInBackground ) + { + UI.showWaitScreen(); + } + + return $.ajax( + url, params + ).always( function() + { + if ( !runInBackground ) + { + UI.hideWaitScreen(); + } + } ); + } + + /** + * Sends a PUT request to ReST-API + * + * @function put + * + * @param {string} url The URL to send the request to + * @param {object} data The data to append to requests body. Will be converted to JSON + * internally + * @param {boolean} [ignoreErrors=false] disable/ enable defaults error handling + * @param {boolean} [runInBackground=false] show wait screen while request is in progress. + * @return {object} jQuery + * deferred Object + */ + function _put( url, data, ignoreErrors, runInBackground ) + { + + if ( !runInBackground ) + { + UI.showWaitScreen(); + } + + return $.ajax( + url, + { + type : 'PUT', + data: JSON.stringify( data ), + dataType: 'json', + contentType: 'application/json', + error : function( jqXHR ) + { + if ( !ignoreErrors ) + { + handleError( jqXHR ) + } + } + } + ).always( function() + { + if ( !runInBackground ) + { + UI.hideWaitScreen(); + } + } ); + + } + + /** + * Sends a DELETE request to ReST-API + * + * @function delete + * + * @param {string} url The URL to send the request to + * @param {object} data The data to append to requests body. Will be converted to JSON + * internally + * @param {boolean} [ignoreErrors=false] disable/ enable defaults error handling + * @param {boolean} [runInBackground=false] show wait screen while request is in progress. + * @returns {object} jQuery + * deferred Object + */ + function _delete( url, data, ignoreErrors, runInBackground ) + { + + if ( !runInBackground ) + { + UI.showWaitScreen(); + } + + return $.ajax( + url, + { + type : 'DELETE', + data: JSON.stringify( data ), + dataType: 'json', + contentType: 'application/json', + error : function( jqXHR ) + { + if ( !ignoreErrors ) + { + handleError( jqXHR ) + } + } + } + ).always( function() + { + if ( !runInBackground ) + { + UI.hideWaitScreen(); + } + } ); + + } + + /** + * Get a idle request doing nothing for chaining methods + * @returns {object} jQuery + * deferred Object + */ + function _idle() + { + return $.Deferred().resolve(); + } + + }, ['UIFactory'] ); +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Factories + */ +(function( pm ) +{ + + /** + * Provide methods for receiving layout containers, layout parameters + * or category content from API
    + * Requires: + *
      + *
    • {{#crossLink "APIFactory"}}APIFactory{{/crossLink}}
    • + *
    + * @class CMSFactory + * @static + */ + pm.factory( 'CMSFactory', function( API ) + { + + return { + getContainer : getContainer, + getParams : getParams, + getCategoryContent: getCategoryContent + }; + + /** + * Prepare the request to receive HTML-Content from CMS + * @function getContainer + * @param {string} containerName The Layoutcontainer to receive. + * @param {object} params Additional GET-parameters. + * @returns {object} The prepared request. Call .from( layoutGroup ) to specify the location in + * the CMS + * (e.g. 'Checkout') + * @example + * CMSFactory.getContainer( 'CheckoutTotals' ).from( 'Checkout' ) + * .done(function( response ) { + * // container content + * var html = response.data[0] + * }); + */ + function getContainer( containerName, params ) + { + + function from( layoutGroup ) + { + return API.get( '/rest/' + layoutGroup.toLowerCase() + '/container_' + containerName.toLowerCase() + '/', params ); + } + + return { + from: from + } + + } + + /** + * Prepare the request to receive Layout parameters for a template + * @function getParams + * @param {string} containerName The Layoutcontainer to receive the parameteres of. + * @param {object} params Additional GET-parameters. + * @returns {object} The prepared request. Call .from( layoutGroup ) to specify the + * location in the CMS + * (e.g. 'ItemView') + * @example + * CMSFactory.getParams( 'BasketItemsList' ).from( 'ItemView' ) + * .done(function( response ) { + * // BasketItems + * var items = response.data; + * }); + */ + function getParams( containerName, params ) + { + + function from( layoutGroup ) + { + return API.get( '/rest/' + layoutGroup.toLowerCase() + '/' + containerName.toLowerCase() + '/', params ); + } + + return { + from: from + } + } + + /** + * Get the content of a category specified by its ID + * @function getCategoryContent + * @param {number} categoryID The ID of the category to get the content from + * @returns {object} jQuery deferred + * Object + */ + function getCategoryContent( categoryID ) + { + + return API.get( '/rest/categoryview/categorycontentbody/?categoryID=' + categoryID ); + } + + }, ['APIFactory'] ); +}( PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Factories + */ +(function( pm ) +{ + + /** + * Holds checkout data for global access and provides methods + * for reloading content dynamically-
    + * Requires: + *
      + *
    • {{#crossLink "APIFactory"}}APIFactory{{/crossLink}}
    • + *
    • {{#crossLink "CMSFactory"}}CMSFactory{{/crossLink}}
    • + *
    • {{#crossLink "UIFactory"}}UIFactory{{/crossLink}}
    • + *
    + * @class CheckoutFactory + * @static + */ + pm.factory( 'CheckoutFactory', function( API, CMS, UI ) + { + + // data received from ReST API + var checkoutData; + + // instance wrapped checkout object for global access + var checkout; + + return { + getCheckout : getCheckout, + setCheckout : setCheckout, + loadCheckout : loadCheckout, + reloadContainer : reloadContainer, + reloadCatContent : reloadCatContent, + reloadItemContainer: reloadItemContainer + }; + + function Checkout() + { + return checkoutData; + } + + /** + * Returns instance of wrapped checkout object + * @function getCheckout + * @returns {Checkout} Instance of checkout object + */ + function getCheckout( copy ) + { + if ( !checkout || !checkoutData ) + { + loadCheckout( true ); + } + + if ( !!copy ) + { + return $.extend( true, {}, checkoutData ); + } + return checkout; + } + + /** + * Receive global checkout data from ReST-API + * @function loadCheckout + * @return {object} jQuery deferred + * Object + */ + function loadCheckout( sync ) + { + + return API.get( '/rest/checkout/', null, false, false, sync ) + .done( function( response ) + { + if ( !!response ) + { + checkoutData = response.data; + checkout = new Checkout(); + } + else + { + UI.throwError( 0, 'Could not receive checkout data [GET "/rest/checkout/" receives null value]' ); + } + } ); + } + + /** + * Update checkout data on server + * @function setCheckout + * @return {object} jQuery deferred + * Object + */ + function setCheckout() + { + + return API.put( '/rest/checkout', checkout ) + .done( function( response ) + { + if ( !!response ) + { + checkoutData = response.data; + checkout = new Checkout(); + } + else + { + UI.throwError( 0, 'Could not receive checkout data [GET "/rest/checkout/" receives null value]' ); + } + } ); + + } + + /** + * Get layout container from server and replace received HTML + * in containers marked with data-plenty-checkout-template="..." + * @function reloadContainer + * @param {string} container Name of the template to load from server + * @return {object} jQuery deferred + * Object + */ + function reloadContainer( container ) + { + + return CMS.getContainer( "checkout" + container ).from( 'checkout' ) + .done( function( response ) + { + $( '[data-plenty-checkout-template="' + container + '"]' ) + .each( function( i, elem ) + { + $( elem ).html( response.data[0] ); + pm.getInstance().bindDirectives( elem ); + $( window ).trigger( 'contentChanged' ); + } ); + } ); + } + + /** + * Get category content from server and replace received HTML + * in containers marked with data-plenty-checkout-catcontent="..." + * @function reloadCatContent + * @param {number} catId ID of the category to load content (description 1) from server + * @return {object} jQuery deferred + * Object + * @deprecated + */ + function reloadCatContent( catId ) + { + + return CMS.getCategoryContent( catId ) + .done( function( response ) + { + $( '[data-plenty-checkout-catcontent="' + catId + '"]' ) + .each( function( i, elem ) + { + $( elem ).html( response.data[0] ); + pm.getInstance().bindDirectives( elem ); + $( window ).trigger( 'contentChanged' ); + + } ); + } ); + + } + + /** + * Get layout container from server and replace received HTML + * in containers marked with data-plenty-itemview-template="..." + * @function reloadItemContainer + * @param {string} container Name of the (item view) template to load from server + * @return {object} jQuery deferred + * Object + */ + function reloadItemContainer( container ) + { + + return CMS.getContainer( 'itemview' + container ).from( 'itemview' ) + .done( function( response ) + { + $( '[data-plenty-itemview-template="' + container + '"]' ) + .each( function( i, elem ) + { + $( elem ).html( response.data[0] ); + pm.getInstance().bindDirectives( elem ); + $( window ).trigger( 'contentChanged' ); + + } ); + } ); + + } + + }, ['APIFactory', 'CMSFactory', 'UIFactory'] ); +}( PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Factories + */ +(function( $, pm ) +{ + + /** + * Provides methods for creating and displaying modal popups. + * @class ModalFactory + * @static + */ + pm.factory( 'ModalFactory', function() + { + + return { + prepare: prepare, + isModal: isModal + }; + + /** + * Detect if given html contains a valid modal + * @function isModal + * @param {string} html + * @returns {boolean} + */ + function isModal( html ) + { + return PlentyFramework.partials.Modal.isModal( html ); + } + + /** + * Create a new Instance of {{#crossLink "ModalFactory.Modal"}}Modal{{/crossLink}} + * @function prepare + * @returns {Modal} + */ + function prepare() + { + return new Modal(); + } + + /** + * Holds configuration of a modal and provides methods for displaying and hiding the modal + * @class Modal + * @for ModalFactory + * @returns {Modal} + * @constructor + */ + function Modal() + { + + var modal = this; + /** + * The title of the modal + * @attribute title + * @type {string} + * @private + * @default "" + */ + modal.title = ''; + + modal.cssClass = ''; + + /** + * The content of the modal + * @attribute content + * @type {string} + * @private + * @default "" + */ + modal.content = ''; + + /** + * The content of the dismiss-button + * @attribute labelDismiss + * @type {string} + * @private + * @default "Abbrechen" + */ + modal.labelDismiss = pm.translate( "Cancel" ); + + /** + * the label of the confirmation button + * @attribute labelConfirm + * @type {string} + * @private + * @default "Bestätigen" + */ + modal.labelConfirm = pm.translate( "Confirm" ); + + /** + * Callback when modal is confirmed by clicking confirmation button. + * Modal will not be dismissed if callback returns false. + * @attribute onConfirm + * @type {function} + * @private + * @default function() {} + */ + modal.onConfirm = function() + { + }; + + /** + * Callback when modal is dismissed by closing the modal + * @attribute onDismiss + * @type {function} + * @private + * @default function() {} + */ + modal.onDismiss = function() + { + }; + + /** + * jQuery selector of the container element to display the modal in. + * @attribute container + * @type {string} + * @private + * @default "body" + */ + modal.container = 'body'; + + /** + * Timeout to close the modal automatically. Set <0 to disable. + * @attribute timeout + * @type {number} + * @private + * @default -1 + */ + modal.timeout = -1; + + modal.hide = hide; + modal.startTimeout = startTimeout; + modal.stopTimeout = stopTimeout; + modal.pauseTimeout = pauseTimeout; + modal.continueTimeout = continueTimeout; + + var bsModal; + var timeout, interval; + var timeRemaining, timeStart; + var paused = false; + + return { + setTitle : setTitle, + setClass : setClass, + setContent : setContent, + setContainer : setContainer, + setLabelConfirm: setLabelConfirm, + setLabelDismiss: setLabelDismiss, + onConfirm : onConfirm, + onDismiss : onDismiss, + setTimeout : setTimeout, + show : show, + hide : hide + }; + + /** + * Set the {{#crossLink "ModalFactory.Modal/title:attribute}}title{{/crossLink}} of the modal + * @function setTitle + * @param {string} title The title + * @returns {Modal} Modal object for chaining methods + */ + function setTitle( title ) + { + modal.title = title; + return this; + } + + function setClass( cssClass ) + { + modal.cssClass = cssClass; + return this; + } + + /** + * Set the {{#crossLink "ModalFactory.Modal/content:attribute}}content{{/crossLink}} of the modal + * @function setContent + * @param {string} content The content + * @returns {Modal} Modal object for chaining methods + */ + function setContent( content ) + { + modal.content = content; + return this; + } + + /** + * Set the {{#crossLink "ModalFactory.Modal/labelConfirm:attribute}}label of the confirmation + * button{{/crossLink}} of the modal + * @function setLabelConfirm + * @param {string} label The label + * @returns {Modal} Modal object for chaining methods + */ + function setLabelConfirm( label ) + { + modal.labelConfirm = label; + return this; + } + + /** + * Set the {{#crossLink "ModalFactory.Modal/labelDismiss:attribute}}label if the dismiss + * button{{/crossLink}} of the modal + * @function setLabelDismiss + * @param {string} label The label + * @returns {Modal} Modal object for chaining methods + */ + function setLabelDismiss( label ) + { + modal.labelDismiss = label; + return this; + } + + /** + * Set the {{#crossLink "ModalFactory.Modal/onConfirm:attribute}}confirmation callback{{/crossLink}} of the + * modal + * @function onConfirm + * @param {function} callback The callback if modal is confirmed + * @returns {Modal} Modal object for chaining methods + */ + function onConfirm( callback ) + { + modal.onConfirm = callback; + return this; + } + + /** + * Set the {{#crossLink "ModalFactory.Modal/onDismiss:attribute}}dismiss callback{{/crossLink}} of the modal + * @function onDismiss + * @param {function} callback The callback if modal is dismissed + * @returns {Modal} Modal object for chaining methods + */ + function onDismiss( callback ) + { + modal.onDismiss = callback; + return this; + } + + /** + * Set the {{#crossLink "ModalFactory.Modal/container:attribute}}container{{/crossLink}} of the modal + * @function setContainer + * @param {string} container The jQuery selector of the container to display the modal in + * @returns {Modal} Modal object for chaining methods + */ + function setContainer( container ) + { + modal.container = container; + return this; + } + + /** + * Set the {{#crossLink "ModalFactory.Modal/timeout:attribute}}timeout{{/crossLink}} of the modal + * @function setTimeout + * @param {number} timeout The timeout to close the modal automatically. Set <0 to disable + * @returns {Modal} Modal object for chaining methods + */ + function setTimeout( timeout ) + { + modal.timeout = timeout; + return this; + } + + /** + * Inject modal data in default template if not template is given + * and display the modal inside the configured container.
    + * Start timer to hide the modal automatically if timeout is set. + * @function show + */ + function show() + { + var entryNumber = 0; + if ( isModal( modal.content ) ) + { + bsModal = PlentyFramework.partials.Modal.getModal( modal.content ); + } + else + { + bsModal = $( PlentyFramework.compileTemplate( 'modal/modal.html', modal ) ); + } + + $( modal.container ).append( bsModal ); + + // append additional scripts executable + var scripts = $( modal.content ).filter( 'script' ); + if ( scripts.length > 0 ) + { + scripts.each( function( i, script ) + { + var element = document.createElement( 'script' ); + element.type = 'text/javascript'; + element.innerHTML = $( script ).text(); + $( modal.container ).append( element ); + } ); + } + + // bind callback functions + PlentyFramework.partials.Modal.init( bsModal, modal ); + bsModal.find( '[data-plenty-modal="confirm"]' ).click( function() + { + var close = modal.onConfirm(); + + if( typeof close == "undefined" ) + { + close = true; + } + + if ( close ) + { + hide( true ); + } + } ); + + PlentyFramework.partials.Modal.show( bsModal ); + + if ( modal.timeout > 0 ) + { + startTimeout(); + } + + } + + /** + * Hide the modal. + * @function hide + * @param {boolean} confirmed Flag indicating of modal is closed by confirmation button or dismissed + */ + function hide( confirmed ) + { + PlentyFramework.partials.Modal.hide( bsModal ); + + if ( !confirmed ) + { + modal.onDismiss(); + } + } + + /** + * Start the configured timeout initially + * @function startTimeout + * @private + */ + function startTimeout() + { + timeRemaining = modal.timeout; + timeStart = (new Date()).getTime(); + + timeout = window.setTimeout( function() + { + window.clearInterval( interval ); + hide(); + }, modal.timeout ); + + bsModal.find( '[data-plenty-modal="timer"]' ).text( timeRemaining / 1000 ); + interval = window.setInterval( function() + { + if ( !paused ) + { + var secondsRemaining = timeRemaining - (new Date()).getTime() + timeStart; + secondsRemaining = Math.round( secondsRemaining / 1000 ); + bsModal.find( '[data-plenty-modal="timer"]' ).text( secondsRemaining ); + } + }, 1000 ) + } + + /** + * Pause the timeout (e.g. on hover) + * @function pauseTimeout + * @private + */ + function pauseTimeout() + { + paused = true; + timeRemaining -= (new Date()).getTime() - timeStart; + window.clearTimeout( timeout ); + } + + /** + * Continue paused timeout + * @function continueTimeout + * @private + */ + function continueTimeout() + { + paused = false; + timeStart = (new Date()).getTime(); + timeout = window.setTimeout( function() + { + hide(); + window.clearInterval( interval ); + }, timeRemaining ); + } + + /** + * Stop timeout. Stopped timeouts cannot be continued. + * @function stopTimeout + * @private + */ + function stopTimeout() + { + window.clearTimeout( timeout ); + window.clearInterval( interval ); + } + + } + + } ); +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Factories + */ +(function( $, pm ) +{ + + /** + * Displaying error messages and handling wait screen + * @class UIFactory + * @static + */ + pm.factory( 'UIFactory', function() + { + /** + * Increased/ decreased when showing/ hiding wait screen to avoid stacking + * multiple instances of overlays. + * @attribute waitScreenCount + * @private + * @type {number} + * @default 0 + */ + var waitScreenCount = 0; + var waitScreen; + var errorPopup = null; + + return { + throwError : throwError, + printErrors : printErrors, + showWaitScreen: showWaitScreen, + hideWaitScreen: hideWaitScreen + }; + + /** + * Display a single error message. + * @function throwError + * @param {number} code A code identifying this error + * @param {string} msg The error message to display + */ + function throwError( code, msg ) + { + printErrors( [{code: code, message: msg}] ); + } + + /** + * Wrap error messages in error popup, if popup doesn't already contain this error + * If popup is already visible, append new errors to popup's inner HTML + * otherwise create new popup + * @function printErrors + * @param {Array} errorMessages A list of errors to display + */ + function printErrors( errorMessages ) + { + + // create error-popup if not exist + if ( !errorPopup || $( 'body' ).has( errorPopup ).length <= 0 ) + { + errorPopup = $( pm.compileTemplate( 'error/errorPopup.html' ) ); + $( 'body' ).append( errorPopup ); + pm.partials.Error.init( errorPopup ); + } + + $.each( errorMessages, function( key, error ) + { + // add additional error, if not exist. + pm.partials.Error.addError( errorPopup, $( pm.compileTemplate( 'error/errorMessage.html', error ) ) ); + } ); + + pm.partials.Error.show( errorPopup ); + + hideWaitScreen( true ); + } + + /** + * Show wait screen if not visible and increase + * {{#crossLink "UIFactory/waitScreenCount:attribute"}}waitScreenCount{{/crossLink}} + * @function showWaitScreen + */ + function showWaitScreen() + { + waitScreenCount = waitScreenCount || 0; + + // create wait-overlay if not exist + if ( !waitScreen || $( 'body' ).has( waitScreen ).length <= 0 ) + { + waitScreen = $( pm.compileTemplate( 'waitscreen/waitscreen.html' ) ); + $( 'body' ).append( waitScreen ); + } + + pm.partials.WaitScreen.show( waitScreen ); + + // increase instance counter to avoid showing multiple overlays + waitScreenCount++; + return waitScreenCount; + } + + /** + * Decrease {{#crossLink "UIFactory/waitScreenCount:attribute"}}waitScreenCount{{/crossLink}} + * and hide wait screen if waitScreenCount is 0 + * @function hideWaitScreen + * @param {boolean} forceClose set true to hide wait screen independent from the value of waitScreenCount. + */ + function hideWaitScreen( forceClose ) + { + + // decrease overlay count + waitScreenCount--; + + // hide if all instances of overlays has been closed + // or if closing is forced by user + if ( waitScreenCount <= 0 || !!forceClose ) + { + waitScreenCount = 0; + pm.partials.WaitScreen.hide( waitScreen ); + } + return waitScreenCount; + } + + } ); +}( jQuery, PlentyFramework )); +/** + * Factories provide static functions and can be injected into + * {{#crossLinkModule "Services"}}services{{/crossLinkModule}}.
    + * Factories also can inject other factories. Compared to services, + * factories are not visible in instances of {{#crossLinkModule "PlentyFramework"}}PlentyFramework{{/crossLinkModule}}. + * + * @module Factories + * @main Factories + */ +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +(function( $, pm ) +{ + pm.service( 'AddressDoctorService', function( API ) + { + return { + validateAddress: validateAddress + }; + + function validateAddress( addressForms ) + { + var addressIsValid = true; + addressForms = addressForms || '[data-plenty-address-doctor]'; + $( addressForms ).filter('[data-plenty-address-doctor]:visible').each( function( i, form ) + { + var addressDoctor = new AddressDoctor( form ); + var requiredFields = $( form ).attr( 'data-plenty-address-doctor' ).replace( /\s/g, '' ).split( ',' ); + if ( !addressDoctor.isValid( requiredFields ) ) + { + addressIsValid = false; + } + + } ); + + return addressIsValid; + } + + function AddressDoctor( form ) + { + var $form = $( form ); + var $inputs = { + Street : $form.find( 'input[name="Street"]' ), + ZIP : $form.find( 'input[name="ZIP"]' ), + City : $form.find( 'input[name="City"]' ), + HouseNo: $form.find( 'input[name="HouseNo"]' ) + }; + var $suggestionContainer = {}; + + var suggestions; + var requiredFields; + + return { + isValid: isValid + }; + + function isValid( fields ) + { + + if ( isPackstation() ) + { + return true; + } + + suggestions = new AddressList( $form.getFormValues() ); + requiredFields = fields; + + refreshView(); + + return suggestions.getAddresses().length == 1; + } + + function refreshView() + { + $( '.suggestion-list' ).remove(); + + var suggestionListVisible = false; + for ( var i = 0; i < requiredFields.length; i++ ) + { + if ( !validateInput( requiredFields[i], suggestionListVisible ) ) + { + $form.trigger( 'validationFailed' ); + suggestionListVisible = true; + } + } + + if ( suggestions.houseNoAllowed( $inputs.HouseNo.val() ) ) + { + $inputs.HouseNo.removeClass( 'has-error' ); + $form.find( 'label[for="' + $inputs.HouseNo.attr( 'id' ) + '"]' ).removeClass( 'has-error' ); + + $inputs.HouseNo.addClass( 'has-success' ); + $form.find( 'label[for="' + $inputs.HouseNo.attr( 'id' ) + '"]' ).addClass( 'has-success' ); + } + else + { + $inputs.HouseNo.removeClass( 'has-success' ); + $form.find( 'label[for="' + $inputs.HouseNo.attr( 'id' ) + '"]' ).removeClass( 'has-success' ); + + $inputs.HouseNo.addClass( 'has-error' ); + $form.find( 'label[for="' + $inputs.HouseNo.attr( 'id' ) + '"]' ).addClass( 'has-error' ); + } + } + + function validateInput( key, suggestionListVisible ) + { + var valueList = suggestions.getList( key ); + + if ( !!$suggestionContainer[key] ) + { + $suggestionContainer[key].remove(); + } + + if ( !$inputs[key] ) + { + return true; + } + + if ( valueList.length == 1 ) + { + $inputs[key].val( valueList[0] ); + + $inputs[key].removeClass( 'has-error' ); + $form.find( 'label[for="' + $inputs[key].attr( 'id' ) + '"]' ).removeClass( 'has-error' ); + + $inputs[key].addClass( 'has-success' ); + $form.find( 'label[for="' + $inputs[key].attr( 'id' ) + '"]' ).addClass( 'has-success' ); + return true; + } + else + { + $inputs[key].removeClass( 'has-success' ); + $form.find( 'label[for="' + $inputs[key].attr( 'id' ) + '"]' ).removeClass( 'has-success' ); + + $inputs[key].addClass( 'has-error' ); + $form.find( 'label[for="' + $inputs[key].attr( 'id' ) + '"]' ).addClass( 'has-error' ); + + if( !suggestionListVisible ) buildSuggestionList( $inputs[key], valueList ); + $inputs[key].off( 'focus' ); + $inputs[key].focus(); + return false; + + } + } + + function buildSuggestionList( $parent, values ) + { + var suggestionKey = $parent.attr( 'name' ); + + // render html content + $suggestionContainer[suggestionKey] = $( pm.compileTemplate( 'addressSuggestions/addressDoctor.html', {values: values} ) ); + $suggestionContainer[suggestionKey].css( { + 'width': $parent.outerWidth( true ), + 'left' : $parent.position().left, + 'top' : $parent.position().top + $parent.outerHeight( true ) + } ); + + // bind click event to list elements + $suggestionContainer[suggestionKey].find( '[data-address-value]' ).each( function( i, elem ) + { + + var $elem = $( elem ); + var value = $elem.attr( 'data-address-value' ); + + $elem.click( function() + { + // insert clicked value in input + $parent.val( value ); + + // filter addresses and show remaining suggestions + var filterAddress = {}; + filterAddress[$parent.attr( 'name' )] = value; + suggestions.filter( filterAddress ); + + // refresh suggestion lists + refreshView(); + + } ); + + } ); + + // inject html + $parent.parent().append( $suggestionContainer[suggestionKey] ); + } + + function isPackstation() + { + return ( $inputs.Street.val().toUpperCase() == "PACKSTATION" || $inputs.Street.val().toUpperCase() == "POSTFILIALE" ); + } + + } + + function AddressList( addressInput ) + { + var addresses = []; + + init(); + + return { + getAddresses : getAddresses, + getList : getList, + filter : filter, + houseNoAllowed: houseNoAllowed + }; + + function init() + { + API.get( '/rest/checkout/addresssuggestionresultslist/', { + suggestionType: "addressdoctor", + street : addressInput.Street, + ZIP : addressInput.ZIP, + city : addressInput.City, + houseNo : addressInput.HouseNo, + country : addressInput.CountryID + }, false, false, true ).done( function( response ) + { + + var responseLength = response.data.length; + + for ( var i = 0; i < responseLength; i++ ) + { + var currentResponse = response.data[i]; + + var address = getAddress( currentResponse ) + if ( !address ) + { + currentResponse.HouseNo = [currentResponse.HouseNo]; + addresses.push( currentResponse ); + } + else + { + address.HouseNo.push( currentResponse.HouseNo ); + } + + } + + } ); + } + + function getAddress( suggestion ) + { + var addressCount = addresses.length; + + for ( var j = 0; j < addressCount; j++ ) + { + if ( suggestion.Street == addresses[j].Street && addresses.ZIP == addresses[j].ZIP && suggestion.City == addresses[j].City ) + { + return addresses[j]; + } + } + + return null; + + } + + function getAddresses() + { + return addresses; + } + + function getList( key ) + { + var results = []; + var addressCount = addresses.length; + + for ( var i = 0; i < addressCount; i++ ) + { + var address = addresses[i]; + if ( $.inArray( address[key], results ) < 0 ) + { + results.push( address[key] ); + } + } + + return results; + } + + function filter( filterAddress ) + { + var filteredAddresses = []; + var addressCount = addresses.length; + + for ( var i = 0; i < addressCount; i++ ) + { + var address = addresses[i]; + if ( (!!filterAddress.Street && filterAddress.Street == address.Street) + || (!!filterAddress.ZIP && filterAddress.ZIP == address.ZIP) + || (!!filterAddress.City && filterAddress.City == address.City) ) + { + filteredAddresses.push( address ); + } + } + + addresses = filteredAddresses; + } + + function houseNoAllowed( houseNo ) + { + houseNo = parseInt( houseNo ); + + var addressCount = addresses.length; + + for ( var i = 0; i < addressCount; i++ ) + { + var address = addresses[i]; + + for ( var j = 0; j < address.HouseNo.length; j++ ) + { + var range = address.HouseNo[j].split( '-' ); + if ( ( range.length == 1 && houseNo == range[0] ) + || range.length == 2 && houseNo >= range[0] && houseNo <= range[1] ) + { + return true; + } + } + } + + return false; + } + } + + }, ['APIFactory'] ); +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Services + */ +(function( $, pm ) +{ + + /** + * Providing methods for logging in and out and registering new customers.
    + * Requires: + *
      + *
    • {{#crossLink "APIFactory"}}APIFactory{{/crossLink}}
    • + *
    • {{#crossLink "CheckoutFactory"}}CheckoutFactory{{/crossLink}}
    • + *
    + * @class AuthenticationService + * @static + */ + pm.service( 'AuthenticationService', function( API, Checkout, UI ) + { + + return { + resetPassword : resetPassword, + customerLogin : customerLogin, + setInvoiceAddress: setInvoiceAddress, + registerCustomer : registerCustomer + }; + + /** + * Reading E-Mail from form marked with data-plenty-checkout="lostPasswordForm" + * and sends request to provide a new password to the entered E-Mail-Address. + * + * @function resetPasswort + * @return {object} jQuery deferred + * Object + */ + function resetPassword() + { + + var form = $( '[data-plenty-checkout="lostPasswordForm"]' ); + + if ( form.validateForm() ) + { + + var values = form.getFormValues(); + + var params = { + Email: values.Email + }; + + return API.post( "/rest/checkout/lostpassword/", params ) + .done( function( response ) + { + if ( response.data.IsMailSend == true ) + { + $( '[data-plenty-checkout="lostPasswordTextContainer"]' ).hide(); + $( '[data-plenty-checkout="lostPasswordSuccessMessage"]' ).show(); + } + } ); + + } + } + + /** + * Try to login in with credentials read from given <form> - element. + * On success redirect to forms 'action' attribute. + * + * @function customerLogin + * @param {object} form The jQuery-wrapped form-element to read the credentials from + * @return {object} jQuery deferred + * Object + */ + function customerLogin( form ) + { + if ( form.validateForm() ) + { + var values = form.getFormValues(); + + var params = { + Email : values.loginMail, + Password: values.loginPassword + }; + + + UI.showWaitScreen(); + return API.post( "/rest/checkout/login/", params ) + .done( function() + { + // successful login -> go to form's target referenced by action-attribute + window.location.assign( form.attr( 'action' ) ); + + } ); + } + } + + /** + * Setting the invoice address of a newly registered customer or a guest. + * + * @function setInvoiceAddress + * @param {object} invoiceAddress containing address-data sent to server + * @return {object} jQuery deferred + * Object + */ + function setInvoiceAddress( invoiceAddress ) + { + + return API.post( "/rest/checkout/customerinvoiceaddress/", invoiceAddress ) + .done( function( response ) + { + Checkout.getCheckout().CustomerInvoiceAddress = response.data; + } ); + } + + /** + * Prepare address-data to register new customer. Read the address-data from a <form> marked with + * data-plenty-checkout-form="customerRegistration"
    + * On success, redirect to forms target referenced by action-attribute + * + * @function registerCustomer + * @return {object} jQuery deferred + * Object + */ + function registerCustomer() + { + var form = $( '[data-plenty-checkout-form="customerRegistration"]' ); + + if ( form.validateForm() && pm.getInstance().AddressDoctorService.validateAddress() ) + { + var values = form.getFormValues(); + + // create new invoice address + var invoiceAddress = { + LoginType : 2, + FormOfAddressID : values.FormOfAddressID, + Company : values.Company, + FirstName : values.FirstName, + LastName : values.LastName, + Street : values.Street, + HouseNo : values.HouseNo, + AddressAdditional: values.AddressAdditional, + ZIP : values.ZIP, + City : values.City, + CountryID : values.CountryID, + VATNumber : values.VATNumber, + Email : values.Email, + EmailRepeat : values.EmailRepeat, + BirthDay : values.BirthDay, + BirthMonth : values.BirthMonth, + BirthYear : values.BirthYear, + Password : values.Password, + PasswordRepeat : values.PasswordRepeat, + PhoneNumber : values.PhoneNumber, + MobileNumber : values.MobileNumber, + FaxNumber : values.FaxNumber, + Postnummer : values.Postnummer + }; + + invoiceAddress.CustomerPropertiesList = invoiceAddress.CustomerPropertiesList || []; + + form.find( "[data-plenty-property-id]" ).each( function( i, propertyInput ) + { + + invoiceAddress.CustomerPropertiesList.push( { + PropertyID : $( propertyInput ).attr( 'data-plenty-property-id' ), + PropertyValue: $( propertyInput ).val() + } ); + } ); + + return setInvoiceAddress( invoiceAddress ) + .done( function() + { + window.location.assign( form.attr( 'action' ) ); + } ); + } + } + }, ['APIFactory', 'CheckoutFactory', 'UIFactory'] ); + +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Services + */ +(function( $, pm ) +{ + + /** + * Providing methods for adding, editing or removing basket items and coupon codes
    + * Requires: + *
      + *
    • {{#crossLink "APIFactory"}}APIFactory{{/crossLink}}
    • + *
    • {{#crossLink "UIFactory"}}UIFactory{{/crossLink}}
    • + *
    • {{#crossLink "CMSFactory"}}CMSFactory{{/crossLink}}
    • + *
    • {{#crossLink "CheckoutFactory"}}CheckoutFactory{{/crossLink}}
    • + *
    • {{#crossLink "ModalFactory"}}ModalFactory{{/crossLink}}
    • + *
    + * @class BasketService + * @static + */ + pm.service( 'BasketService', function( API, UI, CMS, Checkout, Modal ) + { + + return { + addItem : addBasketItem, + removeItem : removeBasketItem, + getItem : getBasketItem, + setItemQuantity : setItemQuantity, + editItemAttributes: editItemAttributes, + editOrderParams : editOrderParams, + addCoupon : addCoupon, + removeCoupon : removeCoupon + }; + + /** + * Add item to basket. Will fail and show a popup if item has order params + * @function addBasketItem + * @param {Array} article Array containing the item to add + * @param {boolean} [isUpdate=false] Indicating if item's OrderParams are updated + * @return {object} jQuery deferred + * Object + */ + function addBasketItem( article ) + { + + if ( !!article ) + { + + API.get( '/rest/checkout/container_' + 'CheckoutOrderParamsList'.toLowerCase() + '/', + { + itemID : article[0].BasketItemItemID, + quantity: article[0].BasketItemQuantity + }, false, true ).done( function( resp ) + { + // checking for order params! + if ( resp.data[0].indexOf( "form-group" ) > 0 ) + { + Modal.prepare() + .setContent( resp.data[0] ) + .setTitle( pm.translate( "Select order parameters" ) ) + .setLabelConfirm( pm.translate( "Save" ) ) + .onConfirm( function() + { + // validate form + if ( $('[data-plenty-checkout-form="OrderParamsForm"]').validateForm() ) + { + // save order params + addArticle( saveOrderParams( article ) ); + + // close modal after saving order params + return true; + } + else + { + return false; + } + } ) + .show(); + } + else + { + addArticle( article ); + } + } ); + } + } + + /** + * Read OrderParams from <form> marked with data-plenty-checkout-form="OrderParamsForm" and inject + * read values in 'addBasketList'. Update item by calling addBasketItem() again + * @function saveOrderParams + * @private + * @param {Array} articleWithParams Containing the current item to add. Read OrderParams will be injected + */ + function saveOrderParams( articleWithParams ) + { + //TODO use $("[data-plenty-checkout-form='OrderParamsForm']").serializeArray() to get order params + var orderParamsForm = $( '[data-plenty-checkout-form="OrderParamsForm"]' ); + var $self = {}; + var attrType = ""; + var match; + + //Groups + orderParamsForm.find( '[name^="ParamGroup"]' ).each( function() + { + match = this.name.match( /^ParamGroup\[(\d+)]\[(\d+)]$/ ); + articleWithParams = addOrderParamValue( articleWithParams, match[1], $( this ).val(), $( this ).val() ); + } ); + + //Values + orderParamsForm.find( '[name^="ParamValue"]' ).each( function() + { + $self = $( this ); + attrType = $self.attr( 'type' ); + + if ( ((attrType == 'checkbox' && $self.is( ':checked' )) || + (attrType == 'radio' && $self.is( ':checked' )) || + (attrType != 'radio' && attrType != 'checkbox')) && attrType != 'file' && attrType != 'hidden' ) + { + + var match = $self[0].name.match( /^ParamValue\[(\d+)]\[(\d+)]$/ ); + articleWithParams = addOrderParamValue( articleWithParams, match[1], match[2], $self.val() ); + + } + else if ( attrType == 'file' ) + { + if( $self[0].files && $self[0].files.length > 0 ) + { + articleWithParams = orderParamFileUpload( $self, articleWithParams ); + } + else + { + var match = $self[0].name.match( /^ParamValueFile\[(\d+)]\[(\d+)]$/ ); + var paramValue = $( 'input[type="hidden"][name="ParamValue[' + match[1] + '][' + match[2] + ']"]' ).val(); + articleWithParams = addOrderParamValue( articleWithParams, match[1], match[2], paramValue ); + } + } + } ); + + return articleWithParams; + } + + function addArticle( article ) + { + API.post( '/rest/checkout/basketitemslist/', article, true ) + .done( function() + { + // Item has no OrderParams -> Refresh Checkout & BasketPreview + Checkout.loadCheckout() + .done( function() + { + refreshBasketPreview(); + // Show confirmation popup + CMS.getContainer( 'ItemViewItemToBasketConfirmationOverlay', {ArticleID: article[0].BasketItemItemID} ).from( 'ItemView' ) + .done( function( response ) + { + Modal.prepare() + .setContent( response.data[0] ) + .setTimeout( 5000 ) + .show(); + } ); + } ); + } ).fail( function( jqXHR ) + { + // some other error occured + UI.printErrors( JSON.parse( jqXHR.responseText ).error.error_stack ); + } ); + } + + function updateArticle( article ) + { + API.put( '/rest/checkout/basketitemslist/', article ) + .done( function() + { + // Item has no OrderParams -> Refresh Checkout & BasketPreview + Checkout.reloadCatContent( pm.getGlobal( 'basketCatID' ) ); + Checkout.loadCheckout() + .done( function() + { + refreshBasketPreview(); + } ); + } ) + } + + function orderParamFileUpload( $input, articleWithParams ) + { + var key = $input[0].id; + var orderParamUploadFiles = {}; + var orderParamFileIdStack = []; + var formData; + var fileData; + var params = { + type : 'POST', + data : {}, + isFile : true, + cache : false, + dataType : 'json', + processData: false, + contentType: false + }; + + orderParamUploadFiles[key] = $input[0].files; + + // if input not pushed before. + if ( orderParamFileIdStack.indexOf( key ) == -1 ) + { + orderParamFileIdStack.push( key ); + } + + for ( var i = 0, length = orderParamFileIdStack.length; i < length; ++i ) + { + formData = new FormData(); + fileData = orderParamUploadFiles[orderParamFileIdStack[i]]; + formData.append( "0", fileData[0], fileData[0].name ); + + params.data = formData; + + API.post( "/rest/checkout/orderparamfile/", params ); + } + + var match = $input[0].name.match( /^ParamValueFile\[(\d+)]\[(\d+)]$/ ); + + return addOrderParamValue( articleWithParams, match[1], match[2], $input.val() ); + } + + /** + * Inject an OrderParam. + * @function addOrderParamValue + * @private + * @param {Array} basketList The target to inject the value in. + * @param {number} position Position where to inject the value + * @param {number} paramId The ID of the OrderParam to inject + * @param {string|number} paramValue the value of the OrderParam to inject + * @returns {Array} Containing the item and the injected OrderParam + */ + function addOrderParamValue( basketList, position, paramId, paramValue ) + { + if ( position > 0 && basketList[position] == undefined ) + { + basketList[position] = $.extend( true, {}, basketList[0] ); + basketList[position].BasketItemOrderParamsList = []; + } + + if ( basketList[position] != undefined ) + { + basketList[position].BasketItemQuantity = 1; + if ( basketList[position].BasketItemOrderParamsList == undefined ) + { + basketList[position].BasketItemOrderParamsList = []; + } + if ( paramValue ) + { + basketList[position].BasketItemOrderParamsList.push( { + BasketItemOrderParamID : paramId, + BasketItemOrderParamValue: paramValue + } ); + } + } + + return basketList; + } + + function editItemAttributes( BasketItemID ) + { + var modal = $( '[data-plenty-basket-item="' + BasketItemID + '"' ); + modal.modal( 'show' ); + modal.find( '[data-plenty-modal="confirm"]' ).on( 'click', function() + { + var basketItem = getBasketItem( BasketItemID ); + var attributesList = []; + + // check for select or list of images + modal.find( 'select, .PlentyFormContainer.AttrImage > input[type="hidden"]' ).each( function( i, attributeSelect ) + { + var match = attributeSelect.name.match( /^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/ ); + if ( match && match[1] ) + { + attributesList.push( { + BasketItemAttributeID : match[1], + BasketItemAttributeValueID: $( attributeSelect ).val() + } ); + } + + } ); + + if ( attributesList.length != 0 ) + { + basketItem.BasketItemAttributesList = attributesList; + } + //update basketItem and refresh previewLists + updateArticle( [basketItem] ); + + } ); + } + + function editOrderParams( BasketItemID ) + { + + var basketItem = getBasketItem( BasketItemID ); + // FIX: unset old order params + + basketItem.BasketItemOrderParamsList = []; + + API.get( '/rest/checkout/container_' + 'CheckoutOrderParamsList'.toLowerCase() + '/', { + itemID : basketItem.BasketItemItemID, + quantity : basketItem.BasketItemQuantity, + basketItemID: BasketItemID + } ).done( function( resp ) + { + // checking for order params! + Modal.prepare() + .setContent( resp.data[0] ) + .setTitle( pm.translate( "Edit order parameters" ) ) + .setLabelConfirm( pm.translate( "Save" ) ) + .onConfirm( function() + { + // validate form + if ( $('[data-plenty-checkout-form="OrderParamsForm"]').validateForm() ) + { + // save order params + updateArticle( saveOrderParams( [basketItem] ) ); + + // close modal after saving order params + return true; + } + else + { + return false; + } + } ) + .show(); + } ); + } + + function getBasketItem( BasketItemID ) + { + var basketItems = Checkout.getCheckout().BasketItemsList; + for ( var i = 0; i < basketItems.length; i++ ) + { + if ( basketItems[i].BasketItemID == BasketItemID ) + { + return basketItems[i]; + } + } + + return null; + } + + /** + * Remove item from basket. Will show a confirmation popup at first. + * @function removeBasketItem + * @param {number} BasketItemID The ID of the basket item to remove + * @param {boolean} [forceDelete=false] Set true to remove the basket item without showing a confirmation popup + * @return Promise + */ + function removeBasketItem( BasketItemID, forceDelete ) + { + + var deferred = $.Deferred(); + + // get item name + var itemName = getBasketItem( BasketItemID ).BasketItemNameMap[1]; + + // calling the delete request + function doDelete() + { + API.delete( '/rest/checkout/basketitemslist/?basketItemIdsList[0]=' + BasketItemID ) + .done( function() + { + Checkout.loadCheckout().done( function() + { + $( '[data-basket-item-id="' + BasketItemID + '"]' ).remove(); + + if ( !Checkout.getCheckout().BasketItemsList || Checkout.getCheckout().BasketItemsList.length <= 0 ) + { + Checkout.reloadCatContent( pm.getGlobal( 'basketCatID' ) ); + } + else + { + Checkout.reloadContainer( 'Totals' ); + } + + refreshBasketPreview(); + + deferred.resolve(); + } ); + } ); + } + + if ( !forceDelete ) + { + // show confirmation popup + Modal.prepare() + .setTitle( pm.translate( 'Please confirm' ) ) + .setContent( '

    ' + pm.translate( "Do you really want to remove \"{{item}}\" from your basket?", {item: itemName} ) + '

    ' ) + .onDismiss( function() + { + //$('[data-basket-item-id="' + BasketItemID + + // '"]').find('[data-plenty="quantityInput"]').val(originalItemQuantity); + deferred.reject(); + } ) + .onConfirm( function() + { + doDelete(); + } ) + .setLabelConfirm( pm.translate( "Delete" ) ) + .show(); + } + else + { + doDelete(); + } + + return deferred; + } + + /** + * Set a new quantity for the given BasketItem. If quantity is set to 0, + * remove the item. + * @function setItemQuantity + * @param {number} BasketItemID The ID of the basket item to change the quantity of + * @param {number} BasketItemQuantity The new quantity to set or 0 to remove the item + */ + function setItemQuantity( BasketItemID, BasketItemQuantity ) + { + // delete item if quantity is 0 + if ( BasketItemQuantity <= 0 ) + { + return removeBasketItem( BasketItemID ); + } + + var deferred = $.Deferred(); + var params = Checkout.getCheckout().BasketItemsList; + var basketItem; + var basketItemIndex; + + for ( var i = 0; i < params.length; i++ ) + { + if ( params[i].BasketItemID == BasketItemID ) + { + basketItemIndex = i; + basketItem = params[i]; + break; + + } + } + + if ( !!basketItem && basketItem.BasketItemQuantity != BasketItemQuantity ) + { + params[basketItemIndex].BasketItemQuantity = parseInt( BasketItemQuantity ); + + API.post( "/rest/checkout/basketitemslist/", params ) + .done( function() + { + Checkout.setCheckout().done( function() + { + Checkout.reloadCatContent( pm.getGlobal( 'basketCatID' ) ); + refreshBasketPreview(); + deferred.resolve(); + } ); + } ); + } + + return deferred; + } + + /** + * Reload BasketPreview-Template and update basket totals + * @function refreshBasketPreview + * @private + */ + function refreshBasketPreview() + { + + Checkout.reloadItemContainer( 'BasketPreviewList' ) + .done( function() + { + + $( '[data-plenty-basket-empty]' ).each( function( i, elem ) + { + var toggleClass = $( elem ).attr( 'data-plenty-basket-empty' ); + if ( Checkout.getCheckout().BasketItemsList.length <= 0 ) + { + $( elem ).addClass( toggleClass ); + } + else + { + $( elem ).removeClass( toggleClass ); + } + } ); + + } ); + + //update quantity + var itemQuantityTotal = 0; + $.each( Checkout.getCheckout().BasketItemsList, function( i, basketItem ) + { + itemQuantityTotal += basketItem.BasketItemQuantity; + } ); + + $( '[data-plenty-basket-preview="itemQuantityTotal"]' ).text( itemQuantityTotal ); + $( '[data-plenty-basket-preview="totalsItemSum"]' ).text( Checkout.getCheckout().Totals.TotalsItemSum ); + } + + /** + * Read the coupon code from an <input> element marked with data-plenty-checkout-form="couponCode" + * and try to add this coupon. + * @function addCoupon + * @return {object} jQuery deferred + * Object + */ + function addCoupon() + { + var params = { + CouponActiveCouponCode: $( '[data-plenty-checkout-form="couponCode"]' ).val() + }; + + return API.post( "/rest/checkout/coupon/", params ) + .done( function() + { + Checkout.setCheckout() + .done( function() + { + + updateContainer(); + } ); + } ); + } + + /** + * Remove the currently added coupon + * @function removeCoupon + * @return {object} jQuery deferred + * Object + */ + function removeCoupon() + { + var params = { + CouponActiveCouponCode: Checkout.getCheckout().Coupon.CouponActiveCouponCode + }; + + return API.delete( "/rest/checkout/coupon/", params ) + .done( function() + { + Checkout.setCheckout() + .done( function() + { + delete Checkout.getCheckout().Coupon; + + updateContainer(); + } ); + } ); + } + + // update container + function updateContainer() + { + Checkout.reloadContainer( 'Coupon' ); + // reload totals, if we are at basket + if ( $( '[data-plenty-checkout-template="Totals"]' ).length > 0 ) + { + Checkout.reloadContainer( 'Totals' ); + } + } + + }, ['APIFactory', 'UIFactory', 'CMSFactory', 'CheckoutFactory', 'ModalFactory'] ); +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Services + */ +(function( $, pm ) +{ + + /** + * Providing methods for checkout process like setting shipping & payment information and placing the order.
    + * Requires: + *
      + *
    • {{#crossLink "APIFactory"}}APIFactory{{/crossLink}}
    • + *
    • {{#crossLink "CMSFactory"}}CMSFactory{{/crossLink}}
    • + *
    • {{#crossLink "CheckoutFactory"}}CheckoutFactory{{/crossLink}}
    • + *
    • {{#crossLink "ModalFactory"}}ModalFactory{{/crossLink}}
    • + *
    + * @class CheckoutService + * @static + */ + pm.service( 'CheckoutService', function( API, CMS, Checkout, Modal ) + { + + return { + init : init, + setCustomerSignAndInfo: setCustomerSignAndInfo, + registerGuest : registerGuest, + setShippingProfile : setShippingProfile, + saveShippingAddress : saveShippingAddress, + loadAddressSuggestion : loadAddressSuggestion, + preparePayment : preparePayment, + setMethodOfPayment : setMethodOfPayment, + editBankDetails : editBankDetails, + editCreditCard : editCreditCard, + placeOrder : placeOrder + }; + + /** + * Load checkout data initially on page load + * @function init + */ + function init() + { + Checkout.loadCheckout( true ); + } + + /** + * Read customer sign and order information text from <form> marked with + * data-plenty-checkout-form="details" and update checkout. + * @function setCustomerSignAndInfo + * @return {object} jQuery deferred + * Object + */ + function setCustomerSignAndInfo() + { + var form = $( '[data-plenty-checkout-form="details"]' ); + var values = form.getFormValues(); + + // initialize CustomerSign & InfoText to avoid updating empty values + if ( !Checkout.getCheckout().CheckoutCustomerSign ) + { + Checkout.getCheckout().CheckoutCustomerSign = ""; + } + if ( !Checkout.getCheckout().CheckoutOrderInfoText ) + { + Checkout.getCheckout().CheckoutOrderInfoText = ""; + } + + if ( ( Checkout.getCheckout().CheckoutCustomerSign !== values.CustomerSign && $( form ).find( '[name="CustomerSign"]' ).length > 0 ) + || ( Checkout.getCheckout().CheckoutOrderInfoText !== values.OrderInfoText && $( form ).find( '[name="OrderInfoText"]' ).length > 0 ) ) + { + + Checkout.getCheckout().CheckoutCustomerSign = values.CustomerSign; + Checkout.getCheckout().CheckoutOrderInfoText = values.OrderInfoText; + + return Checkout.setCheckout(); + + } + else + { + // No changes detected -> Do nothing + return API.idle(); + } + } + + /** + * Read address data from <form> marked with data-plenty-checkout-form="shippingAddress". + * Create new shipping address or update the shipping address ID. + * @function saveShippingAddress + * @param {boolean} [validateForm = false] validate form before processing requests + * @return {object} jQuery deferred + * Object + */ + function saveShippingAddress( validateForm ) + { + var form = $( '[data-plenty-checkout-form="shippingAddress"]' ); + + if ( !validateForm && !form.validateForm() ) + { + return false; + } + + if ( !validateForm && !pm.getInstance().AddressDoctorService.validateAddress( form ) ) + { + return false; + } + + var values = form.getFormValues(); + var shippingAddressID = $( '[name="shippingAddressID"]:checked' ).val(); + + // TODO: move bootstrap specific function + $( '#shippingAdressSelect' ).modal( 'hide' ); + + if ( shippingAddressID < 0 ) + { + // save separate + var shippingAddress = values; + + if ( !addressesAreEqual( shippingAddress, Checkout.getCheckout().CustomerShippingAddress ) ) + { + if ( shippingAddress.Street == "PACKSTATION" ) + { + shippingAddress.isPackstation = 1; + shippingAddress.PackstationNo = shippingAddress.HouseNo; + } + else if ( shippingAddress.Street == "POSTFILIALE" ) + { + shippingAddress.IsPostfiliale = 1; + shippingAddress.PostfilialNo = shippingAddress.HouseNo; + } + + // new shipping address + return API.post( "/rest/checkout/customershippingaddress/", shippingAddress ) + .done( function( response ) + { + + Checkout.getCheckout().CheckoutCustomerShippingAddressID = response.data.ID; + Checkout.getCheckout().CheckoutShippingCountryID = response.data.CountryID; + delete Checkout.getCheckout().CheckoutMethodOfPaymentID; + delete Checkout.getCheckout().CheckoutShippingProfileID; + + Checkout.setCheckout().done( function() + { + Checkout.reloadContainer("MethodsOfPaymentList"); + Checkout.reloadContainer("ShippingProfilesList"); + if ( Checkout.getCheckout().CustomerInvoiceAddress.LoginType == 2 ) + { + Checkout.reloadContainer( 'CustomerShippingAddress' ); + } + } ); + } ); + } + else + { + // no changes detected + return API.idle(); + } + + } + else + { + if ( shippingAddressID != Checkout.getCheckout().CheckoutCustomerShippingAddressID ) + { + // change shipping address id + Checkout.getCheckout().CheckoutCustomerShippingAddressID = shippingAddressID; + delete Checkout.getCheckout().CheckoutMethodOfPaymentID; + delete Checkout.getCheckout().CheckoutShippingProfileID; + + return Checkout.setCheckout().done( function() + { + Checkout.reloadContainer("MethodsOfPaymentList"); + Checkout.reloadContainer("ShippingProfilesList"); + if ( Checkout.getCheckout().CustomerInvoiceAddress.LoginType == 2 ) + { + Checkout.reloadContainer( 'CustomerShippingAddress' ); + } + } ); + } + else + { + return API.idle(); + } + } + } + + /** + * Prepare address-data to register a guest. Reads the address-data from a <form> marked with + * data-plenty-checkout-form="guestRegistration" + * @function registerGuest + * @return {object} jQuery deferred + * Object + */ + function registerGuest() + { + var form = $( '[data-plenty-checkout-form="guestRegistration"]' ); + + var invoiceAddress = form.getFormValues(); + invoiceAddress.LoginType = 1; + + invoiceAddress.CustomerPropertiesList = invoiceAddress.CustomerPropertiesList || []; + + form.find( "[data-plenty-property-id]" ).each( function( i, propertyInput ) + { + invoiceAddress.CustomerPropertiesList.push( { + PropertyID : $( propertyInput ).attr( 'data-plenty-property-id' ), + PropertyValue: $( propertyInput ).val() + } ); + } ); + + if ( !addressesAreEqual( invoiceAddress, Checkout.getCheckout().CustomerInvoiceAddress ) ) + { + return API.post( "/rest/checkout/customerinvoiceaddress/", invoiceAddress ) + .done( function( response ) + { + //Checkout.getCheckout().CheckoutShippingCountryID = response.data.CountryID; + saveShippingAddress().done( function() + { + Checkout.loadCheckout(); + //Checkout.getCheckout().CustomerInvoiceAddress = response.data; + } ); + } ); + } + else + { + return saveShippingAddress(); + } + } + + /** + * Check if values of addresses are equal + * @function addressesAreEqual + * @private + * @param {object} address1 + * @param {object} address2 + * @returns {boolean} + */ + function addressesAreEqual( address1, address2 ) + { + for ( var key in address1 ) + { + if ( address1[key] + '' !== address2[key] + '' && key !== 'EmailRepeat' ) + { + return false; + } + } + return true; + } + + /** + * Set the shipping profile used for this order and update checkout. Selected shipping profile will be + * read from <form> marked with data-plenty-checkout-form="shippingProfileSelect" + * @function setShippingProfile + * @return {object} jQuery deferred + * Object + */ + function setShippingProfile() + { + + var values = $( '[data-plenty-checkout-form="shippingProfileSelect"]' ).getFormValues(); + + Checkout.getCheckout().CheckoutShippingProfileID = values.ShippingProfileID; + delete Checkout.getCheckout().CheckoutCustomerShippingAddressID; + delete Checkout.getCheckout().CheckoutMethodOfPaymentID; + + return Checkout.setCheckout() + .done( function() + { + Checkout.reloadContainer( 'MethodsOfPaymentList' ); + } ); + + } + + /** + * Prepare method of payment to check if external checkout is used or addition content should be displayed + * @function preparePayment + * @return {object} jQuery deferred + * Object + */ + function preparePayment() + { + return API.post( "/rest/checkout/preparepayment/", null ) + .done( function( response ) + { + if ( response.data.CheckoutMethodOfPaymentRedirectURL != '' ) + { + + document.location.assign( response.data.CheckoutMethodOfPaymentRedirectURL ); + + } + else if ( !!response.data.CheckoutMethodOfPaymentAdditionalContent ) + { + + var isBankDetails = $( response.data.CheckoutMethodOfPaymentAdditionalContent ).find( '[data-plenty-checkout-form="bankDetails"]' ).length > 0; + Modal.prepare() + .setContent( response.data.CheckoutMethodOfPaymentAdditionalContent ) + .onConfirm( function() + { + if ( isBankDetails ) + { + return saveBankDetails(); + } + else + { + return saveCreditCard(); + } + } ) + .show(); + } + } ); + + } + + /** + * Set the method of payment used for this order. + * @function setMethodOfPayment + * @param {number|undefined} paymentID ID of the method of payment to use. Read from <form> marked with + * data-plenty-checkout-form="methodOfPayment" if unset. + * @return {object} jQuery deferred + * Object + */ + function setMethodOfPayment( paymentID ) + { + + paymentID = paymentID || $( '[data-plenty-checkout-form="methodOfPayment"]' ).getFormValues().MethodOfPaymentID; + + Checkout.getCheckout().CheckoutMethodOfPaymentID = paymentID; + delete Checkout.getCheckout().CheckoutCustomerShippingAddressID; + delete Checkout.getCheckout().CheckoutShippingProfileID; + + return Checkout.setCheckout() + .done( function() + { + Checkout.reloadContainer( 'ShippingProfilesList' ); + } ); + } + + /** + * Display the popup to enter or edit customers bank details + * @function editBankDetails + */ + function editBankDetails() + { + + CMS.getContainer( 'CheckoutPaymentInformationBankDetails' ).from( 'Checkout' ) + .done( function( response ) + { + Modal.prepare() + .setContent( response.data[0] ) + .onDismiss( function() + { + $( 'input[name="MethodOfPaymentID"]' ).each( function( i, radio ) + { + if ( $( radio ).val() == Checkout.getCheckout().CheckoutMethodOfPaymentID ) + { + $( radio ).attr( 'checked', 'checked' ); + } + else + { + $( radio ).removeAttr( 'checked' ); + } + } ); + } ).onConfirm( function() + { + return saveBankDetails(); + } ) + .show(); + } ); + + } + + /** + * Read entered bank details from data-plenty-checkout-form="bankDetails" and update checkout. + * @function saveBankDetails + * @private + * @return {boolean} the result of form validation + */ + function saveBankDetails() + { + var form = $( '[data-plenty-checkout-form="bankDetails"]' ); + + if ( form.validateForm() ) + { + var values = form.getFormValues().checkout.customerBankDetails; + + var bankDetails = { + CustomerBankName : values.bankName, + CustomerBLZ : values.blz, + CustomerAccountNumber: values.accountNo, + CustomerAccountOwner : values.accountOwner, + CustomerIBAN : values.iban, + CustomerBIC : values.bic + }; + + API.post( "/rest/checkout/paymentinformationbankdetails/", bankDetails ) + .done( function() + { + Checkout.loadCheckout().done( function() + { + setMethodOfPayment( 3 ); + Checkout.reloadContainer( 'MethodsOfPaymentList' ); + } ); + } ); + return true; + } + else + { + return false; + } + } + + /** + * Display a popup containing credit card form + * @function editCreditCard + */ + function editCreditCard() + { + + CMS.getContainer( 'CheckoutPaymentInformationCreditCard' ).from( 'Checkout' ) + .done( function( response ) + { + Modal.prepare() + .setContent( response.data[0] ) + .onDismiss( function() + { + $( 'input[name="MethodOfPaymentID"]' ).each( function( i, radio ) + { + if ( $( radio ).val() == Checkout.getCheckout().CheckoutMethodOfPaymentID ) + { + $( radio ).attr( 'checked', 'checked' ); + } + else + { + $( radio ).removeAttr( 'checked' ); + } + } ); + } ).onConfirm( function() + { + return saveCreditCard(); + } ) + .show(); + } ); + } + + /** + * Read values from <form> marked with data-plenty-checkout-form="creditCard" and update checkout. + * @function saveCreditCard + * @private + * @return {boolean} the result of form validation + */ + function saveCreditCard() + { + var form = $( '[data-plenty-checkout-form="creditCard"]' ); + + if ( form.validateForm() ) + { + + var values = form.getFormValues().checkout.paymentInformationCC; + + var creditCard = { + Owner : values.owner, + Cvv2 : values.cvv2, + Number : values.number, + Year : values.year, + Month : values.month, + Provider: values.provider + }; + + API.post( '/rest/checkout/paymentinformationcreditcard/', creditCard ) + .done( function() + { + Checkout.loadCheckout(); + } ); + return true; + } + else + { + return false; + } + } + + /** + * Display a popup containing address suggestions + * @param {string} type + */ + function loadAddressSuggestion( type ) + { + + //check login type + if ( Checkout.getCheckout().CustomerInvoiceAddress.LoginType == 2 ) + { + var values = $( '[data-plenty-checkout-form="shippingAddress"]' ).getFormValues(); + } + else + { + var values = $( '[data-plenty-checkout-form="guestRegistration"]' ).getFormValues(); + } + + var params = { + street : values.Street, + houseNo : values.HouseNo, + ZIP : values.ZIP, + city : values.City, + postnummer : values.Postnummer, + suggestionType: 'postfinder' + }; + + CMS.getContainer( 'CheckoutAddressSuggestionResultsList', params ).from( 'Checkout' ) + .done( function( response ) + { + Modal.prepare() + .setContent( response.data[0] ) + .show(); + } ); + } + + /** + * Place the order prepared before and finish the checkout process.
    + * Validate required checkboxes in data-plenty-checkout-form="placeOrder" + * @function placeOrder + * @return {object} jQuery deferred + * Object + */ + function placeOrder() + { + var form = $( '[data-plenty-checkout-form="placeOrder"]' ); + if ( form.validateForm() ) + { + + var values = form.getFormValues(); + + // if not shown in layout set default 1 for mandatory fields + var params = { + TermsAndConditionsCheck : values.termsAndConditionsCheck || 0, + WithdrawalCheck : values.withdrawalCheck || 0, + PrivacyPolicyCheck : values.privacyPolicyCheck || 0, + AgeRestrictionCheck : values.ageRestrictionCheck || 0, + NewsletterCheck : values.newsletterCheck || 0, + KlarnaTermsAndConditionsCheck: values.klarnaTermsAndConditionsCheck || 0, + PayoneDirectDebitMandateCheck: values.payoneDirectDebitMandateCheck || 0, + PayoneInvoiceCheck : values.payoneInvoiceCheck || 0 + }; + + return API.post( "/rest/checkout/placeorder/", params ) + .done( function( response ) + { + if ( response.data.MethodOfPaymentRedirectURL != '' ) + { + + window.location.assign( response.data.MethodOfPaymentRedirectURL ); + + } + else if ( response.data.MethodOfPaymentAdditionalContent != '' ) + { + + Modal.prepare() + .setContent( response.data.MethodOfPaymentAdditionalContent ) + .setLabelDismiss( '' ) + .onDismiss( function() + { + window.location.assign( form.attr( 'action' ) ); + } ).onConfirm( function() + { + window.location.assign( form.attr( 'action' ) ); + } ).show(); + + } + else + { + + window.location.assign( form.attr( 'action' ) ); + + } + } ); + } + } + + }, ['APIFactory', 'CMSFactory', 'CheckoutFactory', 'ModalFactory'] ); +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Services + */ +(function( $, pm ) +{ + + /** + * Listens to window's size and trigger 'sizeChange' event if the Bootstrap interval changes. + * @class MediaSizeService + * @static + * @example + * $(window).on('sizeChange', function(newValue, oldValue) { + * console.log('The interval changed from ' + oldValue + ' to ' + newValue.'); + * }); + */ + pm.service( 'MediaSizeService', function() + { + + var bsInterval; + + // recalculation of the current interval on window resize + $( window ).resize( calculateMediaSize ); + + // initially calculation of the interval + $( document ).ready( calculateMediaSize ); + + return { + interval : getInterval, + isInterval: isInterval + }; + + /** + * Get the currently used Bootstrap interval + * @function getInterval + * @return {"xs"|"sm"|"md"|"lg"} + */ + function getInterval() + { + if ( !!bsInterval ) + { + calculateMediaSize(); + } + + return bsInterval; + } + + /** + * Calculate the currently used Bootstrap interval + * @function calculateMediaSize + * @private + */ + function calculateMediaSize() + { + var size; + if ( !!window.matchMedia ) + { // FIX IE support + if ( window.matchMedia( '(min-width:1200px)' ).matches ) + { + size = 'lg'; + } + else if ( window.matchMedia( '(min-width:992px)' ).matches ) + { + size = 'md'; + } + else if ( window.matchMedia( '(min-width:768px)' ).matches ) + { + size = 'sm'; + } + else + { + size = 'xs'; + } + } + else + { + if ( $( window ).width() >= 1200 ) + { + size = 'lg'; + } + else if ( $( window ).width() >= 992 ) + { + size = 'md'; + } + else if ( $( window ).width() >= 768 ) + { + size = 'sm'; + } + else + { + size = 'xs'; + } + } + if ( size != bsInterval ) + { + var oldValue = bsInterval; + bsInterval = size; + $( window ).trigger( 'sizeChange', [bsInterval, oldValue] ); + } + } + + function isInterval( interval ) + { + var intervalList = interval.replace( /\s/g, '' ).split( ',' ); + for ( var i = 0; i < intervalList.length; i++ ) + { + if ( intervalList[i] == bsInterval ) + { + return true; + } + } + return false; + } + + } ); + +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Services + */ +(function( $, pm ) +{ + + /** + * Handling navigation while checkout processes + * @class NavigatorService + * @static + * + */ + pm.service( 'NavigatorService', function( CMS, Checkout ) + { + var navigation = []; // contains navigation list elements + var container = []; // content containers + var current = -1; // index of currently shown content container + var buttonPrev = {}; // navigation buttons + var buttonNext = {}; + var interceptors = { + beforeChange: [], + afterChange : [] + }; + var checkoutStates = []; + + return { + init : init, + getCurrentContainer: getCurrentContainer, + goTo : goTo, + beforeChange : beforeChange, + afterChange : afterChange, + continueChange : continueChange, + next : next, + previous : previous, + goToID : goToID, + fillNavigation : fillNavigation + }; + + /** + * Initialize checkout navigation. Shows first container. + * @function init + * @example + * ```html + * + *
      + *
    • Checkout Step 1
    • + *
    • Checkout Step 2
    • + *
    • ...
    • + *
    + * + * + *
    + *
    + * Checkout Step 1 Content + *
    + *
    + * Checkout Step 2 Content + *
    + *
    ...
    + *
    + * ``` + */ + function init() + { + + // get elements from DOM + navigation = $( '[data-plenty-checkout="navigation"] > li' ); + container = $( '[data-plenty-checkout="container"] > div' ); + buttonNext = $( '[data-plenty-checkout="next"]' ); + buttonPrev = $( '[data-plenty-checkout="prev"]' ); + + if ( navigation.length == container.length && container.length > 0 ) + { + var checkout = Checkout.getCheckout(); + + container.hide(); + + // initialize navigation + navigation.each( function( i, elem ) + { + $( elem ).addClass( 'disabled' ); + // handle navigation click events + $( elem ).click( function() + { + if ( !$( this ).is( '.disabled' ) ) + { + goTo( i ); + } + } ); + } ); + + buttonNext.attr( "disabled", "disabled" ); + buttonNext.click( function() + { + next(); + } ); + + buttonPrev.attr( "disabled", "disabled" ); + buttonPrev.click( function() + { + previous(); + } ); + + window.addEventListener( 'hashchange', function() + { + if ( window.location.hash.length > 0 ) + { + goToID( window.location.hash ); + } + else + { + goTo( 0 ); + } + }, false ); + + // initialize GUI + // check url param for jumping to tab + $.urlParam = function( name ) + { + var results = new RegExp( '[\?&]' + name + '=([^&#]*)' ).exec( window.location.href ); + if ( results == null ) + { + return null; + } + else + { + return results[1] || 0; + } + }; + + var param = $.urlParam( 'gototab' ); + // jump to hash from url param 'gototab' + if ( window.location.hash.length == 0 && !!param && $( '[data-plenty-checkout-id="' + param + '"]' ).length > 0 ) + { + window.location.hash = param; + } + // jump to hash + else if ( !goToID( window.location.hash ) && current >= 0 ) + { + goTo( current ); + } + else + { + goTo( 0 ); + } + + fillNavigation(); + $( window ).on( 'sizeChange', fillNavigation ); + $( window ).resize( function() + { + if ( pm.getInstance().MediaSizeService.interval() == 'xs' ) + { + fillNavigation(); + } + } ); + + } + } + + /** + * Get the currently active checkout container. + * @function getCurrentContainer + * @return {{id: string, index: number}} + */ + function getCurrentContainer() + { + if ( current >= 0 ) + { + return { + id : $( container[current] ).attr( 'data-plenty-checkout-id' ), + index: current + }; + } + else + { + return null; + } + } + + /** + * Register an interceptor called before each tab change. + * Tabchange will break if any interceptor returns false. + * @param {function} interceptor The interceptor callback to register + * @chainable + * @returns {NavigatorService} + * @example + * plenty.NavigatorService.beforeChange( function(targetContainer) { + * if( targetContainer.id === 'details' ) { + * // stop tabchange if user tries to access checkout container with id "details" + * return false; + * } + * return true; + * }); + */ + function beforeChange( interceptor ) + { + interceptors.beforeChange.push( interceptor ); + return pm.getInstance().NavigatorService; + } + + /** + * Register an interceptor called after each tab change. + * @param {function} interceptor The interceptor callback to register + * @chainable + * @returns {NavigatorService} + */ + function afterChange( interceptor ) + { + interceptors.afterChange.push( interceptor ); + return pm.getInstance().NavigatorService; + } + + /** + * Call registered interceptors. Break if any interceptor returns false. + * Do not call beforeChange-interceptors on initially tabchange + * @function resolveInterceptors + * @private + * @param {"beforeChange"|"afterChange"} identifier Describe which interceptors should be called + * @param {number} index the index of the target container + * @returns {boolean} Conjunction of all interceptor return values + */ + function resolveInterceptors( identifier, index ) + { + var continueTabChange = true; + + if ( current >= 0 || identifier === 'afterChange' ) + { + + var currentContainer = getCurrentContainer(); + var targetContainer = { + index: index, + id : $( container[index] ).attr( 'data-plenty-checkout-id' ) + }; + + $.each( interceptors[identifier], function( i, interceptor ) + { + if ( interceptor( currentContainer, targetContainer ) === false ) + { + continueTabChange = false; + return false; + } + } ); + } + + return continueTabChange; + } + + /** + * Show checkout tab given by index + * @function goTo + * @param {number} index Index of target tab, starting at 0 + * @param {boolean} [ignoreInterceptors=false] Set true to not call registered interceptors and force changing + * tab + */ + function goTo( index, ignoreInterceptors ) + { + + var contentChanged = current !== index; + + if ( contentChanged && !ignoreInterceptors ) + { + if ( !resolveInterceptors( "beforeChange", index ) ) + { + return; + } + } + + current = index; + + if ( !Object.equals( checkoutStates[current], Checkout.getCheckout( true ) ) && contentChanged && !!$( container[current] ).attr( 'data-plenty-checkout-content' ) ) + { + checkoutStates[current] = Checkout.getCheckout( true ); + // reload tab content + CMS.getCategoryContent( $( container[current] ).attr( 'data-plenty-checkout-content' ) ) + .done( function( response ) + { + $( container[current] ).html( response.data[0] ); + // continue tab change + proceedTabChange( contentChanged ); + pm.getInstance().bindDirectives( container[current] ); + $( window ).trigger( 'contentChanged' ); + } ); + } + else + { + // continue tab change without reloading tab content + proceedTabChange( contentChanged ); + //pm.getInstance().bindDirectives(); + } + + } + + function proceedTabChange( contentChanged ) + { + + // hide content containers + $( container ).hide(); + + // refresh navigation elements + $( navigation ).each( function( i, elem ) + { + $( elem ).removeClass( 'disabled active' ); + + $( elem ).find( '[role="tab"]' ).attr( 'aria-selected', 'false' ); + + if ( i < current ) + { + // set current element as active + $( elem ).addClass( 'visited' ); + } + else + { + if ( i == current ) + { + $( elem ).addClass( 'active visited' ); + $( elem ).find( '[role="tab"]' ).attr( 'aria-selected', 'true' ); + } + else + { + if ( i > current && !$( elem ).is( '.visited' ) ) + { + // disable elements behind active + $( elem ).addClass( 'disabled' ); + } + } + } + } ); + fillNavigation(); + + // hide "previous"-button if first content container is shown + if ( current <= 0 ) + { + $( buttonPrev ).attr( "disabled", "disabled" ); + } + else + { + $( buttonPrev ).removeAttr( "disabled" ); + } + + // hide "next"-button if last content container is shown + if ( current + 1 == navigation.length ) + { + $( buttonNext ).attr( "disabled", "disabled" ); + } + else + { + $( buttonNext ).removeAttr( "disabled" ); + } + + // show current content container + $( container[current] ).show(); + + // set location hash + if ( current > 0 ) + { + window.location.hash = $( container[current] ).attr( 'data-plenty-checkout-id' ); + } + else + { + if ( window.location.hash.length > 0 ) + { + window.location.hash = ''; + } + } + + if ( contentChanged ) + { + resolveInterceptors( "afterChange", current ); + } + } + + /** + * Continue interrupted tabchange. Shorthand for: goTo(targetContainer.index, true) + * @function continueChange + * @param targetContainer The tab-object received from an interceptor + */ + function continueChange( targetContainer ) + { + goTo( targetContainer.index, true ); + } + + /** + * Show next checkout tab if available. Shorthand for + * + * if (current < navigation.length - 1) { + * goTo(current + 1); + * } + * + * @function next + */ + function next() + { + if ( current < navigation.length - 1 ) + { + goTo( current + 1 ); + } + } + + /** + * Show previous checkout tab if available + * @function next + */ + function previous() + { + if ( current > 0 ) + { + goTo( current - 1 ); + } + } + + /** + * Show checkout tab given by ID + * @function goToID + * @param {string} containerID ID of tab to show. Target tab must be marked with + * data-plenty-checkout-id="#..." + */ + function goToID( containerID ) + { + if ( containerID == 'next' ) + { + next(); + return true; + } + else if ( containerID == 'prev' ) + { + previous(); + return true; + } + else + { + containerID = containerID.replace( '#', '' ); + $( container ).each( function( i, elem ) + { + if ( $( elem ).attr( 'data-plenty-checkout-id' ) == containerID ) + { + goTo( i ); + return true; + } + } ); + } + + return false; + } + + /** + * Calculate navigation's width to match its parent element + * by increasing its items padding. + * @function fillNavigation + */ + function fillNavigation() + { + // break if manager has not been initialized + var navigationCount = navigation.length; + if ( navigationCount <= 0 ) + { + return; + } + + // reset inline styles + $( navigation ).removeAttr( 'style' ); + $( navigation ).children( 'span' ).removeAttr( 'style' ); + $( buttonNext ).removeAttr( 'style' ); + $( buttonPrev ).removeAttr( 'style' ); + + var buttonWidth = ($( buttonPrev ).outerWidth() < $( buttonNext ).outerWidth()) ? $( buttonNext ).outerWidth( true ) + 1 : $( buttonPrev ).outerWidth( true ) + 1; + $( buttonNext ).css( {width: buttonWidth + 'px'} ); + $( buttonPrev ).css( {width: buttonWidth + 'px'} ); + + // calculate width to fill + var width = $( navigation ).parent().parent().outerWidth( true ) - ( 2 * buttonWidth); + width -= parseInt( $( navigation ).parent().css( 'marginLeft' ) ) + parseInt( $( navigation ).parent().css( 'marginRight' ) ); + + var padding = width; + var tabWidth = []; + + $( navigation ).each( function( i, elem ) + { + padding -= parseInt( $( elem ).css( 'marginLeft' ) ); + padding -= parseInt( $( elem ).css( 'marginRight' ) ); + + tabWidth[i] = $( elem ).children( 'span' ).width(); + padding -= tabWidth[i]; + + padding -= parseInt( $( elem ).children( 'span' ).css( 'marginLeft' ) ); + padding -= parseInt( $( elem ).children( 'span' ).css( 'marginRight' ) ); + } ); + + var paddingEachItem = parseInt( padding / navigationCount ); + + var paddingLeft, paddingRight; + if ( paddingEachItem % 2 == 1 ) + { + paddingLeft = ( paddingEachItem / 2 ) + 0.5; + paddingRight = ( paddingEachItem / 2 ) - 0.5; + } + else + { + paddingLeft = paddingEachItem / 2; + paddingRight = paddingEachItem / 2; + } + + var paddingLastItem = parseInt( padding - ( ( navigationCount - 1 ) * ( paddingLeft + paddingRight ) ) ); + var paddingLastLeft, paddingLastRight; + if ( paddingLastItem % 2 == 1 ) + { + paddingLastLeft = ( paddingLastItem / 2 ) + 0.5; + paddingLastRight = ( paddingLastItem / 2) - 0.5; + } + else + { + paddingLastLeft = paddingLastItem / 2; + paddingLastRight = paddingLastItem / 2; + } + + var diff = width; + $( navigation ).each( function( i, elem ) + { + if ( i < navigationCount - 1 ) + { + $( elem ).children( 'span' ).css( {'paddingLeft': paddingLeft + 'px', 'paddingRight': paddingRight + 'px'} ); //.parent().css({ width: ( tabWidth[i] + paddingLeft + paddingRight + parseInt( $(elem).children('span').css('marginLeft') ) + parseInt( $(elem).children('span').css('marginRight') ) )+'px' }); + } + else + { + $( elem ).children( 'span' ).css( {'paddingLeft': paddingLastLeft + 'px', 'paddingRight': paddingLastRight + 'px'} ); //.parent().css({ width: ( tabWidth[i] + paddingLastLeft + paddingLastRight + parseInt( $(elem).children('span').css('marginLeft') ) + parseInt( $(elem).children('span').css('marginRight') ) )+'px' }); + } + } ); + + //$(navigation).parent().css('marginRight', 0); + } + + }, ['CMSFactory', 'CheckoutFactory'] ); + +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Magnus Martin + * ===================================================================================== + */ + + +(function( $, pm ) +{ + pm.service( 'PostfinderService', function( API, Modal, UIFactory ) + { + var packstationID = ''; + var shippingFields = {}; + var numberOfResults = {}; + var result = {}; + + return { + openPostfinderModal: openPostfinderModal, + isPackstation : isPackstation + }; + + function isPackstation() + { + var street = $( 'input[name="Street"]' ).val(); + return ( street.toUpperCase() == "PACKSTATION" || street.toUpperCase() == "POSTFILIALE" ); + } + + function openPostfinderModal() + { + shippingFields = { + PostfinderItemStreet : $( 'input[name="Street"]', '[data-plenty-checkout-form="shippingAddress"]' ), + PostfinderItemZIP : $( 'input[name="ZIP"]', '[data-plenty-checkout-form="shippingAddress"]' ), + PostfinderItemCity : $( 'input[name="City"]', '[data-plenty-checkout-form="shippingAddress"]' ), + PostfinderItemHouseNo: $( 'input[name="HouseNo"]', '[data-plenty-checkout-form="shippingAddress"]' ) + + }; + + shippingFields.PostfinderItemStreet.val( '' ); + + if ( (shippingFields.PostfinderItemZIP.val().length > 2 || shippingFields.PostfinderItemCity.val().length > 2) ) + { + + API.get( '/rest/checkout/shippingaddresspostfinderlist/', + { + suggestionType: "postfinder", + zip : shippingFields.PostfinderItemZIP.val(), + city : shippingFields.PostfinderItemCity.val() + } ) + + .done( function( response ) + { + result = response.data; + numberOfResults = result.length; + + if ( numberOfResults == 0 ) + { + showErrorMessage(); + } + + var params = { + addresses: [] + }; + + for ( var i = 0; i < numberOfResults; i++ ) + { + var dimension = 'km'; + var distInMeters = result[i].PostfinderItemDistance; + var distInKilometers = distInMeters / 1000; + distInKilometers = ((Math.round( distInKilometers * 100 ) / 100).toFixed( 2 )).replace( '.', ',' ); + + if ( distInMeters < 1000 ) + { + distInKilometers = distInMeters; + dimension = 'm'; + } + + params.addresses.push( { + index : i, + dimension: dimension, + type : result[i].PostfinderItemIsPackstation ? 'Packstation' : 'Postfiliale', + number : result[i].PostfinderItemIsPackstation ? result[i].PostfinderItemPackstationNo : result[i].PostfinderItemPostfilialNo, + street : result[i].PostfinderItemStreet, + houseNo : result[i].PostfinderItemHouseNo, + zip : result[i].PostfinderItemZIP, + city : result[i].PostfinderItemCity, + district : result[i].PostfinderItemDistrict, + distance : distInKilometers, + remark : result[i].PostfinderItemRemark + } ); + } + + var html = pm.compileTemplate( 'addressSuggestions/postFinder.html', params ); + + Modal.prepare() + .setTitle( pm.translate( 'Packstations and post offices in your area' ) ) + .setContent( html ) + .setClass( 'checkout' ) + .onConfirm( function() + { + shippingFields.PostfinderItemCity.removeClass( 'has-error' ).addClass( 'has-success' ); + $( 'label[for="' + shippingFields.PostfinderItemCity.attr( 'id' ) + '"]' ).removeClass( 'has-error' ).addClass( 'has-success' ); + + shippingFields.PostfinderItemZIP.removeClass( 'has-error' ).addClass( 'has-success' ); + $( 'label[for="' + shippingFields.PostfinderItemZIP.attr( 'id' ) + '"]' ).removeClass( 'has-error' ).addClass( 'has-success' ); + + shippingFields.PostfinderItemStreet.removeClass( 'has-error' ).addClass( 'has-success' ); + $( 'label[for="' + shippingFields.PostfinderItemStreet.attr( 'id' ) + '"]' ).removeClass( 'has-error' ).addClass( 'has-success' ); + + shippingFields.PostfinderItemHouseNo.removeClass( 'has-error' ).addClass( 'has-success' ); + $( 'label[for="' + shippingFields.PostfinderItemHouseNo.attr( 'id' ) + '"]' ).removeClass( 'has-error' ).addClass( 'has-success' ); + + packstationID = $( 'input[type="radio"][name="postfinder"]:checked' ).val(); + + if ( result[packstationID].PostfinderItemIsPackstation ) + { + $( shippingFields.PostfinderItemStreet ).val( 'PACKSTATION' ); + $( shippingFields.PostfinderItemHouseNo ).val( result[packstationID].PostfinderItemPackstationNo ); + } + else + { + $( shippingFields.PostfinderItemStreet ).val( 'POSTFILIALE' ); + $( shippingFields.PostfinderItemHouseNo ).val( result[packstationID].PostfinderItemPostfilialNo ); + } + $( shippingFields.PostfinderItemStreet ).trigger( 'change' ); + + $( shippingFields.PostfinderItemCity ).val( result[packstationID].PostfinderItemCity ); + $( shippingFields.PostfinderItemZIP ).val( result[packstationID].PostfinderItemZIP ); + return true; + } ) + .show() + } ); + } + else + { + showErrorMessage(); + } + + } + + function showErrorMessage() + { + UIFactory.throwError( 0, pm.translate( 'Please enter a ZIP code and/or a city.' ) ); + + shippingFields.PostfinderItemCity.removeClass( 'has-success' ).addClass( 'has-error' ); + $( 'label[for="' + shippingFields.PostfinderItemCity.attr( 'id' ) + '"]' ).removeClass( 'has-success' ).addClass( 'has-error' ); + + shippingFields.PostfinderItemZIP.removeClass( 'has-success' ).addClass( 'has-error' ); + $( 'label[for="' + shippingFields.PostfinderItemZIP.attr( 'id' ) + '"]' ).removeClass( 'has-success' ).addClass( 'has-error' ); + + shippingFields.PostfinderItemCity.focus(function() { + $(this).removeClass('has-error'); + var inputId = $(this).attr('id'); + $(this).closest('.form-group').find('[for="' + inputId + '"]').removeClass('has-error'); + }); + + shippingFields.PostfinderItemZIP.focus(function() { + $(this).removeClass('has-error'); + var inputId = $(this).attr('id'); + $(this).closest('.form-group').find('[for="' + inputId + '"]').removeClass('has-error'); + }); + } + }, ['APIFactory', 'ModalFactory', 'UIFactory'] ); + +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Services + */ +(function( $, pm ) +{ + + /** + * Provide templates for social share providers to inject them dynamically. + * @class SocialShareService + * @static + */ + pm.service( 'SocialShareService', function() + { + + //TODO: move to global variables + if ( typeof(socialLangLocale) == 'undefined' ) + { + socialLangLocale = 'en_US'; + } + if ( typeof(socialLang) == 'undefined' ) + { + socialLang = 'en'; + } + + return { + getSocialService: getService + }; + + /** + * Get the template for social media provider + * @function getService + * @param {string} identifier name of the social media provider to get the template for + * @returns {string} the template to inject in DOM + */ + function getService( identifier ) + { + var services = { + 'facebook-like': '', + + 'facebook-recommend': '', + + 'twitter': '', + + 'google-plus': '
    ' + + '', + }; + + return services[identifier]; + } + + /** + * get the canonical URL if defined + * @function getURL + * @private + * @return {string} The Canonical URL if defined or the current URI + */ + function getURI() + { + var uri = document.location.href; + var canonical = $( "link[rel=canonical]" ).attr( "href" ); + + if ( canonical && canonical.length > 0 ) + { + if ( canonical.indexOf( "http" ) < 0 ) + { + canonical = document.location.protocol + "//" + document.location.host + canonical; + } + uri = canonical; + } + + return uri; + } + + /** + * returns content of <meta name="" content=""> tags or '' if empty/non existant + * @function getMeta + * @private + * @param {string} name The meta name to get the value of; + */ + function getMeta( name ) + { + var metaContent = $( 'meta[name="' + name + '"]' ).attr( 'content' ); + return metaContent || ''; + } + + /** + * create tweet text from content of <meta name="DC.title"> and <meta name="DC.creator"> + * fallback to content of <title> tag + * @function getTweetText + * @private + */ + function getTweetText() + { + var title = getMeta( 'DC.title' ); + var creator = getMeta( 'DC.creator' ); + + if ( title.length > 0 && creator.length > 0 ) + { + title += ' - ' + creator; + } + else + { + title = $( 'title' ).text(); + } + + return encodeURIComponent( title ); + } + + } ); + +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +/** + * @module Services + */ +(function( $, pm ) +{ + + /** + * Provide methods for client-side form validation. + * @class ValidationService + * @static + */ + pm.service( 'ValidationService', function() + { + + return { + validate: validate + }; + + /** + * Check if element is a form element (input, select, textarea) or search for child form elements + * @function getFormControl + * @private + * @param {object} element the element to get the form element from + * @return {object} a valid form element (input, select, textarea) + */ + function getFormControl( element ) + { + element = $( element ); + if ( element.is( 'input' ) || element.is( 'select' ) || element.is( 'textarea' ) ) + { + return element; + } + else + { + if ( element.find( 'input' ).length > 0 ) + { + return element.find( 'input' ); + } + + else if ( element.find( 'select' ).length > 0 ) + { + return element.find( 'select' ); + } + + else if ( element.find( 'textarea' ).length > 0 ) + { + return element.find( 'textarea' ); + } + + else + { + return null; + } + } + + } + + /** + * Check given element has any value + * @function validateText + * @private + * @param {object} formControl the form element to validate + * @return {boolean} + */ + function validateText( formControl ) + { + // check if formControl is no checkbox or radio + if ( formControl.is( 'input' ) || formControl.is( 'select' ) || formControl.is( 'textarea' ) ) + { + // check if length of trimmed value is greater then zero + return $.trim( formControl.val() ).length > 0; + + } + else + { + console.error( 'Validation Error: Cannot validate Text for <' + formControl.prop( "tagName" ) + '>' ); + return false; + } + } + + /** + * Check given element's value is a valid email-address + * @function validateMail + * @private + * @param {object} formControl the form element to validate + * @return {boolean} + */ + function validateMail( formControl ) + { + var mailRegExp = /[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/; + if ( validateText( formControl ) ) + { + return mailRegExp.test( $.trim( formControl.val() ) ); + } + else + { + return false; + } + } + + /** + * Check given element's value is a valid number + * @function validateNumber + * @private + * @param {object} formControl the form element to validate + * @return {boolean} + */ + function validateNumber( formControl ) + { + if ( validateText( formControl ) ) + { + return $.isNumeric( $.trim( formControl.val() ) ); + } + else + { + return false; + } + } + + /** + * Check given element's value is equal to a references value + * @function validateValue + * @private + * @param {object} formControl the form element to validate + * @param {string} reference the required value + * @return {boolean} + */ + function validateValue( formControl, reference ) + { + if ( $( reference ).length > 0 ) + { + return $.trim( formControl.val() ) == $.trim( $( reference ).val() ); + } + else + { + return $.trim( formControl.val() ) == reference; + } + } + + function visibility( formControl ) + { + return formControl.is( ':visible' ); + } + + function isEnabled( formControl ) + { + return formControl.is( ':enabled' ); + } + + /** + * Validate a form. Triggers event 'validationFailed' if any element has an invalid value + * @function validate + * @param {object} form The form element to validate + * @returns {boolean} + * @example + * ```html + * + *
    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    + * + * + *
    + * + *
    + * ``` + * + * @example + * $(form).on('validationFailed', function(missingFields) { + * // handle missing fields + * }); + */ + function validate( form, errorClass ) + { + var formControl, formControls, validationKey, currentHasError, group, checked, checkedMin, checkedMax, attrValidate, validationKeys, formControlAttrType; + var $form = $( form ); + errorClass = errorClass || 'has-error'; + var missingFields = []; + var hasError = false; + + // check every required input inside form + $form.find( '[data-plenty-validate], input.Required' ).each( function( i, elem ) + { + attrValidate = $( elem ).attr( 'data-plenty-validate' ); + formControls = getFormControl( elem ) + // validate text inputs + validationKeys = !!attrValidate ? attrValidate : 'text'; + validationKeys = validationKeys.split( ',' ); + + for ( var i = 0, length = formControls.length; i < length; i++ ) + { + formControl = $( formControls[i] ); + formControlAttrType = formControl.attr( 'type' ); + + if ( !visibility( formControl ) || !isEnabled( formControl ) ) + { + return; + } + + validationKey = validationKeys[i].trim() || validationKeys[0].trim(); + currentHasError = false; + + // formControl is textfield (text, mail, password) or textarea + if ( (formControl.is( 'input' ) + && formControlAttrType != 'radio' + && formControlAttrType != 'checkbox') + || formControl.is( 'textarea' ) ) + { + switch ( validationKey ) + { + + case 'text': + currentHasError = !validateText( formControl ); + break; + + case 'mail': + currentHasError = !validateMail( formControl ); + break; + + case 'number': + currentHasError = !validateNumber( formControl ); + break; + + case 'value': + currentHasError = !validateValue( formControl, $( elem ).attr( 'data-plenty-validation-value' ) ); + break; + + case 'none': + // do not validate + break; + + default: + console.error( 'Form validation error: unknown validate property: "' + attrValidate + '"' ); + break; + } + } + else if ( formControl.is( 'input' ) + && (formControlAttrType == 'radio' + || formControlAttrType == 'checkbox') ) + { + // validate radio buttons + group = formControl.attr( 'name' ); + checked = $form.find( 'input[name="' + group + '"]:checked' ).length; + + if ( formControlAttrType == 'radio' ) + { + checkedMin = 1; + checkedMax = 1; + } + else + { + eval( "var minMax = " + attrValidate ); + checkedMin = !!minMax ? minMax.min : 1; + checkedMax = !!minMax ? minMax.max : 1; + } + + currentHasError = ( checked < checkedMin || checked > checkedMax ); + + } + else if ( formControl.is( 'select' ) ) + { + // validate selects + currentHasError = ( formControl.val() == '' || formControl.val() == '-1' ); + } + else + { + console.error( 'Form validation error: ' + $( elem ).prop( "tagName" ) + ' does not contain an form element' ); + return; + } + + if ( currentHasError ) + { + hasError = true; + missingFields.push( formControl ); + + if ( formControls.length > 1 ) + { + formControl.addClass( errorClass ); + $form.find( 'label[for="' + formControl.attr( 'id' ) + '"]' ).addClass( errorClass ); + } + else + { + $( elem ).addClass( errorClass ); + } + } + } + + } ); + + // scroll to element on 'validationFailed' + $form.on( 'validationFailed', function() + { + var distanceTop = 50; + var $error = $form.find( '.' + errorClass ).first(); + var errorOffset = $error.offset().top; + var $scrollTarget = $( 'html, body' ); + + // if form is inside of modal, scroll modal instead of body + if ( $form.parents( '.modal' ).length > 0 ) + { + $scrollTarget = $form.parents( '.modal' ).find( '.modal-body' ); + errorOffset = $scrollTarget.scrollTop() - ( $scrollTarget.offset().top - $error.offset().top ); + + } + else if ( $form.is( '.modal' ) ) + { + $scrollTarget = $form.find( '.modal-body' ); + errorOffset = $scrollTarget.scrollTop() - ( $scrollTarget.offset().top - $error.offset().top ); + } + + // only scroll if error is outside of viewport + if ( errorOffset - distanceTop < window.pageYOffset || errorOffset > (window.pageYOffset + window.innerHeight) ) + { + $scrollTarget.animate( { + scrollTop: errorOffset - distanceTop + } ); + } + } ); + + if ( hasError ) + { + // remove error class on focus + $form.find( '.' + errorClass ).each( function( i, elem ) + { + formControl = $( getFormControl( elem ) ); + formControl.on( 'focus click', function() + { + var $errorElement = $(elem); + $errorElement.removeClass( errorClass ); + $form.find( 'label[for="' + $( this ).attr( 'id' ) + '"]' ).removeClass( errorClass ); + } ); + } ); + + $form.trigger( 'validationFailed', [missingFields] ); + } + + var callback = $form.attr( 'data-plenty-callback' ); + + if ( !hasError && !!callback && callback != "submit" && typeof window[callback] == "function" ) + { + + var fields = {}; + $form.find( 'input, textarea, select' ).each( function() + { + if ( $( this ).attr( 'type' ) == 'checkbox' ) + { + fields[$( this ).attr( 'name' )] = $( this ).is( ':checked' ); + } + else + { + fields[$( this ).attr( 'name' )] = $( this ).val(); + } + } ); + + window[callback]( fields ); + return false; + } + else + { + return !hasError; + } + } + } ); + + /** + * jQuery-Plugin to calling {{#crossLink "ValidationService/validate"}}ValidationService.validate{{/crossLink}} + * on jQuery wrapped elements. + * @return {boolean} + */ + $.fn.validateForm = function() + { + return pm.getInstance().ValidationService.validate( this ); + }; + + /** + * jQuery-Plugin to get the values of contained form elements. + * @return {object} + */ + $.fn.getFormValues = function() + { + + var form = this; + var values = {}; + + function inject( position, value ) + { + var match = position.match( /^([^\[]+)(.*)/ ); + + if ( !!match[2] ) + { + var exp = /\[([^\]]+)]/g; + var child; + var children = []; + children[0] = match[1]; + while ( (child = exp.exec( match[2] )) !== null ) + { + children.push( child[1] ); + } + + for ( var i = children.length - 1; i >= 0; i-- ) + { + var val = {}; + val[children[i]] = value; + value = val; + } + values = $.extend( true, values, value ); + } + else + { + values[match[1]] = value; + } + } + + form.find( 'input, select, textarea' ).each( function( i, elem ) + { + if ( !!$( elem ).attr( 'name' ) ) + { + if ( $( elem ).attr( 'type' ) == "checkbox" ) + { + // get checkbox group + var groupValues = []; + $( form ).find( '[name="' + $( elem ).attr( 'name' ) + '"]:checked' ).each( function( j, checkbox ) + { + groupValues.push( $( checkbox ).val() ); + } ); + inject( $( elem ).attr( 'name' ), groupValues ); + } + else if ( $( elem ).attr( 'type' ) == 'radio' ) + { + if ( $( elem ).is( ':checked' ) ) + { + inject( $( elem ).attr( 'name' ), $( elem ).val() ); + } + } + else + { + inject( $( elem ).attr( 'name' ), $( elem ).val() ); + } + } + + } ); + return values; + } +}( jQuery, PlentyFramework )); +/** + * Services provide functions to be called from the instanced PlentyFramework.
    + * Services can inject Factories and can be injected into Directives. The are also + * available from the global instance of PlentyFramework + * @module Services + * @main Services + * @example + * PlentyFramework.service('ServiceName', serviceFunctions() { + * return { + * functionInService: function() {} + * } + * }); + * //... + * plenty.ServiceName.functionInService/(); + */ +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +(function( $, pm ) +{ + pm.directive( 'Authentication', function( AuthenticationService ) + { + return { + login: login + }; + + function login( elem ) + { + pm.getRecentEvent().preventDefault(); + AuthenticationService.customerLogin( $( elem ) ); + } + }, ["AuthenticationService"] ); + +}( jQuery, PlentyFramework )); +(function( $, pm ) +{ + pm.directive( 'Basket', function( BasketService ) + { + + return { + addBasketItem : addBasketItem, + changeItemQuantity: changeItemQuantity, + setItemQuantity : setItemQuantity + }; + + function addBasketItem( elem ) + { + pm.getRecentEvent().preventDefault(); + //init + var basketItemsList = {}; + var $elem = $( elem ); + var parentForm = $elem.parents( 'form' ); + + basketItemsList.BasketItemItemID = parentForm.find( '[name="ArticleID"]' ).val(); + basketItemsList.BasketItemPriceID = parentForm.find( '[name="SYS_P_ID"]' ).val(); + basketItemsList.BasketItemQuantity = parentForm.find( '[name="ArticleQuantity"]' ).val(); + basketItemsList.BasketItemBranchID = parentForm.find( '[name="source_category"]' ).val(); + + //attributes + var attributeInputsList = parentForm.find( '[name^="ArticleAttribute"]' ); + var attributesList = []; + + $.each( attributeInputsList, function( idx, elem ) + { + var match = elem.name.match( /^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/ ); + if ( match && match[1] ) + { + attributesList.push( { + BasketItemAttributeID : match[1], + BasketItemAttributeValueID: $( elem ).val() + } ); + } + } ); + + if ( attributesList.length != 0 ) + { + basketItemsList.BasketItemAttributesList = attributesList; + } + + //add basketItem and refresh previewLists + BasketService.addItem( [basketItemsList] ); + + } + + function changeItemQuantity( elem, increment ) + { + var $elem = $( elem ); + var $quantityInput = $elem.parent().find( 'input' ); + var maxLength = parseInt( $quantityInput.attr( 'maxlength' ) ) || 5; + var value = parseInt( $quantityInput.val() ) + increment; + + var isBasketView = $elem.parents( '[data-basket-item-id]' ).length > 0; + + if ( isBasketView ) + { + if ( (value + '').length <= maxLength && value >= 0 ) + { + $quantityInput.val( value ); + } + + var timeout = $elem.data( 'timeout' ); + + if ( !!timeout ) + { + window.clearTimeout( timeout ); + } + + timeout = window.setTimeout( function() + { + $quantityInput.trigger( 'change' ); + }, 1000 ); + + $elem.data( 'timeout', timeout ); + } + else { + if ( (value + '').length <= maxLength && value >= 1 ) + { + $quantityInput.val( value ); + } + } + } + + function setItemQuantity( basketItemID, input ) + { + BasketService.setItemQuantity( + basketItemID, + parseInt( $( input ).val() ) + ).fail( function() + { + // reset input's value on cancel + var basketItem = BasketService.getItem( basketItemID ); + $( input ).val( basketItem.BasketItemQuantity ); + } ); + } + + }, ['BasketService'] ); +}( jQuery, PlentyFramework )); +/** + * Mobile dropdowns + * Toggles dropdowns using css class 'open' instead of pseudo class :hover + * Usage: + * + * + * possible values for CONDITION + * "touch" : use 'open'-class if device is touch-device AND media size is 'md' or 'lg' + * "toggle-xs-sm-or-touch" : use 'open'-class if device is "touch" (as above) OR media size is 'xs' or 'sm' + * + */ +(function( $, pm ) +{ + pm.directive( 'MobileDropdown', function( MediaSize ) + { + // store all dropdown elements + var dropdownElements = []; + + // store dropdown elements which should be closed by clicking outside the element itself + var closableDropdownElements = []; + + return { + initDropdowns: initDropdowns, + openDropdown: openDropdown, + slideDropdown: slideDropdown + }; + + function initDropdowns() + { + $(window).on('orientationchange sizeChange', function() { + resetDropdowns( dropdownElements ); + }); + + $( 'html' ).click( function( e ) { + resetDropdowns( closableDropdownElements ); + }); + } + + function resetDropdowns( dropdownList ) + { + + for( var i = 0; i < dropdownList.length; i++ ) + { + $( dropdownList[i] ).removeClass('open'); + } + + } + + function openDropdown( elem, closable ) + { + + var $elem = $( elem ); + var $parent = $elem.parent(); + + if( Modernizr.touch ) + { + if ( MediaSize.isInterval('md, lg') && !$parent.is( '.open' ) ) + { + + // avoid redirecting + pm.getRecentEvent().preventDefault(); + + // hide other dropdowns + resetDropdowns( dropdownElements ); + + // show dropdown + $parent.addClass( 'open' ); + + if ( $.inArray( $parent[0], dropdownElements ) < 0 ) + { + dropdownElements.push( $parent[0] ); + } + + if ( !!closable && $.inArray( $parent[0], closableDropdownElements ) < 0 ) + { + closableDropdownElements.push( $parent[0] ); + } + + // avoid closing popup by clicking itself + $parent.off( 'click' ); + $parent.on( 'click', function( e ) + { + e.stopPropagation(); + } ); + } + + } + else + { + // redirect to href + // do nothing + } + + } + + function slideDropdown( elem ) + { + var $elem = $( elem ); + var $elemParent = $elem.parent(); + + $elemParent.addClass( 'animating' ); + $elem.siblings( 'ul' ).slideToggle( 200, function() + { + if ( $elemParent.is( '.open' ) ) + { + $elemParent.removeClass( 'open' ); + } + else + { + $elemParent.addClass( 'open' ); + if( $.inArray( $elemParent[0], dropdownElements) < 0 ) + { + dropdownElements.push( $elemParent[0] ); + } + } + $elem.siblings( 'ul' ).removeAttr( 'style' ); + $elemParent.removeClass( 'animating' ); + } ); + } + + }, ['MediaSizeService'] ); +}( jQuery, PlentyFramework )); +(function( $, pm ) +{ + pm.directive( 'Redirect', function( MediaSizeService, NavigatorService ) + { + + return { + to : to, + toCheckoutTab: toCheckoutTab + }; + + function to( href ) + { + if ( MediaSizeService.interval() != 'xs' ) + { + if ( $( href ).length > 0 ) + { + window.location.assign( $( href ).attr( 'href' ) ); + } + else + { + window.location.assign( href ); + } + } + } + + function toCheckoutTab( tabID ) + { + NavigatorService.goToID( tabID ); + } + + }, ['MediaSizeService', 'NavigatorService'] ); +}( jQuery, PlentyFramework )); +(function( $, pm ) +{ + pm.directive( 'Tab', function( MediaSize ) + { + + var tabGroups = {}; + + return { + showTab : showTab, + initRemoteLabel: initRemoteLabel, + initRemoteTab : initRemoteTab, + showRemoteTab : showRemoteTab + }; + + function showTab( tabSelector ) + { + $( tabSelector ).tab( 'show' ); + } + + function initRemoteLabel( $elem, tabID, groupID ) + { + if ( !tabGroups[groupID] ) + { + tabGroups[groupID] = new TabGroup(); + } + + if ( !tabGroups[groupID].getTab( tabID ) ) + { + tabGroups[groupID].addTab( tabID ); + } + + tabGroups[groupID].getTab( tabID ).addLabel( $elem ); + } + + function initRemoteTab( $elem, tabID, groupID ) + { + if ( !tabGroups[groupID] ) + { + tabGroups[groupID] = new TabGroup(); + } + + if ( !tabGroups[groupID].getTab( tabID ) ) + { + tabGroups[groupID].addTab( tabID ); + } + + tabGroups[groupID].getTab( tabID ).setContent( $elem ); + } + + function showRemoteTab( tabID, groupID, interval ) + { + if( MediaSize.isInterval( interval ) ) + { + pm.getRecentEvent().preventDefault(); + + if ( !!tabGroups[groupID] && !!tabGroups[groupID].getTab( tabID ) ) + { + tabGroups[groupID].showTab( tabID ); + } + + } + } + + function TabGroup() + { + var tabs = {}; + var activeTab; + + return { + addTab : addTab, + showTab: showTab, + getTab : getTab, + resetTabs: resetTabs + }; + + function addTab( tabID ) + { + tabs[tabID] = new Tab( tabID ); + return tabs[tabID]; + } + + function showTab( tabID ) + { + var zIndex = 0; + if ( !!activeTab ) + { + // activeTab is set + zIndex = parseInt( activeTab.getContent().parent().css( 'zIndex' ) ); + activeTab.hide(); + activeTab.getContent().parent().css( 'zIndex', zIndex - 1 ); + } + else + { + // activeTab not set before + for ( var tab in tabs ) + { + if( !!tabs[tab].getContent() ) + { + var currentZ = parseInt( tabs[tab].getContent().parent().css( 'zIndex' ) ); + if ( zIndex == 0 || currentZ < zIndex ) + { + zIndex = currentZ; + } + tabs[tab].hide(); + } + } + + for ( var tab in tabs ) + { + if( !!tabs[tab].getContent() ) + { + tabs[tab].getContent().parent().css( 'zIndex', zIndex - 1 ); + } + } + + $(window ).on('sizeChange', resetTabs); + } + + activeTab = tabs[tabID]; + activeTab.getContent().parent().css( 'zIndex', zIndex ); + activeTab.show(); + } + + function getTab( tabID ) + { + return tabs[tabID]; + } + + function resetTabs() + { + for ( var tab in tabs ) + { + if( !!tabs[tab].getContent() ) + { + tabs[tab].show(); + } + } + + activeTab = null; + } + } + + function Tab( id ) + { + var $labels = []; + var $content; + var tabID = id; + + return { + addLabel : addLabel, + setContent: setContent, + getContent: getContent, + getID : getID, + show : show, + hide : hide + }; + + function getID() + { + return tabID; + } + + function addLabel( label ) + { + $labels.push( label ); + return this; + } + + function setContent( content ) + { + $content = content; + return this; + } + + function getContent() + { + return $content; + } + + function show() + { + for ( var i = 0; i < $labels.length; i++ ) + { + $labels[i].addClass( 'active' ); + } + + if ( !!$content ) + { + $content.show().addClass( 'in' ); + } + + } + + function hide() + { + for ( var i = 0; i < $labels.length; i++ ) + { + $labels[i].removeClass( 'active' ); + } + + if ( !!$content ) + { + $content.hide().removeClass( 'in' ); + } + } + } + + }, ['MediaSizeService'] ); +})( jQuery, PlentyFramework ); +/** + * Add fancy ui modifications - the visual stuff - here. + * Respond functionality like 'event':UI.myFunctionality(currentElement) + * + * Example: + * + * + */ +(function( $, pm ) +{ + pm.directive( 'UI', function( MediaSizeService, SocialShareService ) + { + // elements to calculate height. + var equalHeightElementList = []; + var toTopButtonList = []; + + return { + initUIWindowEvents : initUIWindowEvents, + addContentPageSlider: addContentPageSlider, + equalHeight : equalHeight, + initToTop : initToTop, + initLazyload : initLazyload, + initSlideToggle : initSlideToggle, + slideDown : slideDown, + slideUp : slideUp, + slideToggle : slideToggle, + toggleHideShow : toggleHideShow, + toggleSocialShare : toggleSocialShare, + toggleClass : toggleClass, + addClass : addClass, + removeClass : removeClass + }; + + function initUIWindowEvents() + { + // resize elements on window size change. + $( window ).on( 'sizeChange contentChanged', function() + { + fireEqualHeight(); + } ); + + $( window ).on( "scroll resize", function() + { + if ( toTopButtonList.length > 0 ) + { + if ( $( document ).scrollTop() > 100 ) + { + doToArrayElements( toTopButtonList, "addClass", "visible" ); + } + else + { + doToArrayElements( toTopButtonList, "removeClass", "visible" ); + } + } + } ); + } + + /** + * Adds content page slider (owlCarousel) + * + * usage: + *
    + *
    + * ... + *
    + *
    + * ... + *
    + * ... + *
    + * + * Legacy directive selector: data-plenty="contentpageSlider" + * + * @param elem + */ + function addContentPageSlider( elem ) + { + $( elem ).owlCarousel( { + navigation : true, + navigationText : false, + slideSpeed : 1000, + paginationSpeed: 1000, + singleItem : true, + autoPlay : 6000, + stopOnHover : true, + afterMove : function( current ) + { + $( current ).find( 'img[data-plenty-rel="lazyload"]' ).trigger( 'appear' ); + } + } ); + } + + /** + * Equal Box height + * Calculates equal box height for chosen elements. + * + * Legacy directive selector: data-plenty-equal + * + * @param elem + * @param elementExists - default false + */ + function equalHeight( elem, mediaSizes, elementExists ) + { + var $elem = $( elem ); + var maxHeight = 0; + var $equalTarget = {}; + var $equalTargetList = $elem.find( '[data-plenty-rel="equal-target"]' ).length > 0 ? $elem.find( '[data-plenty-rel="equal-target"]' ) : $elem.children(); + + // if element wasn't pushed before. + if ( elementExists !== true ) + { + equalHeightElementList.push( elem ); + } + + for ( var i = $equalTargetList.length; i >= 0; i-- ) + { + $equalTarget = $( $equalTargetList[i] ); + $equalTarget.css( 'height', '' ); + + if ( $equalTarget.outerHeight( true ) > maxHeight ) + { + maxHeight = $equalTarget.outerHeight( true ); + } + } + + if ( !mediaSizes || MediaSizeService.isInterval( mediaSizes ) ) + { + $equalTargetList.height( maxHeight ); + } + } + + /** + * Scroll page to top. + * Just add without events. + * + * Legacy directive selector: data-plenty="toTop" + * + * @param elem + */ + function initToTop( elem ) + { + var $elem = $( elem ); + + $elem.click( function() + { + $( 'html, body' ).animate( { + scrollTop: 0 + }, 400 ); + return false; + } ); + + if ( !!$.inArray( $elem, toTopButtonList ) ) + { + toTopButtonList.push( $elem ); + } + } + + /** + * lazy load on ready. + * + * Legacy directive selector: img[data-plenty-lazyload] + * + * @param elem + */ + function initLazyload( elem, effect ) + { + var $elem = $( elem ); + + $elem.lazyload( { + effect: effect + } ); + $elem.on( "loaded", function() + { + $elem.css( 'display', 'inline-block' ); + } ); + } + + /** + * Toggle show and hide animation. + * + * Legacy directive selector: data-plenty="openCloseToggle" + * + * @param elem + */ + function toggleHideShow( elem ) + { + + console.log( elem ); + + var $elem = $( elem ); + var $elemParent = $elem.parent(); + + $elemParent.addClass( 'animating' ); + $elem.siblings( 'ul' ).slideToggle( 200, function() + { + if ( $elemParent.is( '.open' ) ) + { + $elemParent.removeClass( 'open' ); + } + else + { + $elemParent.addClass( 'open' ); + } + $elem.siblings( 'ul' ).removeAttr( 'style' ); + $elemParent.removeClass( 'animating' ); + } ); + } + + /** + * Toggle target content on click. + * Bind to checked-/ unchecked-property of radio buttons + * + * Legacy directive selector: data-plenty-slidetoggle + * + * @param elem + */ + function initSlideToggle( elem, checked ) + { + var $elem = $( elem ); + var $targetElement = $( $elem.attr( 'data-plenty-rel' ) ); + + if ( $elem.is( 'input[type="radio"]' ) ) + { + // is radio button + var $radioGroupList = $( 'input[type="radio"][name="' + ( $elem.attr( 'name' ) ) + '"]' ); + var visibleOnChecked = !checked || checked == 'checked'; + + $radioGroupList.change( function() + { + var $self = $( this ); + $targetElement.parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); + + if ( $self.is( ':checked' ) && $self[0] === $elem[0] && visibleOnChecked == true ) + { + // checked + $targetElement.slideDown( 400, function() + { + fireEqualHeight(); + } ); + } + else + { + // unchecked (since other radio button has been checked) + $targetElement.slideUp( 400, function() + { + fireEqualHeight(); + } ); + } + } ); + } + else + { + // is not radio button + $elem.click( function() + { + //$targetElement.parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); + + $elem.addClass( 'animating' ); + $targetElement.slideToggle( 400, function() + { + $elem.removeClass( 'animating' ); + $elem.toggleClass( 'active' ); + fireEqualHeight(); + } ); + } ); + } + } + + function slideDown( target, duration ) + { + duration = duration || 400; + $(target).parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); + $(target).slideDown( duration, function() { + fireEqualHeight(); + }); + } + + function slideUp( target, duration ) + { + duration = duration || 400; + $(target).parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); + $(target).slideUp( duration, function() { + fireEqualHeight(); + }); + } + + function slideToggle( target, duration ) + { + duration = duration || 400; + $(target).parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); + $(target).slideToggle( duration, function() { + fireEqualHeight(); + }); + } + + + /** + * TODO check comment + * Social Share Activation + * Activate and load share-buttons manually by clicking a separate button + * Usage / data-attributes: + *
    + * Will be used to activate the service set in + * data-plenty-social="" + * Will be replaced with loaded share button + *
    + * + * possible values for data-plenty-social: + * "facebook-like" : Load Facebooks "Like"-Button + * "facebook-recommend" : Load Facebooks "Recommend"-Button + * "twitter" : Load Twitter Button + * "google-plus" : Load google "+1"-Button + * + * Additional Tooltips + * You can extend the parent element with a (bootstrap) tooltip by adding data-toggle="tooltip" and + * title="TOOLTIP CONTENT" Tooltip will be destroyed after activating a social service + * (!) Requires bootstrap.js + * + * Legacy directive selector: data-plenty-social + * + * @param elem + */ + function toggleSocialShare( elem, socialShareService ) + { + var $elem = $( elem ); + var $toggle = $elem.find( '[data-plenty-rel="social-switch"]' ); + + // append container to put / delete service.html + $elem.append( '' ); + + // add "off" class to switch, if neither "off" or "on" is set + // replaced hasClass() with is() benchmark: http://jsperf.com/hasclasstest + if ( !$toggle.is( 'off, on' ) ) + { + $toggle.addClass( 'off' ); + } + + // toggle switch + $toggle.on( 'click', function() + { + if ( $toggle.hasClass( 'off' ) ) + { + // TODO remove bootstrap dependency + if ( $elem.attr( "data-toggle" ) == "tooltip" ) + { + $elem.tooltip( 'destroy' ) + } + $toggle.removeClass( 'off' ).addClass( 'on' ); + // hide dummy button + $elem.find( '[data-plenty-rel="social-placeholder"]' ).hide(); + // load HTML defined in 'api' + $elem.find( '.social-container' ).append( SocialShareService.getSocialService( socialShareService ) ); + } + // do not disable social medias after activation + } ); + } + + /** + * Toggle Class + * toggle style-classes on click + * Usage / data-attribute: + *
    + * target : jQuery selector to toggle the class at. + * class : class(es) to toggle at target element + * media : only toggle class on given media sizes (optional) + * + * (!) using data-plenty-toggle on -elements will prevent redirecting to href="" + * + * Legacy directive selector: data-plenty-toggle + * + * @param cssClass + * @param target + * @param interval + */ + function toggleClass( cssClass, target, interval ) + { + + if ( !!target && !!cssClass && ( !interval || MediaSizeService.isInterval( interval ) ) ) + { + var e = pm.getRecentEvent(); + if( !!e ) e.preventDefault(); + + $( target ).toggleClass( cssClass ); + return false; + } + } + + function addClass( cssClass, target, interval ) + { + if ( !!target && !!cssClass && ( !interval || MediaSizeService.isInterval( interval ) ) ) + { + var e = pm.getRecentEvent(); + if( !!e ) e.preventDefault(); + + $( target ).addClass( cssClass ); + return false; + } + } + + function removeClass( cssClass, target, interval ) + { + if ( !!target && !!cssClass && ( !interval || MediaSizeService.isInterval( interval ) ) ) + { + var e = pm.getRecentEvent(); + if( !!e ) e.preventDefault(); + + $( target ).removeClass( cssClass ); + return false; + } + } + + /* + ##### PRIVATE FUNCTIONS ###### + */ + + function fireEqualHeight() + { + for ( var i = equalHeightElementList.length - 1; i >= 0; i-- ) + { + equalHeight( equalHeightElementList[i], '', true ); + } + } + + function doToArrayElements( array, func, params ) + { + for ( var i = array.length - 1; i >= 0; i-- ) + { + array[i][func]( params ); + } + } + + }, ['MediaSizeService', 'SocialShareService'] ); +}( jQuery, PlentyFramework )); +(function( $, pm ) +{ + pm.directive( 'Validator', function( ValidationService ) + { + + return { + validate: validate + }; + + function validate( form, errorClass ) + { + return ValidationService.validate( form, errorClass ); + } + + }, ['ValidationService'] ); +}( jQuery, PlentyFramework )); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + +PlentyFramework.compile(); + +// Create global instance of PlentyFramework for usage in Webshop-Layouts +var plenty = PlentyFramework.getInstance(); + +/* + * initially bind all registered directives + * + * will not be tested. reasons: + * http://stackoverflow.com/questions/29153733/how-to-unit-test-a-document-ready-function-using-jasmine + */ +jQuery( document ).ready( function() +{ + plenty.bindDirectives(); +} ); \ No newline at end of file diff --git a/src/services/CheckoutService.js b/src/services/CheckoutService.js index 9e6274b..bcbd437 100644 --- a/src/services/CheckoutService.js +++ b/src/services/CheckoutService.js @@ -132,7 +132,7 @@ } else if ( shippingAddress.Street == "POSTFILIALE" ) { - shippingAddress.isPostfiliale = 1; + shippingAddress.IsPostfiliale = 1; shippingAddress.PostfilialNo = shippingAddress.HouseNo; } From 5510ae0f2663bb66c1136569cfd5cda308b52b7b Mon Sep 17 00:00:00 2001 From: Maximilian Lauterbach Date: Wed, 9 Dec 2015 12:05:11 +0100 Subject: [PATCH 08/17] fixed variable name for PostFinder --- tools/CheckoutManager-migrate.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tools/CheckoutManager-migrate.js diff --git a/tools/CheckoutManager-migrate.js b/tools/CheckoutManager-migrate.js new file mode 100644 index 0000000..bb503b3 --- /dev/null +++ b/tools/CheckoutManager-migrate.js @@ -0,0 +1,18 @@ +CheckoutManager = {}; + +(function( $, pm ) +{ + CheckoutManager.init = function() + { + pm.NavigatorService.init(); + }; + + CheckoutManager.Navigator = {}; + + CheckoutManager.Navigator.fillNavigation = function() + { + pm.NavigatorService.fillNavigation(); + }; + + +})( jQuery, PlentyFramework ); \ No newline at end of file From e09fc7481245293df83a76a3f3db1676dfdabbc0 Mon Sep 17 00:00:00 2001 From: Maximilian Lauterbach Date: Wed, 9 Dec 2015 12:05:46 +0100 Subject: [PATCH 09/17] fixed variable name for PostFinder --- dist/plentymarketsCMStools-1.0.1.min.js | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 dist/plentymarketsCMStools-1.0.1.min.js diff --git a/dist/plentymarketsCMStools-1.0.1.min.js b/dist/plentymarketsCMStools-1.0.1.min.js new file mode 100644 index 0000000..82ec69a --- /dev/null +++ b/dist/plentymarketsCMStools-1.0.1.min.js @@ -0,0 +1,9 @@ +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== +*/!function(a,b){"object"==typeof exports&&exports&&"string"!=typeof exports.nodeName?b(exports):"function"==typeof define&&define.amd?define(["exports"],b):(a.Mustache={},b(Mustache))}(this,function(a){function b(a){return"function"==typeof a}function c(a){return p(a)?"array":typeof a}function d(a){return a.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function e(a,b){return null!=a&&"object"==typeof a&&b in a}function f(a,b){return q.call(a,b)}function g(a){return!f(r,a)}function h(a){return String(a).replace(/[&<>"'\/]/g,function(a){return s[a]})}function i(b,c){function e(){if(r&&!s)for(;q.length;)delete o[q.pop()];else q=[];r=!1,s=!1}function f(a){if("string"==typeof a&&(a=a.split(u,2)),!p(a)||2!==a.length)throw new Error("Invalid tags: "+a);h=new RegExp(d(a[0])+"\\s*"),i=new RegExp("\\s*"+d(a[1])),m=new RegExp("\\s*"+d("}"+a[1]))}if(!b)return[];var h,i,m,n=[],o=[],q=[],r=!1,s=!1;f(c||a.tags);for(var y,z,A,B,C,D,E=new l(b);!E.eos();){if(y=E.pos,A=E.scanUntil(h))for(var F=0,G=A.length;G>F;++F)B=A.charAt(F),g(B)?q.push(o.length):s=!0,o.push(["text",B,y,y+1]),y+=1,"\n"===B&&e();if(!E.scan(h))break;if(r=!0,z=E.scan(x)||"name",E.scan(t),"="===z?(A=E.scanUntil(v),E.scan(v),E.scanUntil(i)):"{"===z?(A=E.scanUntil(m),E.scan(w),E.scanUntil(i),z="&"):A=E.scanUntil(i),!E.scan(i))throw new Error("Unclosed tag at "+E.pos);if(C=[z,A,y,E.pos],o.push(C),"#"===z||"^"===z)n.push(C);else if("/"===z){if(D=n.pop(),!D)throw new Error('Unopened section "'+A+'" at '+y);if(D[1]!==A)throw new Error('Unclosed section "'+D[1]+'" at '+y)}else"name"===z||"{"===z||"&"===z?s=!0:"="===z&&f(A)}if(D=n.pop())throw new Error('Unclosed section "'+D[1]+'" at '+E.pos);return k(j(o))}function j(a){for(var b,c,d=[],e=0,f=a.length;f>e;++e)b=a[e],b&&("text"===b[0]&&c&&"text"===c[0]?(c[1]+=b[1],c[3]=b[3]):(d.push(b),c=b));return d}function k(a){for(var b,c,d=[],e=d,f=[],g=0,h=a.length;h>g;++g)switch(b=a[g],b[0]){case"#":case"^":e.push(b),f.push(b),e=b[4]=[];break;case"/":c=f.pop(),c[5]=b[2],e=f.length>0?f[f.length-1][4]:d;break;default:e.push(b)}return d}function l(a){this.string=a,this.tail=a,this.pos=0}function m(a,b){this.view=a,this.cache={".":this.view},this.parent=b}function n(){this.cache={}}var o=Object.prototype.toString,p=Array.isArray||function(a){return"[object Array]"===o.call(a)},q=RegExp.prototype.test,r=/\S/,s={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},t=/\s*/,u=/\s+/,v=/\s*=/,w=/\s*\}/,x=/#|\^|\/|>|\{|&|=|!/;l.prototype.eos=function(){return""===this.tail},l.prototype.scan=function(a){var b=this.tail.match(a);if(!b||0!==b.index)return"";var c=b[0];return this.tail=this.tail.substring(c.length),this.pos+=c.length,c},l.prototype.scanUntil=function(a){var b,c=this.tail.search(a);switch(c){case-1:b=this.tail,this.tail="";break;case 0:b="";break;default:b=this.tail.substring(0,c),this.tail=this.tail.substring(c)}return this.pos+=b.length,b},m.prototype.push=function(a){return new m(a,this)},m.prototype.lookup=function(a){var c,d=this.cache;if(d.hasOwnProperty(a))c=d[a];else{for(var f,g,h=this,i=!1;h;){if(a.indexOf(".")>0)for(c=h.view,f=a.split("."),g=0;null!=c&&gi;++i)g=void 0,e=a[i],f=e[0],"#"===f?g=this.renderSection(e,b,c,d):"^"===f?g=this.renderInverted(e,b,c,d):">"===f?g=this.renderPartial(e,b,c,d):"&"===f?g=this.unescapedValue(e,b):"name"===f?g=this.escapedValue(e,b):"text"===f&&(g=this.rawValue(e)),void 0!==g&&(h+=g);return h},n.prototype.renderSection=function(a,c,d,e){function f(a){return g.render(a,c,d)}var g=this,h="",i=c.lookup(a[1]);if(i){if(p(i))for(var j=0,k=i.length;k>j;++j)h+=this.renderTokens(a[4],c.push(i[j]),d,e);else if("object"==typeof i||"string"==typeof i||"number"==typeof i)h+=this.renderTokens(a[4],c.push(i),d,e);else if(b(i)){if("string"!=typeof e)throw new Error("Cannot use higher-order sections without the original template");i=i.call(c.view,e.slice(a[3],a[5]),f),null!=i&&(h+=i)}else h+=this.renderTokens(a[4],c,d,e);return h}},n.prototype.renderInverted=function(a,b,c,d){var e=b.lookup(a[1]);return!e||p(e)&&0===e.length?this.renderTokens(a[4],b,c,d):void 0},n.prototype.renderPartial=function(a,c,d){if(d){var e=b(d)?d(a[1]):d[a[1]];return null!=e?this.renderTokens(this.parse(e),c,d,e):void 0}},n.prototype.unescapedValue=function(a,b){var c=b.lookup(a[1]);return null!=c?c:void 0},n.prototype.escapedValue=function(b,c){var d=c.lookup(b[1]);return null!=d?a.escape(d):void 0},n.prototype.rawValue=function(a){return a[1]},a.name="mustache.js",a.version="2.1.3",a.tags=["{{","}}"];var y=new n;a.clearCache=function(){return y.clearCache()},a.parse=function(a,b){return y.parse(a,b)},a.render=function(a,b,d){if("string"!=typeof a)throw new TypeError('Invalid template! Template should be a "string" but "'+c(a)+'" was given as the first argument for mustache#render(template, view, partials)');return y.render(a,b,d)},a.to_html=function(c,d,e,f){var g=a.render(c,d,e);return b(f)?void f(g):g},a.escape=h,a.Scanner=l,a.Context=m,a.Writer=n}),Object.equals=function(a,b){if(a===b)return!0;if(!(a instanceof Object&&b instanceof Object))return!1;if(a.constructor!==b.constructor)return!1;for(var c in a)if(a.hasOwnProperty(c)){if(!b.hasOwnProperty(c))return!1;if(a[c]!==b[c]){if("object"!=typeof a[c])return!1;if(!Object.equals(a[c],b[c]))return!1}}for(var c in b)if(b.hasOwnProperty(c)&&!a.hasOwnProperty(c))return!1;return!0};var TemplateCache={};TemplateCache["addressSuggestions/addressDoctor.html"]='',TemplateCache["addressSuggestions/postFinder.html"]='{{#addresses}}\n
    \n
    \n \n
    \n
    \n{{/addresses}}\n',TemplateCache["error/errorMessage.html"]='
    \n Code {{code}}:\n {{{message}}}\n
    \n',TemplateCache["error/errorPopup.html"]='
    \n \n
    \n
    \n
    \n',TemplateCache["modal/modal.html"]='\n',TemplateCache["waitscreen/waitscreen.html"]='
    ',function(a){function b(a,b,c,d){a.on(b,function(a){return g.push(a),c.apply(null,d)})}function c(b){var c=a(b);c.is('input[type="checkbox"]')&&c.on("change",function(){c.is(":checked")?c.trigger("check"):c.trigger("uncheck")}),c.is('input[type="radio"]')&&c.on("change",function(){var b=c.attr("name");a('input[type="radio"][name="'+b+'"]').each(function(b,c){var d=a(c);d.is(":checked")?d.trigger("check"):d.trigger("uncheck")})})}function d(a,b){for(var c=/^(([\w]+):)?([\w]+)\.([\w]+)(\((.*)\))?$/,d=a.split(";"),e=[],f=0;f0)for(var j=h[6].match(/(['][^']+['])|([\w-]+)|(["][^"]+["])/g),k=0;k=0;c--)if(a==g[c].type)return g[c];return null},PlentyFramework.pushEvent=function(a){g.push(a)},PlentyFramework.service=function(a,b,c){return"string"!=typeof a?void console.error("Type mismatch: Expect first parameter to be a 'string', '"+typeof a+"' given."):"function"!=typeof b?void console.error("Type mismatch: Expect second parameter to be a 'function', '"+typeof b+"' given."):(c=c||[],void(e.services[a]={name:a,dependencies:c,compile:function(){var d=PlentyFramework.resolveFactories(c);PlentyFramework.prototype[a]=b.apply(null,d)}}))},PlentyFramework.resolveServices=function(b){var c=[];return a.each(b,function(a,b){if(!PlentyFramework.prototype.hasOwnProperty(b)){if(!e.services.hasOwnProperty(b))return console.error('Cannot inject Service "'+b+'": Service not found.'),!1;e.services[b].compile()}var d=PlentyFramework.prototype[b];c.push(d)}),c},PlentyFramework.factories={},PlentyFramework.factory=function(a,b,c){return"string"!=typeof a?void console.error("Type mismatch: Expect first parameter to be a 'string', '"+typeof a+"' given."):"function"!=typeof b?void console.error("Type mismatch: Expect second parameter to be a 'function', '"+typeof b+"' given."):(c=c||[],void(e.factories[a]={name:a,dependencies:c,compile:function(){var d=PlentyFramework.resolveFactories(c);PlentyFramework.factories[a]=b.apply(null,d)}}))},PlentyFramework.resolveFactories=function(b){var c=[];return a.each(b,function(a,b){if(!PlentyFramework.factories.hasOwnProperty(b)){if(!e.factories.hasOwnProperty(b))return console.error('Cannot inject Factory "'+b+'": Factory not found.'),!1;e.factories[b].compile()}var d=PlentyFramework.factories[b];c.push(d)}),c},PlentyFramework.compileTemplate=function(a,b){return b=b||{},b.translate=function(){return function(a,b){return b(PlentyFramework.translate(a))}},Mustache.render(TemplateCache[a],b)},PlentyFramework.scriptPath="",PlentyFramework.Strings={},PlentyFramework.loadLanguageFile=function(b){a.get(PlentyFramework.scriptPath+b).done(function(a){PlentyFramework.Strings=a})},PlentyFramework.translate=function(a,b){var c;return PlentyFramework.Strings.hasOwnProperty(a)?c=PlentyFramework.Strings[a]:(c=a,console.warn('No translation found for "'+c+'".')),b&&(c=Mustache.render(c,b)),c},PlentyFramework.compile=function(){for(var a in e.factories)PlentyFramework.factories.hasOwnProperty(a)||e.factories[a].compile();for(var b in e.services)PlentyFramework.prototype.hasOwnProperty(b)||e.services[b].compile();for(var c in e.directives)PlentyFramework.directives.hasOwnProperty(c)||e.directives[c].compile();var d=document.getElementsByTagName("SCRIPT");d.length>0&&(PlentyFramework.scriptPath=d[d.length-1].src.match(/(.*)\/(.*)\.js(\?\S*)?$/)[1])}}(jQuery),PlentyFramework.cssClasses={active:"active"},function(a,b){b.partials.Error={init:function(b){a(b).find(".close").click(function(){b.hide(),b.find(".plentyErrorBoxInner").html("")})},addError:function(b,c){var d=a(c).attr("data-plenty-error-code");a(b).find('[data-plenty-error-code="'+d+'"]').length<=0&&a(b).find(".plentyErrorBoxInner").append(c)},show:function(b){a(b).show()}}}(jQuery,PlentyFramework),function(a,b){b.partials.Modal={init:function(a,b){a.on("hidden.bs.modal",function(){b.hide(),a.remove()}),b.timeout>0&&(a.on("hide.bs.modal",b.stopTimeout),a.find(".modal-content").hover(function(){b.pauseTimeout()},function(){a.is(".in")&&b.continueTimeout()}))},show:function(a){a.modal("show")},hide:function(a){a.modal("hide")},isModal:function(b){return a(b).filter(".modal").length+a(b).find(".modal").length>0},getModal:function(b){var c=a(b);return c.length>1&&(c=a(b).filter(".modal")||a(b).find(".modal")),c}}}(jQuery,PlentyFramework),function(a){a(document).on("initPartials",function(b,c){a(c).find('[data-toggle="tooltip"]').tooltip({container:"body"})})}(jQuery),function(a,b){b.partials.WaitScreen={show:function(a){a.addClass("in")},hide:function(a){a.removeClass("in")}}}(jQuery,PlentyFramework),function(a,b){b.factory("APIFactory",function(b){function c(c){try{var d=a.parseJSON(c.responseText);b.printErrors(d.error.error_stack)}catch(e){b.throwError(c.status,c.statusText)}}function d(d,e,f,g,h){return g||b.showWaitScreen(),a.ajax(d,{type:"GET",data:e,dataType:"json",async:!h,error:function(a){f||c(a)}}).always(function(){g||b.hideWaitScreen()})}function e(d,e,f,g){var h={type:"POST",dataType:"json",error:function(a){f||c(a)}};return e&&e.isFile?(h.cache=e.cache,h.processData=e.processData,h.data=e.data,h.contentType=!1):(h.data=JSON.stringify(e),h.contentType="application/json"),g||b.showWaitScreen(),a.ajax(d,h).always(function(){g||b.hideWaitScreen()})}function f(d,e,f,g){return g||b.showWaitScreen(),a.ajax(d,{type:"PUT",data:JSON.stringify(e),dataType:"json",contentType:"application/json",error:function(a){f||c(a)}}).always(function(){g||b.hideWaitScreen()})}function g(d,e,f,g){return g||b.showWaitScreen(),a.ajax(d,{type:"DELETE",data:JSON.stringify(e),dataType:"json",contentType:"application/json",error:function(a){f||c(a)}}).always(function(){g||b.hideWaitScreen()})}function h(){return a.Deferred().resolve()}return{get:d,post:e,put:f,"delete":g,idle:h}},["UIFactory"])}(jQuery,PlentyFramework),function(a){a.factory("CMSFactory",function(a){function b(b,c){function d(d){return a.get("/rest/"+d.toLowerCase()+"/container_"+b.toLowerCase()+"/",c)}return{from:d}}function c(b,c){function d(d){return a.get("/rest/"+d.toLowerCase()+"/"+b.toLowerCase()+"/",c)}return{from:d}}function d(b){return a.get("/rest/categoryview/categorycontentbody/?categoryID="+b)}return{getContainer:b,getParams:c,getCategoryContent:d}},["APIFactory"])}(PlentyFramework),function(a){a.factory("CheckoutFactory",function(b,c,d){function e(){return l}function f(a){return m&&l||g(!0),a?$.extend(!0,{},l):m}function g(a){return b.get("/rest/checkout/",null,!1,!1,a).done(function(a){a?(l=a.data,m=new e):d.throwError(0,'Could not receive checkout data [GET "/rest/checkout/" receives null value]')})}function h(){return b.put("/rest/checkout",m).done(function(a){a?(l=a.data,m=new e):d.throwError(0,'Could not receive checkout data [GET "/rest/checkout/" receives null value]')})}function i(b){return c.getContainer("checkout"+b).from("checkout").done(function(c){$('[data-plenty-checkout-template="'+b+'"]').each(function(b,d){$(d).html(c.data[0]),a.getInstance().bindDirectives(d),$(window).trigger("contentChanged")})})}function j(b){return c.getCategoryContent(b).done(function(c){$('[data-plenty-checkout-catcontent="'+b+'"]').each(function(b,d){$(d).html(c.data[0]),a.getInstance().bindDirectives(d),$(window).trigger("contentChanged")})})}function k(b){return c.getContainer("itemview"+b).from("itemview").done(function(c){$('[data-plenty-itemview-template="'+b+'"]').each(function(b,d){$(d).html(c.data[0]),a.getInstance().bindDirectives(d),$(window).trigger("contentChanged")})})}var l,m;return{getCheckout:f,setCheckout:h,loadCheckout:g,reloadContainer:i,reloadCatContent:j,reloadItemContainer:k}},["APIFactory","CMSFactory","UIFactory"])}(PlentyFramework),function(a,b){b.factory("ModalFactory",function(){function c(a){return PlentyFramework.partials.Modal.isModal(a)}function d(){return new e}function e(){function d(a){return s.title=a,this}function e(a){return s.cssClass=a,this}function f(a){return s.content=a,this}function g(a){return s.labelConfirm=a,this}function h(a){return s.labelDismiss=a,this}function i(a){return s.onConfirm=a,this}function j(a){return s.onDismiss=a,this}function k(a){return s.container=a,this}function l(a){return s.timeout=a,this}function m(){t=c(s.content)?PlentyFramework.partials.Modal.getModal(s.content):a(PlentyFramework.compileTemplate("modal/modal.html",s)),a(s.container).append(t);var b=a(s.content).filter("script");b.length>0&&b.each(function(b,c){var d=document.createElement("script");d.type="text/javascript",d.innerHTML=a(c).text(),a(s.container).append(d)}),PlentyFramework.partials.Modal.init(t,s),t.find('[data-plenty-modal="confirm"]').click(function(){var a=s.onConfirm();"undefined"==typeof a&&(a=!0),a&&n(!0)}),PlentyFramework.partials.Modal.show(t),s.timeout>0&&o()}function n(a){PlentyFramework.partials.Modal.hide(t),a||s.onDismiss()}function o(){w=s.timeout,x=(new Date).getTime(),u=window.setTimeout(function(){window.clearInterval(v),n()},s.timeout),t.find('[data-plenty-modal="timer"]').text(w/1e3),v=window.setInterval(function(){if(!y){var a=w-(new Date).getTime()+x;a=Math.round(a/1e3),t.find('[data-plenty-modal="timer"]').text(a)}},1e3)}function p(){y=!0,w-=(new Date).getTime()-x,window.clearTimeout(u)}function q(){y=!1,x=(new Date).getTime(),u=window.setTimeout(function(){n(),window.clearInterval(v)},w)}function r(){window.clearTimeout(u),window.clearInterval(v)}var s=this;s.title="",s.cssClass="",s.content="",s.labelDismiss=b.translate("Cancel"),s.labelConfirm=b.translate("Confirm"),s.onConfirm=function(){},s.onDismiss=function(){},s.container="body",s.timeout=-1,s.hide=n,s.startTimeout=o,s.stopTimeout=r,s.pauseTimeout=p,s.continueTimeout=q;var t,u,v,w,x,y=!1;return{setTitle:d,setClass:e,setContent:f,setContainer:k,setLabelConfirm:g,setLabelDismiss:h,onConfirm:i,onDismiss:j,setTimeout:l,show:m,hide:n}}return{prepare:d,isModal:c}})}(jQuery,PlentyFramework),function(a,b){b.factory("UIFactory",function(){function c(a,b){d([{code:a,message:b}])}function d(c){(!i||a("body").has(i).length<=0)&&(i=a(b.compileTemplate("error/errorPopup.html")),a("body").append(i),b.partials.Error.init(i)),a.each(c,function(c,d){b.partials.Error.addError(i,a(b.compileTemplate("error/errorMessage.html",d)))}),b.partials.Error.show(i),f(!0)}function e(){return h=h||0,(!g||a("body").has(g).length<=0)&&(g=a(b.compileTemplate("waitscreen/waitscreen.html")),a("body").append(g)),b.partials.WaitScreen.show(g),h++,h}function f(a){return h--,(0>=h||a)&&(h=0,b.partials.WaitScreen.hide(g)),h}var g,h=0,i=null;return{throwError:c,printErrors:d,showWaitScreen:e,hideWaitScreen:f}})}(jQuery,PlentyFramework),function(a,b){b.service("AddressDoctorService",function(c){function d(b){var c=!0;return b=b||"[data-plenty-address-doctor]",a(b).filter("[data-plenty-address-doctor]:visible").each(function(b,d){var f=new e(d),g=a(d).attr("data-plenty-address-doctor").replace(/\s/g,"").split(",");f.isValid(g)||(c=!1)}),c}function e(c){function d(a){return i()?!0:(j=new f(l.getFormValues()),k=a,e(),1==j.getAddresses().length)}function e(){a(".suggestion-list").remove();for(var b=!1,c=0;cc;c++){var d=a.data[c],f=e(d);f?f.HouseNo.push(d.HouseNo):(d.HouseNo=[d.HouseNo],j.push(d))}})}function e(a){for(var b=j.length,c=0;b>c;c++)if(a.Street==j[c].Street&&j.ZIP==j[c].ZIP&&a.City==j[c].City)return j[c];return null}function f(){return j}function g(b){for(var c=[],d=j.length,e=0;d>e;e++){var f=j[e];a.inArray(f[b],c)<0&&c.push(f[b])}return c}function h(a){for(var b=[],c=j.length,d=0;c>d;d++){var e=j[d];(a.Street&&a.Street==e.Street||a.ZIP&&a.ZIP==e.ZIP||a.City&&a.City==e.City)&&b.push(e)}j=b}function i(a){a=parseInt(a);for(var b=j.length,c=0;b>c;c++)for(var d=j[c],e=0;e=f[0]&&a<=f[1])return!0}return!1}var j=[];return d(),{getAddresses:f,getList:g,filter:h,houseNoAllowed:i}}return{validateAddress:d}},["APIFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("AuthenticationService",function(c,d,e){function f(){var b=a('[data-plenty-checkout="lostPasswordForm"]');if(b.validateForm()){var d=b.getFormValues(),e={Email:d.Email};return c.post("/rest/checkout/lostpassword/",e).done(function(b){1==b.data.IsMailSend&&(a('[data-plenty-checkout="lostPasswordTextContainer"]').hide(),a('[data-plenty-checkout="lostPasswordSuccessMessage"]').show())})}}function g(a){if(a.validateForm()){var b=a.getFormValues(),d={Email:b.loginMail,Password:b.loginPassword};return e.showWaitScreen(),c.post("/rest/checkout/login/",d).done(function(){window.location.assign(a.attr("action"))})}}function h(a){return c.post("/rest/checkout/customerinvoiceaddress/",a).done(function(a){d.getCheckout().CustomerInvoiceAddress=a.data})}function i(){var c=a('[data-plenty-checkout-form="customerRegistration"]');if(c.validateForm()&&b.getInstance().AddressDoctorService.validateAddress()){var d=c.getFormValues(),e={LoginType:2,FormOfAddressID:d.FormOfAddressID,Company:d.Company,FirstName:d.FirstName,LastName:d.LastName,Street:d.Street,HouseNo:d.HouseNo,AddressAdditional:d.AddressAdditional,ZIP:d.ZIP,City:d.City,CountryID:d.CountryID,VATNumber:d.VATNumber,Email:d.Email,EmailRepeat:d.EmailRepeat,BirthDay:d.BirthDay,BirthMonth:d.BirthMonth,BirthYear:d.BirthYear,Password:d.Password,PasswordRepeat:d.PasswordRepeat,PhoneNumber:d.PhoneNumber,MobileNumber:d.MobileNumber,FaxNumber:d.FaxNumber,Postnummer:d.Postnummer};return e.CustomerPropertiesList=e.CustomerPropertiesList||[],c.find("[data-plenty-property-id]").each(function(b,c){e.CustomerPropertiesList.push({PropertyID:a(c).attr("data-plenty-property-id"),PropertyValue:a(c).val()})}),h(e).done(function(){window.location.assign(c.attr("action"))})}}return{resetPassword:f,customerLogin:g,setInvoiceAddress:h,registerCustomer:i}},["APIFactory","CheckoutFactory","UIFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("BasketService",function(c,d,e,f,g){function h(d){d&&c.get("/rest/checkout/container_"+"CheckoutOrderParamsList".toLowerCase()+"/",{itemID:d[0].BasketItemItemID,quantity:d[0].BasketItemQuantity},!1,!0).done(function(c){c.data[0].indexOf("form-group")>0?g.prepare().setContent(c.data[0]).setTitle(b.translate("Select order parameters")).setLabelConfirm(b.translate("Save")).onConfirm(function(){return a('[data-plenty-checkout-form="OrderParamsForm"]').validateForm()?(j(i(d)),!0):!1}).show():j(d)})}function i(b){var c,d=a('[data-plenty-checkout-form="OrderParamsForm"]'),e={},f="";return d.find('[name^="ParamGroup"]').each(function(){c=this.name.match(/^ParamGroup\[(\d+)]\[(\d+)]$/),b=m(b,c[1],a(this).val(),a(this).val())}),d.find('[name^="ParamValue"]').each(function(){if(e=a(this),f=e.attr("type"),("checkbox"==f&&e.is(":checked")||"radio"==f&&e.is(":checked")||"radio"!=f&&"checkbox"!=f)&&"file"!=f&&"hidden"!=f){var c=e[0].name.match(/^ParamValue\[(\d+)]\[(\d+)]$/);b=m(b,c[1],c[2],e.val())}else if("file"==f)if(e[0].files&&e[0].files.length>0)b=l(e,b);else{var c=e[0].name.match(/^ParamValueFile\[(\d+)]\[(\d+)]$/),d=a('input[type="hidden"][name="ParamValue['+c[1]+"]["+c[2]+']"]').val();b=m(b,c[1],c[2],d)}}),b}function j(a){c.post("/rest/checkout/basketitemslist/",a,!0).done(function(){f.loadCheckout().done(function(){s(),e.getContainer("ItemViewItemToBasketConfirmationOverlay",{ArticleID:a[0].BasketItemItemID}).from("ItemView").done(function(a){g.prepare().setContent(a.data[0]).setTimeout(5e3).show()})})}).fail(function(a){d.printErrors(JSON.parse(a.responseText).error.error_stack)})}function k(a){c.put("/rest/checkout/basketitemslist/",a).done(function(){f.reloadCatContent(b.getGlobal("basketCatID")),f.loadCheckout().done(function(){s()})})}function l(a,b){var d,e,f=a[0].id,g={},h=[],i={type:"POST",data:{},isFile:!0,cache:!1,dataType:"json",processData:!1,contentType:!1};g[f]=a[0].files,-1==h.indexOf(f)&&h.push(f);for(var j=0,k=h.length;k>j;++j)d=new FormData,e=g[h[j]],d.append("0",e[0],e[0].name),i.data=d,c.post("/rest/checkout/orderparamfile/",i);var l=a[0].name.match(/^ParamValueFile\[(\d+)]\[(\d+)]$/);return m(b,l[1],l[2],a.val())}function m(b,c,d,e){return c>0&&void 0==b[c]&&(b[c]=a.extend(!0,{},b[0]),b[c].BasketItemOrderParamsList=[]),void 0!=b[c]&&(b[c].BasketItemQuantity=1,void 0==b[c].BasketItemOrderParamsList&&(b[c].BasketItemOrderParamsList=[]),e&&b[c].BasketItemOrderParamsList.push({BasketItemOrderParamID:d,BasketItemOrderParamValue:e})),b}function n(b){var c=a('[data-plenty-basket-item="'+b+'"');c.modal("show"),c.find('[data-plenty-modal="confirm"]').on("click",function(){var d=p(b),e=[];c.find('select, .PlentyFormContainer.AttrImage > input[type="hidden"]').each(function(b,c){var d=c.name.match(/^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/);d&&d[1]&&e.push({BasketItemAttributeID:d[1],BasketItemAttributeValueID:a(c).val()})}),0!=e.length&&(d.BasketItemAttributesList=e),k([d])})}function o(d){var e=p(d);e.BasketItemOrderParamsList=[],c.get("/rest/checkout/container_"+"CheckoutOrderParamsList".toLowerCase()+"/",{itemID:e.BasketItemItemID,quantity:e.BasketItemQuantity,basketItemID:d}).done(function(c){g.prepare().setContent(c.data[0]).setTitle(b.translate("Edit order parameters")).setLabelConfirm(b.translate("Save")).onConfirm(function(){return a('[data-plenty-checkout-form="OrderParamsForm"]').validateForm()?(k(i([e])),!0):!1}).show()})}function p(a){for(var b=f.getCheckout().BasketItemsList,c=0;c"+b.translate('Do you really want to remove "{{item}}" from your basket?',{item:j})+"

    ").onDismiss(function(){i.reject()}).onConfirm(function(){h()}).setLabelConfirm(b.translate("Delete")).show(),i}function r(d,e){if(0>=e)return q(d);for(var g,h,i=a.Deferred(),j=f.getCheckout().BasketItemsList,k=0;k0&&f.reloadContainer("Totals")}return{addItem:h,removeItem:q,getItem:p,setItemQuantity:r,editItemAttributes:n,editOrderParams:o,addCoupon:t,removeCoupon:u}},["APIFactory","UIFactory","CMSFactory","CheckoutFactory","ModalFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("CheckoutService",function(c,d,e,f){function g(){e.loadCheckout(!0)}function h(){var b=a('[data-plenty-checkout-form="details"]'),d=b.getFormValues();return e.getCheckout().CheckoutCustomerSign||(e.getCheckout().CheckoutCustomerSign=""),e.getCheckout().CheckoutOrderInfoText||(e.getCheckout().CheckoutOrderInfoText=""),e.getCheckout().CheckoutCustomerSign!==d.CustomerSign&&a(b).find('[name="CustomerSign"]').length>0||e.getCheckout().CheckoutOrderInfoText!==d.OrderInfoText&&a(b).find('[name="OrderInfoText"]').length>0?(e.getCheckout().CheckoutCustomerSign=d.CustomerSign,e.getCheckout().CheckoutOrderInfoText=d.OrderInfoText,e.setCheckout()):c.idle()}function i(d){var f=a('[data-plenty-checkout-form="shippingAddress"]');if(!d&&!f.validateForm())return!1;if(!d&&!b.getInstance().AddressDoctorService.validateAddress(f))return!1;var g=f.getFormValues(),h=a('[name="shippingAddressID"]:checked').val();if(a("#shippingAdressSelect").modal("hide"),0>h){var i=g;return k(i,e.getCheckout().CustomerShippingAddress)?c.idle():("PACKSTATION"==i.Street?(i.isPackstation=1,i.PackstationNo=i.HouseNo):"POSTFILIALE"==i.Street&&(i.IsPostfiliale=1,i.PostfilialNo=i.HouseNo),c.post("/rest/checkout/customershippingaddress/",i).done(function(a){e.getCheckout().CheckoutCustomerShippingAddressID=a.data.ID,e.getCheckout().CheckoutShippingCountryID=a.data.CountryID,delete e.getCheckout().CheckoutMethodOfPaymentID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList"),e.reloadContainer("ShippingProfilesList"),2==e.getCheckout().CustomerInvoiceAddress.LoginType&&e.reloadContainer("CustomerShippingAddress")})}))}return h!=e.getCheckout().CheckoutCustomerShippingAddressID?(e.getCheckout().CheckoutCustomerShippingAddressID=h,delete e.getCheckout().CheckoutMethodOfPaymentID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList"),e.reloadContainer("ShippingProfilesList"),2==e.getCheckout().CustomerInvoiceAddress.LoginType&&e.reloadContainer("CustomerShippingAddress")})):c.idle()}function j(){var b=a('[data-plenty-checkout-form="guestRegistration"]'),d=b.getFormValues();return d.LoginType=1,d.CustomerPropertiesList=d.CustomerPropertiesList||[],b.find("[data-plenty-property-id]").each(function(b,c){d.CustomerPropertiesList.push({PropertyID:a(c).attr("data-plenty-property-id"),PropertyValue:a(c).val()})}),k(d,e.getCheckout().CustomerInvoiceAddress)?i():c.post("/rest/checkout/customerinvoiceaddress/",d).done(function(a){i().done(function(){e.loadCheckout()})})}function k(a,b){for(var c in a)if(a[c]+""!=b[c]+""&&"EmailRepeat"!==c)return!1;return!0}function l(){var b=a('[data-plenty-checkout-form="shippingProfileSelect"]').getFormValues();return e.getCheckout().CheckoutShippingProfileID=b.ShippingProfileID,delete e.getCheckout().CheckoutCustomerShippingAddressID,delete e.getCheckout().CheckoutMethodOfPaymentID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList")})}function m(){return c.post("/rest/checkout/preparepayment/",null).done(function(b){if(""!=b.data.CheckoutMethodOfPaymentRedirectURL)document.location.assign(b.data.CheckoutMethodOfPaymentRedirectURL);else if(b.data.CheckoutMethodOfPaymentAdditionalContent){var c=a(b.data.CheckoutMethodOfPaymentAdditionalContent).find('[data-plenty-checkout-form="bankDetails"]').length>0;f.prepare().setContent(b.data.CheckoutMethodOfPaymentAdditionalContent).onConfirm(function(){return c?p():r()}).show()}})}function n(b){return b=b||a('[data-plenty-checkout-form="methodOfPayment"]').getFormValues().MethodOfPaymentID,e.getCheckout().CheckoutMethodOfPaymentID=b,delete e.getCheckout().CheckoutCustomerShippingAddressID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("ShippingProfilesList")})}function o(){d.getContainer("CheckoutPaymentInformationBankDetails").from("Checkout").done(function(b){f.prepare().setContent(b.data[0]).onDismiss(function(){a('input[name="MethodOfPaymentID"]').each(function(b,c){a(c).val()==e.getCheckout().CheckoutMethodOfPaymentID?a(c).attr("checked","checked"):a(c).removeAttr("checked")})}).onConfirm(function(){return p()}).show()})}function p(){var b=a('[data-plenty-checkout-form="bankDetails"]');if(b.validateForm()){var d=b.getFormValues().checkout.customerBankDetails,f={CustomerBankName:d.bankName,CustomerBLZ:d.blz,CustomerAccountNumber:d.accountNo,CustomerAccountOwner:d.accountOwner,CustomerIBAN:d.iban,CustomerBIC:d.bic};return c.post("/rest/checkout/paymentinformationbankdetails/",f).done(function(){e.loadCheckout().done(function(){n(3),e.reloadContainer("MethodsOfPaymentList")})}),!0}return!1}function q(){d.getContainer("CheckoutPaymentInformationCreditCard").from("Checkout").done(function(b){f.prepare().setContent(b.data[0]).onDismiss(function(){a('input[name="MethodOfPaymentID"]').each(function(b,c){a(c).val()==e.getCheckout().CheckoutMethodOfPaymentID?a(c).attr("checked","checked"):a(c).removeAttr("checked")})}).onConfirm(function(){return r()}).show()})}function r(){var b=a('[data-plenty-checkout-form="creditCard"]');if(b.validateForm()){var d=b.getFormValues().checkout.paymentInformationCC,f={Owner:d.owner,Cvv2:d.cvv2,Number:d.number,Year:d.year,Month:d.month,Provider:d.provider};return c.post("/rest/checkout/paymentinformationcreditcard/",f).done(function(){e.loadCheckout()}),!0}return!1}function s(b){if(2==e.getCheckout().CustomerInvoiceAddress.LoginType)var c=a('[data-plenty-checkout-form="shippingAddress"]').getFormValues();else var c=a('[data-plenty-checkout-form="guestRegistration"]').getFormValues();var g={street:c.Street,houseNo:c.HouseNo,ZIP:c.ZIP,city:c.City,postnummer:c.Postnummer,suggestionType:"postfinder"};d.getContainer("CheckoutAddressSuggestionResultsList",g).from("Checkout").done(function(a){f.prepare().setContent(a.data[0]).show()})}function t(){var b=a('[data-plenty-checkout-form="placeOrder"]');if(b.validateForm()){var d=b.getFormValues(),e={TermsAndConditionsCheck:d.termsAndConditionsCheck||0,WithdrawalCheck:d.withdrawalCheck||0,PrivacyPolicyCheck:d.privacyPolicyCheck||0,AgeRestrictionCheck:d.ageRestrictionCheck||0,NewsletterCheck:d.newsletterCheck||0,KlarnaTermsAndConditionsCheck:d.klarnaTermsAndConditionsCheck||0,PayoneDirectDebitMandateCheck:d.payoneDirectDebitMandateCheck||0,PayoneInvoiceCheck:d.payoneInvoiceCheck||0};return c.post("/rest/checkout/placeorder/",e).done(function(a){""!=a.data.MethodOfPaymentRedirectURL?window.location.assign(a.data.MethodOfPaymentRedirectURL):""!=a.data.MethodOfPaymentAdditionalContent?f.prepare().setContent(a.data.MethodOfPaymentAdditionalContent).setLabelDismiss("").onDismiss(function(){window.location.assign(b.attr("action"))}).onConfirm(function(){window.location.assign(b.attr("action"))}).show():window.location.assign(b.attr("action"))})}}return{init:g,setCustomerSignAndInfo:h,registerGuest:j,setShippingProfile:l,saveShippingAddress:i,loadAddressSuggestion:s,preparePayment:m,setMethodOfPayment:n,editBankDetails:o,editCreditCard:q,placeOrder:t}},["APIFactory","CMSFactory","CheckoutFactory","ModalFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("MediaSizeService",function(){function b(){return e&&c(),e}function c(){var b;if(b=window.matchMedia?window.matchMedia("(min-width:1200px)").matches?"lg":window.matchMedia("(min-width:992px)").matches?"md":window.matchMedia("(min-width:768px)").matches?"sm":"xs":a(window).width()>=1200?"lg":a(window).width()>=992?"md":a(window).width()>=768?"sm":"xs",b!=e){var c=e;e=b,a(window).trigger("sizeChange",[e,c])}}function d(a){for(var b=a.replace(/\s/g,"").split(","),c=0;c li'),r=a('[data-plenty-checkout="container"] > div'),u=a('[data-plenty-checkout="next"]'),t=a('[data-plenty-checkout="prev"]'),q.length==r.length&&r.length>0){d.getCheckout();r.hide(),q.each(function(b,c){a(c).addClass("disabled"),a(c).click(function(){a(this).is(".disabled")||j(b)})}),u.attr("disabled","disabled"),u.click(function(){m()}),t.attr("disabled","disabled"),t.click(function(){n()}),window.addEventListener("hashchange",function(){window.location.hash.length>0?o(window.location.hash):j(0)},!1),a.urlParam=function(a){var b=new RegExp("[?&]"+a+"=([^&#]*)").exec(window.location.href);return null==b?null:b[1]||0};var c=a.urlParam("gototab");0==window.location.hash.length&&c&&a('[data-plenty-checkout-id="'+c+'"]').length>0?window.location.hash=c:j(!o(window.location.hash)&&s>=0?s:0),p(),a(window).on("sizeChange",p),a(window).resize(function(){"xs"==b.getInstance().MediaSizeService.interval()&&p()})}}function f(){return s>=0?{id:a(r[s]).attr("data-plenty-checkout-id"),index:s}:null}function g(a){return v.beforeChange.push(a),b.getInstance().NavigatorService}function h(a){return v.afterChange.push(a),b.getInstance().NavigatorService}function i(b,c){var d=!0;if(s>=0||"afterChange"===b){var e=f(),g={index:c,id:a(r[c]).attr("data-plenty-checkout-id")};a.each(v[b],function(a,b){return b(e,g)===!1?(d=!1,!1):void 0})}return d}function j(e,f){var g=s!==e;(!g||f||i("beforeChange",e))&&(s=e,!Object.equals(w[s],d.getCheckout(!0))&&g&&a(r[s]).attr("data-plenty-checkout-content")?(w[s]=d.getCheckout(!0),c.getCategoryContent(a(r[s]).attr("data-plenty-checkout-content")).done(function(c){a(r[s]).html(c.data[0]),k(g),b.getInstance().bindDirectives(r[s]),a(window).trigger("contentChanged")})):k(g))}function k(b){a(r).hide(),a(q).each(function(b,c){a(c).removeClass("disabled active"),a(c).find('[role="tab"]').attr("aria-selected","false"),s>b?a(c).addClass("visited"):b==s?(a(c).addClass("active visited"),a(c).find('[role="tab"]').attr("aria-selected","true")):b>s&&!a(c).is(".visited")&&a(c).addClass("disabled")}),p(),0>=s?a(t).attr("disabled","disabled"):a(t).removeAttr("disabled"),s+1==q.length?a(u).attr("disabled","disabled"):a(u).removeAttr("disabled"),a(r[s]).show(),s>0?window.location.hash=a(r[s]).attr("data-plenty-checkout-id"):window.location.hash.length>0&&(window.location.hash=""),b&&i("afterChange",s)}function l(a){j(a.index,!0)}function m(){s0&&j(s-1)}function o(b){return"next"==b?(m(),!0):"prev"==b?(n(),!0):(b=b.replace("#",""),a(r).each(function(c,d){return a(d).attr("data-plenty-checkout-id")==b?(j(c),!0):void 0}),!1)}function p(){var b=q.length;if(!(0>=b)){a(q).removeAttr("style"),a(q).children("span").removeAttr("style"),a(u).removeAttr("style"),a(t).removeAttr("style");var c=a(t).outerWidth()c?a(d).children("span").css({paddingLeft:g+"px",paddingRight:h+"px"}):a(d).children("span").css({paddingLeft:j+"px",paddingRight:k+"px"})})}}var q=[],r=[],s=-1,t={},u={},v={beforeChange:[],afterChange:[]},w=[];return{init:e,getCurrentContainer:f,goTo:j,beforeChange:g,afterChange:h,continueChange:l,next:m,previous:n,goToID:o,fillNavigation:p}},["CMSFactory","CheckoutFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("PostfinderService",function(c,d,e){function f(){var b=a('input[name="Street"]').val();return"PACKSTATION"==b.toUpperCase()||"POSTFILIALE"==b.toUpperCase()}function g(){j={PostfinderItemStreet:a('input[name="Street"]','[data-plenty-checkout-form="shippingAddress"]'),PostfinderItemZIP:a('input[name="ZIP"]','[data-plenty-checkout-form="shippingAddress"]'),PostfinderItemCity:a('input[name="City"]','[data-plenty-checkout-form="shippingAddress"]'),PostfinderItemHouseNo:a('input[name="HouseNo"]','[data-plenty-checkout-form="shippingAddress"]')},j.PostfinderItemStreet.val(""),j.PostfinderItemZIP.val().length>2||j.PostfinderItemCity.val().length>2?c.get("/rest/checkout/shippingaddresspostfinderlist/",{suggestionType:"postfinder",zip:j.PostfinderItemZIP.val(),city:j.PostfinderItemCity.val()}).done(function(c){l=c.data,k=l.length,0==k&&h();for(var e={addresses:[]},f=0;k>f;f++){var g="km",m=l[f].PostfinderItemDistance,n=m/1e3;n=(Math.round(100*n)/100).toFixed(2).replace(".",","),1e3>m&&(n=m,g="m"),e.addresses.push({index:f,dimension:g,type:l[f].PostfinderItemIsPackstation?"Packstation":"Postfiliale",number:l[f].PostfinderItemIsPackstation?l[f].PostfinderItemPackstationNo:l[f].PostfinderItemPostfilialNo,street:l[f].PostfinderItemStreet,houseNo:l[f].PostfinderItemHouseNo,zip:l[f].PostfinderItemZIP,city:l[f].PostfinderItemCity,district:l[f].PostfinderItemDistrict,distance:n,remark:l[f].PostfinderItemRemark})}var o=b.compileTemplate("addressSuggestions/postFinder.html",e);d.prepare().setTitle(b.translate("Packstations and post offices in your area")).setContent(o).setClass("checkout").onConfirm(function(){return j.PostfinderItemCity.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemCity.attr("id")+'"]').removeClass("has-error").addClass("has-success"),j.PostfinderItemZIP.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemZIP.attr("id")+'"]').removeClass("has-error").addClass("has-success"),j.PostfinderItemStreet.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemStreet.attr("id")+'"]').removeClass("has-error").addClass("has-success"),j.PostfinderItemHouseNo.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemHouseNo.attr("id")+'"]').removeClass("has-error").addClass("has-success"),i=a('input[type="radio"][name="postfinder"]:checked').val(),l[i].PostfinderItemIsPackstation?(a(j.PostfinderItemStreet).val("PACKSTATION"),a(j.PostfinderItemHouseNo).val(l[i].PostfinderItemPackstationNo)):(a(j.PostfinderItemStreet).val("POSTFILIALE"),a(j.PostfinderItemHouseNo).val(l[i].PostfinderItemPostfilialNo)),a(j.PostfinderItemStreet).trigger("change"),a(j.PostfinderItemCity).val(l[i].PostfinderItemCity),a(j.PostfinderItemZIP).val(l[i].PostfinderItemZIP),!0}).show()}):h()}function h(){e.throwError(0,b.translate("Please enter a ZIP code and/or a city.")),j.PostfinderItemCity.removeClass("has-success").addClass("has-error"),a('label[for="'+j.PostfinderItemCity.attr("id")+'"]').removeClass("has-success").addClass("has-error"),j.PostfinderItemZIP.removeClass("has-success").addClass("has-error"),a('label[for="'+j.PostfinderItemZIP.attr("id")+'"]').removeClass("has-success").addClass("has-error"),j.PostfinderItemCity.focus(function(){a(this).removeClass("has-error");var b=a(this).attr("id");a(this).closest(".form-group").find('[for="'+b+'"]').removeClass("has-error")}),j.PostfinderItemZIP.focus(function(){a(this).removeClass("has-error");var b=a(this).attr("id");a(this).closest(".form-group").find('[for="'+b+'"]').removeClass("has-error")})}var i="",j={},k={},l={};return{openPostfinderModal:g,isPackstation:f}},["APIFactory","ModalFactory","UIFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("SocialShareService",function(){function b(a){var b={"facebook-like":'',"facebook-recommend":'',twitter:'',"google-plus":'
    '};return b[a]}function c(){var b=document.location.href,c=a("link[rel=canonical]").attr("href");return c&&c.length>0&&(c.indexOf("http")<0&&(c=document.location.protocol+"//"+document.location.host+c),b=c),b}function d(b){var c=a('meta[name="'+b+'"]').attr("content");return c||""}function e(){var b=d("DC.title"),c=d("DC.creator");return b.length>0&&c.length>0?b+=" - "+c:b=a("title").text(),encodeURIComponent(b)}return"undefined"==typeof socialLangLocale&&(socialLangLocale="en_US"),"undefined"==typeof socialLang&&(socialLang="en"),{getSocialService:b}})}(jQuery,PlentyFramework),function($,pm){pm.service("ValidationService",function(){function getFormControl(a){return a=$(a),a.is("input")||a.is("select")||a.is("textarea")?a:a.find("input").length>0?a.find("input"):a.find("select").length>0?a.find("select"):a.find("textarea").length>0?a.find("textarea"):null}function validateText(a){return a.is("input")||a.is("select")||a.is("textarea")?$.trim(a.val()).length>0:(console.error("Validation Error: Cannot validate Text for <"+a.prop("tagName")+">"),!1)}function validateMail(a){var b=/[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;return validateText(a)?b.test($.trim(a.val())):!1}function validateNumber(a){return validateText(a)?$.isNumeric($.trim(a.val())):!1}function validateValue(a,b){return $(b).length>0?$.trim(a.val())==$.trim($(b).val()):$.trim(a.val())==b}function visibility(a){return a.is(":visible")}function isEnabled(a){return a.is(":enabled")}function validate(form,errorClass){var formControl,formControls,validationKey,currentHasError,group,checked,checkedMin,checkedMax,attrValidate,validationKeys,formControlAttrType,$form=$(form);errorClass=errorClass||"has-error";var missingFields=[],hasError=!1;$form.find("[data-plenty-validate], input.Required").each(function(i,elem){attrValidate=$(elem).attr("data-plenty-validate"),formControls=getFormControl(elem),validationKeys=attrValidate?attrValidate:"text",validationKeys=validationKeys.split(",");for(var i=0,length=formControls.length;length>i;i++){if(formControl=$(formControls[i]),formControlAttrType=formControl.attr("type"),!visibility(formControl)||!isEnabled(formControl))return;if(validationKey=validationKeys[i].trim()||validationKeys[0].trim(),currentHasError=!1,formControl.is("input")&&"radio"!=formControlAttrType&&"checkbox"!=formControlAttrType||formControl.is("textarea"))switch(validationKey){case"text":currentHasError=!validateText(formControl);break;case"mail":currentHasError=!validateMail(formControl);break;case"number":currentHasError=!validateNumber(formControl);break;case"value":currentHasError=!validateValue(formControl,$(elem).attr("data-plenty-validation-value"));break;case"none":break;default:console.error('Form validation error: unknown validate property: "'+attrValidate+'"')}else if(!formControl.is("input")||"radio"!=formControlAttrType&&"checkbox"!=formControlAttrType){if(!formControl.is("select"))return void console.error("Form validation error: "+$(elem).prop("tagName")+" does not contain an form element");currentHasError=""==formControl.val()||"-1"==formControl.val()}else group=formControl.attr("name"),checked=$form.find('input[name="'+group+'"]:checked').length,"radio"==formControlAttrType?(checkedMin=1,checkedMax=1):(eval("var minMax = "+attrValidate),checkedMin=minMax?minMax.min:1,checkedMax=minMax?minMax.max:1),currentHasError=checkedMin>checked||checked>checkedMax;currentHasError&&(hasError=!0,missingFields.push(formControl),formControls.length>1?(formControl.addClass(errorClass),$form.find('label[for="'+formControl.attr("id")+'"]').addClass(errorClass)):$(elem).addClass(errorClass))}}),$form.on("validationFailed",function(){var a=50,b=$form.find("."+errorClass).first(),c=b.offset().top,d=$("html, body");$form.parents(".modal").length>0?(d=$form.parents(".modal").find(".modal-body"),c=d.scrollTop()-(d.offset().top-b.offset().top)):$form.is(".modal")&&(d=$form.find(".modal-body"),c=d.scrollTop()-(d.offset().top-b.offset().top)),(c-awindow.pageYOffset+window.innerHeight)&&d.animate({scrollTop:c-a})}),hasError&&($form.find("."+errorClass).each(function(a,b){formControl=$(getFormControl(b)),formControl.on("focus click",function(){var a=$(b);a.removeClass(errorClass),$form.find('label[for="'+$(this).attr("id")+'"]').removeClass(errorClass)})}),$form.trigger("validationFailed",[missingFields]));var callback=$form.attr("data-plenty-callback");if(!hasError&&callback&&"submit"!=callback&&"function"==typeof window[callback]){var fields={};return $form.find("input, textarea, select").each(function(){"checkbox"==$(this).attr("type")?fields[$(this).attr("name")]=$(this).is(":checked"):fields[$(this).attr("name")]=$(this).val()}),window[callback](fields),!1}return!hasError}return{validate:validate}}),$.fn.validateForm=function(){return pm.getInstance().ValidationService.validate(this)},$.fn.getFormValues=function(){function a(a,b){var d=a.match(/^([^\[]+)(.*)/);if(d[2]){var e,f=/\[([^\]]+)]/g,g=[];for(g[0]=d[1];null!==(e=f.exec(d[2]));)g.push(e[1]);for(var h=g.length-1;h>=0;h--){var i={};i[g[h]]=b,b=i}c=$.extend(!0,c,b)}else c[d[1]]=b}var b=this,c={};return b.find("input, select, textarea").each(function(c,d){if($(d).attr("name"))if("checkbox"==$(d).attr("type")){var e=[];$(b).find('[name="'+$(d).attr("name")+'"]:checked').each(function(a,b){e.push($(b).val())}),a($(d).attr("name"),e)}else"radio"==$(d).attr("type")?$(d).is(":checked")&&a($(d).attr("name"),$(d).val()):a($(d).attr("name"),$(d).val())}),c}}(jQuery,PlentyFramework),function(a,b){b.directive("Authentication",function(c){function d(d){b.getRecentEvent().preventDefault(),c.customerLogin(a(d))}return{login:d}},["AuthenticationService"])}(jQuery,PlentyFramework),function(a,b){b.directive("Basket",function(c){function d(d){b.getRecentEvent().preventDefault();var e={},f=a(d),g=f.parents("form");e.BasketItemItemID=g.find('[name="ArticleID"]').val(),e.BasketItemPriceID=g.find('[name="SYS_P_ID"]').val(),e.BasketItemQuantity=g.find('[name="ArticleQuantity"]').val(),e.BasketItemBranchID=g.find('[name="source_category"]').val();var h=g.find('[name^="ArticleAttribute"]'),i=[];a.each(h,function(b,c){var d=c.name.match(/^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/);d&&d[1]&&i.push({BasketItemAttributeID:d[1],BasketItemAttributeValueID:a(c).val()})}),0!=i.length&&(e.BasketItemAttributesList=i),c.addItem([e])}function e(b,c){var d=a(b),e=d.parent().find("input"),f=parseInt(e.attr("maxlength"))||5,g=parseInt(e.val())+c,h=d.parents("[data-basket-item-id]").length>0;if(h){(g+"").length<=f&&g>=0&&e.val(g);var i=d.data("timeout");i&&window.clearTimeout(i),i=window.setTimeout(function(){e.trigger("change")},1e3),d.data("timeout",i)}else(g+"").length<=f&&g>=1&&e.val(g)}function f(b,d){c.setItemQuantity(b,parseInt(a(d).val())).fail(function(){var e=c.getItem(b);a(d).val(e.BasketItemQuantity)})}return{addBasketItem:d,changeItemQuantity:e,setItemQuantity:f}},["BasketService"])}(jQuery,PlentyFramework),function(a,b){b.directive("MobileDropdown",function(c){function d(){a(window).on("orientationchange sizeChange",function(){e(h)}),a("html").click(function(a){e(i)})}function e(b){for(var c=0;c0?window.location.assign(a(c).attr("href")):window.location.assign(c))}function e(a){c.goToID(a)}return{to:d,toCheckoutTab:e}},["MediaSizeService","NavigatorService"])}(jQuery,PlentyFramework),function(a,b){b.directive("Tab",function(c){function d(b){a(b).tab("show")}function e(a,b,c){j[c]||(j[c]=new h),j[c].getTab(b)||j[c].addTab(b),j[c].getTab(b).addLabel(a)}function f(a,b,c){j[c]||(j[c]=new h),j[c].getTab(b)||j[c].addTab(b),j[c].getTab(b).setContent(a)}function g(a,d,e){c.isInterval(e)&&(b.getRecentEvent().preventDefault(),j[d]&&j[d].getTab(a)&&j[d].showTab(a))}function h(){function b(a){return g[a]=new i(a),g[a]}function c(b){var c=0;if(f)c=parseInt(f.getContent().parent().css("zIndex")),f.hide(),f.getContent().parent().css("zIndex",c-1);else{for(var d in g)if(g[d].getContent()){var h=parseInt(g[d].getContent().parent().css("zIndex"));(0==c||c>h)&&(c=h),g[d].hide()}for(var d in g)g[d].getContent()&&g[d].getContent().parent().css("zIndex",c-1);a(window).on("sizeChange",e)}f=g[b],f.getContent().parent().css("zIndex",c),f.show()}function d(a){return g[a]}function e(){for(var a in g)g[a].getContent()&&g[a].show();f=null}var f,g={};return{addTab:b,showTab:c,getTab:d,resetTabs:e}}function i(a){function b(){return j}function c(a){return i.push(a),this}function d(a){return h=a,this}function e(){return h}function f(){for(var a=0;a0&&(a(document).scrollTop()>100?t(v,"addClass","visible"):t(v,"removeClass","visible"))})}function f(b){a(b).owlCarousel({navigation:!0,navigationText:!1,slideSpeed:1e3,paginationSpeed:1e3,singleItem:!0,autoPlay:6e3,stopOnHover:!0,afterMove:function(b){a(b).find('img[data-plenty-rel="lazyload"]').trigger("appear")}})}function g(b,d,e){var f=a(b),g=0,h={},i=f.find('[data-plenty-rel="equal-target"]').length>0?f.find('[data-plenty-rel="equal-target"]'):f.children();e!==!0&&u.push(b);for(var j=i.length;j>=0;j--)h=a(i[j]),h.css("height",""),h.outerHeight(!0)>g&&(g=h.outerHeight(!0));(!d||c.isInterval(d))&&i.height(g)}function h(b){var c=a(b);c.click(function(){return a("html, body").animate({scrollTop:0},400),!1}),a.inArray(c,v)&&v.push(c)}function i(b,c){var d=a(b);d.lazyload({effect:c}),d.on("loaded",function(){d.css("display","inline-block")})}function j(b){console.log(b);var c=a(b),d=c.parent();d.addClass("animating"),c.siblings("ul").slideToggle(200,function(){d.is(".open")?d.removeClass("open"):d.addClass("open"),c.siblings("ul").removeAttr("style"),d.removeClass("animating")})}function k(b,c){var d=a(b),e=a(d.attr("data-plenty-rel"));if(d.is('input[type="radio"]')){var f=a('input[type="radio"][name="'+d.attr("name")+'"]'),g=!c||"checked"==c;f.change(function(){var b=a(this);e.parents('[data-plenty-rel="equal-target"]').css("height","auto"),b.is(":checked")&&b[0]===d[0]&&1==g?e.slideDown(400,function(){s()}):e.slideUp(400,function(){s()})})}else d.click(function(){d.addClass("animating"),e.slideToggle(400,function(){d.removeClass("animating"),d.toggleClass("active"),s()})})}function l(b,c){c=c||400,a(b).parents('[data-plenty-rel="equal-target"]').css("height","auto"),a(b).slideDown(c,function(){s()})}function m(b,c){c=c||400,a(b).parents('[data-plenty-rel="equal-target"]').css("height","auto"),a(b).slideUp(c,function(){s()})}function n(b,c){c=c||400,a(b).parents('[data-plenty-rel="equal-target"]').css("height","auto"),a(b).slideToggle(c,function(){s()})}function o(b,c){var e=a(b),f=e.find('[data-plenty-rel="social-switch"]');e.append(''),f.is("off, on")||f.addClass("off"),f.on("click",function(){f.hasClass("off")&&("tooltip"==e.attr("data-toggle")&&e.tooltip("destroy"),f.removeClass("off").addClass("on"),e.find('[data-plenty-rel="social-placeholder"]').hide(),e.find(".social-container").append(d.getSocialService(c)))})}function p(d,e,f){if(e&&d&&(!f||c.isInterval(f))){var g=b.getRecentEvent();return g&&g.preventDefault(),a(e).toggleClass(d),!1}}function q(d,e,f){if(e&&d&&(!f||c.isInterval(f))){var g=b.getRecentEvent();return g&&g.preventDefault(),a(e).addClass(d),!1}}function r(d,e,f){if(e&&d&&(!f||c.isInterval(f))){var g=b.getRecentEvent();return g&&g.preventDefault(),a(e).removeClass(d),!1}}function s(){for(var a=u.length-1;a>=0;a--)g(u[a],"",!0)}function t(a,b,c){for(var d=a.length-1;d>=0;d--)a[d][b](c)}var u=[],v=[];return{initUIWindowEvents:e,addContentPageSlider:f,equalHeight:g,initToTop:h,initLazyload:i,initSlideToggle:k,slideDown:l,slideUp:m,slideToggle:n,toggleHideShow:j,toggleSocialShare:o,toggleClass:p,addClass:q,removeClass:r}},["MediaSizeService","SocialShareService"])}(jQuery,PlentyFramework),function(a,b){b.directive("Validator",function(a){function b(b,c){return a.validate(b,c)}return{validate:b}},["ValidationService"])}(jQuery,PlentyFramework),PlentyFramework.compile();var plenty=PlentyFramework.getInstance();jQuery(document).ready(function(){plenty.bindDirectives()}); \ No newline at end of file From 0b8b031701d93bd2874519f376dd5bda1e882add Mon Sep 17 00:00:00 2001 From: Maximilian Lauterbach Date: Wed, 9 Dec 2015 13:30:10 +0100 Subject: [PATCH 10/17] additional: fixed variable name for PostFinder --- src/services/CheckoutService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/CheckoutService.js b/src/services/CheckoutService.js index bcbd437..5d0df6b 100644 --- a/src/services/CheckoutService.js +++ b/src/services/CheckoutService.js @@ -127,7 +127,7 @@ { if ( shippingAddress.Street == "PACKSTATION" ) { - shippingAddress.isPackstation = 1; + shippingAddress.IsPackstation = 1; shippingAddress.PackstationNo = shippingAddress.HouseNo; } else if ( shippingAddress.Street == "POSTFILIALE" ) From 41fe38a9ffab4dfae6cee92381411da9654ffcb2 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 9 Dec 2015 15:42:49 +0100 Subject: [PATCH 11/17] =?UTF-8?q?improve=20dependency=20injection:?= =?UTF-8?q?=E2=80=A8components=20now=20can=20inject=20any=20component=20wi?= =?UTF-8?q?th=20equal=20or=20higher=20priority=20(1:=20directives,=202:=20?= =?UTF-8?q?services,=203:=20factories)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plentyFramework.js | 173 ++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 89 deletions(-) diff --git a/src/plentyFramework.js b/src/plentyFramework.js index 247636c..a3acd7d 100644 --- a/src/plentyFramework.js +++ b/src/plentyFramework.js @@ -130,11 +130,7 @@ components.directives[directiveName] = { name : directiveName, dependencies: dependencies, - compile : function() - { - var params = PlentyFramework.resolveServices( dependencies ); - PlentyFramework.directives[directiveName] = directiveFunctions.apply( null, params ); - } + setup : directiveFunctions }; }; @@ -404,50 +400,11 @@ components.services[serviceName] = { name : serviceName, dependencies: dependencies, - compile : function() - { - var params = PlentyFramework.resolveFactories( dependencies ); - PlentyFramework.prototype[serviceName] = serviceFunctions.apply( null, params ); - } + setup : serviceFunctions }; }; - /** - * Returns an array containing required factories given by string identifier - * @function resolveServices - * @static - * @private - * @param {Array} dependencies Names of required factories - * @return {Array} Objects to apply to callback function - */ - PlentyFramework.resolveServices = function( dependencies ) - { - var compiledServices = []; - - $.each( dependencies, function( j, dependency ) - { - - // factory not found: try to compile dependent factory first - if ( !PlentyFramework.prototype.hasOwnProperty( dependency ) ) - { - if ( components.services.hasOwnProperty( dependency ) ) - { - components.services[dependency].compile(); - } - else - { - console.error( 'Cannot inject Service "' + dependency + '": Service not found.' ); - return false; - } - } - var service = PlentyFramework.prototype[dependency]; - compiledServices.push( service ); - } ); - - return compiledServices; - }; - /** * Collection of compiled factories * @attribute factories @@ -485,48 +442,9 @@ components.factories[factoryName] = { name : factoryName, dependencies: dependencies, - compile : function() - { - var params = PlentyFramework.resolveFactories( dependencies ); - PlentyFramework.factories[factoryName] = factoryFunctions.apply( null, params ); - } - }; - - }; - - /** - * Returns an array containing required factories given by string identifier - * @function resolveFactories - * @static - * @private - * @param {Array} dependencies Names of required factories - * @return {Array} Objects to apply to callback function - */ - PlentyFramework.resolveFactories = function( dependencies ) - { - var compiledFactories = []; - - $.each( dependencies, function( j, dependency ) - { - - // factory not found: try to compile dependent factory first - if ( !PlentyFramework.factories.hasOwnProperty( dependency ) ) - { - if ( components.factories.hasOwnProperty( dependency ) ) - { - components.factories[dependency].compile(); - } - else - { - console.error( 'Cannot inject Factory "' + dependency + '": Factory not found.' ); - return false; - } - } - var factory = PlentyFramework.factories[dependency]; - compiledFactories.push( factory ); - } ); + setup : factoryFunctions + } - return compiledFactories; }; /** @@ -625,7 +543,8 @@ { if ( !PlentyFramework.factories.hasOwnProperty( factory ) ) { - components.factories[factory].compile(); + //components.factories[factory].compile(); + compileComponent( components.factories[factory], 3 ); } } @@ -633,7 +552,8 @@ { if ( !PlentyFramework.prototype.hasOwnProperty( service ) ) { - components.services[service].compile(); + //components.factories[factory].compile(); + compileComponent( components.services[service], 2 ); } } @@ -641,7 +561,8 @@ { if ( !PlentyFramework.directives.hasOwnProperty( directive ) ) { - components.directives[directive].compile(); + //components.factories[factory].compile(); + compileComponent( components.directives[directive], 1 ); } } @@ -653,6 +574,80 @@ }; + // Level: 1 = directive, 2 = service, 3 = factory + function compileComponent( component, componentLevel, dependencyStack ) + { + dependencyStack = dependencyStack || []; + + // resolve dependencies + var compiledDependencies = []; + for( var i = 0; i < component.dependencies.length; i++ ) + { + var dependency = component.dependencies[i]; + if ( $.inArray( dependency, dependencyStack ) < 0 ) + { + // add dependency to stack to avoid cyclic injection + dependencyStack.push( dependency ); + + if ( components.factories.hasOwnProperty( dependency ) ) + { + // required dependency is a factory + if ( !PlentyFramework.factories.hasOwnProperty( dependency ) ) + { + // factory is not compiled yet + compileComponent( components.factories[dependency], 3, dependencyStack ); + } + compiledDependencies.push( PlentyFramework.factories[dependency] ); + continue; + } + + if ( componentLevel <= 2 && components.services.hasOwnProperty( dependency ) ) + { + // required dependency is a service + if ( !PlentyFramework.prototype.hasOwnProperty( dependency ) ) + { + // service is not compiled yet + compileComponent( components.services[dependency], 2, dependencyStack ); + } + compiledDependencies.push( PlentyFramework.prototype[dependency] ); + continue; + } + + if ( componentLevel <= 1 && components.directives.hasOwnProperty( dependency ) ) + { + // required dependency is a directive + if ( !PlentyFramework.directives.hasOwnProperty( dependency ) ) + { + // directive is not compiled yet + compileComponent( components.directives[dependency], 1, dependencyStack ); + } + compiledDependencies.push( PlentyFramework.directives[dependency] ); + continue; + } + + console.error( 'Cannot inject dependency "' + dependency + '": Object not found.' ); + } + else + { + console.error( 'Cyclic dependency injection: ' + dependencyStack.join( ' -> ' ) + ' -> ' + dependency ); + } + } + + // compile component + if( componentLevel == 3 ) + { + PlentyFramework.factories[component.name] = component.setup.apply( null, compiledDependencies ); + } + else if( componentLevel == 2 ) + { + PlentyFramework.prototype[component.name] = component.setup.apply( null, compiledDependencies ); + } + else if( componentLevel == 1 ) + { + PlentyFramework.directives[component.name] = component.setup.apply( null, compiledDependencies ); + } + } + }( jQuery )); From 41a674fa63c7ac7b5ec0b5d79c4e5738d1ac0ad1 Mon Sep 17 00:00:00 2001 From: Maximilian Lauterbach Date: Thu, 10 Dec 2015 15:21:31 +0100 Subject: [PATCH 12/17] version update --- dist/plentymarketsCMStools-1.0.1.min.js | 9 - ....0.1.js => plentymarketsCMStools-1.0.2.js} | 302 ++++++++++++------ dist/plentymarketsCMStools-1.0.2.min.js | 10 + package.json | 2 +- 4 files changed, 223 insertions(+), 100 deletions(-) delete mode 100644 dist/plentymarketsCMStools-1.0.1.min.js rename dist/{plentymarketsCMStools-1.0.1.js => plentymarketsCMStools-1.0.2.js} (96%) create mode 100644 dist/plentymarketsCMStools-1.0.2.min.js diff --git a/dist/plentymarketsCMStools-1.0.1.min.js b/dist/plentymarketsCMStools-1.0.1.min.js deleted file mode 100644 index 82ec69a..0000000 --- a/dist/plentymarketsCMStools-1.0.1.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Licensed under AGPL v3 - * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) - * ===================================================================================== - * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) - * @author Felix Dausch - * ===================================================================================== -*/!function(a,b){"object"==typeof exports&&exports&&"string"!=typeof exports.nodeName?b(exports):"function"==typeof define&&define.amd?define(["exports"],b):(a.Mustache={},b(Mustache))}(this,function(a){function b(a){return"function"==typeof a}function c(a){return p(a)?"array":typeof a}function d(a){return a.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function e(a,b){return null!=a&&"object"==typeof a&&b in a}function f(a,b){return q.call(a,b)}function g(a){return!f(r,a)}function h(a){return String(a).replace(/[&<>"'\/]/g,function(a){return s[a]})}function i(b,c){function e(){if(r&&!s)for(;q.length;)delete o[q.pop()];else q=[];r=!1,s=!1}function f(a){if("string"==typeof a&&(a=a.split(u,2)),!p(a)||2!==a.length)throw new Error("Invalid tags: "+a);h=new RegExp(d(a[0])+"\\s*"),i=new RegExp("\\s*"+d(a[1])),m=new RegExp("\\s*"+d("}"+a[1]))}if(!b)return[];var h,i,m,n=[],o=[],q=[],r=!1,s=!1;f(c||a.tags);for(var y,z,A,B,C,D,E=new l(b);!E.eos();){if(y=E.pos,A=E.scanUntil(h))for(var F=0,G=A.length;G>F;++F)B=A.charAt(F),g(B)?q.push(o.length):s=!0,o.push(["text",B,y,y+1]),y+=1,"\n"===B&&e();if(!E.scan(h))break;if(r=!0,z=E.scan(x)||"name",E.scan(t),"="===z?(A=E.scanUntil(v),E.scan(v),E.scanUntil(i)):"{"===z?(A=E.scanUntil(m),E.scan(w),E.scanUntil(i),z="&"):A=E.scanUntil(i),!E.scan(i))throw new Error("Unclosed tag at "+E.pos);if(C=[z,A,y,E.pos],o.push(C),"#"===z||"^"===z)n.push(C);else if("/"===z){if(D=n.pop(),!D)throw new Error('Unopened section "'+A+'" at '+y);if(D[1]!==A)throw new Error('Unclosed section "'+D[1]+'" at '+y)}else"name"===z||"{"===z||"&"===z?s=!0:"="===z&&f(A)}if(D=n.pop())throw new Error('Unclosed section "'+D[1]+'" at '+E.pos);return k(j(o))}function j(a){for(var b,c,d=[],e=0,f=a.length;f>e;++e)b=a[e],b&&("text"===b[0]&&c&&"text"===c[0]?(c[1]+=b[1],c[3]=b[3]):(d.push(b),c=b));return d}function k(a){for(var b,c,d=[],e=d,f=[],g=0,h=a.length;h>g;++g)switch(b=a[g],b[0]){case"#":case"^":e.push(b),f.push(b),e=b[4]=[];break;case"/":c=f.pop(),c[5]=b[2],e=f.length>0?f[f.length-1][4]:d;break;default:e.push(b)}return d}function l(a){this.string=a,this.tail=a,this.pos=0}function m(a,b){this.view=a,this.cache={".":this.view},this.parent=b}function n(){this.cache={}}var o=Object.prototype.toString,p=Array.isArray||function(a){return"[object Array]"===o.call(a)},q=RegExp.prototype.test,r=/\S/,s={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},t=/\s*/,u=/\s+/,v=/\s*=/,w=/\s*\}/,x=/#|\^|\/|>|\{|&|=|!/;l.prototype.eos=function(){return""===this.tail},l.prototype.scan=function(a){var b=this.tail.match(a);if(!b||0!==b.index)return"";var c=b[0];return this.tail=this.tail.substring(c.length),this.pos+=c.length,c},l.prototype.scanUntil=function(a){var b,c=this.tail.search(a);switch(c){case-1:b=this.tail,this.tail="";break;case 0:b="";break;default:b=this.tail.substring(0,c),this.tail=this.tail.substring(c)}return this.pos+=b.length,b},m.prototype.push=function(a){return new m(a,this)},m.prototype.lookup=function(a){var c,d=this.cache;if(d.hasOwnProperty(a))c=d[a];else{for(var f,g,h=this,i=!1;h;){if(a.indexOf(".")>0)for(c=h.view,f=a.split("."),g=0;null!=c&&gi;++i)g=void 0,e=a[i],f=e[0],"#"===f?g=this.renderSection(e,b,c,d):"^"===f?g=this.renderInverted(e,b,c,d):">"===f?g=this.renderPartial(e,b,c,d):"&"===f?g=this.unescapedValue(e,b):"name"===f?g=this.escapedValue(e,b):"text"===f&&(g=this.rawValue(e)),void 0!==g&&(h+=g);return h},n.prototype.renderSection=function(a,c,d,e){function f(a){return g.render(a,c,d)}var g=this,h="",i=c.lookup(a[1]);if(i){if(p(i))for(var j=0,k=i.length;k>j;++j)h+=this.renderTokens(a[4],c.push(i[j]),d,e);else if("object"==typeof i||"string"==typeof i||"number"==typeof i)h+=this.renderTokens(a[4],c.push(i),d,e);else if(b(i)){if("string"!=typeof e)throw new Error("Cannot use higher-order sections without the original template");i=i.call(c.view,e.slice(a[3],a[5]),f),null!=i&&(h+=i)}else h+=this.renderTokens(a[4],c,d,e);return h}},n.prototype.renderInverted=function(a,b,c,d){var e=b.lookup(a[1]);return!e||p(e)&&0===e.length?this.renderTokens(a[4],b,c,d):void 0},n.prototype.renderPartial=function(a,c,d){if(d){var e=b(d)?d(a[1]):d[a[1]];return null!=e?this.renderTokens(this.parse(e),c,d,e):void 0}},n.prototype.unescapedValue=function(a,b){var c=b.lookup(a[1]);return null!=c?c:void 0},n.prototype.escapedValue=function(b,c){var d=c.lookup(b[1]);return null!=d?a.escape(d):void 0},n.prototype.rawValue=function(a){return a[1]},a.name="mustache.js",a.version="2.1.3",a.tags=["{{","}}"];var y=new n;a.clearCache=function(){return y.clearCache()},a.parse=function(a,b){return y.parse(a,b)},a.render=function(a,b,d){if("string"!=typeof a)throw new TypeError('Invalid template! Template should be a "string" but "'+c(a)+'" was given as the first argument for mustache#render(template, view, partials)');return y.render(a,b,d)},a.to_html=function(c,d,e,f){var g=a.render(c,d,e);return b(f)?void f(g):g},a.escape=h,a.Scanner=l,a.Context=m,a.Writer=n}),Object.equals=function(a,b){if(a===b)return!0;if(!(a instanceof Object&&b instanceof Object))return!1;if(a.constructor!==b.constructor)return!1;for(var c in a)if(a.hasOwnProperty(c)){if(!b.hasOwnProperty(c))return!1;if(a[c]!==b[c]){if("object"!=typeof a[c])return!1;if(!Object.equals(a[c],b[c]))return!1}}for(var c in b)if(b.hasOwnProperty(c)&&!a.hasOwnProperty(c))return!1;return!0};var TemplateCache={};TemplateCache["addressSuggestions/addressDoctor.html"]='',TemplateCache["addressSuggestions/postFinder.html"]='{{#addresses}}\n
    \n
    \n \n
    \n
    \n{{/addresses}}\n',TemplateCache["error/errorMessage.html"]='
    \n Code {{code}}:\n {{{message}}}\n
    \n',TemplateCache["error/errorPopup.html"]='
    \n \n
    \n
    \n
    \n',TemplateCache["modal/modal.html"]='\n',TemplateCache["waitscreen/waitscreen.html"]='
    ',function(a){function b(a,b,c,d){a.on(b,function(a){return g.push(a),c.apply(null,d)})}function c(b){var c=a(b);c.is('input[type="checkbox"]')&&c.on("change",function(){c.is(":checked")?c.trigger("check"):c.trigger("uncheck")}),c.is('input[type="radio"]')&&c.on("change",function(){var b=c.attr("name");a('input[type="radio"][name="'+b+'"]').each(function(b,c){var d=a(c);d.is(":checked")?d.trigger("check"):d.trigger("uncheck")})})}function d(a,b){for(var c=/^(([\w]+):)?([\w]+)\.([\w]+)(\((.*)\))?$/,d=a.split(";"),e=[],f=0;f0)for(var j=h[6].match(/(['][^']+['])|([\w-]+)|(["][^"]+["])/g),k=0;k=0;c--)if(a==g[c].type)return g[c];return null},PlentyFramework.pushEvent=function(a){g.push(a)},PlentyFramework.service=function(a,b,c){return"string"!=typeof a?void console.error("Type mismatch: Expect first parameter to be a 'string', '"+typeof a+"' given."):"function"!=typeof b?void console.error("Type mismatch: Expect second parameter to be a 'function', '"+typeof b+"' given."):(c=c||[],void(e.services[a]={name:a,dependencies:c,compile:function(){var d=PlentyFramework.resolveFactories(c);PlentyFramework.prototype[a]=b.apply(null,d)}}))},PlentyFramework.resolveServices=function(b){var c=[];return a.each(b,function(a,b){if(!PlentyFramework.prototype.hasOwnProperty(b)){if(!e.services.hasOwnProperty(b))return console.error('Cannot inject Service "'+b+'": Service not found.'),!1;e.services[b].compile()}var d=PlentyFramework.prototype[b];c.push(d)}),c},PlentyFramework.factories={},PlentyFramework.factory=function(a,b,c){return"string"!=typeof a?void console.error("Type mismatch: Expect first parameter to be a 'string', '"+typeof a+"' given."):"function"!=typeof b?void console.error("Type mismatch: Expect second parameter to be a 'function', '"+typeof b+"' given."):(c=c||[],void(e.factories[a]={name:a,dependencies:c,compile:function(){var d=PlentyFramework.resolveFactories(c);PlentyFramework.factories[a]=b.apply(null,d)}}))},PlentyFramework.resolveFactories=function(b){var c=[];return a.each(b,function(a,b){if(!PlentyFramework.factories.hasOwnProperty(b)){if(!e.factories.hasOwnProperty(b))return console.error('Cannot inject Factory "'+b+'": Factory not found.'),!1;e.factories[b].compile()}var d=PlentyFramework.factories[b];c.push(d)}),c},PlentyFramework.compileTemplate=function(a,b){return b=b||{},b.translate=function(){return function(a,b){return b(PlentyFramework.translate(a))}},Mustache.render(TemplateCache[a],b)},PlentyFramework.scriptPath="",PlentyFramework.Strings={},PlentyFramework.loadLanguageFile=function(b){a.get(PlentyFramework.scriptPath+b).done(function(a){PlentyFramework.Strings=a})},PlentyFramework.translate=function(a,b){var c;return PlentyFramework.Strings.hasOwnProperty(a)?c=PlentyFramework.Strings[a]:(c=a,console.warn('No translation found for "'+c+'".')),b&&(c=Mustache.render(c,b)),c},PlentyFramework.compile=function(){for(var a in e.factories)PlentyFramework.factories.hasOwnProperty(a)||e.factories[a].compile();for(var b in e.services)PlentyFramework.prototype.hasOwnProperty(b)||e.services[b].compile();for(var c in e.directives)PlentyFramework.directives.hasOwnProperty(c)||e.directives[c].compile();var d=document.getElementsByTagName("SCRIPT");d.length>0&&(PlentyFramework.scriptPath=d[d.length-1].src.match(/(.*)\/(.*)\.js(\?\S*)?$/)[1])}}(jQuery),PlentyFramework.cssClasses={active:"active"},function(a,b){b.partials.Error={init:function(b){a(b).find(".close").click(function(){b.hide(),b.find(".plentyErrorBoxInner").html("")})},addError:function(b,c){var d=a(c).attr("data-plenty-error-code");a(b).find('[data-plenty-error-code="'+d+'"]').length<=0&&a(b).find(".plentyErrorBoxInner").append(c)},show:function(b){a(b).show()}}}(jQuery,PlentyFramework),function(a,b){b.partials.Modal={init:function(a,b){a.on("hidden.bs.modal",function(){b.hide(),a.remove()}),b.timeout>0&&(a.on("hide.bs.modal",b.stopTimeout),a.find(".modal-content").hover(function(){b.pauseTimeout()},function(){a.is(".in")&&b.continueTimeout()}))},show:function(a){a.modal("show")},hide:function(a){a.modal("hide")},isModal:function(b){return a(b).filter(".modal").length+a(b).find(".modal").length>0},getModal:function(b){var c=a(b);return c.length>1&&(c=a(b).filter(".modal")||a(b).find(".modal")),c}}}(jQuery,PlentyFramework),function(a){a(document).on("initPartials",function(b,c){a(c).find('[data-toggle="tooltip"]').tooltip({container:"body"})})}(jQuery),function(a,b){b.partials.WaitScreen={show:function(a){a.addClass("in")},hide:function(a){a.removeClass("in")}}}(jQuery,PlentyFramework),function(a,b){b.factory("APIFactory",function(b){function c(c){try{var d=a.parseJSON(c.responseText);b.printErrors(d.error.error_stack)}catch(e){b.throwError(c.status,c.statusText)}}function d(d,e,f,g,h){return g||b.showWaitScreen(),a.ajax(d,{type:"GET",data:e,dataType:"json",async:!h,error:function(a){f||c(a)}}).always(function(){g||b.hideWaitScreen()})}function e(d,e,f,g){var h={type:"POST",dataType:"json",error:function(a){f||c(a)}};return e&&e.isFile?(h.cache=e.cache,h.processData=e.processData,h.data=e.data,h.contentType=!1):(h.data=JSON.stringify(e),h.contentType="application/json"),g||b.showWaitScreen(),a.ajax(d,h).always(function(){g||b.hideWaitScreen()})}function f(d,e,f,g){return g||b.showWaitScreen(),a.ajax(d,{type:"PUT",data:JSON.stringify(e),dataType:"json",contentType:"application/json",error:function(a){f||c(a)}}).always(function(){g||b.hideWaitScreen()})}function g(d,e,f,g){return g||b.showWaitScreen(),a.ajax(d,{type:"DELETE",data:JSON.stringify(e),dataType:"json",contentType:"application/json",error:function(a){f||c(a)}}).always(function(){g||b.hideWaitScreen()})}function h(){return a.Deferred().resolve()}return{get:d,post:e,put:f,"delete":g,idle:h}},["UIFactory"])}(jQuery,PlentyFramework),function(a){a.factory("CMSFactory",function(a){function b(b,c){function d(d){return a.get("/rest/"+d.toLowerCase()+"/container_"+b.toLowerCase()+"/",c)}return{from:d}}function c(b,c){function d(d){return a.get("/rest/"+d.toLowerCase()+"/"+b.toLowerCase()+"/",c)}return{from:d}}function d(b){return a.get("/rest/categoryview/categorycontentbody/?categoryID="+b)}return{getContainer:b,getParams:c,getCategoryContent:d}},["APIFactory"])}(PlentyFramework),function(a){a.factory("CheckoutFactory",function(b,c,d){function e(){return l}function f(a){return m&&l||g(!0),a?$.extend(!0,{},l):m}function g(a){return b.get("/rest/checkout/",null,!1,!1,a).done(function(a){a?(l=a.data,m=new e):d.throwError(0,'Could not receive checkout data [GET "/rest/checkout/" receives null value]')})}function h(){return b.put("/rest/checkout",m).done(function(a){a?(l=a.data,m=new e):d.throwError(0,'Could not receive checkout data [GET "/rest/checkout/" receives null value]')})}function i(b){return c.getContainer("checkout"+b).from("checkout").done(function(c){$('[data-plenty-checkout-template="'+b+'"]').each(function(b,d){$(d).html(c.data[0]),a.getInstance().bindDirectives(d),$(window).trigger("contentChanged")})})}function j(b){return c.getCategoryContent(b).done(function(c){$('[data-plenty-checkout-catcontent="'+b+'"]').each(function(b,d){$(d).html(c.data[0]),a.getInstance().bindDirectives(d),$(window).trigger("contentChanged")})})}function k(b){return c.getContainer("itemview"+b).from("itemview").done(function(c){$('[data-plenty-itemview-template="'+b+'"]').each(function(b,d){$(d).html(c.data[0]),a.getInstance().bindDirectives(d),$(window).trigger("contentChanged")})})}var l,m;return{getCheckout:f,setCheckout:h,loadCheckout:g,reloadContainer:i,reloadCatContent:j,reloadItemContainer:k}},["APIFactory","CMSFactory","UIFactory"])}(PlentyFramework),function(a,b){b.factory("ModalFactory",function(){function c(a){return PlentyFramework.partials.Modal.isModal(a)}function d(){return new e}function e(){function d(a){return s.title=a,this}function e(a){return s.cssClass=a,this}function f(a){return s.content=a,this}function g(a){return s.labelConfirm=a,this}function h(a){return s.labelDismiss=a,this}function i(a){return s.onConfirm=a,this}function j(a){return s.onDismiss=a,this}function k(a){return s.container=a,this}function l(a){return s.timeout=a,this}function m(){t=c(s.content)?PlentyFramework.partials.Modal.getModal(s.content):a(PlentyFramework.compileTemplate("modal/modal.html",s)),a(s.container).append(t);var b=a(s.content).filter("script");b.length>0&&b.each(function(b,c){var d=document.createElement("script");d.type="text/javascript",d.innerHTML=a(c).text(),a(s.container).append(d)}),PlentyFramework.partials.Modal.init(t,s),t.find('[data-plenty-modal="confirm"]').click(function(){var a=s.onConfirm();"undefined"==typeof a&&(a=!0),a&&n(!0)}),PlentyFramework.partials.Modal.show(t),s.timeout>0&&o()}function n(a){PlentyFramework.partials.Modal.hide(t),a||s.onDismiss()}function o(){w=s.timeout,x=(new Date).getTime(),u=window.setTimeout(function(){window.clearInterval(v),n()},s.timeout),t.find('[data-plenty-modal="timer"]').text(w/1e3),v=window.setInterval(function(){if(!y){var a=w-(new Date).getTime()+x;a=Math.round(a/1e3),t.find('[data-plenty-modal="timer"]').text(a)}},1e3)}function p(){y=!0,w-=(new Date).getTime()-x,window.clearTimeout(u)}function q(){y=!1,x=(new Date).getTime(),u=window.setTimeout(function(){n(),window.clearInterval(v)},w)}function r(){window.clearTimeout(u),window.clearInterval(v)}var s=this;s.title="",s.cssClass="",s.content="",s.labelDismiss=b.translate("Cancel"),s.labelConfirm=b.translate("Confirm"),s.onConfirm=function(){},s.onDismiss=function(){},s.container="body",s.timeout=-1,s.hide=n,s.startTimeout=o,s.stopTimeout=r,s.pauseTimeout=p,s.continueTimeout=q;var t,u,v,w,x,y=!1;return{setTitle:d,setClass:e,setContent:f,setContainer:k,setLabelConfirm:g,setLabelDismiss:h,onConfirm:i,onDismiss:j,setTimeout:l,show:m,hide:n}}return{prepare:d,isModal:c}})}(jQuery,PlentyFramework),function(a,b){b.factory("UIFactory",function(){function c(a,b){d([{code:a,message:b}])}function d(c){(!i||a("body").has(i).length<=0)&&(i=a(b.compileTemplate("error/errorPopup.html")),a("body").append(i),b.partials.Error.init(i)),a.each(c,function(c,d){b.partials.Error.addError(i,a(b.compileTemplate("error/errorMessage.html",d)))}),b.partials.Error.show(i),f(!0)}function e(){return h=h||0,(!g||a("body").has(g).length<=0)&&(g=a(b.compileTemplate("waitscreen/waitscreen.html")),a("body").append(g)),b.partials.WaitScreen.show(g),h++,h}function f(a){return h--,(0>=h||a)&&(h=0,b.partials.WaitScreen.hide(g)),h}var g,h=0,i=null;return{throwError:c,printErrors:d,showWaitScreen:e,hideWaitScreen:f}})}(jQuery,PlentyFramework),function(a,b){b.service("AddressDoctorService",function(c){function d(b){var c=!0;return b=b||"[data-plenty-address-doctor]",a(b).filter("[data-plenty-address-doctor]:visible").each(function(b,d){var f=new e(d),g=a(d).attr("data-plenty-address-doctor").replace(/\s/g,"").split(",");f.isValid(g)||(c=!1)}),c}function e(c){function d(a){return i()?!0:(j=new f(l.getFormValues()),k=a,e(),1==j.getAddresses().length)}function e(){a(".suggestion-list").remove();for(var b=!1,c=0;cc;c++){var d=a.data[c],f=e(d);f?f.HouseNo.push(d.HouseNo):(d.HouseNo=[d.HouseNo],j.push(d))}})}function e(a){for(var b=j.length,c=0;b>c;c++)if(a.Street==j[c].Street&&j.ZIP==j[c].ZIP&&a.City==j[c].City)return j[c];return null}function f(){return j}function g(b){for(var c=[],d=j.length,e=0;d>e;e++){var f=j[e];a.inArray(f[b],c)<0&&c.push(f[b])}return c}function h(a){for(var b=[],c=j.length,d=0;c>d;d++){var e=j[d];(a.Street&&a.Street==e.Street||a.ZIP&&a.ZIP==e.ZIP||a.City&&a.City==e.City)&&b.push(e)}j=b}function i(a){a=parseInt(a);for(var b=j.length,c=0;b>c;c++)for(var d=j[c],e=0;e=f[0]&&a<=f[1])return!0}return!1}var j=[];return d(),{getAddresses:f,getList:g,filter:h,houseNoAllowed:i}}return{validateAddress:d}},["APIFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("AuthenticationService",function(c,d,e){function f(){var b=a('[data-plenty-checkout="lostPasswordForm"]');if(b.validateForm()){var d=b.getFormValues(),e={Email:d.Email};return c.post("/rest/checkout/lostpassword/",e).done(function(b){1==b.data.IsMailSend&&(a('[data-plenty-checkout="lostPasswordTextContainer"]').hide(),a('[data-plenty-checkout="lostPasswordSuccessMessage"]').show())})}}function g(a){if(a.validateForm()){var b=a.getFormValues(),d={Email:b.loginMail,Password:b.loginPassword};return e.showWaitScreen(),c.post("/rest/checkout/login/",d).done(function(){window.location.assign(a.attr("action"))})}}function h(a){return c.post("/rest/checkout/customerinvoiceaddress/",a).done(function(a){d.getCheckout().CustomerInvoiceAddress=a.data})}function i(){var c=a('[data-plenty-checkout-form="customerRegistration"]');if(c.validateForm()&&b.getInstance().AddressDoctorService.validateAddress()){var d=c.getFormValues(),e={LoginType:2,FormOfAddressID:d.FormOfAddressID,Company:d.Company,FirstName:d.FirstName,LastName:d.LastName,Street:d.Street,HouseNo:d.HouseNo,AddressAdditional:d.AddressAdditional,ZIP:d.ZIP,City:d.City,CountryID:d.CountryID,VATNumber:d.VATNumber,Email:d.Email,EmailRepeat:d.EmailRepeat,BirthDay:d.BirthDay,BirthMonth:d.BirthMonth,BirthYear:d.BirthYear,Password:d.Password,PasswordRepeat:d.PasswordRepeat,PhoneNumber:d.PhoneNumber,MobileNumber:d.MobileNumber,FaxNumber:d.FaxNumber,Postnummer:d.Postnummer};return e.CustomerPropertiesList=e.CustomerPropertiesList||[],c.find("[data-plenty-property-id]").each(function(b,c){e.CustomerPropertiesList.push({PropertyID:a(c).attr("data-plenty-property-id"),PropertyValue:a(c).val()})}),h(e).done(function(){window.location.assign(c.attr("action"))})}}return{resetPassword:f,customerLogin:g,setInvoiceAddress:h,registerCustomer:i}},["APIFactory","CheckoutFactory","UIFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("BasketService",function(c,d,e,f,g){function h(d){d&&c.get("/rest/checkout/container_"+"CheckoutOrderParamsList".toLowerCase()+"/",{itemID:d[0].BasketItemItemID,quantity:d[0].BasketItemQuantity},!1,!0).done(function(c){c.data[0].indexOf("form-group")>0?g.prepare().setContent(c.data[0]).setTitle(b.translate("Select order parameters")).setLabelConfirm(b.translate("Save")).onConfirm(function(){return a('[data-plenty-checkout-form="OrderParamsForm"]').validateForm()?(j(i(d)),!0):!1}).show():j(d)})}function i(b){var c,d=a('[data-plenty-checkout-form="OrderParamsForm"]'),e={},f="";return d.find('[name^="ParamGroup"]').each(function(){c=this.name.match(/^ParamGroup\[(\d+)]\[(\d+)]$/),b=m(b,c[1],a(this).val(),a(this).val())}),d.find('[name^="ParamValue"]').each(function(){if(e=a(this),f=e.attr("type"),("checkbox"==f&&e.is(":checked")||"radio"==f&&e.is(":checked")||"radio"!=f&&"checkbox"!=f)&&"file"!=f&&"hidden"!=f){var c=e[0].name.match(/^ParamValue\[(\d+)]\[(\d+)]$/);b=m(b,c[1],c[2],e.val())}else if("file"==f)if(e[0].files&&e[0].files.length>0)b=l(e,b);else{var c=e[0].name.match(/^ParamValueFile\[(\d+)]\[(\d+)]$/),d=a('input[type="hidden"][name="ParamValue['+c[1]+"]["+c[2]+']"]').val();b=m(b,c[1],c[2],d)}}),b}function j(a){c.post("/rest/checkout/basketitemslist/",a,!0).done(function(){f.loadCheckout().done(function(){s(),e.getContainer("ItemViewItemToBasketConfirmationOverlay",{ArticleID:a[0].BasketItemItemID}).from("ItemView").done(function(a){g.prepare().setContent(a.data[0]).setTimeout(5e3).show()})})}).fail(function(a){d.printErrors(JSON.parse(a.responseText).error.error_stack)})}function k(a){c.put("/rest/checkout/basketitemslist/",a).done(function(){f.reloadCatContent(b.getGlobal("basketCatID")),f.loadCheckout().done(function(){s()})})}function l(a,b){var d,e,f=a[0].id,g={},h=[],i={type:"POST",data:{},isFile:!0,cache:!1,dataType:"json",processData:!1,contentType:!1};g[f]=a[0].files,-1==h.indexOf(f)&&h.push(f);for(var j=0,k=h.length;k>j;++j)d=new FormData,e=g[h[j]],d.append("0",e[0],e[0].name),i.data=d,c.post("/rest/checkout/orderparamfile/",i);var l=a[0].name.match(/^ParamValueFile\[(\d+)]\[(\d+)]$/);return m(b,l[1],l[2],a.val())}function m(b,c,d,e){return c>0&&void 0==b[c]&&(b[c]=a.extend(!0,{},b[0]),b[c].BasketItemOrderParamsList=[]),void 0!=b[c]&&(b[c].BasketItemQuantity=1,void 0==b[c].BasketItemOrderParamsList&&(b[c].BasketItemOrderParamsList=[]),e&&b[c].BasketItemOrderParamsList.push({BasketItemOrderParamID:d,BasketItemOrderParamValue:e})),b}function n(b){var c=a('[data-plenty-basket-item="'+b+'"');c.modal("show"),c.find('[data-plenty-modal="confirm"]').on("click",function(){var d=p(b),e=[];c.find('select, .PlentyFormContainer.AttrImage > input[type="hidden"]').each(function(b,c){var d=c.name.match(/^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/);d&&d[1]&&e.push({BasketItemAttributeID:d[1],BasketItemAttributeValueID:a(c).val()})}),0!=e.length&&(d.BasketItemAttributesList=e),k([d])})}function o(d){var e=p(d);e.BasketItemOrderParamsList=[],c.get("/rest/checkout/container_"+"CheckoutOrderParamsList".toLowerCase()+"/",{itemID:e.BasketItemItemID,quantity:e.BasketItemQuantity,basketItemID:d}).done(function(c){g.prepare().setContent(c.data[0]).setTitle(b.translate("Edit order parameters")).setLabelConfirm(b.translate("Save")).onConfirm(function(){return a('[data-plenty-checkout-form="OrderParamsForm"]').validateForm()?(k(i([e])),!0):!1}).show()})}function p(a){for(var b=f.getCheckout().BasketItemsList,c=0;c"+b.translate('Do you really want to remove "{{item}}" from your basket?',{item:j})+"

    ").onDismiss(function(){i.reject()}).onConfirm(function(){h()}).setLabelConfirm(b.translate("Delete")).show(),i}function r(d,e){if(0>=e)return q(d);for(var g,h,i=a.Deferred(),j=f.getCheckout().BasketItemsList,k=0;k0&&f.reloadContainer("Totals")}return{addItem:h,removeItem:q,getItem:p,setItemQuantity:r,editItemAttributes:n,editOrderParams:o,addCoupon:t,removeCoupon:u}},["APIFactory","UIFactory","CMSFactory","CheckoutFactory","ModalFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("CheckoutService",function(c,d,e,f){function g(){e.loadCheckout(!0)}function h(){var b=a('[data-plenty-checkout-form="details"]'),d=b.getFormValues();return e.getCheckout().CheckoutCustomerSign||(e.getCheckout().CheckoutCustomerSign=""),e.getCheckout().CheckoutOrderInfoText||(e.getCheckout().CheckoutOrderInfoText=""),e.getCheckout().CheckoutCustomerSign!==d.CustomerSign&&a(b).find('[name="CustomerSign"]').length>0||e.getCheckout().CheckoutOrderInfoText!==d.OrderInfoText&&a(b).find('[name="OrderInfoText"]').length>0?(e.getCheckout().CheckoutCustomerSign=d.CustomerSign,e.getCheckout().CheckoutOrderInfoText=d.OrderInfoText,e.setCheckout()):c.idle()}function i(d){var f=a('[data-plenty-checkout-form="shippingAddress"]');if(!d&&!f.validateForm())return!1;if(!d&&!b.getInstance().AddressDoctorService.validateAddress(f))return!1;var g=f.getFormValues(),h=a('[name="shippingAddressID"]:checked').val();if(a("#shippingAdressSelect").modal("hide"),0>h){var i=g;return k(i,e.getCheckout().CustomerShippingAddress)?c.idle():("PACKSTATION"==i.Street?(i.isPackstation=1,i.PackstationNo=i.HouseNo):"POSTFILIALE"==i.Street&&(i.IsPostfiliale=1,i.PostfilialNo=i.HouseNo),c.post("/rest/checkout/customershippingaddress/",i).done(function(a){e.getCheckout().CheckoutCustomerShippingAddressID=a.data.ID,e.getCheckout().CheckoutShippingCountryID=a.data.CountryID,delete e.getCheckout().CheckoutMethodOfPaymentID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList"),e.reloadContainer("ShippingProfilesList"),2==e.getCheckout().CustomerInvoiceAddress.LoginType&&e.reloadContainer("CustomerShippingAddress")})}))}return h!=e.getCheckout().CheckoutCustomerShippingAddressID?(e.getCheckout().CheckoutCustomerShippingAddressID=h,delete e.getCheckout().CheckoutMethodOfPaymentID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList"),e.reloadContainer("ShippingProfilesList"),2==e.getCheckout().CustomerInvoiceAddress.LoginType&&e.reloadContainer("CustomerShippingAddress")})):c.idle()}function j(){var b=a('[data-plenty-checkout-form="guestRegistration"]'),d=b.getFormValues();return d.LoginType=1,d.CustomerPropertiesList=d.CustomerPropertiesList||[],b.find("[data-plenty-property-id]").each(function(b,c){d.CustomerPropertiesList.push({PropertyID:a(c).attr("data-plenty-property-id"),PropertyValue:a(c).val()})}),k(d,e.getCheckout().CustomerInvoiceAddress)?i():c.post("/rest/checkout/customerinvoiceaddress/",d).done(function(a){i().done(function(){e.loadCheckout()})})}function k(a,b){for(var c in a)if(a[c]+""!=b[c]+""&&"EmailRepeat"!==c)return!1;return!0}function l(){var b=a('[data-plenty-checkout-form="shippingProfileSelect"]').getFormValues();return e.getCheckout().CheckoutShippingProfileID=b.ShippingProfileID,delete e.getCheckout().CheckoutCustomerShippingAddressID,delete e.getCheckout().CheckoutMethodOfPaymentID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList")})}function m(){return c.post("/rest/checkout/preparepayment/",null).done(function(b){if(""!=b.data.CheckoutMethodOfPaymentRedirectURL)document.location.assign(b.data.CheckoutMethodOfPaymentRedirectURL);else if(b.data.CheckoutMethodOfPaymentAdditionalContent){var c=a(b.data.CheckoutMethodOfPaymentAdditionalContent).find('[data-plenty-checkout-form="bankDetails"]').length>0;f.prepare().setContent(b.data.CheckoutMethodOfPaymentAdditionalContent).onConfirm(function(){return c?p():r()}).show()}})}function n(b){return b=b||a('[data-plenty-checkout-form="methodOfPayment"]').getFormValues().MethodOfPaymentID,e.getCheckout().CheckoutMethodOfPaymentID=b,delete e.getCheckout().CheckoutCustomerShippingAddressID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("ShippingProfilesList")})}function o(){d.getContainer("CheckoutPaymentInformationBankDetails").from("Checkout").done(function(b){f.prepare().setContent(b.data[0]).onDismiss(function(){a('input[name="MethodOfPaymentID"]').each(function(b,c){a(c).val()==e.getCheckout().CheckoutMethodOfPaymentID?a(c).attr("checked","checked"):a(c).removeAttr("checked")})}).onConfirm(function(){return p()}).show()})}function p(){var b=a('[data-plenty-checkout-form="bankDetails"]');if(b.validateForm()){var d=b.getFormValues().checkout.customerBankDetails,f={CustomerBankName:d.bankName,CustomerBLZ:d.blz,CustomerAccountNumber:d.accountNo,CustomerAccountOwner:d.accountOwner,CustomerIBAN:d.iban,CustomerBIC:d.bic};return c.post("/rest/checkout/paymentinformationbankdetails/",f).done(function(){e.loadCheckout().done(function(){n(3),e.reloadContainer("MethodsOfPaymentList")})}),!0}return!1}function q(){d.getContainer("CheckoutPaymentInformationCreditCard").from("Checkout").done(function(b){f.prepare().setContent(b.data[0]).onDismiss(function(){a('input[name="MethodOfPaymentID"]').each(function(b,c){a(c).val()==e.getCheckout().CheckoutMethodOfPaymentID?a(c).attr("checked","checked"):a(c).removeAttr("checked")})}).onConfirm(function(){return r()}).show()})}function r(){var b=a('[data-plenty-checkout-form="creditCard"]');if(b.validateForm()){var d=b.getFormValues().checkout.paymentInformationCC,f={Owner:d.owner,Cvv2:d.cvv2,Number:d.number,Year:d.year,Month:d.month,Provider:d.provider};return c.post("/rest/checkout/paymentinformationcreditcard/",f).done(function(){e.loadCheckout()}),!0}return!1}function s(b){if(2==e.getCheckout().CustomerInvoiceAddress.LoginType)var c=a('[data-plenty-checkout-form="shippingAddress"]').getFormValues();else var c=a('[data-plenty-checkout-form="guestRegistration"]').getFormValues();var g={street:c.Street,houseNo:c.HouseNo,ZIP:c.ZIP,city:c.City,postnummer:c.Postnummer,suggestionType:"postfinder"};d.getContainer("CheckoutAddressSuggestionResultsList",g).from("Checkout").done(function(a){f.prepare().setContent(a.data[0]).show()})}function t(){var b=a('[data-plenty-checkout-form="placeOrder"]');if(b.validateForm()){var d=b.getFormValues(),e={TermsAndConditionsCheck:d.termsAndConditionsCheck||0,WithdrawalCheck:d.withdrawalCheck||0,PrivacyPolicyCheck:d.privacyPolicyCheck||0,AgeRestrictionCheck:d.ageRestrictionCheck||0,NewsletterCheck:d.newsletterCheck||0,KlarnaTermsAndConditionsCheck:d.klarnaTermsAndConditionsCheck||0,PayoneDirectDebitMandateCheck:d.payoneDirectDebitMandateCheck||0,PayoneInvoiceCheck:d.payoneInvoiceCheck||0};return c.post("/rest/checkout/placeorder/",e).done(function(a){""!=a.data.MethodOfPaymentRedirectURL?window.location.assign(a.data.MethodOfPaymentRedirectURL):""!=a.data.MethodOfPaymentAdditionalContent?f.prepare().setContent(a.data.MethodOfPaymentAdditionalContent).setLabelDismiss("").onDismiss(function(){window.location.assign(b.attr("action"))}).onConfirm(function(){window.location.assign(b.attr("action"))}).show():window.location.assign(b.attr("action"))})}}return{init:g,setCustomerSignAndInfo:h,registerGuest:j,setShippingProfile:l,saveShippingAddress:i,loadAddressSuggestion:s,preparePayment:m,setMethodOfPayment:n,editBankDetails:o,editCreditCard:q,placeOrder:t}},["APIFactory","CMSFactory","CheckoutFactory","ModalFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("MediaSizeService",function(){function b(){return e&&c(),e}function c(){var b;if(b=window.matchMedia?window.matchMedia("(min-width:1200px)").matches?"lg":window.matchMedia("(min-width:992px)").matches?"md":window.matchMedia("(min-width:768px)").matches?"sm":"xs":a(window).width()>=1200?"lg":a(window).width()>=992?"md":a(window).width()>=768?"sm":"xs",b!=e){var c=e;e=b,a(window).trigger("sizeChange",[e,c])}}function d(a){for(var b=a.replace(/\s/g,"").split(","),c=0;c li'),r=a('[data-plenty-checkout="container"] > div'),u=a('[data-plenty-checkout="next"]'),t=a('[data-plenty-checkout="prev"]'),q.length==r.length&&r.length>0){d.getCheckout();r.hide(),q.each(function(b,c){a(c).addClass("disabled"),a(c).click(function(){a(this).is(".disabled")||j(b)})}),u.attr("disabled","disabled"),u.click(function(){m()}),t.attr("disabled","disabled"),t.click(function(){n()}),window.addEventListener("hashchange",function(){window.location.hash.length>0?o(window.location.hash):j(0)},!1),a.urlParam=function(a){var b=new RegExp("[?&]"+a+"=([^&#]*)").exec(window.location.href);return null==b?null:b[1]||0};var c=a.urlParam("gototab");0==window.location.hash.length&&c&&a('[data-plenty-checkout-id="'+c+'"]').length>0?window.location.hash=c:j(!o(window.location.hash)&&s>=0?s:0),p(),a(window).on("sizeChange",p),a(window).resize(function(){"xs"==b.getInstance().MediaSizeService.interval()&&p()})}}function f(){return s>=0?{id:a(r[s]).attr("data-plenty-checkout-id"),index:s}:null}function g(a){return v.beforeChange.push(a),b.getInstance().NavigatorService}function h(a){return v.afterChange.push(a),b.getInstance().NavigatorService}function i(b,c){var d=!0;if(s>=0||"afterChange"===b){var e=f(),g={index:c,id:a(r[c]).attr("data-plenty-checkout-id")};a.each(v[b],function(a,b){return b(e,g)===!1?(d=!1,!1):void 0})}return d}function j(e,f){var g=s!==e;(!g||f||i("beforeChange",e))&&(s=e,!Object.equals(w[s],d.getCheckout(!0))&&g&&a(r[s]).attr("data-plenty-checkout-content")?(w[s]=d.getCheckout(!0),c.getCategoryContent(a(r[s]).attr("data-plenty-checkout-content")).done(function(c){a(r[s]).html(c.data[0]),k(g),b.getInstance().bindDirectives(r[s]),a(window).trigger("contentChanged")})):k(g))}function k(b){a(r).hide(),a(q).each(function(b,c){a(c).removeClass("disabled active"),a(c).find('[role="tab"]').attr("aria-selected","false"),s>b?a(c).addClass("visited"):b==s?(a(c).addClass("active visited"),a(c).find('[role="tab"]').attr("aria-selected","true")):b>s&&!a(c).is(".visited")&&a(c).addClass("disabled")}),p(),0>=s?a(t).attr("disabled","disabled"):a(t).removeAttr("disabled"),s+1==q.length?a(u).attr("disabled","disabled"):a(u).removeAttr("disabled"),a(r[s]).show(),s>0?window.location.hash=a(r[s]).attr("data-plenty-checkout-id"):window.location.hash.length>0&&(window.location.hash=""),b&&i("afterChange",s)}function l(a){j(a.index,!0)}function m(){s0&&j(s-1)}function o(b){return"next"==b?(m(),!0):"prev"==b?(n(),!0):(b=b.replace("#",""),a(r).each(function(c,d){return a(d).attr("data-plenty-checkout-id")==b?(j(c),!0):void 0}),!1)}function p(){var b=q.length;if(!(0>=b)){a(q).removeAttr("style"),a(q).children("span").removeAttr("style"),a(u).removeAttr("style"),a(t).removeAttr("style");var c=a(t).outerWidth()c?a(d).children("span").css({paddingLeft:g+"px",paddingRight:h+"px"}):a(d).children("span").css({paddingLeft:j+"px",paddingRight:k+"px"})})}}var q=[],r=[],s=-1,t={},u={},v={beforeChange:[],afterChange:[]},w=[];return{init:e,getCurrentContainer:f,goTo:j,beforeChange:g,afterChange:h,continueChange:l,next:m,previous:n,goToID:o,fillNavigation:p}},["CMSFactory","CheckoutFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("PostfinderService",function(c,d,e){function f(){var b=a('input[name="Street"]').val();return"PACKSTATION"==b.toUpperCase()||"POSTFILIALE"==b.toUpperCase()}function g(){j={PostfinderItemStreet:a('input[name="Street"]','[data-plenty-checkout-form="shippingAddress"]'),PostfinderItemZIP:a('input[name="ZIP"]','[data-plenty-checkout-form="shippingAddress"]'),PostfinderItemCity:a('input[name="City"]','[data-plenty-checkout-form="shippingAddress"]'),PostfinderItemHouseNo:a('input[name="HouseNo"]','[data-plenty-checkout-form="shippingAddress"]')},j.PostfinderItemStreet.val(""),j.PostfinderItemZIP.val().length>2||j.PostfinderItemCity.val().length>2?c.get("/rest/checkout/shippingaddresspostfinderlist/",{suggestionType:"postfinder",zip:j.PostfinderItemZIP.val(),city:j.PostfinderItemCity.val()}).done(function(c){l=c.data,k=l.length,0==k&&h();for(var e={addresses:[]},f=0;k>f;f++){var g="km",m=l[f].PostfinderItemDistance,n=m/1e3;n=(Math.round(100*n)/100).toFixed(2).replace(".",","),1e3>m&&(n=m,g="m"),e.addresses.push({index:f,dimension:g,type:l[f].PostfinderItemIsPackstation?"Packstation":"Postfiliale",number:l[f].PostfinderItemIsPackstation?l[f].PostfinderItemPackstationNo:l[f].PostfinderItemPostfilialNo,street:l[f].PostfinderItemStreet,houseNo:l[f].PostfinderItemHouseNo,zip:l[f].PostfinderItemZIP,city:l[f].PostfinderItemCity,district:l[f].PostfinderItemDistrict,distance:n,remark:l[f].PostfinderItemRemark})}var o=b.compileTemplate("addressSuggestions/postFinder.html",e);d.prepare().setTitle(b.translate("Packstations and post offices in your area")).setContent(o).setClass("checkout").onConfirm(function(){return j.PostfinderItemCity.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemCity.attr("id")+'"]').removeClass("has-error").addClass("has-success"),j.PostfinderItemZIP.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemZIP.attr("id")+'"]').removeClass("has-error").addClass("has-success"),j.PostfinderItemStreet.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemStreet.attr("id")+'"]').removeClass("has-error").addClass("has-success"),j.PostfinderItemHouseNo.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemHouseNo.attr("id")+'"]').removeClass("has-error").addClass("has-success"),i=a('input[type="radio"][name="postfinder"]:checked').val(),l[i].PostfinderItemIsPackstation?(a(j.PostfinderItemStreet).val("PACKSTATION"),a(j.PostfinderItemHouseNo).val(l[i].PostfinderItemPackstationNo)):(a(j.PostfinderItemStreet).val("POSTFILIALE"),a(j.PostfinderItemHouseNo).val(l[i].PostfinderItemPostfilialNo)),a(j.PostfinderItemStreet).trigger("change"),a(j.PostfinderItemCity).val(l[i].PostfinderItemCity),a(j.PostfinderItemZIP).val(l[i].PostfinderItemZIP),!0}).show()}):h()}function h(){e.throwError(0,b.translate("Please enter a ZIP code and/or a city.")),j.PostfinderItemCity.removeClass("has-success").addClass("has-error"),a('label[for="'+j.PostfinderItemCity.attr("id")+'"]').removeClass("has-success").addClass("has-error"),j.PostfinderItemZIP.removeClass("has-success").addClass("has-error"),a('label[for="'+j.PostfinderItemZIP.attr("id")+'"]').removeClass("has-success").addClass("has-error"),j.PostfinderItemCity.focus(function(){a(this).removeClass("has-error");var b=a(this).attr("id");a(this).closest(".form-group").find('[for="'+b+'"]').removeClass("has-error")}),j.PostfinderItemZIP.focus(function(){a(this).removeClass("has-error");var b=a(this).attr("id");a(this).closest(".form-group").find('[for="'+b+'"]').removeClass("has-error")})}var i="",j={},k={},l={};return{openPostfinderModal:g,isPackstation:f}},["APIFactory","ModalFactory","UIFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("SocialShareService",function(){function b(a){var b={"facebook-like":'',"facebook-recommend":'',twitter:'',"google-plus":'
    '};return b[a]}function c(){var b=document.location.href,c=a("link[rel=canonical]").attr("href");return c&&c.length>0&&(c.indexOf("http")<0&&(c=document.location.protocol+"//"+document.location.host+c),b=c),b}function d(b){var c=a('meta[name="'+b+'"]').attr("content");return c||""}function e(){var b=d("DC.title"),c=d("DC.creator");return b.length>0&&c.length>0?b+=" - "+c:b=a("title").text(),encodeURIComponent(b)}return"undefined"==typeof socialLangLocale&&(socialLangLocale="en_US"),"undefined"==typeof socialLang&&(socialLang="en"),{getSocialService:b}})}(jQuery,PlentyFramework),function($,pm){pm.service("ValidationService",function(){function getFormControl(a){return a=$(a),a.is("input")||a.is("select")||a.is("textarea")?a:a.find("input").length>0?a.find("input"):a.find("select").length>0?a.find("select"):a.find("textarea").length>0?a.find("textarea"):null}function validateText(a){return a.is("input")||a.is("select")||a.is("textarea")?$.trim(a.val()).length>0:(console.error("Validation Error: Cannot validate Text for <"+a.prop("tagName")+">"),!1)}function validateMail(a){var b=/[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;return validateText(a)?b.test($.trim(a.val())):!1}function validateNumber(a){return validateText(a)?$.isNumeric($.trim(a.val())):!1}function validateValue(a,b){return $(b).length>0?$.trim(a.val())==$.trim($(b).val()):$.trim(a.val())==b}function visibility(a){return a.is(":visible")}function isEnabled(a){return a.is(":enabled")}function validate(form,errorClass){var formControl,formControls,validationKey,currentHasError,group,checked,checkedMin,checkedMax,attrValidate,validationKeys,formControlAttrType,$form=$(form);errorClass=errorClass||"has-error";var missingFields=[],hasError=!1;$form.find("[data-plenty-validate], input.Required").each(function(i,elem){attrValidate=$(elem).attr("data-plenty-validate"),formControls=getFormControl(elem),validationKeys=attrValidate?attrValidate:"text",validationKeys=validationKeys.split(",");for(var i=0,length=formControls.length;length>i;i++){if(formControl=$(formControls[i]),formControlAttrType=formControl.attr("type"),!visibility(formControl)||!isEnabled(formControl))return;if(validationKey=validationKeys[i].trim()||validationKeys[0].trim(),currentHasError=!1,formControl.is("input")&&"radio"!=formControlAttrType&&"checkbox"!=formControlAttrType||formControl.is("textarea"))switch(validationKey){case"text":currentHasError=!validateText(formControl);break;case"mail":currentHasError=!validateMail(formControl);break;case"number":currentHasError=!validateNumber(formControl);break;case"value":currentHasError=!validateValue(formControl,$(elem).attr("data-plenty-validation-value"));break;case"none":break;default:console.error('Form validation error: unknown validate property: "'+attrValidate+'"')}else if(!formControl.is("input")||"radio"!=formControlAttrType&&"checkbox"!=formControlAttrType){if(!formControl.is("select"))return void console.error("Form validation error: "+$(elem).prop("tagName")+" does not contain an form element");currentHasError=""==formControl.val()||"-1"==formControl.val()}else group=formControl.attr("name"),checked=$form.find('input[name="'+group+'"]:checked').length,"radio"==formControlAttrType?(checkedMin=1,checkedMax=1):(eval("var minMax = "+attrValidate),checkedMin=minMax?minMax.min:1,checkedMax=minMax?minMax.max:1),currentHasError=checkedMin>checked||checked>checkedMax;currentHasError&&(hasError=!0,missingFields.push(formControl),formControls.length>1?(formControl.addClass(errorClass),$form.find('label[for="'+formControl.attr("id")+'"]').addClass(errorClass)):$(elem).addClass(errorClass))}}),$form.on("validationFailed",function(){var a=50,b=$form.find("."+errorClass).first(),c=b.offset().top,d=$("html, body");$form.parents(".modal").length>0?(d=$form.parents(".modal").find(".modal-body"),c=d.scrollTop()-(d.offset().top-b.offset().top)):$form.is(".modal")&&(d=$form.find(".modal-body"),c=d.scrollTop()-(d.offset().top-b.offset().top)),(c-awindow.pageYOffset+window.innerHeight)&&d.animate({scrollTop:c-a})}),hasError&&($form.find("."+errorClass).each(function(a,b){formControl=$(getFormControl(b)),formControl.on("focus click",function(){var a=$(b);a.removeClass(errorClass),$form.find('label[for="'+$(this).attr("id")+'"]').removeClass(errorClass)})}),$form.trigger("validationFailed",[missingFields]));var callback=$form.attr("data-plenty-callback");if(!hasError&&callback&&"submit"!=callback&&"function"==typeof window[callback]){var fields={};return $form.find("input, textarea, select").each(function(){"checkbox"==$(this).attr("type")?fields[$(this).attr("name")]=$(this).is(":checked"):fields[$(this).attr("name")]=$(this).val()}),window[callback](fields),!1}return!hasError}return{validate:validate}}),$.fn.validateForm=function(){return pm.getInstance().ValidationService.validate(this)},$.fn.getFormValues=function(){function a(a,b){var d=a.match(/^([^\[]+)(.*)/);if(d[2]){var e,f=/\[([^\]]+)]/g,g=[];for(g[0]=d[1];null!==(e=f.exec(d[2]));)g.push(e[1]);for(var h=g.length-1;h>=0;h--){var i={};i[g[h]]=b,b=i}c=$.extend(!0,c,b)}else c[d[1]]=b}var b=this,c={};return b.find("input, select, textarea").each(function(c,d){if($(d).attr("name"))if("checkbox"==$(d).attr("type")){var e=[];$(b).find('[name="'+$(d).attr("name")+'"]:checked').each(function(a,b){e.push($(b).val())}),a($(d).attr("name"),e)}else"radio"==$(d).attr("type")?$(d).is(":checked")&&a($(d).attr("name"),$(d).val()):a($(d).attr("name"),$(d).val())}),c}}(jQuery,PlentyFramework),function(a,b){b.directive("Authentication",function(c){function d(d){b.getRecentEvent().preventDefault(),c.customerLogin(a(d))}return{login:d}},["AuthenticationService"])}(jQuery,PlentyFramework),function(a,b){b.directive("Basket",function(c){function d(d){b.getRecentEvent().preventDefault();var e={},f=a(d),g=f.parents("form");e.BasketItemItemID=g.find('[name="ArticleID"]').val(),e.BasketItemPriceID=g.find('[name="SYS_P_ID"]').val(),e.BasketItemQuantity=g.find('[name="ArticleQuantity"]').val(),e.BasketItemBranchID=g.find('[name="source_category"]').val();var h=g.find('[name^="ArticleAttribute"]'),i=[];a.each(h,function(b,c){var d=c.name.match(/^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/);d&&d[1]&&i.push({BasketItemAttributeID:d[1],BasketItemAttributeValueID:a(c).val()})}),0!=i.length&&(e.BasketItemAttributesList=i),c.addItem([e])}function e(b,c){var d=a(b),e=d.parent().find("input"),f=parseInt(e.attr("maxlength"))||5,g=parseInt(e.val())+c,h=d.parents("[data-basket-item-id]").length>0;if(h){(g+"").length<=f&&g>=0&&e.val(g);var i=d.data("timeout");i&&window.clearTimeout(i),i=window.setTimeout(function(){e.trigger("change")},1e3),d.data("timeout",i)}else(g+"").length<=f&&g>=1&&e.val(g)}function f(b,d){c.setItemQuantity(b,parseInt(a(d).val())).fail(function(){var e=c.getItem(b);a(d).val(e.BasketItemQuantity)})}return{addBasketItem:d,changeItemQuantity:e,setItemQuantity:f}},["BasketService"])}(jQuery,PlentyFramework),function(a,b){b.directive("MobileDropdown",function(c){function d(){a(window).on("orientationchange sizeChange",function(){e(h)}),a("html").click(function(a){e(i)})}function e(b){for(var c=0;c0?window.location.assign(a(c).attr("href")):window.location.assign(c))}function e(a){c.goToID(a)}return{to:d,toCheckoutTab:e}},["MediaSizeService","NavigatorService"])}(jQuery,PlentyFramework),function(a,b){b.directive("Tab",function(c){function d(b){a(b).tab("show")}function e(a,b,c){j[c]||(j[c]=new h),j[c].getTab(b)||j[c].addTab(b),j[c].getTab(b).addLabel(a)}function f(a,b,c){j[c]||(j[c]=new h),j[c].getTab(b)||j[c].addTab(b),j[c].getTab(b).setContent(a)}function g(a,d,e){c.isInterval(e)&&(b.getRecentEvent().preventDefault(),j[d]&&j[d].getTab(a)&&j[d].showTab(a))}function h(){function b(a){return g[a]=new i(a),g[a]}function c(b){var c=0;if(f)c=parseInt(f.getContent().parent().css("zIndex")),f.hide(),f.getContent().parent().css("zIndex",c-1);else{for(var d in g)if(g[d].getContent()){var h=parseInt(g[d].getContent().parent().css("zIndex"));(0==c||c>h)&&(c=h),g[d].hide()}for(var d in g)g[d].getContent()&&g[d].getContent().parent().css("zIndex",c-1);a(window).on("sizeChange",e)}f=g[b],f.getContent().parent().css("zIndex",c),f.show()}function d(a){return g[a]}function e(){for(var a in g)g[a].getContent()&&g[a].show();f=null}var f,g={};return{addTab:b,showTab:c,getTab:d,resetTabs:e}}function i(a){function b(){return j}function c(a){return i.push(a),this}function d(a){return h=a,this}function e(){return h}function f(){for(var a=0;a0&&(a(document).scrollTop()>100?t(v,"addClass","visible"):t(v,"removeClass","visible"))})}function f(b){a(b).owlCarousel({navigation:!0,navigationText:!1,slideSpeed:1e3,paginationSpeed:1e3,singleItem:!0,autoPlay:6e3,stopOnHover:!0,afterMove:function(b){a(b).find('img[data-plenty-rel="lazyload"]').trigger("appear")}})}function g(b,d,e){var f=a(b),g=0,h={},i=f.find('[data-plenty-rel="equal-target"]').length>0?f.find('[data-plenty-rel="equal-target"]'):f.children();e!==!0&&u.push(b);for(var j=i.length;j>=0;j--)h=a(i[j]),h.css("height",""),h.outerHeight(!0)>g&&(g=h.outerHeight(!0));(!d||c.isInterval(d))&&i.height(g)}function h(b){var c=a(b);c.click(function(){return a("html, body").animate({scrollTop:0},400),!1}),a.inArray(c,v)&&v.push(c)}function i(b,c){var d=a(b);d.lazyload({effect:c}),d.on("loaded",function(){d.css("display","inline-block")})}function j(b){console.log(b);var c=a(b),d=c.parent();d.addClass("animating"),c.siblings("ul").slideToggle(200,function(){d.is(".open")?d.removeClass("open"):d.addClass("open"),c.siblings("ul").removeAttr("style"),d.removeClass("animating")})}function k(b,c){var d=a(b),e=a(d.attr("data-plenty-rel"));if(d.is('input[type="radio"]')){var f=a('input[type="radio"][name="'+d.attr("name")+'"]'),g=!c||"checked"==c;f.change(function(){var b=a(this);e.parents('[data-plenty-rel="equal-target"]').css("height","auto"),b.is(":checked")&&b[0]===d[0]&&1==g?e.slideDown(400,function(){s()}):e.slideUp(400,function(){s()})})}else d.click(function(){d.addClass("animating"),e.slideToggle(400,function(){d.removeClass("animating"),d.toggleClass("active"),s()})})}function l(b,c){c=c||400,a(b).parents('[data-plenty-rel="equal-target"]').css("height","auto"),a(b).slideDown(c,function(){s()})}function m(b,c){c=c||400,a(b).parents('[data-plenty-rel="equal-target"]').css("height","auto"),a(b).slideUp(c,function(){s()})}function n(b,c){c=c||400,a(b).parents('[data-plenty-rel="equal-target"]').css("height","auto"),a(b).slideToggle(c,function(){s()})}function o(b,c){var e=a(b),f=e.find('[data-plenty-rel="social-switch"]');e.append(''),f.is("off, on")||f.addClass("off"),f.on("click",function(){f.hasClass("off")&&("tooltip"==e.attr("data-toggle")&&e.tooltip("destroy"),f.removeClass("off").addClass("on"),e.find('[data-plenty-rel="social-placeholder"]').hide(),e.find(".social-container").append(d.getSocialService(c)))})}function p(d,e,f){if(e&&d&&(!f||c.isInterval(f))){var g=b.getRecentEvent();return g&&g.preventDefault(),a(e).toggleClass(d),!1}}function q(d,e,f){if(e&&d&&(!f||c.isInterval(f))){var g=b.getRecentEvent();return g&&g.preventDefault(),a(e).addClass(d),!1}}function r(d,e,f){if(e&&d&&(!f||c.isInterval(f))){var g=b.getRecentEvent();return g&&g.preventDefault(),a(e).removeClass(d),!1}}function s(){for(var a=u.length-1;a>=0;a--)g(u[a],"",!0)}function t(a,b,c){for(var d=a.length-1;d>=0;d--)a[d][b](c)}var u=[],v=[];return{initUIWindowEvents:e,addContentPageSlider:f,equalHeight:g,initToTop:h,initLazyload:i,initSlideToggle:k,slideDown:l,slideUp:m,slideToggle:n,toggleHideShow:j,toggleSocialShare:o,toggleClass:p,addClass:q,removeClass:r}},["MediaSizeService","SocialShareService"])}(jQuery,PlentyFramework),function(a,b){b.directive("Validator",function(a){function b(b,c){return a.validate(b,c)}return{validate:b}},["ValidationService"])}(jQuery,PlentyFramework),PlentyFramework.compile();var plenty=PlentyFramework.getInstance();jQuery(document).ready(function(){plenty.bindDirectives()}); \ No newline at end of file diff --git a/dist/plentymarketsCMStools-1.0.1.js b/dist/plentymarketsCMStools-1.0.2.js similarity index 96% rename from dist/plentymarketsCMStools-1.0.1.js rename to dist/plentymarketsCMStools-1.0.2.js index 85181f0..61783c5 100644 --- a/dist/plentymarketsCMStools-1.0.1.js +++ b/dist/plentymarketsCMStools-1.0.2.js @@ -271,11 +271,7 @@ TemplateCache["waitscreen/waitscreen.html"] = "
    ' ) + ' -> ' + dependency ); + } + } + + // compile component + if( componentLevel == 3 ) + { + PlentyFramework.factories[component.name] = component.setup.apply( null, compiledDependencies ); + } + else if( componentLevel == 2 ) + { + PlentyFramework.prototype[component.name] = component.setup.apply( null, compiledDependencies ); + } + else if( componentLevel == 1 ) + { + PlentyFramework.directives[component.name] = component.setup.apply( null, compiledDependencies ); + } + } + }( jQuery )); @@ -3298,7 +3293,7 @@ PlentyFramework.cssClasses = { { if ( shippingAddress.Street == "PACKSTATION" ) { - shippingAddress.isPackstation = 1; + shippingAddress.IsPackstation = 1; shippingAddress.PackstationNo = shippingAddress.HouseNo; } else if ( shippingAddress.Street == "POSTFILIALE" ) @@ -3763,6 +3758,133 @@ PlentyFramework.cssClasses = { * ===================================================================================== */ +/** + * @module Services + */ +(function($, pm) { + + pm.service('FeedbackService', function( API ) { + + return { + getFeedbacks: getFeedbacks, + addFeedback: addFeedback, + ArticleTypes: articleTypes(), + FeedbackTypes: feedbackTypes() + }; + + /* + FeedbackService + .getFeedbacks().between('2014-12-03', '2015-07-01') + .for( FeedbackService.ArticleTypes.ITEM, 2732, FeedbackService.FeedbackTypes.COMMENTS_ONLY ); + */ + function getFeedbacks() { + var feedbackInterval = { + dateStart: null, + dateEnd: null + }; + + return { + between: setFeedbackInterval, + for: listFeedbacks + }; + + function setFeedbackInterval( start, end ) { + feedbackInterval.dateStart = start; + feedbackInterval.dateEnd = end; + return this; + } + + function listFeedbacks( articleType, referenceId, feedbackType ) { + + var params = { + ReferenceId: referenceId, + FromDate: feedbackInterval.dateStart, + ToDate: feedbackInterval.dateEnd, + FeedbackType: feedbackType || feedbackTypes().COMMENTS_AND_RATINGS + }; + return API.get( '/rest/feedback/'+articleType+'/', params ); + + } + } + + /* + FeedbackService + .addFeedback() + .withRating( 5 ) + .withComment( 'Hallo' ) + .withAuthor( 'Felix', 'felix.dausch@plentymarkets.com', 123456 ) + .to( FeedbackService.ArticleTypes.ITEM, 2732 ); + */ + function addFeedback() { + + var params = { + Rating: 1.0, + Text: '', + Author: '', + Email: '', + CustomerId: 0 + }; + + return { + withRating: withRating, + withComment: withComment, + withAuthor: withAuthor, + to: sendFeedback + }; + + function withRating( rating ) { + params.Rating = rating; + return this; + } + + function withComment( comment ) { + params.Text = comment; + return this; + } + + function withAuthor( author, mail, customerID ) { + params.Author = author; + if( !!mail ) params.Email = mail; + if( !!customerID ) params.CustomerId = customerID; + return this; + } + + function sendFeedback( articleType, referenceId ) { + return API.post( '/rest/feedback/'+articleType+'/', params ); + + } + + } + + function feedbackTypes() { + return { + COMMENTS_ONLY: 'comments_only', + RATINGS_ONLY: 'ratings_only', + COMMENTS_AND_RATINGS: 'comments_with_ratings' + } + } + + function articleTypes() { + return { + ITEM: 'item', + CATEGORY: 'category', + BLOG: 'blog' + } + } + + + + }, ['APIFactory']); +}(jQuery, PlentyFramework)); +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== + */ + /** * @module Services */ diff --git a/dist/plentymarketsCMStools-1.0.2.min.js b/dist/plentymarketsCMStools-1.0.2.min.js new file mode 100644 index 0000000..52dc104 --- /dev/null +++ b/dist/plentymarketsCMStools-1.0.2.min.js @@ -0,0 +1,10 @@ +/** + * Licensed under AGPL v3 + * (https://github.com/plentymarkets/plenty-cms-library/blob/master/LICENSE) + * ===================================================================================== + * @copyright Copyright (c) 2015, plentymarkets GmbH (http://www.plentymarkets.com) + * @author Felix Dausch + * ===================================================================================== +*/!function(a,b){"object"==typeof exports&&exports&&"string"!=typeof exports.nodeName?b(exports):"function"==typeof define&&define.amd?define(["exports"],b):(a.Mustache={},b(Mustache))}(this,function(a){function b(a){return"function"==typeof a}function c(a){return p(a)?"array":typeof a}function d(a){return a.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function e(a,b){return null!=a&&"object"==typeof a&&b in a}function f(a,b){return q.call(a,b)}function g(a){return!f(r,a)}function h(a){return String(a).replace(/[&<>"'\/]/g,function(a){return s[a]})}function i(b,c){function e(){if(r&&!s)for(;q.length;)delete o[q.pop()];else q=[];r=!1,s=!1}function f(a){if("string"==typeof a&&(a=a.split(u,2)),!p(a)||2!==a.length)throw new Error("Invalid tags: "+a);h=new RegExp(d(a[0])+"\\s*"),i=new RegExp("\\s*"+d(a[1])),m=new RegExp("\\s*"+d("}"+a[1]))}if(!b)return[];var h,i,m,n=[],o=[],q=[],r=!1,s=!1;f(c||a.tags);for(var y,z,A,B,C,D,E=new l(b);!E.eos();){if(y=E.pos,A=E.scanUntil(h))for(var F=0,G=A.length;G>F;++F)B=A.charAt(F),g(B)?q.push(o.length):s=!0,o.push(["text",B,y,y+1]),y+=1,"\n"===B&&e();if(!E.scan(h))break;if(r=!0,z=E.scan(x)||"name",E.scan(t),"="===z?(A=E.scanUntil(v),E.scan(v),E.scanUntil(i)):"{"===z?(A=E.scanUntil(m),E.scan(w),E.scanUntil(i),z="&"):A=E.scanUntil(i),!E.scan(i))throw new Error("Unclosed tag at "+E.pos);if(C=[z,A,y,E.pos],o.push(C),"#"===z||"^"===z)n.push(C);else if("/"===z){if(D=n.pop(),!D)throw new Error('Unopened section "'+A+'" at '+y);if(D[1]!==A)throw new Error('Unclosed section "'+D[1]+'" at '+y)}else"name"===z||"{"===z||"&"===z?s=!0:"="===z&&f(A)}if(D=n.pop())throw new Error('Unclosed section "'+D[1]+'" at '+E.pos);return k(j(o))}function j(a){for(var b,c,d=[],e=0,f=a.length;f>e;++e)b=a[e],b&&("text"===b[0]&&c&&"text"===c[0]?(c[1]+=b[1],c[3]=b[3]):(d.push(b),c=b));return d}function k(a){for(var b,c,d=[],e=d,f=[],g=0,h=a.length;h>g;++g)switch(b=a[g],b[0]){case"#":case"^":e.push(b),f.push(b),e=b[4]=[];break;case"/":c=f.pop(),c[5]=b[2],e=f.length>0?f[f.length-1][4]:d;break;default:e.push(b)}return d}function l(a){this.string=a,this.tail=a,this.pos=0}function m(a,b){this.view=a,this.cache={".":this.view},this.parent=b}function n(){this.cache={}}var o=Object.prototype.toString,p=Array.isArray||function(a){return"[object Array]"===o.call(a)},q=RegExp.prototype.test,r=/\S/,s={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},t=/\s*/,u=/\s+/,v=/\s*=/,w=/\s*\}/,x=/#|\^|\/|>|\{|&|=|!/;l.prototype.eos=function(){return""===this.tail},l.prototype.scan=function(a){var b=this.tail.match(a);if(!b||0!==b.index)return"";var c=b[0];return this.tail=this.tail.substring(c.length),this.pos+=c.length,c},l.prototype.scanUntil=function(a){var b,c=this.tail.search(a);switch(c){case-1:b=this.tail,this.tail="";break;case 0:b="";break;default:b=this.tail.substring(0,c),this.tail=this.tail.substring(c)}return this.pos+=b.length,b},m.prototype.push=function(a){return new m(a,this)},m.prototype.lookup=function(a){var c,d=this.cache;if(d.hasOwnProperty(a))c=d[a];else{for(var f,g,h=this,i=!1;h;){if(a.indexOf(".")>0)for(c=h.view,f=a.split("."),g=0;null!=c&&gi;++i)g=void 0,e=a[i],f=e[0],"#"===f?g=this.renderSection(e,b,c,d):"^"===f?g=this.renderInverted(e,b,c,d):">"===f?g=this.renderPartial(e,b,c,d):"&"===f?g=this.unescapedValue(e,b):"name"===f?g=this.escapedValue(e,b):"text"===f&&(g=this.rawValue(e)),void 0!==g&&(h+=g);return h},n.prototype.renderSection=function(a,c,d,e){function f(a){return g.render(a,c,d)}var g=this,h="",i=c.lookup(a[1]);if(i){if(p(i))for(var j=0,k=i.length;k>j;++j)h+=this.renderTokens(a[4],c.push(i[j]),d,e);else if("object"==typeof i||"string"==typeof i||"number"==typeof i)h+=this.renderTokens(a[4],c.push(i),d,e);else if(b(i)){if("string"!=typeof e)throw new Error("Cannot use higher-order sections without the original template");i=i.call(c.view,e.slice(a[3],a[5]),f),null!=i&&(h+=i)}else h+=this.renderTokens(a[4],c,d,e);return h}},n.prototype.renderInverted=function(a,b,c,d){var e=b.lookup(a[1]);return!e||p(e)&&0===e.length?this.renderTokens(a[4],b,c,d):void 0},n.prototype.renderPartial=function(a,c,d){if(d){var e=b(d)?d(a[1]):d[a[1]];return null!=e?this.renderTokens(this.parse(e),c,d,e):void 0}},n.prototype.unescapedValue=function(a,b){var c=b.lookup(a[1]);return null!=c?c:void 0},n.prototype.escapedValue=function(b,c){var d=c.lookup(b[1]);return null!=d?a.escape(d):void 0},n.prototype.rawValue=function(a){return a[1]},a.name="mustache.js",a.version="2.1.3",a.tags=["{{","}}"];var y=new n;a.clearCache=function(){return y.clearCache()},a.parse=function(a,b){return y.parse(a,b)},a.render=function(a,b,d){if("string"!=typeof a)throw new TypeError('Invalid template! Template should be a "string" but "'+c(a)+'" was given as the first argument for mustache#render(template, view, partials)');return y.render(a,b,d)},a.to_html=function(c,d,e,f){var g=a.render(c,d,e);return b(f)?void f(g):g},a.escape=h,a.Scanner=l,a.Context=m,a.Writer=n}),Object.equals=function(a,b){if(a===b)return!0;if(!(a instanceof Object&&b instanceof Object))return!1;if(a.constructor!==b.constructor)return!1;for(var c in a)if(a.hasOwnProperty(c)){if(!b.hasOwnProperty(c))return!1;if(a[c]!==b[c]){if("object"!=typeof a[c])return!1;if(!Object.equals(a[c],b[c]))return!1}}for(var c in b)if(b.hasOwnProperty(c)&&!a.hasOwnProperty(c))return!1;return!0};var TemplateCache={};TemplateCache["addressSuggestions/addressDoctor.html"]='',TemplateCache["addressSuggestions/postFinder.html"]='{{#addresses}}\n
    \n
    \n \n
    \n
    \n{{/addresses}}\n',TemplateCache["error/errorMessage.html"]='
    \n Code {{code}}:\n {{{message}}}\n
    \n',TemplateCache["error/errorPopup.html"]='
    \n \n
    \n
    \n
    \n',TemplateCache["modal/modal.html"]='\n',TemplateCache["waitscreen/waitscreen.html"]='
    ',function(a){function b(a,b,c,d){a.on(b,function(a){return h.push(a),c.apply(null,d)})}function c(b){var c=a(b);c.is('input[type="checkbox"]')&&c.on("change",function(){c.is(":checked")?c.trigger("check"):c.trigger("uncheck")}),c.is('input[type="radio"]')&&c.on("change",function(){var b=c.attr("name");a('input[type="radio"][name="'+b+'"]').each(function(b,c){var d=a(c);d.is(":checked")?d.trigger("check"):d.trigger("uncheck")})})}function d(a,b){for(var c=/^(([\w]+):)?([\w]+)\.([\w]+)(\((.*)\))?$/,d=a.split(";"),e=[],f=0;f0)for(var j=h[6].match(/(['][^']+['])|([\w-]+)|(["][^"]+["])/g),k=0;k=c&&f.services.hasOwnProperty(i)){PlentyFramework.prototype.hasOwnProperty(i)||e(f.services[i],2,d),g.push(PlentyFramework.prototype[i]);continue}if(1>=c&&f.directives.hasOwnProperty(i)){PlentyFramework.directives.hasOwnProperty(i)||e(f.directives[i],1,d),g.push(PlentyFramework.directives[i]);continue}console.error('Cannot inject dependency "'+i+'": Object not found.')}else console.error("Cyclic dependency injection: "+d.join(" -> ")+" -> "+i)}3==c?PlentyFramework.factories[b.name]=b.setup.apply(null,g):2==c?PlentyFramework.prototype[b.name]=b.setup.apply(null,g):1==c&&(PlentyFramework.directives[b.name]=b.setup.apply(null,g))}var f={factories:{},services:{},directives:{}};PlentyFramework=function(){};var g=null;PlentyFramework.getInstance=function(){return g=g||new PlentyFramework},PlentyFramework.partials={},PlentyFramework.globals={},PlentyFramework.setGlobal=function(a,b){return PlentyFramework.globals.hasOwnProperty(a)?(console.error('Global variable "'+a+'" already exists and cannot be overridden.'),null):(PlentyFramework.globals[a]=b,PlentyFramework.globals[a])},PlentyFramework.getGlobal=function(a){return PlentyFramework.globals[a]},PlentyFramework.directives={},PlentyFramework.directive=function(a,b,c){return"string"!=typeof a?void console.error("Type mismatch: Expect first parameter to be a 'string', '"+typeof a+"' given."):"function"!=typeof b?void console.error("Type mismatch: Expect second parameter to be a 'function', '"+typeof b+"' given."):(c=c||[],void(f.directives[a]={name:a,dependencies:c,setup:b}))},PlentyFramework.prototype.bindDirectives=function(e){e=e||"html",a(e).find("[data-plenty]").each(function(e,f){var g=d(a(f).attr("data-plenty"),a(f));if(!(g.length<=0)){c(f);for(var e=0;e=0;c--)if(a==h[c].type)return h[c];return null},PlentyFramework.pushEvent=function(a){h.push(a)},PlentyFramework.service=function(a,b,c){return"string"!=typeof a?void console.error("Type mismatch: Expect first parameter to be a 'string', '"+typeof a+"' given."):"function"!=typeof b?void console.error("Type mismatch: Expect second parameter to be a 'function', '"+typeof b+"' given."):(c=c||[],void(f.services[a]={name:a,dependencies:c,setup:b}))},PlentyFramework.factories={},PlentyFramework.factory=function(a,b,c){return"string"!=typeof a?void console.error("Type mismatch: Expect first parameter to be a 'string', '"+typeof a+"' given."):"function"!=typeof b?void console.error("Type mismatch: Expect second parameter to be a 'function', '"+typeof b+"' given."):(c=c||[],void(f.factories[a]={name:a,dependencies:c,setup:b}))},PlentyFramework.compileTemplate=function(a,b){return b=b||{},b.translate=function(){return function(a,b){return b(PlentyFramework.translate(a))}},Mustache.render(TemplateCache[a],b)},PlentyFramework.scriptPath="",PlentyFramework.Strings={},PlentyFramework.loadLanguageFile=function(b){a.get(PlentyFramework.scriptPath+b).done(function(a){PlentyFramework.Strings=a})},PlentyFramework.translate=function(a,b){var c;return PlentyFramework.Strings.hasOwnProperty(a)?c=PlentyFramework.Strings[a]:(c=a,console.warn('No translation found for "'+c+'".')),b&&(c=Mustache.render(c,b)),c},PlentyFramework.compile=function(){for(var a in f.factories)PlentyFramework.factories.hasOwnProperty(a)||e(f.factories[a],3);for(var b in f.services)PlentyFramework.prototype.hasOwnProperty(b)||e(f.services[b],2);for(var c in f.directives)PlentyFramework.directives.hasOwnProperty(c)||e(f.directives[c],1);var d=document.getElementsByTagName("SCRIPT");d.length>0&&(PlentyFramework.scriptPath=d[d.length-1].src.match(/(.*)\/(.*)\.js(\?\S*)?$/)[1])}}(jQuery),PlentyFramework.cssClasses={active:"active"},function(a,b){b.partials.Error={init:function(b){a(b).find(".close").click(function(){b.hide(),b.find(".plentyErrorBoxInner").html("")})},addError:function(b,c){var d=a(c).attr("data-plenty-error-code");a(b).find('[data-plenty-error-code="'+d+'"]').length<=0&&a(b).find(".plentyErrorBoxInner").append(c)},show:function(b){a(b).show()}}}(jQuery,PlentyFramework),function(a,b){b.partials.Modal={init:function(a,b){a.on("hidden.bs.modal",function(){b.hide(),a.remove()}),b.timeout>0&&(a.on("hide.bs.modal",b.stopTimeout),a.find(".modal-content").hover(function(){b.pauseTimeout()},function(){a.is(".in")&&b.continueTimeout()}))},show:function(a){a.modal("show")},hide:function(a){a.modal("hide")},isModal:function(b){return a(b).filter(".modal").length+a(b).find(".modal").length>0},getModal:function(b){var c=a(b);return c.length>1&&(c=a(b).filter(".modal")||a(b).find(".modal")),c}}}(jQuery,PlentyFramework),function(a){a(document).on("initPartials",function(b,c){a(c).find('[data-toggle="tooltip"]').tooltip({container:"body"})})}(jQuery),function(a,b){b.partials.WaitScreen={show:function(a){a.addClass("in")},hide:function(a){a.removeClass("in")}}}(jQuery,PlentyFramework),function(a,b){b.factory("APIFactory",function(b){function c(c){try{var d=a.parseJSON(c.responseText);b.printErrors(d.error.error_stack)}catch(e){b.throwError(c.status,c.statusText)}}function d(d,e,f,g,h){return g||b.showWaitScreen(),a.ajax(d,{type:"GET",data:e,dataType:"json",async:!h,error:function(a){f||c(a)}}).always(function(){g||b.hideWaitScreen()})}function e(d,e,f,g){var h={type:"POST",dataType:"json",error:function(a){f||c(a)}};return e&&e.isFile?(h.cache=e.cache,h.processData=e.processData,h.data=e.data,h.contentType=!1):(h.data=JSON.stringify(e),h.contentType="application/json"),g||b.showWaitScreen(),a.ajax(d,h).always(function(){g||b.hideWaitScreen()})}function f(d,e,f,g){return g||b.showWaitScreen(),a.ajax(d,{type:"PUT",data:JSON.stringify(e),dataType:"json",contentType:"application/json",error:function(a){f||c(a)}}).always(function(){g||b.hideWaitScreen()})}function g(d,e,f,g){return g||b.showWaitScreen(),a.ajax(d,{type:"DELETE",data:JSON.stringify(e),dataType:"json",contentType:"application/json",error:function(a){f||c(a)}}).always(function(){g||b.hideWaitScreen()})}function h(){return a.Deferred().resolve()}return{get:d,post:e,put:f,"delete":g,idle:h}},["UIFactory"])}(jQuery,PlentyFramework),function(a){a.factory("CMSFactory",function(a){function b(b,c){function d(d){return a.get("/rest/"+d.toLowerCase()+"/container_"+b.toLowerCase()+"/",c)}return{from:d}}function c(b,c){function d(d){return a.get("/rest/"+d.toLowerCase()+"/"+b.toLowerCase()+"/",c)}return{from:d}}function d(b){return a.get("/rest/categoryview/categorycontentbody/?categoryID="+b)}return{getContainer:b,getParams:c,getCategoryContent:d}},["APIFactory"])}(PlentyFramework),function(a){a.factory("CheckoutFactory",function(b,c,d){function e(){return l}function f(a){return m&&l||g(!0),a?$.extend(!0,{},l):m}function g(a){return b.get("/rest/checkout/",null,!1,!1,a).done(function(a){a?(l=a.data,m=new e):d.throwError(0,'Could not receive checkout data [GET "/rest/checkout/" receives null value]')})}function h(){return b.put("/rest/checkout",m).done(function(a){a?(l=a.data,m=new e):d.throwError(0,'Could not receive checkout data [GET "/rest/checkout/" receives null value]')})}function i(b){return c.getContainer("checkout"+b).from("checkout").done(function(c){$('[data-plenty-checkout-template="'+b+'"]').each(function(b,d){$(d).html(c.data[0]),a.getInstance().bindDirectives(d),$(window).trigger("contentChanged")})})}function j(b){return c.getCategoryContent(b).done(function(c){$('[data-plenty-checkout-catcontent="'+b+'"]').each(function(b,d){$(d).html(c.data[0]),a.getInstance().bindDirectives(d),$(window).trigger("contentChanged")})})}function k(b){return c.getContainer("itemview"+b).from("itemview").done(function(c){$('[data-plenty-itemview-template="'+b+'"]').each(function(b,d){$(d).html(c.data[0]),a.getInstance().bindDirectives(d),$(window).trigger("contentChanged")})})}var l,m;return{getCheckout:f,setCheckout:h,loadCheckout:g,reloadContainer:i,reloadCatContent:j,reloadItemContainer:k}},["APIFactory","CMSFactory","UIFactory"])}(PlentyFramework),function(a,b){b.factory("ModalFactory",function(){function c(a){return PlentyFramework.partials.Modal.isModal(a)}function d(){return new e}function e(){function d(a){return s.title=a,this}function e(a){return s.cssClass=a,this}function f(a){return s.content=a,this}function g(a){return s.labelConfirm=a,this}function h(a){return s.labelDismiss=a,this}function i(a){return s.onConfirm=a,this}function j(a){return s.onDismiss=a,this}function k(a){return s.container=a,this}function l(a){return s.timeout=a,this}function m(){t=c(s.content)?PlentyFramework.partials.Modal.getModal(s.content):a(PlentyFramework.compileTemplate("modal/modal.html",s)),a(s.container).append(t);var b=a(s.content).filter("script");b.length>0&&b.each(function(b,c){var d=document.createElement("script");d.type="text/javascript",d.innerHTML=a(c).text(),a(s.container).append(d)}),PlentyFramework.partials.Modal.init(t,s),t.find('[data-plenty-modal="confirm"]').click(function(){var a=s.onConfirm();"undefined"==typeof a&&(a=!0),a&&n(!0)}),PlentyFramework.partials.Modal.show(t),s.timeout>0&&o()}function n(a){PlentyFramework.partials.Modal.hide(t),a||s.onDismiss()}function o(){w=s.timeout,x=(new Date).getTime(),u=window.setTimeout(function(){window.clearInterval(v),n()},s.timeout),t.find('[data-plenty-modal="timer"]').text(w/1e3),v=window.setInterval(function(){if(!y){var a=w-(new Date).getTime()+x;a=Math.round(a/1e3),t.find('[data-plenty-modal="timer"]').text(a)}},1e3)}function p(){y=!0,w-=(new Date).getTime()-x,window.clearTimeout(u)}function q(){y=!1,x=(new Date).getTime(),u=window.setTimeout(function(){n(),window.clearInterval(v)},w)}function r(){window.clearTimeout(u),window.clearInterval(v)}var s=this;s.title="",s.cssClass="",s.content="",s.labelDismiss=b.translate("Cancel"),s.labelConfirm=b.translate("Confirm"),s.onConfirm=function(){},s.onDismiss=function(){},s.container="body",s.timeout=-1,s.hide=n,s.startTimeout=o,s.stopTimeout=r,s.pauseTimeout=p,s.continueTimeout=q;var t,u,v,w,x,y=!1;return{setTitle:d,setClass:e,setContent:f,setContainer:k,setLabelConfirm:g,setLabelDismiss:h,onConfirm:i,onDismiss:j,setTimeout:l,show:m,hide:n}}return{prepare:d,isModal:c}})}(jQuery,PlentyFramework),function(a,b){b.factory("UIFactory",function(){function c(a,b){d([{code:a,message:b}])}function d(c){(!i||a("body").has(i).length<=0)&&(i=a(b.compileTemplate("error/errorPopup.html")),a("body").append(i),b.partials.Error.init(i)),a.each(c,function(c,d){b.partials.Error.addError(i,a(b.compileTemplate("error/errorMessage.html",d)))}),b.partials.Error.show(i),f(!0)}function e(){return h=h||0,(!g||a("body").has(g).length<=0)&&(g=a(b.compileTemplate("waitscreen/waitscreen.html")),a("body").append(g)),b.partials.WaitScreen.show(g),h++,h}function f(a){return h--,(0>=h||a)&&(h=0,b.partials.WaitScreen.hide(g)),h}var g,h=0,i=null;return{throwError:c,printErrors:d,showWaitScreen:e,hideWaitScreen:f}})}(jQuery,PlentyFramework),function(a,b){b.service("AddressDoctorService",function(c){function d(b){var c=!0;return b=b||"[data-plenty-address-doctor]",a(b).filter("[data-plenty-address-doctor]:visible").each(function(b,d){var f=new e(d),g=a(d).attr("data-plenty-address-doctor").replace(/\s/g,"").split(",");f.isValid(g)||(c=!1)}),c}function e(c){function d(a){return i()?!0:(j=new f(l.getFormValues()),k=a,e(),1==j.getAddresses().length)}function e(){a(".suggestion-list").remove();for(var b=!1,c=0;cc;c++){var d=a.data[c],f=e(d);f?f.HouseNo.push(d.HouseNo):(d.HouseNo=[d.HouseNo],j.push(d))}})}function e(a){for(var b=j.length,c=0;b>c;c++)if(a.Street==j[c].Street&&j.ZIP==j[c].ZIP&&a.City==j[c].City)return j[c];return null}function f(){return j}function g(b){for(var c=[],d=j.length,e=0;d>e;e++){var f=j[e];a.inArray(f[b],c)<0&&c.push(f[b])}return c}function h(a){for(var b=[],c=j.length,d=0;c>d;d++){var e=j[d];(a.Street&&a.Street==e.Street||a.ZIP&&a.ZIP==e.ZIP||a.City&&a.City==e.City)&&b.push(e)}j=b}function i(a){a=parseInt(a);for(var b=j.length,c=0;b>c;c++)for(var d=j[c],e=0;e=f[0]&&a<=f[1])return!0}return!1}var j=[];return d(),{getAddresses:f,getList:g,filter:h,houseNoAllowed:i}}return{validateAddress:d}},["APIFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("AuthenticationService",function(c,d,e){function f(){var b=a('[data-plenty-checkout="lostPasswordForm"]');if(b.validateForm()){var d=b.getFormValues(),e={Email:d.Email};return c.post("/rest/checkout/lostpassword/",e).done(function(b){1==b.data.IsMailSend&&(a('[data-plenty-checkout="lostPasswordTextContainer"]').hide(),a('[data-plenty-checkout="lostPasswordSuccessMessage"]').show())})}}function g(a){if(a.validateForm()){var b=a.getFormValues(),d={Email:b.loginMail,Password:b.loginPassword};return e.showWaitScreen(),c.post("/rest/checkout/login/",d).done(function(){window.location.assign(a.attr("action"))})}}function h(a){return c.post("/rest/checkout/customerinvoiceaddress/",a).done(function(a){d.getCheckout().CustomerInvoiceAddress=a.data})}function i(){var c=a('[data-plenty-checkout-form="customerRegistration"]');if(c.validateForm()&&b.getInstance().AddressDoctorService.validateAddress()){var d=c.getFormValues(),e={LoginType:2,FormOfAddressID:d.FormOfAddressID,Company:d.Company,FirstName:d.FirstName,LastName:d.LastName,Street:d.Street,HouseNo:d.HouseNo,AddressAdditional:d.AddressAdditional,ZIP:d.ZIP,City:d.City,CountryID:d.CountryID,VATNumber:d.VATNumber,Email:d.Email,EmailRepeat:d.EmailRepeat,BirthDay:d.BirthDay,BirthMonth:d.BirthMonth,BirthYear:d.BirthYear,Password:d.Password,PasswordRepeat:d.PasswordRepeat,PhoneNumber:d.PhoneNumber,MobileNumber:d.MobileNumber,FaxNumber:d.FaxNumber,Postnummer:d.Postnummer};return e.CustomerPropertiesList=e.CustomerPropertiesList||[],c.find("[data-plenty-property-id]").each(function(b,c){e.CustomerPropertiesList.push({PropertyID:a(c).attr("data-plenty-property-id"),PropertyValue:a(c).val()})}),h(e).done(function(){window.location.assign(c.attr("action"))})}}return{resetPassword:f,customerLogin:g,setInvoiceAddress:h,registerCustomer:i}},["APIFactory","CheckoutFactory","UIFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("BasketService",function(c,d,e,f,g){function h(d){d&&c.get("/rest/checkout/container_"+"CheckoutOrderParamsList".toLowerCase()+"/",{itemID:d[0].BasketItemItemID,quantity:d[0].BasketItemQuantity},!1,!0).done(function(c){c.data[0].indexOf("form-group")>0?g.prepare().setContent(c.data[0]).setTitle(b.translate("Select order parameters")).setLabelConfirm(b.translate("Save")).onConfirm(function(){return a('[data-plenty-checkout-form="OrderParamsForm"]').validateForm()?(j(i(d)),!0):!1}).show():j(d)})}function i(b){var c,d=a('[data-plenty-checkout-form="OrderParamsForm"]'),e={},f="";return d.find('[name^="ParamGroup"]').each(function(){c=this.name.match(/^ParamGroup\[(\d+)]\[(\d+)]$/),b=m(b,c[1],a(this).val(),a(this).val())}),d.find('[name^="ParamValue"]').each(function(){if(e=a(this),f=e.attr("type"),("checkbox"==f&&e.is(":checked")||"radio"==f&&e.is(":checked")||"radio"!=f&&"checkbox"!=f)&&"file"!=f&&"hidden"!=f){var c=e[0].name.match(/^ParamValue\[(\d+)]\[(\d+)]$/);b=m(b,c[1],c[2],e.val())}else if("file"==f)if(e[0].files&&e[0].files.length>0)b=l(e,b);else{var c=e[0].name.match(/^ParamValueFile\[(\d+)]\[(\d+)]$/),d=a('input[type="hidden"][name="ParamValue['+c[1]+"]["+c[2]+']"]').val();b=m(b,c[1],c[2],d)}}),b}function j(a){c.post("/rest/checkout/basketitemslist/",a,!0).done(function(){f.loadCheckout().done(function(){s(),e.getContainer("ItemViewItemToBasketConfirmationOverlay",{ArticleID:a[0].BasketItemItemID}).from("ItemView").done(function(a){g.prepare().setContent(a.data[0]).setTimeout(5e3).show()})})}).fail(function(a){d.printErrors(JSON.parse(a.responseText).error.error_stack)})}function k(a){c.put("/rest/checkout/basketitemslist/",a).done(function(){f.reloadCatContent(b.getGlobal("basketCatID")),f.loadCheckout().done(function(){s()})})}function l(a,b){var d,e,f=a[0].id,g={},h=[],i={type:"POST",data:{},isFile:!0,cache:!1,dataType:"json",processData:!1,contentType:!1};g[f]=a[0].files,-1==h.indexOf(f)&&h.push(f);for(var j=0,k=h.length;k>j;++j)d=new FormData,e=g[h[j]],d.append("0",e[0],e[0].name),i.data=d,c.post("/rest/checkout/orderparamfile/",i);var l=a[0].name.match(/^ParamValueFile\[(\d+)]\[(\d+)]$/);return m(b,l[1],l[2],a.val())}function m(b,c,d,e){return c>0&&void 0==b[c]&&(b[c]=a.extend(!0,{},b[0]),b[c].BasketItemOrderParamsList=[]),void 0!=b[c]&&(b[c].BasketItemQuantity=1,void 0==b[c].BasketItemOrderParamsList&&(b[c].BasketItemOrderParamsList=[]),e&&b[c].BasketItemOrderParamsList.push({BasketItemOrderParamID:d,BasketItemOrderParamValue:e})),b}function n(b){var c=a('[data-plenty-basket-item="'+b+'"');c.modal("show"),c.find('[data-plenty-modal="confirm"]').on("click",function(){var d=p(b),e=[];c.find('select, .PlentyFormContainer.AttrImage > input[type="hidden"]').each(function(b,c){var d=c.name.match(/^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/);d&&d[1]&&e.push({BasketItemAttributeID:d[1],BasketItemAttributeValueID:a(c).val()})}),0!=e.length&&(d.BasketItemAttributesList=e),k([d])})}function o(d){var e=p(d);e.BasketItemOrderParamsList=[],c.get("/rest/checkout/container_"+"CheckoutOrderParamsList".toLowerCase()+"/",{itemID:e.BasketItemItemID,quantity:e.BasketItemQuantity,basketItemID:d}).done(function(c){g.prepare().setContent(c.data[0]).setTitle(b.translate("Edit order parameters")).setLabelConfirm(b.translate("Save")).onConfirm(function(){return a('[data-plenty-checkout-form="OrderParamsForm"]').validateForm()?(k(i([e])),!0):!1}).show()})}function p(a){for(var b=f.getCheckout().BasketItemsList,c=0;c"+b.translate('Do you really want to remove "{{item}}" from your basket?',{item:j})+"

    ").onDismiss(function(){i.reject()}).onConfirm(function(){h()}).setLabelConfirm(b.translate("Delete")).show(),i}function r(d,e){if(0>=e)return q(d);for(var g,h,i=a.Deferred(),j=f.getCheckout().BasketItemsList,k=0;k0&&f.reloadContainer("Totals")}return{addItem:h,removeItem:q,getItem:p,setItemQuantity:r,editItemAttributes:n,editOrderParams:o,addCoupon:t,removeCoupon:u}},["APIFactory","UIFactory","CMSFactory","CheckoutFactory","ModalFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("CheckoutService",function(c,d,e,f){function g(){e.loadCheckout(!0)}function h(){var b=a('[data-plenty-checkout-form="details"]'),d=b.getFormValues();return e.getCheckout().CheckoutCustomerSign||(e.getCheckout().CheckoutCustomerSign=""),e.getCheckout().CheckoutOrderInfoText||(e.getCheckout().CheckoutOrderInfoText=""),e.getCheckout().CheckoutCustomerSign!==d.CustomerSign&&a(b).find('[name="CustomerSign"]').length>0||e.getCheckout().CheckoutOrderInfoText!==d.OrderInfoText&&a(b).find('[name="OrderInfoText"]').length>0?(e.getCheckout().CheckoutCustomerSign=d.CustomerSign,e.getCheckout().CheckoutOrderInfoText=d.OrderInfoText,e.setCheckout()):c.idle()}function i(d){var f=a('[data-plenty-checkout-form="shippingAddress"]');if(!d&&!f.validateForm())return!1;if(!d&&!b.getInstance().AddressDoctorService.validateAddress(f))return!1;var g=f.getFormValues(),h=a('[name="shippingAddressID"]:checked').val();if(a("#shippingAdressSelect").modal("hide"),0>h){var i=g;return k(i,e.getCheckout().CustomerShippingAddress)?c.idle():("PACKSTATION"==i.Street?(i.IsPackstation=1,i.PackstationNo=i.HouseNo):"POSTFILIALE"==i.Street&&(i.IsPostfiliale=1,i.PostfilialNo=i.HouseNo),c.post("/rest/checkout/customershippingaddress/",i).done(function(a){e.getCheckout().CheckoutCustomerShippingAddressID=a.data.ID,e.getCheckout().CheckoutShippingCountryID=a.data.CountryID,delete e.getCheckout().CheckoutMethodOfPaymentID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList"),e.reloadContainer("ShippingProfilesList"),2==e.getCheckout().CustomerInvoiceAddress.LoginType&&e.reloadContainer("CustomerShippingAddress")})}))}return h!=e.getCheckout().CheckoutCustomerShippingAddressID?(e.getCheckout().CheckoutCustomerShippingAddressID=h,delete e.getCheckout().CheckoutMethodOfPaymentID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList"),e.reloadContainer("ShippingProfilesList"),2==e.getCheckout().CustomerInvoiceAddress.LoginType&&e.reloadContainer("CustomerShippingAddress")})):c.idle()}function j(){var b=a('[data-plenty-checkout-form="guestRegistration"]'),d=b.getFormValues();return d.LoginType=1,d.CustomerPropertiesList=d.CustomerPropertiesList||[],b.find("[data-plenty-property-id]").each(function(b,c){d.CustomerPropertiesList.push({PropertyID:a(c).attr("data-plenty-property-id"),PropertyValue:a(c).val()})}),k(d,e.getCheckout().CustomerInvoiceAddress)?i():c.post("/rest/checkout/customerinvoiceaddress/",d).done(function(a){i().done(function(){e.loadCheckout()})})}function k(a,b){for(var c in a)if(a[c]+""!=b[c]+""&&"EmailRepeat"!==c)return!1;return!0}function l(){var b=a('[data-plenty-checkout-form="shippingProfileSelect"]').getFormValues();return e.getCheckout().CheckoutShippingProfileID=b.ShippingProfileID,delete e.getCheckout().CheckoutCustomerShippingAddressID,delete e.getCheckout().CheckoutMethodOfPaymentID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList")})}function m(){return c.post("/rest/checkout/preparepayment/",null).done(function(b){if(""!=b.data.CheckoutMethodOfPaymentRedirectURL)document.location.assign(b.data.CheckoutMethodOfPaymentRedirectURL);else if(b.data.CheckoutMethodOfPaymentAdditionalContent){var c=a(b.data.CheckoutMethodOfPaymentAdditionalContent).find('[data-plenty-checkout-form="bankDetails"]').length>0;f.prepare().setContent(b.data.CheckoutMethodOfPaymentAdditionalContent).onConfirm(function(){return c?p():r()}).show()}})}function n(b){return b=b||a('[data-plenty-checkout-form="methodOfPayment"]').getFormValues().MethodOfPaymentID,e.getCheckout().CheckoutMethodOfPaymentID=b,delete e.getCheckout().CheckoutCustomerShippingAddressID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("ShippingProfilesList")})}function o(){d.getContainer("CheckoutPaymentInformationBankDetails").from("Checkout").done(function(b){f.prepare().setContent(b.data[0]).onDismiss(function(){a('input[name="MethodOfPaymentID"]').each(function(b,c){a(c).val()==e.getCheckout().CheckoutMethodOfPaymentID?a(c).attr("checked","checked"):a(c).removeAttr("checked")})}).onConfirm(function(){return p()}).show()})}function p(){var b=a('[data-plenty-checkout-form="bankDetails"]');if(b.validateForm()){var d=b.getFormValues().checkout.customerBankDetails,f={CustomerBankName:d.bankName,CustomerBLZ:d.blz,CustomerAccountNumber:d.accountNo,CustomerAccountOwner:d.accountOwner,CustomerIBAN:d.iban,CustomerBIC:d.bic};return c.post("/rest/checkout/paymentinformationbankdetails/",f).done(function(){e.loadCheckout().done(function(){n(3),e.reloadContainer("MethodsOfPaymentList")})}),!0}return!1}function q(){d.getContainer("CheckoutPaymentInformationCreditCard").from("Checkout").done(function(b){f.prepare().setContent(b.data[0]).onDismiss(function(){a('input[name="MethodOfPaymentID"]').each(function(b,c){a(c).val()==e.getCheckout().CheckoutMethodOfPaymentID?a(c).attr("checked","checked"):a(c).removeAttr("checked")})}).onConfirm(function(){return r()}).show()})}function r(){var b=a('[data-plenty-checkout-form="creditCard"]');if(b.validateForm()){var d=b.getFormValues().checkout.paymentInformationCC,f={Owner:d.owner,Cvv2:d.cvv2,Number:d.number,Year:d.year,Month:d.month,Provider:d.provider};return c.post("/rest/checkout/paymentinformationcreditcard/",f).done(function(){e.loadCheckout()}),!0}return!1}function s(b){if(2==e.getCheckout().CustomerInvoiceAddress.LoginType)var c=a('[data-plenty-checkout-form="shippingAddress"]').getFormValues();else var c=a('[data-plenty-checkout-form="guestRegistration"]').getFormValues();var g={street:c.Street,houseNo:c.HouseNo,ZIP:c.ZIP,city:c.City,postnummer:c.Postnummer,suggestionType:"postfinder"};d.getContainer("CheckoutAddressSuggestionResultsList",g).from("Checkout").done(function(a){f.prepare().setContent(a.data[0]).show()})}function t(){var b=a('[data-plenty-checkout-form="placeOrder"]');if(b.validateForm()){var d=b.getFormValues(),e={TermsAndConditionsCheck:d.termsAndConditionsCheck||0,WithdrawalCheck:d.withdrawalCheck||0,PrivacyPolicyCheck:d.privacyPolicyCheck||0,AgeRestrictionCheck:d.ageRestrictionCheck||0,NewsletterCheck:d.newsletterCheck||0,KlarnaTermsAndConditionsCheck:d.klarnaTermsAndConditionsCheck||0,PayoneDirectDebitMandateCheck:d.payoneDirectDebitMandateCheck||0,PayoneInvoiceCheck:d.payoneInvoiceCheck||0};return c.post("/rest/checkout/placeorder/",e).done(function(a){""!=a.data.MethodOfPaymentRedirectURL?window.location.assign(a.data.MethodOfPaymentRedirectURL):""!=a.data.MethodOfPaymentAdditionalContent?f.prepare().setContent(a.data.MethodOfPaymentAdditionalContent).setLabelDismiss("").onDismiss(function(){window.location.assign(b.attr("action"))}).onConfirm(function(){window.location.assign(b.attr("action"))}).show():window.location.assign(b.attr("action"))})}}return{init:g,setCustomerSignAndInfo:h,registerGuest:j,setShippingProfile:l,saveShippingAddress:i,loadAddressSuggestion:s,preparePayment:m,setMethodOfPayment:n,editBankDetails:o,editCreditCard:q,placeOrder:t}},["APIFactory","CMSFactory","CheckoutFactory","ModalFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("FeedbackService",function(a){function b(){function b(a,b){return e.dateStart=a,e.dateEnd=b,this}function c(b,c,f){var g={ReferenceId:c,FromDate:e.dateStart,ToDate:e.dateEnd,FeedbackType:f||d().COMMENTS_AND_RATINGS};return a.get("/rest/feedback/"+b+"/",g)}var e={dateStart:null,dateEnd:null};return{between:b,"for":c}}function c(){function b(a){return f.Rating=a,this}function c(a){return f.Text=a,this}function d(a,b,c){return f.Author=a,b&&(f.Email=b),c&&(f.CustomerId=c),this}function e(b,c){return a.post("/rest/feedback/"+b+"/",f)}var f={Rating:1,Text:"",Author:"",Email:"",CustomerId:0};return{withRating:b,withComment:c,withAuthor:d,to:e}}function d(){return{COMMENTS_ONLY:"comments_only",RATINGS_ONLY:"ratings_only",COMMENTS_AND_RATINGS:"comments_with_ratings"}}function e(){return{ITEM:"item",CATEGORY:"category",BLOG:"blog"}}return{getFeedbacks:b,addFeedback:c,ArticleTypes:e(),FeedbackTypes:d()}},["APIFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("MediaSizeService",function(){function b(){return e&&c(),e}function c(){var b;if(b=window.matchMedia?window.matchMedia("(min-width:1200px)").matches?"lg":window.matchMedia("(min-width:992px)").matches?"md":window.matchMedia("(min-width:768px)").matches?"sm":"xs":a(window).width()>=1200?"lg":a(window).width()>=992?"md":a(window).width()>=768?"sm":"xs",b!=e){var c=e;e=b,a(window).trigger("sizeChange",[e,c])}}function d(a){for(var b=a.replace(/\s/g,"").split(","),c=0;c li'),r=a('[data-plenty-checkout="container"] > div'),u=a('[data-plenty-checkout="next"]'),t=a('[data-plenty-checkout="prev"]'),q.length==r.length&&r.length>0){d.getCheckout();r.hide(),q.each(function(b,c){a(c).addClass("disabled"),a(c).click(function(){a(this).is(".disabled")||j(b)})}),u.attr("disabled","disabled"),u.click(function(){m()}),t.attr("disabled","disabled"),t.click(function(){n()}),window.addEventListener("hashchange",function(){window.location.hash.length>0?o(window.location.hash):j(0)},!1),a.urlParam=function(a){var b=new RegExp("[?&]"+a+"=([^&#]*)").exec(window.location.href);return null==b?null:b[1]||0};var c=a.urlParam("gototab");0==window.location.hash.length&&c&&a('[data-plenty-checkout-id="'+c+'"]').length>0?window.location.hash=c:j(!o(window.location.hash)&&s>=0?s:0),p(),a(window).on("sizeChange",p),a(window).resize(function(){"xs"==b.getInstance().MediaSizeService.interval()&&p()})}}function f(){return s>=0?{id:a(r[s]).attr("data-plenty-checkout-id"),index:s}:null}function g(a){return v.beforeChange.push(a),b.getInstance().NavigatorService}function h(a){return v.afterChange.push(a),b.getInstance().NavigatorService}function i(b,c){var d=!0;if(s>=0||"afterChange"===b){var e=f(),g={index:c,id:a(r[c]).attr("data-plenty-checkout-id")};a.each(v[b],function(a,b){return b(e,g)===!1?(d=!1,!1):void 0})}return d}function j(e,f){var g=s!==e;(!g||f||i("beforeChange",e))&&(s=e,!Object.equals(w[s],d.getCheckout(!0))&&g&&a(r[s]).attr("data-plenty-checkout-content")?(w[s]=d.getCheckout(!0),c.getCategoryContent(a(r[s]).attr("data-plenty-checkout-content")).done(function(c){a(r[s]).html(c.data[0]),k(g),b.getInstance().bindDirectives(r[s]),a(window).trigger("contentChanged")})):k(g))}function k(b){a(r).hide(),a(q).each(function(b,c){a(c).removeClass("disabled active"),a(c).find('[role="tab"]').attr("aria-selected","false"),s>b?a(c).addClass("visited"):b==s?(a(c).addClass("active visited"),a(c).find('[role="tab"]').attr("aria-selected","true")):b>s&&!a(c).is(".visited")&&a(c).addClass("disabled")}),p(),0>=s?a(t).attr("disabled","disabled"):a(t).removeAttr("disabled"),s+1==q.length?a(u).attr("disabled","disabled"):a(u).removeAttr("disabled"),a(r[s]).show(),s>0?window.location.hash=a(r[s]).attr("data-plenty-checkout-id"):window.location.hash.length>0&&(window.location.hash=""),b&&i("afterChange",s)}function l(a){j(a.index,!0)}function m(){s0&&j(s-1)}function o(b){return"next"==b?(m(),!0):"prev"==b?(n(),!0):(b=b.replace("#",""),a(r).each(function(c,d){return a(d).attr("data-plenty-checkout-id")==b?(j(c),!0):void 0}),!1)}function p(){var b=q.length;if(!(0>=b)){a(q).removeAttr("style"),a(q).children("span").removeAttr("style"),a(u).removeAttr("style"),a(t).removeAttr("style");var c=a(t).outerWidth()c?a(d).children("span").css({paddingLeft:g+"px",paddingRight:h+"px"}):a(d).children("span").css({paddingLeft:j+"px",paddingRight:k+"px"})})}}var q=[],r=[],s=-1,t={},u={},v={beforeChange:[],afterChange:[]},w=[];return{init:e,getCurrentContainer:f,goTo:j,beforeChange:g,afterChange:h,continueChange:l,next:m,previous:n,goToID:o,fillNavigation:p}},["CMSFactory","CheckoutFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("PostfinderService",function(c,d,e){function f(){var b=a('input[name="Street"]').val();return"PACKSTATION"==b.toUpperCase()||"POSTFILIALE"==b.toUpperCase()}function g(){j={PostfinderItemStreet:a('input[name="Street"]','[data-plenty-checkout-form="shippingAddress"]'),PostfinderItemZIP:a('input[name="ZIP"]','[data-plenty-checkout-form="shippingAddress"]'),PostfinderItemCity:a('input[name="City"]','[data-plenty-checkout-form="shippingAddress"]'),PostfinderItemHouseNo:a('input[name="HouseNo"]','[data-plenty-checkout-form="shippingAddress"]')},j.PostfinderItemStreet.val(""),j.PostfinderItemZIP.val().length>2||j.PostfinderItemCity.val().length>2?c.get("/rest/checkout/shippingaddresspostfinderlist/",{suggestionType:"postfinder",zip:j.PostfinderItemZIP.val(),city:j.PostfinderItemCity.val()}).done(function(c){l=c.data,k=l.length,0==k&&h();for(var e={addresses:[]},f=0;k>f;f++){var g="km",m=l[f].PostfinderItemDistance,n=m/1e3;n=(Math.round(100*n)/100).toFixed(2).replace(".",","),1e3>m&&(n=m,g="m"),e.addresses.push({index:f,dimension:g,type:l[f].PostfinderItemIsPackstation?"Packstation":"Postfiliale",number:l[f].PostfinderItemIsPackstation?l[f].PostfinderItemPackstationNo:l[f].PostfinderItemPostfilialNo,street:l[f].PostfinderItemStreet,houseNo:l[f].PostfinderItemHouseNo,zip:l[f].PostfinderItemZIP,city:l[f].PostfinderItemCity,district:l[f].PostfinderItemDistrict,distance:n,remark:l[f].PostfinderItemRemark})}var o=b.compileTemplate("addressSuggestions/postFinder.html",e);d.prepare().setTitle(b.translate("Packstations and post offices in your area")).setContent(o).setClass("checkout").onConfirm(function(){return j.PostfinderItemCity.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemCity.attr("id")+'"]').removeClass("has-error").addClass("has-success"),j.PostfinderItemZIP.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemZIP.attr("id")+'"]').removeClass("has-error").addClass("has-success"),j.PostfinderItemStreet.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemStreet.attr("id")+'"]').removeClass("has-error").addClass("has-success"),j.PostfinderItemHouseNo.removeClass("has-error").addClass("has-success"),a('label[for="'+j.PostfinderItemHouseNo.attr("id")+'"]').removeClass("has-error").addClass("has-success"),i=a('input[type="radio"][name="postfinder"]:checked').val(),l[i].PostfinderItemIsPackstation?(a(j.PostfinderItemStreet).val("PACKSTATION"),a(j.PostfinderItemHouseNo).val(l[i].PostfinderItemPackstationNo)):(a(j.PostfinderItemStreet).val("POSTFILIALE"),a(j.PostfinderItemHouseNo).val(l[i].PostfinderItemPostfilialNo)),a(j.PostfinderItemStreet).trigger("change"),a(j.PostfinderItemCity).val(l[i].PostfinderItemCity),a(j.PostfinderItemZIP).val(l[i].PostfinderItemZIP),!0}).show()}):h()}function h(){e.throwError(0,b.translate("Please enter a ZIP code and/or a city.")),j.PostfinderItemCity.removeClass("has-success").addClass("has-error"),a('label[for="'+j.PostfinderItemCity.attr("id")+'"]').removeClass("has-success").addClass("has-error"),j.PostfinderItemZIP.removeClass("has-success").addClass("has-error"),a('label[for="'+j.PostfinderItemZIP.attr("id")+'"]').removeClass("has-success").addClass("has-error"),j.PostfinderItemCity.focus(function(){a(this).removeClass("has-error");var b=a(this).attr("id");a(this).closest(".form-group").find('[for="'+b+'"]').removeClass("has-error")}),j.PostfinderItemZIP.focus(function(){a(this).removeClass("has-error");var b=a(this).attr("id");a(this).closest(".form-group").find('[for="'+b+'"]').removeClass("has-error")})}var i="",j={},k={},l={};return{openPostfinderModal:g,isPackstation:f}},["APIFactory","ModalFactory","UIFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("SocialShareService",function(){function b(a){var b={"facebook-like":'',"facebook-recommend":'',twitter:'',"google-plus":'
    '};return b[a]}function c(){var b=document.location.href,c=a("link[rel=canonical]").attr("href");return c&&c.length>0&&(c.indexOf("http")<0&&(c=document.location.protocol+"//"+document.location.host+c),b=c),b}function d(b){var c=a('meta[name="'+b+'"]').attr("content");return c||""}function e(){var b=d("DC.title"),c=d("DC.creator");return b.length>0&&c.length>0?b+=" - "+c:b=a("title").text(),encodeURIComponent(b)}return"undefined"==typeof socialLangLocale&&(socialLangLocale="en_US"),"undefined"==typeof socialLang&&(socialLang="en"),{getSocialService:b}})}(jQuery,PlentyFramework),function($,pm){pm.service("ValidationService",function(){function getFormControl(a){return a=$(a),a.is("input")||a.is("select")||a.is("textarea")?a:a.find("input").length>0?a.find("input"):a.find("select").length>0?a.find("select"):a.find("textarea").length>0?a.find("textarea"):null}function validateText(a){return a.is("input")||a.is("select")||a.is("textarea")?$.trim(a.val()).length>0:(console.error("Validation Error: Cannot validate Text for <"+a.prop("tagName")+">"),!1)}function validateMail(a){var b=/[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;return validateText(a)?b.test($.trim(a.val())):!1}function validateNumber(a){return validateText(a)?$.isNumeric($.trim(a.val())):!1}function validateValue(a,b){return $(b).length>0?$.trim(a.val())==$.trim($(b).val()):$.trim(a.val())==b}function visibility(a){return a.is(":visible")}function isEnabled(a){return a.is(":enabled")}function validate(form,errorClass){var formControl,formControls,validationKey,currentHasError,group,checked,checkedMin,checkedMax,attrValidate,validationKeys,formControlAttrType,$form=$(form);errorClass=errorClass||"has-error";var missingFields=[],hasError=!1;$form.find("[data-plenty-validate], input.Required").each(function(i,elem){attrValidate=$(elem).attr("data-plenty-validate"),formControls=getFormControl(elem),validationKeys=attrValidate?attrValidate:"text",validationKeys=validationKeys.split(",");for(var i=0,length=formControls.length;length>i;i++){if(formControl=$(formControls[i]),formControlAttrType=formControl.attr("type"),!visibility(formControl)||!isEnabled(formControl))return;if(validationKey=validationKeys[i].trim()||validationKeys[0].trim(),currentHasError=!1,formControl.is("input")&&"radio"!=formControlAttrType&&"checkbox"!=formControlAttrType||formControl.is("textarea"))switch(validationKey){case"text":currentHasError=!validateText(formControl);break;case"mail":currentHasError=!validateMail(formControl);break;case"number":currentHasError=!validateNumber(formControl);break;case"value":currentHasError=!validateValue(formControl,$(elem).attr("data-plenty-validation-value"));break;case"none":break;default:console.error('Form validation error: unknown validate property: "'+attrValidate+'"')}else if(!formControl.is("input")||"radio"!=formControlAttrType&&"checkbox"!=formControlAttrType){if(!formControl.is("select"))return void console.error("Form validation error: "+$(elem).prop("tagName")+" does not contain an form element");currentHasError=""==formControl.val()||"-1"==formControl.val()}else group=formControl.attr("name"),checked=$form.find('input[name="'+group+'"]:checked').length,"radio"==formControlAttrType?(checkedMin=1,checkedMax=1):(eval("var minMax = "+attrValidate),checkedMin=minMax?minMax.min:1,checkedMax=minMax?minMax.max:1),currentHasError=checkedMin>checked||checked>checkedMax;currentHasError&&(hasError=!0,missingFields.push(formControl),formControls.length>1?(formControl.addClass(errorClass),$form.find('label[for="'+formControl.attr("id")+'"]').addClass(errorClass)):$(elem).addClass(errorClass))}}),$form.on("validationFailed",function(){var a=50,b=$form.find("."+errorClass).first(),c=b.offset().top,d=$("html, body");$form.parents(".modal").length>0?(d=$form.parents(".modal").find(".modal-body"),c=d.scrollTop()-(d.offset().top-b.offset().top)):$form.is(".modal")&&(d=$form.find(".modal-body"),c=d.scrollTop()-(d.offset().top-b.offset().top)),(c-awindow.pageYOffset+window.innerHeight)&&d.animate({scrollTop:c-a})}),hasError&&($form.find("."+errorClass).each(function(a,b){formControl=$(getFormControl(b)),formControl.on("focus click",function(){var a=$(b);a.removeClass(errorClass),$form.find('label[for="'+$(this).attr("id")+'"]').removeClass(errorClass)})}),$form.trigger("validationFailed",[missingFields]));var callback=$form.attr("data-plenty-callback");if(!hasError&&callback&&"submit"!=callback&&"function"==typeof window[callback]){var fields={};return $form.find("input, textarea, select").each(function(){"checkbox"==$(this).attr("type")?fields[$(this).attr("name")]=$(this).is(":checked"):fields[$(this).attr("name")]=$(this).val()}),window[callback](fields),!1}return!hasError}return{validate:validate}}),$.fn.validateForm=function(){return pm.getInstance().ValidationService.validate(this)},$.fn.getFormValues=function(){function a(a,b){var d=a.match(/^([^\[]+)(.*)/);if(d[2]){var e,f=/\[([^\]]+)]/g,g=[];for(g[0]=d[1];null!==(e=f.exec(d[2]));)g.push(e[1]);for(var h=g.length-1;h>=0;h--){var i={};i[g[h]]=b,b=i}c=$.extend(!0,c,b)}else c[d[1]]=b}var b=this,c={};return b.find("input, select, textarea").each(function(c,d){if($(d).attr("name"))if("checkbox"==$(d).attr("type")){var e=[];$(b).find('[name="'+$(d).attr("name")+'"]:checked').each(function(a,b){e.push($(b).val())}),a($(d).attr("name"),e)}else"radio"==$(d).attr("type")?$(d).is(":checked")&&a($(d).attr("name"),$(d).val()):a($(d).attr("name"),$(d).val())}),c}}(jQuery,PlentyFramework),function(a,b){b.directive("Authentication",function(c){function d(d){b.getRecentEvent().preventDefault(),c.customerLogin(a(d))}return{login:d}},["AuthenticationService"])}(jQuery,PlentyFramework),function(a,b){b.directive("Basket",function(c){function d(d){b.getRecentEvent().preventDefault();var e={},f=a(d),g=f.parents("form");e.BasketItemItemID=g.find('[name="ArticleID"]').val(),e.BasketItemPriceID=g.find('[name="SYS_P_ID"]').val(),e.BasketItemQuantity=g.find('[name="ArticleQuantity"]').val(),e.BasketItemBranchID=g.find('[name="source_category"]').val();var h=g.find('[name^="ArticleAttribute"]'),i=[];a.each(h,function(b,c){var d=c.name.match(/^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/);d&&d[1]&&i.push({BasketItemAttributeID:d[1],BasketItemAttributeValueID:a(c).val()})}),0!=i.length&&(e.BasketItemAttributesList=i),c.addItem([e])}function e(b,c){var d=a(b),e=d.parent().find("input"),f=parseInt(e.attr("maxlength"))||5,g=parseInt(e.val())+c,h=d.parents("[data-basket-item-id]").length>0;if(h){(g+"").length<=f&&g>=0&&e.val(g);var i=d.data("timeout");i&&window.clearTimeout(i),i=window.setTimeout(function(){e.trigger("change")},1e3),d.data("timeout",i)}else(g+"").length<=f&&g>=1&&e.val(g)}function f(b,d){c.setItemQuantity(b,parseInt(a(d).val())).fail(function(){var e=c.getItem(b);a(d).val(e.BasketItemQuantity)})}return{addBasketItem:d,changeItemQuantity:e,setItemQuantity:f}},["BasketService"])}(jQuery,PlentyFramework),function(a,b){b.directive("MobileDropdown",function(c){function d(){a(window).on("orientationchange sizeChange",function(){e(h)}),a("html").click(function(a){e(i)})}function e(b){for(var c=0;c0?window.location.assign(a(c).attr("href")):window.location.assign(c))}function e(a){c.goToID(a)}return{to:d,toCheckoutTab:e}},["MediaSizeService","NavigatorService"])}(jQuery,PlentyFramework),function(a,b){b.directive("Tab",function(c){function d(b){a(b).tab("show")}function e(a,b,c){j[c]||(j[c]=new h),j[c].getTab(b)||j[c].addTab(b),j[c].getTab(b).addLabel(a)}function f(a,b,c){j[c]||(j[c]=new h),j[c].getTab(b)||j[c].addTab(b),j[c].getTab(b).setContent(a)}function g(a,d,e){c.isInterval(e)&&(b.getRecentEvent().preventDefault(),j[d]&&j[d].getTab(a)&&j[d].showTab(a))}function h(){function b(a){return g[a]=new i(a),g[a]}function c(b){var c=0;if(f)c=parseInt(f.getContent().parent().css("zIndex")),f.hide(),f.getContent().parent().css("zIndex",c-1);else{for(var d in g)if(g[d].getContent()){var h=parseInt(g[d].getContent().parent().css("zIndex"));(0==c||c>h)&&(c=h),g[d].hide()}for(var d in g)g[d].getContent()&&g[d].getContent().parent().css("zIndex",c-1);a(window).on("sizeChange",e)}f=g[b],f.getContent().parent().css("zIndex",c),f.show()}function d(a){return g[a]}function e(){for(var a in g)g[a].getContent()&&g[a].show();f=null}var f,g={};return{addTab:b,showTab:c,getTab:d,resetTabs:e}}function i(a){function b(){return j}function c(a){return i.push(a),this}function d(a){return h=a,this}function e(){return h}function f(){for(var a=0;a0&&(a(document).scrollTop()>100?t(v,"addClass","visible"):t(v,"removeClass","visible"))})}function f(b){a(b).owlCarousel({navigation:!0,navigationText:!1,slideSpeed:1e3,paginationSpeed:1e3,singleItem:!0,autoPlay:6e3,stopOnHover:!0,afterMove:function(b){a(b).find('img[data-plenty-rel="lazyload"]').trigger("appear")}})}function g(b,d,e){var f=a(b),g=0,h={},i=f.find('[data-plenty-rel="equal-target"]').length>0?f.find('[data-plenty-rel="equal-target"]'):f.children();e!==!0&&u.push(b);for(var j=i.length;j>=0;j--)h=a(i[j]),h.css("height",""),h.outerHeight(!0)>g&&(g=h.outerHeight(!0));(!d||c.isInterval(d))&&i.height(g)}function h(b){var c=a(b);c.click(function(){return a("html, body").animate({scrollTop:0},400),!1}),a.inArray(c,v)&&v.push(c)}function i(b,c){var d=a(b);d.lazyload({effect:c}),d.on("loaded",function(){d.css("display","inline-block")})}function j(b){console.log(b);var c=a(b),d=c.parent();d.addClass("animating"),c.siblings("ul").slideToggle(200,function(){d.is(".open")?d.removeClass("open"):d.addClass("open"),c.siblings("ul").removeAttr("style"),d.removeClass("animating")})}function k(b,c){var d=a(b),e=a(d.attr("data-plenty-rel"));if(d.is('input[type="radio"]')){var f=a('input[type="radio"][name="'+d.attr("name")+'"]'),g=!c||"checked"==c;f.change(function(){var b=a(this);e.parents('[data-plenty-rel="equal-target"]').css("height","auto"),b.is(":checked")&&b[0]===d[0]&&1==g?e.slideDown(400,function(){s()}):e.slideUp(400,function(){s()})})}else d.click(function(){d.addClass("animating"),e.slideToggle(400,function(){d.removeClass("animating"),d.toggleClass("active"),s()})})}function l(b,c){c=c||400,a(b).parents('[data-plenty-rel="equal-target"]').css("height","auto"),a(b).slideDown(c,function(){s()})}function m(b,c){c=c||400,a(b).parents('[data-plenty-rel="equal-target"]').css("height","auto"),a(b).slideUp(c,function(){s()})}function n(b,c){c=c||400,a(b).parents('[data-plenty-rel="equal-target"]').css("height","auto"),a(b).slideToggle(c,function(){s()})}function o(b,c){var e=a(b),f=e.find('[data-plenty-rel="social-switch"]');e.append(''),f.is("off, on")||f.addClass("off"),f.on("click",function(){f.hasClass("off")&&("tooltip"==e.attr("data-toggle")&&e.tooltip("destroy"),f.removeClass("off").addClass("on"),e.find('[data-plenty-rel="social-placeholder"]').hide(),e.find(".social-container").append(d.getSocialService(c)))})}function p(d,e,f){if(e&&d&&(!f||c.isInterval(f))){var g=b.getRecentEvent();return g&&g.preventDefault(),a(e).toggleClass(d),!1}}function q(d,e,f){if(e&&d&&(!f||c.isInterval(f))){var g=b.getRecentEvent();return g&&g.preventDefault(),a(e).addClass(d),!1}}function r(d,e,f){if(e&&d&&(!f||c.isInterval(f))){var g=b.getRecentEvent();return g&&g.preventDefault(),a(e).removeClass(d),!1}}function s(){for(var a=u.length-1;a>=0;a--)g(u[a],"",!0)}function t(a,b,c){for(var d=a.length-1;d>=0;d--)a[d][b](c)}var u=[],v=[];return{initUIWindowEvents:e,addContentPageSlider:f,equalHeight:g,initToTop:h,initLazyload:i,initSlideToggle:k,slideDown:l,slideUp:m,slideToggle:n,toggleHideShow:j,toggleSocialShare:o,toggleClass:p,addClass:q,removeClass:r}},["MediaSizeService","SocialShareService"])}(jQuery,PlentyFramework),function(a,b){b.directive("Validator",function(a){function b(b,c){return a.validate(b,c)}return{validate:b}},["ValidationService"])}(jQuery,PlentyFramework),PlentyFramework.compile();var plenty=PlentyFramework.getInstance(); +jQuery(document).ready(function(){plenty.bindDirectives()}); \ No newline at end of file diff --git a/package.json b/package.json index adf88bc..a9b3085 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "plentymarketsCMStools", "license": "AGPL-3.0", - "version": "1.0.1", + "version": "1.0.2", "repository": "https://github.com/plentymarkets/plenty-cms-library.git", "devDependencies": { "grunt": "^0.4.5", From adacce3a87ffa2b8d7cdd2e702790c692caaaa1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bro=CC=88cker?= Date: Mon, 14 Dec 2015 15:12:47 +0100 Subject: [PATCH 13/17] Fix mobile dropdown (separate 2 different cases for main-navigation and top-navbar) --- src/directives/MobileDropdown.js | 108 ++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 31 deletions(-) diff --git a/src/directives/MobileDropdown.js b/src/directives/MobileDropdown.js index dd2023c..2e66f31 100644 --- a/src/directives/MobileDropdown.js +++ b/src/directives/MobileDropdown.js @@ -31,68 +31,114 @@ { $(window).on('orientationchange sizeChange', function() { resetDropdowns( dropdownElements ); + resetDropdowns( closableDropdownElements ); }); $( 'html' ).click( function( e ) { - resetDropdowns( closableDropdownElements ); + resetDropdowns( closableDropdownElements, e ); }); } - function resetDropdowns( dropdownList ) + function resetDropdowns( dropdownList, event ) { for( var i = 0; i < dropdownList.length; i++ ) { - $( dropdownList[i] ).removeClass('open'); + if ( !! event ) + { + if ( ! $( dropdownList[i] ).is( $(event.target).closest('li') ) ) + { + $( dropdownList[i] ).removeClass('open'); + } + } + else + { + $( dropdownList[i] ).removeClass('open'); + } } } - function openDropdown( elem, closable ) + function openDropdown( elem, alwaysClickable ) { var $elem = $( elem ); var $parent = $elem.parent(); - if( Modernizr.touch ) + // case 1: xs || sm || ( touch && ( md || lg ) ) -> open/close via click on small devices, open/close via css-hover on desktop, open/close via click on touch-desktop (e.g. top navigation) + + if ( !! alwaysClickable && ( MediaSize.isInterval('xs, sm') || ( Modernizr.touch && MediaSize.isInterval('md, lg') ) ) ) { - if ( MediaSize.isInterval('md, lg') && !$parent.is( '.open' ) ) + if ( ! $parent.is('.open') ) { + showDropdownHideOthers ( $elem, $parent ); - // avoid redirecting - pm.getRecentEvent().preventDefault(); - - // hide other dropdowns - resetDropdowns( dropdownElements ); - - // show dropdown - $parent.addClass( 'open' ); - - if ( $.inArray( $parent[0], dropdownElements ) < 0 ) + // if href + if ( ! $elem.attr('href') ) { - dropdownElements.push( $parent[0] ); + avoidRedirectinStopPropagation ( $parent.not($elem) ); } - - if ( !!closable && $.inArray( $parent[0], closableDropdownElements ) < 0 ) + } + else + { + if ( ! $elem.attr('href') ) { - closableDropdownElements.push( $parent[0] ); + // hide dropdown + $parent.removeClass( 'open' ); } - - // avoid closing popup by clicking itself - $parent.off( 'click' ); - $parent.on( 'click', function( e ) - { - e.stopPropagation(); - } ); } + } + // case 2: touch && ( md || lg ) -> open via 1st click on touch-desktop, return false (e.g. main navigation) + + if ( ! alwaysClickable && ( Modernizr.touch && MediaSize.isInterval('md, lg') ) ) + { + if ( ! $parent.is('.open') ) + { + showDropdownHideOthers ( $elem, $parent ); + + avoidRedirectinStopPropagation ( $parent ); + } + else + { + // redirect to href if dropdown is already open + // do nothing + } } - else + + } + + function showDropdownHideOthers ( elem, parent ) + { + var $elem = $( elem ); + var $parent = $( parent ); + + // hide other dropdowns + resetDropdowns( closableDropdownElements ); + + // remember opened dropdown + if ( $.inArray( $parent[0], closableDropdownElements ) < 0 ) { - // redirect to href - // do nothing + closableDropdownElements.push( $parent[0] ); } + // show dropdown + $parent.addClass( 'open' ); + } + + function avoidRedirectinStopPropagation ( elem ) + { + var $elem = $( elem ); + + // avoid redirecting + pm.getRecentEvent().preventDefault(); + + // avoid closing popup by clicking itself + $elem.off( 'click' ); + $elem.on( 'click', function( e ) + { + e.stopPropagation(); + } ); } function slideDropdown( elem ) @@ -101,7 +147,7 @@ var $elemParent = $elem.parent(); $elemParent.addClass( 'animating' ); - $elem.siblings( 'ul' ).slideToggle( 200, function() + $elem.siblings( 'ul' ).slideToggle( 400, function() { if ( $elemParent.is( '.open' ) ) { From e59bd8e5edb779e570dfaad880d2b87705c77ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bro=CC=88cker?= Date: Tue, 15 Dec 2015 10:34:45 +0100 Subject: [PATCH 14/17] Fix slideDropdown() for use on document ready for initial opening mobile navigation. --- src/directives/MobileDropdown.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/directives/MobileDropdown.js b/src/directives/MobileDropdown.js index 2e66f31..cd9011d 100644 --- a/src/directives/MobileDropdown.js +++ b/src/directives/MobileDropdown.js @@ -146,24 +146,28 @@ var $elem = $( elem ); var $elemParent = $elem.parent(); - $elemParent.addClass( 'animating' ); - $elem.siblings( 'ul' ).slideToggle( 400, function() + // size interval query is required since function is used on document ready to initial open active navigation (on small devices) + if ( MediaSize.isInterval('xs, sm') ) { - if ( $elemParent.is( '.open' ) ) + $elemParent.addClass( 'animating' ); + $elem.siblings( 'ul' ).slideToggle( 400, function() { - $elemParent.removeClass( 'open' ); - } - else - { - $elemParent.addClass( 'open' ); - if( $.inArray( $elemParent[0], dropdownElements) < 0 ) + if ( $elemParent.is( '.open' ) ) { - dropdownElements.push( $elemParent[0] ); + $elemParent.removeClass( 'open' ); } - } - $elem.siblings( 'ul' ).removeAttr( 'style' ); - $elemParent.removeClass( 'animating' ); - } ); + else + { + $elemParent.addClass( 'open' ); + if( $.inArray( $elemParent[0], dropdownElements) < 0 ) + { + dropdownElements.push( $elemParent[0] ); + } + } + $elem.siblings( 'ul' ).removeAttr( 'style' ); + $elemParent.removeClass( 'animating' ); + } ); + } } }, ['MediaSizeService'] ); From c71cc4eda90602c1214c69e24b02246087342358 Mon Sep 17 00:00:00 2001 From: Maximilian Lauterbach Date: Tue, 15 Dec 2015 11:51:05 +0100 Subject: [PATCH 15/17] fix for mobile navigation. --- dist/plentymarketsCMStools-1.0.2.js | 225 ++++++++++++++++-------- dist/plentymarketsCMStools-1.0.2.min.js | 4 +- src/directives/MobileDropdown.js | 69 ++++---- src/directives/UI.js | 64 +++++-- 4 files changed, 237 insertions(+), 125 deletions(-) diff --git a/dist/plentymarketsCMStools-1.0.2.js b/dist/plentymarketsCMStools-1.0.2.js index 61783c5..9f0d009 100644 --- a/dist/plentymarketsCMStools-1.0.2.js +++ b/dist/plentymarketsCMStools-1.0.2.js @@ -5502,101 +5502,154 @@ PlentyFramework.cssClasses = { return { initDropdowns: initDropdowns, - openDropdown: openDropdown, + openDropdown : openDropdown, slideDropdown: slideDropdown }; function initDropdowns() { - $(window).on('orientationchange sizeChange', function() { + $( window ).on( 'orientationchange sizeChange', function() + { resetDropdowns( dropdownElements ); - }); - - $( 'html' ).click( function( e ) { resetDropdowns( closableDropdownElements ); - }); + } ); + + // handle "close menu on click outside" + $( 'html' ).on( "click touchstart", function( event ) + { + resetDropdowns( closableDropdownElements, event ); + } ); } - function resetDropdowns( dropdownList ) + function resetDropdowns( dropdownList, event ) { - - for( var i = 0; i < dropdownList.length; i++ ) + var $current; + for ( var i = 0; i < dropdownList.length; i++ ) { - $( dropdownList[i] ).removeClass('open'); + $current = $( dropdownList[i] ); + if ( !!event ) + { + if ( $current.find( $( event.target ) ).length === 0 ) + { + $current.removeClass( 'open' ); + } + } + else + { + $current.removeClass( 'open' ); + } } } - function openDropdown( elem, closable ) + function openDropdown( elem, alwaysClickable ) { - - var $elem = $( elem ); + var $elem = $( elem ); var $parent = $elem.parent(); - if( Modernizr.touch ) + // case 1: xs || sm || ( touch && ( md || lg ) ) -> open/close via click on small devices, open/close via + // css-hover on desktop, open/close via click on touch-desktop (e.g. top navigation) + + if ( !!alwaysClickable && ( MediaSize.isInterval( 'xs, sm' ) || ( Modernizr.touch && MediaSize.isInterval( 'md, lg' ) ) ) ) { - if ( MediaSize.isInterval('md, lg') && !$parent.is( '.open' ) ) + if ( !$parent.is( '.open' ) ) { + showDropdownHideOthers( $elem, $parent ); - // avoid redirecting - pm.getRecentEvent().preventDefault(); - - // hide other dropdowns - resetDropdowns( dropdownElements ); - - // show dropdown - $parent.addClass( 'open' ); - - if ( $.inArray( $parent[0], dropdownElements ) < 0 ) + // if href + if ( !$elem.attr( 'href' ) ) { - dropdownElements.push( $parent[0] ); + avoidRedirectinStopPropagation( $parent.not( $elem ) ); } - - if ( !!closable && $.inArray( $parent[0], closableDropdownElements ) < 0 ) + } + else + { + if ( !$elem.attr( 'href' ) ) { - closableDropdownElements.push( $parent[0] ); + // hide dropdown + $parent.removeClass( 'open' ); } - - // avoid closing popup by clicking itself - $parent.off( 'click' ); - $parent.on( 'click', function( e ) - { - e.stopPropagation(); - } ); } + } + + // case 2: touch && ( md || lg ) -> open via 1st click on touch-desktop, return false (e.g. main navigation) + + if ( !alwaysClickable && ( Modernizr.touch && MediaSize.isInterval( 'md, lg' ) ) ) + { + if ( !$parent.is( '.open' ) ) + { + showDropdownHideOthers( $elem, $parent ); + avoidRedirectinStopPropagation( $parent ); + } + else + { + // redirect to href if dropdown is already open + // do nothing + } } - else + } + + function showDropdownHideOthers( elem, parent ) + { + var $parent = $( parent ); + + // hide other dropdowns + resetDropdowns( closableDropdownElements ); + + // remember opened dropdown + if ( $.inArray( $parent[0], closableDropdownElements ) < 0 ) { - // redirect to href - // do nothing + closableDropdownElements.push( $parent[0] ); } + // show dropdown + $parent.addClass( 'open' ); } - function slideDropdown( elem ) + function avoidRedirectinStopPropagation( elem ) { var $elem = $( elem ); + + // avoid redirecting + pm.getRecentEvent().preventDefault(); + + // avoid closing popup by clicking itself + $elem.off( 'click' ); + $elem.on( 'click', function( e ) + { + e.stopPropagation(); + } ); + } + + function slideDropdown( elem ) + { + var $elem = $( elem ); var $elemParent = $elem.parent(); - $elemParent.addClass( 'animating' ); - $elem.siblings( 'ul' ).slideToggle( 200, function() + // size interval query is required since function is used on document ready to initial open active + // navigation (on small devices) + if ( MediaSize.isInterval( 'xs, sm' ) ) { - if ( $elemParent.is( '.open' ) ) - { - $elemParent.removeClass( 'open' ); - } - else + $elemParent.addClass( 'animating' ); + $elem.siblings( 'ul' ).slideToggle( 400, function() { - $elemParent.addClass( 'open' ); - if( $.inArray( $elemParent[0], dropdownElements) < 0 ) + if ( $elemParent.is( '.open' ) ) { - dropdownElements.push( $elemParent[0] ); + $elemParent.removeClass( 'open' ); } - } - $elem.siblings( 'ul' ).removeAttr( 'style' ); - $elemParent.removeClass( 'animating' ); - } ); + else + { + $elemParent.addClass( 'open' ); + if ( $.inArray( $elemParent[0], dropdownElements ) < 0 ) + { + dropdownElements.push( $elemParent[0] ); + } + } + $elem.siblings( 'ul' ).removeAttr( 'style' ); + $elemParent.removeClass( 'animating' ); + } ); + } } }, ['MediaSizeService'] ); @@ -6112,32 +6165,28 @@ PlentyFramework.cssClasses = { function slideDown( target, duration ) { - duration = duration || 400; - $(target).parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); - $(target).slideDown( duration, function() { - fireEqualHeight(); - }); + slideAction($( target ), duration, 'slideDown'); } function slideUp( target, duration ) { - duration = duration || 400; - $(target).parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); - $(target).slideUp( duration, function() { - fireEqualHeight(); - }); + slideAction($( target ), duration, 'slideUp'); } function slideToggle( target, duration ) { + slideAction($( target ), duration, 'slideToggle'); + } + + function slideAction ($target, duration, callbackString) { duration = duration || 400; - $(target).parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); - $(target).slideToggle( duration, function() { + $target.parents( '[data-plenty-rel="equal-target"]' ).css( 'height', 'auto' ); + $target[callbackString]( duration, function() + { fireEqualHeight(); - }); + } ); } - /** * TODO check comment * Social Share Activation @@ -6218,13 +6267,39 @@ PlentyFramework.cssClasses = { */ function toggleClass( cssClass, target, interval ) { + var $target = $( target ); + /* FIXME + * Callisto 3.1 Design adaption: + * NavigationCategoriesList + * Line 8 + * BEFORE: + *