Changeset 145
- Timestamp:
- Jan 28, 2010, 5:34:50 PM (13 years ago)
- Location:
- trunk
- Files:
-
- 3 added
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/grails-app/controllers/dbnp/studycapturing/WizardController.groovy
r140 r145 51 51 [title: 'Study'], // study 52 52 [title: 'Subjects'], // subjects 53 [title: 'Groups'], // groups 53 54 [title: 'Form elements demo page'] 54 55 ] … … 80 81 if (params.get('startDate')) { 81 82 params.startDate = new Date().parse("d/M/yyyy", params.get('startDate').toString()) 83 } 84 85 // if a template is selected, get template instance 86 if (params.get('template')) { 87 params.template = Template.findByName(params.get('template')) 82 88 } 83 89 … … 125 131 error() 126 132 } 127 }.to " pageThree"133 }.to "groups" 128 134 on("previous") { 129 // handle data? 130 // go to study page 135 // TODO 131 136 }.to "study" 132 137 } 133 138 139 groups { 140 render(view: "_groups") 141 onRender { 142 flow.page = 3 143 144 if (!flow.groups) { 145 flow.groups = [] 146 } 147 } 148 on("next") { 149 // TODO 150 }.to "groups" 151 on("previous") { 152 // TODO 153 }.to "subjects" 154 } 155 134 156 // render page three 135 pageThree{157 demo { 136 158 render(view: "_three") 137 159 onRender { -
trunk/grails-app/domain/dbnp/data/Term.groovy
r139 r145 7 7 * The Term object should point to an existing term in an online ontology, therefore instances of this class can also 8 8 * be seen as a cache of elements of the external ontology. 9 * 10 * Revision information: 11 * $Rev$ 12 * $Author$ 13 * $Date$ 9 14 */ 10 class Term {11 12 13 14 15 class Term implements Serializable { 16 static searchable = true 17 String name 18 Ontology ontology 19 String accession 15 20 16 17 21 static constraints = { 22 } 18 23 19 def String toString() { 20 return name 21 } 22 23 24 def String toString() { 25 return name 26 } 24 27 } -
trunk/grails-app/domain/dbnp/studycapturing/Subject.groovy
r139 r145 5 5 /** 6 6 * This domain class describes the subjects in a study. 7 * 8 * Revision information: 9 * $Rev$ 10 * $Author$ 11 * $Date$ 7 12 */ 8 class Subject {13 class Subject implements Serializable { 9 14 static searchable = true 10 15 String name … … 16 21 17 22 static hasMany = [ 18 templateStringFields: String, // stores both STRING and STRINGLIST items (latter should be checked against the list)19 templateIntegerFields 20 templateFloatFields 21 templateTermFields 23 templateStringFields: String, // stores both STRING and STRINGLIST items (latter should be checked against the list) 24 templateIntegerFields: int, 25 templateFloatFields: float, 26 templateTermFields: Term 22 27 ] 23 28 24 29 static constraints = { 25 30 } -
trunk/grails-app/domain/dbnp/studycapturing/TemplateField.groovy
r136 r145 3 3 /** 4 4 * This is the superclass for template fields. Normally, this class will not be instantiated. 5 * 6 * Revision information: 7 * $Rev$ 8 * $Author$ 9 * $Date$ 5 10 */ 6 abstract class TemplateField {7 8 9 11 abstract class TemplateField implements Serializable { 12 String name 13 TemplateFieldType type 14 String unit 10 15 11 12 name(unique:true)13 unit(nullable:true, blank:true)14 16 static constraints = { 17 name(unique: true) 18 unit(nullable: true, blank: true) 19 } 15 20 16 21 def String toString() { -
trunk/grails-app/domain/dbnp/studycapturing/TemplateFieldType.groovy
r139 r145 3 3 /** 4 4 * Enum describing the type of a templated field. 5 * Revision information: 6 * $Rev$ 7 * $Author$ 8 * $Date$ 5 9 */ 6 10 public enum TemplateFieldType { 7 8 9 10 11 11 STRING('String'), 12 INTEGER('Integer number'), 13 FLOAT('Decimal number'), 14 STRINGLIST('List of items'), 15 ONTOLOGYTERM('Ontology Reference') 12 16 13 17 String name 14 18 15 16 17 19 TemplateFieldType(String name) { 20 this.name = name 21 } 18 22 19 20 21 23 static list() { 24 [STRING, INTEGER, FLOAT, STRINGLIST, ONTOLOGYTERM] 25 } 22 26 23 // It would be nice to see the description string in the scaffolding, 24 // and the following works, but then the item cannot be saved properly. 25 // TODO: find a way to display the enum description but save the enum value in the scaffolding 26 /*def String toString() { 27 return this.name 28 }*/ 29 27 // It would be nice to see the description string in the scaffolding, 28 // and the following works, but then the item cannot be saved properly. 29 // TODO: find a way to display the enum description but save the enum value in the scaffolding 30 /* 31 def String toString() { 32 return this.name 33 } 34 */ 30 35 } -
trunk/grails-app/domain/dbnp/studycapturing/TemplateSubjectField.groovy
r131 r145 3 3 /** 4 4 * Instances of this class describe an extra (template) field for the Subject entity. 5 * 6 * Revision information: 7 * $Rev$ 8 * $Author$ 9 * $Date$ 5 10 */ 6 11 class TemplateSubjectField extends TemplateField { -
trunk/grails-app/taglib/dbnp/studycapturing/WizardTagLib.groovy
r140 r145 176 176 177 177 /** 178 * generate a base form element 179 * @param String inputElement name 180 * @param Map attributes 181 * @param Closure help content 182 */ 183 def baseElement = { inputElement, attrs, help -> 184 // work variables 185 def description = attrs.remove('description') 186 def addExampleElement = attrs.remove('addExampleElement') 187 188 // render a form element 189 out << '<div class="element">' 190 out << ' <div class="description">' 191 out << description 192 out << ' </div>' 193 out << ' <div class="input">' 194 out << "$inputElement"(attrs) 195 if(help()) { 196 out << ' <div class="helpIcon"></div>' 197 } 198 199 // add an disabled input box for feedback purposes 200 // @see dateElement(...) 201 if (addExampleElement) { 202 def exampleAttrs = new LinkedHashMap() 203 exampleAttrs.name = attrs.get('name')+'Example' 204 exampleAttrs.class = 'isExample' 205 exampleAttrs.disabled = 'disabled' 206 exampleAttrs.size = 30 207 out << textField(exampleAttrs) 208 } 209 210 out << ' </div>' 211 212 // add help content if it is available 213 if (help()) { 214 out << ' <div class="helpContent">' 215 out << ' ' + help() 216 out << ' </div>' 217 } 218 219 out << '</div>' 220 } 221 222 /** 178 223 * render a textFieldElement 179 224 * @param Map attrs 180 225 * @param Closure body (help text) 181 226 */ 182 def textFieldElement = { attrs, body ->227 def textFieldElement = { attrs, body -> 183 228 // set default size, or scale to max length if it is less than the default size 184 229 if (!attrs.get("size")) { … … 190 235 } 191 236 192 // work variables 193 def addExampleElement = attrs.remove('addExampleElement') 194 //attrs.remove('addExampleElement') 195 196 // render a text element 197 out << '<div class="element">' 198 out << ' <div class="description">' 199 out << attrs.get('description') 200 out << ' </div>' 201 out << ' <div class="input">' 202 203 // add text input field 204 out << textField(attrs) 205 206 // add a help icon if help information is available 207 if (body()) { 208 out << ' <div class="helpIcon" />' 209 } 210 211 // add an disabled input box for feedback purposes 212 // @see dateElement(...) 213 if (addExampleElement) { 214 def exampleAttrs = new LinkedHashMap() 215 exampleAttrs.name = attrs.get('name')+'Example' 216 exampleAttrs.class = 'isExample' 217 exampleAttrs.disabled = 'disabled' 218 out << textField(exampleAttrs) 219 } 220 221 // end HTML 222 out << ' </div>' 223 224 // add help content if it is available 225 if (body()) { 226 out << ' <div class="helpContent">' 227 out << ' ' + body() 228 out << ' </div>' 229 } 230 231 out << '</div>' 232 } 233 234 //def baseElement 235 236 def templateElement = { attrs, body -> 237 237 // render template element 238 baseElement.call( 239 'textField', 240 attrs, 241 body 242 ) 238 243 } 239 244 240 245 /** 241 246 * render a dateElement 247 * NOTE: datepicker is attached through wizard.js! 242 248 * @param Map attrs 243 249 * @param Closure body (help text) … … 255 261 256 262 // render a normal text field 257 out << textFieldElement(attrs,body) 258 259 // and attach the jquery-ui datepicker 260 out << '<script type="text/javascript">' 261 out << '$(document).ready(function() {' 262 out << ' $("#' + attrs.get('name') + '").datepicker({' 263 out << ' dateFormat: \'dd/mm/yy\',' 264 out << ' altField: \'#' + attrs.get('name') + 'Example\', altFormat: \'DD, d MM, yy\'' 265 out << ' });' 266 out << '});' 267 out << '</script>' 263 //out << textFieldElement(attrs,body) 264 textFieldElement.call( 265 attrs, 266 body 267 ) 268 } 269 270 /** 271 * Template form element 272 * @param Map attributes 273 * @param Closure help content 274 */ 275 def speciesElement = { attrs, body -> 276 // render template element 277 baseElement.call( 278 'speciesSelect', 279 attrs, 280 body 281 ) 268 282 } 269 283 … … 285 299 286 300 /** 301 * Template form element 302 * @param Map attributes 303 * @param Closure help content 304 */ 305 def templateElement = { attrs, body -> 306 // render template element 307 baseElement.call( 308 'templateSelect', 309 attrs, 310 body 311 ) 312 } 313 314 /** 287 315 * render a template select element 288 316 * @param Map attrs … … 296 324 attrs.name = 'template' 297 325 } 298 326 299 327 out << select(attrs) 300 328 } 329 330 def templateColumnHeaders = { attrs, body -> 331 TemplateSubjectField.findAll().each() { 332 out << '<div class="column">' + it + '</div>' 333 } 334 } 335 336 def templateColumns = { attrs, body -> 337 def subjectId = attrs.remove('id') 338 339 // for now, fetch them all 340 // also, this should probably be cached to reduce database load... 341 TemplateSubjectField.findAll().each() { 342 out << '<div class="column">' 343 out << '<input type="text">' 344 out << '</div>' 345 } 346 } 301 347 } -
trunk/grails-app/views/wizard/common/_navigation.gsp
r101 r145 15 15 %> 16 16 <div class="navigation"> 17 <g:if test="${page>1}"><wizard:ajaxButton name="previous" value="« prev" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess=" attachHelpTooltips()" class="prevnext" /></g:if>17 <g:if test="${page>1}"><wizard:ajaxButton name="previous" value="« prev" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess="onWizardPage()" class="prevnext" /></g:if> 18 18 <g:if test="${page>1 && page<pages.size}"> | </g:if> 19 <g:if test="${page<pages.size}"><wizard:ajaxButton name="next" value="next »" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess=" attachHelpTooltips()" class="prevnext" /></g:if>19 <g:if test="${page<pages.size}"><wizard:ajaxButton name="next" value="next »" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess="onWizardPage()" class="prevnext" /></g:if> 20 20 </div> -
trunk/grails-app/views/wizard/common/_wizard.gsp
r107 r145 19 19 <g:form action="pages" name="wizardForm" id="wizardForm"> 20 20 <div id="wizardPage"> 21 <wizard:ajaxFlowRedirect form="form#wizardForm" name="next" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess=" attachHelpTooltips()" />21 <wizard:ajaxFlowRedirect form="form#wizardForm" name="next" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess="onWizardPage()" /> 22 22 </div> 23 23 </g:form> -
trunk/grails-app/views/wizard/index.gsp
r98 r145 19 19 <meta name="layout" content="main"/> 20 20 <link rel="stylesheet" href="${resource(dir: 'css', file: 'wizard.css')}"/> 21 <script type="text/javascript" src="${resource(dir: 'js', file: 'development.js')}"></script> 21 22 <script type="text/javascript" src="${resource(dir: 'js', file: 'jquery.qtip-1.0.0-rc3.min.js')}"></script> 23 <script type="text/javascript" src="${resource(dir: 'js', file: 'swfobject.js')}"></script> 22 24 <script type="text/javascript" src="${resource(dir: 'js', file: 'wizard.js')}"></script> 23 25 </head> -
trunk/grails-app/views/wizard/pages/_study.gsp
r140 r145 16 16 %> 17 17 <wizard:pageContent> 18 <wizard:templateSelect />19 18 <wizard:textFieldElement name="title" description="Title" error="title" value="${study?.title}"> 20 19 The title of the study you are creating … … 31 30 The start date of the study 32 31 </wizard:dateElement> 32 <wizard:templateElement name="template" description="Template" value="${study?.template}"> 33 The meta data template to use for this study 34 </wizard:templateElement> 33 35 </wizard:pageContent> -
trunk/grails-app/views/wizard/pages/_subjects.gsp
r140 r145 16 16 %> 17 17 <wizard:pageContent> 18 <wizard:ajaxButton name="add" value="Add" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess=" attachHelpTooltips()" />18 <wizard:ajaxButton name="add" value="Add" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" afterSuccess="onWizardPage()" /> 19 19 <input name="addNumber" size="4" maxlength="4" value="1"> 20 20 subjects of species 21 21 <wizard:speciesSelect name="addSpecies" /> 22 22 <g:if test="${subjects}"> 23 <div class="subjects"> 23 <div class="table"> 24 <div class="header"> 25 <div class="firstColumn">#</div> 26 <div class="column">name</div> 27 <div class="column">species</div> 28 <wizard:templateColumnHeaders/> 29 </div> 24 30 <g:each var="subject" status="i" in="${subjects}"> 25 <div class=" subject<g:if test="${i>0}"> topborder</g:if>">26 <div class=" row">${i+1}</div>27 <div class=" row"><g:textField name="test" value="test" size="12" maxlength="12" /></div>28 <div class=" row">29 <wizard:speciesSelect value="${subject.species}" name="s pecies_${i}" />31 <div class="row"> 32 <div class="firstColumn">${i}</div> 33 <div class="column"><g:textField name="subject_${i}_name" value="${subject.name}" size="12" maxlength="12" /></div> 34 <div class="column"> 35 <wizard:speciesSelect value="${subject.species}" name="subject_${i}_species" /> 30 36 </div> 31 < div class="row">${subject.name}</div>37 <wizard:templateColumns id="${i}" /> 32 38 </div> 33 39 </g:each> -
trunk/grails-app/views/wizard/pages/_three.gsp
r138 r145 31 31 </wizard:textFieldElement> 32 32 <wizard:dateElement name="startDate" description="Date element" error="startDate" value="${study?.startDate}"> 33 <object width="320" height="265"><param name="movie" value="http://www.youtube.com/v/2WNrx2jq184&hl=en_US&fs=1&rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/2WNrx2jq184&hl=en_US&fs=1&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="320" height="265"></embed></object>33 [youtube:2WNrx2jq184] 34 34 </wizard:dateElement> 35 35 </wizard:pageContent> -
trunk/web-app/css/wizard.css
r138 r145 95 95 } 96 96 97 /** START :: HELP **/ 97 98 .wizard .element .helpIcon { 98 99 display: inline-block; … … 122 123 display: none; 123 124 } 124 125 .wizard .subjects { 126 display: block; 127 /* 128 background-color: #c0ddea; 129 border: 1px solid #575a5d; 130 margin-top: 2px; 131 */ 125 /** END :: HELP **/ 126 127 /* START :: TABLE */ 128 .wizard .table { 129 display: block; 132 130 border: 1px solid #575a5d; 133 131 background-color: #ebf7fc; 134 132 margin-top: 10px; 135 } 136 137 .wizard .subjects .subject { 138 display: block; 139 } 140 141 .wizard .topborder { 133 font-size: 11px; 134 overflow: hidden; 135 } 136 137 .wizard .column { 138 display: inline-block; 139 padding: 2px 4px; 140 width: 120px; 141 } 142 143 .wizard .firstColumn { 144 display: inline-block; 145 padding: 2px 4px; 146 width: 40px; 147 } 148 149 .wizard .table .header { 150 display: block; 151 background-color: #006DBA; 152 color: #fff; 153 font-weight: bold; 154 width: 2000px; 155 } 156 157 .wizard .table .header .column .first { 158 width: 40px; 159 } 160 161 .wizard .table .row { 162 display: block; 163 width: 2000px; 164 height: 28px; 142 165 border-top: 1px solid #8e908f; 143 166 } 144 167 145 .wizard .subjects .subject .row { 146 display: inline-block; 147 /* 148 border: 1px solid #575a5d; 149 margin: 1px 1px 0px 0px; 150 */ 151 } 168 .wizard .table input, .wizard .table select { 169 border: 1px solid #8e908f; 170 margin: 2px 0; 171 padding: 2px 4px; 172 background-color: transparent; 173 width: 100px; 174 } 175 176 .wizard .table .highlight, .wizard .table .highlight input, .wizard .table .highlight select { 177 background-color: #006DBA; 178 color: #fff; 179 } 180 /* END :: TABLE */ 181 182 183 .selectable .ui-selecting { background: #FECA40; } 184 .selectable .ui-selected { background: #F39814; color: white; } 185 .selectable { list-style-type: none; margin: 0; padding: 0; width: 200px; } 186 .selectable li { margin: 3px; padding: 0.4em; font-size: 1em; height: 18px; } 187 188 .wizard .grouping { 189 display: block; 190 } 191 192 .wizard .subjects { 193 display: inline-block; 194 } 195 196 .wizard .groups { 197 display: inline-block; 198 height: 100% 199 } 200 201 .droppable { width: 300px; } 202 203 -
trunk/web-app/js/wizard.js
r107 r145 14 14 */ 15 15 $(document).ready(function() { 16 // check if user is using Firefox 3.6 and warm the user 17 // about the XMLHttpRequest bug that causes the wizard to break... 16 18 re = /Firefox\/3\.6/gi; 17 19 if (navigator.userAgent.match(re)) { … … 22 24 23 25 // attach Tooltips 26 onWizardPage(); 27 }); 28 29 // runs when document is ready or a wizard action has been performs 30 // @see _wizard.gsp, _navigation.gsp, _subjects.gsp 31 function onWizardPage() { 32 // attach help tooltips 33 //insertYoutubePlayers(); 24 34 attachHelpTooltips(); 25 }); 35 attachDatePickers(); 36 attachTableEvents(); 37 attachGroupingEvents(); 38 } 26 39 27 40 // attach help tooltips … … 29 42 // attach help action on all wizard help icons 30 43 $('div#wizard').find('div.helpIcon').each(function() { 31 $(this).qtip({ 44 helpIcon = $(this) 45 helpContent = helpIcon.parent().parent().find('div.helpContent') 46 47 // handle special content 48 var specialContent = helpContent.html().match(/\[([^:]+)\:([^\]]+)\]/) 49 if (specialContent) { 50 // replace content by calling a helper function 51 eval(specialContent[1]+"('"+specialContent[2]+"',helpContent)"); 52 } 53 54 // attach tooltip 55 helpIcon.qtip({ 32 56 content: 'leftMiddle', 33 57 position: { … … 47 71 name: 'blue' 48 72 }, 49 content: $(this).parent().parent().find('div.helpContent').html(),73 content: helpContent.html(), 50 74 show: 'mouseover', 51 hide: 'mouseout' 75 hide: 'mouseout', 76 api: { 77 beforeShow: function() { 78 // not used at this moment 79 } 80 } 52 81 }) 82 83 // remove helpcontent div as we don't need it anymore 84 helpContent.remove(); 53 85 }); 54 86 } 87 88 // insert a youtube player in a certain element 89 function youtube(video,element) { 90 // insert a div we will replace with a youtube player 91 element.html("<div id='"+video+"'></div>") 92 93 // insert youtube player 94 var params = { allowScriptAccess: "always" }; 95 var atts = { id: 'myytplayer_'+video }; 96 swfobject.embedSWF("http://www.youtube.com/v/"+video+"?enablejsapi=1&playerapiid=ytplayer_"+video, 97 video, "200", "150", "8", null, null, params, atts) 98 } 99 100 // when a youtube player is ready, play the video 101 function onYouTubePlayerReady(playerId) { 102 ytplayer = document.getElementById("my"+playerId); 103 ytplayer.playVideo(); 104 } 105 106 // add datepickers to date fields 107 function attachDatePickers() { 108 $('div#wizard').find("input[type=text][name$='Date']").each(function() { 109 $(this).datepicker({ 110 dateFormat : 'dd/mm/yy', 111 altField : '#' + $(this).attr('name') + 'Example', 112 altFormat : 'DD, d MM, yy' 113 }); 114 }) 115 } 116 117 // attach subject events 118 function attachTableEvents() { 119 $('div#wizard').find('div.row').each(function() { 120 $(this).hover( 121 function() { 122 $(this).addClass('highlight'); 123 }, 124 function() { 125 $(this).removeClass('highlight'); 126 } 127 ) 128 }); 129 } 130 131 function attachGroupingEvents() { 132 console.log('attach drag and drop events') 133 134 $(".groups").find('div.droppable').droppable({ 135 drop: function(event, ui) { 136 $(this).addClass('ui-state-highlight').find('p').html('Dropped!'); 137 } 138 }); 139 140 141 $(".subjects").find(".selectable").selectable({ 142 stop: function() { 143 // remove draggable from unselected items 144 $('.ui-selectee:not(.ui-selected)', this).each(function() { 145 $(this).draggable('destroy') 146 }) 147 148 // attach draggable to selected items 149 var subjects = $('.ui-selected', this); 150 subjects.each(function() { 151 var d = this 152 var D = $(this) 153 var content = D.html() 154 var offset = D.offset() 155 156 D.draggable({ 157 revert: 'invalid', 158 start: function(event, ui) { 159 // change dragged item's content to summarize selected items 160 D.html(subjects.length + ' subjects'); 161 162 // hide the other items 163 subjects.each(function() { 164 if (this != d) { 165 $(this).animate( 166 { 167 opacity: 0 168 }, 169 200 170 ); 171 } 172 }); 173 }, 174 stop: function(event, ui) { 175 // restore original content 176 D.html(content); 177 178 // make selected items visible 179 subjects.each(function() { 180 if (this != d) { 181 $(this).animate( 182 { 183 opacity: 100 184 }, 185 200 186 ); 187 } 188 }); 189 } 190 }); 191 }); 192 } 193 }); 194 } 195
Note: See TracChangeset
for help on using the changeset viewer.