source: trunk/grails-app/taglib/dbnp/studycapturing/WizardTagLib.groovy @ 565

Last change on this file since 565 was 565, checked in by duh, 11 years ago
  • upon adding a term the termEditorController now also adds the ontology when the ontologies doest not exist
  • extended the Ontology domain class with functionality to instantiate the Ontology class using the versioned ncboId
  • added 'addDummy' support to termElements and templateElements which caused the study and subject page not to work properly on an empty database (these fields were not rendered so exceptions were thrown).
  • this fixed bug # 107 in half... now the species select (ONTOLOGYTERM) somehow does not show the species in the database, still needs to be fixed
  • Property svn:keywords set to Date Author Rev
File size: 33.4 KB
Line 
1package dbnp.studycapturing
2
3import org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLib
4import dbnp.studycapturing.*
5import dbnp.data.*
6import cr.co.arquetipos.crypto.Blowfish
7
8/**
9 * Wizard tag library
10 *
11 * @author Jeroen Wesbeek
12 * @since 20100113
13 * @package wizard
14 *
15 * Revision information:
16 * $Rev: 565 $
17 * $Author: duh $
18 * $Date: 2010-06-15 15:45:16 +0000 (di, 15 jun 2010) $
19 */
20class WizardTagLib extends JavascriptTagLib {
21        // define the tag namespace (e.g.: <wizard:action ... />
22        static namespace = "wizard"
23
24        // define the AJAX provider to use
25        static ajaxProvider = "jquery"
26
27        // define default text field width
28        static defaultTextFieldSize = 25;
29
30        /**
31         * ajaxButton tag, this is a modified version of the default
32         * grails submitToRemote tag to work with grails webflows.
33         * Usage is identical to submitToRemote with the only exception
34         * that a 'name' form element attribute is required. E.g.
35         * <wizard:ajaxButton name="myAction" value="myButton ... />
36         *
37         * you can also provide a javascript function to execute after
38         * success. This behaviour differs from the default 'after'
39         * action which always fires after a button press...
40         *
41         * @see http://blog.osx.eu/2010/01/18/ajaxifying-a-grails-webflow/
42         * @see http://www.grails.org/WebFlow
43         * @see http://www.grails.org/Tag+-+submitToRemote
44         * @todo perhaps some methods should be moved to a more generic
45         *        'webflow' taglib or plugin
46         * @param Map attributes
47         * @param Closure body
48         */
49        def ajaxButton = {attrs, body ->
50                // get the jQuery version
51                def jQueryVersion = grailsApplication.getMetadata()['plugins.jquery']
52
53                // fetch the element name from the attributes
54                def elementName = attrs['name'].replaceAll(/ /, "_")
55
56                // javascript function to call after success
57                def afterSuccess = attrs['afterSuccess']
58
59                // src parameter?
60                def src = attrs['src']
61                def alt = attrs['alt']
62
63                // generate a normal submitToRemote button
64                def button = submitToRemote(attrs, body)
65
66                /**
67                 * as of now (grails 1.2.0 and jQuery 1.3.2.4) the grails webflow does
68                 * not properly work with AJAX as the submitToRemote button does not
69                 * handle and submit the form properly. In order to support webflows
70                 * this method modifies two parts of a 'normal' submitToRemote button:
71                 *
72                 * 1) replace 'this' with 'this.form' as the 'this' selector in a button
73                 *    action refers to the button and / or the action upon that button.
74                 *    However, it should point to the form the button is part of as the
75                 *    the button should submit the form data.
76                 * 2) prepend the button name to the serialized data. The default behaviour
77                 *    of submitToRemote is to remove the element name altogether, while
78                 *    the grails webflow expects a parameter _eventId_BUTTONNAME to execute
79                 *    the appropriate webflow action. Hence, we are going to prepend the
80                 *    serialized formdata with an _eventId_BUTTONNAME parameter.
81                 */
82                if (jQueryVersion =~ /^1.([1|2|3]).(.*)/) {
83                        // fix for older jQuery plugin versions
84                        button = button.replaceFirst(/data\:jQuery\(this\)\.serialize\(\)/, "data:\'_eventId_${elementName}=1&\'+jQuery(this.form).serialize()")
85                } else {
86                        // as of jQuery plugin version 1.4.0.1 submitToRemote has been modified and the
87                        // this.form part has been fixed. Consequently, our wrapper has changed as well...
88                        button = button.replaceFirst(/data\:jQuery/, "data:\'_eventId_${elementName}=1&\'+jQuery")
89                }
90
91                // add an after success function call?
92                // usefull for performing actions on success data (hence on refreshed
93                // wizard pages, such as attaching tooltips)
94                if (afterSuccess) {
95                        button = button.replaceFirst(/\.html\(data\)\;/, '.html(data);' + afterSuccess + ';')
96                }
97
98                // got an src parameter?
99                if (src) {
100                        def replace = 'type="image" src="' + src + '"'
101
102                        if (alt) replace = replace + ' alt="' + alt + '"'
103
104                        button = button.replaceFirst(/type="button"/, replace)
105                }
106
107                // replace double semi colons
108                button = button.replaceAll(/;{2,}/, ';')
109
110                // render button
111                out << button
112        }
113
114        /**
115         * generate a ajax submit JavaScript
116         * @see WizardTagLib::ajaxFlowRedirect
117         * @see WizardTagLib::baseElement (ajaxSubmitOnChange)
118         */
119        def ajaxSubmitJs = {attrs, body ->
120                // define AJAX provider
121                setProvider([library: ajaxProvider])
122
123                // got a function name?
124                def functionName = attrs.remove('functionName')
125                if (functionName && !attrs.get('name')) {
126                        attrs.name = functionName
127                }
128
129                // generate an ajax button
130                def button = this.ajaxButton(attrs, body)
131
132                // strip the button part to only leave the Ajax call
133                button = button.replaceFirst(/<[^\"]*onclick=\"/, '')
134                button = button.replaceFirst(/return false.*/, '')
135
136                // change form if a form attribute is present
137                if (attrs.get('form')) {
138                        // Old way
139                        /*
140                        button = button.replaceFirst(/this\.form/,
141                                "\\\$('" + attrs.get('form') + "')"
142                        )
143                        */
144
145                        button = button.replace("jQuery(this).parents('form:first')",
146                                "\$('" + attrs.get('form') + "')"
147                        )
148                }
149
150                out << button
151        }
152
153        /**
154         * generate ajax webflow redirect javascript
155         *
156         * As we have an Ajaxified webflow, the initial wizard page
157         * cannot contain a wizard form, as upon a failing submit
158         * (e.g. the form data does not validate) the form should be
159         * shown again. However, the Grails webflow then renders the
160         * complete initial wizard page into the success div. As this
161         * ruins the page layout (a page within a page) we want the
162         * initial page to redirect to the first wizard form to enter
163         * the webflow correctly. We do this by emulating an ajax post
164         * call which updates the wizard content with the first wizard
165         * form.
166         *
167         * Usage: <wizard:ajaxFlowRedirect form="form#wizardForm" name="next" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" />
168         * form = the form identifier
169         * name = the action to execute in the webflow
170         * update = the divs to update upon success or error
171         *
172         * OR: to generate a JavaScript function you can call yourself, use 'functionName' instead of 'name'
173         *
174         * Example initial webflow action to work with this javascript:
175         * ...
176         * mainPage {*  render(view: "/wizard/index")
177         *      onRender {*             flow.page = 1
178         *}*    on("next").to "pageOne"
179         *}* ...
180         *
181         * @param Map attributes
182         * @param Closure body
183         */
184        def ajaxFlowRedirect = {attrs, body ->
185                // generate javascript
186                out << '<script type="text/javascript">'
187                out << '$(document).ready(function() {'
188                out << ajaxSubmitJs(attrs, body)
189                out << '});'
190                out << '</script>'
191        }
192
193        /**
194         * render the content of a particular wizard page
195         * @param Map attrs
196         * @param Closure body  (help text)
197         */
198        def pageContent = {attrs, body ->
199                // define AJAX provider
200                setProvider([library: ajaxProvider])
201
202                // render new body content
203                //      - this JavaScript variable is used by baseElement to workaround an IE
204                //        specific issue (double submit on onchange events). The hell with IE!
205                //        @see baseElement
206                out << '<script type="text/javascript">var lastRequestTime = 0;</script>'
207                out << render(template: "/wizard/common/tabs")
208                out << '<div class="content">'
209                out << body()
210                out << '</div>'
211                out << render(template: "/wizard/common/navigation")
212                out << render(template: "/wizard/common/error")
213        }
214
215        /**
216         * generate a base form element
217         * @param String inputElement name
218         * @param Map attributes
219         * @param Closure help content
220         */
221        def baseElement = {inputElement, attrs, help ->
222println ".rendering [" + inputElement + "] with name [" + attrs.get('name') + "] and value [" + ((attrs.value) ? attrs.get('value').toString() : "-") + "]"
223                // work variables
224                def internetExplorer = (request.getHeader("User-Agent") =~ /MSIE/)
225                def description = attrs.remove('description')
226                def addExampleElement = attrs.remove('addExampleElement')
227                def addExample2Element = attrs.remove('addExample2Element')
228                def helpText = help().trim()
229
230                // got an ajax onchange action?
231                def ajaxOnChange = attrs.remove('ajaxOnChange')
232                if (ajaxOnChange) {
233                        if (!attrs.onChange) attrs.onChange = ''
234
235                        // add onChange AjaxSubmit javascript
236                        if (internetExplorer) {
237                                //              - somehow IE submits these onchanges twice which messes up some parts of the wizard
238                                //                (especially the events page). In order to bypass this issue I have introduced an
239                                //                if statement utilizing the 'before' and 'after' functionality of the submitToRemote
240                                //                function. This check expects lastRequestTime to be in the global Javascript scope,
241                                //                (@see pageContent) and calculates the time difference in miliseconds between two
242                                //                onChange executions. If this is more than 100 miliseconds the request is executed,
243                                //                otherwise it will be ignored... --> 20100527 - Jeroen Wesbeek
244                                attrs.onChange += ajaxSubmitJs(
245                                        [
246                                                before: "var execute=true;try { var currentTime=new Date().getTime();execute = ((currentTime-lastRequestTime) > 100);lastRequestTime=currentTime;  } catch (e) {};if (execute) { 1",
247                                                after: "}",
248                                                functionName: ajaxOnChange,
249                                                url: attrs.get('url'),
250                                                update: attrs.get('update'),
251                                                afterSuccess: attrs.get('afterSuccess')
252                                        ],
253                                        ''
254                                )
255                        } else {
256                                // this another W3C browser that actually behaves as expected... damn you IE, DAMN YOU!
257                                attrs.onChange += ajaxSubmitJs(
258                                        [
259                                                functionName: ajaxOnChange,
260                                                url: attrs.get('url'),
261                                                update: attrs.get('update'),
262                                                afterSuccess: attrs.get('afterSuccess')
263                                        ],
264                                        ''
265                                )
266                        }
267                }
268
269                // execute inputElement call
270                def renderedElement = "$inputElement"(attrs)
271
272                // if false, then we skip this element
273                if (!renderedElement) return false
274
275                // render a form element
276                if (attrs.get('elementId')) {
277                out << '<div class="element" id="'+ attrs.remove('elementId') +'">'
278                } else {
279                        out << '<div class="element">'
280                }
281                out << ' <div class="description">'
282                out << description
283                out << ' </div>'
284                out << ' <div class="input">'
285                out << renderedElement
286                if (helpText.size() > 0) {
287                        out << '        <div class="helpIcon"></div>'
288                }
289
290                // add an disabled input box for feedback purposes
291                // @see dateElement(...)
292                if (addExampleElement) {
293                        def exampleAttrs = new LinkedHashMap()
294                        exampleAttrs.name = attrs.get('name') + 'Example'
295                        exampleAttrs.class = 'isExample'
296                        exampleAttrs.disabled = 'disabled'
297                        exampleAttrs.size = 30
298                        out << textField(exampleAttrs)
299                }
300
301                // add an disabled input box for feedback purposes
302                // @see dateElement(...)
303                if (addExample2Element) {
304                        def exampleAttrs = new LinkedHashMap()
305                        exampleAttrs.name = attrs.get('name') + 'Example2'
306                        exampleAttrs.class = 'isExample'
307                        exampleAttrs.disabled = 'disabled'
308                        exampleAttrs.size = 30
309                        out << textField(exampleAttrs)
310                }
311
312                out << ' </div>'
313
314                // add help content if it is available
315                if (helpText.size() > 0) {
316                        out << '  <div class="helpContent">'
317                        out << '    ' + helpText
318                        out << '  </div>'
319                }
320
321                out << '</div>'
322        }
323
324        /**
325         * render an ajaxButtonElement
326         * @param Map attrs
327         * @param Closure body  (help text)
328         */
329        def ajaxButtonElement = { attrs, body ->
330                baseElement.call(
331                        'ajaxButton',
332                        attrs,
333                        body
334                )
335        }
336
337        /**
338         * render a textFieldElement
339         * @param Map attrs
340         * @param Closure body  (help text)
341         */
342        def textFieldElement = {attrs, body ->
343                // set default size, or scale to max length if it is less than the default size
344                if (!attrs.get("size")) {
345                        if (attrs.get("maxlength")) {
346                                attrs.size = ((attrs.get("maxlength") as int) > defaultTextFieldSize) ? defaultTextFieldSize : attrs.get("maxlength")
347                        } else {
348                                attrs.size = defaultTextFieldSize
349                        }
350                }
351
352                // render template element
353                baseElement.call(
354                        'textField',
355                        attrs,
356                        body
357                )
358        }
359
360        /**
361         * render a textAreaElement
362         * @param Map attrs
363         * @param Closure body  (help text)
364         */
365        def textAreaElement = {attrs, body ->
366                // set default size, or scale to max length if it is less than the default size
367
368                // render template element
369                baseElement.call(
370                        'textArea',
371                        attrs,
372                        body
373                )
374        }
375
376
377        /**
378         * render a select form element
379         * @param Map attrs
380         * @param Closure body  (help text)
381         */
382        def selectElement = {attrs, body ->
383                baseElement.call(
384                        'select',
385                        attrs,
386                        body
387                )
388        }
389
390        /**
391         * render a checkBox form element
392         * @param Map attrs
393         * @param Closure body  (help text)
394         */
395        def checkBoxElement = {attrs, body ->
396                baseElement.call(
397                        'checkBox',
398                        attrs,
399                        body
400                )
401        }
402
403        /**
404         * render a set of radio form elements
405         * @param Map attrs
406         * @param Closure body  (help text)
407         */
408        def radioElement = { attrs, body ->
409                baseElement.call(
410                        'radioList',
411                        attrs,
412                        body
413                )
414        }
415
416        /**
417         * render a set of radio elements
418         * @param Map attrs
419         * @param Closure body  (help text)
420         */
421        def radioList = { attrs ->
422                def checked = true
423
424                attrs.elements.each {
425                        out << radio(
426                                name: attrs.name,
427                                value: it,
428                                checked: (attrs.value == it || (!attrs.value && checked))
429                        )
430                        out << it
431                        checked = false
432                }
433        }
434
435        /**
436         * render a dateElement
437         * NOTE: datepicker is attached through wizard.js!
438         * @param Map attrs
439         * @param Closure body  (help text)
440         */
441        def dateElement = {attrs, body ->
442                // transform value?
443                if (attrs.value instanceof Date) {
444                        // transform date instance to formatted string (dd/mm/yyyy)
445                        attrs.value = String.format('%td/%<tm/%<tY', attrs.value)
446                }
447
448                // add 'rel' field to identity the datefield using javascript
449                attrs.rel = 'date'
450
451                // set some textfield values
452                attrs.maxlength = (attrs.maxlength) ? attrs.maxlength : 10
453                attrs.addExampleElement = true
454
455                // render a normal text field
456                //out << textFieldElement(attrs,body)
457                textFieldElement.call(
458                        attrs,
459                        body
460                )
461        }
462
463        /**
464         * render a dateElement
465         * NOTE: datepicker is attached through wizard.js!
466         * @param Map attrs
467         * @param Closure body  (help text)
468         */
469        def timeElement = {attrs, body ->
470                // transform value?
471                if (attrs.value instanceof Date) {
472                        // transform date instance to formatted string (dd/mm/yyyy)
473                        attrs.value = String.format('%td/%<tm/%<tY %<tH:%<tM', attrs.value)
474                }
475
476                // add 'rel' field to identity the field using javascript
477                attrs.rel = 'datetime'
478
479                attrs.addExampleElement = true
480                attrs.addExample2Element = true
481                attrs.maxlength = 16
482
483                // render a normal text field
484                //out << textFieldElement(attrs,body)
485                textFieldElement.call(
486                        attrs,
487                        body
488                )
489        }
490
491        /**
492         * Button form element
493         * @param Map attributes
494         * @param Closure help content
495         */
496        def buttonElement = {attrs, body ->
497                // render template element
498                baseElement.call(
499                        'ajaxButton',
500                        attrs,
501                        body
502                )
503        }
504
505
506        /**
507         * Term form element
508         * @param Map attributes
509         * @param Closure help content
510         */
511        def termElement = { attrs, body ->
512                // render term element
513                baseElement.call(
514                        'termSelect',
515                        attrs,
516                        body
517                )
518        }
519
520        /**
521         * Term select element
522         * @param Map attributes
523         */
524        def termSelect = { attrs ->
525                def from = []
526
527                // got ontologies?
528                if (attrs.ontologies) {
529                        // are the ontologies a string?
530                        if (attrs.ontologies instanceof String) {
531                                attrs.ontologies.split(/\,/).each() { ncboId ->
532                                        // trim the id
533                                        ncboId.trim()
534
535                                        // fetch all terms for this ontology
536                                        def ontology = Ontology.findAllByNcboId(ncboId)
537
538                                        // does this ontology exist?
539                                        if (ontology) {
540                                                ontology.each() {
541                                                        Term.findAllByOntology(it).each() {
542                                                                // key = ncboId:concept-id
543                                                                from[ from.size() ] = it.name
544                                                        }
545                                                }
546                                        }
547                                }
548                        } else if (attrs.ontologies instanceof Set) {
549                                // are they a set instead?
550                                def ontologyList = ""
551
552                                // iterate through set
553                                attrs.ontologies.each() { ontology ->
554                                        if (ontology) {
555                                                ontologyList += ontology.ncboId + ","
556
557                                                Term.findAllByOntology(ontology).each() {
558                                                        from[ from.size() ] = it.name
559                                                }
560
561                                                // strip trailing comma
562                                                attrs.ontologies = ontologyList[0..-2]
563                                        }
564                                }
565                        }
566
567                        // sort alphabetically
568                        from.sort()
569
570                        // add a dummy field?
571                        if (attrs.remove('addDummy')) {
572                                from.add(0,'')
573                        }
574
575                        // define 'from'
576                        attrs.from = from
577
578                        // add 'rel' attribute
579                        attrs.rel = 'term'
580
581                        out << select(attrs)
582                } else {
583                        out << "<b>ontologies missing!</b>"
584                }
585        }
586
587        /**
588         * Ontology form element
589         * @param Map attributes
590         * @param Closure help content
591         */
592        def ontologyElement = { attrs, body ->
593                // @see http://www.bioontology.org/wiki/index.php/NCBO_Widgets#Term-selection_field_on_a_form
594                // @see ontology-chooser.js, table-editor.js
595                baseElement.call(
596                        'textField',
597                        [
598                            name: attrs.name,
599                                value: attrs.value,
600                                description: attrs.description,
601                                rel: 'ontology-' + ((attrs.ontology) ? attrs.ontology : 'all'),
602                                size: 25
603                        ],
604                        body
605                )
606                out << hiddenField(
607                        name: attrs.name + '-concept_id'
608                )
609                out << hiddenField(
610                        name: attrs.name + '-ontology_id'
611                )
612                out << hiddenField(
613                        name: attrs.name + '-full_id'
614                )
615        }
616
617        /**
618         * Study form element
619         * @param Map attributes
620         * @param Closure help content
621         */
622        def studyElement = { attrs, body ->
623                // render study element
624                baseElement.call(
625                        'studySelect',
626                        attrs,
627                        body
628                )
629        }
630
631        /**
632         * render a study select element
633         * @param Map attrs
634         */
635        def studySelect = { attrs ->
636                // for now, just fetch all studies
637                attrs.from = Study.findAll()
638
639                // got a name?
640                if (!attrs.name) {
641                        attrs.name = "study"
642                }
643
644                // got result?
645                if (attrs.from.size() > 0) {
646                        out << select(attrs)
647                } else {
648                        // no, return false to make sure this element
649                        // is not rendered in the template
650                        return false
651                }
652        }
653
654        /**
655         * Template form element
656         * @param Map attributes
657         * @param Closure help content
658         */
659        def templateElement = {attrs, body ->
660                // add a rel element if it does not exist
661                if (!attrs.rel) {
662                        attrs.rel = 'template'
663                }
664               
665                // render template element
666                baseElement.call(
667                        'templateSelect',
668                        attrs,
669                        body
670                )
671        }
672
673        /**
674         * render a template select element
675         * @param Map attrs
676         */
677        def templateSelect = {attrs ->
678                def entity = attrs.remove('entity')
679
680                // add the entity class name to the element
681                // do we have crypto information available?
682                if (grailsApplication.config.crypto) {
683                        // generate a Blowfish encrypted and Base64 encoded string.
684                        attrs['entity'] = Blowfish.encryptBase64(
685                                entity.toString().replaceAll(/^class /, ''),
686                                grailsApplication.config.crypto.shared.secret
687                        )
688                } else {
689                        // base64 only; this is INSECURE! As this class
690                        // is instantiated elsewehere. Possibly exploitable!
691                        attrs['entity'] = entity.toString().replaceAll(/^class /, '').bytes.encodeBase64()
692                }
693
694                // fetch templates
695                attrs.from = (entity) ? Template.findAllByEntity(entity) : Template.findAll()
696
697                // got a name?
698                if (!attrs.name) {
699                        attrs.name = 'template'
700                }
701
702                // got result?
703                if (attrs.from.size() > 0 || attrs.get('addDummy')) {
704                        // transform all values into strings
705                        def from = []
706                        attrs.from.each { from[ from.size() ] = it.toString() }
707
708                        // sort alphabetically
709                        from.sort()
710
711                        // add a dummy field?
712                        if (attrs.remove('addDummy')) {
713                                from.add(0,'')
714                        }
715
716                        // set attributes
717                        attrs.from = from
718                        attrs.value = (attrs.value) ? attrs.value.toString() : ''
719
720                        // output select element
721                        out << select(attrs)
722                } else {
723                        // no, return false to make sure this element
724                        // is not rendered in the template
725                        return false
726                }
727        }
728
729
730        /**
731         * File form element
732         * @param Map attributes
733         * @param Closure help content
734         */
735        def fileFieldElement = { attrs, body ->
736                // render term element
737                baseElement.call(
738                        'fileField',
739                        attrs,
740                        body
741                )
742        }
743
744        /**
745         * file field.
746         * @param attributes
747         */
748        def fileField = { attrs ->
749                /*
750                out << '<input type="file" name="' + attrs.name + '"/>'
751                if( attrs.value ) {
752                        out << '<a href="' + resource(dir: '') + '/file/get/' + attrs.value + '" class="isExample">Now contains: ' + attrs.value + '</a>'
753                }
754                */
755
756                out << '<div id="upload_button_' + attrs.name + '" class="upload_button">Upload</div>';
757                out << '<input type="hidden" name="' + attrs.name + '" id="' + attrs.name + '" value="' + attrs.value + '">';
758                out << '<div id="' + attrs.name + 'Example" class="upload_info"></div>';
759                out << '<script type="text/javascript">';
760                out << '  $(document).ready( function() { ';
761                out << '    var filename = "' + attrs.value + '";';
762                out << '    fileUploadField( "' + attrs.name + '" );';
763                out << '    if( filename != "" ) {';
764                out << '      $("#' + attrs.name + 'Example").html("Current file: " + createFileHTML( filename ) )';
765                out << '    }';
766                out << '  } );';
767                out << "</script>\n";
768        }
769
770        /**
771         * Protocol form element
772         * @param Map attributes
773         * @param Closure help content
774         */
775        def protocolElement = {attrs, body ->
776                // render protocol element
777                baseElement.call(
778                        'protocolSelect',
779                        attrs,
780                        body
781                )
782        }
783
784        /**
785         * render a protocol select element
786         * @param Map attrs
787         */
788        def protocolSelect = {attrs ->
789                // fetch all protocold
790                attrs.from = Protocol.findAll() // for now, all protocols
791
792                // got a name?
793                if (!attrs.name) {
794                        attrs.name = 'protocol'
795                }
796
797                out << select(attrs)
798        }
799
800        def show = {attrs ->
801                // is object parameter set?
802                def o = attrs.object
803
804                println o.getProperties();
805                o.getProperties().each {
806                        println it
807                }
808
809                out << "!! test version of 'show' tag !!"
810        }
811
812        /**
813         * render table headers for all subjectFields in a template
814         * @param Map attributes
815         */
816        def templateColumnHeaders = { attrs ->
817                def entity              = (attrs.get('entity'))
818                def template    = (entity && entity instanceof TemplateEntity) ? entity.template : null
819
820                // got a template?
821                if (template) {
822                        // render template fields
823                        entity.giveFields().each() {
824                                def ucName              = it.name[0].toUpperCase() + it.name.substring(1)
825
826                                out << '<div class="' + attrs.get('class') + '">' + ucName + (it.unit ? " (${it.unit})" : '')
827                                if (it.comment) {
828                                        out << '<div class="helpIcon"></div>'
829                                        out << '<div class="helpContent">' + it.comment + '</div>'
830                                }
831                                out << '</div>'
832                        }
833                }
834        }
835
836        def templateColumns = { attrs ->
837                // render template fields as columns
838                attrs.renderType = 'column'
839                out << renderTemplateFields(attrs)
840        }
841
842        def templateElements = { attrs ->
843                // render template fields as form elements
844                attrs.renderType = 'element'
845                out << renderTemplateFields(attrs)
846        }
847
848        /**
849         * render form elements based on an entity's template
850         * @param Map attributes
851         * @param String body
852         */
853        def renderTemplateFields = { attrs ->
854                def renderType  = attrs.remove('renderType')
855                def entity              = (attrs.get('entity'))
856                def prependName = (attrs.get('name')) ? attrs.remove('name')+'_' : ''
857                def template    = (entity && entity instanceof TemplateEntity) ? entity.template : null
858                def inputElement= null
859                def addDummy    = (attrs.get('addDummy')) ? true : false
860
861                // got a template?
862                if (template) {
863                        // render template fields
864                        entity.giveFields().each() {
865                                def fieldValue  = entity.getFieldValue(it.name)
866                                def helpText    = (it.comment && renderType == 'element') ? it.comment : ''
867                                def ucName              = it.name[0].toUpperCase() + it.name.substring(1)
868
869                                // output column opening element?
870                                if (renderType == 'column') {
871                                        out << '<div class="' + attrs.get('class') + '">'
872                                }
873
874println ".SHOWING "+it.type.toString()
875println it.ontologies
876
877                                switch (it.type.toString()) {
878                                        case ['STRING', 'INTEGER', 'FLOAT', 'DOUBLE']:
879                                                inputElement = (renderType == 'element') ? 'textFieldElement' : 'textField'
880                                                out << "$inputElement"(
881                                                        description: ucName,
882                                                        name: prependName + it.escapedName(),
883                                                        value: fieldValue
884                                                ){helpText}
885                                                break
886                                        case 'TEXT':
887                                                inputElement = (renderType == 'element') ? 'textAreaElement' : 'textField'
888                                                out << "$inputElement"(
889                                                        description: ucName,
890                                                        name: prependName + it.escapedName(),
891                                                        value: fieldValue
892                                                ){helpText}
893                                                break
894                                        case 'STRINGLIST':
895                                                inputElement = (renderType == 'element') ? 'selectElement' : 'select'
896                                                if (!it.listEntries.isEmpty()) {
897                                                        out << "$inputElement"(
898                                                                description: ucName,
899                                                                name: prependName + it.escapedName(),
900                                                                from: it.listEntries,
901                                                                value: fieldValue
902                                                        ){helpText}
903                                                } else {
904                                                        out << '<span class="warning">no values!!</span>'
905                                                }
906                                                break
907                                        case 'ONTOLOGYTERM':
908                                                // @see http://www.bioontology.org/wiki/index.php/NCBO_Widgets#Term-selection_field_on_a_form
909                                                // @see ontology-chooser.js
910                                                inputElement = (renderType == 'element') ? 'termElement' : 'termSelect'
911
912                                                if (it.ontologies) {
913                                                        out << "$inputElement"(
914                                                                description     : ucName,
915                                                                name            : prependName + it.escapedName(),
916                                                                value           : fieldValue.toString(),
917                                                                ontologies      : it.ontologies,
918                                                                addDummy        : addDummy
919                                                        ){helpText}
920                                                } else {
921                                                        out << "$inputElement"(
922                                                                description     : ucName,
923                                                                name            : prependName + it.escapedName(),
924                                                                value           : fieldValue.toString(),
925                                                                addDummy        : addDummy
926                                                        ){helpText}
927                                                }
928                                                break
929                                        case 'ONTOLOGYTERM-old':
930                                                // @see http://www.bioontology.org/wiki/index.php/NCBO_Widgets#Term-selection_field_on_a_form
931                                                // @see ontology-chooser.js
932                                                inputElement = (renderType == 'element') ? 'textFieldElement' : 'textField'
933                                                out << "$inputElement"(
934                                                        name: prependName + it.escapedName(),
935                                                        value: fieldValue,
936                                                        rel: 'ontology-all',
937                                                        size: 100
938                                                )
939                                                out << hiddenField(
940                                                        name: prependName + it.name + '-concept_id',
941                                                        value: fieldValue
942                                                )
943                                                out << hiddenField(
944                                                        name: prependName + it.escapedName() + '-ontology_id',
945                                                        value: fieldValue
946                                                )
947                                                out << hiddenField(
948                                                        name: prependName + it.escapedName() + '-full_id',
949                                                        value: fieldValue
950                                                )
951                                                break
952                                        case 'DATE':
953                                                inputElement = (renderType == 'element') ? 'dateElement' : 'textField'
954
955                                                // transform value?
956                                                if (fieldValue instanceof Date) {
957                                                        if (fieldValue.getHours() == 0 && fieldValue.getMinutes() == 0) {
958                                                                // transform date instance to formatted string (dd/mm/yyyy)
959                                                                fieldValue = String.format('%td/%<tm/%<tY', fieldValue)
960                                                        } else {
961                                                                // transform to date + time
962                                                                fieldValue = String.format('%td/%<tm/%<tY %<tH:%<tM', fieldValue)
963                                                        }
964                                                }
965
966                                                // render element
967                                                out << "$inputElement"(
968                                                        description: ucName,
969                                                        name: prependName + it.escapedName(),
970                                                        value: fieldValue,
971                                                        rel: 'date'
972                                                ){helpText}
973                                                break
974                                        case ['RELTIME']:
975                                                inputElement = (renderType == 'element') ? 'textFieldElement' : 'textField'
976                                                out << "$inputElement"(
977                                                        description: ucName,
978                                                        name: prependName + it.escapedName(),
979                                                        value: new RelTime( fieldValue ).toString(),
980                            addExampleElement: true,
981                            onBlur: 'showExampleReltime(this)'
982                                                ){helpText}
983                                                break
984                                        case ['FILE']:
985                                                inputElement = (renderType == 'element') ? 'fileFieldElement' : 'fileField'
986                                                out << "$inputElement"(
987                                                        description: ucName,
988                                                        name: prependName + it.escapedName(),
989                                                        value: fieldValue ? fieldValue : "",
990                            addExampleElement: true
991                                                ){helpText}
992                                                break
993                                        case ['BOOLEAN']:
994                                                inputElement = (renderType == 'element') ? 'checkBoxElement' : 'checkBox'
995                                                out << "$inputElement"(
996                                                        description: ucName,
997                                                        name: prependName + it.escapedName(),
998                                                        value: fieldValue
999                                                ){helpText}
1000                                                break
1001                                        default:
1002                                                // unsupported field type
1003                                                out << '<span class="warning">!' + it.type + '</span>'
1004                                                break
1005                                }
1006
1007                                // output column closing element?
1008                                if (renderType == 'column') {
1009                                        out << '</div>'
1010                                }
1011                        }
1012                }
1013        }
1014
1015        def PublicationSelectElement = { attrs, body ->
1016                attrs.description = 'Publications';
1017                // render list with publications currently available
1018                baseElement.call(
1019                        '_publicationList',
1020                        attrs,
1021                        body
1022                )
1023
1024                attrs.description = '';
1025
1026                // render 'Add publication button'
1027                baseElement.call(
1028                        '_publicationAddButton',
1029                        attrs,
1030                        body
1031                )
1032        }
1033
1034        /**
1035         * Renders a input box for publications
1036         */
1037        def publicationSelect = { attrs, body ->
1038                if (attrs.get('value') == null) {
1039                        attrs.value = [];
1040                }
1041                if (attrs.get('description') == null) {
1042                        attrs.description = '';
1043                }
1044                out << '<form id="' + attrs.name + '_form" onSubmit="return false;">';
1045                out << textField(
1046                        name: attrs.get("name"),
1047                        value: '',
1048                        rel: 'publication-pubmed',
1049                        style: 'width: 400px;'
1050                );
1051                out << '</form>';
1052                out << '<script type="text/javascript">';
1053                out << '  var onSelect = function( chooserObject, inputElement, event, ui ) { selectPubMedAdd( chooserObject, inputElement, event, ui ); enableButton( ".' + attrs.name + '_publication_dialog", "Add", true ); };'
1054                out << '  iField = $( "#' + attrs.get('name') + '" );';
1055                out << '  new PublicationChooser().initAutocomplete( iField, { "select" : onSelect } );';
1056                out << '</script>';
1057        }
1058
1059        def _publicationList = { attrs, body ->
1060                def display_none = 'none';
1061                if (!attrs.get('value') || attrs.get('value').size() == 0) {
1062                        display_none = 'inline';
1063                }
1064
1065                // Add a unordered list
1066                out << '<ul class="publication_list" id="' + attrs.name + '_list">';
1067
1068                out << '<li>';
1069                out << '<span class="publication_none" id="' + attrs.name + '_none" style="display: ' + display_none + ';">';
1070                out << 'No publications selected';
1071                out << '</span>';
1072                out << '</li>';
1073
1074                out << '</ul>';
1075
1076                // Add the publications using javascript
1077                out << '<script type="text/javascript">'
1078                if (attrs.get('value') && attrs.get('value').size() > 0) {
1079                        def i = 0;
1080                        attrs.get('value').each {
1081                                out << 'showPublication( ';
1082                                out << '  "' + attrs.name + '",';
1083                                out << '  ' + it.id + ',';
1084                                out << '  "' + it.title + '",';
1085                                out << '  "' + it.authorsList + '",';
1086                                out << '  ' + i++;
1087                                out << ');';
1088                        }
1089                }
1090                out << '</script>';
1091
1092                def ids;
1093                if (attrs.get('value') && attrs.get('value').size() > 0) {
1094                        ids = attrs.get('value').id.join(',')
1095                } else {
1096                        ids = '';
1097                }
1098                out << '<input type="hidden" name="' + attrs.name + '_ids" value="' + ids + '" id="' + attrs.name + '_ids">';
1099        }
1100
1101        def _publicationAddButton = { attrs, body ->
1102
1103                // Output the dialog for the publications
1104                out << '<div id="' + attrs.name + '_dialog">';
1105                out << '<p>Search for a publication on pubmed. You can search on a part of the title or authors. </p>';
1106                out << publicationSelect(attrs, body);
1107                out << '</div>';
1108                out << '<script type="text/javascript">';
1109                out << '  createPublicationDialog( "' + attrs.name + '" );'
1110                out << '</script>';
1111
1112                out << '<input type="button" onClick="openPublicationDialog(\'' + attrs.name + '\' );" value="Add Publication">';
1113        }
1114
1115        def ContactSelectElement = { attrs, body ->
1116
1117                attrs.description = 'Contacts';
1118                // render list with publications currently available
1119                baseElement.call(
1120                        '_contactList',
1121                        attrs,
1122                        body
1123                )
1124
1125                attrs.description = '';
1126
1127                // render 'publications list'
1128                out << '<div id="' + attrs.name + '_dialog" class="contacts_dialog" style="display: none;">'
1129                baseElement.call(
1130                        '_personSelect',
1131                        attrs,
1132                        body
1133                )
1134                baseElement.call(
1135                        '_roleSelect',
1136                        attrs,
1137                        body
1138                )
1139                baseElement.call(
1140                        '_contactAddButtonAddition',
1141                        attrs,
1142                        body
1143                )
1144                out << '</div>';
1145
1146                // render 'Add contact button'
1147                baseElement.call(
1148                        '_contactAddDialogButton',
1149                        attrs,
1150                        body
1151                )
1152        }
1153
1154        def _contactList = { attrs, body ->
1155                def display_none = 'none';
1156                if (!attrs.get('value') || attrs.get('value').size() == 0) {
1157                        display_none = 'inline';
1158                }
1159
1160                // Add a unordered list
1161                out << '<ul class="contact_list" id="' + attrs.name + '_list">';
1162
1163                out << '<li>';
1164                out << '<span class="contacts_none" id="' + attrs.name + '_none" style="display: ' + display_none + ';">';
1165                out << 'No contacts selected';
1166                out << '</span>';
1167                out << '</li>';
1168
1169                out << '</ul>';
1170
1171                // Add the contacts using javascript
1172                out << '<script type="text/javascript">'
1173                if (attrs.get('value') && attrs.get('value').size() > 0) {
1174                        def i = 0;
1175                        attrs.get('value').each {
1176                                out << 'showContact( ';
1177                                out << '  "' + attrs.name + '",';
1178                                out << '  "' + it.person.id + '-' + it.role.id + '",';
1179                                out << '  "' + it.person.lastName + ', ' + it.person.firstName + (it.person.prefix ? ' ' + it.person.prefix : '') + '",';
1180                                out << '  "' + it.role.name + '",';
1181                                out << '  ' + i++;
1182                                out << ');';
1183                        }
1184                }
1185                out << '</script>';
1186
1187                def ids = '';
1188                if (attrs.get('value') && attrs.get('value').size() > 0) {
1189                        ids = attrs.get('value').collect { it.person.id + '-' + it.role.id }
1190                        ids = ids.join(',');
1191                }
1192                out << '<input type="hidden" name="' + attrs.name + '_ids" value="' + ids + '" id="' + attrs.name + '_ids">';
1193        }
1194
1195        def _contactAddSelect = { attrs, body ->
1196                out << _personSelect(attrs) + _roleSelect(attrs);
1197        }
1198
1199        def _contactAddButtonAddition = { attrs, body ->
1200                out << '<input type="button" onClick="addContact ( \'' + attrs.name + '\' ); $(\'#' + attrs.name + '_dialog\').hide(); $( \'#' + attrs.name + '_dialogButton\' ).show();" value="Add">';
1201                out << '<input type="button" onClick="$(\'#' + attrs.name + '_dialog\').hide(); $( \'#' + attrs.name + '_dialogButton\' ).show();" value="Close">';
1202        }
1203
1204        def _contactAddDialogButton = { attrs, body ->
1205                out << '<input type="button" onClick="$( \'#' + attrs.name + '_dialog\' ).show(); $(this).hide();" id="' + attrs.name + '_dialogButton" value="Add Contact">';
1206        }
1207        /**
1208         * Person select element
1209         * @param Map attributes
1210         */
1211        def _personSelect = { attrs ->
1212                def selectAttrs = new LinkedHashMap();
1213
1214                // define 'from'
1215                def persons = Person.findAll().sort({ a, b -> a.lastName == b.lastName ? (a.firstName <=> b.firstName) : (a.lastName <=> b.lastName) } as Comparator);
1216                selectAttrs.from = persons.collect { it.lastName + ', ' + it.firstName + (it.prefix ? ' ' + it.prefix : '') }
1217                selectAttrs.keys = persons.id;
1218
1219                // add 'rel' attribute
1220                selectAttrs.rel = 'person'
1221                selectAttrs.name = attrs.name + '_person';
1222
1223                out << "Person: " + select(selectAttrs)
1224        }
1225
1226        /**
1227         * Role select element
1228         * @param Map attributes
1229         */
1230        def _roleSelect = { attrs ->
1231                println(attrs);
1232                def selectAttrs = new LinkedHashMap();
1233
1234                // define 'from'
1235                def roles = PersonRole.findAll();
1236                selectAttrs.from = roles.collect { it.name };
1237                selectAttrs.keys = roles.id;
1238
1239                // add 'rel' attribute
1240                selectAttrs.rel = 'role'
1241                selectAttrs.name = attrs.name + '_role';
1242
1243                out << "Role: " + select(selectAttrs)
1244        }
1245}
Note: See TracBrowser for help on using the repository browser.