/** * @class de_epages.design.inc.customizeButtonStyle * */ /* * @copyright © Copyright 2006-2012, epages GmbH, All Rights Reserved. * * @module de_epages/design/inc/customizebuttonstyle * * @revision $Revision: 1.12 $ */ /*jslint nomen:true*/ /*global define*/ define("de_epages/design/inc/customizebuttonstyle", [ "jquery", "ep", "util/json", "$tmpl!de_epages/design/inc/customizebuttonstyle", "de_epages/mediagallery/ui/filemanagerdialog", "ep/ui/dialog", "ep/ui/slider", "ep/ui/colorpicker" ], function ($, ep, json, tmplButtonStyle, de_epages) { 'use strict'; // Context for the plugin: var constraints = { border: { radius: { min: 0, max: 40 } }, padding: { width: { min: 5, max: 25 }, height: { min: 0, max: 20 } }, font: { size: { min: 10, max: 20 } } }, // See *constant CUSTOMIZABLE_BUTTONS* in /API/Constants.pm in Design and Order Cartridge. i = 0, // We will reuse this in a couple of *for*-loops. dialogNode, // After *customizeButtonStyle.buildDialog()* the *dialog*'s DOM node is to be found here. contentNode, // Content DOM node for *dialogNode*. callback = null, // Function to be executed on apply. userData = {}, // At any point after *customizeButtonStyle.buildDialog()* the user's input is stored here. // *saveUserData()* will make use of this. startData = null, // This is where we store the initial data set for *customizeButtonStyle.buildDialog()*. // This is especially, if the user "applies" changes and then reopens the dialog. (No page reload needed.) saveDataNames = [], // Names of properties of the data object which are to be send to the server. saveUserData = function () { var _saveDataName, data = $.extend({ ViewAction: 'JSONViewResponse', ChangeAction: 'JSONSaveCustomizableButtons' }, userData); // Default *ViewAction* and *ChangeAction* can be overridden by passing respective properties // within the *data* argument of *customizeButtonStyle.buildDialog()*. for (i = 0; i < saveDataNames.length; i = i + 1) { _saveDataName = saveDataNames[i]; data[_saveDataName] = json.stringify(userData[_saveDataName]); // The server needs *JSON* attributes to be passed as a string. } // Let us ajax the *data* over to the server. $.ajax({ url: '?', dataType: 'json', type: 'POST', data: data }).done(function (data) { // The server passes us back the updated data set. for (i = 0; i < saveDataNames.length; i = i + 1) { _saveDataName = saveDataNames[i]; startData[_saveDataName] = $.extend(true, {}, data[_saveDataName]); } dialogNode.uiDialog('close'); if ($.isFunction(callback)) { callback(); } }); }; // The actual plugin: return { /** * Initialize and open dialog to configure buttons on the SF. * * The `de_epages.design.inc.customizeButtonStyle.init()` creates dialog to configure buttons (e.g. the basket button). After calling `de_epages.design.inc.customizeButtonStyle.init()`, * you should call `de_epages.design.inc.customizeButtonStyle.openDialog()` to actually open the dialog. (See example.) * * ### Examples * Init dialog on hover and open dialog on click. * * JavaScript: * * var customizeButtonsLink = $('#ep-CustomizeButtonStyle'); * // The dialog is built on *mouseenter* and opened on *click*. * customizeButtonsLink * .on('mouseenter', function (event) { * $.ready('de_epages.design.inc.customizeButtonStyle', function ($) { * de_epages.design.inc.customizeButtonStyle.init( * { * CustomizableBasketButton : '<button name="AddToBasket" type="submit"><span class="ep-sprite ep-sprite-s" style="background-image: url(/WebRoot/StoreTypes/6.15.0/Store/SF/Icon/WireframeBlack/ico_s_basket.png)"></span>Add to basket</button>', * BubbleHelpSource : '/WebRoot/StoreTypes/6.15.0/Store/BO/icons/ico_s_bubblehelp.png' * }, * { * title : 'AdjustAddToBasketButton', * restoreDefaults : 'RevertToDefault', * replaceButtonWithCustomImage : 'UploadOwnImage', * restoreDefaultImage : 'RemoveImage', * borderColor : 'BorderColor', * borderRadius : 'BorderRadius', * backgroundGradient : 'ColorGradient', * fontColor : 'FontColor', * fontSize : 'FontSize', * infoWarning : 'OptionNotAvailableInAllBrowsers', * apply : 'Apply', * cancel : 'Cancel', * width : 'Width', * height : 'Height' * }, * { * ChangeAction: 'JSONSaveCustomizableBasketButton', * ObjectID : epConfig.siteId, * * * CustomizableBasketButton : { * Font : { * Size : 13, * Color : '#000' * }, * Padding : { * Height : 1, * Width : 10, * }, * Gradient : { * Color1 : '#F9F9F9', * Color2 : '#E2E2E2' * }, * Border : { * Radius : 3, * Color : '#AAA' * }, * Image : { * Path : null, * Height : null, * Width : null * } * }, * * }, * ['CustomizableBasketButton'], * { * ChangeAction: 'JSONSaveCustomizableBasketButton', * ObjectID : epConfig.siteId, * CustomizableBasketButton : { * * Font : { * Size : 13, * Color : '#000' * }, * Padding : { * Height : 1, * Width : 10, * }, * Gradient : { * Color1 : '#F9F9F9', * Color2 : '#E2E2E2' * }, * Border : { * Radius : 3, * Color : '#AAA' * }, * Image : { * Path : null, * Height : null, * Width : null * } * * } * } * ); * }); * }) * .on('click', function (event) { * $.ready({script : 'builtCustomizeButtonsDialog'}, function ($) { * de_epages.design.inc.customizeButtonStyle.openDialog(); * }) * }); * * HTML: * * <button id="ep-CustomizeButtonStyle">Open Dialog</button> * * * ### Dependencies * * + `jQuery.json` * + `jQuery.tmpl` * + `de_epages` * + `ep` * + `ep.ui.dialog` * + `ep.ui.slider` * + `ep.ui.colorpicker` * + `de_epages.mediagallery.ui.filemanagerdialog` * * @param {Object} [options] A map of additional options pass to the function. * @param {Object} options.defaults A map with keys defining the default buttons as html strings. * @param {Object} options.wording A map with keys defining the wording. (See example for possible keys.) * @param {Object} options.data Button data for init. * @param {Array} options.dataNames Array of names of data properties to pass to the server on apply. (E.g. "CustomizableBasketButton") * @param {Object} options.defaultValues Default button data. Used for restoring defaults in the dialog. * * @method init * @static * @member de_epages.design.inc.customizeButtonStyle * * @since 6.15.0 */ init: function (defaults, wording, data, dataNames, defaultValues, _callback) { callback = _callback; // If the *dialogNode* already exists, we do not have to to anything. if (dialogNode) { return; } this.buildContent(defaults, wording, data, dataNames, defaultValues); this.buildDialog(defaults, wording, data, dataNames, defaultValues); }, // Function to "build" the *dialogNode*, which will later be "opened" by *customizeButtonStyle.openDialog()*. buildContent: function (defaults, wording, data, dataNames, defaultValues) { var templateData = [], // Each array entry represents the template data for one tab. renderedTemplate, // Stores the rendered template for *contentNode*. _dataName = '', // Stores the "current" value of *dataNames[i]* inside of a *for*-loop. _tab, // Stores the DOM elements (see template) of "current" tab inside of a *for*-loop. i = 0, imageSrc; // We will reuse this in a couple of *for*-loops. // Set up *startData*, *userData*, *data* and *saveDataNames*. if (startData === null) { startData = $.extend(true, {}, data); } else { data = startData; } userData = $.extend(true, {}, data); saveDataNames = dataNames; // Add *tabId*s for jQuery *tabs*, if needed. (See template.) for (i = 0; i < dataNames.length; i = i + 1) { _dataName = dataNames[i]; templateData[i] = $.extend(data[_dataName], { 'tabId': 'ep-' + _dataName + '-tab' }); } // Render template. renderedTemplate = tmplButtonStyle(templateData, { 'wording': wording, 'constraints': constraints, 'bubbleHelpSource': defaults.BubbleHelpSource }); /* At this point we do not use tabs. could be used for further implementation, later on. // Make *tabs* if needed. if (renderedTemplate.length > 1) { var $div = $('<div>'), $ul = $('<ul>'); // TODO // Weird work-around, because templating with jQuery('<a href="${blabla}">') did not work in firefox. // Firefox html encoded the curly brackets inside the double quotes, so the templating engine did not recognize the insertion of a value. $.each(dataNames, function (index, value) { $ul.append( $('<li>').append( $('<a>').attr('href', '#ep-'+dataNames[index]+'-tab').text(dataNames[index] === 'CustomizableBasketButton' ? wording.basketButton : wording.otherButtons))); }); contentNode = ep('<div>').append($ul, renderedTemplate).tabs({selected : 0}); // My chrome otherwise opens on the second tab first. } else { */ contentNode = ep('<div>').append(renderedTemplate); //} // Now let us finish every single tab. for (i = 0; i < dataNames.length; i = i + 1) { // Init *_dataName* and *_tab*. _dataName = dataNames[i]; _tab = $.extend({}, $(renderedTemplate[i]).tmplItem('elements'), { defaultButton: $(defaults[_dataName]), customImage: $('<img>') }); // Add and customize preview. _tab.buttonPreview.append(_tab.defaultButton.css({ 'padding': data[_dataName].Padding.Height + 'px ' + data[_dataName].Padding.Width + 'px', 'border-radius': data[_dataName].Border.Radius + 'px', 'border-color': data[_dataName].Border.Color, 'font-size': data[_dataName].Font.Size + 'px', 'color': data[_dataName].Font.Color, 'text-shadow': '0 0 0 transparent', 'line-height': '140%' }).css('background', '-webkit-gradient(linear, 0% 0%, 0% 100%, from(' + data[_dataName].Gradient.Color1 + '), to(' + data[_dataName].Gradient.Color2 + '))').css('background', '-moz-linear-gradient(center top, ' + data[_dataName].Gradient.Color1 + ' 10%, ' + data[_dataName].Gradient.Color2 + ' 90%)')); // Create *slider*(s). _tab.borderRadiusSlider = ep(_tab.borderRadiusSlider).uiSlider({ slide: $.proxy(function (event, ui) { userData[this.dataName].Border.Radius = ui.value; this.defaultButton.css('border-radius', ui.value + 'px'); }, { defaultButton: _tab.defaultButton, dataName: _dataName }) }); _tab.widthSlider = ep(_tab.widthSlider).uiSlider({ slide: $.proxy(function (event, ui) { userData[this.dataName].Padding.Width = ui.value; this.defaultButton.css({ 'padding-left': ui.value + 'px', 'padding-right': ui.value + 'px' }); }, { defaultButton: _tab.defaultButton, dataName: _dataName }) }); _tab.heightSlider = ep(_tab.heightSlider).uiSlider({ slide: $.proxy(function (event, ui) { userData[this.dataName].Padding.Height = ui.value; this.defaultButton.css({ 'padding-top': ui.value + 'px', 'padding-bottom': ui.value + 'px' }); }, { defaultButton: _tab.defaultButton, dataName: _dataName }) }); _tab.fontSizeSlider = ep(_tab.fontSizeSlider).uiSlider({ slide: $.proxy(function (event, ui) { userData[this.dataName].Font.Size = ui.value; this.defaultButton.css('font-size', ui.value); }, { defaultButton: _tab.defaultButton, dataName: _dataName }) }); // Create *colorpicker*(s). _tab.borderColorpicker = ep(_tab.borderColorpicker).on('colorSelect', function (event, color) { $(this).css('background', color); }).on('colorSelect', $.proxy(function (event, color) { userData[this.dataName].Border.Color = color; this.defaultButton.css('border-color', color); }, { defaultButton: _tab.defaultButton, dataName: _dataName })).uiColorpicker({ color: data[_dataName].Border.Color }); _tab.gradientColorpicker1 = ep(_tab.gradientColorpicker1).on('colorSelect', function (event, color) { $(this).css('background', color); }).on('colorSelect', $.proxy(function (event, color) { userData[this.dataName].Gradient.Color1 = color; this.gradientCheckbox.trigger('change'); }, { defaultButton: _tab.defaultButton, dataName: _dataName, gradientCheckbox: _tab.gradientCheckbox })).uiColorpicker({ color: data[_dataName].Gradient.Color1 }); _tab.gradientCheckbox = ep(_tab.gradientCheckbox).uiInput().on('change', $.proxy(function (event) { var color1 = userData[this.dataName].Gradient.Color1, color2, hsb; if (this.gradientCheckbox.is(':checked')) { // Calculate second gradient color. hsb = ep.color.stringToHsb(color1); hsb.b = Math.max(0, hsb.b - 9); color2 = '#' + ep.color.hsbToHex(hsb); userData[this.dataName].Gradient.Color2 = color2; } else { // No gradient, i.e. Color1 = Color2. color2 = userData[this.dataName].Gradient.Color2 = userData[this.dataName].Gradient.Color1; } this.defaultButton.css('background', '-webkit-gradient(linear, 0% 0%, 0% 100%, from(' + color1 + '), to(' + color2 + '))'); this.defaultButton.css('background', '-moz-linear-gradient(center top, ' + color1 + ' 10%, ' + color2 + ' 90%)'); }, { defaultButton: _tab.defaultButton, dataName: _dataName, gradientCheckbox: _tab.gradientCheckbox })).attr('checked', userData[_dataName].Gradient.Color1 !== userData[_dataName].Gradient.Color2); _tab.fontColorpicker = ep(_tab.fontColorpicker).on('colorSelect', function (event, color) { $(this).css('background', color); }).on('colorSelect', $.proxy(function (event, color) { userData[this.dataName].Font.Color = color; this.defaultButton.css('color', color); }, { defaultButton: _tab.defaultButton, dataName: _dataName })).uiColorpicker({ color: data[_dataName].Font.Color, transparent: false }); // Create *tooltip*(s). _tab.tooltip = ep(_tab.tooltip).uiTooltip(); // Create image upload and image removal options. if (data[_dataName].hasOwnProperty('Image')) { imageSrc = data[_dataName].Image.Path; _tab.customImage.appendTo(_tab.buttonPreview).hide(); if (imageSrc) { _tab.customImage.attr('src', imageSrc).attr('alt', imageSrc).show(); _tab.defaultButton.hide(); _tab.bottom.addClass('Disabled'); _tab.bottom.find('.ep-uiSlider-wrap').each(function () { ep(this).uiSlider('option', 'disabled', true); }); _tab.bottom.find('button').each(function () { $(this).attr('disabled', true); }); _tab.gradientCheckbox.attr('disabled', true); } else { _tab.restoreDefaultImage.addClass('Disabled'); } _tab.restoreDefaultImage.on('click', $.proxy(function () { if (!this.tab.restoreDefaultImage.hasClass('Disabled')) { userData[this.dataName].Image.Path = ''; this.tab.customImage.hide(); this.tab.defaultButton.show(); this.tab.bottom.removeClass('Disabled'); this.tab.bottom.find('.ep-uiSlider-wrap').each(function () { ep(this).uiSlider('option', 'disabled', false); }); this.tab.bottom.find('button').each(function () { $(this).attr('disabled', false); }); this.tab.bottom.find('input[type="checkbox"]').each(function () { $(this).attr('disabled', false); }); this.tab.gradientCheckbox.attr('disabled', false); this.tab.restoreDefaultImage.addClass('Disabled'); } }, { dataName: _dataName, tab: _tab })); // Init filemanagerdialog. de_epages(_tab.uploadCustomImage).mediagalleryUiFilemanagerdialog({ filemanager: { selectable: 'image/' }, dialog: { buttons: [{ text: wording.apply, click: $.proxy(function () { var imageSrc = de_epages(this.tab.uploadCustomImage).mediagalleryUiFilemanagerdialog('getSelectedElements')[0].get('fullpath'); de_epages(this.tab.uploadCustomImage).mediagalleryUiFilemanagerdialog('close'); this.tab.customImage.attr('src', imageSrc).attr('alt', imageSrc).show(); this.tab.defaultButton.hide(); userData[this.dataName].Image.Path = imageSrc; this.tab.bottom.addClass('Disabled'); this.tab.bottom.find('.ep-uiSlider-wrap').each(function () { ep(this).uiSlider('option', 'disabled', true); }); this.tab.bottom.find('button').each(function () { $(this).attr('disabled', true); }); this.tab.gradientCheckbox.attr('disabled', true); this.tab.restoreDefaultImage.removeClass('Disabled'); this.tab.restoreDefaultImage.addClass('CursorPointer'); }, { dataName: _dataName, tab: _tab }) }] } }); } } }, // Init *dialogNode*. buildDialog: function (defaults, wording, data, dataNames, defaultValues) { var self = this; dialogNode = ep('<div>').append(contentNode).uiDialog({ width: '640', title: wording.title, buttons: [{ text: wording.restoreDefaults, click: function () { startData = null; contentNode.remove(); self.buildContent(defaults, wording, defaultValues, dataNames, defaultValues); dialogNode.append(contentNode); } }, { text: wording.cancel, click: function () { dialogNode.uiDialog('close'); } }, { text: wording.apply, click: saveUserData }], modal: true, autoOpen: false, close: function (event) { dialogNode.uiDialog('destroy'); dialogNode.remove(); dialogNode = null; } }); // Position buttons. dialogNode.parent().find('div.ui-dialog-buttonset').css('float', 'none').find('button').each(function (index) { if (index > 0) { $(this).css('float', 'right'); } }); $.provide('builtCustomizeButtonsDialog', function () {}); }, // Function to "open" the *dialogNode*, which was "built" by *customizeButtonStyle.buildDialog()*. openDialog: function () { dialogNode.uiDialog('open'); } }; });