root/galaxy-central/templates/grid_base.mako

リビジョン 2, 37.5 KB (コミッタ: hatakeyama, 14 年 前)

import galaxy-central

行番号 
1<%!
2    from galaxy.web.framework.helpers.grids import TextColumn
3    import galaxy.util
4    def inherit(context):
5        if context.get('use_panels'):
6            if context.get('webapp'):
7                webapp = context.get('webapp')
8            else:
9                webapp = 'galaxy'
10            return '/webapps/%s/base_panels.mako' % webapp
11        else:
12            return '/base.mako'
13%>
14<%inherit file="${inherit(context)}"/>
15<%namespace file="/display_common.mako" import="render_message" />
16
17<%def name="init()">
18<%
19    self.has_left_panel=False
20    self.has_right_panel=False
21    self.message_box_visible=False
22    self.active_view="user"
23    self.overlay_visible=False
24%>
25</%def>
26
27##
28## Override methods from base.mako and base_panels.mako
29##
30
31<%def name="center_panel()">
32    ${make_grid( grid )}
33</%def>
34
35## Render the grid's basic elements. Each of these elements can be subclassed.
36<%def name="body()">
37    ${make_grid( grid )}
38</%def>
39
40<%def name="title()">${grid.title}</%def>
41
42<%def name="javascripts()">
43   ${parent.javascripts()}
44   ${self.grid_javascripts()}
45</%def>
46
47<%def name="grid_javascripts()">
48    ${h.js("jquery.autocomplete", "autocomplete_tagging", "jquery.rating" )}
49    <script type="text/javascript">
50        // This is necessary so that, when nested arrays are used in ajax/post/get methods, square brackets ('[]') are
51        // not appended to the identifier of a nested array.
52        jQuery.ajaxSettings.traditional = true;
53       
54        ## TODO: generalize and move into galaxy.base.js
55        $(document).ready(function() {
56            init_grid_elements();
57            init_grid_controls();
58           
59            // Initialize filter elements.
60            $('input[type=text]').each( function() {
61                $(this).click( function() { $(this).attr('value', ''); } );
62            });
63        });
64        ## Can this be moved into base.mako?
65        %if refresh_frames:
66            %if 'masthead' in refresh_frames:           
67                ## Refresh masthead == user changes (backward compatibility)
68                if ( parent.user_changed ) {
69                    %if trans.user:
70                        parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
71                    %else:
72                        parent.user_changed( null, false );
73                    %endif
74                }
75            %endif
76            %if 'history' in refresh_frames:
77                if ( parent.frames && parent.frames.galaxy_history ) {
78                    parent.frames.galaxy_history.location.href="${h.url_for( controller='root', action='history')}";
79                    if ( parent.force_right_panel ) {
80                        parent.force_right_panel( 'show' );
81                    }
82                }
83            %endif
84            %if 'tools' in refresh_frames:
85                if ( parent.frames && parent.frames.galaxy_tools ) {
86                    parent.frames.galaxy_tools.location.href="${h.url_for( controller='root', action='tool_menu')}";
87                    if ( parent.force_left_panel ) {
88                        parent.force_left_panel( 'show' );
89                    }
90                }
91            %endif
92        %endif
93       
94        $(".search-box-input").live("click", function() {
95            $(this).css("font-style", "normal");
96        });
97       
98        //
99        // Code to handle grid operations: filtering, sorting, paging, and operations.
100        //
101       
102        // Operations that are async (AJAX) compatible.
103        var async_ops = {};
104        %for operation in [op for op in grid.operations if op.async_compatible]:
105            async_ops['${operation.label.lower()}'] = "True";
106        %endfor
107       
108        // Initialize grid controls
109        function init_grid_controls() {
110            // Initialize operation buttons.
111            $('input[name=operation]:submit').each(function() {
112                $(this).click( function() {
113                   var webapp = $("input[name=webapp]").attr("value");
114                   var operation_name = $(this).val();
115                   // For some reason, $('input[name=id]:checked').val() does not return all ids for checked boxes.
116                   // The code below performs this function.
117                   var item_ids = [];
118                   $('input[name=id]:checked').each(function() {
119                       item_ids.push( $(this).val() );
120                   });
121                   do_operation(webapp, operation_name, item_ids);
122                });
123            });
124           
125            // Initialize submit image elements.
126            $('.submit-image').each( function() {
127                // On mousedown, add class to simulate click.
128                $(this).mousedown( function() {
129                   $(this).addClass('gray-background');
130                });
131               
132                // On mouseup, add class to simulate click.
133                $(this).mouseup( function() {
134                   $(this).removeClass('gray-background');
135                });
136            });
137           
138            // Initialize sort links.
139            $('.sort-link').each( function() {
140                $(this).click( function() {
141                   set_sort_condition( $(this).attr('sort_key') );
142                   return false;
143                });
144            });
145           
146            // Initialize page links.
147            $('.page-link > a').each( function() {
148                $(this).click( function() {
149                   set_page( $(this).attr('page_num') );
150                   return false;
151                });
152            });
153           
154            $('#show-all-link').click( function() {
155                set_page('all');
156                return false;
157            });
158           
159            // Initialize categorical filters.
160            $('.categorical-filter > a').each( function() {
161                $(this).click( function() {
162                    set_categorical_filter( $(this).attr('filter_key'), $(this).attr('filter_val') );
163                    return false;
164                });
165            });
166           
167            // Initialize text filters.
168            $('.text-filter-form').each( function() {
169                $(this).submit( function() {
170                    var column_key = $(this).attr('column_key');
171                    var text_input_obj = $('#input-' + column_key + '-filter');
172                    var text_input = text_input_obj.val();
173                    text_input_obj.val('');
174                    add_filter_condition(column_key, text_input, true);
175                    return false;
176                });
177            });
178           
179            // Initialize autocomplete for text inputs in search UI.
180            var t = $("#input-tags-filter");
181            if (t.length) {
182                t.autocomplete(  "${h.url_for( controller='tag', action='tag_autocomplete_data', item_class='History' )}",
183                                 { selectFirst: false, autoFill: false, highlight: false, mustMatch: false });
184            }
185 
186            var t2 = $("#input-name-filter");
187            if (t2.length) {
188                t2.autocomplete( "${h.url_for( controller='history', action='name_autocomplete_data' )}",
189                                 { selectFirst: false, autoFill: false, highlight: false, mustMatch: false });
190            }
191           
192            // Initialize advanced search toggles.
193            $('.advanced-search-toggle').each( function() {
194                $(this).click( function() {
195                   $('#more-search-options').slideToggle('fast');
196                   return false;
197                });
198            });
199        }
200 
201        // Initialize grid elements.
202        function init_grid_elements() {
203            // Initialize grid selection checkboxes.
204            $(".grid").each( function() {
205                var checkboxes = $(this).find("input.grid-row-select-checkbox");
206                var check_count = $(this).find("span.grid-selected-count");
207                var update_checked = function() {
208                    check_count.text( $(checkboxes).filter(":checked").length );
209                };
210               
211                $(checkboxes).each( function() {
212                    $(this).change(update_checked);
213                });
214                update_checked();
215            });
216           
217            // Initialize item labels.
218            $(".label").each( function() {
219                // If href has an operation in it, do operation when clicked. Otherwise do nothing.
220                var href = $(this).attr('href');
221                if ( href !== undefined && href.indexOf('operation=') != -1 ) {
222                    $(this).click( function() {
223                        do_operation_from_href( $(this).attr('href') );
224                        return false;
225                    });   
226                }
227            });
228           
229            // Initialize ratings.
230            $('.community_rating_star').rating({});
231           
232            // Initialize item menu operations.
233            make_popup_menus();
234        }
235       
236        // Filter values for categorical filters.
237        var categorical_filters = {};
238        %for column in grid.columns:
239            %if column.filterable is not None and not isinstance( column, TextColumn ):
240                var ${column.key}_filters = ${ h.to_json_string( dict([ (filter.label, filter.args) for filter in column.get_accepted_filters() ]) ) };
241                categorical_filters['${column.key}'] = ${column.key}_filters;
242            %endif
243        %endfor
244           
245        // Initialize URL args with filter arguments.
246        var url_args_init = ${h.to_json_string( cur_filter_dict )},
247            url_args = {};
248       
249        // Place "f-" in front of all filter arguments.
250       
251        for (arg in url_args_init) {
252            url_args["f-" + arg] = url_args_init[arg];
253        }
254       
255        // Add sort argument to URL args.
256        url_args['sort'] = "${sort_key}";
257       
258        // Add async keyword to URL args.
259        url_args['async'] = true;
260       
261        // Add page to URL args.
262        url_args['page'] = ${cur_page_num};
263       
264        var num_pages = ${num_pages};
265       
266        // Go back to page one; this is useful when a filter is applied.
267        function go_page_one() {
268            // Need to go back to page 1 if not showing all.
269            var cur_page = url_args['page'];
270            if (cur_page !== null && cur_page !== undefined && cur_page != 'all') {
271                url_args['page'] = 1;
272            }               
273        }
274       
275        // Add a condition to the grid filter; this adds the condition and refreshes the grid.
276        function add_filter_condition(name, value, append) {
277            // Do nothing is value is empty.
278            if (value == "") {
279                return false;
280            }
281           
282            // Update URL arg with new condition.           
283            if (append) {
284                // Update or append value.
285                var cur_val = url_args["f-" + name];
286                var new_val;
287                if (cur_val === null || cur_val === undefined) {
288                    new_val = value;
289                } else if (typeof(cur_val) == "string") {
290                    if (cur_val == "All") {
291                        new_val = value;
292                    } else {
293                        // Replace string with array.
294                        var values = [];
295                        values[0] = cur_val;
296                        values[1] = value;
297                        new_val = values;   
298                    }
299                } else {
300                    // Current value is an array.
301                    new_val = cur_val;
302                    new_val[new_val.length] = value;
303                }
304                url_args["f-" + name] = new_val;
305            } else {
306                // Replace value.
307                url_args["f-" + name] = value;
308            }
309           
310            // Add button that displays filter and provides a button to delete it.
311            var t = $("<span>" + value + "<a href='javascript:void(0);'><span class='delete-search-icon' /></a></span>");
312            t.addClass('text-filter-val');
313            t.click(function() {
314                // Remove filter condition.
315 
316                // Remove visible element.
317                $(this).remove();
318               
319                // Remove condition from URL args.
320                var cur_val = url_args["f-" + name];
321                if (cur_val === null || cur_val === undefined) {
322                    // Unexpected. Throw error?
323                } else if (typeof(cur_val) == "string") {
324                    if (cur_val == "All") {
325                        // Unexpected. Throw error?
326                    } else {
327                        // Remove condition.
328                        delete url_args["f-" + name];
329                    }
330                } else {
331                    // Current value is an array.
332                    var conditions = cur_val;
333                    for (var index = 0; index < conditions.length; index++) {
334                        if (conditions[index] == value) {
335                            conditions.splice(index, 1);
336                            break;
337                        }
338                    }
339                }
340 
341                go_page_one();
342                update_grid();
343            });
344           
345            var container = $('#' + name + "-filtering-criteria");
346            container.append(t);
347           
348            go_page_one();
349            update_grid();
350        }
351       
352        // Add tag to grid filter.
353        function add_tag_to_grid_filter(tag_name, tag_value) {
354            // Put tag name and value together.
355            var tag = tag_name + (tag_value !== undefined && tag_value != "" ? ":" + tag_value : "");
356            $('#more-search-options').show('fast');
357            add_filter_condition("tags", tag, true);
358        }
359 
360        // Set sort condition for grid.
361        function set_sort_condition(col_key) {
362            // Set new sort condition. New sort is col_key if sorting new column; if reversing sort on
363            // currently sorted column, sort is reversed.
364            var cur_sort = url_args['sort'];
365            var new_sort = col_key;
366            if ( cur_sort.indexOf( col_key ) != -1) {               
367                // Reverse sort.
368                if ( cur_sort.substring(0,1) != '-' ) {
369                    new_sort = '-' + col_key;
370                } else {
371                    // Sort reversed by using just col_key.
372                }
373            }
374           
375            // Remove sort arrows elements.
376            $('.sort-arrow').remove();
377           
378            // Add sort arrow element to new sort column.
379            var sort_arrow = (new_sort.substring(0,1) == '-') ? "&uarr;" : "&darr;";
380            var t = $("<span>" + sort_arrow + "</span>").addClass('sort-arrow');
381            var th = $("#" + col_key + '-header');
382            th.append(t);
383           
384            // Need to go back to page 1 if not showing all.
385            var cur_page = url_args['page'];
386            if (cur_page !== null && cur_page !== undefined && cur_page != 'all') {
387                url_args['page'] = 1;
388            }
389            // Update grid.
390            url_args['sort'] = new_sort;
391            go_page_one();
392            update_grid();
393        }
394       
395        // Set new value for categorical filter.
396        function set_categorical_filter(name, new_value) {
397            // Update filter hyperlinks to reflect new filter value.
398            var category_filter = categorical_filters[name];
399            var cur_value = url_args["f-" + name];
400            $("." + name + "-filter").each( function() {
401                var text = $.trim( $(this).text() );
402                var filter = category_filter[text];
403                var filter_value = filter[name];
404                if (filter_value == new_value) {
405                    // Remove filter link since grid will be using this filter. It is assumed that
406                    // this element has a single child, a hyperlink/anchor with text.
407                    $(this).empty();
408                    $(this).addClass("current-filter");
409                    $(this).append(text);
410                } else if (filter_value == cur_value) {
411                    // Add hyperlink for this filter since grid will no longer be using this filter. It is assumed that
412                    // this element has a single child, a hyperlink/anchor.
413                    $(this).empty();
414                    var t = $("<a href='#'>" + text + "</a>");
415                    t.click(function() {
416                        set_categorical_filter( name, filter_value );
417                    });
418                    $(this).removeClass("current-filter");
419                    $(this).append(t);
420                }
421            });
422           
423            // Update grid.
424            url_args["f-" + name] = new_value;
425            go_page_one();
426            update_grid();
427        }
428       
429        // Set page to view.
430        function set_page(new_page) {
431            // Update page hyperlink to reflect new page.
432            $(".page-link").each( function() {
433               var id = $(this).attr('id');
434               var page_num = parseInt( id.split("-")[2] ); // Id has form 'page-link-<page_num>
435               var cur_page = url_args['page'];
436               if (page_num == new_page) {
437                   // Remove link to page since grid will be on this page. It is assumed that
438                   // this element has a single child, a hyperlink/anchor with text.
439                   var text = $(this).children().text();
440                   $(this).empty();
441                   $(this).addClass("inactive-link");
442                   $(this).text(text);
443               } else if (page_num == cur_page) {
444                   // Add hyperlink to this page since grid will no longer be on this page. It is assumed that
445                   // this element has a single child, a hyperlink/anchor.
446                   var text = $(this).text();
447                   $(this).empty();
448                   $(this).removeClass("inactive-link");
449                   var t = $("<a href='#'>" + text + "</a>");
450                   t.click(function() {
451                      set_page(page_num);
452                   });
453                   $(this).append(t);
454               }
455            });
456 
457            var maintain_page_links = true;
458            if (new_page == "all") {
459                url_args['page'] = new_page;
460                maintain_page_links = false;
461            } else {
462                url_args['page'] = parseInt(new_page);
463            }
464            update_grid(maintain_page_links);
465        }
466       
467        // Perform a grid operation.
468        function do_operation(webapp, operation, item_ids) {
469            operation = operation.toLowerCase();
470           
471            // Update URL args.
472            url_args["webapp"] = webapp;
473            url_args["operation"] = operation;
474            url_args["id"] = item_ids;
475           
476            // If operation cannot be performed asynchronously, redirect to location. Otherwise do operation.
477            var no_async = ( async_ops[operation] === undefined || async_ops[operation] === null);
478            if (no_async) {
479                go_to_URL();
480            } else {
481                update_grid(true);
482                delete url_args['webapp'];
483                delete url_args['operation'];
484                delete url_args['id'];
485            }
486        }
487       
488        // Perform a hyperlink click that initiates an operation. If there is no operation, ignore click.
489        function do_operation_from_href(href) {
490            // Get operation, id in hyperlink's href.
491            var href_parts = href.split("?");
492            if (href_parts.length > 1) {
493                var href_parms_str = href_parts[1];
494                var href_parms = href_parms_str.split("&");
495                var operation = null;
496                var id = -1;
497                var webapp = 'galaxy';
498                for (var index = 0; index < href_parms.length; index++) {
499                    if (href_parms[index].indexOf('operation') != -1) {
500                        // Found operation parm; get operation value.
501                        operation = href_parms[index].split('=')[1];
502                    } else if (href_parms[index].indexOf('id') != -1) {
503                        // Found id parm; get id value.
504                        id = href_parms[index].split('=')[1];
505                    } else if (href_parms[index].indexOf('webapp') != -1) {
506                        // Found webapp parm; get webapp value.
507                        webapp = href_parms[index].split('=')[1];
508                    }
509                }
510                // Do operation.
511                do_operation(webapp, operation, id);
512                return false;
513            }
514        }
515       
516        // Navigate window to the URL defined by url_args. This method should be used to short-circuit grid AJAXing.
517        function go_to_URL() {
518            // Not async request.
519            url_args['async'] = false;
520           
521            // Build argument string.
522            var arg_str = "";
523            for (var arg in url_args) {
524                arg_str = arg_str + arg + "=" + url_args[arg] + "&";
525            }
526           
527            // Go.
528            window.location = encodeURI( "${h.url_for()}?" + arg_str );
529        }
530       
531        // Update grid.
532        function update_grid(maintain_page_links) {
533            ## If grid is not using async, then go to URL.
534            %if not grid.use_async:
535                 go_to_URL();
536                 return;
537            %endif
538           
539            // If there's an operation in the args, do POST; otherwise, do GET.
540            var operation = url_args['operation'];
541            var method = (operation !== null && operation !== undefined ? "POST" : "GET" );
542            $('.loading-elt-overlay').show(); // Show overlay to indicate loading and prevent user actions.
543            $.ajax({
544                type: method,
545                url: "${h.url_for()}",
546                data: url_args,
547                error: function() { alert( "Grid refresh failed" ); },
548                success: function(response_text) {
549                    // HACK: use a simple string to separate the elements in the
550                    // response: (1) table body; (2) number of pages in table; and (3) message.
551                    var parsed_response_text = response_text.split("*****");
552                   
553                    // Update grid body.
554                    var table_body = parsed_response_text[0];
555                    $('#grid-table-body').html(table_body);
556                   
557                    // Process grid body.
558                    init_grid_elements();
559                    make_popup_menus();
560                   
561                    // Update number of pages.
562                    var num_pages = parseInt( parsed_response_text[1] );
563                   
564                    // Rebuild page links.
565                    if (!maintain_page_links) {
566                        // Remove page links.
567                        var page_link_container = $('#page-link-container');
568                        page_link_container.children().remove();
569                       
570                        // First page is the current page.
571                        var t = $("<span>1</span>");
572                        t.addClass('page-link');
573                        t.addClass('inactive-link');
574                        t.attr('id', 'page-link-1');
575                        page_link_container.append(t);
576                       
577                        // Show all link is visible only if there are multiple pages.
578                        var elt = $('#show-all-link-span');
579                        if (num_pages > 1) {
580                            elt.show();
581                        } else {
582                            elt.hide();
583                        }
584       
585                        // Subsequent pages are navigable.
586                        for (var i = 2; i <= num_pages; i++) {
587                            var span = $("<span></span>");
588                            span.addClass('page-link');
589                            span.attr('id', 'page-link-' + i);
590                            var t = $("<a href='#'>" + i + "</a>");
591                            t.attr('page', i);
592                            t.click(function() {
593                                var page = $(this).attr('page');
594                                set_page(page);
595                            });
596                            span.append(t);
597                            page_link_container.append(span);
598                        }
599                    }
600                   
601                    // Hide loading overlay.
602                    $('.loading-elt-overlay').hide();
603                   
604                    // Show message if there is one.
605                    var message = $.trim( parsed_response_text[2] );
606                    if (message != "") {
607                        $('#grid-message').html( message );
608                        setTimeout( function() { $('#grid-message').hide(); }, 5000);
609                    }
610                }
611            });   
612        }
613    </script>
614</%def>
615
616<%def name="stylesheets()">
617    ${parent.stylesheets()}
618    ${h.css( "autocomplete_tagging", "jquery.rating" )}
619    <style>
620        .count-box {
621            min-width: 1.1em;
622            padding: 5px;
623            border-width: 1px;
624            border-style: solid;
625            text-align: center;
626            display: inline-block;
627        }
628        .text-filter-val {
629            border: solid 1px #AAAAAA;
630            padding: 1px 2px 1px 3px;
631            margin-right: 5px;
632            -moz-border-radius: .5em;
633            -webkit-border-radius: .5em;
634            font-style: italic;
635        }
636        .page-link a, .inactive-link {
637            padding: 0px 7px 0px 7px;
638        }
639        .inactive-link, .current-filter {
640            font-style: italic;
641        }
642        .submit-image {
643            background: url(${h.url_for('/static/images/fugue/magnifier-left.png')}) no-repeat right transparent;
644            background-color: #eee;
645            width: 20px;
646            height: 20px;
647            cursor: pointer;
648            border: 0;
649            border-left: 1px solid #ccc;
650            margin: 0;
651            padding: 0;
652            display: block;
653            float: right;
654        }
655        #more-search-options td {
656            padding: 3px;
657        }
658        #more-search-options table {
659            border-collapse: separate;
660        }
661        .delete-search-icon {
662            background: url(${h.url_for("/static/images/delete_tag_icon_gray.png")}) center no-repeat;
663            display: inline-block;
664            width: 10px;
665            cursor: pointer;
666            height: 18px;
667            vertical-align: middle;
668            margin-left: 2px;
669           
670        }
671        .search-box-input {
672            border: 0;
673            margin: 1px 0 0 2px;
674            float: left;
675            outline: medium none;
676            font-style: italic;
677            font-size: inherit;
678        }
679        .search-box {
680            vertical-align: bottom;
681            display: inline-block;
682            padding: 0;
683            border: 1px solid #aaa;
684        }
685        .gray-background {
686            background-color: #DDDDDD;
687        }
688        .loading-elt-overlay {
689            background-color : white;
690            opacity : 0.5;
691            width : 100%;
692            height : 100%;
693            z-index : 14000;
694            position : absolute;
695            display: none;
696        }
697    </style>
698</%def>
699
700##
701## Custom grid methods.
702##
703
704<%namespace file="./grid_common.mako" import="*" />
705
706<%def name="make_grid( grid )">
707    <table>
708        <tr>
709            <td width="75%">${self.render_grid_header( grid )}</td>
710            <td></td>
711            <td></td>
712        </tr>
713        <tr>
714            <td width="100%" id="grid-message" valign="top">${render_message( message, status )}</td>
715            <td></td>
716            <td></td>
717        </tr>
718    </table>
719
720    ${self.render_grid_table( grid )}
721</%def>
722
723## Render grid header.
724<%def name="render_grid_header( grid, render_title=True)">
725    <div class="grid-header">
726        %if render_title:
727            <h2>${grid.title}</h2>
728        %endif
729   
730        %if grid.global_actions:
731            <ul class="manage-table-actions">
732            %for action in grid.global_actions:
733                <li>
734                    <a class="action-button" href="${h.url_for( **action.url_args )}">${action.label}</a>
735                </li>
736            %endfor
737            </ul>
738        %endif
739   
740        ${render_grid_filters( grid )}
741    </div>
742</%def>
743
744## Render grid.
745<%def name="render_grid_table( grid, show_item_checkboxes=False)">
746    <%
747        # Set flag to indicate whether grid has operations that operate on multiple items.
748        multiple_item_ops_exist = False
749        for operation in grid.operations:
750            if operation.allow_multiple:
751                multiple_item_ops_exist = True
752               
753        # Show checkboxes if flag is set or if multiple item ops exist.
754        if show_item_checkboxes or multiple_item_ops_exist:
755            show_item_checkboxes = True
756    %>
757
758    <form action="${url()}" method="post" onsubmit="return false;">
759        <div class="loading-elt-overlay"></div>
760        <table id="grid-table" class="grid">
761            <thead id="grid-table-header">
762                <tr>
763                    %if show_item_checkboxes:
764                        <th></th>
765                    %endif
766                    %for column in grid.columns:
767                        %if column.visible:
768                            <%
769                                href = ""
770                                extra = ""
771                                if column.sortable:
772                                    if sort_key.endswith(column.key):
773                                        if not sort_key.startswith("-"):
774                                            href = url( sort=( "-" + column.key ) )
775                                            extra = "&darr;"
776                                        else:
777                                            href = url( sort=( column.key ) )
778                                            extra = "&uarr;"
779                                    else:
780                                        href = url( sort=column.key )
781                            %>
782                            <th\
783                            id="${column.key}-header"
784                            %if column.ncells > 1:
785                                colspan="${column.ncells}"
786                            %endif
787                            >
788                                %if href:
789                                    <a href="${href}" class="sort-link" sort_key="${column.key}">${column.label}</a>
790                                %else:
791                                    ${column.label}
792                                %endif
793                                <span class="sort-arrow">${extra}</span>
794                            </th>
795                        %endif
796                    %endfor
797                    <th></th>
798                </tr>
799            </thead>
800            <tbody id="grid-table-body">
801                ${render_grid_table_body_contents( grid, show_item_checkboxes )}
802            </tbody>
803            <tfoot>
804                ${render_grid_table_footer_contents( grid, show_item_checkboxes )}
805            </tfoot>
806        </table>
807    </form>
808</%def>
809
810## Render grid table body contents.
811<%def name="render_grid_table_body_contents(grid, show_item_checkboxes=False)">
812        <% num_rows_rendered = 0 %>
813        %if query.count() == 0:
814            ## No results.
815            <tr><td colspan="100"><em>No Items</em></td></tr>
816            <% num_rows_rendered = 1 %>
817        %endif
818        %for i, item in enumerate( query ):
819            <% encoded_id = trans.security.encode_id( item.id ) %>
820            <tr \
821            %if current_item == item:
822                class="current" \
823            %endif
824            >
825                ## Item selection column
826                %if show_item_checkboxes:
827                    <td style="width: 1.5em;">
828                        <input type="checkbox" name="id" value="${encoded_id}" id="${encoded_id}" class="grid-row-select-checkbox" />
829                    </td>
830                %endif
831                ## Data columns
832                %for column in grid.columns:
833                    %if column.visible:
834                        <%
835                            # Link
836                            link = column.get_link( trans, grid, item )
837                            if link:
838                                href = url( **link )
839                            else:
840                                href = None
841                            # Value (coerced to list so we can loop)
842                            value = column.get_value( trans, grid, item )
843                            if column.ncells == 1:
844                                value = [ value ]
845                        %>
846                        %for cellnum, v in enumerate( value ):
847                            <%
848                                id = ""
849                                # Handle non-ascii chars.
850                                if isinstance(v, str):
851                                    v = unicode(v, 'utf-8')
852                                # Attach popup menu?
853                                if column.attach_popup and cellnum == 0:
854                                    id = 'grid-%d-popup' % i
855                                # Determine appropriate class
856                                cls = ""
857                                if column.attach_popup or href:
858                                    cls = "menubutton"
859                                if column.attach_popup and href:
860                                    cls = "menubutton split"
861
862                            %>
863                            <td>
864                            %if href:
865                                %if len(grid.operations) != 0:
866                                <div id="${id}" class="${cls}" style="float: left;">
867                                %endif
868                                    <a class="label" href="${href}">${v}</a>
869                                %if len(grid.operations) != 0:
870                                </div>
871                                %endif
872                            %else:
873                                <div id="${id}" class="${cls}"><label for="${encoded_id}">${v}</label></div>
874                            %endif
875                            </td>
876                        %endfor
877                    %endif
878                %endfor
879                ## Actions column
880                <td>
881                    <div popupmenu="grid-${i}-popup">
882                        %for operation in grid.operations:
883                            %if operation.allowed( item ) and operation.allow_popup:
884                                <%
885                                target = ""
886                                if operation.target:
887                                    target = "target='" + operation.target + "'"
888                                %>
889                                %if operation.confirm:
890                                    <a class="action-button" ${target} confirm="${operation.confirm}" href="${ url( **operation.get_url_args( item ) ) }">${operation.label}</a>
891                                %else:
892                                    <a class="action-button" ${target} href="${ url( **operation.get_url_args( item ) ) }">${operation.label}</a>
893                                %endif 
894                            %endif
895                        %endfor
896                    </div>
897                </td>
898            </tr>
899            <% num_rows_rendered += 1 %>
900        %endfor
901        ## Dummy rows to prevent table for moving too much.
902        ##%if grid.use_paging:
903        ##    %for i in range( num_rows_rendered , grid.num_rows_per_page ):
904        ##        <tr><td colspan="1000">  </td></tr>
905        ##    %endfor
906        ##%endif
907</%def>
908
909## Render grid table footer contents.
910<%def name="render_grid_table_footer_contents(grid, show_item_checkboxes=False)">
911    ## Row for navigating among pages.
912    <%namespace file="/display_common.mako" import="get_class_plural" />
913    <% items_plural = get_class_plural( grid.model_class ).lower() %>
914    %if grid.use_paging and num_pages > 1:
915        <tr id="page-links-row">
916            %if show_item_checkboxes:
917                <td></td>
918            %endif
919            <td colspan="100">
920                <span id='page-link-container'>
921                    ## Page links.
922                    Page:
923                    %for page_index in range(1, num_pages + 1):
924                        %if page_index == cur_page_num:
925                            <span class='page-link inactive-link' id="page-link-${page_index}">${page_index}</span>
926                        %else:
927                            <% args = { 'page' : page_index } %>
928                            <span class='page-link' id="page-link-${page_index}"><a href="${url( args )}" page_num='${page_index}'>${page_index}</a></span>
929                        %endif
930                    %endfor
931                </span>
932               
933                ## Show all link.
934                <% args = { "page" : "all" } %>
935                <span id='show-all-link-span'>| <a href="${url( args )}" id="show-all-link">Show all ${items_plural} on one page</a></span>
936            </td>
937        </tr>   
938    %endif
939    ## Grid operations for multiple items.
940    %if show_item_checkboxes:
941        <tr>
942            <td></td>
943            <td colspan="100">
944                For <span class="grid-selected-count"></span> selected ${items_plural}:
945                %for operation in grid.operations:
946                    %if operation.allow_multiple:
947                        <input type="submit" name="operation" value="${operation.label}" class="action-button">
948                    %endif
949                %endfor
950            </td>
951        </tr>
952    %endif
953</%def>
954
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。