From 4725e2500db693ef051d30d2bfb93ae4ea00f1c4 Mon Sep 17 00:00:00 2001 From: Duc Tri Le Date: Mon, 14 Feb 2011 07:16:39 -0800 Subject: [PATCH] Safe keeping --- change.log | 16 + package.yml | 5 +- .../Class/Class.Mutators.StoredInstances.js | 4 +- source/Element/Element.Delegation.Plus.js | 33 +- source/Element/Element.Plus.js | 3 + source/Interface/AutoComplete.js | 558 ++++++++++++++++++ source/Interface/Layer.js | 60 +- source/Request/Responses.js | 14 + tests/pages/autocomplete.html | 84 +++ tests/pages/layer.html | 6 +- tests/response_setter.php | 6 + tests/responses.php | 9 +- tests/test_cases/autocomplete.js | 3 + tests/test_cases/layer.js | 56 +- 14 files changed, 766 insertions(+), 91 deletions(-) create mode 100644 change.log create mode 100644 source/Interface/AutoComplete.js create mode 100644 tests/pages/autocomplete.html create mode 100644 tests/response_setter.php create mode 100644 tests/test_cases/autocomplete.js diff --git a/change.log b/change.log new file mode 100644 index 0000000..cae2ef0 --- /dev/null +++ b/change.log @@ -0,0 +1,16 @@ + +Version 0.2 +* Fixed bug with the continueChain method of ResponsesJS. +* Include pre-built files. +* Added a new script for auto complete. +* Another bug fix for the focusin and focusout event so that it is only fired if the triggering +element is that element that the event was attached to or is a child of it. +* Fixes JSDoc. + +---------------------------------------------------------------------------------------------------- +2010/11/13: +Version 0.1.1: +* Bug fix with the delegation of the focusin and focusout event. + +Version 0.1 +* Initial release. diff --git a/package.yml b/package.yml index 7111fa2..1956c5d 100644 --- a/package.yml +++ b/package.yml @@ -4,9 +4,9 @@ author: "Duc Tri Le" category: utilities -tags: [mutator, element, event, layer, json] +tags: [mutator, element, event, layer, json, autocomplete] -current: 0.1 +current: 0.2 exports: "mootools-plus.js" @@ -18,6 +18,7 @@ sources: - "source/Class/HtmlOptions.js" - "source/Element/Element.Delegation.Plus.js" - "source/Element/Element.Plus.js" + - "source/Interface/AutoComplete.js" - "source/Interface/Layer.js" - "source/Request/Responses.js" - "source/Types/Array.Plus.js" diff --git a/source/Class/Class.Mutators.StoredInstances.js b/source/Class/Class.Mutators.StoredInstances.js index cda35f4..b7364e2 100644 --- a/source/Class/Class.Mutators.StoredInstances.js +++ b/source/Class/Class.Mutators.StoredInstances.js @@ -3,7 +3,9 @@ name: Class.Mutators.StoredInstances -description: Allow classes to stored instances that has been created. +description: Allow classes to stored instances that has been created. Note that because this + contains static data and methods, any subclass will also need to include this if the parent + class has it. license: MIT-style license diff --git a/source/Element/Element.Delegation.Plus.js b/source/Element/Element.Delegation.Plus.js index be4c41b..51799fa 100644 --- a/source/Element/Element.Delegation.Plus.js +++ b/source/Element/Element.Delegation.Plus.js @@ -21,6 +21,9 @@ provides: ... */ (function() { + /** + * @type {Object} Constants. + */ var constants = { ie_change: 'mootools-plus-element-delegation:ie-change', ie_submit: 'mootools-plus-element-delegation:ie-submit' @@ -92,21 +95,40 @@ provides: // ------------------------------------------------------------------------------------------ // + /** + * @type {Array} The list of elements that is monitoring the focusin event. + */ var focusInElements = []; + + /** + * @type {Array} The list of elements that is monitoring the focusout event. + */ var focusOutElements = []; /** - * Custom handler for the focus/blur event so that it would bubbles. + * Event handler for the focusin event. * * @param event {Event} The event that was triggered. * @returns void */ var focusInHandler = function(event) { - focusInElements.invoke('fireEvent', 'focusin', event); + event = new Event(event); + if((this == event.target) || (this.contains(event.target))) { + focusInElements.invoke('fireEvent', 'focusin', event); + } }; + /** + * Event handler for the focusout event. + * + * @param event {Event} The event that was triggered. + * @returns void + */ var focusOutHandler = function(event) { - focusOutElements.invoke('fireEvent', 'focusout', event); + event = new Event(event); + if((this == event.target) || (this.contains(event.target))) { + focusOutElements.invoke('fireEvent', 'focusout', event); + } }; // Use event capturing to monitor the focus and blur event on browsers that isn't it @@ -116,10 +138,7 @@ provides: } // And finally, allow focusin and focusout to be added as native events - Object.append(Element.NativeEvents, { - 'focusin': 2, - 'focusout': 2 - }); + Object.append(Element.NativeEvents, { 'focusin': 2, 'focusout': 2 }); // ------------------------------------------------------------------------------------------ // diff --git a/source/Element/Element.Plus.js b/source/Element/Element.Plus.js index daee08b..eb6ef4c 100644 --- a/source/Element/Element.Plus.js +++ b/source/Element/Element.Plus.js @@ -96,6 +96,9 @@ Element.implement({ // ---------------------------------------------------------------------------------------------- // (function() { + /** + * @type {int} Counter for generating IDs. + */ var id_counter = 1; Element.Properties.id = { /** diff --git a/source/Interface/AutoComplete.js b/source/Interface/AutoComplete.js new file mode 100644 index 0000000..bcadd5c --- /dev/null +++ b/source/Interface/AutoComplete.js @@ -0,0 +1,558 @@ +/* +--- + +script: AutoComplete.js + +name: AutoCompleteJS + +description: Auto complete. + +license: MIT-style license + +authors: + - Duc Tri Le + +requires: + - Core/MooTools + - More/Element.Shortcuts + - More/Element.Position + - Plus/BindInstances + - Plus/Class.Mutators.Static + - Plus/Class.Mutators.StoredInstances + - Plus/Element.Delegation.Plus + - Plus/Element.Plus + - Plus/Function.Plus + - Plus/NamedChainJS + - Plus/ResponsesJS + +provides: [AutoCompleteJS] + +... +*/ +/** + * Since this allows you to use one instance on multiple input fields, some settings will need to be + * stored on the input field as data attributes. All of the fields are optional however. They are: + * data-hidden-field: The ID of the hidden input field where the value should be set. If this + * is provided, then the input field that triggered the suggestion will get the display + * text. + * + * Events: + * hide: Fired when the auto complete is to be hidden. + * postShow: Fired when the auto complete is shown. + * preShow: Fired when the auto complete layer is to be shown. + * selection: Fired when a suggestion is selected. + */ +var AutoCompleteJS = new Class({ + Implements: [Events, Options], + StoredInstances: true, + Static: { + Chain: { + /** + * The chain of actions for the method hide. + * fire_event: The chain item that fires the event hide. + * render: The chain item that hides the auto complete. + */ + hide: { + fire_event: 'AutoCompleteJS.hide:fire_event', + render: 'AutoCompleteJS.hide:render' + }, + + /** + * The chain of actions for the method show. + * fire_event: The chain item that fires the event preShow. + * request: The chain item that retrieves the suggestions. + * render: The chain item that shows the auto complete and fires the event + * postShow. + */ + show: { + fire_event: 'AutoCompleteJS.show:fire_event', + request: 'AutoCompleteJS.show:request', + render: 'AutoCompleteJS.show:render' + }, + + /** + * The chain of actions for the method useSelected. + * fire_event: The chain item that fires the event selection. + * render: The chain item that highlights the selected item. + */ + useSelected: { + fire_event: 'AutoCompleteJS.useSelected:fire_event', + render: 'AutoCompleteJS.useSelected:render' + } + }, + + /** + * INDEX: The index of the suggestion within the $suggestions array. + * PREVIOUS: The previous value of the input field. + */ + Constants: { + INDEX: 'AutoCompleteJS.Constants.INDEX', + PREVIOUS: 'AutoCompleteJS.Constants.PREVIOUS' + }, + + /** + * Get the instanced stored at the provided name or create a new one and store it there + * using the provided options. + * + * @param name {String} A unique name for the instance. + * @param server {String|Function} Refer to the server property. Optional if the + * instance already exists. + * @param options {Object} Refer to the options property. Optional. + * @returns {AutoCompleteJS} + */ + singleton: function(name, server, options) { + var result = this.retrieveInstance(name); + if(!result) { + result = new AutoCompleteJS(server, options); + result.storeInstance(name); + } + + return result; + } + }, + + /** + * The options are: + * onHide: (Function) Event handler for the event hide. + * onPostShow: (Function) Event handler for the event postShow. + * onPreShow: (Function) Event handler for the event preShow. + * onSelection: (Function) Event handler for the event selection. + * + * min_length: (int) The minimum number of characters before suggestions are fetched. + * query: (String) The name of the parameter to be sent if the server (if it is a string) + * that contains the user's input. + * extra_data: (Object) Any extra data to send along with the request to the server. + * + * @type {Object} Various options. + */ + options: { + /* + // autocompletejs: This AutoCompleteJS instance. + // chain: The chain of actions. + // hidden_field: The hidden input field to store the event. Can be null. + // suggestion: The selected suggestion. Object with property value and display. + + onHide: function(autocompletejs, chain) {}, + onPostShow: function(autocompletejs, chain) {}, + onPreShow: function(autocompletejs, chain) {}, + onSuggestion: function(autocompletejs, chain, suggestion, hidden_field) {}, + */ + + min_length: 3, + query: 'query', + extra_data: {} + }, + + // ------------------------------------------------------------------------------------------ // + + /** + * @type {ResponsesJS} Handles all AJAX requests. + */ + $responses: null, + + /** + * @type {Array} An array of objects containing all the possible suggestions. Each suggestion + * is an object containing the property value and display. + */ + $suggestions: [], + + /** + * @type {Boolean} Whether or not the auto complete is currently visible. + */ + $visible: false, + + // ------------------------------------------------------------------------------------------ // + + /** + * @type {String|Function} If it is a string, it must be the URL to the server that will + * receive the user's input and must return the possible suggestions (Refer to the method + * "handleResponse" for what the response should look like). If it is a function, the + * signature of the function is: function(autocompletejs) {}, where autocompletejs is this + * instance. You can get the query and the like from the input property. + */ + server: null, + + /** + * @type {Element} The element that is this AutoCompleteJS instance in the DOM. + */ + element: null, + + /** + * References includes the following: + * loading: (Element) The element that has the loading message/image. + * suggestions: (Element) The wrapper element of the suggestions. + * + * @type {Object} References to other elements within the auto complete. + */ + elements: null, + + /** + * @type {Element} The element that the provided the input for the suggestions. + */ + input: null, + + // ------------------------------------------------------------------------------------------ // + + /** + * Create a new instance. + * + * @param server {String|Function} Refer to the server property. + * @param options {Object} Refer to the options property. Optional. + * @class {AutoCompleteJS} + */ + initialize: function(server, options) { + Class.bindInstances(this); + this.setOptions(options); + this.server = server; + this.$responses = new ResponsesJS(); + this.$responses.addEvent('processItem', this.handleResponse); + + return this.build().attachInternal(); + }, + + // ------------------------------------------------------------------------------------------ // + + /** + * Event handler for bluring out of the input field. + * + * @param event {Event} The event that was triggered. + * @param element {Element} The element that triggered the event. + * @returns {AutoCompleteJS} + */ + onBlur: function(event, element) { + return this.hide(); + }, + + /** + * Event handler for clicking on a suggestion.. + * + * @param event {Event} The event that was triggered. + * @param element {Element} The element that triggered the event. + * @returns {AutoCompleteJS} + */ + onClick: function(event, element) { + event.preventDefault(); + return this.setSelection(element.retrieve(AutoCompleteJS.Constants.INDEX)); + }, + + /** + * Event handler for focusing on an input field. + * + * @param event {Event} The event that was triggered. + * @param element {Element} The element that triggered the event. + * @returns {AutoCompleteJS} + */ + onFocus: function(event, element) { + // Turn off the browser's auto complete + element.set('autocomplete', 'false'); + return this; + }, + + /** + * Event handler for a key down on the input field. + * + * @param event {Event} The event that was triggered. + * @param element {Element} The element that triggered the event. + * @returns {AutoCompleteJS} + */ + onKeyDown: function(event, element) { + // Prevent the enter and esc key but let the tab key selects and continue its behavior + if((event.key === 'enter') || (event.key === 'esc')) { event.preventDefault(); } + else if(event.key === 'tab') { this.useSelected(); } + + return this; + }, + + /** + * Event handler for a key up on the input field. + * + * @param event {Event} The event that was triggered. + * @param element {Element} The element that triggered the event. + * @returns {AutoCompleteJS} + */ + onKeyUp: function(event, element) { + this.input = element; + switch(event.key) { + case 'enter': // This key selects + this.useSelected(); + break; + case 'esc': // This key terminates + this.hide(); + break; + case 'down': case 'up': // These keys change selections + var index = this.elements.suggestions.getElement('.suggestion.selected') + .retrieve(AutoCompleteJS.Constants.INDEX); + + if((index > 0) && (event.key === 'up')) { --index; } + else if((index < (this.$suggestions.length - 1)) && + (event.key === 'down')) { ++index; } + + this.setSelection(index); + break; + case 'left': case 'right': // Ignore these keys + break; + default: // Check all others + if(this.input.get('value').length >= this.options.min_length) { this.show(); } + else { this.hide(); } + + break; + } + + return this; + }, + + /** + * Event handler for a mouse over event on a suggestion. + * + * @param event {Event} The event that was triggered. + * @param element {Element} The element that triggered the event. + * @returns {AutoCompleteJS} + */ + onMouseOver: function(event, element) { + var index = element.retrieve(AutoCompleteJS.Constants.INDEX); + return this.setSelection(index); + }, + + // ------------------------------------------------------------------------------------------ // + + /** + * Attach this instance to input fields that matched the provided selector. + * + * @param selector {String} The CSS selector for the elements that should have this auto + * complete instance. + * @returns {AutoCompleteJS} + */ + attach: function(selector) { + document.id(document.body).delegateEvent('focusin', selector, this.onFocus) + .delegateEvent('focusout', selector, this.onBlur) + .delegateEvent('keydown', selector, this.onKeyDown) + .delegateEvent('keyup', selector, this.onKeyUp); + + return this; + }, + + /** + * Attach all the necessary events internally for the auto complete. + * + * @returns {AutoCompleteJS} + */ + attachInternal: function() { + this.element.delegateEvent('click', '.suggestion', this.onClick) + .delegateEvent('mouseover', '.suggestion', this.onMouseOver); + + return this; + }, + + /** + * Create the wrapper element for the auto complete. + * + * @returns {AutoCompleteJS} + */ + build: function() { + this.elements = { + loading: new Element('div.loading'), + suggestions: new Element('div.suggestions') + }; + + this.element = new Element('div.autocompletejs').adopt( + this.elements.loading, this.elements.suggestions + ).hide().inject(document.body); + + return this; + }, + + /** + * Wrap the provided text. + * + * @param display {String} The display text. + * @returns {String} The display text after processing. + */ + getDisplayText: function(display) { + return display.replace( + new RegExp('(' + this.input.get('value') + ')', 'gi'), + '$1' + ); + }, + + /** + * Handles the response from the server. The response is expected to be similar to the + * following: + * { + * type: 'autocompletejs', + * data: [ + * { value: "value1", display: "Friendly display 1" }, + * ... + * ] + * } + * + * @param responsesjs {ResponsesJS} The request object. + * @param response {Object} The response to process. + * @returns {AutoCompleteJS} + */ + handleResponse: function(responsesjs, response) { + if(response.type === 'autocompletejs') { + this.$suggestions = response.data; + responsesjs.continueChain(); + } + + return this; + }, + + /** + * Hide the auto complete. + * + * @returns {AutoCompleteJS} + */ + hide: function() { + var chain = new NamedChainJS(); + + chain.append(AutoCompleteJS.Chain.hide.fire_event, this.__hideFireEvent.curry(chain)) + .append(AutoCompleteJS.Chain.hide.render, this.__hideRender.curry(chain)) + .run(); + + return this; + }, __hideFireEvent: function(chain) { + this.fireEvent('hide', [this, chain]); + chain.run(); + }, __hideRender: function(chain) { + this.$visible = false; + this.element.hide(); + chain.run(); + }, + + /** + * Render the suggestions. + * + * @returns {AutoCompleteJS} + */ + renderSuggestions: function() { + // If there are no suggestions, hide + if(!this.$suggestions.length) { return this.hide(); } + + // Update the suggestions + this.elements.suggestions.set('html', ''); + this.$suggestions.each(function(item, index) { + new Element('div.suggestion').store(AutoCompleteJS.Constants.INDEX, index) + .update(this.getDisplayText(item.display)).inject(this.elements.suggestions); + }, this); + + // Make the first item the selected + this.elements.suggestions.getElement('.suggestion').addClass('selected'); + return this; + }, + + /** + * Set the selection. + * + * @param index {int} The index of the selection to set. + * @returns {AutoCompleteJS} + */ + setSelection: function(index) { + var suggestions = this.elements.suggestions.getElements('.suggestion'); + for(var i = 0, l = suggestions.length; i < l; ++i) { + if(i === index) { suggestions[i].addClass('selected'); } + else { suggestions[i].removeClass('selected'); } + } + + // If none has been selected, select the first one + if(!this.elements.suggestions.getElement('.suggestion.selected')) { + this.elements.suggestions.getElement('.suggestion').addClass('selected'); + } + + return this; + }, + + /** + * Show the suggestions for the current input. + * + * @returns {AutoCompleteJS} + */ + show: function() { + // If this is already visible and the value hasn't changed, no need to do anything + if(!this.$visible || + (this.input.get('value') !== this.input.retrieve(AutoCompleteJS.Constants.PREVIOUS))) { + var chain = new NamedChainJS(); + + chain.append(AutoCompleteJS.Chain.show.fire_event, this.__showFireEvent.curry(chain)) + .append(AutoCompleteJS.Chain.show.request, this.__showRequest.curry(chain)) + .append(AutoCompleteJS.Chain.show.render, this.__showRender.curry(chain)) + .run(); + } + + return this; + }, __showFireEvent: function(chain) { + this.fireEvent('preShow', [this, chain]); + + this.elements.loading.show(); + this.elements.suggestions.hide(); + this.element.show(); + + this.element.position({ + relativeTo: this.input, + edge: 'upperLeft', + position: 'bottomLeft' + }); + + chain.run(); + }, __showRequest: function(chain) { + if(typeOf(this.server) === 'function') { + this.$suggestions = this.server(this); + chain.run(); + } else { + var data = Object.append({}, this.options.extra_data); + data[this.options.query] = this.input.get('value'); + + this.$responses.send({ + method: 'get', + url: this.server, + extra_data: { chain: chain }, + data: data + }); + } + }, __showRender: function(chain) { + this.input.store(AutoCompleteJS.Constants.PREVIOUS, this.input.get('value')); + this.renderSuggestions(); + + this.elements.loading.hide(); + this.elements.suggestions.show(); + + this.$visible = true; + this.fireEvent('postShow', [this, chain]); + chain.run(); + }, + + /** + * Use the currently selected value. + * + * @returns {AutoCompleteJS} + */ + useSelected: function() { + var chain = new NamedChainJS(); + var hidden_field = document.id(this.input.get('data-hidden-field')); + var index = this.elements.suggestions.getElement('.suggestion.selected') + .retrieve(AutoCompleteJS.Constants.INDEX); + + chain.append( + AutoCompleteJS.Chain.useSelected.fire_event, + this.__useSelectedFireEvent.curry([chain, this.$suggestions[index], hidden_field]) + ).append( + AutoCompleteJS.Chain.useSelected.render, + this.__useSelectedRender.curry([chain, this.$suggestions[index], hidden_field]) + ).run(); + + return this; + }, __useSelectedFireEvent: function(chain, suggestion, hidden) { + this.fireEvent('selection', [this, chain, suggestion, hidden]); + chain.run(); + }, __useSelectedRender: function(chain, suggestion, hidden) { + if(hidden) { + hidden.set('value', suggestion.value); + this.input.set('value', suggestion.display); + } else { + this.input.set('value', suggestion.value); + } + + this.hide(); + chain.run(); + } +}); diff --git a/source/Interface/Layer.js b/source/Interface/Layer.js index 3aa0ab1..3a50de9 100644 --- a/source/Interface/Layer.js +++ b/source/Interface/Layer.js @@ -97,7 +97,7 @@ var LayerJS = new Class({ * * @param name {String} A unique name for the instance. * @param options {Object} Optional. Refer to the options property. - * @returns LayerJS + * @returns {LayerJS} */ singleton: function(name, options) { var result = this.retrieveInstance(name); @@ -186,7 +186,7 @@ var LayerJS = new Class({ * Create a new instance. * * @param options {Object} Optional. Refer to the options property. - * @class LayerJS + * @class {LayerJS} */ initialize: function(options) { Class.bindInstances(this); @@ -196,10 +196,9 @@ var LayerJS = new Class({ else { this.element = document.id(options.element); } this.element.addClass(this.options.layer_classname); - this.$responses = new ResponsesJS({ - onProcessItem: this.handleResponse, - onFinishProcessing: this.continueChain - }); + this.$responses = new ResponsesJS(); + this.$responses.addEvent('processItem', this.handleResponse) + .addEvent('finishProcessing', this.$responses.continueChain); // Now load all the options and attach the necessary events this.loadAllOptions(this.element, options); @@ -231,22 +230,6 @@ var LayerJS = new Class({ return this; }, - /** - * Continue running the chain stored to the responses object. - * - * @param responsesjs {ResponsesJS} The request object. - * @param responses {Array} All the responses. - * @returns {LayerJS} - */ - continueChain: function(responsesjs, responses) { - if(responsesjs.options.extra_data && - instanceOf(responsesjs.options.extra_data.chain, NamedChainJS)) { - responsesjs.options.extra_data.chain.run(); - } - - return this; - }, - /** * Make a request to the provided URL and use the response to update the layer. Note that all * requests will be made as a GET. @@ -265,19 +248,16 @@ var LayerJS = new Class({ .run(); return this; - }, - __fetchUrlFireEvent: function(chain) { + }, __fetchUrlFireEvent: function(chain) { this.fireEvent('startFetching', [this, chain]); chain.run(); - }, - __fetchUrlRequest: function(chain, url) { + }, __fetchUrlRequest: function(chain, url) { this.$responses.send({ method: 'get', url: url, extra_data : { chain: chain } }); - }, - __fetchUrlWrapup: function(chain, parent_chain) { + }, __fetchUrlWrapup: function(chain, parent_chain) { this.fireEvent('finishFetching', [this, chain]); if(instanceOf(parent_chain, NamedChainJS)) { parent_chain.run(); } chain.run(); @@ -318,12 +298,10 @@ var LayerJS = new Class({ .run(); return this; - }, - __hideFireEvent: function(chain) { + }, __hideFireEvent: function(chain) { this.fireEvent('hide', [this, chain]); chain.run(); - }, - __hideHide: function(chain) { + }, __hideHide: function(chain) { this.element.hide(); chain.run(); }, @@ -393,16 +371,13 @@ var LayerJS = new Class({ .run(); return this; - }, - __showFireEvent: function(chain) { + }, __showFireEvent: function(chain) { this.fireEvent('show', [this, chain]); chain.run(); - }, - __showRequest: function(chain) { + }, __showRequest: function(chain) { if(this.options.url) { this.fetchUrl(this.options.url, chain); } else { chain.run(); } - }, - __showShow: function(chain) { + }, __showShow: function(chain) { if(this.options.url && !this.options.refetch) { this.options.url = null; } this.element.show(); @@ -424,20 +399,17 @@ var LayerJS = new Class({ .run(); return this; - }, - __submitFormFireEvent: function(chain) { + }, __submitFormFireEvent: function(chain) { this.fireEvent('startPosting', [this, chain]); chain.run(); - }, - __submitFormRequest: function(chain, form) { + }, __submitFormRequest: function(chain, form) { this.$responses.send({ method: form.get('method'), url: form.get('action'), data: form.toQueryString(), extra_data: { chain: chain } }); - }, - __submitFormWrapup: function(chain) { + }, __submitFormWrapup: function(chain) { this.fireEvent('finishPosting', [this, chain]); chain.run(); }, diff --git a/source/Request/Responses.js b/source/Request/Responses.js index 0e2a928..4a33fe7 100644 --- a/source/Request/Responses.js +++ b/source/Request/Responses.js @@ -235,6 +235,19 @@ var ResponsesJS = new Class({ return this; }, + /** + * Continue running the chain stored in extra data. + * + * @returns {ResponsesJS} + */ + continueChain: function() { + if(this.options.extra_data && instanceOf(this.options.extra_data.chain, NamedChainJS)) { + this.options.extra_data.chain.run(); + } + + return this; + }, + /** * Get a unique identifier for the provided handler name. * @@ -257,6 +270,7 @@ var ResponsesJS = new Class({ } if(!responses) { + this.options.extra_data = null; this.failure(); return this; } diff --git a/tests/pages/autocomplete.html b/tests/pages/autocomplete.html new file mode 100644 index 0000000..d9119cb --- /dev/null +++ b/tests/pages/autocomplete.html @@ -0,0 +1,84 @@ + + + + AutoCompleteJS + + + + + + + + + +
+ + +
+ +
+ + + + diff --git a/tests/pages/layer.html b/tests/pages/layer.html index c384ab4..05fcd20 100644 --- a/tests/pages/layer.html +++ b/tests/pages/layer.html @@ -17,7 +17,7 @@ width: 400px; z-index: 100; } - + #existing_layer { right: 0; top: 0; @@ -33,10 +33,10 @@ Click to hide layer
Update me.
- +
-
+
diff --git a/tests/response_setter.php b/tests/response_setter.php new file mode 100644 index 0000000..6946e20 --- /dev/null +++ b/tests/response_setter.php @@ -0,0 +1,6 @@ +', onFinishFetching: function(widget, chain) { this.resume(function() { @@ -61,24 +56,19 @@ YUITest.TestCases.LayerJS = { }, testSubmitForm: function() { + // Set the expected result + new Request({ + url: '/tests/mootools-plus/response_setter.php', + async: false, + data: 'session_key=layerjs_submit_form&data=' + JSON.encode([{ + type: 'layerjs:update', + html: 'Data submitted.' + }]) + }).send(); + var wrapper = $C.document.id('layer_with_form'); var form = wrapper.getElement('form'); - var modified = new Class({ - Extends: $C.LayerJS, - __submitFormRequest: function(chain, form) { - this.$responses.send({ - method: form.get('method'), - url: form.get('action'), - extra_data: { chain: chain }, - data: 'data=' + JSON.encode([{ - type: 'layerjs:update', - html: 'Data submitted.' - }]) - }); - } - }); - - var layer = new modified({ + var layer = new $C.LayerJS({ element: wrapper, onFinishPosting: function(widget, chain) { this.resume(function() {