- Timestamp:
- Dec 21, 2010, 12:04:37 AM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/grails-app/taglib/dbnp/studycapturing/WizardTagLib.groovy
r1224 r1286 1 1 package dbnp.studycapturing 2 2 3 import org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLib4 3 import dbnp.studycapturing.* 5 4 import dbnp.authentication.SecUser 6 5 import dbnp.data.* 7 6 import cr.co.arquetipos.crypto.Blowfish 7 import nl.grails.plugins.ajaxflow.AjaxflowTagLib 8 8 9 9 /** … … 19 19 * $Date$ 20 20 */ 21 class WizardTagLib extends JavascriptTagLib {21 class WizardTagLib extends AjaxflowTagLib { 22 22 def AuthenticationService 23 23 … … 25 25 static namespace = "wizard" 26 26 27 // define the AJAX provider to use28 static ajaxProvider = "jquery"29 30 27 // define default text field width 31 28 static defaultTextFieldSize = 25; 32 33 /**34 * ajaxButton tag, this is a modified version of the default35 * grails submitToRemote tag to work with grails webflows.36 * Usage is identical to submitToRemote with the only exception37 * that a 'name' form element attribute is required. E.g.38 * <wizard:ajaxButton name="myAction" value="myButton ... />39 *40 * you can also provide a javascript function to execute after41 * success. This behaviour differs from the default 'after'42 * action which always fires after a button press...43 *44 * @see http://blog.osx.eu/2010/01/18/ajaxifying-a-grails-webflow/45 * @see http://www.grails.org/WebFlow46 * @see http://www.grails.org/Tag+-+submitToRemote47 * @todo perhaps some methods should be moved to a more generic48 * 'webflow' taglib or plugin49 * @param Map attributes50 * @param Closure body51 */52 def ajaxButton = { attrs, body ->53 // get the jQuery version54 def jQueryVersion = grailsApplication.getMetadata()['plugins.jquery']55 56 // fetch the element name from the attributes57 def elementName = attrs['name'].replaceAll(/ /, "_")58 59 // javascript function to call after success60 def afterSuccess = attrs['afterSuccess']61 62 // src parameter?63 def src = attrs['src']64 def alt = attrs['alt']65 66 // generate a normal submitToRemote button67 def button = submitToRemote(attrs, body)68 69 /**70 * as of now (grails 1.2.0 and jQuery 1.3.2.4) the grails webflow does71 * not properly work with AJAX as the submitToRemote button does not72 * handle and submit the form properly. In order to support webflows73 * this method modifies two parts of a 'normal' submitToRemote button:74 *75 * 1) replace 'this' with 'this.form' as the 'this' selector in a button76 * action refers to the button and / or the action upon that button.77 * However, it should point to the form the button is part of as the78 * the button should submit the form data.79 * 2) prepend the button name to the serialized data. The default behaviour80 * of submitToRemote is to remove the element name altogether, while81 * the grails webflow expects a parameter _eventId_BUTTONNAME to execute82 * the appropriate webflow action. Hence, we are going to prepend the83 * serialized formdata with an _eventId_BUTTONNAME parameter.84 */85 if (jQueryVersion =~ /^1.([1|2|3]).(.*)/) {86 // fix for older jQuery plugin versions87 button = button.replaceFirst(/data\:jQuery\(this\)\.serialize\(\)/, "data:\'_eventId_${elementName}=1&\'+jQuery(this.form).serialize()")88 } else {89 // as of jQuery plugin version 1.4.0.1 submitToRemote has been modified and the90 // this.form part has been fixed. Consequently, our wrapper has changed as well...91 button = button.replaceFirst(/data\:jQuery/, "data:\'_eventId_${elementName}=1&\'+jQuery")92 }93 94 // add an after success function call?95 // usefull for performing actions on success data (hence on refreshed96 // wizard pages, such as attaching tooltips)97 if (afterSuccess) {98 button = button.replaceFirst(/\.html\(data\)\;/, '.html(data);' + afterSuccess + ';')99 }100 101 // got an src parameter?102 if (src) {103 def replace = 'type="image" src="' + src + '"'104 105 if (alt) replace = replace + ' alt="' + alt + '"'106 107 button = button.replaceFirst(/type="button"/, replace)108 }109 110 // replace double semi colons111 button = button.replaceAll(/;{2,}/, ';')112 113 // render button114 out << button115 }116 117 /**118 * generate a ajax submit JavaScript119 * @see WizardTagLib::ajaxFlowRedirect120 * @see WizardTagLib::baseElement (ajaxSubmitOnChange)121 */122 def ajaxSubmitJs = { attrs, body ->123 // define AJAX provider124 setProvider([library: ajaxProvider])125 126 // got a function name?127 def functionName = attrs.remove('functionName')128 if (functionName && !attrs.get('name')) {129 attrs.name = functionName130 }131 132 // generate an ajax button133 def button = this.ajaxButton(attrs, body)134 135 // strip the button part to only leave the Ajax call136 button = button.replaceFirst(/<[^\"]*onclick=\"/, '')137 button = button.replaceFirst(/return false.*/, '')138 139 // change form if a form attribute is present140 if (attrs.get('form')) {141 button = button.replace(142 "jQuery(this).parents('form:first')",143 "\$('" + attrs.get('form') + "')"144 )145 }146 147 // change 'this' if a this attribute is preset148 if (attrs.get('this')) {149 button = button.replace('this', attrs.get('this'))150 }151 152 out << button153 }154 155 /**156 * generate ajax webflow redirect javascript157 *158 * As we have an Ajaxified webflow, the initial wizard page159 * cannot contain a wizard form, as upon a failing submit160 * (e.g. the form data does not validate) the form should be161 * shown again. However, the Grails webflow then renders the162 * complete initial wizard page into the success div. As this163 * ruins the page layout (a page within a page) we want the164 * initial page to redirect to the first wizard form to enter165 * the webflow correctly. We do this by emulating an ajax post166 * call which updates the wizard content with the first wizard167 * form.168 *169 * Usage: <wizard:ajaxFlowRedirect form="form#wizardForm" name="next" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" />170 * form = the form identifier171 * name = the action to execute in the webflow172 * update = the divs to update upon success or error173 *174 * OR: to generate a JavaScript function you can call yourself, use 'functionName' instead of 'name'175 *176 * Example initial webflow action to work with this javascript:177 * ...178 * mainPage {179 * render(view: "/wizard/index")180 * onRender {181 * flow.page = 1182 * }183 * on("next").to "pageOne"184 * }185 * ...186 *187 * @param Map attributes188 * @param Closure body189 */190 def ajaxFlowRedirect = { attrs, body ->191 // generate javascript192 out << '<script type="text/javascript">'193 out << '$(document).ready(function() {'194 out << ajaxSubmitJs(attrs, body)195 out << '});'196 out << '</script>'197 }198 199 /**200 * render the content of a particular wizard page201 * @param Map attrs202 * @param Closure body (help text)203 */204 def pageContent = { attrs, body ->205 // define AJAX provider206 setProvider([library: ajaxProvider])207 208 // render new body content209 // - this JavaScript variable is used by baseElement to workaround an IE210 // specific issue (double submit on onchange events). The hell with IE!211 // @see baseElement212 out << '<script type="text/javascript">var lastRequestTime = 0;</script>'213 out << render(template: "/wizard/common/tabs")214 out << '<div class="content">'215 out << body()216 out << '</div>'217 out << render(template: "/wizard/common/navigation")218 out << render(template: "/wizard/common/error")219 }220 29 221 30 /**
Note: See TracChangeset
for help on using the changeset viewer.