1*4882a593Smuzhiyun{% load projecttags %} 2*4882a593Smuzhiyun<!-- component to display a generic table --> 3*4882a593Smuzhiyun <script> 4*4882a593Smuzhiyun 5*4882a593Smuzhiyun // 6*4882a593Smuzhiyun // most of the following javascript is for managing the 'Edit Columns' 7*4882a593Smuzhiyun // pop-up dialog and actions. the idea is that there are 2 types 8*4882a593Smuzhiyun // of actions: immediate - performed while the dialog is still 9*4882a593Smuzhiyun // visible - hide/show columns, and delayed - performed when the 10*4882a593Smuzhiyun // dialog becomes invisible - any resorting if necessary. 11*4882a593Smuzhiyun // 12*4882a593Smuzhiyun // When the dialog is open, an interval timer is set up to 13*4882a593Smuzhiyun // determine if the dialog is still visible. when the dialog 14*4882a593Smuzhiyun // closes - goes invisible, the delayed actions are performed. 15*4882a593Smuzhiyun // 16*4882a593Smuzhiyun // the interval timer and interrupt handler is a way of simulating 17*4882a593Smuzhiyun // an onclose event. there is probably a simpler way to do this 18*4882a593Smuzhiyun // however the pop-up window id was elusive. 19*4882a593Smuzhiyun // 20*4882a593Smuzhiyun 21*4882a593Smuzhiyun var editColTimer; 22*4882a593Smuzhiyun var editColAction; 23*4882a593Smuzhiyun 24*4882a593Smuzhiyun // 25*4882a593Smuzhiyun // this is the target function of the interval timeout. 26*4882a593Smuzhiyun // check to see if the dialog is visible. if the dialog 27*4882a593Smuzhiyun // has gone invisible since the last check, take any delayed 28*4882a593Smuzhiyun // actions indicated in the action list and clear the timer. 29*4882a593Smuzhiyun // 30*4882a593Smuzhiyun 31*4882a593Smuzhiyun function checkVisible( ) { 32*4882a593Smuzhiyun editcol = document.getElementById( 'editcol' ); 33*4882a593Smuzhiyun if ( editcol.offsetWidth <= 0 ) { 34*4882a593Smuzhiyun clearInterval( editColTimer ); 35*4882a593Smuzhiyun editColTimer = false; 36*4882a593Smuzhiyun hideshowColumns( ); 37*4882a593Smuzhiyun editColAction = [ ]; 38*4882a593Smuzhiyun } 39*4882a593Smuzhiyun } 40*4882a593Smuzhiyun 41*4882a593Smuzhiyun function filterTableRows(test) { 42*4882a593Smuzhiyun if (test.length > 0) { 43*4882a593Smuzhiyun var r = test.split(/[ ,]+/).map(function (e) { return new RegExp(e, 'i') }); 44*4882a593Smuzhiyun $('tr.data').map( function (i, el) { 45*4882a593Smuzhiyun (! r.map(function (j) { return j.test($(el).html())}).reduce(function (c, p) { return c && p;} )) ? $(el).hide() : $(el).show(); 46*4882a593Smuzhiyun }); 47*4882a593Smuzhiyun } else 48*4882a593Smuzhiyun { 49*4882a593Smuzhiyun $('tr.data').show(); 50*4882a593Smuzhiyun } 51*4882a593Smuzhiyun } 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun // 54*4882a593Smuzhiyun // determine the value of the indicated url arg. 55*4882a593Smuzhiyun // this is needed to determine whether a resort 56*4882a593Smuzhiyun // is necessary. it looks like a lot of gorp stuff 57*4882a593Smuzhiyun // but its actually pretty simple. 58*4882a593Smuzhiyun // 59*4882a593Smuzhiyun 60*4882a593Smuzhiyun function getURLParameter( name ) { 61*4882a593Smuzhiyun return decodeURIComponent((new RegExp('[?|&]' + name + '=' + 62*4882a593Smuzhiyun '([^&;]+?)(&|#|;|$)').exec(location.search)||[,""])[1].replace(/\+/g, 63*4882a593Smuzhiyun '%20'))||null 64*4882a593Smuzhiyun } 65*4882a593Smuzhiyun 66*4882a593Smuzhiyun // 67*4882a593Smuzhiyun // when the dialog box goes invisible 68*4882a593Smuzhiyun // this function is called to interpret 69*4882a593Smuzhiyun // the action list and take any delayed actions necessary. 70*4882a593Smuzhiyun // the editColAction list is a hash table with 71*4882a593Smuzhiyun // the column name as the hash key, the hash value 72*4882a593Smuzhiyun // is a 2 element list. the first element is a flag 73*4882a593Smuzhiyun // indicating whether the column is on or off. the 74*4882a593Smuzhiyun // 2nd element is the sort order indicator for the column. 75*4882a593Smuzhiyun // 76*4882a593Smuzhiyun 77*4882a593Smuzhiyun function hideshowColumns( ) { 78*4882a593Smuzhiyun for( var k in editColAction ) { 79*4882a593Smuzhiyun showhideDelayedTableAction( k, editColAction[ k ][ 0 ], editColAction[ k ][ 1 ]); 80*4882a593Smuzhiyun } 81*4882a593Smuzhiyun } 82*4882a593Smuzhiyun 83*4882a593Smuzhiyun // 84*4882a593Smuzhiyun // this function actually performs the delayed table actions 85*4882a593Smuzhiyun // namely any resorting if necessary 86*4882a593Smuzhiyun // 87*4882a593Smuzhiyun 88*4882a593Smuzhiyun function showhideDelayedTableAction( clname, sh, orderkey ) { 89*4882a593Smuzhiyun if ( !sh ) { 90*4882a593Smuzhiyun p = getURLParameter( "orderby" ).split( ":" )[ 0 ]; 91*4882a593Smuzhiyun if ( p == orderkey ) { 92*4882a593Smuzhiyun reload_params({ 'orderby' : '{{default_orderby}}'}); 93*4882a593Smuzhiyun } 94*4882a593Smuzhiyun } 95*4882a593Smuzhiyun } 96*4882a593Smuzhiyun 97*4882a593Smuzhiyun // 98*4882a593Smuzhiyun // this function actually performs the immediate table actions 99*4882a593Smuzhiyun // namely any colums that need to be hidden/shown 100*4882a593Smuzhiyun // 101*4882a593Smuzhiyun 102*4882a593Smuzhiyun function showhideImmediateTableAction( clname, sh, orderkey ) { 103*4882a593Smuzhiyun if ( sh ) { 104*4882a593Smuzhiyun $( '.' + clname ).show( 100 ); 105*4882a593Smuzhiyun } 106*4882a593Smuzhiyun else { 107*4882a593Smuzhiyun $( '.' + clname ).hide( 100 ); 108*4882a593Smuzhiyun } 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun // save cookie for all checkboxes 111*4882a593Smuzhiyun save = ''; 112*4882a593Smuzhiyun $( '.chbxtoggle' ).each(function( ) { 113*4882a593Smuzhiyun if ( $( this ).attr( 'id' ) != undefined ) { 114*4882a593Smuzhiyun save += ';' + $( this ).attr( 'id' ) +':'+ $( this ).is( ':checked' ) 115*4882a593Smuzhiyun } 116*4882a593Smuzhiyun }); 117*4882a593Smuzhiyun $.cookie( '_displaycols_{{objectname}}', save ); 118*4882a593Smuzhiyun save = ''; 119*4882a593Smuzhiyun } 120*4882a593Smuzhiyun 121*4882a593Smuzhiyun // 122*4882a593Smuzhiyun // this is the onclick handler for all of the check box 123*4882a593Smuzhiyun // items in edit columns dialog 124*4882a593Smuzhiyun // 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun function showhideTableColumn( clname, sh, orderkey ) { 127*4882a593Smuzhiyun editcol = document.getElementById( 'editcol' ); 128*4882a593Smuzhiyun if ( editcol.offsetWidth <= 0 ) { 129*4882a593Smuzhiyun 130*4882a593Smuzhiyun // 131*4882a593Smuzhiyun // this path is taken when the page is first 132*4882a593Smuzhiyun // getting initialized - no dialog visible, 133*4882a593Smuzhiyun // perform both the immediate and delayed actions 134*4882a593Smuzhiyun // 135*4882a593Smuzhiyun 136*4882a593Smuzhiyun showhideImmediateTableAction( clname, sh, orderkey ); 137*4882a593Smuzhiyun showhideDelayedTableAction( clname, sh, orderkey ); 138*4882a593Smuzhiyun return; 139*4882a593Smuzhiyun } 140*4882a593Smuzhiyun if ( !editColTimer ) { 141*4882a593Smuzhiyun 142*4882a593Smuzhiyun // 143*4882a593Smuzhiyun // we don't have a timer active so set one up 144*4882a593Smuzhiyun // and clear the action list 145*4882a593Smuzhiyun // 146*4882a593Smuzhiyun 147*4882a593Smuzhiyun editColTimer = setInterval( checkVisible, 250 ); 148*4882a593Smuzhiyun editColAction = [ ]; 149*4882a593Smuzhiyun } 150*4882a593Smuzhiyun 151*4882a593Smuzhiyun // 152*4882a593Smuzhiyun // save the action to be taken when the dialog closes 153*4882a593Smuzhiyun // 154*4882a593Smuzhiyun 155*4882a593Smuzhiyun editColAction[ clname ] = [ sh, orderkey ]; 156*4882a593Smuzhiyun showhideImmediateTableAction( clname, sh, orderkey ); 157*4882a593Smuzhiyun } 158*4882a593Smuzhiyun 159*4882a593Smuzhiyun </script> 160*4882a593Smuzhiyun 161*4882a593Smuzhiyun<!-- control header --> 162*4882a593Smuzhiyun<div class="navbar navbar-default"> 163*4882a593Smuzhiyun <div class="container-fluid"> 164*4882a593Smuzhiyun <div class="navbar-header"> 165*4882a593Smuzhiyun <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#table-chrome-collapse-variablehistory" aria-expanded="false"> 166*4882a593Smuzhiyun <span class="sr-only">Toggle table options</span> 167*4882a593Smuzhiyun <span class="icon-bar"></span> 168*4882a593Smuzhiyun <span class="icon-bar"></span> 169*4882a593Smuzhiyun <span class="icon-bar"></span> 170*4882a593Smuzhiyun </button> 171*4882a593Smuzhiyun </div> 172*4882a593Smuzhiyun <div class="collapse navbar-collapse" id="table-chrome-collapse-variablehistory"> 173*4882a593Smuzhiyun <form class="navbar-form navbar-left" id="searchform"> 174*4882a593Smuzhiyun <div class="form-group"> 175*4882a593Smuzhiyun <div class="btn-group"> 176*4882a593Smuzhiyun <input class="form-control" id="search" name="search" type="text" placeholder="Search {%if object_search_display %}{{object_search_display}}{%else%}{{objectname}}{%endif%}" value="{%if request.GET.search %}{{request.GET.search}}{% endif %}"/> 177*4882a593Smuzhiyun {% if request.GET.search %}<a href="javascript:$('#search').val('');searchform.submit()" tabindex="-1"><span class="remove-search-btn-variables glyphicon glyphicon-remove-circle"></span></a>{%endif%} 178*4882a593Smuzhiyun </div> 179*4882a593Smuzhiyun </div> 180*4882a593Smuzhiyun <input type="hidden" name="orderby" value="{{request.GET.orderby}}"> 181*4882a593Smuzhiyun <input type="hidden" name="page" value="1"> 182*4882a593Smuzhiyun <button class="btn btn-default" id="search-button" type="submit" value="Search">Search</button> 183*4882a593Smuzhiyun </form> 184*4882a593Smuzhiyun <form class="navbar-form navbar-right"> 185*4882a593Smuzhiyun <div class="form-group"> 186*4882a593Smuzhiyun <label>Show rows:</label> 187*4882a593Smuzhiyun <select class="pagesize form-control"> 188*4882a593Smuzhiyun {% with "10 25 50 100 150" as list%} 189*4882a593Smuzhiyun {% for i in list.split %} 190*4882a593Smuzhiyun <option value="{{i}}">{{i}}</option> 191*4882a593Smuzhiyun {% endfor %} 192*4882a593Smuzhiyun {% endwith %} 193*4882a593Smuzhiyun </select> 194*4882a593Smuzhiyun </div> 195*4882a593Smuzhiyun </form> 196*4882a593Smuzhiyun 197*4882a593Smuzhiyun <div class="btn-group navbar-right"> 198*4882a593Smuzhiyun {% if tablecols %} 199*4882a593Smuzhiyun <button id="edit-columns-button" class="btn btn-default navbar-btn dropdown-toggle" data-toggle="dropdown">Edit columns 200*4882a593Smuzhiyun <span class="caret"></span> 201*4882a593Smuzhiyun </button> 202*4882a593Smuzhiyun<!-- 203*4882a593Smuzhiyun {{tablecols|sortcols}} 204*4882a593Smuzhiyun--> 205*4882a593Smuzhiyun <ul id="editcol" class="dropdown-menu editcol"> 206*4882a593Smuzhiyun {% for i in tablecols|sortcols %} 207*4882a593Smuzhiyun <li> 208*4882a593Smuzhiyun <div class="checkbox"> 209*4882a593Smuzhiyun <label {% if not i.clclass %} class="muted" {%endif%}> 210*4882a593Smuzhiyun <input type="checkbox" class="chbxtoggle" 211*4882a593Smuzhiyun {% if i.clclass %} 212*4882a593Smuzhiyun id="{{i.clclass}}" 213*4882a593Smuzhiyun value="ct{{i.name}}" 214*4882a593Smuzhiyun {% if not i.hidden %} 215*4882a593Smuzhiyun checked="checked" 216*4882a593Smuzhiyun {%endif%} 217*4882a593Smuzhiyun onclick="showhideTableColumn( 218*4882a593Smuzhiyun $(this).attr('id'), 219*4882a593Smuzhiyun $(this).is(':checked'), 220*4882a593Smuzhiyun {% if i.ordericon %} 221*4882a593Smuzhiyun '{{i.orderkey}}' 222*4882a593Smuzhiyun {% else %} 223*4882a593Smuzhiyun undefined 224*4882a593Smuzhiyun {% endif %} 225*4882a593Smuzhiyun )" 226*4882a593Smuzhiyun {%else%} 227*4882a593Smuzhiyun checked disabled 228*4882a593Smuzhiyun {% endif %}/>{{i.name}} 229*4882a593Smuzhiyun </label> 230*4882a593Smuzhiyun </div> 231*4882a593Smuzhiyun </li> 232*4882a593Smuzhiyun {% endfor %} 233*4882a593Smuzhiyun </ul> 234*4882a593Smuzhiyun {% endif %} 235*4882a593Smuzhiyun </div> 236*4882a593Smuzhiyun </div> <!-- navbar-collapse --> 237*4882a593Smuzhiyun </div> <!-- container-fluid --> 238*4882a593Smuzhiyun</div> <!-- navbar-default --> 239*4882a593Smuzhiyun 240*4882a593Smuzhiyun<!-- the actual rows of the table --> 241*4882a593Smuzhiyun <table class="table table-bordered table-hover tablesorter" id="otable"> 242*4882a593Smuzhiyun <thead> 243*4882a593Smuzhiyun <!-- Table header row; generated from "tablecols" entry in the context dict --> 244*4882a593Smuzhiyun <tr> 245*4882a593Smuzhiyun {% for tc in tablecols %}<th class="{%if tc.dclass%}{{tc.dclass}}{%endif%} {% if tc.clclass %}{{tc.clclass}}{% endif %}"> 246*4882a593Smuzhiyun {%if tc.qhelp%}<span class="glyphicon glyphicon-question-sign get-help" title="{{tc.qhelp}}"></span>{%endif%} 247*4882a593Smuzhiyun {%if tc.orderfield%}<a {%if tc.ordericon%} class="sorted" {%endif%}href="javascript:reload_params({'page': 1, 'orderby' : '{{tc.orderfield}}' })">{{tc.name}}</a>{%else%}<span class="text-muted">{{tc.name}}</span>{%endif%} 248*4882a593Smuzhiyun {%if tc.ordericon%} <i class="icon-caret-{{tc.ordericon}}"></i>{%endif%} 249*4882a593Smuzhiyun {%if tc.filter%}<div class="btn-group pull-right"> 250*4882a593Smuzhiyun <a href="#filter_{{tc.filter.class}}" role="button" class="btn btn-xs {%if request.GET.filter%}{{tc.filter.options|filtered_icon:request.GET.filter}} {%endif%}" {%if request.GET.filter and tc.filter.options|filtered_tooltip:request.GET.filter %} title="<p>{{tc.filter.options|filtered_tooltip:request.GET.filter}}</p><p><a class='btn btn-sm btn-primary' href=javascript:reload_params({'filter':''})>Show all {% if filter_search_display %}{{filter_search_display}}{% else %}{{objectname}}{% endif %}</a></p>" {%endif%} data-toggle="modal"> <span class="glyphicon glyphicon-filter filtered"></span> </a> 251*4882a593Smuzhiyun </div>{%endif%} 252*4882a593Smuzhiyun </th>{% endfor %} 253*4882a593Smuzhiyun </tr> 254*4882a593Smuzhiyun </thead> 255*4882a593Smuzhiyun <tbody> 256*4882a593Smuzhiyun 257