diff --git a/dist/README.md b/dist/README.md deleted file mode 100644 index 12db1bf..0000000 --- a/dist/README.md +++ /dev/null @@ -1,203 +0,0 @@ -![plentymarkets Logo](http://www.plentymarkets.eu/layout/pm/images/logo/plentymarkets-logo.jpg) - -# plentymarkets CMS library 0.9.1 - -## Update your **Callisto Light 2** to plentymarketsCMStools 0.9.1 - -### Step 1: Download plentymarketsCMStools 0.9.1 - -You can download this repository as a [zip archive](https://github.com/plentymarkets/plenty-cms-library/archive/master.zip). -All required files can be found in `dist/`-folder - -### Step 2: Upload required files - -Upload the following files to `LAYOUT_FOLDER/js/plenty/`: -- `plentymarketsCMStools-0.9.1.js` -- `lang/de_DE.json` (You have to create the folder `lang` manually) -- `lang/en_EN.json` - -### Step 3: Include new files in your template - -Download and open `LAYOUT_FOLDER/js/plenty/scripts.json` and change the file to use: -```js -{ - "PageDesignGlobal": { - "head": [], - "body": [ - . - . - . - "plenty/plentymarketsCMStools-0.9.1.js" - ] - }, - . - . - . -} -``` -If the `scripts.json` doesn't exist see [Update your Callisto Light using dependecyInjector](#update-your-callisto-light-using-dependecyinjector). -Upload the edited file back to your webspace (you will override the existing version). - -### Step 4: Load language files in your PageDesign-templates - -Open templates: `PageDesignContent`, `PageDesignCheckout`, `PageDesignMyAccount` and search for: - -```html - -``` -If you cannot find this code see [Update your Callisto Light using dependecyInjector](#update-your-callisto-light-using-dependecyinjector). - -**ATTENTION:** In each template you will find two similar occurrences of this snippet. Make sure that the value of the parameter `position` is `'body'` - -Add the following lines after each occurrence: - -```html - -``` - -**ATTENTION:** Do not wrap these calls in the same <script> tag. Be sure to have separate <script> tags for each call. - -### Step 5: Edit Category for Checkout-Step 3 - -Open the category which is set for Checkout-Step 3. By default this category is named "Kasse". -Search for -```html -{% if GetGlobal("ShowTabDetails") %} - -
- {% $_id = CheckoutStepPageID(4) %} - {% CategoryContentBody($_id) %} -
-{% endif %} - - -
- {% $_id = CheckoutStepPageID(5) %} - {% CategoryContentBody($_id) %} -
- - -
- {% $_id = CheckoutStepPageID(6) %} - {% CategoryContentBody($_id) %} -
- - -
- {% $_id = CheckoutStepPageID(7) %} - {% CategoryContentBody($_id) %} -
-``` -and edit to: - -```html -{% if GetGlobal("ShowTabDetails") %} - -{% $_id = CheckoutStepPageID(4) %} -
- {% CategoryContentBody($_id) %} -
-{% endif %} - - -{% $_id = CheckoutStepPageID(5) %} -
- {% CategoryContentBody($_id) %} -
- - -{% $_id = CheckoutStepPageID(6) %} -
- {% CategoryContentBody($_id) %} -
- - -{% $_id = CheckoutStepPageID(7) %} -
- {% CategoryContentBody($_id) %} -
-``` - -### Step 6: Edit Category for Checkout-Step 6 - -Open the category which is set for Checkout-Step 6. By default this category is named "Zahlung & Versand". -Search for: - -```js -if ( response.data.CheckoutMethodOfPaymentRedirectURL == '' && response.data.CheckoutMethodOfPaymentAdditionalContent == '' ) -``` -and edit to: - -```js -if ( !response || (response.data.CheckoutMethodOfPaymentRedirectURL == '' && response.data.CheckoutMethodOfPaymentAdditionalContent == '' ) ) -``` - - - - -## Update your **Callisto Light** using `dependecyInjector` - -Perform **Step 1** and **Step 2** as described above. - -### Step 3 (alternative): Include new files in your template - -Download and open `LAYOUT_FOLDER/js/plenty/dependencies.json` and change the file to use: -```js -{ - "PageDesignGlobal": { - "head": [], - "body": [ - . - . - . - "plenty/plentymarketsCMStools-0.9.1.js" - ] - }, - . - . - . -} -``` -Upload the edited file back to your webspace (you will override the existing version). - -### Step 4 (alternative): Load language files in your PageDesign-templates - -Open templates: `PageDesignContent`, `PageDesignCheckout`, `PageDesignMyAccount` and search for: - -```html - -``` - -**ATTENTION:** In each template you will find two similar occurrences of this snippet. Make sure that the value of the second parameter is `'body'`. - -Add the following lines after each occurrence: - -```html - -``` - -**ATTENTION:** Do not wrap these calls in the same <script> tag. Be sure to have separate <script> tags for each call. - - -Perform **Step 5** and **Step 6** as described above. \ No newline at end of file diff --git a/dist/lang/de_DE.json b/dist/lang/de_DE.json index 1853c64..98a1169 100644 --- a/dist/lang/de_DE.json +++ b/dist/lang/de_DE.json @@ -7,5 +7,7 @@ "Save": "Speichern", "Do you really want to remove \"{{item}}\" from your basket?": "Möchten Sie den Artikel \"{{item}}\" wirklich aus dem Warenkorb entfernen?", "Select order parameters": "Bestellmerkmale wählen", - "Edit order parameters": "Bestellmerkmale ändern" + "Edit order parameters": "Bestellmerkmale ändern", + "Packstations and post offices in your area": "Packstationen und Postfilialen in der Nähe", + "Please enter a ZIP code and/or a city.": "Bitte geben Sie eine Postleitzahl und/oder einen Ort an." } \ No newline at end of file diff --git a/dist/lang/en_EN.json b/dist/lang/en_EN.json index c23e698..d67956c 100644 --- a/dist/lang/en_EN.json +++ b/dist/lang/en_EN.json @@ -7,5 +7,7 @@ "Save": "Save", "Do you really want to remove \"{{item}}\" from your basket?": "Do you really want to remove \"{{item}}\" from your basket?", "Select order parameters": "Select order parameters", - "Edit order parameters": "Edit order parameters" + "Edit order parameters": "Edit order parameters", + "Packstations and post offices in your area": "Packstations and post offices in your area", + "Please enter a ZIP code and/or a city.": "Please enter a ZIP code and/or a city." } \ No newline at end of file diff --git a/dist/plentymarketsCMStools-0.9.0.js b/dist/plentymarketsCMStools-0.9.0.js deleted file mode 100644 index 55391ba..0000000 --- a/dist/plentymarketsCMStools-0.9.0.js +++ /dev/null @@ -1,4473 +0,0 @@ -(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" + - " 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" + - "
"; - -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($) { - - /** - * 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(selector, callback, dependencies, allowDuplicates) { - var directive = { - id: PlentyFramework.directives.length, - selector: selector, - callback: callback, - dependencies: dependencies, - allowDuplicates: !!allowDuplicates, - elements: [] - }; - PlentyFramework.directives.push(directive); - - return directive; - }; - - /** - * Bind registered directives. - * @function bindDirectives - * @param {string} [directiveSelector] restrict binding to elements matching this selector - */ - PlentyFramework.prototype.bindDirectives = function( directiveSelector ) { - - $.each( PlentyFramework.directives, function(i, directive) { - if( !directiveSelector || directiveSelector === directive.selector ) { - var elements = []; - // filter elements already bound - $(directive.selector).each(function (j, obj) { - if ($.inArray(obj, directive.elements) < 0) { - elements.push(obj); - } - }); - - $.each(elements, function (i, elem) { - var params = [i, elem]; - - // append dependencies if exists - if (!!directive.dependencies && directive.dependencies.length > 0) { - params = params.concat(PlentyFramework.resolveServices(directive.dependencies)); - } - - // apply loop variables and depending services to callback - directive.callback.apply(null, params); - }); - - $(elements).each(function (j, obj) { - // store function ID to avoid duplicate bindings - if (!directive.allowDuplicates) { - directive.elements.push(obj); - } - }); - } - - }); - }; - - - /** - * Collection of uncompiled registered factories & services. - * See {{#crossLink "PlentyFramework/compile:method"}}.compile(){{/crossLink}} - * @attribute components - * @static - * @type {{factories: {}, services: {}}} - */ - PlentyFramework.components = { - factories: {}, - services: {} - }; - - /** - * 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 || []; - - PlentyFramework.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( PlentyFramework.components.services.hasOwnProperty(dependency) ) { - PlentyFramework.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 || []; - PlentyFramework.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( PlentyFramework.components.factories.hasOwnProperty(dependency) ) { - PlentyFramework.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 PlentyFramework.components.factories ) { - if( !PlentyFramework.factories.hasOwnProperty(factory) ) { - PlentyFramework.components.factories[factory].compile(); - } - } - - for( var service in PlentyFramework.components.services ) { - if( !PlentyFramework.prototype.hasOwnProperty(service) ) { - PlentyFramework.components.services[service].compile(); - } - } - - var scripts = document.getElementsByTagName( 'SCRIPT' ); - if( PlentyFramework.scriptPath.length > 0 ) { - PlentyFramework.scriptPath = scripts[ scripts.length - 1 ].src.match( /(.*)\/(.*)\.js(\?\S*)?$/ )[ 1 ]; - } - - }; - -}(jQuery)); - - - - -(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($, 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.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() { - if(!checkout || !checkoutData) { - loadCheckout(true); - } - - 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, true, 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(); - }); - }); - } - - /** - * 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 - */ - 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(); - }); - }); - - } - - /** - * 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(); - }); - }); - - } - - }, ['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 = ''; - - /** - * 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, - 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; - } - - /** - * 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() { - 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( 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 - * ===================================================================================== - */ - -/** - * @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) { - - 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 - }; - - 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() ) { - 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 - }; - - return setInvoiceAddress(invoiceAddress) - .done(function () { - window.location.assign( form.attr('action') ); - }); - } - } - }, ['APIFactory', '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 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, - setItemQuantity: setItemQuantity, - 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 }).done(function (resp) { - // checking for order params! - if (resp.data[0].indexOf("form-group") > 0) { - Modal.prepare() - .setContent(resp.data[0]) - .onConfirm(function() { - // save order params - saveOrderParams(article); - - // close modal after saving order params - return true; - }) - .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 wrappedThis = {}; - var attrType = ""; - - //Groups - orderParamsForm.find('[name^="ParamGroup"]').each(function(){ - var match = this.name.match(/^ParamGroup\[(\d+)]\[(\d+)]$/); - articleWithParams = addOrderParamValue(articleWithParams, match[1], $(this).val(), $(this).val()); - }); - - //Values - orderParamsForm.find('[name^="ParamValue"]').each(function(){ - wrappedThis = $(this); - attrType = wrappedThis.attr('type'); - - if( ((attrType == 'checkbox' && wrappedThis.is(':checked')) || - (attrType == 'radio' && wrappedThis.is(':checked')) || - (attrType != 'radio' && attrType != 'checkbox')) && - attrType != 'file') - { - var match = this.name.match(/^ParamValue\[(\d+)]\[(\d+)]$/); - - articleWithParams = addOrderParamValue(articleWithParams, match[1], match[2], wrappedThis.val()); - - } else if (attrType == 'file') { - articleWithParams = orderParamFileUpload(this, articleWithParams); - } - }); - - addArticle( 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 orderParamFileUpload(input , articleWithParams ) { - var key = input.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 (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.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; - } - - /** - * 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 ) { - - // get item name - var itemName, originalItemQuantity; - var params = Checkout.getCheckout().BasketItemsList; - - for ( var i = 0; i < params.length; i++ ) { - if ( params[i].BasketItemID == BasketItemID ) { - originalItemQuantity = params[i].BasketItemQuantity; - itemName = params[i].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(); - }); - }); - } - - 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); - }) - .onConfirm(function () { - doDelete(); - }) - .setLabelConfirm( pm.translate("Delete") ) - .show(); - } else { - doDelete(); - } - } - - /** - * 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 ) { - removeBasketItem( BasketItemID ); - } - - 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.reloadContainer('Totals'); - - var basketItemsPriceTotal = 0; - var params2 = Checkout.getCheckout().BasketItemsList; - for (var i = 0; i < params2.length; i++) { - if (params2[i].BasketItemID == BasketItemID) { - basketItemsPriceTotal = params2[i].BasketItemPriceTotal; - } - } - $('[data-basket-item-id="' + BasketItemID + '"]').find('[data-plenty-checkout="basket-item-price-total"]').html(basketItemsPriceTotal); - refreshBasketPreview(); - }); - }); - } - } - - /** - * 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 category, if we are at checkout - if ( $('[data-plenty-checkout-catcontent="' + pm.getGlobal('checkoutConfirmCatID') + '"]').length > 0 ) { - Checkout.reloadCatContent( pm.getGlobal('checkoutConfirmCatID') ); - } - else - // 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(); - } - - - /** - * 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() - .done(function () { - Checkout.reloadCatContent( pm.getGlobal('checkoutConfirmCatID') ); - }); - - } 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; - } - - 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) ) { - - // new shipping address - return API.post("/rest/checkout/customershippingaddress/", shippingAddress) - .done(function (response) { - - Checkout.getCheckout().CheckoutCustomerShippingAddressID = response.data.ID; - delete Checkout.getCheckout().CheckoutMethodOfPaymentID; - delete Checkout.getCheckout().CheckoutShippingProfileID; - - Checkout.setCheckout().done(function () { - Checkout.reloadContainer('MethodsOfPaymentList'); - // TODO: the following container may not be reloaded if guest registration - if (Checkout.getCheckout().CustomerInvoiceAddress.LoginType == 2) { - Checkout.reloadContainer('CustomerShippingAddress'); - } - Checkout.reloadContainer('ShippingProfilesList'); - Checkout.reloadCatContent( pm.getGlobal('checkoutConfirmCatID') ); - }); - }); - } 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('CustomerShippingAddress'); - Checkout.reloadContainer('ShippingProfilesList'); - Checkout.reloadCatContent(pm.getGlobal('checkoutConfirmCatID')); - }); - } 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; - - - if( !addressesAreEqual( invoiceAddress, Checkout.getCheckout().CustomerInvoiceAddress ) ) { - - return API.post("/rest/checkout/customerinvoiceaddress/", invoiceAddress) - .done(function (response) { - saveShippingAddress().done(function(){ - Checkout.getCheckout().CustomerInvoiceAddress = response.data; - Checkout.reloadCatContent(pm.getGlobal('checkoutConfirmCatID')); - }); - }); - - } 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'); - Checkout.reloadCatContent( pm.getGlobal('checkoutConfirmCatID') ); - }); - - } - - /** - * 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'); - Checkout.reloadCatContent( pm.getGlobal('checkoutConfirmCatID') ); - }); - } - - /** - * 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 - }; - - /** - * 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]); - } - } - - - }); - -}(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() { - 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: [] - }; - - 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 ) { - 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; - - // 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", index); - } - - } - - /** - * 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); - } - - }); - -}(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 ) { - var formControl, formControls, validationKey, currentHasError, group, checked, checkedMin, checkedMax, attrValidate, validationKeys, formControlAttrType; - var wrappedForm = $(form); - var errorClass = !!wrappedForm.attr('data-plenty-checkform') ? wrappedForm.attr('data-plenty-checkform') : 'has-error'; - var missingFields = []; - var hasError = false; - - // check every required input inside form - wrappedForm.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 = wrappedForm.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); - wrappedForm.find('label[for="'+formControl.attr('id')+'"]').addClass(errorClass); - } else { - $(elem).addClass(errorClass); - } - } - } - - }); - - // scroll to element on 'validationFailed' - wrappedForm.on('validationFailed', function() { - var distanceTop = 50; - var errorOffset = wrappedForm.find('.has-error').first().offset().top; - var scrollTarget = $('html, body'); - - // if form is inside of modal, scroll modal instead of body - if( wrappedForm.parents('.modal').length > 0 ) { - scrollTarget = wrappedForm.parents('.modal'); - } else if( wrappedForm.is('.modal') ) { - scrollTarget = wrappedForm; - } - - // 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 - wrappedForm.find('.has-error').each(function(i, elem) { - formControl = $(getFormControl(elem)); - formControl.on('focus click', function() { - formControl.removeClass( errorClass ); - wrappedForm.find('label[for="'+formControl.attr('id')+'"]').removeClass(errorClass); - $(elem).removeClass( errorClass ); - }); - }); - - wrappedForm.trigger('validationFailed', [missingFields]); - } - - var callback = wrappedForm.attr('data-plenty-callback'); - - if( !hasError && !!callback && callback != "submit" && typeof window[callback] == "function") { - - var fields = {}; - wrappedForm.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 - * ===================================================================================== - */ - -/** - * @module Directives - */ -/** - * 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('[data-plenty="addBasketItemButton"]', function(i, button, BasketService) - { - - $(button).click( function(e) - { - // avoid directing to href - e.preventDefault(); - - //init - var basketItemsList = {}; - var parentForm = $(button).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]); - - }); - }, ['BasketService']); -} (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 - * ===================================================================================== - */ - -(function($, pm) { - - // append Bootstrap Tooltip - pm.directive('[data-toggle="tooltip"]', function(i, elem) { - $(elem).tooltip({ - container: 'body' - }); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - pm.directive('[data-plenty-checkout-href]', function(i, elem, NavigatorService) { - $(elem).click(function () { - NavigatorService.goToID( $(this).attr('data-plenty-checkout-href') ); - }); - }, ['NavigatorService']); -} (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 - * ===================================================================================== - */ - -(function($, pm) { - - - /* - * content page slider - * - * usage (functionality requires only attribute data-plenty="contentpageSlider"): - *
- *
- * ... - *
- *
- * ... - *
- * ... - *
- */ - pm.directive('[data-plenty="contentpageSlider"]', function(i, 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-lazyload]').trigger('appear'); } - }); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* - * Equal Box heights - */ - pm.directive('[data-plenty-equal]', function(i, elem, MediaSizeService) { - var mediaSizes = $(elem).data('plenty-equal').replace(/\s/g, '').split(','); - - var targets = ( $(elem).find('[data-plenty-equal-target]').length > 0 ) ? $(elem).find('[data-plenty-equal-target]') : $(elem).children(); - - var maxHeight = 0; - $(targets).each(function(j, child) { - - $(child).css('height', ''); - - if( $(child).outerHeight(true) > maxHeight ) { - maxHeight = $(child).outerHeight(true); - } - }); - - if( !mediaSizes || $.inArray( MediaSizeService.interval(), mediaSizes ) >= 0 ) targets.height(maxHeight); - - }, ['MediaSizeService'], true); - - // refresh calculation on window resize - $(window).on('sizeChange', function() { - pm.getInstance().bindDirectives( '[data-plenty-equal]' ); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // lazyload images (requires lazyload.min.js) - // TODO: handle external dependencies dependencies - pm.directive('img[data-plenty-lazyload]', function(i, elem) { - $(elem).lazyload({ - effect: $(this).attr('data-plenty-lazyload') - }); - $(elem).on("loaded", function() { - $(elem).css('display', 'inline-block'); - }); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - pm.directive('[data-plenty-checkout-form="customerLogin"]', function(i, elem, AuthenticationService) { - $(elem).on('submit', function (e) { - e.preventDefault(); - AuthenticationService.customerLogin( $(e.target) ); - }); - }, ["AuthenticationService"]); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* - * 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' - */ - // TODO: handle external dependency to Modernizr - pm.directive('.dropdown > a[data-plenty-enable]', function(i, elem, MediaSizeService) { - - if( $(elem).attr('data-plenty-enable') == "toggle-xs-sm-or-touch" ) { - $(elem).click(function(e) { - if ( MediaSizeService.interval() == 'xs' || MediaSizeService.interval() == 'sm' || ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) ) { - $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').not( $(this) ).parent().removeClass('open'); - $(this).parent().toggleClass('open'); - return false; - } - }); - } - - // dropdown enabled touch - else if( $(elem).attr('data-plenty-enable') == "touch" ) { - $(elem).click(function() { - if ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) { // otherwise already has mobile navigation - $('.dropdown.open > a[data-plenty-enable="touch"]').not( $(this) ).parent().removeClass('open'); - if ( ! $(this).parent().hasClass('open') ) { - $(this).parent().addClass('open'); - return false; - } - } - }); - } - }, ['MediaSizeService']); - - - pm.directive('*', function(i, elem, MediaSizeService) { - - $(elem).click(function (e) { - if (MediaSizeService.interval() == 'xs' || MediaSizeService.interval() == 'sm' || ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch )) { - var dropdown = $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent(); - if (dropdown.length > 0 && !dropdown.is(e.target) && dropdown.has(e.target).length <= 0) { - dropdown.removeClass('open'); - } - } - - if (MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch) { - var dropdown = $('.dropdown.open > a[data-plenty-enable="touch"]').parent(); - if (dropdown.length > 0 && !dropdown.is(e.target) && dropdown.has(e.target).length <= 0) { - dropdown.removeClass('open'); - } - } - }); - }, ['MediaSizeService']); - - - pm.directive(window, function(i, elem, MediaSizeService) { - $(window).on('orientationchange', function() { - if ( MediaSizeService.interval() == 'xs' || MediaSizeService.interval() == 'sm' || ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) ) { - $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent().removeClass('open'); - } - - if ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) { - $('.dropdown.open > a[data-plenty-enable="touch"]').parent().removeClass('open'); - } - }); - $(window).on('sizeChange', function(newValue) { - if ( newValue != 'xs' && newValue != 'sm' && ! Modernizr.touch ) { - $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent().removeClass('open'); - } - }); - }, ['MediaSizeService']); - - $(document).ready(function() { - - if ( pm.getInstance().MediaSizeService.interval() != 'xs' && pm.getInstance().MediaSizeService.interval() != 'sm' && Modernizr.touch ) { - $('.dropdown.open > a[data-plenty-enable="touch"]').parent().removeClass('open'); - } - - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // Call function if enter key pressed while element is focused - pm.directive('[data-plenty-onenter]', function(i, elem) { - var onEnter = $(elem).attr('data-plenty-onenter'); - var callback = typeof window[onEnter] === 'function' ? window[onEnter] : (new Function('return ' + onEnter)); - $(elem).on('keypress', function(e) { - - if(e.which === 13 && !!callback && typeof callback === "function") { - callback.call(); - } - }); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // Tree navigation toggle - pm.directive('[data-plenty="openCloseToggle"]', function(i, elem) { - $(elem).click(function () { - $(elem).parent().addClass('animating'); - $(elem).siblings('ul').slideToggle(200, function () { - if ($(elem).parent().is('.open')) { - $(elem).parent().removeClass('open'); - } - else { - $(elem).parent().addClass('open'); - } - $(elem).removeAttr('style'); - $(elem).parent().removeClass('animating'); - }); - }); - - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // TODO: merge to single directive. Differentiate between increasing and decreasing by additional parameter - pm.directive('[data-plenty="quantityInputButtonPlus"]', function(i, elem) { - // quantity input plus/minus buttons - $(elem).click(function() { - var input = $($(elem).closest('[data-plenty="quantityInputWrapper"]').find('input')); - var value = parseInt( input.val() ); - var maxLength = parseInt(input.attr('maxlength')) || 1000; - if ( ( (value + 1) + '').length <= maxLength ) { - input.val(value + 1); - } - }); - }); - - pm.directive('[data-plenty="quantityInputButtonMinus"]', function(i, elem) { - $(elem).click(function() { - var input = $($(elem).closest('[data-plenty="quantityInputWrapper"]').find('input')); - var value = parseInt( input.val() ); - if ( value > 1 ) { - input.val(value - 1); - } - }); - }); - - // Quantity Buttons in BasketView - pm.directive('[data-basket-item-id] [data-plenty="quantityInputButtonPlus"], [data-basket-item-id] [data-plenty="quantityInputButtonMinus"]', function(i, button) { - $(button).click(function() { - - var self = $(this); - if( !!self.data('timeout') ) { - window.clearTimeout( self.data('timeout') ); - } - - var timeout = window.setTimeout(function() { - self.parents('[data-plenty="quantityInputWrapper"]').find('[data-plenty="quantityInput"]').trigger('change'); - }, 1000); - - self.data('timeout', timeout); - - }); - }); - - pm.directive('[data-basket-item-id] [data-plenty="quantityInput"]', function(i, input, BasketService) { - $(input).change( function() { - - var self = $(this); - var newQuantity = parseInt( self.val() ); - var basketItemID = self.parents('[data-basket-item-id]').attr('data-basket-item-id'); - - BasketService.setItemQuantity( - basketItemID, - newQuantity - ); - }); - }, ['BasketService']); - - - - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // link non-anchor elements - pm.directive('a[data-plenty-href]', function(i, elem, MediaSizeService) { - $(elem).each(function() { - var href = $(this).attr('href'); - var identifier = $(this).attr('data-plenty-href'); - - $('[data-plenty-link="'+identifier+'"]').click(function() { - if( MediaSizeService.interval() != 'xs' ) { - window.location.assign( href ); - } - }); - }); - }, ['MediaSizeService']); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // Toggle target content on click. - // Can be bound on checked-/ unchecked-property of radio buttons - pm.directive('[data-plenty-slidetoggle]', function(i, trigger) { - - var target = $( $(trigger).attr('data-plenty-target') ); - - if( $(trigger).is('input[type="radio"]') ) { - // is radio button - var radioList = $('input[type="radio"][name="'+( $(trigger).attr('name') )+'"]'); - var visibleOnChecked = $(trigger).is('[data-plenty-slidetoggle="checked"]'); - $(radioList).change(function() { - $(target).parents('[data-plenty-equal-target]').css('height', 'auto'); - - if ( $(this).is(':checked') && $(this)[0] === $(trigger)[0] ) { - // checked - if ( visibleOnChecked == true ) { - $(target).slideDown(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } else { - $(target).slideUp(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } - } - else { - // unchecked (since other radio button has been checked) - if ( visibleOnChecked == true ) { - $(target).slideUp(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } else { - $(target).slideDown(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } - } - }); - } else { - // is not radio button - $(trigger).click(function() { - $(target).parents('[data-plenty-equal-target]').css('height', 'auto'); - - $(trigger).addClass('animating'); - $(target).slideToggle(400, function() { - $(trigger).removeClass('animating'); - $(trigger).toggleClass('active'); - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - }); - } - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* - * 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 - */ - pm.directive('[data-plenty-social]', function(i, elem, SocialShareService) { - - var toggle = $(elem).find('[data-plenty="switch"]'); - - // append container to put / delete service.html - $(elem).append(''); - - // add "off" class to switch, if neither "off" or "on" is set - if ( !toggle.hasClass('off') && !toggle.hasClass('on') ) { - toggle.addClass('off'); - } - - // toggle switch - toggle.on('click', function() { - if ( toggle.hasClass('off') ) { - if ( $(elem).attr("data-toggle") == "tooltip" ) { $(elem).tooltip('destroy') }; - toggle.removeClass('off').addClass('on'); - // hide dummy button - $(elem).find('[data-plenty="placeholder"]').hide(); - // load HTML defined in 'api' - $(elem).find('.social-container').append( SocialShareService.getSocialService( $(elem).attr('data-plenty-social') ) ); - } - // do not disable social medias after activation - /* - else - { - toggle.removeClass('on').addClass('off'); - // show dummy button - $(elem).find('[data-plenty="placeholder"]').show(); - // remove api HTML - $(elem).find('.social-container').html(''); - } - */ - }); - }, ['SocialShareService']); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* Tab Handling - * - * Show tab with jQuery-selector 'TAB_SELECTOR' - - * (!) Requires bootstrap.js - * - * Show remote tab with jQuery-selector 'TAB_1' in target container (below) - - * - */ - pm.directive('a[data-plenty-opentab]', function(i, elem) { - // open tab - $(elem).click(function() { - var tabSelector = $(this).attr('data-plenty-opentab'); - tabSelector = ( tabSelector == 'href' ) ? $(this).attr('href') : tabSelector; - $(tabSelector).tab('show'); - }); - }); - - pm.directive('[data-plenty-openremotetab]', function(i, elem) { - // open remote tab{ - $(elem).click(function () { - var tabSelector = $(this).attr('data-plenty-openremotetab'); - $(tabSelector).trigger('tabchange'); - }); - }); - - /* - * Remote tabs - * tab content can be placed anywhere in body - * - * Content of remote tab -
- -
- * - * Remote tab navigation - * [...] -
- * - */ - pm.directive('[data-plenty="remoteTabs"]', function(i, remoteTab) { - - var tabsId = $(remoteTab).attr('data-plenty-remotetabs-id'); - - // find tabs grouped by remotetabs-id - $('[data-plenty="remoteTabs"][data-plenty-remotetabs-id="'+tabsId+'"]').each(function(i, tabs) { - - // bind each remote-tab - $(tabs).find('a').each(function(i, singleTab) { - - var singleTabId = $(singleTab).attr('data-plenty-tab-id'); - - // listen to 'tabchange' event - $(singleTab).on('tabchange', function() { - // toggle class 'active' - $(singleTab).closest('[data-plenty="remoteTabs"]').children('.active').removeClass('active'); - $(singleTab).closest('li').addClass('active'); - - // hide inactive tabs & show active tab - var tabpanelsInactive = $('[data-plenty-remotetabs-id="'+tabsId+'"][data-plenty-tabpanel-labelledby]').not('[data-plenty-tabpanel-labelledby="'+singleTabId+'"]'); - var tabpanelActive = $('[data-plenty-remotetabs-id="'+tabsId+'"][data-plenty-tabpanel-labelledby="'+singleTabId+'"]'); - var zIndexTabpanelParents = 0; - if ( $(tabs).attr('data-plenty-remotetabs-adapt') == 'tabpanel-parent' ) { - zIndexTabpanelParents = 2147483646; - $('[data-plenty-remotetabs-id="'+tabsId+'"][data-plenty-tabpanel-labelledby]').parent().each(function() { - var zIndexCurrent = parseInt( $(this).css('zIndex') ); - if ( typeof zIndexCurrent == 'number' && zIndexCurrent < zIndexTabpanelParents ) zIndexTabpanelParents = zIndexCurrent; - }); - } - - // adjust z-index if neccessary - $(tabpanelsInactive).hide().removeClass('in'); - $(tabpanelActive).show().addClass('in'); - if ( zIndexTabpanelParents != 0 ) { - $(tabpanelsInactive).parent().css('zIndex', zIndexTabpanelParents); - $(tabpanelActive).parent().css('zIndex', zIndexTabpanelParents + 1); - } - }); - }); - }); - - // trigger 'tabchange' event - $(remoteTab).find('a').click(function() { - $(this).trigger('tabchange'); - }); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - pm.directive('[data-plenty="toTop"]', function(i, elem) { - $(elem).click(function() { - $('html, body').animate({ - scrollTop: 0 - }, 400); - return false; - }); - - var positionToTopButton = function() { - if( $(document).scrollTop() > 100 ) { - $(elem).addClass('visible'); - } else { - $(elem).removeClass('visible'); - } - }; - - $(window).on("scroll resize", function() { - positionToTopButton(); - }); - - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* - * 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="" - */ - pm.directive('[data-plenty-toggle]', function(i, elem, MediaSizeService) { - if( $(elem).attr('data-plenty-toggle').search(';') < 0 ) { - eval('var data = ' + $(elem).attr('data-plenty-toggle')); - if ( data.target && data.class ) { - $(elem).click(function() { - var isMedia = false; - if ( data.media ) { - if ( data.media.indexOf(' ') != -1 ) { - var mediaArr = data.media.split(' '); - for ( i = 0; i < mediaArr.length; i++ ) { - if ( MediaSizeService.interval() == mediaArr[i] ) { - isMedia = true; - } - } - } - else { - if ( MediaSizeService.interval() == data.media ) isMedia = true; - } - } - if ( ! data.media || isMedia == true ) { - $(data.target).toggleClass(data.class); - if ( $(elem).is('a') ) return false; - } - }); - } - } - }, ['MediaSizeService']); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* - * Form Validation - * Validate required form inputs as given type of value - * Usage / data-Attributes: - *
This will activate the script for this form - * and add ERROR_CLASS to invalid elements - * - * Check if the value of this input is text (or number) and add ERROR_CLASS on failure - *
You can put the data-plenty-validate="" on a parent element of an input. - * This will check the value of the input(s) inside and add ERROR_CLASS to the
on failure - - * possible values for data-plenty-validate="" text : validate if value is text (or number or mixed) - * mail : checks if value is a valid mail-address (not depending on inputs type-attribute) - * number: checks if value is a numeric value. For detailed information see: isNumberic() - * {min: 1, max: 3} validate that at least 'min' and maximum 'max' options are selected (checkboxes) - * - * possible form elements to validate - * can validate "text", "mail", "number" - * check if one radio-button in group "myRadio" is checked. - * Ignores the value of data-plenty-validate - * check if one checkbox in group "myCheck" is checked or use - * data-plenty-valudate="{min: 3, max: 5}" to define custom range - * check if an option is selected and otions value is not "-1" (plenty default for: "choose"-option) - * - * Events: - * 'validationFailed' will be triggered if at least one element is not valid. - * Usage: - * form.on('validationFailed', function(event, invalidFields) { - * $(invalidFields).each({ - * // manipulate invalid fields - * }); - * }); - */ - pm.directive('form[data-plenty-checkform], form.PlentySubmitForm', function(i, elem, ValidationService) { - - $(elem).submit(function() { - return ValidationService.validate( elem ); - }); - - }, ['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/dist/plentymarketsCMStools-0.9.0.min.js b/dist/plentymarketsCMStools-0.9.0.min.js deleted file mode 100644 index 8a12759..0000000 --- a/dist/plentymarketsCMStools-0.9.0.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});var TemplateCache={};TemplateCache["error/errorMessage.html"]='
\n Code {{code}}:\n {{{message}}}\n
\n',TemplateCache["error/errorPopup.html"]='
\n \n
\n
\n
\n',TemplateCache["modal/modal.html"]='',TemplateCache["waitscreen/waitscreen.html"]='
',function(a){PlentyFramework=function(){};var b=null;PlentyFramework.getInstance=function(){return b=b||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,d){var e={id:PlentyFramework.directives.length,selector:a,callback:b,dependencies:c,allowDuplicates:!!d,elements:[]};return PlentyFramework.directives.push(e),e},PlentyFramework.prototype.bindDirectives=function(b){a.each(PlentyFramework.directives,function(c,d){if(!b||b===d.selector){var e=[];a(d.selector).each(function(b,c){a.inArray(c,d.elements)<0&&e.push(c)}),a.each(e,function(a,b){var c=[a,b];d.dependencies&&d.dependencies.length>0&&(c=c.concat(PlentyFramework.resolveServices(d.dependencies))),d.callback.apply(null,c)}),a(e).each(function(a,b){d.allowDuplicates||d.elements.push(b)})}})},PlentyFramework.components={factories:{},services:{}},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(PlentyFramework.components.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(!PlentyFramework.components.services.hasOwnProperty(b))return console.error('Cannot inject Service "'+b+'": Service not found.'),!1;PlentyFramework.components.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(PlentyFramework.components.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(!PlentyFramework.components.factories.hasOwnProperty(b))return console.error('Cannot inject Factory "'+b+'": Factory not found.'),!1;PlentyFramework.components.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 PlentyFramework.components.factories)PlentyFramework.factories.hasOwnProperty(a)||PlentyFramework.components.factories[a].compile();for(var b in PlentyFramework.components.services)PlentyFramework.prototype.hasOwnProperty(b)||PlentyFramework.components.services[b].compile();var c=document.getElementsByTagName("SCRIPT");PlentyFramework.scriptPath.length>0&&(PlentyFramework.scriptPath=c[c.length-1].src.match(/(.*)\/(.*)\.js(\?\S*)?$/)[1])}}(jQuery),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,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.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(){return m&&l||g(!0),m}function g(a){return b.get("/rest/checkout/",null,!1,!0,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()})})}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()})})}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()})})}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 r.title=a,this}function e(a){return r.content=a,this}function f(a){return r.labelConfirm=a,this}function g(a){return r.labelDismiss=a,this}function h(a){return r.onConfirm=a,this}function i(a){return r.onDismiss=a,this}function j(a){return r.container=a,this}function k(a){return r.timeout=a,this}function l(){s=c(r.content)?PlentyFramework.partials.Modal.getModal(r.content):a(PlentyFramework.compileTemplate("modal/modal.html",r)),a(r.container).append(s);var b=a(r.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(r.container).append(d)}),PlentyFramework.partials.Modal.init(s,r),s.find('[data-plenty-modal="confirm"]').click(function(){var a=r.onConfirm();a&&m(!0)}),PlentyFramework.partials.Modal.show(s),r.timeout>0&&n()}function m(a){PlentyFramework.partials.Modal.hide(s),a||r.onDismiss()}function n(){v=r.timeout,w=(new Date).getTime(),t=window.setTimeout(function(){window.clearInterval(u),m()},r.timeout),s.find('[data-plenty-modal="timer"]').text(v/1e3),u=window.setInterval(function(){if(!x){var a=v-(new Date).getTime()+w;a=Math.round(a/1e3),s.find('[data-plenty-modal="timer"]').text(a)}},1e3)}function o(){x=!0,v-=(new Date).getTime()-w,window.clearTimeout(t)}function p(){x=!1,w=(new Date).getTime(),t=window.setTimeout(function(){m(),window.clearInterval(u)},v)}function q(){window.clearTimeout(t),window.clearInterval(u)}var r=this;r.title="",r.content="",r.labelDismiss=b.translate("Cancel"),r.labelConfirm=b.translate("Confirm"),r.onConfirm=function(){},r.onDismiss=function(){},r.container="body",r.timeout=-1,r.hide=m,r.startTimeout=n,r.stopTimeout=q,r.pauseTimeout=o,r.continueTimeout=p;var s,t,u,v,w,x=!1;return{setTitle:d,setContent:e,setContainer:j,setLabelConfirm:f,setLabelDismiss:g,onConfirm:h,onDismiss:i,setTimeout:k,show:l,hide:m}}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("AuthenticationService",function(b,c){function d(){var c=a('[data-plenty-checkout="lostPasswordForm"]');if(c.validateForm()){var d=c.getFormValues(),e={Email:d.Email};return b.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 e(a){if(a.validateForm()){var c=a.getFormValues(),d={Email:c.loginMail,Password:c.loginPassword};return b.post("/rest/checkout/login/",d).done(function(){window.location.assign(a.attr("action"))})}}function f(a){return b.post("/rest/checkout/customerinvoiceaddress/",a).done(function(a){c.getCheckout().CustomerInvoiceAddress=a.data})}function g(){var b=a('[data-plenty-checkout-form="customerRegistration"]');if(b.validateForm()){var c=b.getFormValues(),d={LoginType:2,FormOfAddressID:c.FormOfAddressID,Company:c.Company,FirstName:c.FirstName,LastName:c.LastName,Street:c.Street,HouseNo:c.HouseNo,AddressAdditional:c.AddressAdditional,ZIP:c.ZIP,City:c.City,CountryID:c.CountryID,VATNumber:c.VATNumber,Email:c.Email,EmailRepeat:c.EmailRepeat,BirthDay:c.BirthDay,BirthMonth:c.BirthMonth,BirthYear:c.BirthYear,Password:c.Password,PasswordRepeat:c.PasswordRepeat,PhoneNumber:c.PhoneNumber,MobileNumber:c.MobileNumber,FaxNumber:c.FaxNumber,Postnummer:c.Postnummer};return f(d).done(function(){window.location.assign(b.attr("action"))})}}return{resetPassword:d,customerLogin:e,setInvoiceAddress:f,registerCustomer:g}},["APIFactory","CheckoutFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("BasketService",function(c,d,e,f,g){function h(a){a&&c.get("/rest/checkout/container_"+"CheckoutOrderParamsList".toLowerCase()+"/",{itemID:a[0].BasketItemItemID,quantity:a[0].BasketItemQuantity}).done(function(b){b.data[0].indexOf("form-group")>0?g.prepare().setContent(b.data[0]).onConfirm(function(){return i(a),!0}).show():j(a)})}function i(b){var c=a('[data-plenty-checkout-form="OrderParamsForm"]'),d={},e="";c.find('[name^="ParamGroup"]').each(function(){var c=this.name.match(/^ParamGroup\[(\d+)]\[(\d+)]$/);b=l(b,c[1],a(this).val(),a(this).val())}),c.find('[name^="ParamValue"]').each(function(){if(d=a(this),e=d.attr("type"),("checkbox"==e&&d.is(":checked")||"radio"==e&&d.is(":checked")||"radio"!=e&&"checkbox"!=e)&&"file"!=e){var c=this.name.match(/^ParamValue\[(\d+)]\[(\d+)]$/);b=l(b,c[1],c[2],d.val())}else"file"==e&&(b=k(this,b))}),j(b)}function j(a){c.post("/rest/checkout/basketitemslist/",a,!0).done(function(){f.loadCheckout().done(function(){o(),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(b,d){var e,f,g=b.id,h={},i=[],j={type:"POST",data:{},isFile:!0,cache:!1,dataType:"json",processData:!1,contentType:!1};h[g]=a(b)[0].files,-1==i.indexOf(g)&&i.push(g);for(var k=0,m=i.length;m>k;++k)e=new FormData,f=h[i[k]],e.append("0",f[0],f[0].name),j.data=e,c.post("/rest/checkout/orderparamfile/",j);var n=b.name.match(/^ParamValueFile\[(\d+)]\[(\d+)]$/);return l(d,n[1],n[2],a(b).val())}function l(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 m(d,e){function h(){c["delete"]("/rest/checkout/basketitemslist/?basketItemIdsList[0]="+d).done(function(){f.loadCheckout().done(function(){a('[data-basket-item-id="'+d+'"]').remove(),!f.getCheckout().BasketItemsList||f.getCheckout().BasketItemsList.length<=0?f.reloadCatContent(b.getGlobal("basketCatID")):f.reloadContainer("Totals"),o()})})}for(var i,j,k=f.getCheckout().BasketItemsList,l=0;l"+b.translate('Do you really want to remove "{{item}}" from your basket?',{item:i})+"

").onDismiss(function(){a('[data-basket-item-id="'+d+'"]').find('[data-plenty="quantityInput"]').val(j)}).onConfirm(function(){h()}).setLabelConfirm(b.translate("Delete")).show()}function n(b,d){0>=d&&m(b);for(var e,g,h=f.getCheckout().BasketItemsList,i=0;i0?f.reloadCatContent(b.getGlobal("checkoutConfirmCatID")):a('[data-plenty-checkout-template="Totals"]').length>0&&f.reloadContainer("Totals")}return{addItem:h,removeItem:m,setItemQuantity:n,addCoupon:p,removeCoupon:q}},["APIFactory","UIFactory","CMSFactory","CheckoutFactory","ModalFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("CheckoutService",function(c,d,e,f){function g(){e.loadCheckout()}function h(){var d=a('[data-plenty-checkout-form="details"]'),f=d.getFormValues();return e.getCheckout().CheckoutCustomerSign||(e.getCheckout().CheckoutCustomerSign=""),e.getCheckout().CheckoutOrderInfoText||(e.getCheckout().CheckoutOrderInfoText=""),e.getCheckout().CheckoutCustomerSign!==f.CustomerSign&&a(d).find('[name="CustomerSign"]').length>0||e.getCheckout().CheckoutOrderInfoText!==f.OrderInfoText&&a(d).find('[name="OrderInfoText"]').length>0?(e.getCheckout().CheckoutCustomerSign=f.CustomerSign,e.getCheckout().CheckoutOrderInfoText=f.OrderInfoText,e.setCheckout().done(function(){e.reloadCatContent(b.getGlobal("checkoutConfirmCatID"))})):c.idle()}function i(d){var f=a('[data-plenty-checkout-form="shippingAddress"]');if(!d&&!f.validateForm())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():c.post("/rest/checkout/customershippingaddress/",i).done(function(a){e.getCheckout().CheckoutCustomerShippingAddressID=a.data.ID,delete e.getCheckout().CheckoutMethodOfPaymentID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList"),2==e.getCheckout().CustomerInvoiceAddress.LoginType&&e.reloadContainer("CustomerShippingAddress"),e.reloadContainer("ShippingProfilesList"),e.reloadCatContent(b.getGlobal("checkoutConfirmCatID"))})})}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("CustomerShippingAddress"),e.reloadContainer("ShippingProfilesList"),e.reloadCatContent(b.getGlobal("checkoutConfirmCatID"))})):c.idle()}function j(){var d=a('[data-plenty-checkout-form="guestRegistration"]'),f=d.getFormValues();return f.LoginType=1,k(f,e.getCheckout().CustomerInvoiceAddress)?i():c.post("/rest/checkout/customerinvoiceaddress/",f).done(function(a){i().done(function(){e.getCheckout().CustomerInvoiceAddress=a.data,e.reloadCatContent(b.getGlobal("checkoutConfirmCatID"))})})}function k(a,b){for(var c in a)if(a[c]+""!=b[c]+""&&"EmailRepeat"!==c)return!1;return!0}function l(){var c=a('[data-plenty-checkout-form="shippingProfileSelect"]').getFormValues();return e.getCheckout().CheckoutShippingProfileID=c.ShippingProfileID,delete e.getCheckout().CheckoutCustomerShippingAddressID,delete e.getCheckout().CheckoutMethodOfPaymentID,e.setCheckout().done(function(){e.reloadContainer("MethodsOfPaymentList"),e.reloadCatContent(b.getGlobal("checkoutConfirmCatID"))})}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(c){return c=c||a('[data-plenty-checkout-form="methodOfPayment"]').getFormValues().MethodOfPaymentID,e.getCheckout().CheckoutMethodOfPaymentID=c,delete e.getCheckout().CheckoutCustomerShippingAddressID,delete e.getCheckout().CheckoutShippingProfileID,e.setCheckout().done(function(){e.reloadContainer("ShippingProfilesList"),e.reloadCatContent(b.getGlobal("checkoutConfirmCatID"))})}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 d&&c(),d}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!=d){var c=d;d=b,a(window).trigger("sizeChange",[d,c])}}var d;return a(window).resize(c),a(document).ready(c),{interval:b}})}(jQuery,PlentyFramework),function(a,b){b.service("NavigatorService",function(){ -function c(){if(n=a('[data-plenty-checkout="navigation"] > li'),o=a('[data-plenty-checkout="container"] > div'),r=a('[data-plenty-checkout="next"]'),q=a('[data-plenty-checkout="prev"]'),n.length==o.length&&o.length>0){o.hide(),n.each(function(b,c){a(c).addClass("disabled"),a(c).click(function(){a(this).is(".disabled")||h(b)})}),r.attr("disabled","disabled"),r.click(function(){j()}),q.attr("disabled","disabled"),q.click(function(){k()}),window.addEventListener("hashchange",function(){window.location.hash.length>0?l(window.location.hash):h(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:h(!l(window.location.hash)&&p>=0?p:0),m(),a(window).on("sizeChange",m),a(window).resize(function(){"xs"==b.getInstance().MediaSizeService.interval()&&m()})}}function d(){return p>=0?{id:a(o[p]).attr("data-plenty-checkout-id"),index:p}:null}function e(a){return s.beforeChange.push(a),b.getInstance().NavigatorService}function f(a){return s.afterChange.push(a),b.getInstance().NavigatorService}function g(b,c){var e=!0;if(p>=0||"afterChange"===b){var f=d(),g={index:c,id:a(o[c]).attr("data-plenty-checkout-id")};a.each(s[b],function(a,b){return b(f,g)===!1?(e=!1,!1):void 0})}return e}function h(b,c){var d=p!==b;(!d||c||g("beforeChange",b))&&(p=b,a(o).hide(),a(n).each(function(b,c){a(c).removeClass("disabled active"),a(c).find('[role="tab"]').attr("aria-selected","false"),p>b?a(c).addClass("visited"):b==p?(a(c).addClass("active visited"),a(c).find('[role="tab"]').attr("aria-selected","true")):b>p&&!a(c).is(".visited")&&a(c).addClass("disabled")}),m(),0>=p?a(q).attr("disabled","disabled"):a(q).removeAttr("disabled"),p+1==n.length?a(r).attr("disabled","disabled"):a(r).removeAttr("disabled"),a(o[p]).show(),p>0?window.location.hash=a(o[p]).attr("data-plenty-checkout-id"):window.location.hash.length>0&&(window.location.hash=""),d&&g("afterChange",b))}function i(a){h(a.index,!0)}function j(){p0&&h(p-1)}function l(b){return"next"==b?(j(),!0):"prev"==b?(k(),!0):(b=b.replace("#",""),a(o).each(function(c,d){return a(d).attr("data-plenty-checkout-id")==b?(h(c),!0):void 0}),!1)}function m(){var b=n.length;if(!(0>=b)){a(n).removeAttr("style"),a(n).children("span").removeAttr("style"),a(r).removeAttr("style"),a(q).removeAttr("style");var c=a(q).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 n=[],o=[],p=-1,q={},r={},s={beforeChange:[],afterChange:[]};return{init:c,getCurrentContainer:d,goTo:h,beforeChange:e,afterChange:f,continueChange:i,next:j,previous:k,goToID:l,fillNavigation:m}})}(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){var formControl,formControls,validationKey,currentHasError,group,checked,checkedMin,checkedMax,attrValidate,validationKeys,formControlAttrType,wrappedForm=$(form),errorClass=wrappedForm.attr("data-plenty-checkform")?wrappedForm.attr("data-plenty-checkform"):"has-error",missingFields=[],hasError=!1;wrappedForm.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=wrappedForm.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),wrappedForm.find('label[for="'+formControl.attr("id")+'"]').addClass(errorClass)):$(elem).addClass(errorClass))}}),wrappedForm.on("validationFailed",function(){var a=50,b=wrappedForm.find(".has-error").first().offset().top,c=$("html, body");wrappedForm.parents(".modal").length>0?c=wrappedForm.parents(".modal"):wrappedForm.is(".modal")&&(c=wrappedForm),(b-awindow.pageYOffset+window.innerHeight)&&c.animate({scrollTop:b-a})}),hasError&&(wrappedForm.find(".has-error").each(function(a,b){formControl=$(getFormControl(b)),formControl.on("focus click",function(){formControl.removeClass(errorClass),wrappedForm.find('label[for="'+formControl.attr("id")+'"]').removeClass(errorClass),$(b).removeClass(errorClass)})}),wrappedForm.trigger("validationFailed",[missingFields]));var callback=wrappedForm.attr("data-plenty-callback");if(!hasError&&callback&&"submit"!=callback&&"function"==typeof window[callback]){var fields={};return wrappedForm.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('[data-plenty="addBasketItemButton"]',function(b,c,d){a(c).click(function(b){b.preventDefault();var e={},f=a(c).parents("form");e.BasketItemItemID=f.find('[name="ArticleID"]').val(),e.BasketItemPriceID=f.find('[name="SYS_P_ID"]').val(),e.BasketItemQuantity=f.find('[name="ArticleQuantity"]').val(),e.BasketItemBranchID=f.find('[name="source_category"]').val();var g=f.find('[name^="ArticleAttribute"]'),h=[];a.each(g,function(b,c){var d=c.name.match(/^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/);d&&d[1]&&h.push({BasketItemAttributeID:d[1],BasketItemAttributeValueID:a(c).val()})}),0!=h.length&&(e.BasketItemAttributesList=h),d.addItem([e])})},["BasketService"])}(jQuery,PlentyFramework),function(a,b){b.directive('[data-toggle="tooltip"]',function(b,c){a(c).tooltip({container:"body"})})}(jQuery,PlentyFramework),function(a,b){b.directive("[data-plenty-checkout-href]",function(b,c,d){a(c).click(function(){d.goToID(a(this).attr("data-plenty-checkout-href"))})},["NavigatorService"])}(jQuery,PlentyFramework),function(a,b){b.directive('[data-plenty="contentpageSlider"]',function(b,c){a(c).owlCarousel({navigation:!0,navigationText:!1,slideSpeed:1e3,paginationSpeed:1e3,singleItem:!0,autoPlay:6e3,stopOnHover:!0,afterMove:function(b){a(b).find("img[data-plenty-lazyload]").trigger("appear")}})})}(jQuery,PlentyFramework),function(a,b){b.directive("[data-plenty-equal]",function(b,c,d){var e=a(c).data("plenty-equal").replace(/\s/g,"").split(","),f=a(c).find("[data-plenty-equal-target]").length>0?a(c).find("[data-plenty-equal-target]"):a(c).children(),g=0;a(f).each(function(b,c){a(c).css("height",""),a(c).outerHeight(!0)>g&&(g=a(c).outerHeight(!0))}),(!e||a.inArray(d.interval(),e)>=0)&&f.height(g)},["MediaSizeService"],!0),a(window).on("sizeChange",function(){b.getInstance().bindDirectives("[data-plenty-equal]")})}(jQuery,PlentyFramework),function(a,b){b.directive("img[data-plenty-lazyload]",function(b,c){a(c).lazyload({effect:a(this).attr("data-plenty-lazyload")}),a(c).on("loaded",function(){a(c).css("display","inline-block")})})}(jQuery,PlentyFramework),function(a,b){b.directive('[data-plenty-checkout-form="customerLogin"]',function(b,c,d){a(c).on("submit",function(b){b.preventDefault(),d.customerLogin(a(b.target))})},["AuthenticationService"])}(jQuery,PlentyFramework),function(a,b){b.directive(".dropdown > a[data-plenty-enable]",function(b,c,d){"toggle-xs-sm-or-touch"==a(c).attr("data-plenty-enable")?a(c).click(function(b){return"xs"==d.interval()||"sm"==d.interval()||"xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch?(a('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').not(a(this)).parent().removeClass("open"),a(this).parent().toggleClass("open"),!1):void 0}):"touch"==a(c).attr("data-plenty-enable")&&a(c).click(function(){return"xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch&&(a('.dropdown.open > a[data-plenty-enable="touch"]').not(a(this)).parent().removeClass("open"),!a(this).parent().hasClass("open"))?(a(this).parent().addClass("open"),!1):void 0})},["MediaSizeService"]),b.directive("*",function(b,c,d){a(c).click(function(b){if("xs"==d.interval()||"sm"==d.interval()||"xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch){var c=a('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent();c.length>0&&!c.is(b.target)&&c.has(b.target).length<=0&&c.removeClass("open")}if("xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch){var c=a('.dropdown.open > a[data-plenty-enable="touch"]').parent();c.length>0&&!c.is(b.target)&&c.has(b.target).length<=0&&c.removeClass("open")}})},["MediaSizeService"]),b.directive(window,function(b,c,d){a(window).on("orientationchange",function(){("xs"==d.interval()||"sm"==d.interval()||"xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch)&&a('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent().removeClass("open"),"xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch&&a('.dropdown.open > a[data-plenty-enable="touch"]').parent().removeClass("open")}),a(window).on("sizeChange",function(b){"xs"==b||"sm"==b||Modernizr.touch||a('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent().removeClass("open")})},["MediaSizeService"]),a(document).ready(function(){"xs"!=b.getInstance().MediaSizeService.interval()&&"sm"!=b.getInstance().MediaSizeService.interval()&&Modernizr.touch&&a('.dropdown.open > a[data-plenty-enable="touch"]').parent().removeClass("open")})}(jQuery,PlentyFramework),function(a,b){b.directive("[data-plenty-onenter]",function(b,c){var d=a(c).attr("data-plenty-onenter"),e="function"==typeof window[d]?window[d]:new Function("return "+d);a(c).on("keypress",function(a){13===a.which&&e&&"function"==typeof e&&e.call()})})}(jQuery,PlentyFramework),function(a,b){b.directive('[data-plenty="openCloseToggle"]',function(b,c){a(c).click(function(){a(c).parent().addClass("animating"),a(c).siblings("ul").slideToggle(200,function(){a(c).parent().is(".open")?a(c).parent().removeClass("open"):a(c).parent().addClass("open"),a(c).removeAttr("style"),a(c).parent().removeClass("animating")})})})}(jQuery,PlentyFramework),function(a,b){b.directive('[data-plenty="quantityInputButtonPlus"]',function(b,c){a(c).click(function(){var b=a(a(c).closest('[data-plenty="quantityInputWrapper"]').find("input")),d=parseInt(b.val()),e=parseInt(b.attr("maxlength"))||1e3;(d+1+"").length<=e&&b.val(d+1)})}),b.directive('[data-plenty="quantityInputButtonMinus"]',function(b,c){a(c).click(function(){var b=a(a(c).closest('[data-plenty="quantityInputWrapper"]').find("input")),d=parseInt(b.val());d>1&&b.val(d-1)})}),b.directive('[data-basket-item-id] [data-plenty="quantityInputButtonPlus"], [data-basket-item-id] [data-plenty="quantityInputButtonMinus"]',function(b,c){a(c).click(function(){var b=a(this);b.data("timeout")&&window.clearTimeout(b.data("timeout"));var c=window.setTimeout(function(){b.parents('[data-plenty="quantityInputWrapper"]').find('[data-plenty="quantityInput"]').trigger("change")},1e3);b.data("timeout",c)})}),b.directive('[data-basket-item-id] [data-plenty="quantityInput"]',function(b,c,d){a(c).change(function(){var b=a(this),c=parseInt(b.val()),e=b.parents("[data-basket-item-id]").attr("data-basket-item-id");d.setItemQuantity(e,c)})},["BasketService"])}(jQuery,PlentyFramework),function(a,b){b.directive("a[data-plenty-href]",function(b,c,d){a(c).each(function(){var b=a(this).attr("href"),c=a(this).attr("data-plenty-href");a('[data-plenty-link="'+c+'"]').click(function(){"xs"!=d.interval()&&window.location.assign(b)})})},["MediaSizeService"])}(jQuery,PlentyFramework),function(a,b){b.directive("[data-plenty-slidetoggle]",function(c,d){var e=a(a(d).attr("data-plenty-target"));if(a(d).is('input[type="radio"]')){var f=a('input[type="radio"][name="'+a(d).attr("name")+'"]'),g=a(d).is('[data-plenty-slidetoggle="checked"]');a(f).change(function(){a(e).parents("[data-plenty-equal-target]").css("height","auto"),a(this).is(":checked")&&a(this)[0]===a(d)[0]?1==g?a(e).slideDown(400,function(){b.getInstance().bindDirectives("[data-plenty-equal]")}):a(e).slideUp(400,function(){b.getInstance().bindDirectives("[data-plenty-equal]")}):1==g?a(e).slideUp(400,function(){b.getInstance().bindDirectives("[data-plenty-equal]")}):a(e).slideDown(400,function(){b.getInstance().bindDirectives("[data-plenty-equal]")})})}else a(d).click(function(){a(e).parents("[data-plenty-equal-target]").css("height","auto"),a(d).addClass("animating"),a(e).slideToggle(400,function(){a(d).removeClass("animating"),a(d).toggleClass("active"),b.getInstance().bindDirectives("[data-plenty-equal]")})})})}(jQuery,PlentyFramework),function(a,b){b.directive("[data-plenty-social]",function(b,c,d){var e=a(c).find('[data-plenty="switch"]');a(c).append(''),e.hasClass("off")||e.hasClass("on")||e.addClass("off"),e.on("click",function(){e.hasClass("off")&&("tooltip"==a(c).attr("data-toggle")&&a(c).tooltip("destroy"),e.removeClass("off").addClass("on"),a(c).find('[data-plenty="placeholder"]').hide(),a(c).find(".social-container").append(d.getSocialService(a(c).attr("data-plenty-social"))))})},["SocialShareService"])}(jQuery,PlentyFramework),function(a,b){b.directive("a[data-plenty-opentab]",function(b,c){a(c).click(function(){var b=a(this).attr("data-plenty-opentab");b="href"==b?a(this).attr("href"):b,a(b).tab("show")})}),b.directive("[data-plenty-openremotetab]",function(b,c){a(c).click(function(){var b=a(this).attr("data-plenty-openremotetab");a(b).trigger("tabchange")})}),b.directive('[data-plenty="remoteTabs"]',function(b,c){var d=a(c).attr("data-plenty-remotetabs-id");a('[data-plenty="remoteTabs"][data-plenty-remotetabs-id="'+d+'"]').each(function(b,c){a(c).find("a").each(function(b,e){var f=a(e).attr("data-plenty-tab-id");a(e).on("tabchange",function(){a(e).closest('[data-plenty="remoteTabs"]').children(".active").removeClass("active"),a(e).closest("li").addClass("active");var b=a('[data-plenty-remotetabs-id="'+d+'"][data-plenty-tabpanel-labelledby]').not('[data-plenty-tabpanel-labelledby="'+f+'"]'),g=a('[data-plenty-remotetabs-id="'+d+'"][data-plenty-tabpanel-labelledby="'+f+'"]'),h=0;"tabpanel-parent"==a(c).attr("data-plenty-remotetabs-adapt")&&(h=2147483646,a('[data-plenty-remotetabs-id="'+d+'"][data-plenty-tabpanel-labelledby]').parent().each(function(){var b=parseInt(a(this).css("zIndex"));"number"==typeof b&&h>b&&(h=b)})),a(b).hide().removeClass("in"),a(g).show().addClass("in"),0!=h&&(a(b).parent().css("zIndex",h),a(g).parent().css("zIndex",h+1))})})}),a(c).find("a").click(function(){a(this).trigger("tabchange")})})}(jQuery,PlentyFramework),function(a,b){b.directive('[data-plenty="toTop"]',function(b,c){a(c).click(function(){return a("html, body").animate({scrollTop:0},400),!1});var d=function(){a(document).scrollTop()>100?a(c).addClass("visible"):a(c).removeClass("visible")};a(window).on("scroll resize",function(){d()})})}(jQuery,PlentyFramework),function($,pm){pm.directive("[data-plenty-toggle]",function(i,elem,MediaSizeService){$(elem).attr("data-plenty-toggle").search(";")<0&&(eval("var data = "+$(elem).attr("data-plenty-toggle")),data.target&&data["class"]&&$(elem).click(function(){var a=!1;if(data.media)if(-1!=data.media.indexOf(" ")){var b=data.media.split(" ");for(i=0;i":">",'"':""","'":"'","/":"/"};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" + - " 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" + - "
"; - -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($) { - - /** - * 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(selector, callback, dependencies, allowDuplicates) { - var directive = { - id: PlentyFramework.directives.length, - selector: selector, - callback: callback, - dependencies: dependencies, - allowDuplicates: !!allowDuplicates, - elements: [] - }; - PlentyFramework.directives.push(directive); - - return directive; - }; - - /** - * Bind registered directives. - * @function bindDirectives - * @param {string} [directiveSelector] restrict binding to elements matching this selector - */ - PlentyFramework.prototype.bindDirectives = function( directiveSelector ) { - - $.each( PlentyFramework.directives, function(i, directive) { - if( !directiveSelector || directiveSelector === directive.selector ) { - var elements = []; - // filter elements already bound - $(directive.selector).each(function (j, obj) { - if ($.inArray(obj, directive.elements) < 0) { - elements.push(obj); - } - }); - - $.each(elements, function (i, elem) { - var params = [i, elem]; - - // append dependencies if exists - if (!!directive.dependencies && directive.dependencies.length > 0) { - params = params.concat(PlentyFramework.resolveServices(directive.dependencies)); - } - - // apply loop variables and depending services to callback - directive.callback.apply(null, params); - }); - - $(elements).each(function (j, obj) { - // store function ID to avoid duplicate bindings - if (!directive.allowDuplicates) { - directive.elements.push(obj); - } - }); - } - - }); - }; - - - /** - * Collection of uncompiled registered factories & services. - * See {{#crossLink "PlentyFramework/compile:method"}}.compile(){{/crossLink}} - * @attribute components - * @static - * @type {{factories: {}, services: {}}} - */ - PlentyFramework.components = { - factories: {}, - services: {} - }; - - /** - * 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 || []; - - PlentyFramework.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( PlentyFramework.components.services.hasOwnProperty(dependency) ) { - PlentyFramework.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 || []; - PlentyFramework.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( PlentyFramework.components.factories.hasOwnProperty(dependency) ) { - PlentyFramework.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 PlentyFramework.components.factories ) { - if( !PlentyFramework.factories.hasOwnProperty(factory) ) { - PlentyFramework.components.factories[factory].compile(); - } - } - - for( var service in PlentyFramework.components.services ) { - if( !PlentyFramework.prototype.hasOwnProperty(service) ) { - PlentyFramework.components.services[service].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($, 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, true, 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(); - }); - }); - } - - /** - * 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(); - }); - }); - - } - - /** - * 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(); - }); - }); - - } - - }, ['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 = ''; - - /** - * 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, - 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; - } - - /** - * 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() { - 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( 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 - * ===================================================================================== - */ - -/** - * @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) { - - 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 - }; - - 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() ) { - 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 - }; - - return setInvoiceAddress(invoiceAddress) - .done(function () { - window.location.assign( form.attr('action') ); - }); - } - } - }, ['APIFactory', '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 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, - 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 }).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() { - // save order params - addArticle( saveOrderParams(article) ); - - // close modal after saving order params - return true; - }) - .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 wrappedThis = {}; - var attrType = ""; - - //Groups - orderParamsForm.find('[name^="ParamGroup"]').each(function(){ - var match = this.name.match(/^ParamGroup\[(\d+)]\[(\d+)]$/); - articleWithParams = addOrderParamValue(articleWithParams, match[1], $(this).val(), $(this).val()); - }); - - //Values - orderParamsForm.find('[name^="ParamValue"]').each(function(){ - wrappedThis = $(this); - attrType = wrappedThis.attr('type'); - - if( ((attrType == 'checkbox' && wrappedThis.is(':checked')) || - (attrType == 'radio' && wrappedThis.is(':checked')) || - (attrType != 'radio' && attrType != 'checkbox')) && - attrType != 'file') - { - var match = this.name.match(/^ParamValue\[(\d+)]\[(\d+)]$/); - - articleWithParams = addOrderParamValue(articleWithParams, match[1], match[2], wrappedThis.val()); - - } else if (attrType == 'file') { - articleWithParams = orderParamFileUpload(this, articleWithParams); - } - }); - - 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.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 (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.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 = []; - modal.find('select').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() { - // save order params - updateArticle( saveOrderParams([basketItem]) ); - - // close modal after saving order params - return true; - }) - .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 ) { - - // get item name - var itemName, originalItemQuantity; - var params = Checkout.getCheckout().BasketItemsList; - - for ( var i = 0; i < params.length; i++ ) { - if ( params[i].BasketItemID == BasketItemID ) { - originalItemQuantity = params[i].BasketItemQuantity; - itemName = params[i].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(); - }); - }); - } - - 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); - }) - .onConfirm(function () { - doDelete(); - }) - .setLabelConfirm( pm.translate("Delete") ) - .show(); - } else { - doDelete(); - } - } - - /** - * 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 ) { - removeBasketItem( BasketItemID ); - } - - 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.reloadContainer('Totals'); - - var basketItemsPriceTotal = 0; - var params2 = Checkout.getCheckout().BasketItemsList; - for (var i = 0; i < params2.length; i++) { - if (params2[i].BasketItemID == BasketItemID) { - basketItemsPriceTotal = params2[i].BasketItemPriceTotal; - } - } - $('[data-basket-item-id="' + BasketItemID + '"]').find('[data-plenty-checkout="basket-item-price-total"]').html(basketItemsPriceTotal); - refreshBasketPreview(); - }); - }); - } - } - - /** - * 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) { - - var checkoutState; - - 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); - checkoutState = Checkout.getCheckout(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; - } - - 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) ) { - - // 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 () { - 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 () { - 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; - - - if( !addressesAreEqual( invoiceAddress, Checkout.getCheckout().CustomerInvoiceAddress ) ) { - - return API.post("/rest/checkout/customerinvoiceaddress/", invoiceAddress) - .done(function (response) { - saveShippingAddress().done(function(){ - 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() { - if( Object.equals(checkoutState, Checkout.getCheckout(true)) ) { - return API.idle(); - } else { - checkoutState = Checkout.getCheckout(true); - 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 - }; - - /** - * 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]); - } - } - - - }); - -}(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(); - }); - } 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 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 ) { - var formControl, formControls, validationKey, currentHasError, group, checked, checkedMin, checkedMax, attrValidate, validationKeys, formControlAttrType; - var wrappedForm = $(form); - var errorClass = !!wrappedForm.attr('data-plenty-checkform') ? wrappedForm.attr('data-plenty-checkform') : 'has-error'; - var missingFields = []; - var hasError = false; - - // check every required input inside form - wrappedForm.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 = wrappedForm.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); - wrappedForm.find('label[for="'+formControl.attr('id')+'"]').addClass(errorClass); - } else { - $(elem).addClass(errorClass); - } - } - } - - }); - - // scroll to element on 'validationFailed' - wrappedForm.on('validationFailed', function() { - var distanceTop = 50; - var errorOffset = wrappedForm.find('.has-error').first().offset().top; - var scrollTarget = $('html, body'); - - // if form is inside of modal, scroll modal instead of body - if( wrappedForm.parents('.modal').length > 0 ) { - scrollTarget = wrappedForm.parents('.modal'); - } else if( wrappedForm.is('.modal') ) { - scrollTarget = wrappedForm; - } - - // 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 - wrappedForm.find('.has-error').each(function(i, elem) { - formControl = $(getFormControl(elem)); - formControl.on('focus click', function() { - formControl.removeClass( errorClass ); - wrappedForm.find('label[for="'+formControl.attr('id')+'"]').removeClass(errorClass); - $(elem).removeClass( errorClass ); - }); - }); - - wrappedForm.trigger('validationFailed', [missingFields]); - } - - var callback = wrappedForm.attr('data-plenty-callback'); - - if( !hasError && !!callback && callback != "submit" && typeof window[callback] == "function") { - - var fields = {}; - wrappedForm.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 - * ===================================================================================== - */ - -/** - * @module Directives - */ -/** - * 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('[data-plenty="addBasketItemButton"]', function(i, button, BasketService) - { - - $(button).click( function(e) - { - // avoid directing to href - e.preventDefault(); - - //init - var basketItemsList = {}; - var parentForm = $(button).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]); - - }); - }, ['BasketService']); -} (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 - * ===================================================================================== - */ - -(function($, pm) { - - // append Bootstrap Tooltip - pm.directive('[data-toggle="tooltip"]', function(i, elem) { - $(elem).tooltip({ - container: 'body' - }); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - pm.directive('[data-plenty-checkout-href]', function(i, elem, NavigatorService) { - $(elem).click(function () { - NavigatorService.goToID( $(this).attr('data-plenty-checkout-href') ); - }); - }, ['NavigatorService']); -} (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 - * ===================================================================================== - */ - -(function($, pm) { - - - /* - * content page slider - * - * usage (functionality requires only attribute data-plenty="contentpageSlider"): - *
- *
- * ... - *
- *
- * ... - *
- * ... - *
- */ - pm.directive('[data-plenty="contentpageSlider"]', function(i, 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-lazyload]').trigger('appear'); } - }); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* - * Equal Box heights - */ - pm.directive('[data-plenty-equal]', function(i, elem, MediaSizeService) { - var mediaSizes = $(elem).data('plenty-equal').replace(/\s/g, '').split(','); - - var targets = ( $(elem).find('[data-plenty-equal-target]').length > 0 ) ? $(elem).find('[data-plenty-equal-target]') : $(elem).children(); - - var maxHeight = 0; - $(targets).each(function(j, child) { - - $(child).css('height', ''); - - if( $(child).outerHeight(true) > maxHeight ) { - maxHeight = $(child).outerHeight(true); - } - }); - - if( !mediaSizes || $.inArray( MediaSizeService.interval(), mediaSizes ) >= 0 ) targets.height(maxHeight); - - }, ['MediaSizeService'], true); - - // refresh calculation on window resize - $(window).on('sizeChange', function() { - pm.getInstance().bindDirectives( '[data-plenty-equal]' ); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // lazyload images (requires lazyload.min.js) - // TODO: handle external dependencies dependencies - pm.directive('img[data-plenty-lazyload]', function(i, elem) { - $(elem).lazyload({ - effect: $(this).attr('data-plenty-lazyload') - }); - $(elem).on("loaded", function() { - $(elem).css('display', 'inline-block'); - }); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - pm.directive('[data-plenty-checkout-form="customerLogin"]', function(i, elem, AuthenticationService) { - $(elem).on('submit', function (e) { - e.preventDefault(); - AuthenticationService.customerLogin( $(e.target) ); - }); - }, ["AuthenticationService"]); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* - * 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' - */ - // TODO: handle external dependency to Modernizr - pm.directive('.dropdown > a[data-plenty-enable]', function(i, elem, MediaSizeService) { - - if( $(elem).attr('data-plenty-enable') == "toggle-xs-sm-or-touch" ) { - $(elem).click(function(e) { - if ( MediaSizeService.interval() == 'xs' || MediaSizeService.interval() == 'sm' || ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) ) { - $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').not( $(this) ).parent().removeClass('open'); - $(this).parent().toggleClass('open'); - return false; - } - }); - } - - // dropdown enabled touch - else if( $(elem).attr('data-plenty-enable') == "touch" ) { - $(elem).click(function() { - if ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) { // otherwise already has mobile navigation - $('.dropdown.open > a[data-plenty-enable="touch"]').not( $(this) ).parent().removeClass('open'); - if ( ! $(this).parent().hasClass('open') ) { - $(this).parent().addClass('open'); - return false; - } - } - }); - } - }, ['MediaSizeService']); - - - pm.directive('*', function(i, elem, MediaSizeService) { - - $(elem).click(function (e) { - if (MediaSizeService.interval() == 'xs' || MediaSizeService.interval() == 'sm' || ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch )) { - var dropdown = $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent(); - if (dropdown.length > 0 && !dropdown.is(e.target) && dropdown.has(e.target).length <= 0) { - dropdown.removeClass('open'); - } - } - - if (MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch) { - var dropdown = $('.dropdown.open > a[data-plenty-enable="touch"]').parent(); - if (dropdown.length > 0 && !dropdown.is(e.target) && dropdown.has(e.target).length <= 0) { - dropdown.removeClass('open'); - } - } - }); - }, ['MediaSizeService']); - - - pm.directive(window, function(i, elem, MediaSizeService) { - $(window).on('orientationchange', function() { - if ( MediaSizeService.interval() == 'xs' || MediaSizeService.interval() == 'sm' || ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) ) { - $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent().removeClass('open'); - } - - if ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) { - $('.dropdown.open > a[data-plenty-enable="touch"]').parent().removeClass('open'); - } - }); - $(window).on('sizeChange', function(newValue) { - if ( newValue != 'xs' && newValue != 'sm' && ! Modernizr.touch ) { - $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent().removeClass('open'); - } - }); - }, ['MediaSizeService']); - - $(document).ready(function() { - - if ( pm.getInstance().MediaSizeService.interval() != 'xs' && pm.getInstance().MediaSizeService.interval() != 'sm' && Modernizr.touch ) { - $('.dropdown.open > a[data-plenty-enable="touch"]').parent().removeClass('open'); - } - - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // Call function if enter key pressed while element is focused - pm.directive('[data-plenty-onenter]', function(i, elem) { - var onEnter = $(elem).attr('data-plenty-onenter'); - var callback = typeof window[onEnter] === 'function' ? window[onEnter] : (new Function('return ' + onEnter)); - $(elem).on('keypress', function(e) { - - if(e.which === 13 && !!callback && typeof callback === "function") { - callback.call(); - } - }); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // Tree navigation toggle - pm.directive('[data-plenty="openCloseToggle"]', function(i, elem) { - $(elem).click(function () { - $(elem).parent().addClass('animating'); - $(elem).siblings('ul').slideToggle(200, function () { - if ($(elem).parent().is('.open')) { - $(elem).parent().removeClass('open'); - } - else { - $(elem).parent().addClass('open'); - } - $(elem).removeAttr('style'); - $(elem).parent().removeClass('animating'); - }); - }); - - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // TODO: merge to single directive. Differentiate between increasing and decreasing by additional parameter - pm.directive('[data-plenty="quantityInputButtonPlus"]', function(i, elem) { - // quantity input plus/minus buttons - $(elem).click(function() { - var input = $($(elem).closest('[data-plenty="quantityInputWrapper"]').find('input')); - var value = parseInt( input.val() ); - var maxLength = parseInt(input.attr('maxlength')) || 1000; - if ( ( (value + 1) + '').length <= maxLength ) { - input.val(value + 1); - } - }); - }); - - pm.directive('[data-plenty="quantityInputButtonMinus"]', function(i, elem) { - $(elem).click(function() { - var input = $($(elem).closest('[data-plenty="quantityInputWrapper"]').find('input')); - var value = parseInt( input.val() ); - if ( value > 1 ) { - input.val(value - 1); - } - }); - }); - - // Quantity Buttons in BasketView - pm.directive('[data-basket-item-id] [data-plenty="quantityInputButtonPlus"], [data-basket-item-id] [data-plenty="quantityInputButtonMinus"]', function(i, button) { - $(button).click(function() { - - var self = $(this); - if( !!self.data('timeout') ) { - window.clearTimeout( self.data('timeout') ); - } - - var timeout = window.setTimeout(function() { - self.parents('[data-plenty="quantityInputWrapper"]').find('[data-plenty="quantityInput"]').trigger('change'); - }, 1000); - - self.data('timeout', timeout); - - }); - }); - - pm.directive('[data-basket-item-id] [data-plenty="quantityInput"]', function(i, input, BasketService) { - $(input).change( function() { - - var self = $(this); - var newQuantity = parseInt( self.val() ); - var basketItemID = self.parents('[data-basket-item-id]').attr('data-basket-item-id'); - - BasketService.setItemQuantity( - basketItemID, - newQuantity - ); - }); - }, ['BasketService']); - - - - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // link non-anchor elements - pm.directive('a[data-plenty-href]', function(i, elem, MediaSizeService) { - $(elem).each(function() { - var href = $(this).attr('href'); - var identifier = $(this).attr('data-plenty-href'); - - $('[data-plenty-link="'+identifier+'"]').click(function() { - if( MediaSizeService.interval() != 'xs' ) { - window.location.assign( href ); - } - }); - }); - }, ['MediaSizeService']); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - // Toggle target content on click. - // Can be bound on checked-/ unchecked-property of radio buttons - pm.directive('[data-plenty-slidetoggle]', function(i, trigger) { - - var target = $( $(trigger).attr('data-plenty-target') ); - - if( $(trigger).is('input[type="radio"]') ) { - // is radio button - var radioList = $('input[type="radio"][name="'+( $(trigger).attr('name') )+'"]'); - var visibleOnChecked = $(trigger).is('[data-plenty-slidetoggle="checked"]'); - $(radioList).change(function() { - $(target).parents('[data-plenty-equal-target]').css('height', 'auto'); - - if ( $(this).is(':checked') && $(this)[0] === $(trigger)[0] ) { - // checked - if ( visibleOnChecked == true ) { - $(target).slideDown(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } else { - $(target).slideUp(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } - } - else { - // unchecked (since other radio button has been checked) - if ( visibleOnChecked == true ) { - $(target).slideUp(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } else { - $(target).slideDown(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } - } - }); - } else { - // is not radio button - $(trigger).click(function() { - $(target).parents('[data-plenty-equal-target]').css('height', 'auto'); - - $(trigger).addClass('animating'); - $(target).slideToggle(400, function() { - $(trigger).removeClass('animating'); - $(trigger).toggleClass('active'); - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - }); - } - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* - * 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 - */ - pm.directive('[data-plenty-social]', function(i, elem, SocialShareService) { - - var toggle = $(elem).find('[data-plenty="switch"]'); - - // append container to put / delete service.html - $(elem).append(''); - - // add "off" class to switch, if neither "off" or "on" is set - if ( !toggle.hasClass('off') && !toggle.hasClass('on') ) { - toggle.addClass('off'); - } - - // toggle switch - toggle.on('click', function() { - if ( toggle.hasClass('off') ) { - if ( $(elem).attr("data-toggle") == "tooltip" ) { $(elem).tooltip('destroy') }; - toggle.removeClass('off').addClass('on'); - // hide dummy button - $(elem).find('[data-plenty="placeholder"]').hide(); - // load HTML defined in 'api' - $(elem).find('.social-container').append( SocialShareService.getSocialService( $(elem).attr('data-plenty-social') ) ); - } - // do not disable social medias after activation - /* - else - { - toggle.removeClass('on').addClass('off'); - // show dummy button - $(elem).find('[data-plenty="placeholder"]').show(); - // remove api HTML - $(elem).find('.social-container').html(''); - } - */ - }); - }, ['SocialShareService']); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* Tab Handling - * - * Show tab with jQuery-selector 'TAB_SELECTOR' - - * (!) Requires bootstrap.js - * - * Show remote tab with jQuery-selector 'TAB_1' in target container (below) - - * - */ - pm.directive('a[data-plenty-opentab]', function(i, elem) { - // open tab - $(elem).click(function() { - var tabSelector = $(this).attr('data-plenty-opentab'); - tabSelector = ( tabSelector == 'href' ) ? $(this).attr('href') : tabSelector; - $(tabSelector).tab('show'); - }); - }); - - pm.directive('[data-plenty-openremotetab]', function(i, elem) { - // open remote tab{ - $(elem).click(function () { - var tabSelector = $(this).attr('data-plenty-openremotetab'); - $(tabSelector).trigger('tabchange'); - }); - }); - - /* - * Remote tabs - * tab content can be placed anywhere in body - * - * Content of remote tab -
- -
- * - * Remote tab navigation - * [...] -
- * - */ - pm.directive('[data-plenty="remoteTabs"]', function(i, remoteTab) { - - var tabsId = $(remoteTab).attr('data-plenty-remotetabs-id'); - - // find tabs grouped by remotetabs-id - $('[data-plenty="remoteTabs"][data-plenty-remotetabs-id="'+tabsId+'"]').each(function(i, tabs) { - - // bind each remote-tab - $(tabs).find('a').each(function(i, singleTab) { - - var singleTabId = $(singleTab).attr('data-plenty-tab-id'); - - // listen to 'tabchange' event - $(singleTab).on('tabchange', function() { - // toggle class 'active' - $(singleTab).closest('[data-plenty="remoteTabs"]').children('.active').removeClass('active'); - $(singleTab).closest('li').addClass('active'); - - // hide inactive tabs & show active tab - var tabpanelsInactive = $('[data-plenty-remotetabs-id="'+tabsId+'"][data-plenty-tabpanel-labelledby]').not('[data-plenty-tabpanel-labelledby="'+singleTabId+'"]'); - var tabpanelActive = $('[data-plenty-remotetabs-id="'+tabsId+'"][data-plenty-tabpanel-labelledby="'+singleTabId+'"]'); - var zIndexTabpanelParents = 0; - if ( $(tabs).attr('data-plenty-remotetabs-adapt') == 'tabpanel-parent' ) { - zIndexTabpanelParents = 2147483646; - $('[data-plenty-remotetabs-id="'+tabsId+'"][data-plenty-tabpanel-labelledby]').parent().each(function() { - var zIndexCurrent = parseInt( $(this).css('zIndex') ); - if ( typeof zIndexCurrent == 'number' && zIndexCurrent < zIndexTabpanelParents ) zIndexTabpanelParents = zIndexCurrent; - }); - } - - // adjust z-index if neccessary - $(tabpanelsInactive).hide().removeClass('in'); - $(tabpanelActive).show().addClass('in'); - if ( zIndexTabpanelParents != 0 ) { - $(tabpanelsInactive).parent().css('zIndex', zIndexTabpanelParents); - $(tabpanelActive).parent().css('zIndex', zIndexTabpanelParents + 1); - } - }); - }); - }); - - // trigger 'tabchange' event - $(remoteTab).find('a').click(function() { - $(this).trigger('tabchange'); - }); - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - pm.directive('[data-plenty="toTop"]', function(i, elem) { - $(elem).click(function() { - $('html, body').animate({ - scrollTop: 0 - }, 400); - return false; - }); - - var positionToTopButton = function() { - if( $(document).scrollTop() > 100 ) { - $(elem).addClass('visible'); - } else { - $(elem).removeClass('visible'); - } - }; - - $(window).on("scroll resize", function() { - positionToTopButton(); - }); - - }); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* - * 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="" - */ - pm.directive('[data-plenty-toggle]', function(i, elem, MediaSizeService) { - if( $(elem).attr('data-plenty-toggle').search(';') < 0 ) { - eval('var data = ' + $(elem).attr('data-plenty-toggle')); - if ( data.target && data.class ) { - $(elem).click(function() { - var isMedia = false; - if ( data.media ) { - if ( data.media.indexOf(' ') != -1 ) { - var mediaArr = data.media.split(' '); - for ( i = 0; i < mediaArr.length; i++ ) { - if ( MediaSizeService.interval() == mediaArr[i] ) { - isMedia = true; - } - } - } - else { - if ( MediaSizeService.interval() == data.media ) isMedia = true; - } - } - if ( ! data.media || isMedia == true ) { - $(data.target).toggleClass(data.class); - if ( $(elem).is('a') ) return false; - } - }); - } - } - }, ['MediaSizeService']); - -}(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 - * ===================================================================================== - */ - -(function($, pm) { - - /* - * Form Validation - * Validate required form inputs as given type of value - * Usage / data-Attributes: - *
This will activate the script for this form - * and add ERROR_CLASS to invalid elements - * - * Check if the value of this input is text (or number) and add ERROR_CLASS on failure - *
You can put the data-plenty-validate="" on a parent element of an input. - * This will check the value of the input(s) inside and add ERROR_CLASS to the
on failure - - * possible values for data-plenty-validate="" text : validate if value is text (or number or mixed) - * mail : checks if value is a valid mail-address (not depending on inputs type-attribute) - * number: checks if value is a numeric value. For detailed information see: isNumberic() - * {min: 1, max: 3} validate that at least 'min' and maximum 'max' options are selected (checkboxes) - * - * possible form elements to validate - * can validate "text", "mail", "number" - * check if one radio-button in group "myRadio" is checked. - * Ignores the value of data-plenty-validate - * check if one checkbox in group "myCheck" is checked or use - * data-plenty-valudate="{min: 3, max: 5}" to define custom range - * check if an option is selected and otions value is not "-1" (plenty default for: "choose"-option) - * - * Events: - * 'validationFailed' will be triggered if at least one element is not valid. - * Usage: - * form.on('validationFailed', function(event, invalidFields) { - * $(invalidFields).each({ - * // manipulate invalid fields - * }); - * }); - */ - pm.directive('form[data-plenty-checkform], form.PlentySubmitForm', function(i, elem, ValidationService) { - - $(elem).submit(function() { - return ValidationService.validate( elem ); - }); - - }, ['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/dist/plentymarketsCMStools-0.9.1.min.js b/dist/plentymarketsCMStools-0.9.1.min.js deleted file mode 100644 index 2008374..0000000 --- a/dist/plentymarketsCMStools-0.9.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["error/errorMessage.html"]='
\n Code {{code}}:\n {{{message}}}\n
\n',TemplateCache["error/errorPopup.html"]='
\n \n
\n
\n
\n',TemplateCache["modal/modal.html"]='',TemplateCache["waitscreen/waitscreen.html"]='
',function(a){PlentyFramework=function(){};var b=null;PlentyFramework.getInstance=function(){return b=b||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,d){var e={id:PlentyFramework.directives.length,selector:a,callback:b,dependencies:c,allowDuplicates:!!d,elements:[]};return PlentyFramework.directives.push(e),e},PlentyFramework.prototype.bindDirectives=function(b){a.each(PlentyFramework.directives,function(c,d){if(!b||b===d.selector){var e=[];a(d.selector).each(function(b,c){a.inArray(c,d.elements)<0&&e.push(c)}),a.each(e,function(a,b){var c=[a,b];d.dependencies&&d.dependencies.length>0&&(c=c.concat(PlentyFramework.resolveServices(d.dependencies))),d.callback.apply(null,c)}),a(e).each(function(a,b){d.allowDuplicates||d.elements.push(b)})}})},PlentyFramework.components={factories:{},services:{}},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(PlentyFramework.components.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(!PlentyFramework.components.services.hasOwnProperty(b))return console.error('Cannot inject Service "'+b+'": Service not found.'),!1;PlentyFramework.components.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(PlentyFramework.components.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(!PlentyFramework.components.factories.hasOwnProperty(b))return console.error('Cannot inject Factory "'+b+'": Factory not found.'),!1;PlentyFramework.components.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 PlentyFramework.components.factories)PlentyFramework.factories.hasOwnProperty(a)||PlentyFramework.components.factories[a].compile();for(var b in PlentyFramework.components.services)PlentyFramework.prototype.hasOwnProperty(b)||PlentyFramework.components.services[b].compile();var c=document.getElementsByTagName("SCRIPT");c.length>0&&(PlentyFramework.scriptPath=c[c.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,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,!0,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()})})}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()})})}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()})})}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 r.title=a,this}function e(a){return r.content=a,this}function f(a){return r.labelConfirm=a,this}function g(a){return r.labelDismiss=a,this}function h(a){return r.onConfirm=a,this}function i(a){return r.onDismiss=a,this}function j(a){return r.container=a,this}function k(a){return r.timeout=a,this}function l(){s=c(r.content)?PlentyFramework.partials.Modal.getModal(r.content):a(PlentyFramework.compileTemplate("modal/modal.html",r)),a(r.container).append(s);var b=a(r.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(r.container).append(d)}),PlentyFramework.partials.Modal.init(s,r),s.find('[data-plenty-modal="confirm"]').click(function(){var a=r.onConfirm();a&&m(!0)}),PlentyFramework.partials.Modal.show(s),r.timeout>0&&n()}function m(a){PlentyFramework.partials.Modal.hide(s),a||r.onDismiss()}function n(){v=r.timeout,w=(new Date).getTime(),t=window.setTimeout(function(){window.clearInterval(u),m()},r.timeout),s.find('[data-plenty-modal="timer"]').text(v/1e3),u=window.setInterval(function(){if(!x){var a=v-(new Date).getTime()+w;a=Math.round(a/1e3),s.find('[data-plenty-modal="timer"]').text(a)}},1e3)}function o(){x=!0,v-=(new Date).getTime()-w,window.clearTimeout(t)}function p(){x=!1,w=(new Date).getTime(),t=window.setTimeout(function(){m(),window.clearInterval(u)},v)}function q(){window.clearTimeout(t),window.clearInterval(u)}var r=this;r.title="",r.content="",r.labelDismiss=b.translate("Cancel"),r.labelConfirm=b.translate("Confirm"),r.onConfirm=function(){},r.onDismiss=function(){},r.container="body",r.timeout=-1,r.hide=m,r.startTimeout=n,r.stopTimeout=q,r.pauseTimeout=o,r.continueTimeout=p;var s,t,u,v,w,x=!1;return{setTitle:d,setContent:e,setContainer:j,setLabelConfirm:f,setLabelDismiss:g,onConfirm:h,onDismiss:i,setTimeout:k,show:l,hide:m}}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("AuthenticationService",function(b,c){function d(){var c=a('[data-plenty-checkout="lostPasswordForm"]');if(c.validateForm()){var d=c.getFormValues(),e={Email:d.Email};return b.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 e(a){if(a.validateForm()){var c=a.getFormValues(),d={Email:c.loginMail,Password:c.loginPassword};return b.post("/rest/checkout/login/",d).done(function(){window.location.assign(a.attr("action"))})}}function f(a){return b.post("/rest/checkout/customerinvoiceaddress/",a).done(function(a){c.getCheckout().CustomerInvoiceAddress=a.data})}function g(){var b=a('[data-plenty-checkout-form="customerRegistration"]');if(b.validateForm()){var c=b.getFormValues(),d={LoginType:2,FormOfAddressID:c.FormOfAddressID,Company:c.Company,FirstName:c.FirstName,LastName:c.LastName,Street:c.Street,HouseNo:c.HouseNo,AddressAdditional:c.AddressAdditional,ZIP:c.ZIP,City:c.City,CountryID:c.CountryID,VATNumber:c.VATNumber,Email:c.Email,EmailRepeat:c.EmailRepeat,BirthDay:c.BirthDay,BirthMonth:c.BirthMonth,BirthYear:c.BirthYear,Password:c.Password,PasswordRepeat:c.PasswordRepeat,PhoneNumber:c.PhoneNumber,MobileNumber:c.MobileNumber,FaxNumber:c.FaxNumber,Postnummer:c.Postnummer};return f(d).done(function(){window.location.assign(b.attr("action"))})}}return{resetPassword:d,customerLogin:e,setInvoiceAddress:f,registerCustomer:g}},["APIFactory","CheckoutFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("BasketService",function(c,d,e,f,g){function h(a){a&&c.get("/rest/checkout/container_"+"CheckoutOrderParamsList".toLowerCase()+"/",{itemID:a[0].BasketItemItemID,quantity:a[0].BasketItemQuantity}).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 j(i(a)),!0}).show():j(a)})}function i(b){var c=a('[data-plenty-checkout-form="OrderParamsForm"]'),d={},e="";return c.find('[name^="ParamGroup"]').each(function(){var c=this.name.match(/^ParamGroup\[(\d+)]\[(\d+)]$/);b=m(b,c[1],a(this).val(),a(this).val())}),c.find('[name^="ParamValue"]').each(function(){if(d=a(this),e=d.attr("type"),("checkbox"==e&&d.is(":checked")||"radio"==e&&d.is(":checked")||"radio"!=e&&"checkbox"!=e)&&"file"!=e){var c=this.name.match(/^ParamValue\[(\d+)]\[(\d+)]$/);b=m(b,c[1],c[2],d.val())}else"file"==e&&(b=l(this,b))}),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(b,d){var e,f,g=b.id,h={},i=[],j={type:"POST",data:{},isFile:!0,cache:!1,dataType:"json",processData:!1,contentType:!1};h[g]=a(b)[0].files,-1==i.indexOf(g)&&i.push(g);for(var k=0,l=i.length;l>k;++k)e=new FormData,f=h[i[k]],e.append("0",f[0],f[0].name),j.data=e,c.post("/rest/checkout/orderparamfile/",j);var n=b.name.match(/^ParamValueFile\[(\d+)]\[(\d+)]$/);return m(d,n[1],n[2],a(b).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").each(function(b,c){var f=c.name.match(/^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/);f&&f[1]&&e.push({BasketItemAttributeID:f[1],BasketItemAttributeValueID:a(c).val()}),0!=e.length&&(d.BasketItemAttributesList=e)}),k([d])})}function o(a){var d=p(a);d.BasketItemOrderParamsList=[],c.get("/rest/checkout/container_"+"CheckoutOrderParamsList".toLowerCase()+"/",{itemID:d.BasketItemItemID,quantity:d.BasketItemQuantity,basketItemID:a}).done(function(a){g.prepare().setContent(a.data[0]).setTitle(b.translate("Edit order parameters")).setLabelConfirm(b.translate("Save")).onConfirm(function(){return k(i([d])),!0}).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:i})+"

").onDismiss(function(){a('[data-basket-item-id="'+d+'"]').find('[data-plenty="quantityInput"]').val(j)}).onConfirm(function(){h()}).setLabelConfirm(b.translate("Delete")).show()}function r(b,d){0>=d&&q(b);for(var e,g,h=f.getCheckout().BasketItemsList,i=0;i0&&f.reloadContainer("Totals")}return{addItem:h,removeItem:q,setItemQuantity:r,editItemAttributes:n,editOrderParams:o,addCoupon:t,removeCoupon:u}},["APIFactory","UIFactory","CMSFactory","CheckoutFactory","ModalFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("CheckoutService",function(b,c,d,e){function f(){d.loadCheckout(!0),t=d.getCheckout(!0)}function g(){var c=a('[data-plenty-checkout-form="details"]'),e=c.getFormValues();return d.getCheckout().CheckoutCustomerSign||(d.getCheckout().CheckoutCustomerSign=""),d.getCheckout().CheckoutOrderInfoText||(d.getCheckout().CheckoutOrderInfoText=""),d.getCheckout().CheckoutCustomerSign!==e.CustomerSign&&a(c).find('[name="CustomerSign"]').length>0||d.getCheckout().CheckoutOrderInfoText!==e.OrderInfoText&&a(c).find('[name="OrderInfoText"]').length>0?(d.getCheckout().CheckoutCustomerSign=e.CustomerSign,d.getCheckout().CheckoutOrderInfoText=e.OrderInfoText,d.setCheckout()):b.idle()}function h(c){var e=a('[data-plenty-checkout-form="shippingAddress"]');if(!c&&!e.validateForm())return!1;var f=e.getFormValues(),g=a('[name="shippingAddressID"]:checked').val();if(a("#shippingAdressSelect").modal("hide"),0>g){var h=f;return j(h,d.getCheckout().CustomerShippingAddress)?b.idle():b.post("/rest/checkout/customershippingaddress/",h).done(function(a){d.getCheckout().CheckoutCustomerShippingAddressID=a.data.ID,d.getCheckout().CheckoutShippingCountryID=a.data.CountryID,delete d.getCheckout().CheckoutMethodOfPaymentID,delete d.getCheckout().CheckoutShippingProfileID,d.setCheckout().done(function(){2==d.getCheckout().CustomerInvoiceAddress.LoginType&&d.reloadContainer("CustomerShippingAddress")})})}return g!=d.getCheckout().CheckoutCustomerShippingAddressID?(d.getCheckout().CheckoutCustomerShippingAddressID=g,delete d.getCheckout().CheckoutMethodOfPaymentID,delete d.getCheckout().CheckoutShippingProfileID,d.setCheckout().done(function(){2==d.getCheckout().CustomerInvoiceAddress.LoginType&&d.reloadContainer("CustomerShippingAddress")})):b.idle()}function i(){var c=a('[data-plenty-checkout-form="guestRegistration"]'),e=c.getFormValues();return e.LoginType=1,j(e,d.getCheckout().CustomerInvoiceAddress)?h():b.post("/rest/checkout/customerinvoiceaddress/",e).done(function(a){h().done(function(){d.getCheckout().CustomerInvoiceAddress=a.data})})}function j(a,b){for(var c in a)if(a[c]+""!=b[c]+""&&"EmailRepeat"!==c)return!1;return!0}function k(){var b=a('[data-plenty-checkout-form="shippingProfileSelect"]').getFormValues();return d.getCheckout().CheckoutShippingProfileID=b.ShippingProfileID,delete d.getCheckout().CheckoutCustomerShippingAddressID,delete d.getCheckout().CheckoutMethodOfPaymentID,d.setCheckout().done(function(){d.reloadContainer("MethodsOfPaymentList")})}function l(){return Object.equals(t,d.getCheckout(!0))?b.idle():(t=d.getCheckout(!0),b.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;e.prepare().setContent(b.data.CheckoutMethodOfPaymentAdditionalContent).onConfirm(function(){return c?o():q()}).show()}}))}function m(b){return b=b||a('[data-plenty-checkout-form="methodOfPayment"]').getFormValues().MethodOfPaymentID,d.getCheckout().CheckoutMethodOfPaymentID=b,delete d.getCheckout().CheckoutCustomerShippingAddressID,delete d.getCheckout().CheckoutShippingProfileID,d.setCheckout().done(function(){d.reloadContainer("ShippingProfilesList")})}function n(){c.getContainer("CheckoutPaymentInformationBankDetails").from("Checkout").done(function(b){e.prepare().setContent(b.data[0]).onDismiss(function(){a('input[name="MethodOfPaymentID"]').each(function(b,c){a(c).val()==d.getCheckout().CheckoutMethodOfPaymentID?a(c).attr("checked","checked"):a(c).removeAttr("checked")})}).onConfirm(function(){return o()}).show()})}function o(){var c=a('[data-plenty-checkout-form="bankDetails"]');if(c.validateForm()){var e=c.getFormValues().checkout.customerBankDetails,f={CustomerBankName:e.bankName,CustomerBLZ:e.blz,CustomerAccountNumber:e.accountNo,CustomerAccountOwner:e.accountOwner,CustomerIBAN:e.iban,CustomerBIC:e.bic};return b.post("/rest/checkout/paymentinformationbankdetails/",f).done(function(){d.loadCheckout().done(function(){m(3),d.reloadContainer("MethodsOfPaymentList")})}),!0}return!1}function p(){c.getContainer("CheckoutPaymentInformationCreditCard").from("Checkout").done(function(b){e.prepare().setContent(b.data[0]).onDismiss(function(){a('input[name="MethodOfPaymentID"]').each(function(b,c){a(c).val()==d.getCheckout().CheckoutMethodOfPaymentID?a(c).attr("checked","checked"):a(c).removeAttr("checked")})}).onConfirm(function(){return q()}).show()})}function q(){var c=a('[data-plenty-checkout-form="creditCard"]');if(c.validateForm()){var e=c.getFormValues().checkout.paymentInformationCC,f={Owner:e.owner,Cvv2:e.cvv2,Number:e.number,Year:e.year,Month:e.month,Provider:e.provider};return b.post("/rest/checkout/paymentinformationcreditcard/",f).done(function(){d.loadCheckout()}),!0}return!1}function r(b){if(2==d.getCheckout().CustomerInvoiceAddress.LoginType)var f=a('[data-plenty-checkout-form="shippingAddress"]').getFormValues();else var f=a('[data-plenty-checkout-form="guestRegistration"]').getFormValues();var g={street:f.Street,houseNo:f.HouseNo,ZIP:f.ZIP,city:f.City,postnummer:f.Postnummer,suggestionType:"postfinder"};c.getContainer("CheckoutAddressSuggestionResultsList",g).from("Checkout").done(function(a){e.prepare().setContent(a.data[0]).show()})}function s(){var c=a('[data-plenty-checkout-form="placeOrder"]');if(c.validateForm()){var d=c.getFormValues(),f={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 b.post("/rest/checkout/placeorder/",f).done(function(a){""!=a.data.MethodOfPaymentRedirectURL?window.location.assign(a.data.MethodOfPaymentRedirectURL):""!=a.data.MethodOfPaymentAdditionalContent?e.prepare().setContent(a.data.MethodOfPaymentAdditionalContent).setLabelDismiss("").onDismiss(function(){ -window.location.assign(c.attr("action"))}).onConfirm(function(){window.location.assign(c.attr("action"))}).show():window.location.assign(c.attr("action"))})}}var t;return{init:f,setCustomerSignAndInfo:g,registerGuest:i,setShippingProfile:k,saveShippingAddress:h,loadAddressSuggestion:r,preparePayment:l,setMethodOfPayment:m,editBankDetails:n,editCreditCard:p,placeOrder:s}},["APIFactory","CMSFactory","CheckoutFactory","ModalFactory"])}(jQuery,PlentyFramework),function(a,b){b.service("MediaSizeService",function(){function b(){return d&&c(),d}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!=d){var c=d;d=b,a(window).trigger("sizeChange",[d,c])}}var d;return a(window).resize(c),a(document).ready(c),{interval:b}})}(jQuery,PlentyFramework),function(a,b){b.service("NavigatorService",function(c,d){function e(){if(q=a('[data-plenty-checkout="navigation"] > 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()})):(k(g),b.getInstance().bindDirectives()))}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("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){var formControl,formControls,validationKey,currentHasError,group,checked,checkedMin,checkedMax,attrValidate,validationKeys,formControlAttrType,wrappedForm=$(form),errorClass=wrappedForm.attr("data-plenty-checkform")?wrappedForm.attr("data-plenty-checkform"):"has-error",missingFields=[],hasError=!1;wrappedForm.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=wrappedForm.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),wrappedForm.find('label[for="'+formControl.attr("id")+'"]').addClass(errorClass)):$(elem).addClass(errorClass))}}),wrappedForm.on("validationFailed",function(){var a=50,b=wrappedForm.find(".has-error").first().offset().top,c=$("html, body");wrappedForm.parents(".modal").length>0?c=wrappedForm.parents(".modal"):wrappedForm.is(".modal")&&(c=wrappedForm),(b-awindow.pageYOffset+window.innerHeight)&&c.animate({scrollTop:b-a})}),hasError&&(wrappedForm.find(".has-error").each(function(a,b){formControl=$(getFormControl(b)),formControl.on("focus click",function(){formControl.removeClass(errorClass),wrappedForm.find('label[for="'+formControl.attr("id")+'"]').removeClass(errorClass),$(b).removeClass(errorClass)})}),wrappedForm.trigger("validationFailed",[missingFields]));var callback=wrappedForm.attr("data-plenty-callback");if(!hasError&&callback&&"submit"!=callback&&"function"==typeof window[callback]){var fields={};return wrappedForm.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('[data-plenty="addBasketItemButton"]',function(b,c,d){a(c).click(function(b){b.preventDefault();var e={},f=a(c).parents("form");e.BasketItemItemID=f.find('[name="ArticleID"]').val(),e.BasketItemPriceID=f.find('[name="SYS_P_ID"]').val(),e.BasketItemQuantity=f.find('[name="ArticleQuantity"]').val(),e.BasketItemBranchID=f.find('[name="source_category"]').val();var g=f.find('[name^="ArticleAttribute"]'),h=[];a.each(g,function(b,c){var d=c.name.match(/^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/);d&&d[1]&&h.push({BasketItemAttributeID:d[1],BasketItemAttributeValueID:a(c).val()})}),0!=h.length&&(e.BasketItemAttributesList=h),d.addItem([e])})},["BasketService"])}(jQuery,PlentyFramework),function(a,b){b.directive('[data-toggle="tooltip"]',function(b,c){a(c).tooltip({container:"body"})})}(jQuery,PlentyFramework),function(a,b){b.directive("[data-plenty-checkout-href]",function(b,c,d){a(c).click(function(){d.goToID(a(this).attr("data-plenty-checkout-href"))})},["NavigatorService"])}(jQuery,PlentyFramework),function(a,b){b.directive('[data-plenty="contentpageSlider"]',function(b,c){a(c).owlCarousel({navigation:!0,navigationText:!1,slideSpeed:1e3,paginationSpeed:1e3,singleItem:!0,autoPlay:6e3,stopOnHover:!0,afterMove:function(b){a(b).find("img[data-plenty-lazyload]").trigger("appear")}})})}(jQuery,PlentyFramework),function(a,b){b.directive("[data-plenty-equal]",function(b,c,d){var e=a(c).data("plenty-equal").replace(/\s/g,"").split(","),f=a(c).find("[data-plenty-equal-target]").length>0?a(c).find("[data-plenty-equal-target]"):a(c).children(),g=0;a(f).each(function(b,c){a(c).css("height",""),a(c).outerHeight(!0)>g&&(g=a(c).outerHeight(!0))}),(!e||a.inArray(d.interval(),e)>=0)&&f.height(g)},["MediaSizeService"],!0),a(window).on("sizeChange",function(){b.getInstance().bindDirectives("[data-plenty-equal]")})}(jQuery,PlentyFramework),function(a,b){b.directive("img[data-plenty-lazyload]",function(b,c){a(c).lazyload({effect:a(this).attr("data-plenty-lazyload")}),a(c).on("loaded",function(){a(c).css("display","inline-block")})})}(jQuery,PlentyFramework),function(a,b){b.directive('[data-plenty-checkout-form="customerLogin"]',function(b,c,d){a(c).on("submit",function(b){b.preventDefault(),d.customerLogin(a(b.target))})},["AuthenticationService"])}(jQuery,PlentyFramework),function(a,b){b.directive(".dropdown > a[data-plenty-enable]",function(b,c,d){"toggle-xs-sm-or-touch"==a(c).attr("data-plenty-enable")?a(c).click(function(b){return"xs"==d.interval()||"sm"==d.interval()||"xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch?(a('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').not(a(this)).parent().removeClass("open"),a(this).parent().toggleClass("open"),!1):void 0}):"touch"==a(c).attr("data-plenty-enable")&&a(c).click(function(){return"xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch&&(a('.dropdown.open > a[data-plenty-enable="touch"]').not(a(this)).parent().removeClass("open"),!a(this).parent().hasClass("open"))?(a(this).parent().addClass("open"),!1):void 0})},["MediaSizeService"]),b.directive("*",function(b,c,d){a(c).click(function(b){if("xs"==d.interval()||"sm"==d.interval()||"xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch){var c=a('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent();c.length>0&&!c.is(b.target)&&c.has(b.target).length<=0&&c.removeClass("open")}if("xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch){var c=a('.dropdown.open > a[data-plenty-enable="touch"]').parent();c.length>0&&!c.is(b.target)&&c.has(b.target).length<=0&&c.removeClass("open")}})},["MediaSizeService"]),b.directive(window,function(b,c,d){a(window).on("orientationchange",function(){("xs"==d.interval()||"sm"==d.interval()||"xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch)&&a('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent().removeClass("open"),"xs"!=d.interval()&&"sm"!=d.interval()&&Modernizr.touch&&a('.dropdown.open > a[data-plenty-enable="touch"]').parent().removeClass("open")}),a(window).on("sizeChange",function(b){"xs"==b||"sm"==b||Modernizr.touch||a('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent().removeClass("open")})},["MediaSizeService"]),a(document).ready(function(){"xs"!=b.getInstance().MediaSizeService.interval()&&"sm"!=b.getInstance().MediaSizeService.interval()&&Modernizr.touch&&a('.dropdown.open > a[data-plenty-enable="touch"]').parent().removeClass("open")})}(jQuery,PlentyFramework),function(a,b){b.directive("[data-plenty-onenter]",function(b,c){var d=a(c).attr("data-plenty-onenter"),e="function"==typeof window[d]?window[d]:new Function("return "+d);a(c).on("keypress",function(a){13===a.which&&e&&"function"==typeof e&&e.call()})})}(jQuery,PlentyFramework),function(a,b){b.directive('[data-plenty="openCloseToggle"]',function(b,c){a(c).click(function(){a(c).parent().addClass("animating"),a(c).siblings("ul").slideToggle(200,function(){a(c).parent().is(".open")?a(c).parent().removeClass("open"):a(c).parent().addClass("open"),a(c).removeAttr("style"),a(c).parent().removeClass("animating")})})})}(jQuery,PlentyFramework),function(a,b){b.directive('[data-plenty="quantityInputButtonPlus"]',function(b,c){a(c).click(function(){var b=a(a(c).closest('[data-plenty="quantityInputWrapper"]').find("input")),d=parseInt(b.val()),e=parseInt(b.attr("maxlength"))||1e3;(d+1+"").length<=e&&b.val(d+1)})}),b.directive('[data-plenty="quantityInputButtonMinus"]',function(b,c){a(c).click(function(){var b=a(a(c).closest('[data-plenty="quantityInputWrapper"]').find("input")),d=parseInt(b.val());d>1&&b.val(d-1)})}),b.directive('[data-basket-item-id] [data-plenty="quantityInputButtonPlus"], [data-basket-item-id] [data-plenty="quantityInputButtonMinus"]',function(b,c){a(c).click(function(){var b=a(this);b.data("timeout")&&window.clearTimeout(b.data("timeout"));var c=window.setTimeout(function(){b.parents('[data-plenty="quantityInputWrapper"]').find('[data-plenty="quantityInput"]').trigger("change")},1e3);b.data("timeout",c)})}),b.directive('[data-basket-item-id] [data-plenty="quantityInput"]',function(b,c,d){a(c).change(function(){var b=a(this),c=parseInt(b.val()),e=b.parents("[data-basket-item-id]").attr("data-basket-item-id");d.setItemQuantity(e,c)})},["BasketService"])}(jQuery,PlentyFramework),function(a,b){b.directive("a[data-plenty-href]",function(b,c,d){a(c).each(function(){var b=a(this).attr("href"),c=a(this).attr("data-plenty-href");a('[data-plenty-link="'+c+'"]').click(function(){"xs"!=d.interval()&&window.location.assign(b)})})},["MediaSizeService"])}(jQuery,PlentyFramework),function(a,b){b.directive("[data-plenty-slidetoggle]",function(c,d){var e=a(a(d).attr("data-plenty-target"));if(a(d).is('input[type="radio"]')){var f=a('input[type="radio"][name="'+a(d).attr("name")+'"]'),g=a(d).is('[data-plenty-slidetoggle="checked"]');a(f).change(function(){a(e).parents("[data-plenty-equal-target]").css("height","auto"),a(this).is(":checked")&&a(this)[0]===a(d)[0]?1==g?a(e).slideDown(400,function(){b.getInstance().bindDirectives("[data-plenty-equal]")}):a(e).slideUp(400,function(){b.getInstance().bindDirectives("[data-plenty-equal]")}):1==g?a(e).slideUp(400,function(){b.getInstance().bindDirectives("[data-plenty-equal]")}):a(e).slideDown(400,function(){b.getInstance().bindDirectives("[data-plenty-equal]")})})}else a(d).click(function(){a(e).parents("[data-plenty-equal-target]").css("height","auto"),a(d).addClass("animating"),a(e).slideToggle(400,function(){a(d).removeClass("animating"),a(d).toggleClass("active"),b.getInstance().bindDirectives("[data-plenty-equal]")})})})}(jQuery,PlentyFramework),function(a,b){b.directive("[data-plenty-social]",function(b,c,d){var e=a(c).find('[data-plenty="switch"]');a(c).append(''),e.hasClass("off")||e.hasClass("on")||e.addClass("off"),e.on("click",function(){e.hasClass("off")&&("tooltip"==a(c).attr("data-toggle")&&a(c).tooltip("destroy"),e.removeClass("off").addClass("on"),a(c).find('[data-plenty="placeholder"]').hide(),a(c).find(".social-container").append(d.getSocialService(a(c).attr("data-plenty-social"))))})},["SocialShareService"])}(jQuery,PlentyFramework),function(a,b){b.directive("a[data-plenty-opentab]",function(b,c){a(c).click(function(){var b=a(this).attr("data-plenty-opentab");b="href"==b?a(this).attr("href"):b,a(b).tab("show")})}),b.directive("[data-plenty-openremotetab]",function(b,c){a(c).click(function(){var b=a(this).attr("data-plenty-openremotetab");a(b).trigger("tabchange")})}),b.directive('[data-plenty="remoteTabs"]',function(b,c){var d=a(c).attr("data-plenty-remotetabs-id");a('[data-plenty="remoteTabs"][data-plenty-remotetabs-id="'+d+'"]').each(function(b,c){a(c).find("a").each(function(b,e){var f=a(e).attr("data-plenty-tab-id");a(e).on("tabchange",function(){a(e).closest('[data-plenty="remoteTabs"]').children(".active").removeClass("active"),a(e).closest("li").addClass("active");var b=a('[data-plenty-remotetabs-id="'+d+'"][data-plenty-tabpanel-labelledby]').not('[data-plenty-tabpanel-labelledby="'+f+'"]'),g=a('[data-plenty-remotetabs-id="'+d+'"][data-plenty-tabpanel-labelledby="'+f+'"]'),h=0;"tabpanel-parent"==a(c).attr("data-plenty-remotetabs-adapt")&&(h=2147483646,a('[data-plenty-remotetabs-id="'+d+'"][data-plenty-tabpanel-labelledby]').parent().each(function(){var b=parseInt(a(this).css("zIndex"));"number"==typeof b&&h>b&&(h=b)})),a(b).hide().removeClass("in"),a(g).show().addClass("in"),0!=h&&(a(b).parent().css("zIndex",h),a(g).parent().css("zIndex",h+1))})})}),a(c).find("a").click(function(){a(this).trigger("tabchange")})})}(jQuery,PlentyFramework),function(a,b){b.directive('[data-plenty="toTop"]',function(b,c){a(c).click(function(){return a("html, body").animate({scrollTop:0},400),!1});var d=function(){a(document).scrollTop()>100?a(c).addClass("visible"):a(c).removeClass("visible")};a(window).on("scroll resize",function(){d()})})}(jQuery,PlentyFramework),function($,pm){pm.directive("[data-plenty-toggle]",function(i,elem,MediaSizeService){$(elem).attr("data-plenty-toggle").search(";")<0&&(eval("var data = "+$(elem).attr("data-plenty-toggle")),data.target&&data["class"]&&$(elem).click(function(){var a=!1;if(data.media)if(-1!=data.media.indexOf(" ")){var b=data.media.split(" ");for(i=0;i":">",'"':""","'":"'","/":"/"};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; + } + + 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; + + }; + + + /** + * 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 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, true, 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 ) + { + + 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 + }; + + 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'] ); + +}( 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 + } ).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' && $self[0].files && $self[0].files.length > 0 ) + { + articleWithParams = orderParamFileUpload( $self, articleWithParams ); + } + } ); + + 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 ) ) + { + + // 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() + { + 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() + { + 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 ) + { + saveShippingAddress().done( function() + { + 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.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' ); + } + }, ['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 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 ) +{ + // TODO: handle external dependency to Modernizr + pm.directive( 'MobileDropdown', function( MediaSizeService ) + { + var toggleClass = "open"; + var activeDropdown = null; + + return { + initMobileDropdown: initMobileDropdown, + openDropdown : openDropdown + }; + + function initMobileDropdown() + { + $( window ).on( 'orientationchange sizeChange', function() + { + $( '[data-plenty="click:UI.toggleHideShow(this)"]' ).parent( "li." + toggleClass ).removeClass( toggleClass ); + /*if ( !!activeDropdown ) + { + activeDropdown.parent().removeClass( toggleClass ); + + //activeDropdown.parents( "ul" ).find( "li." + toggleClass ).removeClass( toggleClass ); + activeDropdown = null; + }*/ + } ); + + // close open menu on click outside menu + $( 'html' ).click( function() + { + $( '[data-plenty="click:UI.toggleHideShow(this)"]' ).parent( "li.open" ).removeClass( 'open' ); + /*if ( !!activeDropdown ) + { + activeDropdown.parent().removeClass( toggleClass ); + activeDropdown = null; + }*/ + } ); + } + + function openDropdown( elem, mediaSizes ) + { + var $elem = $( elem ); + var activeParent; + + if ( Modernizr.touch && MediaSizeService.isInterval( 'md, lg' ) + || MediaSizeService.isInterval( mediaSizes ) ) + { + if ( !!activeDropdown && activeDropdown[0] == $elem[0] ) + { + activeDropdown.parent().removeClass( toggleClass ); + activeDropdown = null; + } + else + { + if ( !!activeDropdown && activeDropdown[0] != $elem[0] ) + { + activeDropdown.parent().removeClass( toggleClass ); + } + activeDropdown = $elem; + activeParent = activeDropdown.parent(); + activeParent.click( function( event ) + { + event.stopPropagation(); + } ); + if ( !activeParent.hasClass( toggleClass ) ) + { + activeParent.addClass( toggleClass ); + } + } + } + else + { + $elem.unbind( 'click' ); + } + } + + }, ['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() + { + + 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 ) + { + if ( !!tabGroups[groupID] && !!tabGroups[groupID].getTab( tabID ) ) + { + tabGroups[groupID].showTab( tabID ); + } + } + + function TabGroup() + { + var tabs = {}; + var activeTab; + + return { + addTab : addTab, + showTab: showTab, + getTab : getTab + }; + + function addTab( tabID ) + { + tabs[tabID] = new Tab(); + return tabs[tabID]; + } + + function showTab( tabID ) + { + var zIndex = 0; + if ( !!activeTab ) + { + zIndex = parseInt( activeTab.getContent().parent().css( 'zIndex' ) ); + activeTab.hide(); + activeTab.getContent().parent().css( 'zIndex', zIndex - 1 ); + } + else + { + for ( var tab in tabs ) + { + var currentZ = parseInt( tabs[tab].getContent().parent().css( 'zIndex' ) ); + if ( zIndex == 0 || currentZ < zIndex ) + { + zIndex = currentZ; + } + tabs[tab].hide(); + } + + for ( var tab in tabs ) + { + tabs[tab].getContent().parent().css( 'zIndex', zIndex ); + } + } + + activeTab = tabs[tabID]; + activeTab.getContent().parent().css( 'zIndex', zIndex ); + activeTab.show(); + } + + function getTab( tabID ) + { + return tabs[tabID]; + } + } + + function Tab() + { + var $labels = []; + var $content; + + return { + addLabel : addLabel, + setContent: setContent, + getContent: getContent, + show : show, + hide : hide + }; + + 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' ); + } + } + } + + } ); +})( 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, + toggleHideShow : toggleHideShow, + toggleSocialShare : toggleSocialShare, + toggleClass : toggleClass + }; + + 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 ) + { + 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(); + } ); + } ); + } + } + + /** + * 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 $elem = $( target ); + $elem.toggleClass( 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/dist/plentymarketsCMStools-1.0.0.min.js b/dist/plentymarketsCMStools-1.0.0.min.js new file mode 100644 index 0000000..598e689 --- /dev/null +++ b/dist/plentymarketsCMStools-1.0.0.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 f.push(a),c.apply(null,d)})}function c(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==f[c].type)return f[c];return null},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(d.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(!d.services.hasOwnProperty(b))return console.error('Cannot inject Service "'+b+'": Service not found.'),!1;d.services[b].compile()}var e=PlentyFramework.prototype[b];c.push(e)}),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(d.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(!d.factories.hasOwnProperty(b))return console.error('Cannot inject Factory "'+b+'": Factory not found.'),!1;d.factories[b].compile()}var e=PlentyFramework.factories[b];c.push(e)}),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 d.factories)PlentyFramework.factories.hasOwnProperty(a)||d.factories[a].compile();for(var b in d.services)PlentyFramework.prototype.hasOwnProperty(b)||d.services[b].compile();for(var c in d.directives)PlentyFramework.directives.hasOwnProperty(c)||d.directives[c].compile();var e=document.getElementsByTagName("SCRIPT");e.length>0&&(PlentyFramework.scriptPath=e[e.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,!0,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){function e(){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 f(a){if(a.validateForm()){var b=a.getFormValues(),d={Email:b.loginMail,Password:b.loginPassword};return c.post("/rest/checkout/login/",d).done(function(){window.location.assign(a.attr("action"))})}}function g(a){return c.post("/rest/checkout/customerinvoiceaddress/",a).done(function(a){d.getCheckout().CustomerInvoiceAddress=a.data})}function h(){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()})}),g(e).done(function(){window.location.assign(c.attr("action"))})}}return{resetPassword:e,customerLogin:f,setInvoiceAddress:g,registerCustomer:h}},["APIFactory","CheckoutFactory"])}(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}).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"file"==f&&e[0].files&&e[0].files.length>0&&(b=l(e,b))}),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():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(){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(){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.getCheckout().CustomerInvoiceAddress=a.data})})}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.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")}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=d.parents("form");e.BasketItemItemID=f.find('[name="ArticleID"]').val(),e.BasketItemPriceID=f.find('[name="SYS_P_ID"]').val(),e.BasketItemQuantity=f.find('[name="ArticleQuantity"]').val(),e.BasketItemBranchID=f.find('[name="source_category"]').val();var g=f.find('[name^="ArticleAttribute"]'),h=[];a.each(g,function(b,c){var d=c.name.match(/^ArticleAttribute\[\d+]\[\d+]\[(\d+)]$/);d&&d[1]&&h.push({BasketItemAttributeID:d[1],BasketItemAttributeValueID:a(c).val()})}),0!=h.length&&(e.BasketItemAttributesList=h),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=b.parents("[data-basket-item-id]").length>0;if(h){(g+"").length<=f&&g>=0&&e.val(g);var i=b.data("timeout");i&&window.clearTimeout(i),i=window.setTimeout(function(){e.trigger("change")},1e3),b.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(b){function c(){a(window).on("orientationchange sizeChange",function(){a('[data-plenty="click:UI.toggleHideShow(this)"]').parent("li."+e).removeClass(e)}),a("html").click(function(){a('[data-plenty="click:UI.toggleHideShow(this)"]').parent("li.open").removeClass("open")})}function d(c,d){var g,h=a(c);Modernizr.touch&&b.isInterval("md, lg")||b.isInterval(d)?f&&f[0]==h[0]?(f.parent().removeClass(e),f=null):(f&&f[0]!=h[0]&&f.parent().removeClass(e),f=h,g=f.parent(),g.click(function(a){a.stopPropagation()}),g.hasClass(e)||g.addClass(e)):h.unbind("click")}var e="open",f=null;return{initMobileDropdown:c,openDropdown:d}},["MediaSizeService"])}(jQuery,PlentyFramework),function(a,b){b.directive("Redirect",function(b,c){function d(c){"xs"!=b.interval()&&(a(c).length>0?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(){function b(b){a(b).tab("show")}function c(a,b,c){h[c]||(h[c]=new f),h[c].getTab(b)||h[c].addTab(b),h[c].getTab(b).addLabel(a)}function d(a,b,c){h[c]||(h[c]=new f),h[c].getTab(b)||h[c].addTab(b),h[c].getTab(b).setContent(a)}function e(a,b){h[b]&&h[b].getTab(a)&&h[b].showTab(a)}function f(){function a(a){return e[a]=new g,e[a]}function b(a){var b=0;if(d)b=parseInt(d.getContent().parent().css("zIndex")),d.hide(),d.getContent().parent().css("zIndex",b-1);else{for(var c in e){var f=parseInt(e[c].getContent().parent().css("zIndex"));(0==b||b>f)&&(b=f),e[c].hide()}for(var c in e)e[c].getContent().parent().css("zIndex",b)}d=e[a],d.getContent().parent().css("zIndex",b),d.show()}function c(a){return e[a]}var d,e={};return{addTab:a,showTab:b,getTab:c}}function g(){function a(a){return g.push(a),this}function b(a){return f=a,this}function c(){return f}function d(){for(var a=0;a0&&(a(document).scrollTop()>100?n(p,"addClass","visible"):n(p,"removeClass","visible"))})}function e(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 f(c,d,e){var f=a(c),g=0,h={},i=f.find('[data-plenty-rel="equal-target"]').length>0?f.find('[data-plenty-rel="equal-target"]'):f.children();e!==!0&&o.push(c);for(var j=i.length;j>=0;j--)h=a(i[j]),h.css("height",""),h.outerHeight(!0)>g&&(g=h.outerHeight(!0));(!d||b.isInterval(d))&&i.height(g)}function g(b){var c=a(b);c.click(function(){return a("html, body").animate({scrollTop:0},400),!1}),a.inArray(c,p)&&p.push(c)}function h(b,c){var d=a(b);d.lazyload({effect:c}),d.on("loaded",function(){d.css("display","inline-block")})}function i(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 j(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(){m()}):e.slideUp(400,function(){m()})})}else d.click(function(){d.addClass("animating"),e.slideToggle(400,function(){d.removeClass("animating"),d.toggleClass("active"),m()})})}function k(b,d){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(c.getSocialService(d)))})}function l(c,d,e){if(d&&c&&(!e||b.isInterval(e))){var f=a(d);return f.toggleClass(c),!1}}function m(){for(var a=o.length-1;a>=0;a--)f(o[a],"",!0)}function n(a,b,c){for(var d=a.length-1;d>=0;d--)a[d][b](c)}var o=[],p=[];return{initUIWindowEvents:d,addContentPageSlider:e,equalHeight:f,initToTop:g,initLazyload:h,initSlideToggle:j,toggleHideShow:i,toggleSocialShare:k,toggleClass:l}},["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/lang/de_DE.json b/lang/de_DE.json index 1853c64..98a1169 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -7,5 +7,7 @@ "Save": "Speichern", "Do you really want to remove \"{{item}}\" from your basket?": "Möchten Sie den Artikel \"{{item}}\" wirklich aus dem Warenkorb entfernen?", "Select order parameters": "Bestellmerkmale wählen", - "Edit order parameters": "Bestellmerkmale ändern" + "Edit order parameters": "Bestellmerkmale ändern", + "Packstations and post offices in your area": "Packstationen und Postfilialen in der Nähe", + "Please enter a ZIP code and/or a city.": "Bitte geben Sie eine Postleitzahl und/oder einen Ort an." } \ No newline at end of file diff --git a/lang/en_EN.json b/lang/en_EN.json index c23e698..d67956c 100644 --- a/lang/en_EN.json +++ b/lang/en_EN.json @@ -7,5 +7,7 @@ "Save": "Save", "Do you really want to remove \"{{item}}\" from your basket?": "Do you really want to remove \"{{item}}\" from your basket?", "Select order parameters": "Select order parameters", - "Edit order parameters": "Edit order parameters" + "Edit order parameters": "Edit order parameters", + "Packstations and post offices in your area": "Packstations and post offices in your area", + "Please enter a ZIP code and/or a city.": "Please enter a ZIP code and/or a city." } \ No newline at end of file diff --git a/package.json b/package.json index 226718a..251ffe3 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "plentymarketsCMStools", "license": "AGPL-3.0", - "version": "0.9.1", + "version": "1.0.0", "repository": "https://github.com/plentymarkets/plenty-cms-library.git", "devDependencies": { - "grunt": "~0.4.5", + "grunt": "^0.4.5", "grunt-contrib-clean": "^0.6.0", "grunt-contrib-concat": "^0.5.1", "grunt-contrib-copy": "^0.8.0", @@ -13,6 +13,7 @@ "grunt-html-convert": "0.0.2", "grunt-jsdoc": "^0.6.7", "grunt-karma": "^0.11.2", + "jasmine-core": "^2.3.4", "karma": "^0.12.37", "karma-commonjs": "0.0.13", "karma-coverage": "^0.4.2", diff --git a/src/directives/bootstrap-tooltip.js b/src/directives/Authentication.js similarity index 53% rename from src/directives/bootstrap-tooltip.js rename to src/directives/Authentication.js index 142459c..bc1aeab 100644 --- a/src/directives/bootstrap-tooltip.js +++ b/src/directives/Authentication.js @@ -7,13 +7,19 @@ * ===================================================================================== */ -(function($, pm) { +(function( $, pm ) +{ + pm.directive( 'Authentication', function( AuthenticationService ) + { + return { + login: login + }; - // append Bootstrap Tooltip - pm.directive('[data-toggle="tooltip"]', function(i, elem) { - $(elem).tooltip({ - container: 'body' - }); - }); + function login( elem ) + { + pm.getRecentEvent().preventDefault(); + AuthenticationService.customerLogin( $( elem ) ); + } + }, ["AuthenticationService"] ); -}(jQuery, PlentyFramework)); \ No newline at end of file +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/directives/Basket.js b/src/directives/Basket.js new file mode 100644 index 0000000..cf6f909 --- /dev/null +++ b/src/directives/Basket.js @@ -0,0 +1,103 @@ +(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 )); \ No newline at end of file diff --git a/src/directives/DirectivesModule.js b/src/directives/DirectivesModule.js deleted file mode 100644 index 7fd5eef..0000000 --- a/src/directives/DirectivesModule.js +++ /dev/null @@ -1,12 +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 - * ===================================================================================== - */ - -/** - * @module Directives - */ \ No newline at end of file diff --git a/src/directives/MobileDropdown.js b/src/directives/MobileDropdown.js new file mode 100644 index 0000000..dd2023c --- /dev/null +++ b/src/directives/MobileDropdown.js @@ -0,0 +1,124 @@ +/** + * 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 )); \ No newline at end of file diff --git a/src/directives/Redirect.js b/src/directives/Redirect.js new file mode 100644 index 0000000..3cf521d --- /dev/null +++ b/src/directives/Redirect.js @@ -0,0 +1,32 @@ +(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 )); \ No newline at end of file diff --git a/src/directives/Tab.js b/src/directives/Tab.js new file mode 100644 index 0000000..2114f91 --- /dev/null +++ b/src/directives/Tab.js @@ -0,0 +1,209 @@ +(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 ); \ No newline at end of file diff --git a/src/directives/UI.js b/src/directives/UI.js new file mode 100644 index 0000000..0e7b042 --- /dev/null +++ b/src/directives/UI.js @@ -0,0 +1,376 @@ +/** + * 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, + toggleHideShow : toggleHideShow, + toggleSocialShare : toggleSocialShare, + toggleClass : toggleClass + }; + + 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(); + } ); + } ); + } + } + + /** + * 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(); + var $elem = $( target ); + $elem.toggleClass( 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 )); \ No newline at end of file diff --git a/src/directives/Validator.js b/src/directives/Validator.js new file mode 100644 index 0000000..def0cfe --- /dev/null +++ b/src/directives/Validator.js @@ -0,0 +1,16 @@ +(function( $, pm ) +{ + pm.directive( 'Validator', function( ValidationService ) + { + + return { + validate: validate + }; + + function validate( form, errorClass ) + { + return ValidationService.validate( form, errorClass ); + } + + }, ['ValidationService'] ); +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/directives/addToBasket.js b/src/directives/addToBasket.js deleted file mode 100644 index 5b980d3..0000000 --- a/src/directives/addToBasket.js +++ /dev/null @@ -1,54 +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($, pm) { - - pm.directive('[data-plenty="addBasketItemButton"]', function(i, button, BasketService) - { - - $(button).click( function(e) - { - // avoid directing to href - e.preventDefault(); - - //init - var basketItemsList = {}; - var parentForm = $(button).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]); - - }); - }, ['BasketService']); -} (jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/checkoutHref.js b/src/directives/checkoutHref.js deleted file mode 100644 index 7438d30..0000000 --- a/src/directives/checkoutHref.js +++ /dev/null @@ -1,16 +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($, pm) { - pm.directive('[data-plenty-checkout-href]', function(i, elem, NavigatorService) { - $(elem).click(function () { - NavigatorService.goToID( $(this).attr('data-plenty-checkout-href') ); - }); - }, ['NavigatorService']); -} (jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/contentPageSlider.js b/src/directives/contentPageSlider.js deleted file mode 100644 index ea25693..0000000 --- a/src/directives/contentPageSlider.js +++ /dev/null @@ -1,40 +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($, pm) { - - - /* - * content page slider - * - * usage (functionality requires only attribute data-plenty="contentpageSlider"): - *
    - *
    - * ... - *
    - *
    - * ... - *
    - * ... - *
    - */ - pm.directive('[data-plenty="contentpageSlider"]', function(i, 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-lazyload]').trigger('appear'); } - }); - }); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/equalHeight.js b/src/directives/equalHeight.js deleted file mode 100644 index 384d3ab..0000000 --- a/src/directives/equalHeight.js +++ /dev/null @@ -1,39 +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($, pm) { - - /* - * Equal Box heights - */ - pm.directive('[data-plenty-equal]', function(i, elem, MediaSizeService) { - var mediaSizes = $(elem).data('plenty-equal').replace(/\s/g, '').split(','); - - var targets = ( $(elem).find('[data-plenty-equal-target]').length > 0 ) ? $(elem).find('[data-plenty-equal-target]') : $(elem).children(); - - var maxHeight = 0; - $(targets).each(function(j, child) { - - $(child).css('height', ''); - - if( $(child).outerHeight(true) > maxHeight ) { - maxHeight = $(child).outerHeight(true); - } - }); - - if( !mediaSizes || $.inArray( MediaSizeService.interval(), mediaSizes ) >= 0 ) targets.height(maxHeight); - - }, ['MediaSizeService'], true); - - // refresh calculation on window resize - $(window).on('sizeChange', function() { - pm.getInstance().bindDirectives( '[data-plenty-equal]' ); - }); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/lazyLoad.js b/src/directives/lazyLoad.js deleted file mode 100644 index 66c2b34..0000000 --- a/src/directives/lazyLoad.js +++ /dev/null @@ -1,23 +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($, pm) { - - // lazyload images (requires lazyload.min.js) - // TODO: handle external dependencies dependencies - pm.directive('img[data-plenty-lazyload]', function(i, elem) { - $(elem).lazyload({ - effect: $(this).attr('data-plenty-lazyload') - }); - $(elem).on("loaded", function() { - $(elem).css('display', 'inline-block'); - }); - }); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/loginElement.js b/src/directives/loginElement.js deleted file mode 100644 index 5926442..0000000 --- a/src/directives/loginElement.js +++ /dev/null @@ -1,19 +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($, pm) { - - pm.directive('[data-plenty-checkout-form="customerLogin"]', function(i, elem, AuthenticationService) { - $(elem).on('submit', function (e) { - e.preventDefault(); - AuthenticationService.customerLogin( $(e.target) ); - }); - }, ["AuthenticationService"]); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/mobileDropdowns.js b/src/directives/mobileDropdowns.js deleted file mode 100644 index 78ac33f..0000000 --- a/src/directives/mobileDropdowns.js +++ /dev/null @@ -1,97 +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($, pm) { - - /* - * 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' - */ - // TODO: handle external dependency to Modernizr - pm.directive('.dropdown > a[data-plenty-enable]', function(i, elem, MediaSizeService) { - - if( $(elem).attr('data-plenty-enable') == "toggle-xs-sm-or-touch" ) { - $(elem).click(function(e) { - if ( MediaSizeService.interval() == 'xs' || MediaSizeService.interval() == 'sm' || ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) ) { - $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').not( $(this) ).parent().removeClass('open'); - $(this).parent().toggleClass('open'); - return false; - } - }); - } - - // dropdown enabled touch - else if( $(elem).attr('data-plenty-enable') == "touch" ) { - $(elem).click(function() { - if ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) { // otherwise already has mobile navigation - $('.dropdown.open > a[data-plenty-enable="touch"]').not( $(this) ).parent().removeClass('open'); - if ( ! $(this).parent().hasClass('open') ) { - $(this).parent().addClass('open'); - return false; - } - } - }); - } - }, ['MediaSizeService']); - - - pm.directive('*', function(i, elem, MediaSizeService) { - - $(elem).click(function (e) { - if (MediaSizeService.interval() == 'xs' || MediaSizeService.interval() == 'sm' || ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch )) { - var dropdown = $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent(); - if (dropdown.length > 0 && !dropdown.is(e.target) && dropdown.has(e.target).length <= 0) { - dropdown.removeClass('open'); - } - } - - if (MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch) { - var dropdown = $('.dropdown.open > a[data-plenty-enable="touch"]').parent(); - if (dropdown.length > 0 && !dropdown.is(e.target) && dropdown.has(e.target).length <= 0) { - dropdown.removeClass('open'); - } - } - }); - }, ['MediaSizeService']); - - - pm.directive(window, function(i, elem, MediaSizeService) { - $(window).on('orientationchange', function() { - if ( MediaSizeService.interval() == 'xs' || MediaSizeService.interval() == 'sm' || ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) ) { - $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent().removeClass('open'); - } - - if ( MediaSizeService.interval() != 'xs' && MediaSizeService.interval() != 'sm' && Modernizr.touch ) { - $('.dropdown.open > a[data-plenty-enable="touch"]').parent().removeClass('open'); - } - }); - $(window).on('sizeChange', function(newValue) { - if ( newValue != 'xs' && newValue != 'sm' && ! Modernizr.touch ) { - $('.dropdown.open > a[data-plenty-enable="toggle-xs-sm-or-touch"]').parent().removeClass('open'); - } - }); - }, ['MediaSizeService']); - - $(document).ready(function() { - - if ( pm.getInstance().MediaSizeService.interval() != 'xs' && pm.getInstance().MediaSizeService.interval() != 'sm' && Modernizr.touch ) { - $('.dropdown.open > a[data-plenty-enable="touch"]').parent().removeClass('open'); - } - - }); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/onEnter.js b/src/directives/onEnter.js deleted file mode 100644 index fd2df1f..0000000 --- a/src/directives/onEnter.js +++ /dev/null @@ -1,24 +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($, pm) { - - // Call function if enter key pressed while element is focused - pm.directive('[data-plenty-onenter]', function(i, elem) { - var onEnter = $(elem).attr('data-plenty-onenter'); - var callback = typeof window[onEnter] === 'function' ? window[onEnter] : (new Function('return ' + onEnter)); - $(elem).on('keypress', function(e) { - - if(e.which === 13 && !!callback && typeof callback === "function") { - callback.call(); - } - }); - }); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/openCloseToggle.js b/src/directives/openCloseToggle.js deleted file mode 100644 index a799815..0000000 --- a/src/directives/openCloseToggle.js +++ /dev/null @@ -1,30 +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($, pm) { - - // Tree navigation toggle - pm.directive('[data-plenty="openCloseToggle"]', function(i, elem) { - $(elem).click(function () { - $(elem).parent().addClass('animating'); - $(elem).siblings('ul').slideToggle(200, function () { - if ($(elem).parent().is('.open')) { - $(elem).parent().removeClass('open'); - } - else { - $(elem).parent().addClass('open'); - } - $(elem).removeAttr('style'); - $(elem).parent().removeClass('animating'); - }); - }); - - }); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/quantityInputButtons.js b/src/directives/quantityInputButtons.js deleted file mode 100644 index 2b2589d..0000000 --- a/src/directives/quantityInputButtons.js +++ /dev/null @@ -1,70 +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($, pm) { - - // TODO: merge to single directive. Differentiate between increasing and decreasing by additional parameter - pm.directive('[data-plenty="quantityInputButtonPlus"]', function(i, elem) { - // quantity input plus/minus buttons - $(elem).click(function() { - var input = $($(elem).closest('[data-plenty="quantityInputWrapper"]').find('input')); - var value = parseInt( input.val() ); - var maxLength = parseInt(input.attr('maxlength')) || 1000; - if ( ( (value + 1) + '').length <= maxLength ) { - input.val(value + 1); - } - }); - }); - - pm.directive('[data-plenty="quantityInputButtonMinus"]', function(i, elem) { - $(elem).click(function() { - var input = $($(elem).closest('[data-plenty="quantityInputWrapper"]').find('input')); - var value = parseInt( input.val() ); - if ( value > 1 ) { - input.val(value - 1); - } - }); - }); - - // Quantity Buttons in BasketView - pm.directive('[data-basket-item-id] [data-plenty="quantityInputButtonPlus"], [data-basket-item-id] [data-plenty="quantityInputButtonMinus"]', function(i, button) { - $(button).click(function() { - - var self = $(this); - if( !!self.data('timeout') ) { - window.clearTimeout( self.data('timeout') ); - } - - var timeout = window.setTimeout(function() { - self.parents('[data-plenty="quantityInputWrapper"]').find('[data-plenty="quantityInput"]').trigger('change'); - }, 1000); - - self.data('timeout', timeout); - - }); - }); - - pm.directive('[data-basket-item-id] [data-plenty="quantityInput"]', function(i, input, BasketService) { - $(input).change( function() { - - var self = $(this); - var newQuantity = parseInt( self.val() ); - var basketItemID = self.parents('[data-basket-item-id]').attr('data-basket-item-id'); - - BasketService.setItemQuantity( - basketItemID, - newQuantity - ); - }); - }, ['BasketService']); - - - - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/redirectHref.js b/src/directives/redirectHref.js deleted file mode 100644 index 25bc4d4..0000000 --- a/src/directives/redirectHref.js +++ /dev/null @@ -1,26 +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($, pm) { - - // link non-anchor elements - pm.directive('a[data-plenty-href]', function(i, elem, MediaSizeService) { - $(elem).each(function() { - var href = $(this).attr('href'); - var identifier = $(this).attr('data-plenty-href'); - - $('[data-plenty-link="'+identifier+'"]').click(function() { - if( MediaSizeService.interval() != 'xs' ) { - window.location.assign( href ); - } - }); - }); - }, ['MediaSizeService']); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/slideToggle.js b/src/directives/slideToggle.js deleted file mode 100644 index ef4227d..0000000 --- a/src/directives/slideToggle.js +++ /dev/null @@ -1,65 +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($, pm) { - - // Toggle target content on click. - // Can be bound on checked-/ unchecked-property of radio buttons - pm.directive('[data-plenty-slidetoggle]', function(i, trigger) { - - var target = $( $(trigger).attr('data-plenty-target') ); - - if( $(trigger).is('input[type="radio"]') ) { - // is radio button - var radioList = $('input[type="radio"][name="'+( $(trigger).attr('name') )+'"]'); - var visibleOnChecked = $(trigger).is('[data-plenty-slidetoggle="checked"]'); - $(radioList).change(function() { - $(target).parents('[data-plenty-equal-target]').css('height', 'auto'); - - if ( $(this).is(':checked') && $(this)[0] === $(trigger)[0] ) { - // checked - if ( visibleOnChecked == true ) { - $(target).slideDown(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } else { - $(target).slideUp(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } - } - else { - // unchecked (since other radio button has been checked) - if ( visibleOnChecked == true ) { - $(target).slideUp(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } else { - $(target).slideDown(400, function() { - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - } - } - }); - } else { - // is not radio button - $(trigger).click(function() { - $(target).parents('[data-plenty-equal-target]').css('height', 'auto'); - - $(trigger).addClass('animating'); - $(target).slideToggle(400, function() { - $(trigger).removeClass('animating'); - $(trigger).toggleClass('active'); - pm.getInstance().bindDirectives('[data-plenty-equal]'); - }); - }); - } - }); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/socialShare.js b/src/directives/socialShare.js deleted file mode 100644 index 20ffcaa..0000000 --- a/src/directives/socialShare.js +++ /dev/null @@ -1,68 +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($, pm) { - - /* - * 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 - */ - pm.directive('[data-plenty-social]', function(i, elem, SocialShareService) { - - var toggle = $(elem).find('[data-plenty="switch"]'); - - // append container to put / delete service.html - $(elem).append(''); - - // add "off" class to switch, if neither "off" or "on" is set - if ( !toggle.hasClass('off') && !toggle.hasClass('on') ) { - toggle.addClass('off'); - } - - // toggle switch - toggle.on('click', function() { - if ( toggle.hasClass('off') ) { - if ( $(elem).attr("data-toggle") == "tooltip" ) { $(elem).tooltip('destroy') }; - toggle.removeClass('off').addClass('on'); - // hide dummy button - $(elem).find('[data-plenty="placeholder"]').hide(); - // load HTML defined in 'api' - $(elem).find('.social-container').append( SocialShareService.getSocialService( $(elem).attr('data-plenty-social') ) ); - } - // do not disable social medias after activation - /* - else - { - toggle.removeClass('on').addClass('off'); - // show dummy button - $(elem).find('[data-plenty="placeholder"]').show(); - // remove api HTML - $(elem).find('.social-container').html(''); - } - */ - }); - }, ['SocialShareService']); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/tabs.js b/src/directives/tabs.js deleted file mode 100644 index b78935e..0000000 --- a/src/directives/tabs.js +++ /dev/null @@ -1,113 +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($, pm) { - - /* Tab Handling - * - * Show tab with jQuery-selector 'TAB_SELECTOR' - - * (!) Requires bootstrap.js - * - * Show remote tab with jQuery-selector 'TAB_1' in target container (below) - - * - */ - pm.directive('a[data-plenty-opentab]', function(i, elem) { - // open tab - $(elem).click(function() { - var tabSelector = $(this).attr('data-plenty-opentab'); - tabSelector = ( tabSelector == 'href' ) ? $(this).attr('href') : tabSelector; - $(tabSelector).tab('show'); - }); - }); - - pm.directive('[data-plenty-openremotetab]', function(i, elem) { - // open remote tab{ - $(elem).click(function () { - var tabSelector = $(this).attr('data-plenty-openremotetab'); - $(tabSelector).trigger('tabchange'); - }); - }); - - /* - * Remote tabs - * tab content can be placed anywhere in body - * - * Content of remote tab -
    - -
    - * - * Remote tab navigation - * [...] -
    - * - */ - pm.directive('[data-plenty="remoteTabs"]', function(i, remoteTab) { - - var tabsId = $(remoteTab).attr('data-plenty-remotetabs-id'); - - // find tabs grouped by remotetabs-id - $('[data-plenty="remoteTabs"][data-plenty-remotetabs-id="'+tabsId+'"]').each(function(i, tabs) { - - // bind each remote-tab - $(tabs).find('a').each(function(i, singleTab) { - - var singleTabId = $(singleTab).attr('data-plenty-tab-id'); - - // listen to 'tabchange' event - $(singleTab).on('tabchange', function() { - // toggle class 'active' - $(singleTab).closest('[data-plenty="remoteTabs"]').children('.active').removeClass('active'); - $(singleTab).closest('li').addClass('active'); - - // hide inactive tabs & show active tab - var tabpanelsInactive = $('[data-plenty-remotetabs-id="'+tabsId+'"][data-plenty-tabpanel-labelledby]').not('[data-plenty-tabpanel-labelledby="'+singleTabId+'"]'); - var tabpanelActive = $('[data-plenty-remotetabs-id="'+tabsId+'"][data-plenty-tabpanel-labelledby="'+singleTabId+'"]'); - var zIndexTabpanelParents = 0; - if ( $(tabs).attr('data-plenty-remotetabs-adapt') == 'tabpanel-parent' ) { - zIndexTabpanelParents = 2147483646; - $('[data-plenty-remotetabs-id="'+tabsId+'"][data-plenty-tabpanel-labelledby]').parent().each(function() { - var zIndexCurrent = parseInt( $(this).css('zIndex') ); - if ( typeof zIndexCurrent == 'number' && zIndexCurrent < zIndexTabpanelParents ) zIndexTabpanelParents = zIndexCurrent; - }); - } - - // adjust z-index if neccessary - $(tabpanelsInactive).hide().removeClass('in'); - $(tabpanelActive).show().addClass('in'); - if ( zIndexTabpanelParents != 0 ) { - $(tabpanelsInactive).parent().css('zIndex', zIndexTabpanelParents); - $(tabpanelActive).parent().css('zIndex', zIndexTabpanelParents + 1); - } - }); - }); - }); - - // trigger 'tabchange' event - $(remoteTab).find('a').click(function() { - $(this).trigger('tabchange'); - }); - }); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/toTop.js b/src/directives/toTop.js deleted file mode 100644 index 7c7921a..0000000 --- a/src/directives/toTop.js +++ /dev/null @@ -1,34 +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($, pm) { - - pm.directive('[data-plenty="toTop"]', function(i, elem) { - $(elem).click(function() { - $('html, body').animate({ - scrollTop: 0 - }, 400); - return false; - }); - - var positionToTopButton = function() { - if( $(document).scrollTop() > 100 ) { - $(elem).addClass('visible'); - } else { - $(elem).removeClass('visible'); - } - }; - - $(window).on("scroll resize", function() { - positionToTopButton(); - }); - - }); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/toggleClass.js b/src/directives/toggleClass.js deleted file mode 100644 index d82ab4f..0000000 --- a/src/directives/toggleClass.js +++ /dev/null @@ -1,51 +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($, pm) { - - /* - * 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="" - */ - pm.directive('[data-plenty-toggle]', function(i, elem, MediaSizeService) { - if( $(elem).attr('data-plenty-toggle').search(';') < 0 ) { - eval('var data = ' + $(elem).attr('data-plenty-toggle')); - if ( data.target && data.class ) { - $(elem).click(function() { - var isMedia = false; - if ( data.media ) { - if ( data.media.indexOf(' ') != -1 ) { - var mediaArr = data.media.split(' '); - for ( i = 0; i < mediaArr.length; i++ ) { - if ( MediaSizeService.interval() == mediaArr[i] ) { - isMedia = true; - } - } - } - else { - if ( MediaSizeService.interval() == data.media ) isMedia = true; - } - } - if ( ! data.media || isMedia == true ) { - $(data.target).toggleClass(data.class); - if ( $(elem).is('a') ) return false; - } - }); - } - } - }, ['MediaSizeService']); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/directives/validateForm.js b/src/directives/validateForm.js deleted file mode 100644 index 0f2aff8..0000000 --- a/src/directives/validateForm.js +++ /dev/null @@ -1,53 +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($, pm) { - - /* - * Form Validation - * Validate required form inputs as given type of value - * Usage / data-Attributes: - *
    This will activate the script for this form - * and add ERROR_CLASS to invalid elements - * - * Check if the value of this input is text (or number) and add ERROR_CLASS on failure - *
    You can put the data-plenty-validate="" on a parent element of an input. - * This will check the value of the input(s) inside and add ERROR_CLASS to the
    on failure - - * possible values for data-plenty-validate="" text : validate if value is text (or number or mixed) - * mail : checks if value is a valid mail-address (not depending on inputs type-attribute) - * number: checks if value is a numeric value. For detailed information see: isNumberic() - * {min: 1, max: 3} validate that at least 'min' and maximum 'max' options are selected (checkboxes) - * - * possible form elements to validate - * can validate "text", "mail", "number" - * check if one radio-button in group "myRadio" is checked. - * Ignores the value of data-plenty-validate - * check if one checkbox in group "myCheck" is checked or use - * data-plenty-valudate="{min: 3, max: 5}" to define custom range - * check if an option is selected and otions value is not "-1" (plenty default for: "choose"-option) - * - * Events: - * 'validationFailed' will be triggered if at least one element is not valid. - * Usage: - * form.on('validationFailed', function(event, invalidFields) { - * $(invalidFields).each({ - * // manipulate invalid fields - * }); - * }); - */ - pm.directive('form[data-plenty-checkform], form.PlentySubmitForm', function(i, elem, ValidationService) { - - $(elem).submit(function() { - return ValidationService.validate( elem ); - }); - - }, ['ValidationService']); - -}(jQuery, PlentyFramework)); \ No newline at end of file diff --git a/src/factories/APIFactory.js b/src/factories/APIFactory.js index c79579a..56946ad 100644 --- a/src/factories/APIFactory.js +++ b/src/factories/APIFactory.js @@ -10,11 +10,12 @@ /** * @module Factories */ -(function($, pm) { +(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
    + * 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}}
    • @@ -22,15 +23,16 @@ * @class APIFactory * @static */ - pm.factory('APIFactory', function(UI) { + pm.factory( 'APIFactory', function( UI ) + { - return { - get: _get, - post: _post, - put: _put, + return { + get : _get, + post : _post, + put : _put, delete: _delete, - idle: _idle - }; + idle : _idle + }; /** * Is called by default if a request failed.
      @@ -39,45 +41,65 @@ * @function handleError * @private * - * @param {object} jqXHR
      jQuery deferred Object + * @param {object} jqXHR jQuery + * deferred Object */ - function handleError( jqXHR ) { - try { - var responseText = $.parseJSON(jqXHR.responseText); - UI.printErrors(responseText.error.error_stack); - } catch(e) { + 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 {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 + * @return {object} jQuery + * deferred Object */ - function _get( url, params, ignoreErrors, runInBackground, sync ) { + function _get( url, params, ignoreErrors, runInBackground, sync ) + { - if( !runInBackground ) UI.showWaitScreen(); + if ( !runInBackground ) + { + UI.showWaitScreen(); + } return $.ajax( url, { - type: 'GET', - data: params, - dataType: 'json', - async: !sync, - error: function( jqXHR ) { if( !ignoreErrors ) handleError( jqXHR ) } + type : 'GET', + data: params, + dataType: 'json', + async : !sync, + error : function( jqXHR ) + { + if ( !ignoreErrors ) + { + handleError( jqXHR ) + } + } + } + ).always( function() + { + if ( !runInBackground ) + { + UI.hideWaitScreen(); } - ).always( function() { - if( !runInBackground ) UI.hideWaitScreen(); - }); + } ); } @@ -87,36 +109,55 @@ * @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 {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 + * @return {object} jQuery + * deferred Object */ - function _post( url, data, ignoreErrors, runInBackground ) { + function _post( url, data, ignoreErrors, runInBackground ) + { var params = { - type: 'POST', - dataType: 'json', - error: function( jqXHR ) { if( !ignoreErrors ) handleError( jqXHR ) } + 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 ( !!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(); + if ( !runInBackground ) + { + UI.showWaitScreen(); + } return $.ajax( url, params - ).always( function() { - if( !runInBackground ) UI.hideWaitScreen(); - }); + ).always( function() + { + if ( !runInBackground ) + { + UI.hideWaitScreen(); + } + } ); } /** @@ -125,27 +166,43 @@ * @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 {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 + * @return {object} jQuery + * deferred Object */ - function _put( url, data, ignoreErrors, runInBackground ) { + function _put( url, data, ignoreErrors, runInBackground ) + { - if( !runInBackground ) UI.showWaitScreen(); + 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 ) } + type : 'PUT', + data: JSON.stringify( data ), + dataType: 'json', + contentType: 'application/json', + error : function( jqXHR ) + { + if ( !ignoreErrors ) + { + handleError( jqXHR ) + } + } + } + ).always( function() + { + if ( !runInBackground ) + { + UI.hideWaitScreen(); } - ).always( function() { - if( !runInBackground ) UI.hideWaitScreen(); - }); + } ); } @@ -155,37 +212,55 @@ * @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 {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 + * @returns {object} jQuery + * deferred Object */ - function _delete( url, data, ignoreErrors, runInBackground ) { + function _delete( url, data, ignoreErrors, runInBackground ) + { - if( !runInBackground ) UI.showWaitScreen(); + 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 ) } + type : 'DELETE', + data: JSON.stringify( data ), + dataType: 'json', + contentType: 'application/json', + error : function( jqXHR ) + { + if ( !ignoreErrors ) + { + handleError( jqXHR ) + } + } + } + ).always( function() + { + if ( !runInBackground ) + { + UI.hideWaitScreen(); } - ).always( function() { - if( !runInBackground ) UI.hideWaitScreen(); - }); + } ); } /** * Get a idle request doing nothing for chaining methods - * @returns {object} jQuery deferred Object + * @returns {object} jQuery + * deferred Object */ - function _idle() { + function _idle() + { return $.Deferred().resolve(); } - }, ['UIFactory']); -}(jQuery, PlentyFramework)); \ No newline at end of file + }, ['UIFactory'] ); +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/factories/CMSFactory.js b/src/factories/CMSFactory.js index 32c5d98..66703f4 100644 --- a/src/factories/CMSFactory.js +++ b/src/factories/CMSFactory.js @@ -10,7 +10,8 @@ /** * @module Factories */ -(function(pm) { +(function( pm ) +{ /** * Provide methods for receiving layout containers, layout parameters @@ -22,20 +23,22 @@ * @class CMSFactory * @static */ - pm.factory('CMSFactory', function(API) { + pm.factory( 'CMSFactory', function( API ) + { - return { - getContainer: getContainer, - getParams: getParams, + 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 + * @returns {object} The prepared request. Call .from( layoutGroup ) to specify the location in + * the CMS * (e.g. 'Checkout') * @example * CMSFactory.getContainer( 'CheckoutTotals' ).from( 'Checkout' ) @@ -44,9 +47,11 @@ * var html = response.data[0] * }); */ - function getContainer( containerName, params ) { + function getContainer( containerName, params ) + { - function from( layoutGroup ) { + function from( layoutGroup ) + { return API.get( '/rest/' + layoutGroup.toLowerCase() + '/container_' + containerName.toLowerCase() + '/', params ); } @@ -61,7 +66,8 @@ * @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 + * @returns {object} The prepared request. Call .from( layoutGroup ) to specify the + * location in the CMS * (e.g. 'ItemView') * @example * CMSFactory.getParams( 'BasketItemsList' ).from( 'ItemView' ) @@ -70,10 +76,12 @@ * var items = response.data; * }); */ - function getParams( containerName, params ) { + function getParams( containerName, params ) + { - function from( layoutGroup ) { - return API.get( '/rest/' + layoutGroup.toLowerCase() + '/' + containerName.toLowerCase() + '/', params ); + function from( layoutGroup ) + { + return API.get( '/rest/' + layoutGroup.toLowerCase() + '/' + containerName.toLowerCase() + '/', params ); } return { @@ -85,12 +93,14 @@ * 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 + * @returns {object} jQuery deferred + * Object */ - function getCategoryContent( categoryID ) { + function getCategoryContent( categoryID ) + { return API.get( '/rest/categoryview/categorycontentbody/?categoryID=' + categoryID ); } - }, ['APIFactory']); -}(PlentyFramework)); \ No newline at end of file + }, ['APIFactory'] ); +}( PlentyFramework )); \ No newline at end of file diff --git a/src/factories/CheckoutFactory.js b/src/factories/CheckoutFactory.js index ca73a86..b6d59bf 100644 --- a/src/factories/CheckoutFactory.js +++ b/src/factories/CheckoutFactory.js @@ -10,7 +10,8 @@ /** * @module Factories */ -(function(pm) { +(function( pm ) +{ /** * Holds checkout data for global access and provides methods @@ -24,7 +25,8 @@ * @class CheckoutFactory * @static */ - pm.factory('CheckoutFactory', function(API, CMS, UI) { + pm.factory( 'CheckoutFactory', function( API, CMS, UI ) + { // data received from ReST API var checkoutData; @@ -32,17 +34,17 @@ // instance wrapped checkout object for global access var checkout; - return { - getCheckout: getCheckout, - setCheckout: setCheckout, - loadCheckout: loadCheckout, - reloadContainer: reloadContainer, - reloadCatContent: reloadCatContent, + return { + getCheckout : getCheckout, + setCheckout : setCheckout, + loadCheckout : loadCheckout, + reloadContainer : reloadContainer, + reloadCatContent : reloadCatContent, reloadItemContainer: reloadItemContainer - }; + }; - - function Checkout() { + function Checkout() + { return checkoutData; } @@ -51,49 +53,66 @@ * @function getCheckout * @returns {Checkout} Instance of checkout object */ - function getCheckout( copy ) { - if(!checkout || !checkoutData) { - loadCheckout(true); + function getCheckout( copy ) + { + if ( !checkout || !checkoutData ) + { + loadCheckout( true ); } - if( !!copy ) { - return $.extend(true, {}, checkoutData); + if ( !!copy ) + { + return $.extend( true, {}, checkoutData ); } return checkout; } + /** * Receive global checkout data from ReST-API * @function loadCheckout - * @return {object} jQuery deferred Object + * @return {object} jQuery deferred + * Object */ - function loadCheckout(sync) { - - return API.get('/rest/checkout/', null, false, true, sync) - .done(function(response) { - if( !!response ) { + function loadCheckout( sync ) + { + + return API.get( '/rest/checkout/', null, false, false, sync ) + .done( function( response ) + { + if ( !!response ) + { checkoutData = response.data; - checkout = new Checkout(); + checkout = new Checkout(); } - else UI.throwError(0, 'Could not receive checkout data [GET "/rest/checkout/" receives null value]'); - }); + 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 + * @return {object} jQuery deferred + * Object */ - function setCheckout() { - - - return API.put('/rest/checkout', checkout) - .done(function(response) { - if( !!response ) { + function setCheckout() + { + + return API.put( '/rest/checkout', checkout ) + .done( function( response ) + { + if ( !!response ) + { checkoutData = response.data; - checkout = new Checkout(); + checkout = new Checkout(); + } + else + { + UI.throwError( 0, 'Could not receive checkout data [GET "/rest/checkout/" receives null value]' ); } - else UI.throwError(0, 'Could not receive checkout data [GET "/rest/checkout/" receives null value]'); - }); + } ); } @@ -102,38 +121,49 @@ * 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 + * @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(); - }); - }); + 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 + * @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(); - }); - }); + 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' ); + + } ); + } ); } @@ -141,21 +171,27 @@ * 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 + * @param {string} container Name of the (item view) template to load from server + * @return {object} jQuery deferred + * Object */ - function reloadItemContainer( container ) { + 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(); - }); - }); + .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)); \ No newline at end of file + + }, ['APIFactory', 'CMSFactory', 'UIFactory'] ); +}( PlentyFramework )); \ No newline at end of file diff --git a/src/factories/ModalFactory.js b/src/factories/ModalFactory.js index 8b8d394..a454187 100644 --- a/src/factories/ModalFactory.js +++ b/src/factories/ModalFactory.js @@ -10,19 +10,21 @@ /** * @module Factories */ -(function($, pm) { +(function( $, pm ) +{ /** * Provides methods for creating and displaying modal popups. * @class ModalFactory * @static */ - pm.factory('ModalFactory', function() { + pm.factory( 'ModalFactory', function() + { - return { + return { prepare: prepare, isModal: isModal - }; + }; /** * Detect if given html contains a valid modal @@ -30,7 +32,8 @@ * @param {string} html * @returns {boolean} */ - function isModal( html ) { + function isModal( html ) + { return PlentyFramework.partials.Modal.isModal( html ); } @@ -39,7 +42,8 @@ * @function prepare * @returns {Modal} */ - function prepare() { + function prepare() + { return new Modal(); } @@ -50,7 +54,8 @@ * @returns {Modal} * @constructor */ - function Modal() { + function Modal() + { var modal = this; /** @@ -60,7 +65,9 @@ * @private * @default "" */ - modal.title = ''; + modal.title = ''; + + modal.cssClass = ''; /** * The content of the modal @@ -69,7 +76,7 @@ * @private * @default "" */ - modal.content = ''; + modal.content = ''; /** * The content of the dismiss-button @@ -78,7 +85,7 @@ * @private * @default "Abbrechen" */ - modal.labelDismiss = pm.translate("Cancel"); + modal.labelDismiss = pm.translate( "Cancel" ); /** * the label of the confirmation button @@ -87,7 +94,7 @@ * @private * @default "Bestätigen" */ - modal.labelConfirm = pm.translate("Confirm"); + modal.labelConfirm = pm.translate( "Confirm" ); /** * Callback when modal is confirmed by clicking confirmation button. @@ -97,7 +104,9 @@ * @private * @default function() {} */ - modal.onConfirm = function() {}; + modal.onConfirm = function() + { + }; /** * Callback when modal is dismissed by closing the modal @@ -106,7 +115,9 @@ * @private * @default function() {} */ - modal.onDismiss = function() {}; + modal.onDismiss = function() + { + }; /** * jQuery selector of the container element to display the modal in. @@ -115,7 +126,7 @@ * @private * @default "body" */ - modal.container = 'body'; + modal.container = 'body'; /** * Timeout to close the modal automatically. Set <0 to disable. @@ -126,10 +137,10 @@ */ modal.timeout = -1; - modal.hide = hide; - modal.startTimeout = startTimeout; - modal.stopTimeout = stopTimeout; - modal.pauseTimeout = pauseTimeout; + modal.hide = hide; + modal.startTimeout = startTimeout; + modal.stopTimeout = stopTimeout; + modal.pauseTimeout = pauseTimeout; modal.continueTimeout = continueTimeout; var bsModal; @@ -138,16 +149,17 @@ var paused = false; return { - setTitle: setTitle, - setContent: setContent, - setContainer: setContainer, + setTitle : setTitle, + setClass : setClass, + setContent : setContent, + setContainer : setContainer, setLabelConfirm: setLabelConfirm, setLabelDismiss: setLabelDismiss, - onConfirm: onConfirm, - onDismiss: onDismiss, - setTimeout: setTimeout, - show: show, - hide: hide + onConfirm : onConfirm, + onDismiss : onDismiss, + setTimeout : setTimeout, + show : show, + hide : hide }; /** @@ -156,51 +168,65 @@ * @param {string} title The title * @returns {Modal} Modal object for chaining methods */ - function setTitle( title ) { + 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 ) { + function setContent( content ) + { modal.content = content; return this; } /** - * Set the {{#crossLink "ModalFactory.Modal/labelConfirm:attribute}}label of the confirmation button{{/crossLink}} of the modal + * 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 ) { + function setLabelConfirm( label ) + { modal.labelConfirm = label; return this; } /** - * Set the {{#crossLink "ModalFactory.Modal/labelDismiss:attribute}}label if the dismiss button{{/crossLink}} of the modal + * 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 ) { + function setLabelDismiss( label ) + { modal.labelDismiss = label; return this; } /** - * Set the {{#crossLink "ModalFactory.Modal/onConfirm:attribute}}confirmation callback{{/crossLink}} of the modal + * 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 ) { + function onConfirm( callback ) + { modal.onConfirm = callback; return this; } @@ -211,20 +237,20 @@ * @param {function} callback The callback if modal is dismissed * @returns {Modal} Modal object for chaining methods */ - function onDismiss( callback ) { + 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 ) { + function setContainer( container ) + { modal.container = container; return this; } @@ -235,7 +261,8 @@ * @param {number} timeout The timeout to close the modal automatically. Set <0 to disable * @returns {Modal} Modal object for chaining methods */ - function setTimeout( timeout ) { + function setTimeout( timeout ) + { modal.timeout = timeout; return this; } @@ -246,36 +273,54 @@ * Start timer to hide the modal automatically if timeout is set. * @function show */ - function show() { - if( isModal( modal.content ) ) { + function show() + { + var entryNumber = 0; + if ( isModal( modal.content ) ) + { bsModal = PlentyFramework.partials.Modal.getModal( modal.content ); - } else { - bsModal = $( PlentyFramework.compileTemplate('modal/modal.html', modal) ); + } + else + { + bsModal = $( PlentyFramework.compileTemplate( 'modal/modal.html', modal ) ); } - $(modal.container).append( bsModal ); + $( 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(); + 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() { + bsModal.find( '[data-plenty-modal="confirm"]' ).click( function() + { var close = modal.onConfirm(); - if( close ) hide(true); - }); + + if( typeof close == "undefined" ) + { + close = true; + } + + if ( close ) + { + hide( true ); + } + } ); PlentyFramework.partials.Modal.show( bsModal ); - if( modal.timeout > 0 ) { + if ( modal.timeout > 0 ) + { startTimeout(); } @@ -286,10 +331,12 @@ * @function hide * @param {boolean} confirmed Flag indicating of modal is closed by confirmation button or dismissed */ - function hide( confirmed ) { + function hide( confirmed ) + { PlentyFramework.partials.Modal.hide( bsModal ); - if( !confirmed ) { + if ( !confirmed ) + { modal.onDismiss(); } } @@ -299,23 +346,27 @@ * @function startTimeout * @private */ - function startTimeout() { + function startTimeout() + { timeRemaining = modal.timeout; - timeStart = (new Date()).getTime(); + timeStart = (new Date()).getTime(); - timeout = window.setTimeout(function () { - window.clearInterval(interval); + timeout = window.setTimeout( function() + { + window.clearInterval( interval ); hide(); - }, modal.timeout); + }, modal.timeout ); - bsModal.find('[data-plenty-modal="timer"]').text(timeRemaining / 1000); - interval = window.setInterval(function () { - if (!paused) { + 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); + secondsRemaining = Math.round( secondsRemaining / 1000 ); + bsModal.find( '[data-plenty-modal="timer"]' ).text( secondsRemaining ); } - }, 1000) + }, 1000 ) } /** @@ -323,10 +374,11 @@ * @function pauseTimeout * @private */ - function pauseTimeout() { + function pauseTimeout() + { paused = true; timeRemaining -= (new Date()).getTime() - timeStart; - window.clearTimeout(timeout); + window.clearTimeout( timeout ); } /** @@ -334,13 +386,15 @@ * @function continueTimeout * @private */ - function continueTimeout() { - paused = false; + function continueTimeout() + { + paused = false; timeStart = (new Date()).getTime(); - timeout = window.setTimeout(function () { + timeout = window.setTimeout( function() + { hide(); - window.clearInterval(interval); - }, timeRemaining); + window.clearInterval( interval ); + }, timeRemaining ); } /** @@ -348,15 +402,13 @@ * @function stopTimeout * @private */ - function stopTimeout() { + function stopTimeout() + { window.clearTimeout( timeout ); window.clearInterval( interval ); } } - - - - }); -}(jQuery, PlentyFramework)); \ No newline at end of file + } ); +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/factories/UIFactory.js b/src/factories/UIFactory.js index 7a88f20..7c98ee3 100644 --- a/src/factories/UIFactory.js +++ b/src/factories/UIFactory.js @@ -10,14 +10,16 @@ /** * @module Factories */ -(function($, pm) { +(function( $, pm ) +{ /** * Displaying error messages and handling wait screen * @class UIFactory * @static */ - pm.factory('UIFactory', function() { + pm.factory( 'UIFactory', function() + { /** * Increased/ decreased when showing/ hiding wait screen to avoid stacking * multiple instances of overlays. @@ -28,11 +30,11 @@ */ var waitScreenCount = 0; var waitScreen; - var errorPopup = null; + var errorPopup = null; return { - throwError: throwError, - printErrors: printErrors, + throwError : throwError, + printErrors : printErrors, showWaitScreen: showWaitScreen, hideWaitScreen: hideWaitScreen }; @@ -43,8 +45,9 @@ * @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}]); + function throwError( code, msg ) + { + printErrors( [{code: code, message: msg}] ); } /** @@ -54,38 +57,42 @@ * @function printErrors * @param {Array} errorMessages A list of errors to display */ - function printErrors(errorMessages) { + 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 ); + 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) { + $.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.addError( errorPopup, $( pm.compileTemplate( 'error/errorMessage.html', error ) ) ); + } ); pm.partials.Error.show( errorPopup ); - hideWaitScreen(true); + hideWaitScreen( true ); } - /** * Show wait screen if not visible and increase * {{#crossLink "UIFactory/waitScreenCount:attribute"}}waitScreenCount{{/crossLink}} * @function showWaitScreen */ - 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); + if ( !waitScreen || $( 'body' ).has( waitScreen ).length <= 0 ) + { + waitScreen = $( pm.compileTemplate( 'waitscreen/waitscreen.html' ) ); + $( 'body' ).append( waitScreen ); } pm.partials.WaitScreen.show( waitScreen ); @@ -101,19 +108,21 @@ * @function hideWaitScreen * @param {boolean} forceClose set true to hide wait screen independent from the value of waitScreenCount. */ - function hideWaitScreen( forceClose ) { + 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 ) { + if ( waitScreenCount <= 0 || !!forceClose ) + { waitScreenCount = 0; pm.partials.WaitScreen.hide( waitScreen ); } return waitScreenCount; } - }); -}(jQuery, PlentyFramework)); \ No newline at end of file + } ); +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/helpers/Object.equals.js b/src/helpers/Object.equals.js index 4c6ef57..6d803c8 100644 --- a/src/helpers/Object.equals.js +++ b/src/helpers/Object.equals.js @@ -1,18 +1,48 @@ -Object.equals = function( a, b ) { - if( a === b ) return true; - if( !(a instanceof Object) || !(b instanceof Object) ) return false; - if( a.constructor !== b.constructor ) return false; +Object.equals = function( a, b ) +{ + if ( a === b ) + { + return true; + } + if ( !(a instanceof Object) || !(b instanceof Object) ) + { + return false; + } + if ( a.constructor !== b.constructor ) + { + return false; + } - for( var key in a ) { - if( !a.hasOwnProperty(key) ) continue; - if( !b.hasOwnProperty(key) ) return false; - if( a[key] === b[key] ) continue; - if( typeof( a[key] ) !== "object" ) return false; - if( !Object.equals(a[key], b[key]) ) return false; + for ( var key in a ) + { + if ( !a.hasOwnProperty( key ) ) + { + continue; + } + if ( !b.hasOwnProperty( key ) ) + { + return false; + } + if ( a[key] === b[key] ) + { + continue; + } + if ( typeof( a[key] ) !== "object" ) + { + return false; + } + if ( !Object.equals( a[key], b[key] ) ) + { + return false; + } } - for( var key in b ) { - if( b.hasOwnProperty(key) && !a.hasOwnProperty(key) ) return false; + for ( var key in b ) + { + if ( b.hasOwnProperty( key ) && !a.hasOwnProperty( key ) ) + { + return false; + } } return true; diff --git a/src/partials/addressSuggestions/addressDoctor.html b/src/partials/addressSuggestions/addressDoctor.html new file mode 100644 index 0000000..464b2f0 --- /dev/null +++ b/src/partials/addressSuggestions/addressDoctor.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/src/partials/addressSuggestions/postFinder.html b/src/partials/addressSuggestions/postFinder.html new file mode 100644 index 0000000..24f3cc2 --- /dev/null +++ b/src/partials/addressSuggestions/postFinder.html @@ -0,0 +1,26 @@ +{{#addresses}} +
      +
      + +
      +
      +{{/addresses}} diff --git a/src/partials/error/error.js b/src/partials/error/error.js index 9003ebb..3c7c4bc 100644 --- a/src/partials/error/error.js +++ b/src/partials/error/error.js @@ -1,4 +1,5 @@ -(function($, pm) { +(function( $, pm ) +{ pm.partials.Error = { @@ -6,11 +7,13 @@ * 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() { + init: function( popup ) + { + $( popup ).find( '.close' ).click( function() + { popup.hide(); - popup.find('.plentyErrorBoxInner').html(''); - }); + popup.find( '.plentyErrorBoxInner' ).html( '' ); + } ); }, /** @@ -18,11 +21,13 @@ * @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'); + 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 ); + if ( $( popup ).find( '[data-plenty-error-code="' + errorCode + '"]' ).length <= 0 ) + { + $( popup ).find( '.plentyErrorBoxInner' ).append( error ); } }, @@ -30,10 +35,11 @@ * Will be called, after initialization and injection of all errors * @param {HTMLElement} popup The error popup element */ - show: function( popup ) { - $(popup).show(); + show: function( popup ) + { + $( popup ).show(); } } -})(jQuery, PlentyFramework); \ No newline at end of file +})( jQuery, PlentyFramework ); \ No newline at end of file diff --git a/src/partials/modal/modal.html b/src/partials/modal/modal.html index 90a35a2..3456426 100644 --- a/src/partials/modal/modal.html +++ b/src/partials/modal/modal.html @@ -1,4 +1,4 @@ - \ No newline at end of file +
    diff --git a/src/partials/modal/modal.js b/src/partials/modal/modal.js index 11bbbc2..f11ec4d 100644 --- a/src/partials/modal/modal.js +++ b/src/partials/modal/modal.js @@ -1,4 +1,5 @@ -(function($, pm) { +(function( $, pm ) +{ pm.partials.Modal = { @@ -7,21 +8,27 @@ * @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 () { + init: function( element, modal ) + { + element.on( 'hidden.bs.modal', function() + { modal.hide(); element.remove(); - }); + } ); - if ( modal.timeout > 0 ) { + if ( modal.timeout > 0 ) + { element.on( 'hide.bs.modal', modal.stopTimeout ); - element.find( '.modal-content' ).hover( function() { + element.find( '.modal-content' ).hover( function() + { modal.pauseTimeout(); - }, function () { - if ( element.is( '.in' ) ) { + }, function() + { + if ( element.is( '.in' ) ) + { modal.continueTimeout(); } - }); + } ); } }, @@ -29,7 +36,8 @@ * Will be called if a Modal requests to show. * @param {HTMLElement} element The injected modal element */ - show: function ( element ) { + show: function( element ) + { element.modal( 'show' ); }, @@ -37,7 +45,8 @@ * Will be called if a Modal requests to hide. * @param {HTMLElement} element The injected modal element */ - hide: function ( element ) { + hide: function( element ) + { element.modal( 'hide' ); }, @@ -46,7 +55,8 @@ * @param {HTMLElement} html the element to search a modal in. * @returns {boolean} true if a modal was found */ - isModal: function ( html ) { + isModal: function( html ) + { return $( html ).filter( '.modal' ).length + $( html ).find( '.modal' ).length > 0; }, @@ -55,14 +65,17 @@ * @param {HTMLElement} html the element to get a modal from. * @returns {HTMLElement} the filtered modal element */ - getModal: function ( html ) { + getModal: function( html ) + { var modal = $( html ); - if ( modal.length > 1 ) { + if ( modal.length > 1 ) + { modal = $( html ).filter( '.modal' ) || $( html ).find( '.modal' ); } return modal; } + }; -}(jQuery, PlentyFramework)); \ No newline at end of file +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/partials/partialsInitialization.js b/src/partials/partialsInitialization.js new file mode 100644 index 0000000..e8c4c50 --- /dev/null +++ b/src/partials/partialsInitialization.js @@ -0,0 +1,13 @@ +(function( $ ) +{ + + $( document ).on( 'initPartials', function( e, root ) + { + + $( root ).find( '[data-toggle="tooltip"]' ).tooltip( { + container: 'body' + } ); + + } ); + +})( jQuery ); \ No newline at end of file diff --git a/src/partials/waitscreen/waitscreen.js b/src/partials/waitscreen/waitscreen.js index 363228c..ff783ff 100644 --- a/src/partials/waitscreen/waitscreen.js +++ b/src/partials/waitscreen/waitscreen.js @@ -1,4 +1,5 @@ -(function($, pm) { +(function( $, pm ) +{ pm.partials.WaitScreen = { @@ -6,18 +7,20 @@ * Will be called if the wait screen should be shown * @param {HTMLElement} element The wait screen element */ - show: function( element ) { - element.addClass('in'); + 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'); + hide: function( element ) + { + element.removeClass( 'in' ); } }; -})(jQuery, PlentyFramework); \ No newline at end of file +})( jQuery, PlentyFramework ); \ No newline at end of file diff --git a/src/plentyFramework.js b/src/plentyFramework.js index e505f1b..8e71709 100644 --- a/src/plentyFramework.js +++ b/src/plentyFramework.js @@ -10,17 +10,34 @@ /** * @module PlentyFramework */ -(function($) { +(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() {}; + PlentyFramework = function() + { + }; - var instance = null; - PlentyFramework.getInstance = function() { + var instance = null; + PlentyFramework.getInstance = function() + { instance = instance || new PlentyFramework(); return instance; }; @@ -50,9 +67,11 @@ * @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.'); + PlentyFramework.setGlobal = function( identifier, value ) + { + if ( PlentyFramework.globals.hasOwnProperty( identifier ) ) + { + console.error( 'Global variable "' + identifier + '" already exists and cannot be overridden.' ); return null; } @@ -68,7 +87,8 @@ * @param identifier The identifier of the requested variable * @return {*} The value of the variable */ - PlentyFramework.getGlobal = function( identifier ) { + PlentyFramework.getGlobal = function( identifier ) + { return PlentyFramework.globals[identifier]; }; @@ -77,7 +97,7 @@ * @type {Array} * @static */ - PlentyFramework.directives = []; + PlentyFramework.directives = {}; /** * Register directive. Directives can be bound to dynamically added nodes by calling pm.bindPlentyFunctions(); @@ -89,18 +109,33 @@ * @param {boolean} allowDuplicates Defines if a directive can be bound to the same element multiple times * @return {object} The created directive */ - PlentyFramework.directive = function(selector, callback, dependencies, allowDuplicates) { - var directive = { - id: PlentyFramework.directives.length, - selector: selector, - callback: callback, + 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, - allowDuplicates: !!allowDuplicates, - elements: [] + compile : function() + { + var params = PlentyFramework.resolveServices( dependencies ); + PlentyFramework.directives[directiveName] = directiveFunctions.apply( null, params ); + } }; - PlentyFramework.directives.push(directive); - - return directive; }; /** @@ -108,53 +143,189 @@ * @function bindDirectives * @param {string} [directiveSelector] restrict binding to elements matching this selector */ - PlentyFramework.prototype.bindDirectives = function( directiveSelector ) { - - $.each( PlentyFramework.directives, function(i, directive) { - if( !directiveSelector || directiveSelector === directive.selector ) { - var elements = []; - // filter elements already bound - $(directive.selector).each(function (j, obj) { - if ($.inArray(obj, directive.elements) < 0) { - elements.push(obj); - } - }); + PlentyFramework.prototype.bindDirectives = function( rootElement ) + { - $.each(elements, function (i, elem) { - var params = [i, elem]; + rootElement = rootElement || 'html'; - // append dependencies if exists - if (!!directive.dependencies && directive.dependencies.length > 0) { - params = params.concat(PlentyFramework.resolveServices(directive.dependencies)); - } + $( rootElement ).find( '[data-plenty]' ).each( function( i, element ) + { - // apply loop variables and depending services to callback - directive.callback.apply(null, params); - }); + var directives = parseDirectives( $( element ).attr( 'data-plenty' ), $( element ) ); + + if ( directives.length <= 0 ) + { + // continue + return; + } + + 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 ); + } ); + */ + } - $(elements).each(function (j, obj) { - // store function ID to avoid duplicate bindings - if (!directive.allowDuplicates) { - directive.elements.push(obj); } - }); + 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 ); }; /** - * Collection of uncompiled registered factories & services. - * See {{#crossLink "PlentyFramework/compile:method"}}.compile(){{/crossLink}} - * @attribute components - * @static - * @type {{factories: {}, services: {}}} + * 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. */ - PlentyFramework.components = { - factories: {}, - services: {} - }; + function bindEventCallback( $elem, eventType, callback, params ) + { + $elem.on( eventType, function( event ) + { + eventStack.push( event ); + return callback.apply( null, params ); + } ); + } + + 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 @@ -163,29 +334,34 @@ * @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] + * @return {object} The object described in serviceFunctions(). Can be received via + * PlentyFramework.[serviceName] */ - PlentyFramework.service = function( serviceName, serviceFunctions, dependencies ) { + 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."); + 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."); + if ( typeof serviceFunctions !== 'function' ) + { + console.error( "Type mismatch: Expect second parameter to be a 'function', '" + typeof serviceFunctions + "' given." ); return; } dependencies = dependencies || []; - PlentyFramework.components.services[serviceName] = { - name: serviceName, + components.services[serviceName] = { + name : serviceName, dependencies: dependencies, - compile: function() { - var params = PlentyFramework.resolveFactories( dependencies ); + compile : function() + { + var params = PlentyFramework.resolveFactories( dependencies ); PlentyFramework.prototype[serviceName] = serviceFunctions.apply( null, params ); } }; @@ -200,23 +376,29 @@ * @param {Array} dependencies Names of required factories * @return {Array} Objects to apply to callback function */ - PlentyFramework.resolveServices = function( dependencies ) { + PlentyFramework.resolveServices = function( dependencies ) + { var compiledServices = []; - $.each( dependencies, function(j, dependency) { + $.each( dependencies, function( j, dependency ) + { // factory not found: try to compile dependent factory first - if( !PlentyFramework.prototype.hasOwnProperty(dependency) ) { - if( PlentyFramework.components.services.hasOwnProperty(dependency) ) { - PlentyFramework.components.services[dependency].compile(); - } else { - console.error('Cannot inject Service "' + dependency + '": Service not found.'); + 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; }; @@ -237,26 +419,30 @@ * @param {function} factoryFunctions The function describing the factory * @param {Array} dependencies List of required factories to inject */ - PlentyFramework.factory = function( factoryName, factoryFunctions, dependencies ) { + 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."); + 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."); + if ( typeof factoryFunctions !== 'function' ) + { + console.error( "Type mismatch: Expect second parameter to be a 'function', '" + typeof factoryFunctions + "' given." ); return; } - dependencies = dependencies || []; - PlentyFramework.components.factories[factoryName] = { - name: factoryName, + dependencies = dependencies || []; + components.factories[factoryName] = { + name : factoryName, dependencies: dependencies, - compile: function() { - var params = PlentyFramework.resolveFactories( dependencies ); + compile : function() + { + var params = PlentyFramework.resolveFactories( dependencies ); PlentyFramework.factories[factoryName] = factoryFunctions.apply( null, params ); } }; @@ -271,23 +457,29 @@ * @param {Array} dependencies Names of required factories * @return {Array} Objects to apply to callback function */ - PlentyFramework.resolveFactories = function( dependencies ) { + PlentyFramework.resolveFactories = function( dependencies ) + { var compiledFactories = []; - $.each( dependencies, function(j, dependency) { + $.each( dependencies, function( j, dependency ) + { // factory not found: try to compile dependent factory first - if( !PlentyFramework.factories.hasOwnProperty(dependency) ) { - if( PlentyFramework.components.factories.hasOwnProperty(dependency) ) { - PlentyFramework.components.factories[dependency].compile(); - } else { - console.error('Cannot inject Factory "' + dependency + '": Factory not found.'); + 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; }; @@ -301,11 +493,14 @@ * @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) ); + 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 ); @@ -333,10 +528,12 @@ * @static * @param fileName relative path to language file. */ - PlentyFramework.loadLanguageFile = function( fileName ) { - $.get( PlentyFramework.scriptPath + fileName ).done(function(response) { + PlentyFramework.loadLanguageFile = function( fileName ) + { + $.get( PlentyFramework.scriptPath + fileName ).done( function( response ) + { PlentyFramework.Strings = response; - }); + } ); }; /** @@ -349,16 +546,21 @@ * @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 ) { + PlentyFramework.translate = function( string, params ) + { var localeString; - if( PlentyFramework.Strings.hasOwnProperty(string) ) { + if ( PlentyFramework.Strings.hasOwnProperty( string ) ) + { localeString = PlentyFramework.Strings[string]; - } else { + } + else + { localeString = string; - console.warn('No translation found for "' + localeString + '".'); + console.warn( 'No translation found for "' + localeString + '".' ); } - if( !!params ) { + if ( !!params ) + { localeString = Mustache.render( localeString, params ); } @@ -371,28 +573,42 @@ * @function compile * @static */ - PlentyFramework.compile = function() { + PlentyFramework.compile = function() + { + + for ( var factory in components.factories ) + { + if ( !PlentyFramework.factories.hasOwnProperty( factory ) ) + { + components.factories[factory].compile(); + } + } - for( var factory in PlentyFramework.components.factories ) { - if( !PlentyFramework.factories.hasOwnProperty(factory) ) { - PlentyFramework.components.factories[factory].compile(); + for ( var service in components.services ) + { + if ( !PlentyFramework.prototype.hasOwnProperty( service ) ) + { + components.services[service].compile(); } } - for( var service in PlentyFramework.components.services ) { - if( !PlentyFramework.prototype.hasOwnProperty(service) ) { - PlentyFramework.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 ]; + if ( scripts.length > 0 ) + { + PlentyFramework.scriptPath = scripts[scripts.length - 1].src.match( /(.*)\/(.*)\.js(\?\S*)?$/ )[1]; } }; -}(jQuery)); +}( jQuery )); diff --git a/src/plentyFrameworkCompiler.js b/src/plentyFrameworkCompiler.js index c93a695..4ba8bd9 100644 --- a/src/plentyFrameworkCompiler.js +++ b/src/plentyFrameworkCompiler.js @@ -17,7 +17,8 @@ var plenty = PlentyFramework.getInstance(); * * will not be tested. reasons: * http://stackoverflow.com/questions/29153733/how-to-unit-test-a-document-ready-function-using-jasmine - */ -jQuery(document).ready(function() { + */ +jQuery( document ).ready( function() +{ plenty.bindDirectives(); -}); \ No newline at end of file +} ); \ No newline at end of file diff --git a/src/services/AddressDoctorService.js b/src/services/AddressDoctorService.js new file mode 100644 index 0000000..83b5d86 --- /dev/null +++ b/src/services/AddressDoctorService.js @@ -0,0 +1,320 @@ +/** + * 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 )); \ No newline at end of file diff --git a/src/services/AuthenticationService.js b/src/services/AuthenticationService.js index f2ddb2c..37e8f61 100644 --- a/src/services/AuthenticationService.js +++ b/src/services/AuthenticationService.js @@ -10,7 +10,8 @@ /** * @module Services */ -(function ($, pm) { +(function( $, pm ) +{ /** * Providing methods for logging in and out and registering new customers.
    @@ -22,13 +23,14 @@ * @class AuthenticationService * @static */ - pm.service('AuthenticationService', function (API, Checkout) { + pm.service( 'AuthenticationService', function( API, Checkout, UI ) + { return { - resetPassword: resetPassword, - customerLogin: customerLogin, + resetPassword : resetPassword, + customerLogin : customerLogin, setInvoiceAddress: setInvoiceAddress, - registerCustomer: registerCustomer + registerCustomer : registerCustomer }; /** @@ -36,13 +38,16 @@ * and sends request to provide a new password to the entered E-Mail-Address. * * @function resetPasswort - * @return {object} jQuery deferred Object + * @return {object} jQuery deferred + * Object */ - function resetPassword() { + function resetPassword() + { - var form = $('[data-plenty-checkout="lostPasswordForm"]'); + var form = $( '[data-plenty-checkout="lostPasswordForm"]' ); - if( form.validateForm() ) { + if ( form.validateForm() ) + { var values = form.getFormValues(); @@ -50,13 +55,15 @@ 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(); + 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(); } - }); + } ); } } @@ -67,23 +74,29 @@ * * @function customerLogin * @param {object} form The jQuery-wrapped form-element to read the credentials from - * @return {object} jQuery deferred Object + * @return {object} jQuery deferred + * Object */ - function customerLogin( form ) { - if( form.validateForm() ) { + function customerLogin( form ) + { + if ( form.validateForm() ) + { var values = form.getFormValues(); var params = { - Email: values.loginMail, + Email : values.loginMail, Password: values.loginPassword }; - return API.post("/rest/checkout/login/", params) - .done(function () { + + 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') ); + window.location.assign( form.attr( 'action' ) ); - }); + } ); } } @@ -92,14 +105,17 @@ * * @function setInvoiceAddress * @param {object} invoiceAddress containing address-data sent to server - * @return {object} jQuery deferred Object + * @return {object} jQuery deferred + * Object */ - function setInvoiceAddress( invoiceAddress ) { + function setInvoiceAddress( invoiceAddress ) + { - return API.post("/rest/checkout/customerinvoiceaddress/", invoiceAddress) - .done(function (response) { + return API.post( "/rest/checkout/customerinvoiceaddress/", invoiceAddress ) + .done( function( response ) + { Checkout.getCheckout().CustomerInvoiceAddress = response.data; - }); + } ); } /** @@ -108,47 +124,62 @@ * On success, redirect to forms target referenced by action-attribute * * @function registerCustomer - * @return {object} jQuery deferred Object + * @return {object} jQuery deferred + * Object */ - function registerCustomer() { - var form = $('[data-plenty-checkout-form="customerRegistration"]'); + function registerCustomer() + { + var form = $( '[data-plenty-checkout-form="customerRegistration"]' ); - if( form.validateForm() ) { + 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, + 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 + 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 }; - return setInvoiceAddress(invoiceAddress) - .done(function () { - window.location.assign( form.attr('action') ); - }); + 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']); + }, ['APIFactory', 'CheckoutFactory', 'UIFactory'] ); -}(jQuery, PlentyFramework)); \ No newline at end of file +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/services/BasketService.js b/src/services/BasketService.js index cfb347f..e3665ed 100644 --- a/src/services/BasketService.js +++ b/src/services/BasketService.js @@ -10,7 +10,8 @@ /** * @module Services */ -(function($, pm) { +(function( $, pm ) +{ /** * Providing methods for adding, editing or removing basket items and coupon codes
    @@ -25,17 +26,19 @@ * @class BasketService * @static */ - pm.service('BasketService', function( API, UI, CMS, Checkout, Modal ) { - - return { - addItem: addBasketItem, - removeItem: removeBasketItem, - setItemQuantity: setItemQuantity, + 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 - }; + editOrderParams : editOrderParams, + addCoupon : addCoupon, + removeCoupon : removeCoupon + }; /** * Add item to basket. Will fail and show a popup if item has order params @@ -45,31 +48,48 @@ * @return {object} jQuery deferred * Object */ - function addBasketItem( article ) { + function addBasketItem( article ) + { - if( !!article ) { + if ( !!article ) + { API.get( '/rest/checkout/container_' + 'CheckoutOrderParamsList'.toLowerCase() + '/', - { itemID : article[0].BasketItemItemID, - quantity : article[0].BasketItemQuantity }).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() { - // save order params - addArticle( saveOrderParams(article) ); - - // close modal after saving order params - return true; - }) - .show(); - } else { - addArticle(article); - } - }); + { + 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 ); + } + } ); } } @@ -80,109 +100,135 @@ * @private * @param {Array} articleWithParams Containing the current item to add. Read OrderParams will be injected */ - function saveOrderParams( articleWithParams ) { + function saveOrderParams( articleWithParams ) + { //TODO use $("[data-plenty-checkout-form='OrderParamsForm']").serializeArray() to get order params - var orderParamsForm = $('[data-plenty-checkout-form="OrderParamsForm"]'); - var wrappedThis = {}; - var attrType = ""; + var orderParamsForm = $( '[data-plenty-checkout-form="OrderParamsForm"]' ); + var $self = {}; + var attrType = ""; + var match; //Groups - orderParamsForm.find('[name^="ParamGroup"]').each(function(){ - var match = this.name.match(/^ParamGroup\[(\d+)]\[(\d+)]$/); - articleWithParams = addOrderParamValue(articleWithParams, match[1], $(this).val(), $(this).val()); - }); + 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(){ - wrappedThis = $(this); - attrType = wrappedThis.attr('type'); - - if( ((attrType == 'checkbox' && wrappedThis.is(':checked')) || - (attrType == 'radio' && wrappedThis.is(':checked')) || - (attrType != 'radio' && attrType != 'checkbox')) && - attrType != 'file') + 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 = this.name.match(/^ParamValue\[(\d+)]\[(\d+)]$/); - articleWithParams = addOrderParamValue(articleWithParams, match[1], match[2], wrappedThis.val()); + var match = $self[0].name.match( /^ParamValue\[(\d+)]\[(\d+)]$/ ); + articleWithParams = addOrderParamValue( articleWithParams, match[1], match[2], $self.val() ); - } else if (attrType == 'file') { - articleWithParams = orderParamFileUpload(this, articleWithParams); } - }); + 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() { + function addArticle( article ) + { + API.post( '/rest/checkout/basketitemslist/', article, true ) + .done( function() + { // Item has no OrderParams -> Refresh Checkout & BasketPreview Checkout.loadCheckout() - .done(function() { + .done( function() + { refreshBasketPreview(); // Show confirmation popup - CMS.getContainer('ItemViewItemToBasketConfirmationOverlay', { ArticleID : article[0].BasketItemItemID }).from('ItemView') - .done(function(response) { + CMS.getContainer( 'ItemViewItemToBasketConfirmationOverlay', {ArticleID: article[0].BasketItemItemID} ).from( 'ItemView' ) + .done( function( response ) + { Modal.prepare() - .setContent(response.data[0]) - .setTimeout(5000) + .setContent( response.data[0] ) + .setTimeout( 5000 ) .show(); - }); - }); - }).fail(function(jqXHR) { - // some other error occured - UI.printErrors(JSON.parse(jqXHR.responseText).error.error_stack); - }); + } ); + } ); + } ).fail( function( jqXHR ) + { + // some other error occured + UI.printErrors( JSON.parse( jqXHR.responseText ).error.error_stack ); + } ); } - function updateArticle( article ) { + function updateArticle( article ) + { API.put( '/rest/checkout/basketitemslist/', article ) - .done(function() { + .done( function() + { // Item has no OrderParams -> Refresh Checkout & BasketPreview Checkout.reloadCatContent( pm.getGlobal( 'basketCatID' ) ); Checkout.loadCheckout() - .done(function() { + .done( function() + { refreshBasketPreview(); - }); - }) + } ); + } ) } - function orderParamFileUpload(input, articleWithParams ) { - var key = input.id; + 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', + var params = { + type : 'POST', + data : {}, + isFile : true, + cache : false, + dataType : 'json', processData: false, contentType: false }; - orderParamUploadFiles[key] = $(input)[0].files; + orderParamUploadFiles[key] = $input[0].files; - if (orderParamFileIdStack.indexOf(key) == -1) { - orderParamFileIdStack.push(key); + // if input not pushed before. + if ( orderParamFileIdStack.indexOf( key ) == -1 ) + { + orderParamFileIdStack.push( key ); } - for(var i= 0, length = orderParamFileIdStack.length; i < length; ++i) { + 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); + formData.append( "0", fileData[0], fileData[0].name ); params.data = formData; - API.post("/rest/checkout/orderparamfile/", params); + API.post( "/rest/checkout/orderparamfile/", params ); } - var match = input.name.match(/^ParamValueFile\[(\d+)]\[(\d+)]$/); + var match = $input[0].name.match( /^ParamValueFile\[(\d+)]\[(\d+)]$/ ); - return addOrderParamValue(articleWithParams, match[1], match[2], $(input).val()); + return addOrderParamValue( articleWithParams, match[1], match[2], $input.val() ); } /** @@ -195,90 +241,112 @@ * @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) + function addOrderParamValue( basketList, position, paramId, paramValue ) + { + if ( position > 0 && basketList[position] == undefined ) { - basketList[position] = $.extend(true, {}, basketList[0]); + basketList[position] = $.extend( true, {}, basketList[0] ); basketList[position].BasketItemOrderParamsList = []; } - if(basketList[position] != undefined) + if ( basketList[position] != undefined ) { basketList[position].BasketItemQuantity = 1; - if(basketList[position].BasketItemOrderParamsList == undefined) + if ( basketList[position].BasketItemOrderParamsList == undefined ) { basketList[position].BasketItemOrderParamsList = []; } - if(paramValue){ - basketList[position].BasketItemOrderParamsList.push({ - BasketItemOrderParamID : paramId, - BasketItemOrderParamValue : paramValue - }); + 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); + 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 = []; - modal.find('select').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) + // 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] ) { - basketItem.BasketItemAttributesList = attributesList; + attributesList.push( { + BasketItemAttributeID : match[1], + BasketItemAttributeValueID: $( attributeSelect ).val() + } ); } - }); + } ); + + if ( attributesList.length != 0 ) + { + basketItem.BasketItemAttributesList = attributesList; + } //update basketItem and refresh previewLists - updateArticle([basketItem]); + updateArticle( [basketItem] ); - }); + } ); } - function editOrderParams( BasketItemID ) { + 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() { + 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]) ); + updateArticle( saveOrderParams( [basketItem] ) ); // close modal after saving order params return true; - }) - .show(); - }); + } + else + { + return false; + } + } ) + .show(); + } ); } - function getBasketItem( BasketItemID ) { + function getBasketItem( BasketItemID ) + { var basketItems = Checkout.getCheckout().BasketItemsList; - for( var i = 0; i < basketItems.length; i++ ) { - if( basketItems[i].BasketItemID == BasketItemID ) { + for ( var i = 0; i < basketItems.length; i++ ) + { + if ( basketItems[i].BasketItemID == BasketItemID ) + { return basketItems[i]; } } @@ -293,53 +361,65 @@ * @param {boolean} [forceDelete=false] Set true to remove the basket item without showing a confirmation popup * @return Promise */ - function removeBasketItem( BasketItemID, forceDelete ) { + function removeBasketItem( BasketItemID, forceDelete ) + { - // get item name - var itemName, originalItemQuantity; - var params = Checkout.getCheckout().BasketItemsList; + var deferred = $.Deferred(); - for ( var i = 0; i < params.length; i++ ) { - if ( params[i].BasketItemID == BasketItemID ) { - originalItemQuantity = params[i].BasketItemQuantity; - itemName = params[i].BasketItemNameMap[1]; - } - } + // 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(); + 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 ) { + if ( !Checkout.getCheckout().BasketItemsList || Checkout.getCheckout().BasketItemsList.length <= 0 ) + { Checkout.reloadCatContent( pm.getGlobal( 'basketCatID' ) ); - } else { - Checkout.reloadContainer('Totals'); + } + else + { + Checkout.reloadContainer( 'Totals' ); } refreshBasketPreview(); - }); - }); + + deferred.resolve(); + } ); + } ); } - if( !forceDelete ) { + 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); - }) - .onConfirm(function () { + .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") ) + } ) + .setLabelConfirm( pm.translate( "Delete" ) ) .show(); - } else { + } + else + { doDelete(); } + + return deferred; } /** @@ -349,45 +429,47 @@ * @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 ) { + function setItemQuantity( BasketItemID, BasketItemQuantity ) + { // delete item if quantity is 0 - if( BasketItemQuantity <= 0 ) { - removeBasketItem( BasketItemID ); + if ( BasketItemQuantity <= 0 ) + { + return removeBasketItem( BasketItemID ); } - var params = Checkout.getCheckout().BasketItemsList; + 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 ) { + for ( var i = 0; i < params.length; i++ ) + { + if ( params[i].BasketItemID == BasketItemID ) + { basketItemIndex = i; - basketItem = params[i]; + basketItem = params[i]; break; } } - if( !!basketItem && basketItem.BasketItemQuantity != BasketItemQuantity ) { + if ( !!basketItem && basketItem.BasketItemQuantity != BasketItemQuantity ) + { params[basketItemIndex].BasketItemQuantity = parseInt( BasketItemQuantity ); - API.post("/rest/checkout/basketitemslist/", params) - .done(function () { - Checkout.setCheckout().done(function () { - Checkout.reloadContainer('Totals'); - - var basketItemsPriceTotal = 0; - var params2 = Checkout.getCheckout().BasketItemsList; - for (var i = 0; i < params2.length; i++) { - if (params2[i].BasketItemID == BasketItemID) { - basketItemsPriceTotal = params2[i].BasketItemPriceTotal; - } - } - $('[data-basket-item-id="' + BasketItemID + '"]').find('[data-plenty-checkout="basket-item-price-total"]').html(basketItemsPriceTotal); + API.post( "/rest/checkout/basketitemslist/", params ) + .done( function() + { + Checkout.setCheckout().done( function() + { + Checkout.reloadCatContent( pm.getGlobal( 'basketCatID' ) ); refreshBasketPreview(); - }); - }); + deferred.resolve(); + } ); + } ); } + + return deferred; } /** @@ -395,30 +477,37 @@ * @function refreshBasketPreview * @private */ - function refreshBasketPreview() { + function refreshBasketPreview() + { - Checkout.reloadItemContainer('BasketPreviewList') - .done(function() { + 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 ); + $( '[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) { + $.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 ); + $( '[data-plenty-basket-preview="itemQuantityTotal"]' ).text( itemQuantityTotal ); + $( '[data-plenty-basket-preview="totalsItemSum"]' ).text( Checkout.getCheckout().Totals.TotalsItemSum ); } /** @@ -428,19 +517,22 @@ * @return {object} jQuery deferred * Object */ - function addCoupon() { + function addCoupon() + { var params = { - CouponActiveCouponCode: $('[data-plenty-checkout-form="couponCode"]').val() + CouponActiveCouponCode: $( '[data-plenty-checkout-form="couponCode"]' ).val() }; - return API.post("/rest/checkout/coupon/", params) - .done(function() { + return API.post( "/rest/checkout/coupon/", params ) + .done( function() + { Checkout.setCheckout() - .done(function() { + .done( function() + { updateContainer(); - }); - }); + } ); + } ); } /** @@ -449,30 +541,35 @@ * @return {object} jQuery deferred * Object */ - function removeCoupon() { + function removeCoupon() + { var params = { CouponActiveCouponCode: Checkout.getCheckout().Coupon.CouponActiveCouponCode }; - return API.delete("/rest/checkout/coupon/", params) - .done(function() { + return API.delete( "/rest/checkout/coupon/", params ) + .done( function() + { Checkout.setCheckout() - .done(function() { + .done( function() + { delete Checkout.getCheckout().Coupon; updateContainer(); - }); - }); + } ); + } ); } // update container - function updateContainer() { - Checkout.reloadContainer('Coupon'); + function updateContainer() + { + Checkout.reloadContainer( 'Coupon' ); // reload totals, if we are at basket - if ( $('[data-plenty-checkout-template="Totals"]').length > 0 ) { - Checkout.reloadContainer('Totals'); + if ( $( '[data-plenty-checkout-template="Totals"]' ).length > 0 ) + { + Checkout.reloadContainer( 'Totals' ); } } - }, ['APIFactory', 'UIFactory', 'CMSFactory', 'CheckoutFactory', 'ModalFactory']); -}(jQuery, PlentyFramework)); \ No newline at end of file + }, ['APIFactory', 'UIFactory', 'CMSFactory', 'CheckoutFactory', 'ModalFactory'] ); +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/services/CheckoutService.js b/src/services/CheckoutService.js index 9a4290f..4fdc52d 100644 --- a/src/services/CheckoutService.js +++ b/src/services/CheckoutService.js @@ -10,7 +10,8 @@ /** * @module Services */ -(function($, pm) { +(function( $, pm ) +{ /** * Providing methods for checkout process like setting shipping & payment information and placing the order.
    @@ -24,57 +25,66 @@ * @class CheckoutService * @static */ - pm.service('CheckoutService', function(API, CMS, Checkout, Modal) { + pm.service( 'CheckoutService', function( API, CMS, Checkout, Modal ) + { - var checkoutState; - - return { - init: init, + return { + init : init, setCustomerSignAndInfo: setCustomerSignAndInfo, - registerGuest: registerGuest, - setShippingProfile: setShippingProfile, - saveShippingAddress: saveShippingAddress, - loadAddressSuggestion: loadAddressSuggestion, - preparePayment: preparePayment, - setMethodOfPayment: setMethodOfPayment, - editBankDetails: editBankDetails, - editCreditCard: editCreditCard, - placeOrder: placeOrder - }; + 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); - checkoutState = Checkout.getCheckout(true); + function init() + { + Checkout.loadCheckout( true ); } - /** - * Read customer sign and order information text from <form> marked with data-plenty-checkout-form="details" - * and update checkout. + * 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 + * @return {object} jQuery deferred + * Object */ - function setCustomerSignAndInfo() { - var form = $('[data-plenty-checkout-form="details"]'); + 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 ) + { + 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 ) ) { + 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().CheckoutCustomerSign = values.CustomerSign; Checkout.getCheckout().CheckoutOrderInfoText = values.OrderInfoText; return Checkout.setCheckout(); - } else { + } + else + { // No changes detected -> Do nothing return API.idle(); } @@ -85,60 +95,92 @@ * 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 + * @return {object} jQuery deferred + * Object */ - function saveShippingAddress( validateForm ) { - var form = $('[data-plenty-checkout-form="shippingAddress"]'); + function saveShippingAddress( validateForm ) + { + var form = $( '[data-plenty-checkout-form="shippingAddress"]' ); - if( !validateForm && !form.validateForm() ) { + if ( !validateForm && !form.validateForm() ) + { return false; } - var values = form.getFormValues(); - var shippingAddressID = $('[name="shippingAddressID"]:checked').val(); + 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'); + $( '#shippingAdressSelect' ).modal( 'hide' ); - if ( shippingAddressID < 0) { + if ( shippingAddressID < 0 ) + { // save separate var shippingAddress = values; - if( !addressesAreEqual( shippingAddress, Checkout.getCheckout().CustomerShippingAddress) ) { + 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) { + return API.post( "/rest/checkout/customershippingaddress/", shippingAddress ) + .done( function( response ) + { Checkout.getCheckout().CheckoutCustomerShippingAddressID = response.data.ID; - Checkout.getCheckout().CheckoutShippingCountryID = response.data.CountryID; + Checkout.getCheckout().CheckoutShippingCountryID = response.data.CountryID; delete Checkout.getCheckout().CheckoutMethodOfPaymentID; delete Checkout.getCheckout().CheckoutShippingProfileID; - Checkout.setCheckout().done(function () { - if (Checkout.getCheckout().CustomerInvoiceAddress.LoginType == 2) { - Checkout.reloadContainer('CustomerShippingAddress'); + Checkout.setCheckout().done( function() + { + if ( Checkout.getCheckout().CustomerInvoiceAddress.LoginType == 2 ) + { + Checkout.reloadContainer( 'CustomerShippingAddress' ); } - }); - }); - } else { + } ); + } ); + } + else + { // no changes detected return API.idle(); } - } else { - if( shippingAddressID != Checkout.getCheckout().CheckoutCustomerShippingAddressID ) { + } + 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 () { - if (Checkout.getCheckout().CustomerInvoiceAddress.LoginType == 2) { - Checkout.reloadContainer('CustomerShippingAddress'); + return Checkout.setCheckout().done( function() + { + if ( Checkout.getCheckout().CustomerInvoiceAddress.LoginType == 2 ) + { + Checkout.reloadContainer( 'CustomerShippingAddress' ); } - }); - } else { + } ); + } + else + { return API.idle(); } } @@ -148,25 +190,42 @@ * 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 + * @return {object} jQuery deferred + * Object */ - function registerGuest() { - var form = $('[data-plenty-checkout-form="guestRegistration"]'); + function registerGuest() + { + var form = $( '[data-plenty-checkout-form="guestRegistration"]' ); - var invoiceAddress = form.getFormValues(); + var invoiceAddress = form.getFormValues(); invoiceAddress.LoginType = 1; + invoiceAddress.CustomerPropertiesList = invoiceAddress.CustomerPropertiesList || []; + + form.find( "[data-plenty-property-id]" ).each( function( i, propertyInput ) + { - if( !addressesAreEqual( invoiceAddress, Checkout.getCheckout().CustomerInvoiceAddress ) ) { + invoiceAddress.CustomerPropertiesList.push( { + PropertyID : $( propertyInput ).attr( 'data-plenty-property-id' ), + PropertyValue: $( propertyInput ).val() + } ); + } ); - return API.post("/rest/checkout/customerinvoiceaddress/", invoiceAddress) - .done(function (response) { - saveShippingAddress().done(function(){ + if ( !addressesAreEqual( invoiceAddress, Checkout.getCheckout().CustomerInvoiceAddress ) ) + { + + return API.post( "/rest/checkout/customerinvoiceaddress/", invoiceAddress ) + .done( function( response ) + { + saveShippingAddress().done( function() + { Checkout.getCheckout().CustomerInvoiceAddress = response.data; - }); - }); + } ); + } ); - } else { + } + else + { return saveShippingAddress(); @@ -181,9 +240,12 @@ * @param {object} address2 * @returns {boolean} */ - function addressesAreEqual( address1, address2 ) { - for ( var key in address1 ) { - if ( address1[key]+'' !== address2[key]+'' && key !== 'EmailRepeat' ) { + function addressesAreEqual( address1, address2 ) + { + for ( var key in address1 ) + { + if ( address1[key] + '' !== address2[key] + '' && key !== 'EmailRepeat' ) + { return false; } } @@ -194,55 +256,64 @@ * 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 + * @return {object} jQuery deferred + * Object */ - function setShippingProfile() { + function setShippingProfile() + { - var values = $('[data-plenty-checkout-form="shippingProfileSelect"]').getFormValues(); + 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'); - }); + .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 + * @return {object} jQuery deferred + * Object */ - function preparePayment() { - if( Object.equals(checkoutState, Checkout.getCheckout(true)) ) { - return API.idle(); - } else { - checkoutState = Checkout.getCheckout(true); - 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 ) { + 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(); + } + } ); - 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(); - } - } ); - } } /** @@ -250,45 +321,56 @@ * @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 + * @return {object} jQuery deferred + * Object */ - function setMethodOfPayment( paymentID ) { + function setMethodOfPayment( paymentID ) + { - paymentID = paymentID || $('[data-plenty-checkout-form="methodOfPayment"]').getFormValues().MethodOfPaymentID; + 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'); - }); + .done( function() + { + Checkout.reloadContainer( 'ShippingProfilesList' ); + } ); } /** * Display the popup to enter or edit customers bank details * @function editBankDetails */ - function editBankDetails() { + function editBankDetails() + { - CMS.getContainer('CheckoutPaymentInformationBankDetails').from('Checkout') - .done(function(response) { + 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'); + .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() { + } ); + } ).onConfirm( function() + { return saveBankDetails(); - }) + } ) .show(); - }); + } ); } @@ -298,30 +380,36 @@ * @private * @return {boolean} the result of form validation */ - function saveBankDetails() { - var form = $('[data-plenty-checkout-form="bankDetails"]'); + function saveBankDetails() + { + var form = $( '[data-plenty-checkout-form="bankDetails"]' ); - if( form.validateForm() ) { + 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 + 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'); - }); - }); + API.post( "/rest/checkout/paymentinformationbankdetails/", bankDetails ) + .done( function() + { + Checkout.loadCheckout().done( function() + { + setMethodOfPayment( 3 ); + Checkout.reloadContainer( 'MethodsOfPaymentList' ); + } ); + } ); return true; - } else { + } + else + { return false; } } @@ -330,25 +418,33 @@ * Display a popup containing credit card form * @function editCreditCard */ - function editCreditCard() { + function editCreditCard() + { - CMS.getContainer('CheckoutPaymentInformationCreditCard').from('Checkout') - .done(function(response) { + 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'); + .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() { + } ); + } ).onConfirm( function() + { return saveCreditCard(); - }) + } ) .show(); - }); + } ); } /** @@ -357,28 +453,33 @@ * @private * @return {boolean} the result of form validation */ - function saveCreditCard() { - var form = $('[data-plenty-checkout-form="creditCard"]'); + function saveCreditCard() + { + var form = $( '[data-plenty-checkout-form="creditCard"]' ); - if( form.validateForm() ) { + 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 + 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() { + API.post( '/rest/checkout/paymentinformationcreditcard/', creditCard ) + .done( function() + { Checkout.loadCheckout(); - }); + } ); return true; - } else { + } + else + { return false; } } @@ -387,83 +488,97 @@ * Display a popup containing address suggestions * @param {string} type */ - function loadAddressSuggestion(type) { + function loadAddressSuggestion( type ) + { //check login type - if (Checkout.getCheckout().CustomerInvoiceAddress.LoginType == 2) { - var values = $('[data-plenty-checkout-form="shippingAddress"]').getFormValues(); + if ( Checkout.getCheckout().CustomerInvoiceAddress.LoginType == 2 ) + { + var values = $( '[data-plenty-checkout-form="shippingAddress"]' ).getFormValues(); } - else { - var values = $('[data-plenty-checkout-form="guestRegistration"]').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, + 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) { + CMS.getContainer( 'CheckoutAddressSuggestionResultsList', params ).from( 'Checkout' ) + .done( function( response ) + { Modal.prepare() - .setContent(response.data[0]) + .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 + * @return {object} jQuery deferred + * Object */ - function placeOrder() { - var form = $('[data-plenty-checkout-form="placeOrder"]'); - if ( form.validateForm() ) { + 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, + 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 + PayoneInvoiceCheck : values.payoneInvoiceCheck || 0 }; - return API.post("/rest/checkout/placeorder/", params) - .done(function(response) { - if(response.data.MethodOfPaymentRedirectURL != '') { + 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 != '') { + } + 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(); + .onDismiss( function() + { + window.location.assign( form.attr( 'action' ) ); + } ).onConfirm( function() + { + window.location.assign( form.attr( 'action' ) ); + } ).show(); - } else { + } + else + { - window.location.assign( form.attr('action') ); + window.location.assign( form.attr( 'action' ) ); } - }); + } ); } } - - }, ['APIFactory', 'CMSFactory', 'CheckoutFactory', 'ModalFactory']); -}(jQuery, PlentyFramework)); \ No newline at end of file + }, ['APIFactory', 'CMSFactory', 'CheckoutFactory', 'ModalFactory'] ); +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/services/MediaSizeService.js b/src/services/MediaSizeService.js index 9242648..0802d61 100644 --- a/src/services/MediaSizeService.js +++ b/src/services/MediaSizeService.js @@ -10,7 +10,8 @@ /** * @module Services */ -(function($, pm){ +(function( $, pm ) +{ /** * Listens to window's size and trigger 'sizeChange' event if the Bootstrap interval changes. @@ -21,18 +22,20 @@ * console.log('The interval changed from ' + oldValue + ' to ' + newValue.'); * }); */ - pm.service('MediaSizeService', function() { + pm.service( 'MediaSizeService', function() + { var bsInterval; // recalculation of the current interval on window resize - $(window).resize( calculateMediaSize ); + $( window ).resize( calculateMediaSize ); // initially calculation of the interval - $(document).ready( calculateMediaSize ); + $( document ).ready( calculateMediaSize ); return { - interval: getInterval + interval : getInterval, + isInterval: isInterval }; /** @@ -40,8 +43,12 @@ * @function getInterval * @return {"xs"|"sm"|"md"|"lg"} */ - function getInterval() { - if( !!bsInterval ) calculateMediaSize(); + function getInterval() + { + if ( !!bsInterval ) + { + calculateMediaSize(); + } return bsInterval; } @@ -51,27 +58,68 @@ * @function calculateMediaSize * @private */ - function calculateMediaSize() { + 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 ( !!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 ) { + if ( size != bsInterval ) + { var oldValue = bsInterval; - bsInterval = size; - $(window).trigger('sizeChange', [bsInterval, oldValue]); + 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)); \ No newline at end of file +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/services/NavigatorService.js b/src/services/NavigatorService.js index 9c47ded..2cb4514 100644 --- a/src/services/NavigatorService.js +++ b/src/services/NavigatorService.js @@ -10,7 +10,8 @@ /** * @module Services */ -(function($, pm){ +(function( $, pm ) +{ /** * Handling navigation while checkout processes @@ -18,29 +19,30 @@ * @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: [] - }; + 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, + init : init, getCurrentContainer: getCurrentContainer, - goTo: goTo, - beforeChange: beforeChange, - afterChange: afterChange, - continueChange: continueChange, - next: next, - previous: previous, - goToID: goToID, - fillNavigation: fillNavigation + goTo : goTo, + beforeChange : beforeChange, + afterChange : afterChange, + continueChange : continueChange, + next : next, + previous : previous, + goToID : goToID, + fillNavigation : fillNavigation }; /** @@ -67,80 +69,99 @@ *
    * ``` */ - function init() { + 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"]'); + 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 ) { + if ( navigation.length == container.length && container.length > 0 ) + { var checkout = Checkout.getCheckout(); container.hide(); // initialize navigation - navigation.each(function(i, elem) { - $(elem).addClass('disabled'); + navigation.each( function( i, elem ) + { + $( elem ).addClass( 'disabled' ); // handle navigation click events - $(elem).click(function() { - if( !$(this).is('.disabled') ) { + $( elem ).click( function() + { + if ( !$( this ).is( '.disabled' ) ) + { goTo( i ); } - }); - }); + } ); + } ); - buttonNext.attr("disabled", "disabled"); - buttonNext.click(function() { + buttonNext.attr( "disabled", "disabled" ); + buttonNext.click( function() + { next(); - }); + } ); - buttonPrev.attr("disabled", "disabled"); - buttonPrev.click(function() { + 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); + window.addEventListener( 'hashchange', function() + { + if ( window.location.hash.length > 0 ) + { + goToID( window.location.hash ); + } + else + { + goTo( 0 ); } - }, false); + }, 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 ) { + $.urlParam = function( name ) + { + var results = new RegExp( '[\?&]' + name + '=([^&#]*)' ).exec( window.location.href ); + if ( results == null ) + { return null; } - else { + else + { return results[1] || 0; } }; - var param = $.urlParam('gototab'); + 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 ) { + 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); + 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') { + $( window ).on( 'sizeChange', fillNavigation ); + $( window ).resize( function() + { + if ( pm.getInstance().MediaSizeService.interval() == 'xs' ) + { fillNavigation(); } - }); + } ); } } @@ -150,13 +171,17 @@ * @function getCurrentContainer * @return {{id: string, index: number}} */ - function getCurrentContainer() { - if (current >= 0) { + function getCurrentContainer() + { + if ( current >= 0 ) + { return { - id: $(container[current]).attr('data-plenty-checkout-id'), + id : $( container[current] ).attr( 'data-plenty-checkout-id' ), index: current }; - } else { + } + else + { return null; } } @@ -176,7 +201,8 @@ * return true; * }); */ - function beforeChange( interceptor ) { + function beforeChange( interceptor ) + { interceptors.beforeChange.push( interceptor ); return pm.getInstance().NavigatorService; } @@ -187,7 +213,8 @@ * @chainable * @returns {NavigatorService} */ - function afterChange( interceptor ) { + function afterChange( interceptor ) + { interceptors.afterChange.push( interceptor ); return pm.getInstance().NavigatorService; } @@ -201,23 +228,27 @@ * @param {number} index the index of the target container * @returns {boolean} Conjunction of all interceptor return values */ - function resolveInterceptors( identifier, index ) { + function resolveInterceptors( identifier, index ) + { var continueTabChange = true; - if( current >= 0 || identifier === 'afterChange' ) { + if ( current >= 0 || identifier === 'afterChange' ) + { var currentContainer = getCurrentContainer(); - var targetContainer = { + var targetContainer = { index: index, - id: $(container[index]).attr('data-plenty-checkout-id') + id : $( container[index] ).attr( 'data-plenty-checkout-id' ) }; - $.each(interceptors[identifier], function (i, interceptor) { - if (interceptor(currentContainer, targetContainer) === false) { + $.each( interceptors[identifier], function( i, interceptor ) + { + if ( interceptor( currentContainer, targetContainer ) === false ) + { continueTabChange = false; return false; } - }); + } ); } return continueTabChange; @@ -227,111 +258,134 @@ * 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 + * @param {boolean} [ignoreInterceptors=false] Set true to not call registered interceptors and force changing + * tab */ - function goTo(index, ignoreInterceptors) { - - + function goTo( index, ignoreInterceptors ) + { var contentChanged = current !== index; - if( contentChanged && !ignoreInterceptors ) { - if( !resolveInterceptors( "beforeChange", 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); + 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] ); + 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(); - }); - } else { + proceedTabChange( contentChanged ); + pm.getInstance().bindDirectives( container[current] ); + $( window ).trigger( 'contentChanged' ); + } ); + } + else + { // continue tab change without reloading tab content - proceedTabChange(contentChanged); - pm.getInstance().bindDirectives(); + proceedTabChange( contentChanged ); + //pm.getInstance().bindDirectives(); } } - function proceedTabChange( contentChanged ) { + function proceedTabChange( contentChanged ) + { // hide content containers - $(container).hide(); + $( container ).hide(); // refresh navigation elements - $(navigation).each(function (i, elem) { - $(elem).removeClass('disabled active'); + $( navigation ).each( function( i, elem ) + { + $( elem ).removeClass( 'disabled active' ); - $(elem).find('[role="tab"]').attr('aria-selected', 'false'); + $( elem ).find( '[role="tab"]' ).attr( 'aria-selected', 'false' ); - if (i < current) { + if ( i < current ) + { // set current element as active - $(elem).addClass('visited'); + $( elem ).addClass( 'visited' ); } - else { - if (i == current) { - $(elem).addClass('active visited'); - $(elem).find('[role="tab"]').attr('aria-selected', 'true'); + else + { + if ( i == current ) + { + $( elem ).addClass( 'active visited' ); + $( elem ).find( '[role="tab"]' ).attr( 'aria-selected', 'true' ); } - else { - if (i > current && !$(elem).is('.visited')) { + else + { + if ( i > current && !$( elem ).is( '.visited' ) ) + { // disable elements behind active - $(elem).addClass('disabled'); + $( elem ).addClass( 'disabled' ); } } } - }); + } ); fillNavigation(); // hide "previous"-button if first content container is shown - if (current <= 0) { - $(buttonPrev).attr("disabled", "disabled"); - } else { - $(buttonPrev).removeAttr("disabled"); + 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"); + if ( current + 1 == navigation.length ) + { + $( buttonNext ).attr( "disabled", "disabled" ); } - else { - $(buttonNext).removeAttr("disabled"); + else + { + $( buttonNext ).removeAttr( "disabled" ); } // show current content container - $(container[current]).show(); + $( 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) { + 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); + 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); + function continueChange( targetContainer ) + { + goTo( targetContainer.index, true ); } /** @@ -343,9 +397,11 @@ * * @function next */ - function next() { - if (current < navigation.length - 1) { - goTo(current + 1); + function next() + { + if ( current < navigation.length - 1 ) + { + goTo( current + 1 ); } } @@ -353,34 +409,43 @@ * Show previous checkout tab if available * @function next */ - function previous() { - if (current > 0) { - goTo(current - 1); + 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="#..." + * @param {string} containerID ID of tab to show. Target tab must be marked with + * data-plenty-checkout-id="#..." */ - function goToID(containerID) { - if (containerID == 'next') { + function goToID( containerID ) + { + if ( containerID == 'next' ) + { next(); return true; } - else if (containerID == 'prev') { + 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); + else + { + containerID = containerID.replace( '#', '' ); + $( container ).each( function( i, elem ) + { + if ( $( elem ).attr( 'data-plenty-checkout-id' ) == containerID ) + { + goTo( i ); return true; } - }); + } ); } return false; @@ -391,76 +456,87 @@ * by increasing its items padding. * @function fillNavigation */ - function fillNavigation() { + function fillNavigation() + { // break if manager has not been initialized var navigationCount = navigation.length; - if( navigationCount <= 0 ) return; + if ( navigationCount <= 0 ) + { + return; + } // reset inline styles - $(navigation).removeAttr('style'); - $(navigation).children('span').removeAttr('style'); - $(buttonNext).removeAttr('style'); - $(buttonPrev).removeAttr('style'); - + $( 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' }); + 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 width = $( navigation ).parent().parent().outerWidth( true ) - ( 2 * buttonWidth); + width -= parseInt( $( navigation ).parent().css( 'marginLeft' ) ) + parseInt( $( navigation ).parent().css( 'marginRight' ) ); - var padding = width; + var padding = width; var tabWidth = []; - $(navigation).each(function(i, elem) { - padding -= parseInt( $(elem).css('marginLeft') ); - padding -= parseInt( $(elem).css('marginRight') ); + $( navigation ).each( function( i, elem ) + { + padding -= parseInt( $( elem ).css( 'marginLeft' ) ); + padding -= parseInt( $( elem ).css( 'marginRight' ) ); - tabWidth[i] = $(elem).children('span').width(); + tabWidth[i] = $( elem ).children( 'span' ).width(); padding -= tabWidth[i]; - padding -= parseInt( $(elem).children('span').css('marginLeft') ); - padding -= parseInt( $(elem).children('span').css('marginRight') ); - }); + 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; + if ( paddingEachItem % 2 == 1 ) + { + paddingLeft = ( paddingEachItem / 2 ) + 0.5; paddingRight = ( paddingEachItem / 2 ) - 0.5; } - else { - paddingLeft = paddingEachItem / 2; + 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; + if ( paddingLastItem % 2 == 1 ) + { + paddingLastLeft = ( paddingLastItem / 2 ) + 0.5; paddingLastRight = ( paddingLastItem / 2) - 0.5; } - else { - paddingLastLeft = paddingLastItem / 2; + 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' }); + $( 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' }); + 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']); + }, ['CMSFactory', 'CheckoutFactory'] ); -}(jQuery, PlentyFramework)); \ No newline at end of file +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/services/PostfinderService.js b/src/services/PostfinderService.js new file mode 100644 index 0000000..01ba9b0 --- /dev/null +++ b/src/services/PostfinderService.js @@ -0,0 +1,155 @@ +/** + * 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' ); + } + }, ['APIFactory', 'ModalFactory', 'UIFactory'] ); + +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/services/SocialShareService.js b/src/services/SocialShareService.js index 85f1a66..cd26344 100644 --- a/src/services/SocialShareService.js +++ b/src/services/SocialShareService.js @@ -10,18 +10,26 @@ /** * @module Services */ -(function($, pm) { +(function( $, pm ) +{ /** * Provide templates for social share providers to inject them dynamically. * @class SocialShareService * @static */ - pm.service('SocialShareService', function() { + pm.service( 'SocialShareService', function() + { //TODO: move to global variables - if ( typeof(socialLangLocale) == 'undefined' ) socialLangLocale = 'en_US'; - if ( typeof(socialLang) == 'undefined' ) socialLang = 'en'; + if ( typeof(socialLangLocale) == 'undefined' ) + { + socialLangLocale = 'en_US'; + } + if ( typeof(socialLang) == 'undefined' ) + { + socialLang = 'en'; + } return { getSocialService: getService @@ -33,41 +41,42 @@ * @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 ) { + function getService( identifier ) + { var services = { - 'facebook-like' : '', - - 'facebook-recommend' : '', - - 'twitter' : '', - - 'google-plus' : '
    ' - +'', + 'facebook-like': '', + + 'facebook-recommend': '', + + 'twitter': '', + + 'google-plus': '
    ' + + '', }; return services[identifier]; @@ -79,12 +88,15 @@ * @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) { + 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; @@ -99,8 +111,9 @@ * @private * @param {string} name The meta name to get the value of; */ - function getMeta(name) { - var metaContent = $('meta[name="' + name + '"]').attr('content'); + function getMeta( name ) + { + var metaContent = $( 'meta[name="' + name + '"]' ).attr( 'content' ); return metaContent || ''; } @@ -110,19 +123,23 @@ * @function getTweetText * @private */ - function getTweetText() { - var title = getMeta('DC.title'); - var creator = getMeta('DC.creator'); + function getTweetText() + { + var title = getMeta( 'DC.title' ); + var creator = getMeta( 'DC.creator' ); - if (title.length > 0 && creator.length > 0) { + if ( title.length > 0 && creator.length > 0 ) + { title += ' - ' + creator; - } else { - title = $('title').text(); + } + else + { + title = $( 'title' ).text(); } - return encodeURIComponent(title); + return encodeURIComponent( title ); } - }); + } ); -}(jQuery, PlentyFramework)); \ No newline at end of file +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/src/services/ValidationService.js b/src/services/ValidationService.js index a0bb4b1..06913ce 100644 --- a/src/services/ValidationService.js +++ b/src/services/ValidationService.js @@ -10,14 +10,16 @@ /** * @module Services */ -(function ($, pm) { +(function( $, pm ) +{ /** * Provide methods for client-side form validation. * @class ValidationService * @static */ - pm.service( 'ValidationService', function() { + pm.service( 'ValidationService', function() + { return { validate: validate @@ -30,24 +32,32 @@ * @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') ) { + 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( 'input' ).length > 0 ) + { + return element.find( 'input' ); } - else if ( element.find('select').length > 0 ) { - return element.find('select'); + else if ( element.find( 'select' ).length > 0 ) + { + return element.find( 'select' ); } - else if ( element.find('textarea').length > 0 ) { - return element.find('textarea'); + else if ( element.find( 'textarea' ).length > 0 ) + { + return element.find( 'textarea' ); } - else { + else + { return null; } } @@ -61,14 +71,18 @@ * @param {object} formControl the form element to validate * @return {boolean} */ - function validateText( formControl ) { + function validateText( formControl ) + { // check if formControl is no checkbox or radio - if ( formControl.is('input') || formControl.is('select') || formControl.is('textarea') ) { + 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") + '>'); + } + else + { + console.error( 'Validation Error: Cannot validate Text for <' + formControl.prop( "tagName" ) + '>' ); return false; } } @@ -80,11 +94,15 @@ * @param {object} formControl the form element to validate * @return {boolean} */ - function validateMail( formControl ) { + 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) ) { + if ( validateText( formControl ) ) + { return mailRegExp.test( $.trim( formControl.val() ) ); - } else { + } + else + { return false; } } @@ -96,10 +114,14 @@ * @param {object} formControl the form element to validate * @return {boolean} */ - function validateNumber( formControl ) { - if ( validateText(formControl) ) { + function validateNumber( formControl ) + { + if ( validateText( formControl ) ) + { return $.isNumeric( $.trim( formControl.val() ) ); - } else { + } + else + { return false; } } @@ -112,20 +134,26 @@ * @param {string} reference the required value * @return {boolean} */ - function validateValue( formControl, reference ) { - if( $(reference).length > 0 ) { - return $.trim( formControl.val() ) == $.trim( $(reference).val() ); - } else { + 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 visibility( formControl ) + { + return formControl.is( ':visible' ); } - function isEnabled( formControl ) { - return formControl.is(':enabled'); + function isEnabled( formControl ) + { + return formControl.is( ':enabled' ); } /** @@ -177,53 +205,59 @@ * // handle missing fields * }); */ - function validate( form ) { + function validate( form, errorClass ) + { var formControl, formControls, validationKey, currentHasError, group, checked, checkedMin, checkedMax, attrValidate, validationKeys, formControlAttrType; - var wrappedForm = $(form); - var errorClass = !!wrappedForm.attr('data-plenty-checkform') ? wrappedForm.attr('data-plenty-checkform') : 'has-error'; + var $form = $( form ); + errorClass = errorClass || 'has-error'; var missingFields = []; - var hasError = false; + var hasError = false; // check every required input inside form - wrappedForm.find('[data-plenty-validate], input.Required').each(function(i, elem) { - attrValidate = $(elem).attr('data-plenty-validate'); - formControls = getFormControl(elem) + $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(','); + validationKeys = validationKeys.split( ',' ); - for(var i = 0, length = formControls.length; i < length; i++) { - formControl = $(formControls[i]); - formControlAttrType = formControl.attr('type'); + for ( var i = 0, length = formControls.length; i < length; i++ ) + { + formControl = $( formControls[i] ); + formControlAttrType = formControl.attr( 'type' ); - if (!visibility(formControl) || !isEnabled(formControl)) { + if ( !visibility( formControl ) || !isEnabled( formControl ) ) + { return; } - validationKey = validationKeys[i].trim() || validationKeys[0].trim(); + validationKey = validationKeys[i].trim() || validationKeys[0].trim(); currentHasError = false; // formControl is textfield (text, mail, password) or textarea - if ((formControl.is('input') + if ( (formControl.is( 'input' ) && formControlAttrType != 'radio' && formControlAttrType != 'checkbox') - || formControl.is('textarea')) { - switch (validationKey) { + || formControl.is( 'textarea' ) ) + { + switch ( validationKey ) + { case 'text': - currentHasError = !validateText(formControl); + currentHasError = !validateText( formControl ); break; case 'mail': - currentHasError = !validateMail(formControl); + currentHasError = !validateMail( formControl ); break; case 'number': - currentHasError = !validateNumber(formControl); + currentHasError = !validateNumber( formControl ); break; case 'value': - currentHasError = !validateValue(formControl, $(elem).attr('data-plenty-validation-value')); + currentHasError = !validateValue( formControl, $( elem ).attr( 'data-plenty-validation-value' ) ); break; case 'none': @@ -231,112 +265,145 @@ break; default: - console.error('Form validation error: unknown validate property: "' + attrValidate + '"'); + console.error( 'Form validation error: unknown validate property: "' + attrValidate + '"' ); break; } - } else if (formControl.is('input') + } + else if ( formControl.is( 'input' ) && (formControlAttrType == 'radio' - || formControlAttrType == 'checkbox')) { + || formControlAttrType == 'checkbox') ) + { // validate radio buttons - group = formControl.attr('name'); - checked = wrappedForm.find('input[name="' + group + '"]:checked').length; + group = formControl.attr( 'name' ); + checked = $form.find( 'input[name="' + group + '"]:checked' ).length; - if (formControlAttrType == 'radio') { + if ( formControlAttrType == 'radio' ) + { checkedMin = 1; checkedMax = 1; - } else { - eval("var minMax = " + attrValidate); + } + 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')) { + } + 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'); + } + else + { + console.error( 'Form validation error: ' + $( elem ).prop( "tagName" ) + ' does not contain an form element' ); return; } - if (currentHasError) { + if ( currentHasError ) + { hasError = true; - missingFields.push(formControl); + missingFields.push( formControl ); - if(formControls.length > 1 ) { - formControl.addClass(errorClass); - wrappedForm.find('label[for="'+formControl.attr('id')+'"]').addClass(errorClass); - } else { - $(elem).addClass(errorClass); + 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' - wrappedForm.on('validationFailed', function() { - var distanceTop = 50; - var errorOffset = wrappedForm.find('.has-error').first().offset().top; - var scrollTarget = $('html, body'); + $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( wrappedForm.parents('.modal').length > 0 ) { - scrollTarget = wrappedForm.parents('.modal'); - } else if( wrappedForm.is('.modal') ) { - scrollTarget = wrappedForm; + 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({ + if ( errorOffset - distanceTop < window.pageYOffset || errorOffset > (window.pageYOffset + window.innerHeight) ) + { + $scrollTarget.animate( { scrollTop: errorOffset - distanceTop - }); + } ); } - }); + } ); - if ( hasError ) { + if ( hasError ) + { // remove error class on focus - wrappedForm.find('.has-error').each(function(i, elem) { - formControl = $(getFormControl(elem)); - formControl.on('focus click', function() { - formControl.removeClass( errorClass ); - wrappedForm.find('label[for="'+formControl.attr('id')+'"]').removeClass(errorClass); - $(elem).removeClass( errorClass ); - }); - }); - - wrappedForm.trigger('validationFailed', [missingFields]); + $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 = wrappedForm.attr('data-plenty-callback'); + var callback = $form.attr( 'data-plenty-callback' ); - if( !hasError && !!callback && callback != "submit" && typeof window[callback] == "function") { + if ( !hasError && !!callback && callback != "submit" && typeof window[callback] == "function" ) + { var fields = {}; - wrappedForm.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(); + $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); + window[callback]( fields ); return false; - } else { + } + else + { return !hasError; } } - }); + } ); /** * jQuery-Plugin to calling {{#crossLink "ValidationService/validate"}}ValidationService.validate{{/crossLink}} * on jQuery wrapped elements. * @return {boolean} */ - $.fn.validateForm = function() { + $.fn.validateForm = function() + { return pm.getInstance().ValidationService.validate( this ); }; @@ -344,50 +411,69 @@ * jQuery-Plugin to get the values of contained form elements. * @return {object} */ - $.fn.getFormValues = function() { + $.fn.getFormValues = function() + { - var form = this; + var form = this; var values = {}; - function inject( position, value ) { - var match = position.match(/^([^\[]+)(.*)/); - if( !!match[2] ) { - var exp = /\[([^\]]+)]/g; + 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[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 = {}; + for ( var i = children.length - 1; i >= 0; i-- ) + { + var val = {}; val[children[i]] = value; - value = val; + value = val; } - values = $.extend(true, values, value); - } else { + 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") { + 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()); + $( 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)); \ No newline at end of file +}( jQuery, PlentyFramework )); \ No newline at end of file diff --git a/test/helpers/responses/checkout.js b/test/helpers/responses/checkout.js index b164b43..ff84c53 100644 --- a/test/helpers/responses/checkout.js +++ b/test/helpers/responses/checkout.js @@ -48,7 +48,7 @@ Responses.GET.container_totals = { message: "" }, data: [ - '
    New Totals
    ' + '
    New Totals
    ' ] }; @@ -59,7 +59,7 @@ Responses.GET.category_content = { message: "" }, data: [ - '
    New category content
    ' + '
    New category content
    ' ] }; @@ -70,7 +70,7 @@ Responses.GET.container_basketpreviewlist = { message: "" }, data: [ - '
    New itemview content
    ' + '
    New itemview content
    ' ] }; diff --git a/test/spec/factories/CheckoutFactory.spec.js b/test/spec/factories/CheckoutFactory.spec.js index 258056c..fd602bd 100644 --- a/test/spec/factories/CheckoutFactory.spec.js +++ b/test/spec/factories/CheckoutFactory.spec.js @@ -94,8 +94,12 @@ describe("CheckoutFactory", function() { responseText: JSON.stringify(Responses.GET.container_totals) }); - testDirective = PlentyFramework.directive('[data-test]', function(i, elem) { - $(elem).addClass('test'); + testDirective = PlentyFramework.directive('Test', function() { + return { + init: function( $elem ) { + $elem.addClass('test'); + } + }; }); setFixtures('
    Old Totals
    '); @@ -114,7 +118,6 @@ describe("CheckoutFactory", function() { CheckoutFactory.reloadContainer('Totals'); expect( $('#testContainer').find('div') ).toHaveClass('test'); - expect( testDirective.elements ).toContain( $('#testContainer').find('div')[0] ); }); @@ -179,7 +182,6 @@ describe("CheckoutFactory", function() { CheckoutFactory.reloadCatContent(5); expect( $('#testContainer').find('div') ).toHaveClass('test'); - expect( testDirective.elements ).toContain( $('#testContainer').find('div')[0] ); }); @@ -240,7 +242,6 @@ describe("CheckoutFactory", function() { CheckoutFactory.reloadItemContainer('BasketPreviewList'); expect( $('#testContainer').find('div') ).toHaveClass('test'); - expect( testDirective.elements ).toContain( $('#testContainer').find('div')[0] ); }); diff --git a/tools/plentymarketsCMStools-migrate-0.9.1.js b/tools/plentymarketsCMStools-migrate-0.9.1.js new file mode 100644 index 0000000..d5d24bd --- /dev/null +++ b/tools/plentymarketsCMStools-migrate-0.9.1.js @@ -0,0 +1,235 @@ +(function( $, pm ) +{ + + var elementCache = []; + + PlentyFramework.getInstance().bindDirectives = function( rootElement ) + { + + var $rootElement = $( rootElement || 'html' ); + + if( $.inArray($rootElement[0], elementCache) >= 0 ) + { + return; + } + + elementCache.push( $rootElement[0] ); + + // #### COMMON ACTIONS + if ( $rootElement.find( 'body' ).length > 0 ) + { + pm.directives['MobileDropdown'].initMobileDropdown(); + pm.directives['UI'].initUIWindowEvents(); + } + + $rootElement.find( '[data-plenty="addBasketItemButton"]' ).each( function( i, button ) + { + + $( button ).click( function( e ) + { + pm.pushEvent( e ); + pm.directives['Basket'].addBasketItem( button ); + } ); + + } ); + + $rootElement.find( '[data-plenty="quantityInputButtonPlus"]' ).each( function( i, button ) + { + + $( button ).click( function( e ) + { + pm.pushEvent( e ); + pm.directives['Basket'].changeItemQuantity( button, 1 ); + } ); + + } ); + + $rootElement.find( '[data-plenty="quantityInputButtonMinus"]' ).each( function( i, button ) + { + + $( button ).click( function( e ) + { + pm.pushEvent( e ); + pm.directives['Basket'].changeItemQuantity( button, -1 ); + } ); + + } ); + + $rootElement.find( '[data-basket-item-id] [data-plenty="quantityInput"]' ).each( function( i, input ) + { + + $( input ).on( 'change', function( e ) + { + pm.pushEvent( e ); + var basketItemID = $(input).parents( '[data-basket-item-id]' ).attr( 'data-basket-item-id' ); + pm.directives['Basket'].setItemQuantity( basketItemID, input ); + } ); + + } ); + + $rootElement.find( 'form[data-plenty-checkform], form.PlentySubmitForm' ).each( function( i, form ) + { + + $( form ).submit( function( e ) + { + pm.pushEvent( e ); + return pm.directives['Validator'].validate( form, $( form ).attr( '[data-plenty-checkform]' ) || 'has-error' ); + } ); + + } ); + + $rootElement.find( 'a[data-plenty-opentab]' ).each( function( i, elem ) + { + + $( elem ).click( function( e ) + { + pm.pushEvent( e ); + var tabSelector = $( this ).attr( 'data-plenty-opentab' ); + tabSelector = ( tabSelector == 'href' ) ? $( this ).attr( 'href' ) : tabSelector; + pm.directives['Tab'].showTab( tabSelector ); + } ); + + } ); + + $rootElement.find( '[data-plenty-openremotetab]' ).each( function( i, elem ) + { + $( elem ).click( function( e ) + { + pm.pushEvent( e ); + var tabSelector = $( this ).attr( 'data-plenty-openremotetab' ); + var tabID = $( tabSelector ).attr( 'data-plenty-tab-id' ); + var groupID = $( tabSelector ).parents( '[data-plenty-remotetabs-id]' ).attr( 'data-plenty-remotetabs-id' ); + + pm.directives['Tab'].showRemoteTab( tabID, groupID ); + + return false; + } ); + + } ); + + $rootElement.find( '[data-plenty-remotetabs-id][data-plenty-tabpanel-labelledby]' ).each( function( i, elem ) + { + var tabID = $( elem ).attr( 'data-plenty-tabpanel-labelledby' ); + var groupID = $( elem ).attr( 'data-plenty-remotetabs-id' ); + pm.directives['Tab'].initRemoteTab( $( elem ), tabID, groupID ); + + } ); + + $rootElement.find( '[data-plenty="remoteTabs"]' ).each( function( i, elem ) + { + + var groupID = $( elem ).attr( 'data-plenty-remotetabs-id' ); + + $( elem ).find( '[data-plenty-tab-id]' ).each( function( i, trigger ) + { + var tabID = $( trigger ).attr( 'data-plenty-tab-id' ); + + pm.directives['Tab'].initRemoteLabel( $( trigger ).parent(), tabID, groupID ); + + $( trigger ).click( function( e ) + { + pm.pushEvent( e ); + pm.directives['Tab'].showRemoteTab( tabID, groupID ); + } ); + } ); + } ); + + $rootElement.find( '[data-plenty-link]' ).each( function( i, elem ) + { + + $( elem ).click( function( e ) + { + pm.pushEvent( e ); + var itemID = $( elem ).attr( 'data-plenty-link' ); + pm.directives['Redirect'].to( '[data-plenty-href="' + itemID + '"]' ) + } ); + + } ); + + $rootElement.find( '[data-plenty-checkout-href]' ).each( function( i, elem ) + { + + $( elem ).click( function( e ) + { + pm.pushEvent( e ); + pm.directives['Redirect'].toCheckoutTab( + $( elem ).attr( 'data-plenty-checkout-href' ) + ) + } ); + + } ); + + $rootElement.find( '[data-plenty-onenter]' ).each( function( i, elem ) + { + var onEnter = $( elem ).attr( 'data-plenty-onenter' ); + var callback = typeof window[onEnter] === 'function' ? window[onEnter] : (new Function( 'return ' + onEnter )); + $( elem ).on( 'keypress', function( e ) + { + pm.pushEvent( e ); + if ( e.which === 13 && !!callback && typeof callback === "function" ) + { + callback.call(); + } + } ); + } ); + + $rootElement.find( '[data-plenty-enable]' ).each( function( i, elem ) + { + pm.directives['MobileDropdown'].openDropdown( elem, $( elem ).attr( 'data-plenty-enable' ) ); + } ); + + $rootElement.find( '[data-plenty="contentpageSlider"]' ).each( function( i, elem ) + { + pm.directives['UI'].addContentPageSlider( elem ); + } ); + + $rootElement.find( '[data-plenty-equal]' ).each( function( i, elem ) + { + pm.directives['UI'].equalHeight( elem, $(elem ).attr("data-plenty-equal") ); + } ); + + $rootElement.find( '[data-plenty="toTop"]' ).each( function( i, elem ) + { + pm.directives['UI'].initToTop( elem ); + } ); + + $rootElement.find( 'img[data-plenty-lazyload]' ).each( function( i, elem ) + { + pm.directives['UI'].initLazyload( elem, $( elem ).attr( "data-plenty-lazyload" ) ); + } ); + + $rootElement.find( '[data-plenty="openCloseToggle"]' ).each( function( i, elem ) + { + $( elem ).click( function(e) { + pm.pushEvent( e ); + pm.directives['UI'].toggleHideShow( elem ); + } ); + } ); + + $rootElement.find( '[data-plenty-slidetoggle]' ).each( function( i, elem ) + { + pm.directives['UI'].initSlideToggle( elem, $( elem ).attr( '[data-plenty-slidetoggle]' ) ); + } ); + + $rootElement.find( '[data-plenty-social]' ).each( function( i, elem ) + { + pm.directives['UI'].toggleSocialShare( elem, $( elem ).attr( '[data-plenty-social]' ) ); + } ); + + $rootElement.find( '[data-plenty-toggle]' ).each( function( i, elem ) + { + + $( elem ).click( function( e ) + { + pm.pushEvent( e ); + var dataString = $( elem ).attr( 'data-plenty-toggle' ); + var data = (new Function( "return " + dataString )).call(); + var media = !!data.media ? data.media.replace( ' ', ',' ) : ''; + + pm.directives['UI'].toggleClass( data.class, data.target, media ); + } ); + + } ); + }; + +})( jQuery, PlentyFramework ); \ No newline at end of file