| 1 | /* |
|---|
| 2 | Dialog Library |
|---|
| 3 | $Id$ |
|---|
| 4 | |
|---|
| 5 | Copyright (c) 2006, Six Apart, Ltd. |
|---|
| 6 | All rights reserved. |
|---|
| 7 | |
|---|
| 8 | Redistribution and use in source and binary forms, with or without |
|---|
| 9 | modification, are permitted provided that the following conditions are |
|---|
| 10 | met: |
|---|
| 11 | |
|---|
| 12 | * Redistributions of source code must retain the above copyright |
|---|
| 13 | notice, this list of conditions and the following disclaimer. |
|---|
| 14 | |
|---|
| 15 | * Redistributions in binary form must reproduce the above |
|---|
| 16 | copyright notice, this list of conditions and the following disclaimer |
|---|
| 17 | in the documentation and/or other materials provided with the |
|---|
| 18 | distribution. |
|---|
| 19 | |
|---|
| 20 | * Neither the name of "Six Apart" nor the names of its |
|---|
| 21 | contributors may be used to endorse or promote products derived from |
|---|
| 22 | this software without specific prior written permission. |
|---|
| 23 | |
|---|
| 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|---|
| 25 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|---|
| 26 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|---|
| 27 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|---|
| 28 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|---|
| 29 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|---|
| 30 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|---|
| 31 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|---|
| 32 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|---|
| 33 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|---|
| 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 35 | */ |
|---|
| 36 | |
|---|
| 37 | Dialog = {}; |
|---|
| 38 | |
|---|
| 39 | |
|---|
| 40 | /* modal message dialog subclass */ |
|---|
| 41 | |
|---|
| 42 | Dialog.Message = new Class( Modal, { |
|---|
| 43 | |
|---|
| 44 | /* events */ |
|---|
| 45 | |
|---|
| 46 | /** |
|---|
| 47 | * Class: <code>Dialog.Message</code><br/> |
|---|
| 48 | * Uses <code>getMouseEventCommand</code> to close the dialog if the user has clicked 'ok'. |
|---|
| 49 | * @param event <code>Event</code> A prepared event object. |
|---|
| 50 | */ |
|---|
| 51 | eventClick: function( event ) { |
|---|
| 52 | var command = this.getMouseEventCommand( event ); |
|---|
| 53 | if( !command ) |
|---|
| 54 | return; |
|---|
| 55 | command = (command == "ok" ? true : false); |
|---|
| 56 | event.stop(); |
|---|
| 57 | this.close( command ); |
|---|
| 58 | }, |
|---|
| 59 | |
|---|
| 60 | |
|---|
| 61 | /** |
|---|
| 62 | * Class: <code>Dialog.Message</code><br/> |
|---|
| 63 | * This method allows a <code>Dialog.Message</code> to disappear on keypress of 'esc' (cancels the |
|---|
| 64 | * dialog action, just like clicking 'cancel' would do) (closes with a value of <code>false</code>), |
|---|
| 65 | * cancel on keypress of the 'Cancel' button (mouseEventCommand 'cancel') (closes with a value |
|---|
| 66 | * of <code>false</code>), or close with a value of <code>true</code> (i.e., user presses |
|---|
| 67 | * 'enter' key over the 'ok'/'yes' button) (no mouseEventCommand needed). |
|---|
| 68 | * @param event <code>Event</code> A prepared (processed by the custom js framework) |
|---|
| 69 | * <code>Event</code> object. |
|---|
| 70 | */ |
|---|
| 71 | eventKeyPress: function( event ) { |
|---|
| 72 | switch( event.keyCode ) { |
|---|
| 73 | case 13: |
|---|
| 74 | var command = this.getMouseEventCommand( event ); // Handle 'Confirm'-type dialgs. |
|---|
| 75 | event.stop(); |
|---|
| 76 | if( command == "cancel" ) |
|---|
| 77 | this.close( false ); |
|---|
| 78 | else |
|---|
| 79 | this.close( true ); |
|---|
| 80 | break; |
|---|
| 81 | case 27: |
|---|
| 82 | event.stop(); |
|---|
| 83 | this.close( false ); |
|---|
| 84 | } |
|---|
| 85 | }, |
|---|
| 86 | |
|---|
| 87 | |
|---|
| 88 | /* execution */ |
|---|
| 89 | |
|---|
| 90 | /** |
|---|
| 91 | * Class: <code>Dialog.Message</code><br/> |
|---|
| 92 | * Set the basic customizable properties of the dialog and show it to the user. |
|---|
| 93 | * @param data <code>Object</code> A basic container object for the <code>innerHTML</code> of |
|---|
| 94 | * the message dialog. |
|---|
| 95 | * @param callback <code>function</code> A function/bound-method callback invoked with the arguments |
|---|
| 96 | * '<code>data</code>, <code>this</code>' when the dialog is closed. |
|---|
| 97 | */ |
|---|
| 98 | open: function() { |
|---|
| 99 | arguments.callee.applySuper( this, arguments ); |
|---|
| 100 | if( typeof this.data == "string" ) |
|---|
| 101 | this.data = { message: this.data }; |
|---|
| 102 | var e = DOM.getElement( this.element.id + "-message" ); |
|---|
| 103 | if( e ) |
|---|
| 104 | e.innerHTML = this.data.message; |
|---|
| 105 | } |
|---|
| 106 | } ); |
|---|
| 107 | |
|---|
| 108 | |
|---|
| 109 | /* simple serializing dialog */ |
|---|
| 110 | |
|---|
| 111 | Dialog.Simple = new Class( Modal, { |
|---|
| 112 | |
|---|
| 113 | inputBlacklist: { |
|---|
| 114 | image: defined, |
|---|
| 115 | submit: defined |
|---|
| 116 | }, |
|---|
| 117 | |
|---|
| 118 | |
|---|
| 119 | destroyObject: function() { |
|---|
| 120 | this.firstInput = null; |
|---|
| 121 | arguments.callee.applySuper( this, arguments ); |
|---|
| 122 | }, |
|---|
| 123 | |
|---|
| 124 | |
|---|
| 125 | /* events */ |
|---|
| 126 | |
|---|
| 127 | /** |
|---|
| 128 | * Class: <code>Dialog.Simple</code><br/> |
|---|
| 129 | * This method allows a <code>Dialog.Simple</code> to disappear on keypress of 'esc' ( cancels the |
|---|
| 130 | * dialog action, just like clicking 'cancel' would do) or 'return' (finishes the dialog action, |
|---|
| 131 | * just as clicking 'ok' would do). |
|---|
| 132 | * @param event <code>Event</code> A prepared (processed by the custom js framework) <code>Event</code> object. |
|---|
| 133 | */ |
|---|
| 134 | eventKeyPress: function( event ) { |
|---|
| 135 | switch( event.keyCode ) { |
|---|
| 136 | case 13: |
|---|
| 137 | if( event.target && event.target.tagName && event.target.tagName.toLowerCase() == "textarea" ) |
|---|
| 138 | return true; |
|---|
| 139 | this.data = DOM.getFormData( this.element, this.data ); |
|---|
| 140 | this.close( this.data ); |
|---|
| 141 | return event.stop(); |
|---|
| 142 | |
|---|
| 143 | case 27: |
|---|
| 144 | this.close( false ); |
|---|
| 145 | } |
|---|
| 146 | }, |
|---|
| 147 | |
|---|
| 148 | |
|---|
| 149 | /** |
|---|
| 150 | * Class: <code>Dialog.Simple</code><br/> |
|---|
| 151 | * Close the dialog without returning data if the user presses 'cancel'. Otherwise, close the dialog |
|---|
| 152 | * and return the form data (see the doc for the method <code>open</code>. |
|---|
| 153 | * @param event <code>Event</code> A prepared (processed by the custom js framework) <code>Event</code> object. |
|---|
| 154 | */ |
|---|
| 155 | eventClick: function( event ) { |
|---|
| 156 | var command = this.getMouseEventCommand( event ); |
|---|
| 157 | |
|---|
| 158 | switch( command ) { |
|---|
| 159 | case "cancel": |
|---|
| 160 | return this.close( false ); |
|---|
| 161 | |
|---|
| 162 | case "ok": |
|---|
| 163 | this.data = DOM.getFormData( this.element, this.data ); |
|---|
| 164 | return this.close( this.data ); |
|---|
| 165 | } |
|---|
| 166 | }, |
|---|
| 167 | |
|---|
| 168 | |
|---|
| 169 | /** |
|---|
| 170 | * Get the first input element that matters to the application data. |
|---|
| 171 | * @param element <code>Node</code> A dom element. |
|---|
| 172 | * @param data <code>Object</code> |
|---|
| 173 | * @param firstInput <code>Node</code> MAY BE NULL A reference to the first input element in the form. |
|---|
| 174 | * @return firstInput <code>Node</code> A reference to the first input element in the form. |
|---|
| 175 | */ |
|---|
| 176 | getFirstSpecifiedInput: function( element, data, firstInput ) { |
|---|
| 177 | |
|---|
| 178 | if( !element.tagName ) |
|---|
| 179 | return firstInput; |
|---|
| 180 | if( exists( element.name ) && !data.hasOwnProperty( element.name ) ) |
|---|
| 181 | return firstInput; |
|---|
| 182 | |
|---|
| 183 | var tagName = element.tagName.toLowerCase(); |
|---|
| 184 | var type = element.getAttribute( "type" ); |
|---|
| 185 | type = type ? type.toLowerCase() : ""; |
|---|
| 186 | |
|---|
| 187 | switch( tagName ) { |
|---|
| 188 | case "input": |
|---|
| 189 | if( this.inputBlacklist[ type ] === defined || type == "radio" || type == "checkbox" ) |
|---|
| 190 | return firstInput; |
|---|
| 191 | |
|---|
| 192 | case "textarea": // Catches "text" input types too. |
|---|
| 193 | case "select": |
|---|
| 194 | if( !firstInput ) |
|---|
| 195 | firstInput = element; |
|---|
| 196 | return firstInput; |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | for( var i = 0; i < element.childNodes.length; i++ ) { |
|---|
| 200 | // Reference types are immutable, so we must assign 'firstInput': |
|---|
| 201 | var firstInput = this.getFirstSpecifiedInput( element.childNodes[ i ], data ); |
|---|
| 202 | if( firstInput ) |
|---|
| 203 | return firstInput; |
|---|
| 204 | } |
|---|
| 205 | }, |
|---|
| 206 | |
|---|
| 207 | |
|---|
| 208 | setFirstInput: function() { |
|---|
| 209 | if( !this.firstInput ) |
|---|
| 210 | this.firstInput = this.getFirstSpecifiedInput( this.element, this.data, null ); |
|---|
| 211 | }, |
|---|
| 212 | |
|---|
| 213 | |
|---|
| 214 | selectFirstInput: function() { |
|---|
| 215 | if( this.firstInput && this.firstInput.select ) |
|---|
| 216 | this.firstInput.select(); |
|---|
| 217 | }, |
|---|
| 218 | |
|---|
| 219 | |
|---|
| 220 | focusFirstInput: function() { |
|---|
| 221 | if( this.firstInput ) { |
|---|
| 222 | this.firstInput.focus(); |
|---|
| 223 | if( this.firstInput.focusIn ) |
|---|
| 224 | this.firstInput.focusIn(); |
|---|
| 225 | } |
|---|
| 226 | }, |
|---|
| 227 | |
|---|
| 228 | /* execution */ |
|---|
| 229 | |
|---|
| 230 | /** |
|---|
| 231 | * Class: <code>Dialog.Simple</code><br/> |
|---|
| 232 | * Set the basic customizable properties of the dialog and show it to the user. |
|---|
| 233 | * @param data <code>Object</code> A combined setting and getting container object for the form data of |
|---|
| 234 | * the message dialog. <br/>On open, the dialog will set any form elements with |
|---|
| 235 | * a <code>name</code> property equivalent to a property in <code>data</code> |
|---|
| 236 | * to the value of that property in <code>data</code>.<br/> |
|---|
| 237 | * On close, this dialog loads all form data, regardless of name, into the |
|---|
| 238 | * <code>data</code> object, creating new properties as needed. |
|---|
| 239 | * @param callback <code>function</code> A function/bound-method callback invoked with the arguments |
|---|
| 240 | * '<code>data</code>, <code>this</code>' when the dialog is closed. |
|---|
| 241 | */ |
|---|
| 242 | open: function( data, callback ) { |
|---|
| 243 | arguments.callee.applySuper( this, arguments ); |
|---|
| 244 | if( typeof this.data == "string" ) |
|---|
| 245 | this.data = { message: this.data }; |
|---|
| 246 | var e = DOM.getElement( this.element.id + "-message" ); |
|---|
| 247 | if( e ) |
|---|
| 248 | e.innerHTML = this.data.message; |
|---|
| 249 | |
|---|
| 250 | this.firstInput = null; |
|---|
| 251 | DOM.setFormData( this.element, this.data ); |
|---|
| 252 | this.setFirstInput(); |
|---|
| 253 | this.focusFirstInput(); |
|---|
| 254 | } |
|---|
| 255 | } ); |
|---|