Spaces:
Sleeping
Sleeping
| Ext.namespace('Ext.ux'); | |
| /** | |
| * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3 | |
| * | |
| * @author Thorsten Suckow-Homberg <ts@siteartwork.de> | |
| * @url http://www.siteartwork.de/wizardcomponent | |
| */ | |
| /** | |
| * @class Ext.ux.Wiz | |
| * @extends Ext.Window | |
| * | |
| * A specific {@link Ext.Window} that models a wizard component. | |
| * A wizard is basically a dialog that guides a user through various steps | |
| * where he has to fill out form-data. | |
| * A {@link Ext.ux.Wiz}-component consists typically of a {@link Ext.ux.Wiz.Header} | |
| * and window-buttons ({@link Ext.Button}) which are linked to the {@link Ext.ux.Wiz.Card}s | |
| * which themself represent the forms the user has to fill out. | |
| * | |
| * In order to switch between the cards in the wizard, you need the {@link Ext.ux.layout.CardLayout}, | |
| * which will check if an active-item can be hidden, before the requested new item will be set to | |
| * 'active', i.e. shown. This is needed since the wizard may not allow a card to be hidden, if | |
| * the input entered by the user was not valid. You can get this custom layout at | |
| * {@link http://www.siteartwork.de/cardlayout}. | |
| * | |
| * Note: | |
| * When data has been collected and teh "onFinish" listener triggers an AJAX-request, | |
| * you should call the "switchDialogState" method so that the the dialog shows a loadmask. | |
| * Once the requests finishes, call "switchDialogState" again, specially before any call | |
| * to the "close" method of this component, otherwise the "closable" property of this | |
| * instance might prevent a "close" operation for this dialog. | |
| * | |
| * | |
| * @constructor | |
| * @param {Object} config The config object | |
| */ | |
| Ext.ux.Wiz = Ext.extend(Ext.Window, { | |
| /** | |
| * @cfg {Object} An object containing the messages for the {@link Ext.LoadMask} | |
| * covering the card-panel on request, whereas the property identifies the | |
| * msg-text to show, and the value is the message text itself. Defaults to | |
| <pre><code> | |
| { | |
| default : _('ID_SAVING') | |
| } | |
| </code></pre> | |
| * | |
| * Depending on the contexts the loadMask has to be shown in (using the method | |
| * showLoadMask of this class), the object can be configure to hold | |
| * various messages. | |
| <pre><code> | |
| this.loadMaskConfig = { | |
| default : _('ID_SAVING'), | |
| validating : 'Please wait, validating input...', | |
| }; | |
| // loadMask will be shown, displaying the message 'Please wait, validating input...' | |
| this.showLoadMask(true, 'validating'); | |
| </code></pre> | |
| */ | |
| loadMaskConfig : { | |
| 'default' : _('ID_SAVING') | |
| }, | |
| /** | |
| * @cfg {Number} height The height of the dialog. Defaults to "400". | |
| */ | |
| height : 400, | |
| /** | |
| * @cfg {Number} width The width of the dialog. Defaults to "540". | |
| */ | |
| width : 540, | |
| /** | |
| * @cfg {Boolean} closable Wether the dialog is closable. Defaults to "true". | |
| * This property will be changed by the "switchDialogState"-method, which will | |
| * enable/disable controls based on the passed argument. Thus, this config property | |
| * serves two purposes: Tell the init config to render a "close"-tool, and create a | |
| * "beforeclose"-listener which will either return true or false, indicating if the | |
| * dialog may be closed. | |
| */ | |
| closable : true, | |
| /** | |
| * @cfg {Boolean} resizable Wether the dialog is resizable. Defaults to "false". | |
| */ | |
| resizable : false, | |
| /** | |
| * @cfg {Boolean} resizable Wether the dialog is modal. Defaults to "true". | |
| */ | |
| modal : true, | |
| /** | |
| * @cfg {Array} cards A numeric array with the configured {@link Ext.ux.Wiz.Card}s. | |
| * The index of the cards in the array represent the order in which they get displayed | |
| * in the wizard (i.e. card at index 0 gets displayed in the first step, card at index 1 gets | |
| * displayed in the second step and so on). | |
| */ | |
| cards : null, | |
| /** | |
| * @cfg {String} previousButtonText The text to render the previous-button with. | |
| * Defaults to "< Back" (< Back) | |
| */ | |
| previousButtonText : _('ID_PREVIUS'), | |
| /** | |
| * @cfg {String} nextButtonText The text to render the next-button with. | |
| * Defaults to "Next >" (Next >) | |
| */ | |
| nextButtonText : _('ID_NEXT'), | |
| /** | |
| * @cfg {String} cancelButtonText The text to render the cancel-button with. | |
| * Defaults to "Cancel" | |
| */ | |
| cancelButtonText : _('ID_CANCEL'), | |
| /** | |
| * @cfg {String} finishButtonText The text to render the next-button with when the last | |
| * step of the wizard is reached. Defaults to "Finish" | |
| */ | |
| finishButtonText : _('ID_FINISH'), | |
| /** | |
| * @cfg {Object} headerConfig A config-object to use with {@link Ext.ux.Wiz.Header}. | |
| * If not present, it defaults to an empty object. | |
| */ | |
| headerConfig : {}, | |
| /** | |
| * @cfg {Object} cardPanelConfig A config-object to use with {@link Ext.Panel}, which | |
| * represents the card-panel in this dialog. | |
| * If not present, it defaults to an empty object | |
| */ | |
| cardPanelConfig : {}, | |
| /** | |
| * @param {Ext.Button} The window-button for paging to the previous card. | |
| * @private | |
| */ | |
| previousButton : null, | |
| /** | |
| * @param {Ext.Button} The window-button for paging to the next card. When the | |
| * last card is reached, the event fired by and the text rendered to this button | |
| * will change. | |
| * @private | |
| */ | |
| nextButton : null, | |
| /** | |
| * @param {Ext.Button} The window-button for canceling the wizard. The event | |
| * fired by this button will usually close the dialog. | |
| * @private | |
| */ | |
| cancelButton : null, | |
| /** | |
| * @param {Ex.Panel} The card-panel that holds the various wizard cards | |
| * ({@link Ext.ux.Wiz.Card}). The card-panel itself uses the custom | |
| * {@link Ext.ux.layout.CardLayout}, which needs to be accessible by this class. | |
| * You can get it at {@link http://www.siteartwork.de/cardlayout}. | |
| * @private | |
| */ | |
| cardPanel : null, | |
| /** | |
| * @param {Number} currentCard The current {@link Ext.ux.Wiz.Card} displayed. | |
| * Defaults to -1. | |
| * @private | |
| */ | |
| currentCard : -1, | |
| /** | |
| * @param {Ext.ux.Wiz.Header} The header-panel of the wizard. | |
| * @private | |
| */ | |
| headPanel : null, | |
| /** | |
| * @param {Number} cardCount Helper for storing the number of cards used | |
| * by this wizard. Defaults to 0 (inherits "cards.length" later on). | |
| * @private | |
| */ | |
| cardCount : 0, | |
| /** | |
| * Inits this component with the specified config-properties and automatically | |
| * creates its components. | |
| */ | |
| initComponent : function() | |
| { | |
| this.initButtons(); | |
| this.initPanels(); | |
| var title = this.title || this.headerConfig.title; | |
| title = title || ""; | |
| Ext.apply(this, { | |
| title : title, | |
| layout : 'border', | |
| cardCount : this.cards.length, | |
| buttons : [ | |
| this.previousButton, | |
| this.nextButton, | |
| this.cancelButton | |
| ], | |
| items : [ | |
| this.headPanel, | |
| this.cardPanel | |
| ] | |
| }); | |
| this.addEvents( | |
| /** | |
| * @event cancel | |
| * Fires after the cancel-button has been clicked. | |
| * @param {Ext.ux.Wiz} this | |
| */ | |
| 'cancel', | |
| /** | |
| * @event finish | |
| * Fires after the last card was reached in the wizard and the | |
| * next/finish-button has been clicked. | |
| * @param {Ext.ux.Wiz} this | |
| * @param {Object} data The collected data of the cards, whereas | |
| * the index is the id of the card and the specific values | |
| * are objects with key/value pairs in the form formElementName : value | |
| */ | |
| 'finish' | |
| ); | |
| Ext.ux.Wiz.superclass.initComponent.call(this); | |
| }, | |
| // -------- helper | |
| /** | |
| * Returns the form-data of all cards in this wizard. The first index is the | |
| * id of the card in this wizard, | |
| * and the values are objects containing key/value pairs in the form of | |
| * fieldName : fieldValue. | |
| * | |
| * @return {Array} | |
| */ | |
| getWizardData : function() | |
| { | |
| var formValues = {}; | |
| var cards = this.cards; | |
| for (var i = 0, len = cards.length; i < len; i++) { | |
| if (cards[i].form) { | |
| formValues[cards[i].id] = cards[i].form.getValues(false); | |
| } else { | |
| formValues[cards[i].id] = {}; | |
| } | |
| } | |
| return formValues; | |
| }, | |
| /** | |
| * Switches the state of this wizard between disabled/enabled. | |
| * A disabled dialog will have a {@link Ext.LoadMask} covering the card-panel | |
| * to prevent user input, and the buttons will be rendered disabled/enabled. | |
| * If the dialog is closable, the close-tool will be masked, too, and the dialog will not | |
| * be closable by clicking the "close" tool. | |
| * | |
| * @param {Boolean} enabled "false" to prevent user input and mask the elements, | |
| * otherwise true. | |
| * @param {String} type The type of msg for the {@Ext.LoadMask} covering | |
| * the cardPanel, as defined in the cfg property "loadMaskConfig" | |
| */ | |
| switchDialogState : function(enabled, type) | |
| { | |
| this.showLoadMask(!enabled, type); | |
| this.previousButton.setDisabled(!enabled); | |
| this.nextButton.setDisabled(!enabled); | |
| this.cancelButton.setDisabled(!enabled); | |
| var ct = this.tools['close']; | |
| if (ct) { | |
| switch (enabled) { | |
| case true: | |
| this.tools['close'].unmask(); | |
| break; | |
| default: | |
| this.tools['close'].mask(); | |
| break; | |
| } | |
| } | |
| this.closable = enabled; | |
| }, | |
| /** | |
| * Shows the load mask for this wizard. By default, the cardPanel's body | |
| * will be masked. | |
| * | |
| * @param {Boolean} show true to show the load mask, otherwise false. | |
| * @param {String} type The type of message for the {@Ext.LoadMask} covering | |
| * the cardPanel, as defined in the cfg property "loadMaskConfig" | |
| */ | |
| showLoadMask : function(show, type) | |
| { | |
| if (!type) { | |
| type = 'default'; | |
| } | |
| if (show) { | |
| if (this.loadMask == null) { | |
| this.loadMask = new Ext.LoadMask(this.body); | |
| } | |
| this.loadMask.msg = this.loadMaskConfig[type]; | |
| this.loadMask.show(); | |
| } else { | |
| if (this.loadMask) { | |
| this.loadMask.hide(); | |
| } | |
| } | |
| }, | |
| /** | |
| * Inits the listener for the various {@link Ext.ux.Wiz.Card}s used | |
| * by this component. | |
| */ | |
| initEvents : function() | |
| { | |
| Ext.ux.Wiz.superclass.initEvents.call(this); | |
| this.on('beforeclose', this.onBeforeClose, this); | |
| }, | |
| /** | |
| * Creates the head- and the card-panel. | |
| * Be sure to have the custom {@link Ext.ux.layout.CardLayout} available | |
| * in order to make the card-panel work as expected by this component | |
| * ({@link http://www.siteartwork.de/cardlayout}). | |
| */ | |
| initPanels : function() | |
| { | |
| var cards = this.cards; | |
| var cardPanelConfig = this.cardPanelConfig; | |
| Ext.apply(this.headerConfig, { | |
| steps : cards.length | |
| }); | |
| this.headPanel = new Ext.ux.Wiz.Header(this.headerConfig); | |
| Ext.apply(cardPanelConfig, { | |
| layout : new Ext.ux.layout.CardLayout(), | |
| items : cards | |
| }); | |
| Ext.applyIf(cardPanelConfig, { | |
| region : 'center', | |
| border : false, | |
| activeItem : 0 | |
| }); | |
| var cards = this.cards; | |
| for (var i = 0, len = cards.length; i < len; i++) { | |
| cards[i].on('show', this.onCardShow, this); | |
| cards[i].on('hide', this.onCardHide, this); | |
| cards[i].on('clientvalidation', this.onClientValidation, this); | |
| } | |
| this.cardPanel = new Ext.Panel(cardPanelConfig); | |
| }, | |
| /** | |
| * Creates the instances for the the window buttons. | |
| */ | |
| initButtons : function() | |
| { | |
| this.previousButton = new Ext.Button({ | |
| text : this.previousButtonText, | |
| disabled : true, | |
| minWidth : 75, | |
| handler : this.onPreviousClick, | |
| scope : this | |
| }); | |
| this.nextButton = new Ext.Button({ | |
| text : this.nextButtonText, | |
| minWidth : 75, | |
| handler : this.onNextClick, | |
| scope : this | |
| }); | |
| this.cancelButton = new Ext.Button({ | |
| text : this.cancelButtonText, | |
| handler : this.onCancelClick, | |
| scope : this, | |
| minWidth : 75 | |
| }); | |
| }, | |
| // -------- listeners | |
| /** | |
| * Listener for the beforeclose event. | |
| * This listener will return true or false based on the "closable" | |
| * property by this component. This property will be changed by the "switchDialogState" | |
| * method, indicating if there is currently any process running that should prevent | |
| * this dialog from being closed. | |
| * | |
| * @param {Ext.Panel} panel The panel being closed | |
| * | |
| * @return {Boolean} | |
| */ | |
| onBeforeClose : function(panel) | |
| { | |
| return this.closable; | |
| }, | |
| /** | |
| * By default, the card firing this event monitors user input in a frequent | |
| * interval and fires the 'clientvalidation'-event along with it. This listener | |
| * will enable/disable the next/finish-button in accordance with it, based upon | |
| * the parameter isValid. isValid" will be set by the form validation and depends | |
| * on the validators you are using for the different input-elemnts in your form. | |
| * If the card does not contain any forms, this listener will never be called by the | |
| * card itself. | |
| * | |
| * @param {Ext.ux.Wiz.Card} The card that triggered the event. | |
| * @param {Boolean} isValid "true", if the user input was valid, otherwise | |
| * "false" | |
| */ | |
| onClientValidation : function(card, isValid) | |
| { | |
| if (!isValid) { | |
| this.nextButton.setDisabled(true); | |
| } else { | |
| this.nextButton.setDisabled(false); | |
| } | |
| }, | |
| /** | |
| * This will render the "next" button as disabled since the bindHandler's delay | |
| * of the next card to show might be lagging on slower systems | |
| * | |
| */ | |
| onCardHide : function(card) | |
| { | |
| if (this.cardPanel.layout.activeItem.id === card.id) { | |
| this.nextButton.setDisabled(true); | |
| } | |
| }, | |
| /** | |
| * Listener for the "show" event of the card that gets shown in the card-panel. | |
| * Renders the next/previous buttons based on the position of the card in the wizard | |
| * and updates the head-panel accordingly. | |
| * | |
| * @param {Ext.ux.Wiz.Card} The card being shown. | |
| */ | |
| onCardShow : function(card) | |
| { | |
| var parent = card.ownerCt; | |
| var items = parent.items; | |
| for (var i = 0, len = items.length; i < len; i++) { | |
| if (items.get(i).id == card.id) { | |
| break; | |
| } | |
| } | |
| this.currentCard = i; | |
| this.headPanel.updateStep(i, card.title); | |
| if (i == len-1) { | |
| this.nextButton.setText(this.finishButtonText); | |
| } else { | |
| this.nextButton.setText(this.nextButtonText); | |
| } | |
| if (card.isValid()) { | |
| this.nextButton.setDisabled(false); | |
| } | |
| if (i == 0) { | |
| this.previousButton.setDisabled(true); | |
| } else { | |
| this.previousButton.setDisabled(false); | |
| } | |
| }, | |
| /** | |
| * Fires the 'cancel'-event. Closes this dialog if the return value of the | |
| * listeners does not equal to "false". | |
| */ | |
| onCancelClick : function() | |
| { | |
| if (this.fireEvent('cancel', this) !== false) { | |
| this.close(); | |
| } | |
| }, | |
| /** | |
| * Fires the 'finish'-event. Closes this dialog if the return value of the | |
| * listeners does not equal to "false". | |
| */ | |
| onFinish : function() | |
| { | |
| if (this.fireEvent('finish', this, this.getWizardData()) !== false) { | |
| this.close(); | |
| } | |
| }, | |
| /** | |
| * Listener for the previous-button. | |
| * Switches to the previous displayed {@link Ext.ux.Wiz.Card}. | |
| */ | |
| onPreviousClick : function() | |
| { | |
| if (this.currentCard > 0) { | |
| this.cardPanel.getLayout().setActiveItem(this.currentCard - 1); | |
| } | |
| }, | |
| /** | |
| * Listener for the next-button. Switches to the next {@link Ext.ux.Wiz.Card} | |
| * if the 'beforehide'-method of it did not return false. The functionality | |
| * for this is implemented in {@link Ext.ux.layout.CardLayout}, which is needed | |
| * as the layout for the card-panel of this component. | |
| */ | |
| onNextClick : function() | |
| { | |
| this.nextButton.setDisabled(true); | |
| if (this.currentCard == this.cardCount-1) { | |
| this.onFinish(); | |
| } else { | |
| this.cardPanel.getLayout().setActiveItem(this.currentCard+1); | |
| } | |
| } | |
| }); |