Ignore:
Timestamp:
Jan 24, 2014, 6:36:41 PM (10 years ago)
Author:
Eric
Message:

merge r26094, r26292, r26686, r26706, r26861, r26862, r26914, r26933, r26934 and r26947 from trunk to branch 2.6

Dev:

  • Replace word "categorie" by "album" in en_UK and fr_FR translation files
  • Fix html5 validation errors
  • Piwigo 2.6 compliance: Improve confirmation email sending - More accurate when users or admins are set to confirm registrations
  • Piwigo 2.6 compliance: Update tablesorter jquery plugin for compliance with jquery v1.10
  • Improve confirmation and information email sending workflow
  • HTML5 recommandation improvement - Tablesorter and tablepager jquery plugin customization (step 1)

Translations:
Update pt_BR, thanks to : flaviove
Update pt_PT, thanks to : Bridges
Add sk_SK, thanks to : JoeKundlak
Update sk_SK, thanks to : JoeKundlak

Location:
extensions/UserAdvManager/branches/2.6
Files:
5 added
1 deleted
16 edited

Legend:

Unmodified
Added
Removed
  • extensions/UserAdvManager/branches/2.6/admin/UAM_admin.php

    r25745 r26948  
    169169    $conf_UAM_ConfirmMail = unserialize($conf['UserAdvManager_ConfirmMail']);
    170170   
    171     if (((isset($conf_UAM['1']) and ($conf_UAM['1'] == 'false' or $conf_UAM['1'] == 'local')) or ($_POST['UAM_Confirm_Mail'] == 'false' or $_POST['UAM_Confirm_Mail'] == 'local')) and $_POST['UAM_GTAutoMail'] == 'true')
     171    if (((isset($conf_UAM['CONFIRM_MAIL']) and ($conf_UAM['CONFIRM_MAIL'] == 'false' or $conf_UAM['CONFIRM_MAIL'] == 'local')) or ($_POST['UAM_Confirm_Mail'] == 'false' or $_POST['UAM_Confirm_Mail'] == 'local')) and $_POST['UAM_GTAutoMail'] == 'true')
    172172    {
    173173      $newvalue = 'false';
  • extensions/UserAdvManager/branches/2.6/admin/template/ghosttracker.tpl

    r21747 r26948  
    22{combine_script id='jquery.cluetip' require='jquery' path='themes/default/js/plugins/jquery.cluetip.js'}
    33{combine_script id='jquery.tablesorter' require='jquery' path=$UAM_PATH|@cat:'admin/template/js/jquery.tablesorter.min.js'}
    4 {combine_script id='jquery.tablesorter.pager' require='jquery' path=$UAM_PATH|@cat:'admin/template/js/jquery.tablesorter.pager.js'}
     4{combine_script id='jquery.tablesorter.pager' require='jquery' path=$UAM_PATH|@cat:'admin/template/js/jquery.tablesorter.pager.min.js'}
    55
    66{combine_css path= $UAM_PATH|@cat:'admin/template/uam.css'}
     
    1919    {ldelim}
    2020      $("#sorting")
    21       .tablesorter({ldelim}sortList:[[4,1]], headers: {ldelim} 0: {ldelim} sorter: false {rdelim}{rdelim}{rdelim})
    22       .tablesorterPager({ldelim}container: $("#pager"), positionFixed: false, size: 20, totalPages: 0{rdelim});
     21      .tablesorter({ldelim}sortList:[[4,1]], headers: {ldelim} 0: {ldelim} sorter: false {rdelim},2: {ldelim} sorter: false {rdelim}{rdelim}{rdelim})
     22      .tablesorterPager({ldelim}container: $("#pager"), page: 0, size: 20, output: '{ldelim}page{rdelim} / {ldelim}totalPages{rdelim}',{rdelim});
    2323    {rdelim});
    2424</script>
     
    2828</div>
    2929
    30 <form method="post" action="" class="general">
     30<form method="post" class="general">
    3131  <fieldset>
    3232        <legend>{'UAM_GT_Init'|@translate}</legend>
     
    3939  </fieldset>
    4040 
     41    {if count($users) > 0}
    4142  <fieldset>
    4243    <legend class="cluetip" title="{'UAM_GhostTracker_Title'|translate}|{'UAM_gtTitle_d'|translate}">{'UAM_GhostTracker_Title'|@translate}</legend>
    43     {if count($users) > 0}
    44       <table id="sorting" class="table2" width="97%" summary="">
     44      <table id="sorting" class="table2">
    4545        <thead>
    4646          <tr class="throw">
     
    5858              <td><input type="checkbox" name="selection[]" value="{$user.ID}" {$user.CHECKED} id="selection-{$user.ID}"/></td>
    5959              <td><label for="selection-{$user.ID}">{$user.USERNAME}</label></td>
    60               <td style="text-align:center;"><a href="./admin.php?page=profile&amp;user_id={$user.ID}" title="{'Profile'|@translate}" onclick="window.open(this.href); return false;"><img src="{$UAM_PATH}admin/template/icon/edit_s.png"/></a></td>
     60              <td style="text-align:center;"><a href="./admin.php?page=profile&amp;user_id={$user.ID}" title="{'Profile'|@translate}" onclick="window.open(this.href); return false;"><img src="{$UAM_PATH}admin/template/icon/edit_s.png" alt=""/></a></td>
    6161              <td>{$user.EMAIL}</td>
    6262            {if $user.REMINDER == l10n('UAM_Reminder_Sent_NOK')}
     
    7575      </table>
    7676      <div id="pager" class="pager">
    77       <form>
    78         <img src="{$UAM_PATH}admin/template/icon/first.png" class="first"/>
    79         <img src="{$UAM_PATH}admin/template/icon/prev.png" class="prev"/>
     77        <img src="{$UAM_PATH}admin/template/icon/first.png" class="first" alt=""/>
     78        <img src="{$UAM_PATH}admin/template/icon/prev.png" class="prev" alt=""/>
    8079        <input type="text" class="pagedisplay"/>
    81         <img src="{$UAM_PATH}admin/template/icon/next.png" class="next"/>
    82         <img src="{$UAM_PATH}admin/template/icon/last.png" class="last"/>
    83         <select class="pagesize">
     80        <img src="{$UAM_PATH}admin/template/icon/next.png" class="next" alt=""/>
     81        <img src="{$UAM_PATH}admin/template/icon/last.png" class="last" alt=""/>
     82        <select class="pagesize" title="{'UAM_Select page size'|@translate}">
    8483          <option  value="10">10</option>
    8584          <option selected="selected" value="20">20</option>
     
    8786          <option value="40">40</option>
    8887        </select>
    89       </form>
     88        <select class="gotoPage" title="{'UAM_Select page number'|@translate}"></select>
    9089      </div>
    9190      <br/>
     
    104103  </fieldset>
    105104    {else}
     105  <fieldset>
     106    <legend class="cluetip" title="{'UAM_GhostTracker_Title'|translate}|{'UAM_gtTitle_d'|translate}">{'UAM_GhostTracker_Title'|@translate}</legend>
    106107                  <div>
    107108                         {'UAM_No_Ghosts'|@translate}
    108109                  </div>
     110  </fieldset>
    109111                {/if}
    110112</form>
  • extensions/UserAdvManager/branches/2.6/admin/template/global.tpl

    r25092 r26948  
    151151</div>
    152152
    153 <form method="post" action="" class="general">
     153<form method="post" class="general">
    154154
    155155  <p>
     
    181181            <label for="UAM_Username_Char_true"><input id="UAM_Username_Char_true" type="radio" value="true" {$UAM_USERNAME_CHAR_TRUE} name="UAM_Username_Char"/>
    182182              {'UAM_Username_Char_true'|@translate}
     183            </label>
    183184            <div class="uam_leftmargin">
    184185              <input type="text" name="UAM_Username_List" value="{$UAM_USERNAME_CHAR_LIST}" size="20" style="text-align: center;"/>
    185186            </div>
    186             </label>
    187187          </li>
    188188
     
    297297              &nbsp;&nbsp;<textarea class="uam_textfields" name="UAM_AdminValidationMail_Subject" id="UAM_AdminValidationMail_Subject" rows="5" {$TAG_INPUT_ENABLED}>{$UAM_ADMINVALIDATIONMAIL_SUBJECT}</textarea>
    298298            </li>
     299            <li style="list-style-type: none;">
    299300            <ul>
    300301              <li>
     
    305306              </li>
    306307            </ul>
     308            </li>
    307309          </ul>
    308310          <ul>
     
    313315              &nbsp;&nbsp;<textarea class="uam_textfields" name="UAM_ConfirmMail_Subject" id="UAM_ConfirmMail_Subject" rows="5" {$TAG_INPUT_ENABLED}>{$UAM_CONFIRMMAIL_SUBJECT}</textarea>
    314316            </li>
     317            <li style="list-style-type: none;">
    315318            <ul>
    316319              <li>
     
    325328              </li>
    326329            </ul>
     330            </li>
    327331<!--
    328332            {* if 'FCK_PATH'|@defined *}
     
    332336            {* /if *}
    333337-->
     338          <li style="list-style-type: none;">
    334339          <br/><hr/><br/>
     340          </li>
    335341            <li>
    336342              <label class="cluetip" title="{'UAM_confirmmail_custom_Txt1'|translate}|{'UAM_confirmmail_custom1_d'|translate}">
     
    340346            </li>
    341347            {if 'FCK_PATH'|@defined}
     348            <li style="list-style-type: none;">
    342349              <div style="text-align:right;">
    343350                <a href="#" onClick="toogleEditor('UAM_ConfirmMail_Custom_Txt1'); return false;">FCK Editor On/Off</a>
    344351              </div>
     352            </li>
    345353            {/if}
    346354            <li>
     
    351359            </li>
    352360            {if 'FCK_PATH'|@defined}
     361            <li style="list-style-type: none;">
    353362              <div style="text-align:right;">
    354363                <a href="#" onClick="toogleEditor('UAM_ConfirmMail_Custom_Txt2'); return false;">FCK Editor On/Off</a>
    355364              </div>
     365            </li>
    356366            {/if}
    357367          </ul>
     
    377387                <textarea class="uam_textfields" name="UAM_CustomRejectConnexion_Text" id="UAM_CustomRejectConnexion_Text" rows="10" {$TAG_INPUT_ENABLED}>{$UAM_REJECTCONNECT_TEXT}</textarea>
    378388            {if 'FCK_PATH'|@defined}
     389            <li style="list-style-type: none;">
    379390              <div style="text-align:right;">
    380391                <a href="#" onClick="toogleEditor('UAM_CustomRejectConnexion_Text'); return false;">FCK Editor On/Off</a>
    381392              </div>
     393            </li>
    382394            {/if}
    383395              </li>
     
    388400          <fieldset id="UAM_Change" style="display: none">
    389401          <ul>
     402          <li style="list-style-type: none;">
    390403            <div id="uam_notice">{'UAM_Confirm_grpstat_notice'|@translate}</div>
     404          </li>
     405              <li style="list-style-type: none;">
    391406              <br/>
     407              </li>
    392408              <li>
    393409                <label class="cluetip" title="{'UAM_confirmgrpTitle'|translate}|{'UAM_confirmgrpTitle_d'|translate}">
     
    395411                </label>
    396412              </li>
     413              <li style="list-style-type: none;">
    397414              <ul>
    398415                <li>
     
    413430                </li>
    414431              </ul>
     432              </li>
    415433              <li>
    416434                <label class="cluetip" title="{'UAM_confirmstatTitle'|translate}|{'UAM_confirmstatTitle_d'|translate}">
     
    418436                </label>
    419437              </li>
     438              <li style="list-style-type: none;">
    420439              <ul>
    421440                <li>
     
    436455                </li>
    437456              </ul>
     457              </li>
    438458              <li>
    439459                <label class="cluetip" title="{'UAM_confirmlevelTitle'|translate}|{'UAM_confirmlevelTitle_d'|translate}">
     
    441461                </label>
    442462              </li>
     463              <li style="list-style-type: none;">
    443464              <ul>
    444465                <li>
     
    459480                </li>
    460481              </ul>
     482              </li>
    461483            </ul>
    462484            </fieldset>
     
    467489                  {'UAM_ValidationLimit_Info'|@translate}
    468490                </label>
    469                 <label for="UAM_ConfirmMail_TimeOut_false" ><input id="UAM_ConfirmMail_TimeOut_false" type="radio" value="false" {$UAM_CONFIRMMAIL_TIMEOUT_FALSE} name="UAM_ConfirmMail_TimeOut"/>
     491                <label for="UAM_ConfirmMail_TimeOut_false" >
     492                  <input id="UAM_ConfirmMail_TimeOut_false" type="radio" value="false" {$UAM_CONFIRMMAIL_TIMEOUT_FALSE} name="UAM_ConfirmMail_TimeOut"/>
    470493                  {'UAM_Disable'|@translate}
    471494                </label>
    472                 <label for="UAM_ConfirmMail_TimeOut_true" ><input id="UAM_ConfirmMail_TimeOut_true" type="radio" value="true" {$UAM_CONFIRMMAIL_TIMEOUT_TRUE} name="UAM_ConfirmMail_TimeOut"/>
     495                <label for="UAM_ConfirmMail_TimeOut_true" >
     496                  <input id="UAM_ConfirmMail_TimeOut_true" type="radio" value="true" {$UAM_CONFIRMMAIL_TIMEOUT_TRUE} name="UAM_ConfirmMail_TimeOut"/>
    473497                  {'UAM_ConfirmMail_TimeOut_true'|@translate}
    474                 <input type="text" name="UAM_ConfirmMail_Delay" value="{$UAM_CONFIRMMAIL_DELAY}" size="5" style="text-align: center;"/>
    475                 </label>
     498                </label>
     499                  <input type="text" name="UAM_ConfirmMail_Delay" value="{$UAM_CONFIRMMAIL_DELAY}" size="5" style="text-align: center;"/>
    476500              </li>
    477501              <li>
     
    486510                </label>
    487511              </li>
    488             <a id="show_UAM_ConfirmMail" >{'UAM_Customize_messagesandmails'|translate}</a>
    489             <a id="hide_UAM_ConfirmMail" style="display: none">{'hide details'|translate}</a>
     512            <li style="list-style-type: none;">
     513              <a id="show_UAM_ConfirmMail" >{'UAM_Customize_messagesandmails'|translate}</a>
     514              <a id="hide_UAM_ConfirmMail" style="display: none">{'hide details'|translate}</a>
     515            </li>
    490516            <fieldset id="UAM_ConfirmMail" style="display: none">
    491517              <ul>
     
    550576                {'UAM_Enable'|@translate}
    551577              </label>
    552             </li>
    553578            <ul>
    554579              <li>
     
    575600              </li>
    576601            </ul>
     602            </li>
    577603          </ul>
    578604          </fieldset>
     
    593619              {'UAM_GhostTracker'|@translate}
    594620            </label>
    595             <label for"UAM_GhostUser_Tracker_false">
     621            <label for="UAM_GhostUser_Tracker_false">
    596622              <input id="UAM_GhostUser_Tracker_false" type="radio" value="false" {$UAM_GHOSTRACKER_FALSE} name="UAM_GhostUser_Tracker"/>
    597623              {'UAM_Disable'|@translate}
    598624            </label>
    599             <label for"UAM_GhostUser_Tracker_true">
     625            <label for="UAM_GhostUser_Tracker_true">
    600626              <input id="UAM_GhostUser_Tracker_true" type="radio" value="true" {$UAM_GHOSTRACKER_TRUE} name="UAM_GhostUser_Tracker"/>
    601627              {'UAM_GhostTracker_true'|@translate}
     628            </label>
    602629            <input type="text" name="UAM_GhostTracker_DayLimit" value="{$UAM_GHOSTRACKER_DAYLIMIT}" size="5" style="text-align: center;"/>
    603             </label>
    604630         
    605631          </li>
     
    636662          </ul>
    637663          </fieldset>
    638           <br><hr><br>
     664          <br/><hr/><br/>
    639665          <ul>
    640666            <li>
     
    642668                {'UAM_GTAuto'|@translate}
    643669              </label>
    644            
     670
    645671              <label for="UAM_GTAuto_false"><input id="UAM_GTAuto_false" type="radio" value="false" {$UAM_GTAUTO_FALSE} name="UAM_GTAuto"/>
    646672                {'UAM_Disable'|@translate}
    647673              </label>
    648              
     674
    649675              <label for="UAM_GTAuto_true"><input id="UAM_GTAuto_true" type="radio" value="true" {$UAM_GTAUTO_TRUE} name="UAM_GTAuto"/>
    650676                {'UAM_Enable'|@translate}
    651677              </label>
    652            
    653             </li>
     678
    654679              <ul id="UAM_GTAUTO" {if $UAM_GTAUTO_FALSE}style="display: none"{/if}>
    655680                <li>
     
    657682                    {'UAM_GTAutoDel'|@translate}
    658683                  </label>
    659                
     684
    660685                    <textarea class="uam_textfields" name="UAM_GTAutoDelText" id="UAM_GTAutoDelText" rows="10" {$TAG_INPUT_ENABLED}>{$UAM_GTAUTODEL_TEXT}</textarea>
    661                
    662                    
     686
    663687                {if 'FCK_PATH'|@defined}
    664688                  <div style="text-align:right;">
     
    667691                {/if}
    668692                </li>
    669  
     693
    670694                <li>
    671695                  <label class="cluetip" title="{'UAM_GTAutoGp'|translate}|{'UAM_GTAutoGpTitle_d'|translate}">
    672696                    {'UAM_GTAutoGp'|@translate}
    673697                  </label>
    674                
     698
    675699                  <ul>
    676700                    <li>
     
    681705                          {html_options name="UAM_Downgrade_Group" options=$Downgrade_Group.group_options selected=$Downgrade_Group.group_selected}
    682706                        </div>
    683                    
    684707                    </li>
    685708 
     
    691714                        {html_options name="UAM_Downgrade_Status" options=$Downgrade_Status.Status_options selected=$Downgrade_Status.Status_selected}
    692715                      </div>
    693                    
    694716                    </li>
    695  
     717
    696718                    <li>
    697719                      <label>
     
    701723                        {html_options name="UAM_Downgrade_Level" options=$Downgrade_Level.Level_options selected=$Downgrade_Level.Level_selected}
    702724                      </div>
    703                    
    704725                    </li>
    705726                  </ul>
    706  
     727
    707728                  <ul>
    708729                    <li>
     
    717738                        {'UAM_Enable'|@translate}
    718739                      </label>
    719                    
     740
    720741                      <li>
    721742                        <label class="cluetip" title="{'UAM_GTAutomail_Subject'|translate}|{'UAM_GTAutomail_Subject_d'|translate}">
    722743                          {'UAM_GTAutomail_Subject'|@translate}
    723744                        </label>
    724                      
     745
    725746                        &nbsp;&nbsp;<textarea class="uam_textfields" name="UAM_GTAutoMail_Subject" id="UAM_GTAutoMail_Subject" rows="5" {$TAG_INPUT_ENABLED}>{$UAM_GTAUTOMAIL_SUBJECT}</textarea>
    726                      
    727747                      </li>
    728  
    729                       <ul>
    730                         <li>
    731                           <label class="cluetip" title="{'UAM_GTAutomail_Text'|translate}|{'UAM_GTAutomail_Text_d'|translate}">
    732                             {'UAM_GTAutomail_Text'|@translate}
    733                           </label>
    734                        
    735                           <textarea class="uam_textfields" name="UAM_GTAutoMailText" id="UAM_GTAutoMailText" rows="10" {$TAG_INPUT_ENABLED}>{$UAM_GTAUTOMAILTEXT}</textarea>
    736                        
     748                      <li style="list-style-type: none;">
     749                        <ul>
     750                          <li>
     751                            <label class="cluetip" title="{'UAM_GTAutomail_Text'|translate}|{'UAM_GTAutomail_Text_d'|translate}">
     752                              {'UAM_GTAutomail_Text'|@translate}
     753                            </label>
     754
     755                            <textarea class="uam_textfields" name="UAM_GTAutoMailText" id="UAM_GTAutoMailText" rows="10" {$TAG_INPUT_ENABLED}>{$UAM_GTAUTOMAILTEXT}</textarea>
    737756                        </li>
    738757                      </ul>
     758                      </li>
    739759                    </li>
    740760                  </ul>
    741761                </li>
    742762              </ul>
    743             </ul>
     763            </li>
    744764          </ul>
    745765        </div>
     
    787807           
    788808              &nbsp;&nbsp;<textarea class="uam_textfields" name="UAM_InfoMail_Subject" id="UAM_InfoMail_Subject" rows="5" {$TAG_INPUT_ENABLED}>{$UAM_INFOMAIL_SUBJECT}</textarea>
    789            
    790             </li>
    791 
    792             <ul>
    793               <li>
    794                 <label class="cluetip" title="{'UAM_MailInfo_Text'|translate}|{'UAM_infotxtTitle_d'|translate}">
    795                   {'UAM_MailInfo_Text'|@translate}
    796                 </label>
    797              
    798                 <textarea class="uam_textfields" name="UAM_MailInfo_Text" id="UAM_MailInfo_Text" rows="10" {$TAG_INPUT_ENABLED}>{$UAM_MAILINFO_TEXT}</textarea>
    799              
    800               </li>
    801             </ul>
     809            </li>
     810
     811            <li style="list-style-type: none;">
     812              <ul>
     813                <li>
     814                  <label class="cluetip" title="{'UAM_MailInfo_Text'|translate}|{'UAM_infotxtTitle_d'|translate}">
     815                    {'UAM_MailInfo_Text'|@translate}
     816                  </label>
     817
     818                  <textarea class="uam_textfields" name="UAM_MailInfo_Text" id="UAM_MailInfo_Text" rows="10" {$TAG_INPUT_ENABLED}>{$UAM_MAILINFO_TEXT}</textarea>
     819                </li>
     820              </ul>
     821            </li>
    802822<!--
    803823            {* if 'FCK_PATH'|@defined *}
     
    811831
    812832        <ul>
    813             <li>
    814               <label class="cluetip" title="{'UAM_AddURL2Mail'|translate}|{'UAM_AddURL2Mail_d'|translate}">
    815                 {'UAM_AddURL2Mail'|@translate}
    816               </label>
    817               <label for="UAM_AddURL2Mail_false"><input id="UAM_AddURL2Mail_false" value="false" {$UAM_ADDURL2MAIL_FALSE} name="UAM_AddURL2Mail" type="radio"/>
    818                 {'UAM_Disable'|@translate}
    819               </label>
    820               <label for="UAM_AddURL2Mail_true"><input id="UAM_AddURL2Mail_true" value="true" {$UAM_ADDURL2MAIL_TRUE} name="UAM_AddURL2Mail" type="radio"/>
    821                 {'UAM_Enable'|@translate}
    822               </label>
    823             </li>
     833          <li>
     834            <label class="cluetip" title="{'UAM_AddURL2Mail'|translate}|{'UAM_AddURL2Mail_d'|translate}">
     835              {'UAM_AddURL2Mail'|@translate}
     836              </label>
     837                <label for="UAM_AddURL2Mail_false"><input id="UAM_AddURL2Mail_false" value="false" {$UAM_ADDURL2MAIL_FALSE} name="UAM_AddURL2Mail" type="radio"/>
     838                  {'UAM_Disable'|@translate}
     839                </label>
     840                <label for="UAM_AddURL2Mail_true"><input id="UAM_AddURL2Mail_true" value="true" {$UAM_ADDURL2MAIL_TRUE} name="UAM_AddURL2Mail" type="radio"/>
     841                  {'UAM_Enable'|@translate}
     842                </label>
     843          </li>
    824844          <li>
    825845            <label class="cluetip" title="{'UAM_Tracking registered users'|translate}|{'UAM_Tracking registered users_d'|translate}">
    826846              {'UAM_Tracking registered users'|@translate}
    827847            </label>
    828          
     848
    829849            <label for="UAM_Add_LastVisit_Column_false"><input id="UAM_Add_LastVisit_Column_false" value="false" {$UAM_ADDLASTVISIT_FALSE} name="UAM_Add_LastVisit_Column" type="radio"/>
    830850              {'UAM_Disable'|@translate}
     
    838858              {'UAM_RedirToProfile'|@translate}
    839859            </label>
    840          
     860
    841861            <label for="UAM_RedirToProfile_false"><input id="UAM_RedirToProfile_false" value="false" {$UAM_REDIRTOPROFILE_FALSE} name="UAM_RedirToProfile" type="radio"/>
    842862              {'UAM_Disable'|@translate}
     
    852872              {'UAM_CustomPasswRetr'|@translate}
    853873            </label>
    854          
     874
    855875            <label for="UAM_CustomPasswRetr_false"><input id="UAM_CustomPasswRetr_false" value="false" {$UAM_CUSTOMPASSWRETR_FALSE} name="UAM_CustomPasswRetr" type="radio"/>
    856876              {'UAM_Disable'|@translate}
     
    868888
    869889  <p>
    870     <input class="submit" type="submit" value="{'UAM_submit'|@translate}" name="submit" {$TAG_INPUT_ENABLED} >&nbsp;<input class="submit" type="submit" value="{'UAM_audit'|@translate}" name="audit"/>
     890    <input class="submit" type="submit" value="{'UAM_submit'|@translate}" name="submit" {$TAG_INPUT_ENABLED} />&nbsp;<input class="submit" type="submit" value="{'UAM_audit'|@translate}" name="audit"/>
    871891  </p>
    872892</form>
    873893
    874894
    875 <div id="instructionTips" class="instructionBlock" >
     895<div id="instructionBkp" class="instructionBlock" >
    876896  <div id="Backup_header" class="instructionBlockHeaderCollapsed" onclick="uam_blockToggleDisplay('Backup_header', 'Backup')">
    877897    <span class="cluetip" title="{'UAM_DumpTxt'|translate}|{'UAM_DumpTitle_d'|translate}">{'UAM_DumpTxt'|@translate}</span>
    878898  </div>
    879  
     899
    880900  <div id="Backup" class="instructionBlockContent" style="display:none">
    881 <fieldset>
    882 <form method="post" action="" class="general">
    883   <p>
    884     {'UAM_Dump_Download'|@translate}&nbsp;
    885       <input type="checkbox" name="dump_download" value="true" {$UAM_DUMP_DOWNLOAD}/>
    886  
    887       <input class="submit" type="submit" value="{'UAM_Save'|@translate}" name="save" {$TAG_INPUT_ENABLED}/>
    888   </p>
    889 </form>
    890 </fieldset>
    891 <fieldset>
    892 <form method="post" action="" class="general">
    893   <p>
    894     {'UAM_Restore'|@translate}
    895       <input class="submit" type="submit" value="{'UAM_Restore_File'|@translate}" name="restore" {$TAG_INPUT_ENABLED}/>
    896   </p>
    897 </form>
    898 </fieldset>
     901    <fieldset>
     902      <form method="post" class="general">
     903        <p>
     904          {'UAM_Dump_Download'|@translate}&nbsp;
     905          <input type="checkbox" name="dump_download" value="true" {$UAM_DUMP_DOWNLOAD}/>
     906
     907          <input class="submit" type="submit" value="{'UAM_Save'|@translate}" name="save" {$TAG_INPUT_ENABLED}/>
     908        </p>
     909      </form>
     910    </fieldset>
     911    <fieldset>
     912      <form method="post" class="general">
     913        <p>
     914          {'UAM_Restore'|@translate}
     915          <input class="submit" type="submit" value="{'UAM_Restore_File'|@translate}" name="restore" {$TAG_INPUT_ENABLED}/>
     916        </p>
     917      </form>
     918    </fieldset>
    899919  </div>
    900 
    901920</div>
    902921
     
    906925    <span class="cluetip" title="{'UAM_Title4'|translate}|{'UAM_tipsTitle_d'|translate}">{'UAM_Title4'|@translate}</span>
    907926  </div>
    908  
     927
    909928  <div id="Tips" class="instructionBlockContent" style="display:none">
    910929    <fieldset>
     
    930949    </fieldset>
    931950  </div>
    932 
    933951</div>
    934952
  • extensions/UserAdvManager/branches/2.6/admin/template/js/jquery.metadata.js

    r6378 r26948  
    22 * Metadata - jQuery plugin for parsing metadata from elements
    33 *
    4  * Copyright (c) 2006 John Resig, Yehuda Katz, Jörn Zaefferer, Paul McLanahan
     4 * Copyright (c) 2006 John Resig, Yehuda Katz, Jörn Zaefferer, Paul McLanahan
    55 *
    66 * Dual licensed under the MIT and GPL licenses:
    77 *   http://www.opensource.org/licenses/mit-license.php
    88 *   http://www.gnu.org/licenses/gpl.html
    9  *
    10  * Revision: $Id$
    119 *
    1210 */
     
    1917 *
    2018 *   attr:  Inside an attribute. The name parameter indicates *which* attribute.
    21  *         
     19 *
    2220 *   class: Inside the class attribute, wrapped in curly braces: { }
    23  *   
     21 *
    2422 *   elem:  Inside a child element (e.g. a script tag). The
    2523 *          name parameter indicates *which* element.
    26  *         
     24 *
    2725 * The metadata for an element is loaded the first time the element is accessed via jQuery.
    2826 *
    2927 * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements
    3028 * matched by expr, then redefine the metadata type and run another $(expr) for other elements.
    31  * 
     29 *
    3230 * @name $.metadata.setType
    3331 *
     
    3634 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
    3735 * @desc Reads metadata from the class attribute
    38  * 
     36 *
    3937 * @example <p id="one" class="some_class" data="{item_id: 1, item_label: 'Label'}">This is a p</p>
    4038 * @before $.metadata.setType("attr", "data")
    4139 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
    4240 * @desc Reads metadata from a "data" attribute
    43  * 
     41 *
    4442 * @example <p id="one" class="some_class"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>
    4543 * @before $.metadata.setType("elem", "script")
    4644 * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
    4745 * @desc Reads metadata from a nested script element
    48  * 
     46 *
    4947 * @param String type The encoding type
    5048 * @param String name The name of the attribute to be used to get metadata (optional)
     
    6260                        type: 'class',
    6361                        name: 'metadata',
    64                         cre: /({.*})/,
     62                        cre: /(\{.*\})/,
    6563                        single: 'metadata'
    6664                },
     
    7068                },
    7169                get: function( elem, opts ){
    72                         var settings = $.extend({},this.defaults,opts);
     70                        var data, m, e, attr,
     71                                settings = $.extend({},this.defaults,opts);
    7372                        // check for empty string in single property
    74                         if ( !settings.single.length ) settings.single = 'metadata';
    75                        
    76                         var data = $.data(elem, settings.single);
     73                        if ( !settings.single.length ) { settings.single = 'metadata'; }
     74
     75                        data = $.data(elem, settings.single);
    7776                        // returned cached data if it already exists
    78                         if ( data ) return data;
    79                        
     77                        if ( data ) { return data; }
     78
    8079                        data = "{}";
    81                        
    82                         if ( settings.type == "class" ) {
    83                                 var m = settings.cre.exec( elem.className );
    84                                 if ( m )
    85                                         data = m[1];
    86                         } else if ( settings.type == "elem" ) {
    87                                 if( !elem.getElementsByTagName )
    88                                         return undefined;
    89                                 var e = elem.getElementsByTagName(settings.name);
    90                                 if ( e.length )
    91                                         data = $.trim(e[0].innerHTML);
    92                         } else if ( elem.getAttribute != undefined ) {
    93                                 var attr = elem.getAttribute( settings.name );
    94                                 if ( attr )
    95                                         data = attr;
     80
     81                        if ( settings.type === "class" ) {
     82                                m = settings.cre.exec( elem.className );
     83                                if ( m ) { data = m[1]; }
     84                        } else if ( settings.type === "elem" ) {
     85                                if( !elem.getElementsByTagName ) { return undefined; }
     86                                e = elem.getElementsByTagName(settings.name);
     87                                if ( e.length ) { data = $.trim(e[0].innerHTML); }
     88                        } else if ( elem.getAttribute !== undefined ) {
     89                                attr = elem.getAttribute( settings.name );
     90                                if ( attr ) { data = attr; }
    9691                        }
    97                        
    98                         if ( data.indexOf( '{' ) <0 )
    99                         data = "{" + data + "}";
    100                        
     92
     93                        if ( data.indexOf( '{' ) <0 ) { data = "{" + data + "}"; }
     94
    10195                        data = eval("(" + data + ")");
    102                        
     96
    10397                        $.data( elem, settings.single, data );
    10498                        return data;
  • extensions/UserAdvManager/branches/2.6/admin/template/js/jquery.tablesorter.js

    r8875 r26948  
    1 /*
    2  *
    3  * TableSorter 2.0 - Client-side table sorting with ease!
    4  * Version 2.0.5b
    5  * @requires jQuery v1.2.3
    6  *
    7  * Copyright (c) 2007 Christian Bach
    8  * Examples and docs at: http://tablesorter.com
    9  * Dual licensed under the MIT and GPL licenses:
    10  * http://www.opensource.org/licenses/mit-license.php
    11  * http://www.gnu.org/licenses/gpl.html
    12  *
    13  */
    14 /**
    15  *
    16  * @description Create a sortable table with multi-column sorting capabilitys
    17  *
    18  * @example $('table').tablesorter();
    19  * @desc Create a simple tablesorter interface.
    20  *
    21  * @example $('table').tablesorter({ sortList:[[0,0],[1,0]] });
    22  * @desc Create a tablesorter interface and sort on the first and secound column column headers.
    23  *
    24  * @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } });
    25  *         
    26  * @desc Create a tablesorter interface and disableing the first and second  column headers.
    27  *     
    28  *
    29  * @example $('table').tablesorter({ headers: { 0: {sorter:"integer"}, 1: {sorter:"currency"} } });
    30  *
    31  * @desc Create a tablesorter interface and set a column parser for the first
    32  *       and second column.
    33  *
    34  *
    35  * @param Object
    36  *            settings An object literal containing key/value pairs to provide
    37  *            optional settings.
    38  *
    39  *
    40  * @option String cssHeader (optional) A string of the class name to be appended
    41  *         to sortable tr elements in the thead of the table. Default value:
    42  *         "header"
    43  *
    44  * @option String cssAsc (optional) A string of the class name to be appended to
    45  *         sortable tr elements in the thead on a ascending sort. Default value:
    46  *         "headerSortUp"
    47  *
    48  * @option String cssDesc (optional) A string of the class name to be appended
    49  *         to sortable tr elements in the thead on a descending sort. Default
    50  *         value: "headerSortDown"
    51  *
    52  * @option String sortInitialOrder (optional) A string of the inital sorting
    53  *         order can be asc or desc. Default value: "asc"
    54  *
    55  * @option String sortMultisortKey (optional) A string of the multi-column sort
    56  *         key. Default value: "shiftKey"
    57  *
    58  * @option String textExtraction (optional) A string of the text-extraction
    59  *         method to use. For complex html structures inside td cell set this
    60  *         option to "complex", on large tables the complex option can be slow.
    61  *         Default value: "simple"
    62  *
    63  * @option Object headers (optional) An array containing the forces sorting
    64  *         rules. This option let's you specify a default sorting rule. Default
    65  *         value: null
    66  *
    67  * @option Array sortList (optional) An array containing the forces sorting
    68  *         rules. This option let's you specify a default sorting rule. Default
    69  *         value: null
    70  *
    71  * @option Array sortForce (optional) An array containing forced sorting rules.
    72  *         This option let's you specify a default sorting rule, which is
    73  *         prepended to user-selected rules. Default value: null
    74  *
    75  * @option Boolean sortLocaleCompare (optional) Boolean flag indicating whatever
    76  *         to use String.localeCampare method or not. Default set to true.
    77  *
    78  *
    79  * @option Array sortAppend (optional) An array containing forced sorting rules.
    80  *         This option let's you specify a default sorting rule, which is
    81  *         appended to user-selected rules. Default value: null
    82  *
    83  * @option Boolean widthFixed (optional) Boolean flag indicating if tablesorter
    84  *         should apply fixed widths to the table columns. This is usefull when
    85  *         using the pager companion plugin. This options requires the dimension
    86  *         jquery plugin. Default value: false
    87  *
    88  * @option Boolean cancelSelection (optional) Boolean flag indicating if
    89  *         tablesorter should cancel selection of the table headers text.
    90  *         Default value: true
    91  *
    92  * @option Boolean debug (optional) Boolean flag indicating if tablesorter
    93  *         should display debuging information usefull for development.
    94  *
    95  * @type jQuery
    96  *
    97  * @name tablesorter
    98  *
    99  * @cat Plugins/Tablesorter
    100  *
    101  * @author Christian Bach/christian.bach@polyester.se
    102  */
    103 
    104 (function ($) {
    105     $.extend({
    106         tablesorter: new
    107         function () {
    108 
    109             var parsers = [],
    110                 widgets = [];
    111 
    112             this.defaults = {
    113                 cssHeader: "header",
    114                 cssAsc: "headerSortUp",
    115                 cssDesc: "headerSortDown",
    116                 cssChildRow: "expand-child",
    117                 sortInitialOrder: "asc",
    118                 sortMultiSortKey: "shiftKey",
    119                 sortForce: null,
    120                 sortAppend: null,
    121                 sortLocaleCompare: true,
    122                 textExtraction: "simple",
    123                 parsers: {}, widgets: [],
    124                 widgetZebra: {
    125                     css: ["even", "odd"]
    126                 }, headers: {}, widthFixed: false,
    127                 cancelSelection: true,
    128                 sortList: [],
    129                 headerList: [],
    130                 dateFormat: "us",
    131                 decimal: '/\.|\,/g',
    132                 onRenderHeader: null,
    133                 selectorHeaders: 'thead th',
    134                 debug: false
    135             };
    136 
    137             /* debuging utils */
    138 
    139             function benchmark(s, d) {
    140                 log(s + "," + (new Date().getTime() - d.getTime()) + "ms");
    141             }
    142 
    143             this.benchmark = benchmark;
    144 
    145             function log(s) {
    146                 if (typeof console != "undefined" && typeof console.debug != "undefined") {
    147                     console.log(s);
    148                 } else {
    149                     alert(s);
    150                 }
    151             }
    152 
    153             /* parsers utils */
    154 
    155             function buildParserCache(table, $headers) {
    156 
    157                 if (table.config.debug) {
    158                     var parsersDebug = "";
    159                 }
    160 
    161                 if (table.tBodies.length == 0) return; // In the case of empty tables
    162                 var rows = table.tBodies[0].rows;
    163 
    164                 if (rows[0]) {
    165 
    166                     var list = [],
    167                         cells = rows[0].cells,
    168                         l = cells.length;
    169 
    170                     for (var i = 0; i < l; i++) {
    171 
    172                         var p = false;
    173 
    174                         if ($.metadata && ($($headers[i]).metadata() && $($headers[i]).metadata().sorter)) {
    175 
    176                             p = getParserById($($headers[i]).metadata().sorter);
    177 
    178                         } else if ((table.config.headers[i] && table.config.headers[i].sorter)) {
    179 
    180                             p = getParserById(table.config.headers[i].sorter);
    181                         }
    182                         if (!p) {
    183 
    184                             p = detectParserForColumn(table, rows, -1, i);
    185                         }
    186 
    187                         if (table.config.debug) {
    188                             parsersDebug += "column:" + i + " parser:" + p.id + "\n";
    189                         }
    190 
    191                         list.push(p);
    192                     }
    193                 }
    194 
    195                 if (table.config.debug) {
    196                     log(parsersDebug);
    197                 }
    198 
    199                 return list;
    200             };
    201 
    202             function detectParserForColumn(table, rows, rowIndex, cellIndex) {
    203                 var l = parsers.length,
    204                     node = false,
    205                     nodeValue = false,
    206                     keepLooking = true;
    207                 while (nodeValue == '' && keepLooking) {
    208                     rowIndex++;
    209                     if (rows[rowIndex]) {
    210                         node = getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex);
    211                         nodeValue = trimAndGetNodeText(table.config, node);
    212                         if (table.config.debug) {
    213                             log('Checking if value was empty on row:' + rowIndex);
    214                         }
    215                     } else {
    216                         keepLooking = false;
    217                     }
    218                 }
    219                 for (var i = 1; i < l; i++) {
    220                     if (parsers[i].is(nodeValue, table, node)) {
    221                         return parsers[i];
    222                     }
    223                 }
    224                 // 0 is always the generic parser (text)
    225                 return parsers[0];
    226             }
    227 
    228             function getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex) {
    229                 return rows[rowIndex].cells[cellIndex];
    230             }
    231 
    232             function trimAndGetNodeText(config, node) {
    233                 return $.trim(getElementText(config, node));
    234             }
    235 
    236             function getParserById(name) {
    237                 var l = parsers.length;
    238                 for (var i = 0; i < l; i++) {
    239                     if (parsers[i].id.toLowerCase() == name.toLowerCase()) {
    240                         return parsers[i];
    241                     }
    242                 }
    243                 return false;
    244             }
    245 
    246             /* utils */
    247 
    248             function buildCache(table) {
    249 
    250                 if (table.config.debug) {
    251                     var cacheTime = new Date();
    252                 }
    253 
    254                 var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0,
    255                     totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0,
    256                     parsers = table.config.parsers,
    257                     cache = {
    258                         row: [],
    259                         normalized: []
    260                     };
    261 
    262                 for (var i = 0; i < totalRows; ++i) {
    263 
    264                     /** Add the table data to main data array */
    265                     var c = $(table.tBodies[0].rows[i]),
    266                         cols = [];
    267 
    268                     // if this is a child row, add it to the last row's children and
    269                     // continue to the next row
    270                     if (c.hasClass(table.config.cssChildRow)) {
    271                         cache.row[cache.row.length - 1] = cache.row[cache.row.length - 1].add(c);
    272                         // go to the next for loop
    273                         continue;
    274                     }
    275 
    276                     cache.row.push(c);
    277 
    278                     for (var j = 0; j < totalCells; ++j) {
    279                         cols.push(parsers[j].format(getElementText(table.config, c[0].cells[j]), table, c[0].cells[j]));
    280                     }
    281 
    282                     cols.push(cache.normalized.length); // add position for rowCache
    283                     cache.normalized.push(cols);
    284                     cols = null;
    285                 };
    286 
    287                 if (table.config.debug) {
    288                     benchmark("Building cache for " + totalRows + " rows:", cacheTime);
    289                 }
    290 
    291                 return cache;
    292             };
    293 
    294             function getElementText(config, node) {
    295 
    296                 var text = "";
    297 
    298                 if (!node) return "";
    299 
    300                 if (!config.supportsTextContent) config.supportsTextContent = node.textContent || false;
    301 
    302                 if (config.textExtraction == "simple") {
    303                     if (config.supportsTextContent) {
    304                         text = node.textContent;
    305                     } else {
    306                         if (node.childNodes[0] && node.childNodes[0].hasChildNodes()) {
    307                             text = node.childNodes[0].innerHTML;
    308                         } else {
    309                             text = node.innerHTML;
    310                         }
    311                     }
    312                 } else {
    313                     if (typeof(config.textExtraction) == "function") {
    314                         text = config.textExtraction(node);
    315                     } else {
    316                         text = $(node).text();
    317                     }
    318                 }
    319                 return text;
    320             }
    321 
    322             function appendToTable(table, cache) {
    323 
    324                 if (table.config.debug) {
    325                     var appendTime = new Date()
    326                 }
    327 
    328                 var c = cache,
    329                     r = c.row,
    330                     n = c.normalized,
    331                     totalRows = n.length,
    332                     checkCell = (n[0].length - 1),
    333                     tableBody = $(table.tBodies[0]),
    334                     rows = [];
    335 
    336 
    337                 for (var i = 0; i < totalRows; i++) {
    338                     var pos = n[i][checkCell];
    339 
    340                     rows.push(r[pos]);
    341 
    342                     if (!table.config.appender) {
    343 
    344                         //var o = ;
    345                         var l = r[pos].length;
    346                         for (var j = 0; j < l; j++) {
    347                             tableBody[0].appendChild(r[pos][j]);
    348                         }
    349 
    350                         //
    351                     }
    352                 }
    353 
    354 
    355 
    356                 if (table.config.appender) {
    357 
    358                     table.config.appender(table, rows);
    359                 }
    360 
    361                 rows = null;
    362 
    363                 if (table.config.debug) {
    364                     benchmark("Rebuilt table:", appendTime);
    365                 }
    366 
    367                 // apply table widgets
    368                 applyWidget(table);
    369 
    370                 // trigger sortend
    371                 setTimeout(function () {
    372                     $(table).trigger("sortEnd");
    373                 }, 0);
    374 
    375             };
    376 
    377             function buildHeaders(table) {
    378 
    379                 if (table.config.debug) {
    380                     var time = new Date();
    381                 }
    382 
    383                 var meta = ($.metadata) ? true : false;
    384                
    385                 var header_index = computeTableHeaderCellIndexes(table);
    386 
    387                 $tableHeaders = $(table.config.selectorHeaders, table).each(function (index) {
    388 
    389                     this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex];
    390                     // this.column = index;
    391                     this.order = formatSortingOrder(table.config.sortInitialOrder);
    392                    
    393                                        
    394                                         this.count = this.order;
    395 
    396                     if (checkHeaderMetadata(this) || checkHeaderOptions(table, index)) this.sortDisabled = true;
    397                                         if (checkHeaderOptionsSortingLocked(table, index)) this.order = this.lockedOrder = checkHeaderOptionsSortingLocked(table, index);
    398 
    399                     if (!this.sortDisabled) {
    400                         var $th = $(this).addClass(table.config.cssHeader);
    401                         if (table.config.onRenderHeader) table.config.onRenderHeader.apply($th);
    402                     }
    403 
    404                     // add cell to headerList
    405                     table.config.headerList[index] = this;
    406                 });
    407 
    408                 if (table.config.debug) {
    409                     benchmark("Built headers:", time);
    410                     log($tableHeaders);
    411                 }
    412 
    413                 return $tableHeaders;
    414 
    415             };
    416 
    417             // from:
    418             // http://www.javascripttoolbox.com/lib/table/examples.php
    419             // http://www.javascripttoolbox.com/temp/table_cellindex.html
    420 
    421 
    422             function computeTableHeaderCellIndexes(t) {
    423                 var matrix = [];
    424                 var lookup = {};
    425                 var thead = t.getElementsByTagName('THEAD')[0];
    426                 var trs = thead.getElementsByTagName('TR');
    427 
    428                 for (var i = 0; i < trs.length; i++) {
    429                     var cells = trs[i].cells;
    430                     for (var j = 0; j < cells.length; j++) {
    431                         var c = cells[j];
    432 
    433                         var rowIndex = c.parentNode.rowIndex;
    434                         var cellId = rowIndex + "-" + c.cellIndex;
    435                         var rowSpan = c.rowSpan || 1;
    436                         var colSpan = c.colSpan || 1
    437                         var firstAvailCol;
    438                         if (typeof(matrix[rowIndex]) == "undefined") {
    439                             matrix[rowIndex] = [];
    440                         }
    441                         // Find first available column in the first row
    442                         for (var k = 0; k < matrix[rowIndex].length + 1; k++) {
    443                             if (typeof(matrix[rowIndex][k]) == "undefined") {
    444                                 firstAvailCol = k;
    445                                 break;
    446                             }
    447                         }
    448                         lookup[cellId] = firstAvailCol;
    449                         for (var k = rowIndex; k < rowIndex + rowSpan; k++) {
    450                             if (typeof(matrix[k]) == "undefined") {
    451                                 matrix[k] = [];
    452                             }
    453                             var matrixrow = matrix[k];
    454                             for (var l = firstAvailCol; l < firstAvailCol + colSpan; l++) {
    455                                 matrixrow[l] = "x";
    456                             }
    457                         }
    458                     }
    459                 }
    460                 return lookup;
    461             }
    462 
    463             function checkCellColSpan(table, rows, row) {
    464                 var arr = [],
    465                     r = table.tHead.rows,
    466                     c = r[row].cells;
    467 
    468                 for (var i = 0; i < c.length; i++) {
    469                     var cell = c[i];
    470 
    471                     if (cell.colSpan > 1) {
    472                         arr = arr.concat(checkCellColSpan(table, headerArr, row++));
    473                     } else {
    474                         if (table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row + 1])) {
    475                             arr.push(cell);
    476                         }
    477                         // headerArr[row] = (i+row);
    478                     }
    479                 }
    480                 return arr;
    481             };
    482 
    483             function checkHeaderMetadata(cell) {
    484                 if (($.metadata) && ($(cell).metadata().sorter === false)) {
    485                     return true;
    486                 };
    487                 return false;
    488             }
    489 
    490             function checkHeaderOptions(table, i) {
    491                 if ((table.config.headers[i]) && (table.config.headers[i].sorter === false)) {
    492                     return true;
    493                 };
    494                 return false;
    495             }
    496                        
    497                          function checkHeaderOptionsSortingLocked(table, i) {
    498                 if ((table.config.headers[i]) && (table.config.headers[i].lockedOrder)) return table.config.headers[i].lockedOrder;
    499                 return false;
    500             }
    501                        
    502             function applyWidget(table) {
    503                 var c = table.config.widgets;
    504                 var l = c.length;
    505                 for (var i = 0; i < l; i++) {
    506 
    507                     getWidgetById(c[i]).format(table);
    508                 }
    509 
    510             }
    511 
    512             function getWidgetById(name) {
    513                 var l = widgets.length;
    514                 for (var i = 0; i < l; i++) {
    515                     if (widgets[i].id.toLowerCase() == name.toLowerCase()) {
    516                         return widgets[i];
    517                     }
    518                 }
    519             };
    520 
    521             function formatSortingOrder(v) {
    522                 if (typeof(v) != "Number") {
    523                     return (v.toLowerCase() == "desc") ? 1 : 0;
    524                 } else {
    525                     return (v == 1) ? 1 : 0;
    526                 }
    527             }
    528 
    529             function isValueInArray(v, a) {
    530                 var l = a.length;
    531                 for (var i = 0; i < l; i++) {
    532                     if (a[i][0] == v) {
    533                         return true;
    534                     }
    535                 }
    536                 return false;
    537             }
    538 
    539             function setHeadersCss(table, $headers, list, css) {
    540                 // remove all header information
    541                 $headers.removeClass(css[0]).removeClass(css[1]);
    542 
    543                 var h = [];
    544                 $headers.each(function (offset) {
    545                     if (!this.sortDisabled) {
    546                         h[this.column] = $(this);
    547                     }
    548                 });
    549 
    550                 var l = list.length;
    551                 for (var i = 0; i < l; i++) {
    552                     h[list[i][0]].addClass(css[list[i][1]]);
    553                 }
    554             }
    555 
    556             function fixColumnWidth(table, $headers) {
    557                 var c = table.config;
    558                 if (c.widthFixed) {
    559                     var colgroup = $('<colgroup>');
    560                     $("tr:first td", table.tBodies[0]).each(function () {
    561                         colgroup.append($('<col>').css('width', $(this).width()));
    562                     });
    563                     $(table).prepend(colgroup);
    564                 };
    565             }
    566 
    567             function updateHeaderSortCount(table, sortList) {
    568                 var c = table.config,
    569                     l = sortList.length;
    570                 for (var i = 0; i < l; i++) {
    571                     var s = sortList[i],
    572                         o = c.headerList[s[0]];
    573                     o.count = s[1];
    574                     o.count++;
    575                 }
    576             }
    577 
    578             /* sorting methods */
    579 
    580             function multisort(table, sortList, cache) {
    581 
    582                 if (table.config.debug) {
    583                     var sortTime = new Date();
    584                 }
    585 
    586                 var dynamicExp = "var sortWrapper = function(a,b) {",
    587                     l = sortList.length;
    588 
    589                 // TODO: inline functions.
    590                 for (var i = 0; i < l; i++) {
    591 
    592                     var c = sortList[i][0];
    593                     var order = sortList[i][1];
    594                     // var s = (getCachedSortType(table.config.parsers,c) == "text") ?
    595                     // ((order == 0) ? "sortText" : "sortTextDesc") : ((order == 0) ?
    596                     // "sortNumeric" : "sortNumericDesc");
    597                     // var s = (table.config.parsers[c].type == "text") ? ((order == 0)
    598                     // ? makeSortText(c) : makeSortTextDesc(c)) : ((order == 0) ?
    599                     // makeSortNumeric(c) : makeSortNumericDesc(c));
    600                     var s = (table.config.parsers[c].type == "text") ? ((order == 0) ? makeSortFunction("text", "asc", c) : makeSortFunction("text", "desc", c)) : ((order == 0) ? makeSortFunction("numeric", "asc", c) : makeSortFunction("numeric", "desc", c));
    601                     var e = "e" + i;
    602 
    603                     dynamicExp += "var " + e + " = " + s; // + "(a[" + c + "],b[" + c
    604                     // + "]); ";
    605                     dynamicExp += "if(" + e + ") { return " + e + "; } ";
    606                     dynamicExp += "else { ";
    607 
    608                 }
    609 
    610                 // if value is the same keep orignal order
    611                 var orgOrderCol = cache.normalized[0].length - 1;
    612                 dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
    613 
    614                 for (var i = 0; i < l; i++) {
    615                     dynamicExp += "}; ";
    616                 }
    617 
    618                 dynamicExp += "return 0; ";
    619                 dynamicExp += "}; ";
    620 
    621                 if (table.config.debug) {
    622                     benchmark("Evaling expression:" + dynamicExp, new Date());
    623                 }
    624 
    625                 eval(dynamicExp);
    626 
    627                 cache.normalized.sort(sortWrapper);
    628 
    629                 if (table.config.debug) {
    630                     benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time:", sortTime);
    631                 }
    632 
    633                 return cache;
    634             };
    635 
    636             function makeSortFunction(type, direction, index) {
    637                 var a = "a[" + index + "]",
    638                     b = "b[" + index + "]";
    639                 if (type == 'text' && direction == 'asc') {
    640                     return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + a + " < " + b + ") ? -1 : 1 )));";
    641                 } else if (type == 'text' && direction == 'desc') {
    642                     return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + b + " < " + a + ") ? -1 : 1 )));";
    643                 } else if (type == 'numeric' && direction == 'asc') {
    644                     return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + a + " - " + b + "));";
    645                 } else if (type == 'numeric' && direction == 'desc') {
    646                     return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + b + " - " + a + "));";
    647                 }
    648             };
    649 
    650             function makeSortText(i) {
    651                 return "((a[" + i + "] < b[" + i + "]) ? -1 : ((a[" + i + "] > b[" + i + "]) ? 1 : 0));";
    652             };
    653 
    654             function makeSortTextDesc(i) {
    655                 return "((b[" + i + "] < a[" + i + "]) ? -1 : ((b[" + i + "] > a[" + i + "]) ? 1 : 0));";
    656             };
    657 
    658             function makeSortNumeric(i) {
    659                 return "a[" + i + "]-b[" + i + "];";
    660             };
    661 
    662             function makeSortNumericDesc(i) {
    663                 return "b[" + i + "]-a[" + i + "];";
    664             };
    665 
    666             function sortText(a, b) {
    667                 if (table.config.sortLocaleCompare) return a.localeCompare(b);
    668                 return ((a < b) ? -1 : ((a > b) ? 1 : 0));
    669             };
    670 
    671             function sortTextDesc(a, b) {
    672                 if (table.config.sortLocaleCompare) return b.localeCompare(a);
    673                 return ((b < a) ? -1 : ((b > a) ? 1 : 0));
    674             };
    675 
    676             function sortNumeric(a, b) {
    677                 return a - b;
    678             };
    679 
    680             function sortNumericDesc(a, b) {
    681                 return b - a;
    682             };
    683 
    684             function getCachedSortType(parsers, i) {
    685                 return parsers[i].type;
    686             }; /* public methods */
    687             this.construct = function (settings) {
    688                 return this.each(function () {
    689                     // if no thead or tbody quit.
    690                     if (!this.tHead || !this.tBodies) return;
    691                     // declare
    692                     var $this, $document, $headers, cache, config, shiftDown = 0,
    693                         sortOrder;
    694                     // new blank config object
    695                     this.config = {};
    696                     // merge and extend.
    697                     config = $.extend(this.config, $.tablesorter.defaults, settings);
    698                     // store common expression for speed
    699                     $this = $(this);
    700                     // save the settings where they read
    701                     $.data(this, "tablesorter", config);
    702                     // build headers
    703                     $headers = buildHeaders(this);
    704                     // try to auto detect column type, and store in tables config
    705                     this.config.parsers = buildParserCache(this, $headers);
    706                     // build the cache for the tbody cells
    707                     cache = buildCache(this);
    708                     // get the css class names, could be done else where.
    709                     var sortCSS = [config.cssDesc, config.cssAsc];
    710                     // fixate columns if the users supplies the fixedWidth option
    711                     fixColumnWidth(this);
    712                     // apply event handling to headers
    713                     // this is to big, perhaps break it out?
    714                     $headers.click(
    715 
    716                     function (e) {
    717                         var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
    718                         if (!this.sortDisabled && totalRows > 0) {
    719                             // Only call sortStart if sorting is
    720                             // enabled.
    721                             $this.trigger("sortStart");
    722                             // store exp, for speed
    723                             var $cell = $(this);
    724                             // get current column index
    725                             var i = this.column;
    726                             // get current column sort order
    727                             this.order = this.count++ % 2;
    728                                                         // always sort on the locked order.
    729                                                         if(this.lockedOrder) this.order = this.lockedOrder;
    730                                                        
    731                                                         // user only whants to sort on one
    732                             // column
    733                             if (!e[config.sortMultiSortKey]) {
    734                                 // flush the sort list
    735                                 config.sortList = [];
    736                                 if (config.sortForce != null) {
    737                                     var a = config.sortForce;
    738                                     for (var j = 0; j < a.length; j++) {
    739                                         if (a[j][0] != i) {
    740                                             config.sortList.push(a[j]);
    741                                         }
    742                                     }
    743                                 }
    744                                 // add column to sort list
    745                                 config.sortList.push([i, this.order]);
    746                                 // multi column sorting
    747                             } else {
    748                                 // the user has clicked on an all
    749                                 // ready sortet column.
    750                                 if (isValueInArray(i, config.sortList)) {
    751                                     // revers the sorting direction
    752                                     // for all tables.
    753                                     for (var j = 0; j < config.sortList.length; j++) {
    754                                         var s = config.sortList[j],
    755                                             o = config.headerList[s[0]];
    756                                         if (s[0] == i) {
    757                                             o.count = s[1];
    758                                             o.count++;
    759                                             s[1] = o.count % 2;
    760                                         }
    761                                     }
    762                                 } else {
    763                                     // add column to sort list array
    764                                     config.sortList.push([i, this.order]);
    765                                 }
    766                             };
    767                             setTimeout(function () {
    768                                 // set css for headers
    769                                 setHeadersCss($this[0], $headers, config.sortList, sortCSS);
    770                                 appendToTable(
    771                                         $this[0], multisort(
    772                                         $this[0], config.sortList, cache)
    773                                                                 );
    774                             }, 1);
    775                             // stop normal event by returning false
    776                             return false;
    777                         }
    778                         // cancel selection
    779                     }).mousedown(function () {
    780                         if (config.cancelSelection) {
    781                             this.onselectstart = function () {
    782                                 return false
    783                             };
    784                             return false;
    785                         }
    786                     });
    787                     // apply easy methods that trigger binded events
    788                     $this.bind("update", function () {
    789                         var me = this;
    790                         setTimeout(function () {
    791                             // rebuild parsers.
    792                             me.config.parsers = buildParserCache(
    793                             me, $headers);
    794                             // rebuild the cache map
    795                             cache = buildCache(me);
    796                         }, 1);
    797                     }).bind("updateCell", function (e, cell) {
    798                         var config = this.config;
    799                         // get position from the dom.
    800                         var pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex];
    801                         // update cache
    802                         cache.normalized[pos[0]][pos[1]] = config.parsers[pos[1]].format(
    803                         getElementText(config, cell), cell);
    804                     }).bind("sorton", function (e, list) {
    805                         $(this).trigger("sortStart");
    806                         config.sortList = list;
    807                         // update and store the sortlist
    808                         var sortList = config.sortList;
    809                         // update header count index
    810                         updateHeaderSortCount(this, sortList);
    811                         // set css for headers
    812                         setHeadersCss(this, $headers, sortList, sortCSS);
    813                         // sort the table and append it to the dom
    814                         appendToTable(this, multisort(this, sortList, cache));
    815                     }).bind("appendCache", function () {
    816                         appendToTable(this, cache);
    817                     }).bind("applyWidgetId", function (e, id) {
    818                         getWidgetById(id).format(this);
    819                     }).bind("applyWidgets", function () {
    820                         // apply widgets
    821                         applyWidget(this);
    822                     });
    823                     if ($.metadata && ($(this).metadata() && $(this).metadata().sortlist)) {
    824                         config.sortList = $(this).metadata().sortlist;
    825                     }
    826                     // if user has supplied a sort list to constructor.
    827                     if (config.sortList.length > 0) {
    828                         $this.trigger("sorton", [config.sortList]);
    829                     }
    830                     // apply widgets
    831                     applyWidget(this);
    832                 });
    833             };
    834             this.addParser = function (parser) {
    835                 var l = parsers.length,
    836                     a = true;
    837                 for (var i = 0; i < l; i++) {
    838                     if (parsers[i].id.toLowerCase() == parser.id.toLowerCase()) {
    839                         a = false;
    840                     }
    841                 }
    842                 if (a) {
    843                     parsers.push(parser);
    844                 };
    845             };
    846             this.addWidget = function (widget) {
    847                 widgets.push(widget);
    848             };
    849             this.formatFloat = function (s) {
    850                 var i = parseFloat(s);
    851                 return (isNaN(i)) ? 0 : i;
    852             };
    853             this.formatInt = function (s) {
    854                 var i = parseInt(s);
    855                 return (isNaN(i)) ? 0 : i;
    856             };
    857             this.isDigit = function (s, config) {
    858                 // replace all an wanted chars and match.
    859                 return /^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g, '')));
    860             };
    861             this.clearTableBody = function (table) {
    862                 if ($.browser.msie) {
    863                     function empty() {
    864                         while (this.firstChild)
    865                         this.removeChild(this.firstChild);
    866                     }
    867                     empty.apply(table.tBodies[0]);
    868                 } else {
    869                     table.tBodies[0].innerHTML = "";
    870                 }
    871             };
    872         }
    873     });
    874 
    875     // extend plugin scope
    876     $.fn.extend({
    877         tablesorter: $.tablesorter.construct
    878     });
    879 
    880     // make shortcut
    881     var ts = $.tablesorter;
    882 
    883     // add default parsers
    884     ts.addParser({
    885         id: "text",
    886         is: function (s) {
    887             return true;
    888         }, format: function (s) {
    889             return $.trim(s.toLocaleLowerCase());
    890         }, type: "text"
    891     });
    892 
    893     ts.addParser({
    894         id: "digit",
    895         is: function (s, table) {
    896             var c = table.config;
    897             return $.tablesorter.isDigit(s, c);
    898         }, format: function (s) {
    899             return $.tablesorter.formatFloat(s);
    900         }, type: "numeric"
    901     });
    902 
    903     ts.addParser({
    904         id: "currency",
    905         is: function (s) {
    906             return /^[£$€?.]/.test(s);
    907         }, format: function (s) {
    908             return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g), ""));
    909         }, type: "numeric"
    910     });
    911 
    912     ts.addParser({
    913         id: "ipAddress",
    914         is: function (s) {
    915             return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
    916         }, format: function (s) {
    917             var a = s.split("."),
    918                 r = "",
    919                 l = a.length;
    920             for (var i = 0; i < l; i++) {
    921                 var item = a[i];
    922                 if (item.length == 2) {
    923                     r += "0" + item;
    924                 } else {
    925                     r += item;
    926                 }
    927             }
    928             return $.tablesorter.formatFloat(r);
    929         }, type: "numeric"
    930     });
    931 
    932     ts.addParser({
    933         id: "url",
    934         is: function (s) {
    935             return /^(https?|ftp|file):\/\/$/.test(s);
    936         }, format: function (s) {
    937             return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//), ''));
    938         }, type: "text"
    939     });
    940 
    941     ts.addParser({
    942         id: "isoDate",
    943         is: function (s) {
    944             return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
    945         }, format: function (s) {
    946             return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(
    947             new RegExp(/-/g), "/")).getTime() : "0");
    948         }, type: "numeric"
    949     });
    950 
    951     ts.addParser({
    952         id: "percent",
    953         is: function (s) {
    954             return /\%$/.test($.trim(s));
    955         }, format: function (s) {
    956             return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g), ""));
    957         }, type: "numeric"
    958     });
    959 
    960     ts.addParser({
    961         id: "usLongDate",
    962         is: function (s) {
    963             return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));
    964         }, format: function (s) {
    965             return $.tablesorter.formatFloat(new Date(s).getTime());
    966         }, type: "numeric"
    967     });
    968 
    969     ts.addParser({
    970         id: "shortDate",
    971         is: function (s) {
    972             return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);
    973         }, format: function (s, table) {
    974             var c = table.config;
    975             s = s.replace(/\-/g, "/");
    976             if (c.dateFormat == "us") {
    977                 // reformat the string in ISO format
    978                 s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2");
    979             } else if (c.dateFormat == "uk") {
    980                 // reformat the string in ISO format
    981                 s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
    982             } else if (c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") {
    983                 s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3");
    984             }
    985             return $.tablesorter.formatFloat(new Date(s).getTime());
    986         }, type: "numeric"
    987     });
    988     ts.addParser({
    989         id: "time",
    990         is: function (s) {
    991             return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
    992         }, format: function (s) {
    993             return $.tablesorter.formatFloat(new Date("2000/01/01 " + s).getTime());
    994         }, type: "numeric"
    995     });
    996     ts.addParser({
    997         id: "metadata",
    998         is: function (s) {
    999             return false;
    1000         }, format: function (s, table, cell) {
    1001             var c = table.config,
    1002                 p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
    1003             return $(cell).metadata()[p];
    1004         }, type: "numeric"
    1005     });
    1006     // add default widgets
    1007     ts.addWidget({
    1008         id: "zebra",
    1009         format: function (table) {
    1010             if (table.config.debug) {
    1011                 var time = new Date();
    1012             }
    1013             var $tr, row = -1,
    1014                 odd;
    1015             // loop through the visible rows
    1016             $("tr:visible", table.tBodies[0]).each(function (i) {
    1017                 $tr = $(this);
    1018                 // style children rows the same way the parent
    1019                 // row was styled
    1020                 if (!$tr.hasClass(table.config.cssChildRow)) row++;
    1021                 odd = (row % 2 == 0);
    1022                 $tr.removeClass(
    1023                 table.config.widgetZebra.css[odd ? 0 : 1]).addClass(
    1024                 table.config.widgetZebra.css[odd ? 1 : 0])
    1025             });
    1026             if (table.config.debug) {
    1027                 $.tablesorter.benchmark("Applying Zebra widget", time);
    1028             }
    1029         }
    1030     });
     1/**!
     2* TableSorter 2.14.5 - Client-side table sorting with ease!
     3* @requires jQuery v1.2.6+
     4*
     5* Copyright (c) 2007 Christian Bach
     6* Examples and docs at: http://tablesorter.com
     7* Dual licensed under the MIT and GPL licenses:
     8* http://www.opensource.org/licenses/mit-license.php
     9* http://www.gnu.org/licenses/gpl.html
     10*
     11* @type jQuery
     12* @name tablesorter
     13* @cat Plugins/Tablesorter
     14* @author Christian Bach/christian.bach@polyester.se
     15* @contributor Rob Garrison/https://github.com/Mottie/tablesorter
     16*/
     17/*jshint browser:true, jquery:true, unused:false, expr: true */
     18/*global console:false, alert:false */
     19!(function($) {
     20        "use strict";
     21        $.extend({
     22                /*jshint supernew:true */
     23                tablesorter: new function() {
     24
     25                        var ts = this;
     26
     27                        ts.version = "2.14.5";
     28
     29                        ts.parsers = [];
     30                        ts.widgets = [];
     31                        ts.defaults = {
     32
     33                                // *** appearance
     34                                theme            : 'default',  // adds tablesorter-{theme} to the table for styling
     35                                widthFixed       : false,      // adds colgroup to fix widths of columns
     36                                showProcessing   : false,      // show an indeterminate timer icon in the header when the table is sorted or filtered.
     37
     38                                headerTemplate   : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = <i/> (class from cssIcon)
     39                                onRenderTemplate : null,       // function(index, template){ return template; }, (template is a string)
     40                                onRenderHeader   : null,       // function(index){}, (nothing to return)
     41
     42                                // *** functionality
     43                                cancelSelection  : true,       // prevent text selection in the header
     44                                tabIndex         : true,       // add tabindex to header for keyboard accessibility
     45                                dateFormat       : 'mmddyyyy', // other options: "ddmmyyy" or "yyyymmdd"
     46                                sortMultiSortKey : 'shiftKey', // key used to select additional columns
     47                                sortResetKey     : 'ctrlKey',  // key used to remove sorting on a column
     48                                usNumberFormat   : true,       // false for German "1.234.567,89" or French "1 234 567,89"
     49                                delayInit        : false,      // if false, the parsed table contents will not update until the first sort
     50                                serverSideSorting: false,      // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used.
     51
     52                                // *** sort options
     53                                headers          : {},         // set sorter, string, empty, locked order, sortInitialOrder, filter, etc.
     54                                ignoreCase       : true,       // ignore case while sorting
     55                                sortForce        : null,       // column(s) first sorted; always applied
     56                                sortList         : [],         // Initial sort order; applied initially; updated when manually sorted
     57                                sortAppend       : null,       // column(s) sorted last; always applied
     58                                sortStable       : false,      // when sorting two rows with exactly the same content, the original sort order is maintained
     59
     60                                sortInitialOrder : 'asc',      // sort direction on first click
     61                                sortLocaleCompare: false,      // replace equivalent character (accented characters)
     62                                sortReset        : false,      // third click on the header will reset column to default - unsorted
     63                                sortRestart      : false,      // restart sort to "sortInitialOrder" when clicking on previously unsorted columns
     64
     65                                emptyTo          : 'bottom',   // sort empty cell to bottom, top, none, zero
     66                                stringTo         : 'max',      // sort strings in numerical column as max, min, top, bottom, zero
     67                                textExtraction   : 'simple',   // text extraction method/function - function(node, table, cellIndex){}
     68                                textSorter       : null,       // choose overall or specific column sorter function(a, b, direction, table, columnIndex) [alt: ts.sortText]
     69                                numberSorter     : null,       // choose overall numeric sorter function(a, b, direction, maxColumnValue)
     70
     71                                // *** widget options
     72                                widgets: [],                   // method to add widgets, e.g. widgets: ['zebra']
     73                                widgetOptions    : {
     74                                        zebra : [ 'even', 'odd' ]    // zebra widget alternating row class names
     75                                },
     76                                initWidgets      : true,       // apply widgets on tablesorter initialization
     77
     78                                // *** callbacks
     79                                initialized      : null,       // function(table){},
     80
     81                                // *** extra css class names
     82                                tableClass       : '',
     83                                cssAsc           : '',
     84                                cssDesc          : '',
     85                                cssHeader        : '',
     86                                cssHeaderRow     : '',
     87                                cssProcessing    : '', // processing icon applied to header during sort/filter
     88
     89                                cssChildRow      : 'tablesorter-childRow', // class name indiciating that a row is to be attached to the its parent
     90                                cssIcon          : 'tablesorter-icon',     //  if this class exists, a <i> will be added to the header automatically
     91                                cssInfoBlock     : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!)
     92
     93                                // *** selectors
     94                                selectorHeaders  : '> thead th, > thead td',
     95                                selectorSort     : 'th, td',   // jQuery selector of content within selectorHeaders that is clickable to trigger a sort
     96                                selectorRemove   : '.remove-me',
     97
     98                                // *** advanced
     99                                debug            : false,
     100
     101                                // *** Internal variables
     102                                headerList: [],
     103                                empties: {},
     104                                strings: {},
     105                                parsers: []
     106
     107                                // deprecated; but retained for backwards compatibility
     108                                // widgetZebra: { css: ["even", "odd"] }
     109
     110                        };
     111
     112                        // internal css classes - these will ALWAYS be added to
     113                        // the table and MUST only contain one class name - fixes #381
     114                        ts.css = {
     115                                table      : 'tablesorter',
     116                                childRow   : 'tablesorter-childRow',
     117                                header     : 'tablesorter-header',
     118                                headerRow  : 'tablesorter-headerRow',
     119                                icon       : 'tablesorter-icon',
     120                                info       : 'tablesorter-infoOnly',
     121                                processing : 'tablesorter-processing',
     122                                sortAsc    : 'tablesorter-headerAsc',
     123                                sortDesc   : 'tablesorter-headerDesc'
     124                        };
     125
     126                        /* debuging utils */
     127                        function log() {
     128                                var s = arguments.length > 1 ? Array.prototype.slice.call(arguments) : arguments[0];
     129                                if (typeof console !== "undefined" && typeof console.log !== "undefined") {
     130                                        console.log(s);
     131                                } else {
     132                                        alert(s);
     133                                }
     134                        }
     135
     136                        function benchmark(s, d) {
     137                                log(s + " (" + (new Date().getTime() - d.getTime()) + "ms)");
     138                        }
     139
     140                        ts.log = log;
     141                        ts.benchmark = benchmark;
     142
     143                        // $.isEmptyObject from jQuery v1.4
     144                        function isEmptyObject(obj) {
     145                                /*jshint forin: false */
     146                                for (var name in obj) {
     147                                        return false;
     148                                }
     149                                return true;
     150                        }
     151
     152                        function getElementText(table, node, cellIndex) {
     153                                if (!node) { return ""; }
     154                                var c = table.config,
     155                                        t = c.textExtraction, text = "";
     156                                if (t === "simple") {
     157                                        if (c.supportsTextContent) {
     158                                                text = node.textContent; // newer browsers support this
     159                                        } else {
     160                                                text = $(node).text();
     161                                        }
     162                                } else {
     163                                        if (typeof t === "function") {
     164                                                text = t(node, table, cellIndex);
     165                                        } else if (typeof t === "object" && t.hasOwnProperty(cellIndex)) {
     166                                                text = t[cellIndex](node, table, cellIndex);
     167                                        } else {
     168                                                text = c.supportsTextContent ? node.textContent : $(node).text();
     169                                        }
     170                                }
     171                                return $.trim(text);
     172                        }
     173
     174                        function detectParserForColumn(table, rows, rowIndex, cellIndex) {
     175                                var cur,
     176                                i = ts.parsers.length,
     177                                node = false,
     178                                nodeValue = '',
     179                                keepLooking = true;
     180                                while (nodeValue === '' && keepLooking) {
     181                                        rowIndex++;
     182                                        if (rows[rowIndex]) {
     183                                                node = rows[rowIndex].cells[cellIndex];
     184                                                nodeValue = getElementText(table, node, cellIndex);
     185                                                if (table.config.debug) {
     186                                                        log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': "' + nodeValue + '"');
     187                                                }
     188                                        } else {
     189                                                keepLooking = false;
     190                                        }
     191                                }
     192                                while (--i >= 0) {
     193                                        cur = ts.parsers[i];
     194                                        // ignore the default text parser because it will always be true
     195                                        if (cur && cur.id !== 'text' && cur.is && cur.is(nodeValue, table, node)) {
     196                                                return cur;
     197                                        }
     198                                }
     199                                // nothing found, return the generic parser (text)
     200                                return ts.getParserById('text');
     201                        }
     202
     203                        function buildParserCache(table) {
     204                                var c = table.config,
     205                                        // update table bodies in case we start with an empty table
     206                                        tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'),
     207                                        rows, list, l, i, h, ch, p, time, parsersDebug = "";
     208                                if ( tb.length === 0) {
     209                                        return c.debug ? log('*Empty table!* Not building a parser cache') : '';
     210                                } else if (c.debug) {
     211                                        time = new Date();
     212                                        log('Detecting parsers for each column');
     213                                }
     214                                rows = tb[0].rows;
     215                                if (rows[0]) {
     216                                        list = [];
     217                                        l = rows[0].cells.length;
     218                                        for (i = 0; i < l; i++) {
     219                                                // tons of thanks to AnthonyM1229 for working out the following selector (issue #74) to make this work in IE8!
     220                                                // More fixes to this selector to work properly in iOS and jQuery 1.8+ (issue #132 & #174)
     221                                                h = c.$headers.filter(':not([colspan])');
     222                                                h = h.add( c.$headers.filter('[colspan="1"]') ) // ie8 fix
     223                                                        .filter('[data-column="' + i + '"]:last');
     224                                                ch = c.headers[i];
     225                                                // get column parser
     226                                                p = ts.getParserById( ts.getData(h, ch, 'sorter') );
     227                                                // empty cells behaviour - keeping emptyToBottom for backwards compatibility
     228                                                c.empties[i] = ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' );
     229                                                // text strings behaviour in numerical sorts
     230                                                c.strings[i] = ts.getData(h, ch, 'string') || c.stringTo || 'max';
     231                                                if (!p) {
     232                                                        p = detectParserForColumn(table, rows, -1, i);
     233                                                }
     234                                                if (c.debug) {
     235                                                        parsersDebug += "column:" + i + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n";
     236                                                }
     237                                                list.push(p);
     238                                        }
     239                                }
     240                                if (c.debug) {
     241                                        log(parsersDebug);
     242                                        benchmark("Completed detecting parsers", time);
     243                                }
     244                                c.parsers = list;
     245                        }
     246
     247                        /* utils */
     248                        function buildCache(table) {
     249                                var b = table.tBodies,
     250                                tc = table.config,
     251                                totalRows,
     252                                totalCells,
     253                                parsers = tc.parsers,
     254                                t, v, i, j, k, c, cols, cacheTime, colMax = [];
     255                                tc.cache = {};
     256                                // if no parsers found, return - it's an empty table.
     257                                if (!parsers) {
     258                                        return tc.debug ? log('*Empty table!* Not building a cache') : '';
     259                                }
     260                                if (tc.debug) {
     261                                        cacheTime = new Date();
     262                                }
     263                                // processing icon
     264                                if (tc.showProcessing) {
     265                                        ts.isProcessing(table, true);
     266                                }
     267                                for (k = 0; k < b.length; k++) {
     268                                        tc.cache[k] = { row: [], normalized: [] };
     269                                        // ignore tbodies with class name from c.cssInfoBlock
     270                                        if (!$(b[k]).hasClass(tc.cssInfoBlock)) {
     271                                                totalRows = (b[k] && b[k].rows.length) || 0;
     272                                                totalCells = (b[k].rows[0] && b[k].rows[0].cells.length) || 0;
     273                                                for (i = 0; i < totalRows; ++i) {
     274                                                        /** Add the table data to main data array */
     275                                                        c = $(b[k].rows[i]);
     276                                                        cols = [];
     277                                                        // if this is a child row, add it to the last row's children and continue to the next row
     278                                                        if (c.hasClass(tc.cssChildRow)) {
     279                                                                tc.cache[k].row[tc.cache[k].row.length - 1] = tc.cache[k].row[tc.cache[k].row.length - 1].add(c);
     280                                                                // go to the next for loop
     281                                                                continue;
     282                                                        }
     283                                                        tc.cache[k].row.push(c);
     284                                                        for (j = 0; j < totalCells; ++j) {
     285                                                                t = getElementText(table, c[0].cells[j], j);
     286                                                                // allow parsing if the string is empty, previously parsing would change it to zero,
     287                                                                // in case the parser needs to extract data from the table cell attributes
     288                                                                v = parsers[j].format(t, table, c[0].cells[j], j);
     289                                                                cols.push(v);
     290                                                                if ((parsers[j].type || '').toLowerCase() === "numeric") {
     291                                                                        colMax[j] = Math.max(Math.abs(v) || 0, colMax[j] || 0); // determine column max value (ignore sign)
     292                                                                }
     293                                                        }
     294                                                        cols.push(tc.cache[k].normalized.length); // add position for rowCache
     295                                                        tc.cache[k].normalized.push(cols);
     296                                                }
     297                                                tc.cache[k].colMax = colMax;
     298                                        }
     299                                }
     300                                if (tc.showProcessing) {
     301                                        ts.isProcessing(table); // remove processing icon
     302                                }
     303                                if (tc.debug) {
     304                                        benchmark("Building cache for " + totalRows + " rows", cacheTime);
     305                                }
     306                        }
     307
     308                        // init flag (true) used by pager plugin to prevent widget application
     309                        function appendToTable(table, init) {
     310                                var c = table.config,
     311                                        wo = c.widgetOptions,
     312                                        b = table.tBodies,
     313                                        rows = [],
     314                                        c2 = c.cache,
     315                                        r, n, totalRows, checkCell, $bk, $tb,
     316                                        i, j, k, l, pos, appendTime;
     317                                if (isEmptyObject(c2)) { return; } // empty table - fixes #206/#346
     318                                if (c.debug) {
     319                                        appendTime = new Date();
     320                                }
     321                                for (k = 0; k < b.length; k++) {
     322                                        $bk = $(b[k]);
     323                                        if ($bk.length && !$bk.hasClass(c.cssInfoBlock)) {
     324                                                // get tbody
     325                                                $tb = ts.processTbody(table, $bk, true);
     326                                                r = c2[k].row;
     327                                                n = c2[k].normalized;
     328                                                totalRows = n.length;
     329                                                checkCell = totalRows ? (n[0].length - 1) : 0;
     330                                                for (i = 0; i < totalRows; i++) {
     331                                                        pos = n[i][checkCell];
     332                                                        rows.push(r[pos]);
     333                                                        // removeRows used by the pager plugin; don't render if using ajax - fixes #411
     334                                                        if (!c.appender || (c.pager && (!c.pager.removeRows || !wo.pager_removeRows) && !c.pager.ajax)) {
     335                                                                l = r[pos].length;
     336                                                                for (j = 0; j < l; j++) {
     337                                                                        $tb.append(r[pos][j]);
     338                                                                }
     339                                                        }
     340                                                }
     341                                                // restore tbody
     342                                                ts.processTbody(table, $tb, false);
     343                                        }
     344                                }
     345                                if (c.appender) {
     346                                        c.appender(table, rows);
     347                                }
     348                                if (c.debug) {
     349                                        benchmark("Rebuilt table", appendTime);
     350                                }
     351                                // apply table widgets; but not before ajax completes
     352                                if (!init && !c.appender) { ts.applyWidget(table); }
     353                                // trigger sortend
     354                                $(table).trigger("sortEnd", table);
     355                                $(table).trigger("updateComplete", table);
     356                        }
     357
     358                        // computeTableHeaderCellIndexes from:
     359                        // http://www.javascripttoolbox.com/lib/table/examples.php
     360                        // http://www.javascripttoolbox.com/temp/table_cellindex.html
     361                        function computeThIndexes(t) {
     362                                var matrix = [],
     363                                lookup = {},
     364                                cols = 0, // determine the number of columns
     365                                trs = $(t).find('thead:eq(0), tfoot').children('tr'), // children tr in tfoot - see issue #196
     366                                i, j, k, l, c, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow;
     367                                for (i = 0; i < trs.length; i++) {
     368                                        cells = trs[i].cells;
     369                                        for (j = 0; j < cells.length; j++) {
     370                                                c = cells[j];
     371                                                rowIndex = c.parentNode.rowIndex;
     372                                                cellId = rowIndex + "-" + c.cellIndex;
     373                                                rowSpan = c.rowSpan || 1;
     374                                                colSpan = c.colSpan || 1;
     375                                                if (typeof(matrix[rowIndex]) === "undefined") {
     376                                                        matrix[rowIndex] = [];
     377                                                }
     378                                                // Find first available column in the first row
     379                                                for (k = 0; k < matrix[rowIndex].length + 1; k++) {
     380                                                        if (typeof(matrix[rowIndex][k]) === "undefined") {
     381                                                                firstAvailCol = k;
     382                                                                break;
     383                                                        }
     384                                                }
     385                                                lookup[cellId] = firstAvailCol;
     386                                                cols = Math.max(firstAvailCol, cols);
     387                                                // add data-column
     388                                                $(c).attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex
     389                                                for (k = rowIndex; k < rowIndex + rowSpan; k++) {
     390                                                        if (typeof(matrix[k]) === "undefined") {
     391                                                                matrix[k] = [];
     392                                                        }
     393                                                        matrixrow = matrix[k];
     394                                                        for (l = firstAvailCol; l < firstAvailCol + colSpan; l++) {
     395                                                                matrixrow[l] = "x";
     396                                                        }
     397                                                }
     398                                        }
     399                                }
     400                                // may not be accurate if # header columns !== # tbody columns
     401                                t.config.columns = cols + 1; // add one because it's a zero-based index
     402                                return lookup;
     403                        }
     404
     405                        function formatSortingOrder(v) {
     406                                // look for "d" in "desc" order; return true
     407                                return (/^d/i.test(v) || v === 1);
     408                        }
     409
     410                        function buildHeaders(table) {
     411                                var header_index = computeThIndexes(table), ch, $t,
     412                                        h, i, t, lock, time, c = table.config;
     413                                c.headerList = [];
     414                                c.headerContent = [];
     415                                if (c.debug) {
     416                                        time = new Date();
     417                                }
     418                                // add icon if cssIcon option exists
     419                                i = c.cssIcon ? '<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' : '';
     420                                c.$headers = $(table).find(c.selectorHeaders).each(function(index) {
     421                                        $t = $(this);
     422                                        ch = c.headers[index];
     423                                        c.headerContent[index] = $(this).html(); // save original header content
     424                                        // set up header template
     425                                        t = c.headerTemplate.replace(/\{content\}/g, $(this).html()).replace(/\{icon\}/g, i);
     426                                        if (c.onRenderTemplate) {
     427                                                h = c.onRenderTemplate.apply($t, [index, t]);
     428                                                if (h && typeof h === 'string') { t = h; } // only change t if something is returned
     429                                        }
     430                                        $(this).html('<div class="tablesorter-header-inner">' + t + '</div>'); // faster than wrapInner
     431
     432                                        if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index]); }
     433
     434                                        this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex];
     435                                        this.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2];
     436                                        this.count = -1; // set to -1 because clicking on the header automatically adds one
     437                                        this.lockedOrder = false;
     438                                        lock = ts.getData($t, ch, 'lockedOrder') || false;
     439                                        if (typeof lock !== 'undefined' && lock !== false) {
     440                                                this.order = this.lockedOrder = formatSortingOrder(lock) ? [1,1,1] : [0,0,0];
     441                                        }
     442                                        $t.addClass(ts.css.header + ' ' + c.cssHeader);
     443                                        // add cell to headerList
     444                                        c.headerList[index] = this;
     445                                        // add to parent in case there are multiple rows
     446                                        $t.parent().addClass(ts.css.headerRow + ' ' + c.cssHeaderRow);
     447                                        // allow keyboard cursor to focus on element
     448                                        if (c.tabIndex) { $t.attr("tabindex", 0); }
     449                                });
     450                                // enable/disable sorting
     451                                updateHeader(table);
     452                                if (c.debug) {
     453                                        benchmark("Built headers:", time);
     454                                        log(c.$headers);
     455                                }
     456                        }
     457
     458                        function commonUpdate(table, resort, callback) {
     459                                var c = table.config;
     460                                // remove rows/elements before update
     461                                c.$table.find(c.selectorRemove).remove();
     462                                // rebuild parsers
     463                                buildParserCache(table);
     464                                // rebuild the cache map
     465                                buildCache(table);
     466                                checkResort(c.$table, resort, callback);
     467                        }
     468
     469                        function updateHeader(table) {
     470                                var s, c = table.config;
     471                                c.$headers.each(function(index, th){
     472                                        s = ts.getData( th, c.headers[index], 'sorter' ) === 'false';
     473                                        th.sortDisabled = s;
     474                                        $(th)[ s ? 'addClass' : 'removeClass' ]('sorter-false');
     475                                });
     476                        }
     477
     478                        function setHeadersCss(table) {
     479                                var f, i, j, l,
     480                                        c = table.config,
     481                                        list = c.sortList,
     482                                        css = [ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc],
     483                                        // find the footer
     484                                        $t = $(table).find('tfoot tr').children().removeClass(css.join(' '));
     485                                // remove all header information
     486                                c.$headers.removeClass(css.join(' '));
     487                                l = list.length;
     488                                for (i = 0; i < l; i++) {
     489                                        // direction = 2 means reset!
     490                                        if (list[i][1] !== 2) {
     491                                                // multicolumn sorting updating - choose the :last in case there are nested columns
     492                                                f = c.$headers.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (l === 1 ? ':last' : '') );
     493                                                if (f.length) {
     494                                                        for (j = 0; j < f.length; j++) {
     495                                                                if (!f[j].sortDisabled) {
     496                                                                        f.eq(j).addClass(css[list[i][1]]);
     497                                                                        // add sorted class to footer, if it exists
     498                                                                        if ($t.length) {
     499                                                                                $t.filter('[data-column="' + list[i][0] + '"]').eq(j).addClass(css[list[i][1]]);
     500                                                                        }
     501                                                                }
     502                                                        }
     503                                                }
     504                                        }
     505                                }
     506                        }
     507
     508                        // automatically add col group, and column sizes if set
     509                        function fixColumnWidth(table) {
     510                                if (table.config.widthFixed && $(table).find('colgroup').length === 0) {
     511                                        var colgroup = $('<colgroup>'),
     512                                                overallWidth = $(table).width();
     513                                        // only add col for visible columns - fixes #371
     514                                        $(table.tBodies[0]).find("tr:first").children("td:visible").each(function() {
     515                                                colgroup.append($('<col>').css('width', parseInt(($(this).width()/overallWidth)*1000, 10)/10 + '%'));
     516                                        });
     517                                        $(table).prepend(colgroup);
     518                                }
     519                        }
     520
     521                        function updateHeaderSortCount(table, list) {
     522                                var s, t, o, c = table.config,
     523                                        sl = list || c.sortList;
     524                                c.sortList = [];
     525                                $.each(sl, function(i,v){
     526                                        // ensure all sortList values are numeric - fixes #127
     527                                        s = [ parseInt(v[0], 10), parseInt(v[1], 10) ];
     528                                        // make sure header exists
     529                                        o = c.$headers[s[0]];
     530                                        if (o) { // prevents error if sorton array is wrong
     531                                                c.sortList.push(s);
     532                                                t = $.inArray(s[1], o.order); // fixes issue #167
     533                                                o.count = t >= 0 ? t : s[1] % (c.sortReset ? 3 : 2);
     534                                        }
     535                                });
     536                        }
     537
     538                        function getCachedSortType(parsers, i) {
     539                                return (parsers && parsers[i]) ? parsers[i].type || '' : '';
     540                        }
     541
     542                        function initSort(table, cell, e){
     543                                var a, i, j, o, s,
     544                                        c = table.config,
     545                                        k = !e[c.sortMultiSortKey],
     546                                        $this = $(table);
     547                                // Only call sortStart if sorting is enabled
     548                                $this.trigger("sortStart", table);
     549                                // get current column sort order
     550                                cell.count = e[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2);
     551                                // reset all sorts on non-current column - issue #30
     552                                if (c.sortRestart) {
     553                                        i = cell;
     554                                        c.$headers.each(function() {
     555                                                // only reset counts on columns that weren't just clicked on and if not included in a multisort
     556                                                if (this !== i && (k || !$(this).is('.' + ts.css.sortDesc + ',.' + ts.css.sortAsc))) {
     557                                                        this.count = -1;
     558                                                }
     559                                        });
     560                                }
     561                                // get current column index
     562                                i = cell.column;
     563                                // user only wants to sort on one column
     564                                if (k) {
     565                                        // flush the sort list
     566                                        c.sortList = [];
     567                                        if (c.sortForce !== null) {
     568                                                a = c.sortForce;
     569                                                for (j = 0; j < a.length; j++) {
     570                                                        if (a[j][0] !== i) {
     571                                                                c.sortList.push(a[j]);
     572                                                        }
     573                                                }
     574                                        }
     575                                        // add column to sort list
     576                                        o = cell.order[cell.count];
     577                                        if (o < 2) {
     578                                                c.sortList.push([i, o]);
     579                                                // add other columns if header spans across multiple
     580                                                if (cell.colSpan > 1) {
     581                                                        for (j = 1; j < cell.colSpan; j++) {
     582                                                                c.sortList.push([i + j, o]);
     583                                                        }
     584                                                }
     585                                        }
     586                                        // multi column sorting
     587                                } else {
     588                                        // get rid of the sortAppend before adding more - fixes issue #115
     589                                        if (c.sortAppend && c.sortList.length > 1) {
     590                                                if (ts.isValueInArray(c.sortAppend[0][0], c.sortList)) {
     591                                                        c.sortList.pop();
     592                                                }
     593                                        }
     594                                        // the user has clicked on an already sorted column
     595                                        if (ts.isValueInArray(i, c.sortList)) {
     596                                                // reverse the sorting direction
     597                                                for (j = 0; j < c.sortList.length; j++) {
     598                                                        s = c.sortList[j];
     599                                                        o = c.$headers[s[0]];
     600                                                        if (s[0] === i) {
     601                                                                // o.count seems to be incorrect when compared to cell.count
     602                                                                s[1] = o.order[cell.count];
     603                                                                if (s[1] === 2) {
     604                                                                        c.sortList.splice(j,1);
     605                                                                        o.count = -1;
     606                                                                }
     607                                                        }
     608                                                }
     609                                        } else {
     610                                                // add column to sort list array
     611                                                o = cell.order[cell.count];
     612                                                if (o < 2) {
     613                                                        c.sortList.push([i, o]);
     614                                                        // add other columns if header spans across multiple
     615                                                        if (cell.colSpan > 1) {
     616                                                                for (j = 1; j < cell.colSpan; j++) {
     617                                                                        c.sortList.push([i + j, o]);
     618                                                                }
     619                                                        }
     620                                                }
     621                                        }
     622                                }
     623                                if (c.sortAppend !== null) {
     624                                        a = c.sortAppend;
     625                                        for (j = 0; j < a.length; j++) {
     626                                                if (a[j][0] !== i) {
     627                                                        c.sortList.push(a[j]);
     628                                                }
     629                                        }
     630                                }
     631                                // sortBegin event triggered immediately before the sort
     632                                $this.trigger("sortBegin", table);
     633                                // setTimeout needed so the processing icon shows up
     634                                setTimeout(function(){
     635                                        // set css for headers
     636                                        setHeadersCss(table);
     637                                        multisort(table);
     638                                        appendToTable(table);
     639                                }, 1);
     640                        }
     641
     642                        // sort multiple columns
     643                        function multisort(table) { /*jshint loopfunc:true */
     644                                var i, k, num, col, colMax, cache, lc,
     645                                        order, orgOrderCol, sortTime, sort, x, y,
     646                                        dir = 0,
     647                                        c = table.config,
     648                                        cts = c.textSorter || '',
     649                                        sortList = c.sortList,
     650                                        l = sortList.length,
     651                                        bl = table.tBodies.length;
     652                                if (c.serverSideSorting || isEmptyObject(c.cache)) { // empty table - fixes #206/#346
     653                                        return;
     654                                }
     655                                if (c.debug) { sortTime = new Date(); }
     656                                for (k = 0; k < bl; k++) {
     657                                        colMax = c.cache[k].colMax;
     658                                        cache = c.cache[k].normalized;
     659                                        lc = cache.length;
     660                                        orgOrderCol = (cache && cache[0]) ? cache[0].length - 1 : 0;
     661                                        cache.sort(function(a, b) {
     662                                                // cache is undefined here in IE, so don't use it!
     663                                                for (i = 0; i < l; i++) {
     664                                                        col = sortList[i][0];
     665                                                        order = sortList[i][1];
     666                                                        // sort direction, true = asc, false = desc
     667                                                        dir = order === 0;
     668
     669                                                        if (c.sortStable && a[col] === b[col] && l === 1) {
     670                                                                return a[orgOrderCol] - b[orgOrderCol];
     671                                                        }
     672
     673                                                        // fallback to natural sort since it is more robust
     674                                                        num = /n/i.test(getCachedSortType(c.parsers, col));
     675                                                        if (num && c.strings[col]) {
     676                                                                // sort strings in numerical columns
     677                                                                if (typeof (c.string[c.strings[col]]) === 'boolean') {
     678                                                                        num = (dir ? 1 : -1) * (c.string[c.strings[col]] ? -1 : 1);
     679                                                                } else {
     680                                                                        num = (c.strings[col]) ? c.string[c.strings[col]] || 0 : 0;
     681                                                                }
     682                                                                // fall back to built-in numeric sort
     683                                                                // var sort = $.tablesorter["sort" + s](table, a[c], b[c], c, colMax[c], dir);
     684                                                                sort = c.numberSorter ? c.numberSorter(x[col], y[col], dir, colMax[col], table) :
     685                                                                        ts[ 'sortNumeric' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], num, colMax[col], col, table);
     686                                                        } else {
     687                                                                // set a & b depending on sort direction
     688                                                                x = dir ? a : b;
     689                                                                y = dir ? b : a;
     690                                                                // text sort function
     691                                                                if (typeof(cts) === 'function') {
     692                                                                        // custom OVERALL text sorter
     693                                                                        sort = cts(x[col], y[col], dir, col, table);
     694                                                                } else if (typeof(cts) === 'object' && cts.hasOwnProperty(col)) {
     695                                                                        // custom text sorter for a SPECIFIC COLUMN
     696                                                                        sort = cts[col](x[col], y[col], dir, col, table);
     697                                                                } else {
     698                                                                        // fall back to natural sort
     699                                                                        sort = ts[ 'sortNatural' + (dir ? 'Asc' : 'Desc') ](a[col], b[col], col, table, c);
     700                                                                }
     701                                                        }
     702                                                        if (sort) { return sort; }
     703                                                }
     704                                                return a[orgOrderCol] - b[orgOrderCol];
     705                                        });
     706                                }
     707                                if (c.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time", sortTime); }
     708                        }
     709
     710                        function resortComplete($table, callback){
     711                                var c = $table[0].config;
     712                                if (c.pager && !c.pager.ajax) {
     713                                        $table.trigger('updateComplete');
     714                                }
     715                                if (typeof callback === "function") {
     716                                        callback($table[0]);
     717                                }
     718                        }
     719
     720                        function checkResort($table, flag, callback) {
     721                                // don't try to resort if the table is still processing
     722                                // this will catch spamming of the updateCell method
     723                                if (flag !== false && !$table[0].isProcessing) {
     724                                        $table.trigger("sorton", [$table[0].config.sortList, function(){
     725                                                resortComplete($table, callback);
     726                                        }]);
     727                                } else {
     728                                        resortComplete($table, callback);
     729                                }
     730                        }
     731
     732                        function bindEvents(table){
     733                                var c = table.config,
     734                                        $this = c.$table,
     735                                        j, downTime;
     736                                // apply event handling to headers
     737                                c.$headers
     738                                // http://stackoverflow.com/questions/5312849/jquery-find-self;
     739                                .find(c.selectorSort).add( c.$headers.filter(c.selectorSort) )
     740                                .unbind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keypress.tablesorter')
     741                                .bind('mousedown.tablesorter mouseup.tablesorter sort.tablesorter keypress.tablesorter', function(e, external) {
     742                                        // only recognize left clicks or enter
     743                                        if ( ((e.which || e.button) !== 1 && !/sort|keypress/.test(e.type)) || (e.type === 'keypress' && e.which !== 13) ) {
     744                                                return;
     745                                        }
     746                                        // ignore long clicks (prevents resizable widget from initializing a sort)
     747                                        if (e.type === 'mouseup' && external !== true && (new Date().getTime() - downTime > 250)) { return; }
     748                                        // set timer on mousedown
     749                                        if (e.type === 'mousedown') {
     750                                                downTime = new Date().getTime();
     751                                                return e.target.tagName === "INPUT" ? '' : !c.cancelSelection;
     752                                        }
     753                                        if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); }
     754                                        // jQuery v1.2.6 doesn't have closest()
     755                                        var $cell = /TH|TD/.test(this.tagName) ? $(this) : $(this).parents('th, td').filter(':first'), cell = $cell[0];
     756                                        if (!cell.sortDisabled) {
     757                                                initSort(table, cell, e);
     758                                        }
     759                                });
     760                                if (c.cancelSelection) {
     761                                        // cancel selection
     762                                        c.$headers
     763                                                .attr('unselectable', 'on')
     764                                                .bind('selectstart', false)
     765                                                .css({
     766                                                        'user-select': 'none',
     767                                                        'MozUserSelect': 'none' // not needed for jQuery 1.8+
     768                                                });
     769                                }
     770                                // apply easy methods that trigger bound events
     771                                $this
     772                                .unbind('sortReset update updateRows updateCell updateAll addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter '))
     773                                .bind("sortReset.tablesorter", function(e){
     774                                        e.stopPropagation();
     775                                        c.sortList = [];
     776                                        setHeadersCss(table);
     777                                        multisort(table);
     778                                        appendToTable(table);
     779                                })
     780                                .bind("updateAll.tablesorter", function(e, resort, callback){
     781                                        e.stopPropagation();
     782                                        ts.refreshWidgets(table, true, true);
     783                                        ts.restoreHeaders(table);
     784                                        buildHeaders(table);
     785                                        bindEvents(table);
     786                                        commonUpdate(table, resort, callback);
     787                                })
     788                                .bind("update.tablesorter updateRows.tablesorter", function(e, resort, callback) {
     789                                        e.stopPropagation();
     790                                        // update sorting (if enabled/disabled)
     791                                        updateHeader(table);
     792                                        commonUpdate(table, resort, callback);
     793                                })
     794                                .bind("updateCell.tablesorter", function(e, cell, resort, callback) {
     795                                        e.stopPropagation();
     796                                        $this.find(c.selectorRemove).remove();
     797                                        // get position from the dom
     798                                        var l, row, icell,
     799                                        $tb = $this.find('tbody'),
     800                                        // update cache - format: function(s, table, cell, cellIndex)
     801                                        // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr');
     802                                        tbdy = $tb.index( $(cell).parents('tbody').filter(':first') ),
     803                                        $row = $(cell).parents('tr').filter(':first');
     804                                        cell = $(cell)[0]; // in case cell is a jQuery object
     805                                        // tbody may not exist if update is initialized while tbody is removed for processing
     806                                        if ($tb.length && tbdy >= 0) {
     807                                                row = $tb.eq(tbdy).find('tr').index( $row );
     808                                                icell = cell.cellIndex;
     809                                                l = c.cache[tbdy].normalized[row].length - 1;
     810                                                c.cache[tbdy].row[table.config.cache[tbdy].normalized[row][l]] = $row;
     811                                                c.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText(table, cell, icell), table, cell, icell );
     812                                                checkResort($this, resort, callback);
     813                                        }
     814                                })
     815                                .bind("addRows.tablesorter", function(e, $row, resort, callback) {
     816                                        e.stopPropagation();
     817                                        if (isEmptyObject(c.cache)) {
     818                                                // empty table, do an update instead - fixes #450
     819                                                updateHeader(table);
     820                                                commonUpdate(table, resort, callback);
     821                                        } else {
     822                                                var i, rows = $row.filter('tr').length,
     823                                                dat = [], l = $row[0].cells.length,
     824                                                tbdy = $this.find('tbody').index( $row.parents('tbody').filter(':first') );
     825                                                // fixes adding rows to an empty table - see issue #179
     826                                                if (!c.parsers) {
     827                                                        buildParserCache(table);
     828                                                }
     829                                                // add each row
     830                                                for (i = 0; i < rows; i++) {
     831                                                        // add each cell
     832                                                        for (j = 0; j < l; j++) {
     833                                                                dat[j] = c.parsers[j].format( getElementText(table, $row[i].cells[j], j), table, $row[i].cells[j], j );
     834                                                        }
     835                                                        // add the row index to the end
     836                                                        dat.push(c.cache[tbdy].row.length);
     837                                                        // update cache
     838                                                        c.cache[tbdy].row.push([$row[i]]);
     839                                                        c.cache[tbdy].normalized.push(dat);
     840                                                        dat = [];
     841                                                }
     842                                                // resort using current settings
     843                                                checkResort($this, resort, callback);
     844                                        }
     845                                })
     846                                .bind("sorton.tablesorter", function(e, list, callback, init) {
     847                                        var c = table.config;
     848                                        e.stopPropagation();
     849                                        $this.trigger("sortStart", this);
     850                                        // update header count index
     851                                        updateHeaderSortCount(table, list);
     852                                        // set css for headers
     853                                        setHeadersCss(table);
     854                                        // fixes #346
     855                                        if (c.delayInit && isEmptyObject(c.cache)) { buildCache(table); }
     856                                        $this.trigger("sortBegin", this);
     857                                        // sort the table and append it to the dom
     858                                        multisort(table);
     859                                        appendToTable(table, init);
     860                                        if (typeof callback === "function") {
     861                                                callback(table);
     862                                        }
     863                                })
     864                                .bind("appendCache.tablesorter", function(e, callback, init) {
     865                                        e.stopPropagation();
     866                                        appendToTable(table, init);
     867                                        if (typeof callback === "function") {
     868                                                callback(table);
     869                                        }
     870                                })
     871                                .bind("applyWidgetId.tablesorter", function(e, id) {
     872                                        e.stopPropagation();
     873                                        ts.getWidgetById(id).format(table, c, c.widgetOptions);
     874                                })
     875                                .bind("applyWidgets.tablesorter", function(e, init) {
     876                                        e.stopPropagation();
     877                                        // apply widgets
     878                                        ts.applyWidget(table, init);
     879                                })
     880                                .bind("refreshWidgets.tablesorter", function(e, all, dontapply){
     881                                        e.stopPropagation();
     882                                        ts.refreshWidgets(table, all, dontapply);
     883                                })
     884                                .bind("destroy.tablesorter", function(e, c, cb){
     885                                        e.stopPropagation();
     886                                        ts.destroy(table, c, cb);
     887                                });
     888                        }
     889
     890                        /* public methods */
     891                        ts.construct = function(settings) {
     892                                return this.each(function() {
     893                                        var table = this,
     894                                                // merge & extend config options
     895                                                c = $.extend(true, {}, ts.defaults, settings);
     896                                        // create a table from data (build table widget)
     897                                        if (!table.hasInitialized && ts.buildTable && this.tagName !== 'TABLE') {
     898                                                // return the table (in case the original target is the table's container)
     899                                                ts.buildTable(table, c);
     900                                        }
     901                                        ts.setup(table, c);
     902                                });
     903                        };
     904
     905                        ts.setup = function(table, c) {
     906                                // if no thead or tbody, or tablesorter is already present, quit
     907                                if (!table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true) {
     908                                        return c.debug ? log('stopping initialization! No table, thead, tbody or tablesorter has already been initialized') : '';
     909                                }
     910
     911                                var k = '',
     912                                        $this = $(table),
     913                                        m = $.metadata;
     914                                // initialization flag
     915                                table.hasInitialized = false;
     916                                // table is being processed flag
     917                                table.isProcessing = true;
     918                                // make sure to store the config object
     919                                table.config = c;
     920                                // save the settings where they read
     921                                $.data(table, "tablesorter", c);
     922                                if (c.debug) { $.data( table, 'startoveralltimer', new Date()); }
     923
     924                                // constants
     925                                c.supportsTextContent = $('<span>x</span>')[0].textContent === 'x';
     926                                // removing this in version 3 (only supports jQuery 1.7+)
     927                                c.supportsDataObject = (function(version) {
     928                                        version[0] = parseInt(version[0], 10);
     929                                        return (version[0] > 1) || (version[0] === 1 && parseInt(version[1], 10) >= 4);
     930                                })($.fn.jquery.split("."));
     931                                // digit sort text location; keeping max+/- for backwards compatibility
     932                                c.string = { 'max': 1, 'min': -1, 'max+': 1, 'max-': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false };
     933                                // add table theme class only if there isn't already one there
     934                                if (!/tablesorter\-/.test($this.attr('class'))) {
     935                                        k = (c.theme !== '' ? ' tablesorter-' + c.theme : '');
     936                                }
     937                                c.$table = $this.addClass(ts.css.table + ' ' + c.tableClass + k);
     938                                c.$tbodies = $this.children('tbody:not(.' + c.cssInfoBlock + ')');
     939                                c.widgetInit = {}; // keep a list of initialized widgets
     940                                // build headers
     941                                buildHeaders(table);
     942                                // fixate columns if the users supplies the fixedWidth option
     943                                // do this after theme has been applied
     944                                fixColumnWidth(table);
     945                                // try to auto detect column type, and store in tables config
     946                                buildParserCache(table);
     947                                // build the cache for the tbody cells
     948                                // delayInit will delay building the cache until the user starts a sort
     949                                if (!c.delayInit) { buildCache(table); }
     950                                // bind all header events and methods
     951                                bindEvents(table);
     952                                // get sort list from jQuery data or metadata
     953                                // in jQuery < 1.4, an error occurs when calling $this.data()
     954                                if (c.supportsDataObject && typeof $this.data().sortlist !== 'undefined') {
     955                                        c.sortList = $this.data().sortlist;
     956                                } else if (m && ($this.metadata() && $this.metadata().sortlist)) {
     957                                        c.sortList = $this.metadata().sortlist;
     958                                }
     959                                // apply widget init code
     960                                ts.applyWidget(table, true);
     961                                // if user has supplied a sort list to constructor
     962                                if (c.sortList.length > 0) {
     963                                        $this.trigger("sorton", [c.sortList, {}, !c.initWidgets]);
     964                                } else if (c.initWidgets) {
     965                                        // apply widget format
     966                                        ts.applyWidget(table);
     967                                }
     968
     969                                // show processesing icon
     970                                if (c.showProcessing) {
     971                                        $this
     972                                        .unbind('sortBegin.tablesorter sortEnd.tablesorter')
     973                                        .bind('sortBegin.tablesorter sortEnd.tablesorter', function(e) {
     974                                                ts.isProcessing(table, e.type === 'sortBegin');
     975                                        });
     976                                }
     977
     978                                // initialized
     979                                table.hasInitialized = true;
     980                                table.isProcessing = false;
     981                                if (c.debug) {
     982                                        ts.benchmark("Overall initialization time", $.data( table, 'startoveralltimer'));
     983                                }
     984                                $this.trigger('tablesorter-initialized', table);
     985                                if (typeof c.initialized === 'function') { c.initialized(table); }
     986                        };
     987
     988                        // *** Process table ***
     989                        // add processing indicator
     990                        ts.isProcessing = function(table, toggle, $ths) {
     991                                table = $(table);
     992                                var c = table[0].config,
     993                                        // default to all headers
     994                                        $h = $ths || table.find('.' + ts.css.header);
     995                                if (toggle) {
     996                                        if (c.sortList.length > 0) {
     997                                                // get headers from the sortList
     998                                                $h = $h.filter(function(){
     999                                                        // get data-column from attr to keep  compatibility with jQuery 1.2.6
     1000                                                        return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList);
     1001                                                });
     1002                                        }
     1003                                        $h.addClass(ts.css.processing + ' ' + c.cssProcessing);
     1004                                } else {
     1005                                        $h.removeClass(ts.css.processing + ' ' + c.cssProcessing);
     1006                                }
     1007                        };
     1008
     1009                        // detach tbody but save the position
     1010                        // don't use tbody because there are portions that look for a tbody index (updateCell)
     1011                        ts.processTbody = function(table, $tb, getIt){
     1012                                var holdr;
     1013                                if (getIt) {
     1014                                        table.isProcessing = true;
     1015                                        $tb.before('<span class="tablesorter-savemyplace"/>');
     1016                                        holdr = ($.fn.detach) ? $tb.detach() : $tb.remove();
     1017                                        return holdr;
     1018                                }
     1019                                holdr = $(table).find('span.tablesorter-savemyplace');
     1020                                $tb.insertAfter( holdr );
     1021                                holdr.remove();
     1022                                table.isProcessing = false;
     1023                        };
     1024
     1025                        ts.clearTableBody = function(table) {
     1026                                $(table)[0].config.$tbodies.empty();
     1027                        };
     1028
     1029                        // restore headers
     1030                        ts.restoreHeaders = function(table){
     1031                                var c = table.config;
     1032                                // don't use c.$headers here in case header cells were swapped
     1033                                c.$table.find(c.selectorHeaders).each(function(i){
     1034                                        // only restore header cells if it is wrapped
     1035                                        // because this is also used by the updateAll method
     1036                                        if ($(this).find('.tablesorter-header-inner').length){
     1037                                                $(this).html( c.headerContent[i] );
     1038                                        }
     1039                                });
     1040                        };
     1041
     1042                        ts.destroy = function(table, removeClasses, callback){
     1043                                table = $(table)[0];
     1044                                if (!table.hasInitialized) { return; }
     1045                                // remove all widgets
     1046                                ts.refreshWidgets(table, true, true);
     1047                                var $t = $(table), c = table.config,
     1048                                $h = $t.find('thead:first'),
     1049                                $r = $h.find('tr.' + ts.css.headerRow).removeClass(ts.css.headerRow + ' ' + c.cssHeaderRow),
     1050                                $f = $t.find('tfoot:first > tr').children('th, td');
     1051                                // remove widget added rows, just in case
     1052                                $h.find('tr').not($r).remove();
     1053                                // disable tablesorter
     1054                                $t
     1055                                        .removeData('tablesorter')
     1056                                        .unbind('sortReset update updateAll updateRows updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join('.tablesorter '));
     1057                                c.$headers.add($f)
     1058                                        .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc].join(' ') )
     1059                                        .removeAttr('data-column');
     1060                                $r.find(c.selectorSort).unbind('mousedown.tablesorter mouseup.tablesorter keypress.tablesorter');
     1061                                ts.restoreHeaders(table);
     1062                                if (removeClasses !== false) {
     1063                                        $t.removeClass(ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme);
     1064                                }
     1065                                // clear flag in case the plugin is initialized again
     1066                                table.hasInitialized = false;
     1067                                if (typeof callback === 'function') {
     1068                                        callback(table);
     1069                                }
     1070                        };
     1071
     1072                        // *** sort functions ***
     1073                        // regex used in natural sort
     1074                        ts.regex = {
     1075                                chunk : /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters
     1076                                hex: /^0x[0-9a-f]+$/i // hex
     1077                        };
     1078
     1079                        // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed)
     1080                        ts.sortNatural = function(a, b) {
     1081                                if (a === b) { return 0; }
     1082                                var xN, xD, yN, yD, xF, yF, i, mx,
     1083                                        r = ts.regex;
     1084                                // first try and sort Hex codes
     1085                                if (r.hex.test(b)) {
     1086                                        xD = parseInt(a.match(r.hex), 16);
     1087                                        yD = parseInt(b.match(r.hex), 16);
     1088                                        if ( xD < yD ) { return -1; }
     1089                                        if ( xD > yD ) { return 1; }
     1090                                }
     1091
     1092                                // chunk/tokenize
     1093                                xN = a.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0');
     1094                                yN = b.replace(r.chunk, '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0');
     1095                                mx = Math.max(xN.length, yN.length);
     1096                                // natural sorting through split numeric strings and default strings
     1097                                for (i = 0; i < mx; i++) {
     1098                                        // find floats not starting with '0', string or 0 if not defined
     1099                                        xF = isNaN(xN[i]) ? xN[i] || 0 : parseFloat(xN[i]) || 0;
     1100                                        yF = isNaN(yN[i]) ? yN[i] || 0 : parseFloat(yN[i]) || 0;
     1101                                        // handle numeric vs string comparison - number < string - (Kyle Adams)
     1102                                        if (isNaN(xF) !== isNaN(yF)) { return (isNaN(xF)) ? 1 : -1; }
     1103                                        // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
     1104                                        if (typeof xF !== typeof yF) {
     1105                                                xF += '';
     1106                                                yF += '';
     1107                                        }
     1108                                        if (xF < yF) { return -1; }
     1109                                        if (xF > yF) { return 1; }
     1110                                }
     1111                                return 0;
     1112                        };
     1113
     1114                        ts.sortNaturalAsc = function(a, b, col, table, c) {
     1115                                if (a === b) { return 0; }
     1116                                var e = c.string[ (c.empties[col] || c.emptyTo ) ];
     1117                                if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; }
     1118                                if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; }
     1119                                return ts.sortNatural(a, b);
     1120                        };
     1121
     1122                        ts.sortNaturalDesc = function(a, b, col, table, c) {
     1123                                if (a === b) { return 0; }
     1124                                var e = c.string[ (c.empties[col] || c.emptyTo ) ];
     1125                                if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; }
     1126                                if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; }
     1127                                return ts.sortNatural(b, a);
     1128                        };
     1129
     1130                        // basic alphabetical sort
     1131                        ts.sortText = function(a, b) {
     1132                                return a > b ? 1 : (a < b ? -1 : 0);
     1133                        };
     1134
     1135                        // return text string value by adding up ascii value
     1136                        // so the text is somewhat sorted when using a digital sort
     1137                        // this is NOT an alphanumeric sort
     1138                        ts.getTextValue = function(a, num, mx) {
     1139                                if (mx) {
     1140                                        // make sure the text value is greater than the max numerical value (mx)
     1141                                        var i, l = a ? a.length : 0, n = mx + num;
     1142                                        for (i = 0; i < l; i++) {
     1143                                                n += a.charCodeAt(i);
     1144                                        }
     1145                                        return num * n;
     1146                                }
     1147                                return 0;
     1148                        };
     1149
     1150                        ts.sortNumericAsc = function(a, b, num, mx, col, table) {
     1151                                if (a === b) { return 0; }
     1152                                var c = table.config,
     1153                                        e = c.string[ (c.empties[col] || c.emptyTo ) ];
     1154                                if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : -e || -1; }
     1155                                if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : e || 1; }
     1156                                if (isNaN(a)) { a = ts.getTextValue(a, num, mx); }
     1157                                if (isNaN(b)) { b = ts.getTextValue(b, num, mx); }
     1158                                return a - b;
     1159                        };
     1160
     1161                        ts.sortNumericDesc = function(a, b, num, mx, col, table) {
     1162                                if (a === b) { return 0; }
     1163                                var c = table.config,
     1164                                        e = c.string[ (c.empties[col] || c.emptyTo ) ];
     1165                                if (a === '' && e !== 0) { return typeof e === 'boolean' ? (e ? -1 : 1) : e || 1; }
     1166                                if (b === '' && e !== 0) { return typeof e === 'boolean' ? (e ? 1 : -1) : -e || -1; }
     1167                                if (isNaN(a)) { a = ts.getTextValue(a, num, mx); }
     1168                                if (isNaN(b)) { b = ts.getTextValue(b, num, mx); }
     1169                                return b - a;
     1170                        };
     1171
     1172                        ts.sortNumeric = function(a, b) {
     1173                                return a - b;
     1174                        };
     1175
     1176                        // used when replacing accented characters during sorting
     1177                        ts.characterEquivalents = {
     1178                                "a" : "\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5", // áàâãäąå
     1179                                "A" : "\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5", // ÁÀÂÃÄĄÅ
     1180                                "c" : "\u00e7\u0107\u010d", // çćč
     1181                                "C" : "\u00c7\u0106\u010c", // ÇĆČ
     1182                                "e" : "\u00e9\u00e8\u00ea\u00eb\u011b\u0119", // éèêëěę
     1183                                "E" : "\u00c9\u00c8\u00ca\u00cb\u011a\u0118", // ÉÈÊËĚĘ
     1184                                "i" : "\u00ed\u00ec\u0130\u00ee\u00ef\u0131", // íìİîïı
     1185                                "I" : "\u00cd\u00cc\u0130\u00ce\u00cf", // ÍÌİÎÏ
     1186                                "o" : "\u00f3\u00f2\u00f4\u00f5\u00f6", // óòôõö
     1187                                "O" : "\u00d3\u00d2\u00d4\u00d5\u00d6", // ÓÒÔÕÖ
     1188                                "ss": "\u00df", // ß (s sharp)
     1189                                "SS": "\u1e9e", // ẞ (Capital sharp s)
     1190                                "u" : "\u00fa\u00f9\u00fb\u00fc\u016f", // úùûüů
     1191                                "U" : "\u00da\u00d9\u00db\u00dc\u016e" // ÚÙÛÜŮ
     1192                        };
     1193                        ts.replaceAccents = function(s) {
     1194                                var a, acc = '[', eq = ts.characterEquivalents;
     1195                                if (!ts.characterRegex) {
     1196                                        ts.characterRegexArray = {};
     1197                                        for (a in eq) {
     1198                                                if (typeof a === 'string') {
     1199                                                        acc += eq[a];
     1200                                                        ts.characterRegexArray[a] = new RegExp('[' + eq[a] + ']', 'g');
     1201                                                }
     1202                                        }
     1203                                        ts.characterRegex = new RegExp(acc + ']');
     1204                                }
     1205                                if (ts.characterRegex.test(s)) {
     1206                                        for (a in eq) {
     1207                                                if (typeof a === 'string') {
     1208                                                        s = s.replace( ts.characterRegexArray[a], a );
     1209                                                }
     1210                                        }
     1211                                }
     1212                                return s;
     1213                        };
     1214
     1215                        // *** utilities ***
     1216                        ts.isValueInArray = function(v, a) {
     1217                                var i, l = a.length;
     1218                                for (i = 0; i < l; i++) {
     1219                                        if (a[i][0] === v) {
     1220                                                return true;
     1221                                        }
     1222                                }
     1223                                return false;
     1224                        };
     1225
     1226                        ts.addParser = function(parser) {
     1227                                var i, l = ts.parsers.length, a = true;
     1228                                for (i = 0; i < l; i++) {
     1229                                        if (ts.parsers[i].id.toLowerCase() === parser.id.toLowerCase()) {
     1230                                                a = false;
     1231                                        }
     1232                                }
     1233                                if (a) {
     1234                                        ts.parsers.push(parser);
     1235                                }
     1236                        };
     1237
     1238                        ts.getParserById = function(name) {
     1239                                var i, l = ts.parsers.length;
     1240                                for (i = 0; i < l; i++) {
     1241                                        if (ts.parsers[i].id.toLowerCase() === (name.toString()).toLowerCase()) {
     1242                                                return ts.parsers[i];
     1243                                        }
     1244                                }
     1245                                return false;
     1246                        };
     1247
     1248                        ts.addWidget = function(widget) {
     1249                                ts.widgets.push(widget);
     1250                        };
     1251
     1252                        ts.getWidgetById = function(name) {
     1253                                var i, w, l = ts.widgets.length;
     1254                                for (i = 0; i < l; i++) {
     1255                                        w = ts.widgets[i];
     1256                                        if (w && w.hasOwnProperty('id') && w.id.toLowerCase() === name.toLowerCase()) {
     1257                                                return w;
     1258                                        }
     1259                                }
     1260                        };
     1261
     1262                        ts.applyWidget = function(table, init) {
     1263                                table = $(table)[0]; // in case this is called externally
     1264                                var c = table.config,
     1265                                        wo = c.widgetOptions,
     1266                                        widgets = [],
     1267                                        time, w, wd;
     1268                                if (c.debug) { time = new Date(); }
     1269                                if (c.widgets.length) {
     1270                                        // ensure unique widget ids
     1271                                        c.widgets = $.grep(c.widgets, function(v, k){
     1272                                                return $.inArray(v, c.widgets) === k;
     1273                                        });
     1274                                        // build widget array & add priority as needed
     1275                                        $.each(c.widgets || [], function(i,n){
     1276                                                wd = ts.getWidgetById(n);
     1277                                                if (wd && wd.id) {
     1278                                                        // set priority to 10 if not defined
     1279                                                        if (!wd.priority) { wd.priority = 10; }
     1280                                                        widgets[i] = wd;
     1281                                                }
     1282                                        });
     1283                                        // sort widgets by priority
     1284                                        widgets.sort(function(a, b){
     1285                                                return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1;
     1286                                        });
     1287                                        // add/update selected widgets
     1288                                        $.each(widgets, function(i,w){
     1289                                                if (w) {
     1290                                                        if (init || !(c.widgetInit[w.id])) {
     1291                                                                if (w.hasOwnProperty('options')) {
     1292                                                                        wo = table.config.widgetOptions = $.extend( true, {}, w.options, wo );
     1293                                                                }
     1294                                                                if (w.hasOwnProperty('init')) {
     1295                                                                        w.init(table, w, c, wo);
     1296                                                                }
     1297                                                                c.widgetInit[w.id] = true;
     1298                                                        }
     1299                                                        if (!init && w.hasOwnProperty('format')) {
     1300                                                                w.format(table, c, wo, false);
     1301                                                        }
     1302                                                }
     1303                                        });
     1304                                }
     1305                                if (c.debug) {
     1306                                        w = c.widgets.length;
     1307                                        benchmark("Completed " + (init === true ? "initializing " : "applying ") + w + " widget" + (w !== 1 ? "s" : ""), time);
     1308                                }
     1309                        };
     1310
     1311                        ts.refreshWidgets = function(table, doAll, dontapply) {
     1312                                table = $(table)[0]; // see issue #243
     1313                                var i, c = table.config,
     1314                                        cw = c.widgets,
     1315                                        w = ts.widgets, l = w.length;
     1316                                // remove previous widgets
     1317                                for (i = 0; i < l; i++){
     1318                                        if ( w[i] && w[i].id && (doAll || $.inArray( w[i].id, cw ) < 0) ) {
     1319                                                if (c.debug) { log( 'Refeshing widgets: Removing ' + w[i].id  ); }
     1320                                                // only remove widgets that have been initialized - fixes #442
     1321                                                if (w[i].hasOwnProperty('remove') && c.widgetInit[w[i].id]) {
     1322                                                        w[i].remove(table, c, c.widgetOptions);
     1323                                                        c.widgetInit[w[i].id] = false;
     1324                                                }
     1325                                        }
     1326                                }
     1327                                if (dontapply !== true) {
     1328                                        ts.applyWidget(table, doAll);
     1329                                }
     1330                        };
     1331
     1332                        // get sorter, string, empty, etc options for each column from
     1333                        // jQuery data, metadata, header option or header class name ("sorter-false")
     1334                        // priority = jQuery data > meta > headers option > header class name
     1335                        ts.getData = function(h, ch, key) {
     1336                                var val = '', $h = $(h), m, cl;
     1337                                if (!$h.length) { return ''; }
     1338                                m = $.metadata ? $h.metadata() : false;
     1339                                cl = ' ' + ($h.attr('class') || '');
     1340                                if (typeof $h.data(key) !== 'undefined' || typeof $h.data(key.toLowerCase()) !== 'undefined'){
     1341                                        // "data-lockedOrder" is assigned to "lockedorder"; but "data-locked-order" is assigned to "lockedOrder"
     1342                                        // "data-sort-initial-order" is assigned to "sortInitialOrder"
     1343                                        val += $h.data(key) || $h.data(key.toLowerCase());
     1344                                } else if (m && typeof m[key] !== 'undefined') {
     1345                                        val += m[key];
     1346                                } else if (ch && typeof ch[key] !== 'undefined') {
     1347                                        val += ch[key];
     1348                                } else if (cl !== ' ' && cl.match(' ' + key + '-')) {
     1349                                        // include sorter class name "sorter-text", etc; now works with "sorter-my-custom-parser"
     1350                                        val = cl.match( new RegExp('\\s' + key + '-([\\w-]+)') )[1] || '';
     1351                                }
     1352                                return $.trim(val);
     1353                        };
     1354
     1355                        ts.formatFloat = function(s, table) {
     1356                                if (typeof s !== 'string' || s === '') { return s; }
     1357                                // allow using formatFloat without a table; defaults to US number format
     1358                                var i,
     1359                                        t = table && table.config ? table.config.usNumberFormat !== false :
     1360                                                typeof table !== "undefined" ? table : true;
     1361                                if (t) {
     1362                                        // US Format - 1,234,567.89 -> 1234567.89
     1363                                        s = s.replace(/,/g,'');
     1364                                } else {
     1365                                        // German Format = 1.234.567,89 -> 1234567.89
     1366                                        // French Format = 1 234 567,89 -> 1234567.89
     1367                                        s = s.replace(/[\s|\.]/g,'').replace(/,/g,'.');
     1368                                }
     1369                                if(/^\s*\([.\d]+\)/.test(s)) {
     1370                                        // make (#) into a negative number -> (10) = -10
     1371                                        s = s.replace(/^\s*\(([.\d]+)\)/, '-$1');
     1372                                }
     1373                                i = parseFloat(s);
     1374                                // return the text instead of zero
     1375                                return isNaN(i) ? $.trim(s) : i;
     1376                        };
     1377
     1378                        ts.isDigit = function(s) {
     1379                                // replace all unwanted chars and match
     1380                                return isNaN(s) ? (/^[\-+(]?\d+[)]?$/).test(s.toString().replace(/[,.'"\s]/g, '')) : true;
     1381                        };
     1382
     1383                }()
     1384        });
     1385
     1386        // make shortcut
     1387        var ts = $.tablesorter;
     1388
     1389        // extend plugin scope
     1390        $.fn.extend({
     1391                tablesorter: ts.construct
     1392        });
     1393
     1394        // add default parsers
     1395        ts.addParser({
     1396                id: "text",
     1397                is: function() {
     1398                        return true;
     1399                },
     1400                format: function(s, table) {
     1401                        var c = table.config;
     1402                        if (s) {
     1403                                s = $.trim( c.ignoreCase ? s.toLocaleLowerCase() : s );
     1404                                s = c.sortLocaleCompare ? ts.replaceAccents(s) : s;
     1405                        }
     1406                        return s;
     1407                },
     1408                type: "text"
     1409        });
     1410
     1411        ts.addParser({
     1412                id: "digit",
     1413                is: function(s) {
     1414                        return ts.isDigit(s);
     1415                },
     1416                format: function(s, table) {
     1417                        var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ""), table);
     1418                        return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s;
     1419                },
     1420                type: "numeric"
     1421        });
     1422
     1423        ts.addParser({
     1424                id: "currency",
     1425                is: function(s) {
     1426                        return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[,. ]/g,'')); // £$€¤¥¢
     1427                },
     1428                format: function(s, table) {
     1429                        var n = ts.formatFloat((s || '').replace(/[^\w,. \-()]/g, ""), table);
     1430                        return s && typeof n === 'number' ? n : s ? $.trim( s && table.config.ignoreCase ? s.toLocaleLowerCase() : s ) : s;
     1431                },
     1432                type: "numeric"
     1433        });
     1434
     1435        ts.addParser({
     1436                id: "ipAddress",
     1437                is: function(s) {
     1438                        return (/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/).test(s);
     1439                },
     1440                format: function(s, table) {
     1441                        var i, a = s ? s.split(".") : '',
     1442                        r = "",
     1443                        l = a.length;
     1444                        for (i = 0; i < l; i++) {
     1445                                r += ("00" + a[i]).slice(-3);
     1446                        }
     1447                        return s ? ts.formatFloat(r, table) : s;
     1448                },
     1449                type: "numeric"
     1450        });
     1451
     1452        ts.addParser({
     1453                id: "url",
     1454                is: function(s) {
     1455                        return (/^(https?|ftp|file):\/\//).test(s);
     1456                },
     1457                format: function(s) {
     1458                        return s ? $.trim(s.replace(/(https?|ftp|file):\/\//, '')) : s;
     1459                },
     1460                type: "text"
     1461        });
     1462
     1463        ts.addParser({
     1464                id: "isoDate",
     1465                is: function(s) {
     1466                        return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/).test(s);
     1467                },
     1468                format: function(s, table) {
     1469                        return s ? ts.formatFloat((s !== "") ? (new Date(s.replace(/-/g, "/")).getTime() || "") : "", table) : s;
     1470                },
     1471                type: "numeric"
     1472        });
     1473
     1474        ts.addParser({
     1475                id: "percent",
     1476                is: function(s) {
     1477                        return (/(\d\s*?%|%\s*?\d)/).test(s) && s.length < 15;
     1478                },
     1479                format: function(s, table) {
     1480                        return s ? ts.formatFloat(s.replace(/%/g, ""), table) : s;
     1481                },
     1482                type: "numeric"
     1483        });
     1484
     1485        ts.addParser({
     1486                id: "usLongDate",
     1487                is: function(s) {
     1488                        // two digit years are not allowed cross-browser
     1489                        // Jan 01, 2013 12:34:56 PM or 01 Jan 2013
     1490                        return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s);
     1491                },
     1492                format: function(s, table) {
     1493                        return s ? ts.formatFloat( (new Date(s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || ''), table) : s;
     1494                },
     1495                type: "numeric"
     1496        });
     1497
     1498        ts.addParser({
     1499                id: "shortDate", // "mmddyyyy", "ddmmyyyy" or "yyyymmdd"
     1500                is: function(s) {
     1501                        // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included
     1502                        return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/).test((s || '').replace(/\s+/g," ").replace(/[\-.,]/g, "/"));
     1503                },
     1504                format: function(s, table, cell, cellIndex) {
     1505                        if (s) {
     1506                                var c = table.config, ci = c.headerList[cellIndex],
     1507                                format = ci.dateFormat || ts.getData( ci, c.headers[cellIndex], 'dateFormat') || c.dateFormat;
     1508                                s = s.replace(/\s+/g," ").replace(/[\-.,]/g, "/"); // escaped - because JSHint in Firefox was showing it as an error
     1509                                if (format === "mmddyyyy") {
     1510                                        s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$1/$2");
     1511                                } else if (format === "ddmmyyyy") {
     1512                                        s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$2/$1");
     1513                                } else if (format === "yyyymmdd") {
     1514                                        s = s.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$1/$2/$3");
     1515                                }
     1516                        }
     1517                        return s ? ts.formatFloat( (new Date(s).getTime() || ''), table) : s;
     1518                },
     1519                type: "numeric"
     1520        });
     1521
     1522        ts.addParser({
     1523                id: "time",
     1524                is: function(s) {
     1525                        return (/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i).test(s);
     1526                },
     1527                format: function(s, table) {
     1528                        return s ? ts.formatFloat( (new Date("2000/01/01 " + s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || ""), table) : s;
     1529                },
     1530                type: "numeric"
     1531        });
     1532
     1533        ts.addParser({
     1534                id: "metadata",
     1535                is: function() {
     1536                        return false;
     1537                },
     1538                format: function(s, table, cell) {
     1539                        var c = table.config,
     1540                        p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
     1541                        return $(cell).metadata()[p];
     1542                },
     1543                type: "numeric"
     1544        });
     1545
     1546        // add default widgets
     1547        ts.addWidget({
     1548                id: "zebra",
     1549                priority: 90,
     1550                format: function(table, c, wo) {
     1551                        var $tb, $tv, $tr, row, even, time, k, l,
     1552                        child = new RegExp(c.cssChildRow, 'i'),
     1553                        b = c.$tbodies;
     1554                        if (c.debug) {
     1555                                time = new Date();
     1556                        }
     1557                        for (k = 0; k < b.length; k++ ) {
     1558                                // loop through the visible rows
     1559                                $tb = b.eq(k);
     1560                                l = $tb.children('tr').length;
     1561                                if (l > 1) {
     1562                                        row = 0;
     1563                                        $tv = $tb.children('tr:visible').not(c.selectorRemove);
     1564                                        // revered back to using jQuery each - strangely it's the fastest method
     1565                                        /*jshint loopfunc:true */
     1566                                        $tv.each(function(){
     1567                                                $tr = $(this);
     1568                                                // style children rows the same way the parent row was styled
     1569                                                if (!child.test(this.className)) { row++; }
     1570                                                even = (row % 2 === 0);
     1571                                                $tr.removeClass(wo.zebra[even ? 1 : 0]).addClass(wo.zebra[even ? 0 : 1]);
     1572                                        });
     1573                                }
     1574                        }
     1575                        if (c.debug) {
     1576                                ts.benchmark("Applying Zebra widget", time);
     1577                        }
     1578                },
     1579                remove: function(table, c, wo){
     1580                        var k, $tb,
     1581                                b = c.$tbodies,
     1582                                rmv = (wo.zebra || [ "even", "odd" ]).join(' ');
     1583                        for (k = 0; k < b.length; k++ ){
     1584                                $tb = $.tablesorter.processTbody(table, b.eq(k), true); // remove tbody
     1585                                $tb.children().removeClass(rmv);
     1586                                $.tablesorter.processTbody(table, $tb, false); // restore tbody
     1587                        }
     1588                }
     1589        });
     1590
    10311591})(jQuery);
  • extensions/UserAdvManager/branches/2.6/admin/template/js/jquery.tablesorter.min.js

    r8875 r26948  
    1 
    2 (function($){$.extend({tablesorter:new
    3 function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1
    4 var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);
     1/*!
     2* TableSorter 2.14.4 min - Client-side table sorting with ease!
     3* Copyright (c) 2007 Christian Bach
     4*/
     5!function(g){g.extend({tablesorter:new function(){function c(){var a=1<arguments.length?Array.prototype.slice.call(arguments):arguments[0];"undefined"!==typeof console&&"undefined"!==typeof console.log?console.log(a):alert(a)}function r(a,b){c(a+" ("+((new Date).getTime()-b.getTime())+"ms)")}function k(a){for(var b in a)return!1;return!0}function p(a,b,d){if(!b)return"";var h=a.config,e=h.textExtraction,n="",n="simple"===e?h.supportsTextContent?b.textContent:g(b).text():"function"===typeof e?e(b, a,d):"object"===typeof e&&e.hasOwnProperty(d)?e[d](b,a,d):h.supportsTextContent?b.textContent:g(b).text();return g.trim(n)}function s(a){var b=a.config,d=b.$tbodies=b.$table.children("tbody:not(."+b.cssInfoBlock+")"),h,e,n,l,x,g,m,v="";if(0===d.length)return b.debug?c("*Empty table!* Not building a parser cache"):"";b.debug&&(m=new Date,c("Detecting parsers for each column"));d=d[0].rows;if(d[0])for(h=[],e=d[0].cells.length,n=0;n<e;n++){l=b.$headers.filter(":not([colspan])");l=l.add(b.$headers.filter('[colspan="1"]')).filter('[data-column="'+ n+'"]:last');x=b.headers[n];g=f.getParserById(f.getData(l,x,"sorter"));b.empties[n]=f.getData(l,x,"empty")||b.emptyTo||(b.emptyToBottom?"bottom":"top");b.strings[n]=f.getData(l,x,"string")||b.stringTo||"max";if(!g)a:{l=a;x=d;g=-1;for(var k=n,u=void 0,A=f.parsers.length,q=!1,s="",u=!0;""===s&&u;)g++,x[g]?(q=x[g].cells[k],s=p(l,q,k),l.config.debug&&c("Checking if value was empty on row "+g+", column: "+k+': "'+s+'"')):u=!1;for(;0<=--A;)if((u=f.parsers[A])&&"text"!==u.id&&u.is&&u.is(s,l,q)){g=u;break a}g= f.getParserById("text")}b.debug&&(v+="column:"+n+"; parser:"+g.id+"; string:"+b.strings[n]+"; empty: "+b.empties[n]+"\n");h.push(g)}b.debug&&(c(v),r("Completed detecting parsers",m));b.parsers=h}function w(a){var b=a.tBodies,d=a.config,h,e,n=d.parsers,l,x,y,m,v,k,u,A=[];d.cache={};if(!n)return d.debug?c("*Empty table!* Not building a cache"):"";d.debug&&(u=new Date);d.showProcessing&&f.isProcessing(a,!0);for(m=0;m<b.length;m++)if(d.cache[m]={row:[],normalized:[]},!g(b[m]).hasClass(d.cssInfoBlock)){h= b[m]&&b[m].rows.length||0;e=b[m].rows[0]&&b[m].rows[0].cells.length||0;for(x=0;x<h;++x)if(v=g(b[m].rows[x]),k=[],v.hasClass(d.cssChildRow))d.cache[m].row[d.cache[m].row.length-1]=d.cache[m].row[d.cache[m].row.length-1].add(v);else{d.cache[m].row.push(v);for(y=0;y<e;++y)l=p(a,v[0].cells[y],y),l=n[y].format(l,a,v[0].cells[y],y),k.push(l),"numeric"===(n[y].type||"").toLowerCase()&&(A[y]=Math.max(Math.abs(l)||0,A[y]||0));k.push(d.cache[m].normalized.length);d.cache[m].normalized.push(k)}d.cache[m].colMax= A}d.showProcessing&&f.isProcessing(a);d.debug&&r("Building cache for "+h+" rows",u)}function z(a,b){var d=a.config,h=d.widgetOptions,e=a.tBodies,n=[],l=d.cache,c,y,m,v,p,u,A,q,s,t,w;if(!k(l)){d.debug&&(w=new Date);for(q=0;q<e.length;q++)if(c=g(e[q]),c.length&&!c.hasClass(d.cssInfoBlock)){p=f.processTbody(a,c,!0);c=l[q].row;y=l[q].normalized;v=(m=y.length)?y[0].length-1:0;for(u=0;u<m;u++)if(t=y[u][v],n.push(c[t]),!d.appender||d.pager&&!(d.pager.removeRows&&h.pager_removeRows||d.pager.ajax))for(s=c[t].length, A=0;A<s;A++)p.append(c[t][A]);f.processTbody(a,p,!1)}d.appender&&d.appender(a,n);d.debug&&r("Rebuilt table",w);b||d.appender||f.applyWidget(a);g(a).trigger("sortEnd",a);g(a).trigger("updateComplete",a)}}function D(a){var b=[],d={},h=0,e=g(a).find("thead:eq(0), tfoot").children("tr"),n,l,c,f,m,k,r,p,t,q;for(n=0;n<e.length;n++)for(m=e[n].cells,l=0;l<m.length;l++){f=m[l];k=f.parentNode.rowIndex;r=k+"-"+f.cellIndex;p=f.rowSpan||1;t=f.colSpan||1;"undefined"===typeof b[k]&&(b[k]=[]);for(c=0;c<b[k].length+ 1;c++)if("undefined"===typeof b[k][c]){q=c;break}d[r]=q;h=Math.max(q,h);g(f).attr({"data-column":q});for(c=k;c<k+p;c++)for("undefined"===typeof b[c]&&(b[c]=[]),r=b[c],f=q;f<q+t;f++)r[f]="x"}a.config.columns=h+1;return d}function C(a){return/^d/i.test(a)||1===a}function E(a){var b=D(a),d,h,e,n,l,x,k,m=a.config;m.headerList=[];m.headerContent=[];m.debug&&(k=new Date);n=m.cssIcon?'<i class="'+(m.cssIcon===f.css.icon?f.css.icon:m.cssIcon+" "+f.css.icon)+'"></i>':"";m.$headers=g(a).find(m.selectorHeaders).each(function(a){h= g(this);d=m.headers[a];m.headerContent[a]=g(this).html();l=m.headerTemplate.replace(/\{content\}/g,g(this).html()).replace(/\{icon\}/g,n);m.onRenderTemplate&&(e=m.onRenderTemplate.apply(h,[a,l]))&&"string"===typeof e&&(l=e);g(this).html('<div class="tablesorter-header-inner">'+l+"</div>");m.onRenderHeader&&m.onRenderHeader.apply(h,[a]);this.column=b[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=C(f.getData(h,d,"sortInitialOrder")||m.sortInitialOrder)?[1,0,2]:[0,1,2];this.count=-1;this.lockedOrder= !1;x=f.getData(h,d,"lockedOrder")||!1;"undefined"!==typeof x&&!1!==x&&(this.order=this.lockedOrder=C(x)?[1,1,1]:[0,0,0]);h.addClass(f.css.header+" "+m.cssHeader);m.headerList[a]=this;h.parent().addClass(f.css.headerRow+" "+m.cssHeaderRow);m.tabIndex&&h.attr("tabindex",0)});F(a);m.debug&&(r("Built headers:",k),c(m.$headers))}function B(a,b,d){var h=a.config;h.$table.find(h.selectorRemove).remove();s(a);w(a);G(h.$table,b,d)}function F(a){var b,d=a.config;d.$headers.each(function(a,e){b="false"===f.getData(e, d.headers[a],"sorter");e.sortDisabled=b;g(e)[b?"addClass":"removeClass"]("sorter-false")})}function H(a){var b,d,h,e=a.config,n=e.sortList,l=[f.css.sortAsc+" "+e.cssAsc,f.css.sortDesc+" "+e.cssDesc],c=g(a).find("tfoot tr").children().removeClass(l.join(" "));e.$headers.removeClass(l.join(" "));h=n.length;for(b=0;b<h;b++)if(2!==n[b][1]&&(a=e.$headers.not(".sorter-false").filter('[data-column="'+n[b][0]+'"]'+(1===h?":last":"")),a.length))for(d=0;d<a.length;d++)a[d].sortDisabled||(a.eq(d).addClass(l[n[b][1]]), c.length&&c.filter('[data-column="'+n[b][0]+'"]').eq(d).addClass(l[n[b][1]]))}function L(a){if(a.config.widthFixed&&0===g(a).find("colgroup").length){var b=g("<colgroup>"),d=g(a).width();g(a.tBodies[0]).find("tr:first").children("td:visible").each(function(){b.append(g("<col>").css("width",parseInt(g(this).width()/d*1E3,10)/10+"%"))});g(a).prepend(b)}}function M(a,b){var d,h,e,n=a.config,c=b||n.sortList;n.sortList=[];g.each(c,function(a,b){d=[parseInt(b[0],10),parseInt(b[1],10)];if(e=n.$headers[d[0]])n.sortList.push(d), h=g.inArray(d[1],e.order),e.count=0<=h?h:d[1]%(n.sortReset?3:2)})}function N(a,b){return a&&a[b]?a[b].type||"":""}function O(a,b,d){var h,e,n,c=a.config,x=!d[c.sortMultiSortKey],k=g(a);k.trigger("sortStart",a);b.count=d[c.sortResetKey]?2:(b.count+1)%(c.sortReset?3:2);c.sortRestart&&(e=b,c.$headers.each(function(){this===e||!x&&g(this).is("."+f.css.sortDesc+",."+f.css.sortAsc)||(this.count=-1)}));e=b.column;if(x){c.sortList=[];if(null!==c.sortForce)for(h=c.sortForce,d=0;d<h.length;d++)h[d][0]!==e&& c.sortList.push(h[d]);h=b.order[b.count];if(2>h&&(c.sortList.push([e,h]),1<b.colSpan))for(d=1;d<b.colSpan;d++)c.sortList.push([e+d,h])}else if(c.sortAppend&&1<c.sortList.length&&f.isValueInArray(c.sortAppend[0][0],c.sortList)&&c.sortList.pop(),f.isValueInArray(e,c.sortList))for(d=0;d<c.sortList.length;d++)n=c.sortList[d],h=c.$headers[n[0]],n[0]===e&&(n[1]=h.order[b.count],2===n[1]&&(c.sortList.splice(d,1),h.count=-1));else if(h=b.order[b.count],2>h&&(c.sortList.push([e,h]),1<b.colSpan))for(d=1;d< b.colSpan;d++)c.sortList.push([e+d,h]);if(null!==c.sortAppend)for(h=c.sortAppend,d=0;d<h.length;d++)h[d][0]!==e&&c.sortList.push(h[d]);k.trigger("sortBegin",a);setTimeout(function(){H(a);I(a);z(a)},1)}function I(a){var b,d,c,e,n,l,g,p,m,v,t,u,s=0,q=a.config,w=q.textSorter||"",z=q.sortList,B=z.length,C=a.tBodies.length;if(!q.serverSideSorting&&!k(q.cache)){q.debug&&(m=new Date);for(d=0;d<C;d++)n=q.cache[d].colMax,p=(l=q.cache[d].normalized)&&l[0]?l[0].length-1:0,l.sort(function(d,l){for(b=0;b<B;b++){e= z[b][0];g=z[b][1];s=0===g;if(q.sortStable&&d[e]===l[e]&&1===B)break;(c=/n/i.test(N(q.parsers,e)))&&q.strings[e]?(c="boolean"===typeof q.string[q.strings[e]]?(s?1:-1)*(q.string[q.strings[e]]?-1:1):q.strings[e]?q.string[q.strings[e]]||0:0,v=q.numberSorter?q.numberSorter(t[e],u[e],s,n[e],a):f["sortNumeric"+(s?"Asc":"Desc")](d[e],l[e],c,n[e],e,a)):(t=s?d:l,u=s?l:d,v="function"===typeof w?w(t[e],u[e],s,e,a):"object"===typeof w&&w.hasOwnProperty(e)?w[e](t[e],u[e],s,e,a):f["sortNatural"+(s?"Asc":"Desc")](d[e], l[e],e,a,q));if(v)return v}return d[p]-l[p]});q.debug&&r("Sorting on "+z.toString()+" and dir "+g+" time",m)}}function J(a,b){var d=a[0].config;d.pager&&!d.pager.ajax&&a.trigger("updateComplete");"function"===typeof b&&b(a[0])}function G(a,b,d){!1===b||a[0].isProcessing?J(a,d):a.trigger("sorton",[a[0].config.sortList,function(){J(a,d)}])}function K(a){var b=a.config,d=b.$table,c,e;b.$headers.find(b.selectorSort).add(b.$headers.filter(b.selectorSort)).unbind("mousedown.tablesorter mouseup.tablesorter sort.tablesorter keypress.tablesorter").bind("mousedown.tablesorter mouseup.tablesorter sort.tablesorter keypress.tablesorter", function(d,c){if(!(1!==(d.which||d.button)&&!/sort|keypress/.test(d.type)||"keypress"===d.type&&13!==d.which||"mouseup"===d.type&&!0!==c&&250<(new Date).getTime()-e)){if("mousedown"===d.type)return e=(new Date).getTime(),"INPUT"===d.target.tagName?"":!b.cancelSelection;b.delayInit&&k(b.cache)&&w(a);var h=(/TH|TD/.test(this.tagName)?g(this):g(this).parents("th, td").filter(":first"))[0];h.sortDisabled||O(a,h,d)}});b.cancelSelection&&b.$headers.attr("unselectable","on").bind("selectstart",!1).css({"user-select":"none", MozUserSelect:"none"});d.unbind("sortReset update updateRows updateCell updateAll addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave ".split(" ").join(".tablesorter ")).bind("sortReset.tablesorter",function(d){d.stopPropagation();b.sortList=[];H(a);I(a);z(a)}).bind("updateAll.tablesorter",function(b,d,c){b.stopPropagation();f.refreshWidgets(a,!0,!0);f.restoreHeaders(a);E(a);K(a);B(a,d,c)}).bind("update.tablesorter updateRows.tablesorter",function(b,d,c){b.stopPropagation(); F(a);B(a,d,c)}).bind("updateCell.tablesorter",function(c,e,h,f){c.stopPropagation();d.find(b.selectorRemove).remove();var m,k,r;m=d.find("tbody");c=m.index(g(e).parents("tbody").filter(":first"));var s=g(e).parents("tr").filter(":first");e=g(e)[0];m.length&&0<=c&&(k=m.eq(c).find("tr").index(s),r=e.cellIndex,m=b.cache[c].normalized[k].length-1,b.cache[c].row[a.config.cache[c].normalized[k][m]]=s,b.cache[c].normalized[k][r]=b.parsers[r].format(p(a,e,r),a,e,r),G(d,h,f))}).bind("addRows.tablesorter", function(e,f,g,r){e.stopPropagation();if(k(b.cache))F(a),B(a,g,r);else{var m=f.filter("tr").length,v=[],w=f[0].cells.length,u=d.find("tbody").index(f.parents("tbody").filter(":first"));b.parsers||s(a);for(e=0;e<m;e++){for(c=0;c<w;c++)v[c]=b.parsers[c].format(p(a,f[e].cells[c],c),a,f[e].cells[c],c);v.push(b.cache[u].row.length);b.cache[u].row.push([f[e]]);b.cache[u].normalized.push(v);v=[]}G(d,g,r)}}).bind("sorton.tablesorter",function(b,c,e,h){var f=a.config;b.stopPropagation();d.trigger("sortStart", this);M(a,c);H(a);f.delayInit&&k(f.cache)&&w(a);d.trigger("sortBegin",this);I(a);z(a,h);"function"===typeof e&&e(a)}).bind("appendCache.tablesorter",function(b,d,c){b.stopPropagation();z(a,c);"function"===typeof d&&d(a)}).bind("applyWidgetId.tablesorter",function(d,c){d.stopPropagation();f.getWidgetById(c).format(a,b,b.widgetOptions)}).bind("applyWidgets.tablesorter",function(b,d){b.stopPropagation();f.applyWidget(a,d)}).bind("refreshWidgets.tablesorter",function(b,d,c){b.stopPropagation();f.refreshWidgets(a, d,c)}).bind("destroy.tablesorter",function(b,d,c){b.stopPropagation();f.destroy(a,d,c)})}var f=this;f.version="2.14.4";f.parsers=[];f.widgets=[];f.defaults={theme:"default",widthFixed:!1,showProcessing:!1,headerTemplate:"{content}",onRenderTemplate:null,onRenderHeader:null,cancelSelection:!0,tabIndex:!0,dateFormat:"mmddyyyy",sortMultiSortKey:"shiftKey",sortResetKey:"ctrlKey",usNumberFormat:!0,delayInit:!1,serverSideSorting:!1,headers:{},ignoreCase:!0,sortForce:null,sortList:[],sortAppend:null,sortStable:!1, sortInitialOrder:"asc",sortLocaleCompare:!1,sortReset:!1,sortRestart:!1,emptyTo:"bottom",stringTo:"max",textExtraction:"simple",textSorter:null,numberSorter:null,widgets:[],widgetOptions:{zebra:["even","odd"]},initWidgets:!0,initialized:null,tableClass:"",cssAsc:"",cssDesc:"",cssHeader:"",cssHeaderRow:"",cssProcessing:"",cssChildRow:"tablesorter-childRow",cssIcon:"tablesorter-icon",cssInfoBlock:"tablesorter-infoOnly",selectorHeaders:"> thead th, > thead td",selectorSort:"th, td",selectorRemove:".remove-me", debug:!1,headerList:[],empties:{},strings:{},parsers:[]};f.css={table:"tablesorter",childRow:"tablesorter-childRow",header:"tablesorter-header",headerRow:"tablesorter-headerRow",icon:"tablesorter-icon",info:"tablesorter-infoOnly",processing:"tablesorter-processing",sortAsc:"tablesorter-headerAsc",sortDesc:"tablesorter-headerDesc"};f.log=c;f.benchmark=r;f.construct=function(a){return this.each(function(){var b=g.extend(!0,{},f.defaults,a);!this.hasInitialized&&f.buildTable&&"TABLE"!==this.tagName&& f.buildTable(this,b);f.setup(this,b)})};f.setup=function(a,b){if(!a||!a.tHead||0===a.tBodies.length||!0===a.hasInitialized)return b.debug?c("stopping initialization! No table, thead, tbody or tablesorter has already been initialized"):"";var d="",h=g(a),e=g.metadata;a.hasInitialized=!1;a.isProcessing=!0;a.config=b;g.data(a,"tablesorter",b);b.debug&&g.data(a,"startoveralltimer",new Date);b.supportsTextContent="x"===g("<span>x</span>")[0].textContent;b.supportsDataObject=function(a){a[0]=parseInt(a[0], 10);return 1<a[0]||1===a[0]&&4<=parseInt(a[1],10)}(g.fn.jquery.split("."));b.string={max:1,min:-1,"max+":1,"max-":-1,zero:0,none:0,"null":0,top:!0,bottom:!1};/tablesorter\-/.test(h.attr("class"))||(d=""!==b.theme?" tablesorter-"+b.theme:"");b.$table=h.addClass(f.css.table+" "+b.tableClass+d);b.$tbodies=h.children("tbody:not(."+b.cssInfoBlock+")");b.widgetInit={};E(a);L(a);s(a);b.delayInit||w(a);K(a);b.supportsDataObject&&"undefined"!==typeof h.data().sortlist?b.sortList=h.data().sortlist:e&&h.metadata()&& h.metadata().sortlist&&(b.sortList=h.metadata().sortlist);f.applyWidget(a,!0);0<b.sortList.length?h.trigger("sorton",[b.sortList,{},!b.initWidgets]):b.initWidgets&&f.applyWidget(a);b.showProcessing&&h.unbind("sortBegin.tablesorter sortEnd.tablesorter").bind("sortBegin.tablesorter sortEnd.tablesorter",function(b){f.isProcessing(a,"sortBegin"===b.type)});a.hasInitialized=!0;a.isProcessing=!1;b.debug&&f.benchmark("Overall initialization time",g.data(a,"startoveralltimer"));h.trigger("tablesorter-initialized", a);"function"===typeof b.initialized&&b.initialized(a)};f.isProcessing=function(a,b,d){a=g(a);var c=a[0].config;a=d||a.find("."+f.css.header);b?(0<c.sortList.length&&(a=a.filter(function(){return this.sortDisabled?!1:f.isValueInArray(parseFloat(g(this).attr("data-column")),c.sortList)})),a.addClass(f.css.processing+" "+c.cssProcessing)):a.removeClass(f.css.processing+" "+c.cssProcessing)};f.processTbody=function(a,b,d){if(d)return a.isProcessing=!0,b.before('<span class="tablesorter-savemyplace"/>'), d=g.fn.detach?b.detach():b.remove();d=g(a).find("span.tablesorter-savemyplace");b.insertAfter(d);d.remove();a.isProcessing=!1};f.clearTableBody=function(a){g(a)[0].config.$tbodies.empty()};f.restoreHeaders=function(a){var b=a.config;b.$table.find(b.selectorHeaders).each(function(a){g(this).find(".tablesorter-header-inner").length&&g(this).html(b.headerContent[a])})};f.destroy=function(a,b,d){a=g(a)[0];if(a.hasInitialized){f.refreshWidgets(a,!0,!0);var c=g(a),e=a.config,n=c.find("thead:first"),l=n.find("tr."+ f.css.headerRow).removeClass(f.css.headerRow+" "+e.cssHeaderRow),k=c.find("tfoot:first > tr").children("th, td");n.find("tr").not(l).remove();c.removeData("tablesorter").unbind("sortReset update updateAll updateRows updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd ".split(" ").join(".tablesorter "));e.$headers.add(k).removeClass([f.css.header,e.cssHeader,e.cssAsc,e.cssDesc,f.css.sortAsc,f.css.sortDesc].join(" ")).removeAttr("data-column"); l.find(e.selectorSort).unbind("mousedown.tablesorter mouseup.tablesorter keypress.tablesorter");f.restoreHeaders(a);!1!==b&&c.removeClass(f.css.table+" "+e.tableClass+" tablesorter-"+e.theme);a.hasInitialized=!1;"function"===typeof d&&d(a)}};f.regex={chunk:/(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi,hex:/^0x[0-9a-f]+$/i};f.sortNatural=function(a,b){if(a===b)return 0;var d,c,e,g,l,k;c=f.regex;if(c.hex.test(b)){d=parseInt(a.match(c.hex),16);e=parseInt(b.match(c.hex), 16);if(d<e)return-1;if(d>e)return 1}d=a.replace(c.chunk,"\\0$1\\0").replace(/\\0$/,"").replace(/^\\0/,"").split("\\0");c=b.replace(c.chunk,"\\0$1\\0").replace(/\\0$/,"").replace(/^\\0/,"").split("\\0");k=Math.max(d.length,c.length);for(l=0;l<k;l++){e=isNaN(d[l])?d[l]||0:parseFloat(d[l])||0;g=isNaN(c[l])?c[l]||0:parseFloat(c[l])||0;if(isNaN(e)!==isNaN(g))return isNaN(e)?1:-1;typeof e!==typeof g&&(e+="",g+="");if(e<g)return-1;if(e>g)return 1}return 0};f.sortNaturalAsc=function(a,b,d,c,e){if(a===b)return 0; d=e.string[e.empties[d]||e.emptyTo];return""===a&&0!==d?"boolean"===typeof d?d?-1:1:-d||-1:""===b&&0!==d?"boolean"===typeof d?d?1:-1:d||1:f.sortNatural(a,b)};f.sortNaturalDesc=function(a,b,d,c,e){if(a===b)return 0;d=e.string[e.empties[d]||e.emptyTo];return""===a&&0!==d?"boolean"===typeof d?d?-1:1:d||1:""===b&&0!==d?"boolean"===typeof d?d?1:-1:-d||-1:f.sortNatural(b,a)};f.sortText=function(a,b){return a>b?1:a<b?-1:0};f.getTextValue=function(a,b,d){if(d){var c=a?a.length:0,e=d+b;for(d=0;d<c;d++)e+= a.charCodeAt(d);return b*e}return 0};f.sortNumericAsc=function(a,b,d,c,e,g){if(a===b)return 0;g=g.config;e=g.string[g.empties[e]||g.emptyTo];if(""===a&&0!==e)return"boolean"===typeof e?e?-1:1:-e||-1;if(""===b&&0!==e)return"boolean"===typeof e?e?1:-1:e||1;isNaN(a)&&(a=f.getTextValue(a,d,c));isNaN(b)&&(b=f.getTextValue(b,d,c));return a-b};f.sortNumericDesc=function(a,b,d,c,e,g){if(a===b)return 0;g=g.config;e=g.string[g.empties[e]||g.emptyTo];if(""===a&&0!==e)return"boolean"===typeof e?e?-1:1:e||1;if(""=== b&&0!==e)return"boolean"===typeof e?e?1:-1:-e||-1;isNaN(a)&&(a=f.getTextValue(a,d,c));isNaN(b)&&(b=f.getTextValue(b,d,c));return b-a};f.sortNumeric=function(a,b){return a-b};f.characterEquivalents={a:"\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5",A:"\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5",c:"\u00e7\u0107\u010d",C:"\u00c7\u0106\u010c",e:"\u00e9\u00e8\u00ea\u00eb\u011b\u0119",E:"\u00c9\u00c8\u00ca\u00cb\u011a\u0118",i:"\u00ed\u00ec\u0130\u00ee\u00ef\u0131",I:"\u00cd\u00cc\u0130\u00ce\u00cf",o:"\u00f3\u00f2\u00f4\u00f5\u00f6", O:"\u00d3\u00d2\u00d4\u00d5\u00d6",ss:"\u00df",SS:"\u1e9e",u:"\u00fa\u00f9\u00fb\u00fc\u016f",U:"\u00da\u00d9\u00db\u00dc\u016e"};f.replaceAccents=function(a){var b,d="[",c=f.characterEquivalents;if(!f.characterRegex){f.characterRegexArray={};for(b in c)"string"===typeof b&&(d+=c[b],f.characterRegexArray[b]=RegExp("["+c[b]+"]","g"));f.characterRegex=RegExp(d+"]")}if(f.characterRegex.test(a))for(b in c)"string"===typeof b&&(a=a.replace(f.characterRegexArray[b],b));return a};f.isValueInArray=function(a, b){var d,c=b.length;for(d=0;d<c;d++)if(b[d][0]===a)return!0;return!1};f.addParser=function(a){var b,d=f.parsers.length,c=!0;for(b=0;b<d;b++)f.parsers[b].id.toLowerCase()===a.id.toLowerCase()&&(c=!1);c&&f.parsers.push(a)};f.getParserById=function(a){var b,d=f.parsers.length;for(b=0;b<d;b++)if(f.parsers[b].id.toLowerCase()===a.toString().toLowerCase())return f.parsers[b];return!1};f.addWidget=function(a){f.widgets.push(a)};f.getWidgetById=function(a){var b,d,c=f.widgets.length;for(b=0;b<c;b++)if((d= f.widgets[b])&&d.hasOwnProperty("id")&&d.id.toLowerCase()===a.toLowerCase())return d};f.applyWidget=function(a,b){a=g(a)[0];var d=a.config,c=d.widgetOptions,e=[],k,l,p;d.debug&&(k=new Date);d.widgets.length&&(d.widgets=g.grep(d.widgets,function(a,b){return g.inArray(a,d.widgets)===b}),g.each(d.widgets||[],function(a,b){(p=f.getWidgetById(b))&&p.id&&(p.priority||(p.priority=10),e[a]=p)}),e.sort(function(a,b){return a.priority<b.priority?-1:a.priority===b.priority?0:1}),g.each(e,function(e,f){if(f){if(b|| !d.widgetInit[f.id])f.hasOwnProperty("options")&&(c=a.config.widgetOptions=g.extend(!0,{},f.options,c)),f.hasOwnProperty("init")&&f.init(a,f,d,c),d.widgetInit[f.id]=!0;!b&&f.hasOwnProperty("format")&&f.format(a,d,c,!1)}}));d.debug&&(l=d.widgets.length,r("Completed "+(!0===b?"initializing ":"applying ")+l+" widget"+(1!==l?"s":""),k))};f.refreshWidgets=function(a,b,d){a=g(a)[0];var h,e=a.config,k=e.widgets,l=f.widgets,r=l.length;for(h=0;h<r;h++)l[h]&&l[h].id&&(b||0>g.inArray(l[h].id,k))&&(e.debug&& c("Refeshing widgets: Removing "+l[h].id),l[h].hasOwnProperty("remove")&&e.widgetInit[l[h].id]&&(l[h].remove(a,e,e.widgetOptions),e.widgetInit[l[h].id]=!1));!0!==d&&f.applyWidget(a,b)};f.getData=function(a,b,d){var c="";a=g(a);var e,f;if(!a.length)return"";e=g.metadata?a.metadata():!1;f=" "+(a.attr("class")||"");"undefined"!==typeof a.data(d)||"undefined"!==typeof a.data(d.toLowerCase())?c+=a.data(d)||a.data(d.toLowerCase()):e&&"undefined"!==typeof e[d]?c+=e[d]:b&&"undefined"!==typeof b[d]?c+=b[d]: " "!==f&&f.match(" "+d+"-")&&(c=f.match(RegExp("\\s"+d+"-([\\w-]+)"))[1]||"");return g.trim(c)};f.formatFloat=function(a,b){if("string"!==typeof a||""===a)return a;var c;a=(b&&b.config?!1!==b.config.usNumberFormat:"undefined"!==typeof b?b:1)?a.replace(/,/g,""):a.replace(/[\s|\.]/g,"").replace(/,/g,".");/^\s*\([.\d]+\)/.test(a)&&(a=a.replace(/^\s*\(([.\d]+)\)/,"-$1"));c=parseFloat(a);return isNaN(c)?g.trim(a):c};f.isDigit=function(a){return isNaN(a)?/^[\-+(]?\d+[)]?$/.test(a.toString().replace(/[,.'"\s]/g, "")):!0}}});var p=g.tablesorter;g.fn.extend({tablesorter:p.construct});p.addParser({id:"text",is:function(){return!0},format:function(c,r){var k=r.config;c&&(c=g.trim(k.ignoreCase?c.toLocaleLowerCase():c),c=k.sortLocaleCompare?p.replaceAccents(c):c);return c},type:"text"});p.addParser({id:"digit",is:function(c){return p.isDigit(c)},format:function(c,r){var k=p.formatFloat((c||"").replace(/[^\w,. \-()]/g,""),r);return c&&"number"===typeof k?k:c?g.trim(c&&r.config.ignoreCase?c.toLocaleLowerCase():c): c},type:"numeric"});p.addParser({id:"currency",is:function(c){return/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/.test((c||"").replace(/[,. ]/g,""))},format:function(c,r){var k=p.formatFloat((c||"").replace(/[^\w,. \-()]/g,""),r);return c&&"number"===typeof k?k:c?g.trim(c&&r.config.ignoreCase?c.toLocaleLowerCase():c):c},type:"numeric"});p.addParser({id:"ipAddress",is:function(c){return/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/.test(c)},format:function(c, g){var k,t=c?c.split("."):"",s="",w=t.length;for(k=0;k<w;k++)s+=("00"+t[k]).slice(-3);return c?p.formatFloat(s,g):c},type:"numeric"});p.addParser({id:"url",is:function(c){return/^(https?|ftp|file):\/\//.test(c)},format:function(c){return c?g.trim(c.replace(/(https?|ftp|file):\/\//,"")):c},type:"text"});p.addParser({id:"isoDate",is:function(c){return/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/.test(c)},format:function(c,g){return c?p.formatFloat(""!==c?(new Date(c.replace(/-/g,"/"))).getTime()||"":"",g):c}, type:"numeric"});p.addParser({id:"percent",is:function(c){return/(\d\s*?%|%\s*?\d)/.test(c)&&15>c.length},format:function(c,g){return c?p.formatFloat(c.replace(/%/g,""),g):c},type:"numeric"});p.addParser({id:"usLongDate",is:function(c){return/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i.test(c)||/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i.test(c)},format:function(c,g){return c?p.formatFloat((new Date(c.replace(/(\S)([AP]M)$/i,"$1 $2"))).getTime()||"",g):c},type:"numeric"}); p.addParser({id:"shortDate",is:function(c){return/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/.test((c||"").replace(/\s+/g," ").replace(/[\-.,]/g,"/"))},format:function(c,g,k,t){if(c){k=g.config;var s=k.headerList[t];t=s.dateFormat||p.getData(s,k.headers[t],"dateFormat")||k.dateFormat;c=c.replace(/\s+/g," ").replace(/[\-.,]/g,"/");"mmddyyyy"===t?c=c.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/,"$3/$1/$2"):"ddmmyyyy"===t?c=c.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$2/$1"):"yyyymmdd"===t&&(c=c.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/,"$1/$2/$3"))}return c?p.formatFloat((new Date(c)).getTime()||"",g):c},type:"numeric"});p.addParser({id:"time",is:function(c){return/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i.test(c)},format:function(c,g){return c?p.formatFloat((new Date("2000/01/01 "+c.replace(/(\S)([AP]M)$/i,"$1 $2"))).getTime()||"",g):c},type:"numeric"});p.addParser({id:"metadata",is:function(){return!1},format:function(c,p,k){c=p.config; c=c.parserMetadataName?c.parserMetadataName:"sortValue";return g(k).metadata()[c]},type:"numeric"});p.addWidget({id:"zebra",priority:90,format:function(c,r,k){var t,s,w,z,D,C,E=RegExp(r.cssChildRow,"i"),B=r.$tbodies;r.debug&&(D=new Date);for(c=0;c<B.length;c++)t=B.eq(c),C=t.children("tr").length,1<C&&(w=0,t=t.children("tr:visible").not(r.selectorRemove),t.each(function(){s=g(this);E.test(this.className)||w++;z=0===w%2;s.removeClass(k.zebra[z?1:0]).addClass(k.zebra[z?0:1])}));r.debug&&p.benchmark("Applying Zebra widget", D)},remove:function(c,p,k){var t;p=p.$tbodies;var s=(k.zebra||["even","odd"]).join(" ");for(k=0;k<p.length;k++)t=g.tablesorter.processTbody(c,p.eq(k),!0),t.children().removeClass(s),g.tablesorter.processTbody(c,t,!1)}})}(jQuery);
  • extensions/UserAdvManager/branches/2.6/admin/template/js/jquery.tablesorter.pager.js

    r6399 r26948  
    1 (function($) {
    2         $.extend({
    3                 tablesorterPager: new function() {
    4                        
    5                         function updatePageDisplay(c) {
    6                                 var s = $(c.cssPageDisplay,c.container).val((c.page+1) + c.seperator + c.totalPages);   
    7                         }
    8                        
    9                         function setPageSize(table,size) {
    10                                 var c = table.config;
    11                                 c.size = size;
    12                                 c.totalPages = Math.ceil(c.totalRows / c.size);
    13                                 c.pagerPositionSet = false;
    14                                 moveToPage(table);
    15                                 fixPosition(table);
    16                         }
    17                        
    18                         function fixPosition(table) {
    19                                 var c = table.config;
    20                                 if(!c.pagerPositionSet && c.positionFixed) {
    21                                         var c = table.config, o = $(table);
    22                                         if(o.offset) {
    23                                                 c.container.css({
    24                                                         top: o.offset().top + o.height() + 'px',
    25                                                         position: 'absolute'
     1/*!
     2 * tablesorter pager plugin
     3 * updated 12/16/2013 (v2.14.5)
     4 */
     5/*jshint browser:true, jquery:true, unused:false */
     6;(function($) {
     7        "use strict";
     8        /*jshint supernew:true */
     9        var ts = $.tablesorter;
     10
     11        $.extend({ tablesorterPager: new function() {
     12
     13                this.defaults = {
     14                        // target the pager markup
     15                        container: null,
     16
     17                        // use this format: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}"
     18                        // where {page} is replaced by the page number, {size} is replaced by the number of records to show,
     19                        // {sortList:col} adds the sortList to the url into a "col" array, and {filterList:fcol} adds
     20                        // the filterList to the url into an "fcol" array.
     21                        // So a sortList = [[2,0],[3,0]] becomes "&col[2]=0&col[3]=0" in the url
     22                        // and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url
     23                        ajaxUrl: null,
     24
     25                        // modify the url after all processing has been applied
     26                        customAjaxUrl: function(table, url) { return url; },
     27
     28                        // modify the $.ajax object to allow complete control over your ajax requests
     29                        ajaxObject: {
     30                                dataType: 'json'
     31                        },
     32
     33                        // set this to false if you want to block ajax loading on init
     34                        processAjaxOnInit: true,
     35
     36                        // process ajax so that the following information is returned:
     37                        // [ total_rows (number), rows (array of arrays), headers (array; optional) ]
     38                        // example:
     39                        // [
     40                        //   100,  // total rows
     41                        //   [
     42                        //     [ "row1cell1", "row1cell2", ... "row1cellN" ],
     43                        //     [ "row2cell1", "row2cell2", ... "row2cellN" ],
     44                        //     ...
     45                        //     [ "rowNcell1", "rowNcell2", ... "rowNcellN" ]
     46                        //   ],
     47                        //   [ "header1", "header2", ... "headerN" ] // optional
     48                        // ]
     49                        ajaxProcessing: function(ajax){ return [ 0, [], null ]; },
     50
     51                        // output default: '{page}/{totalPages}'
     52                        // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows}
     53                        output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}'
     54
     55                        // apply disabled classname to the pager arrows when the rows at either extreme is visible
     56                        updateArrows: true,
     57
     58                        // starting page of the pager (zero based index)
     59                        page: 0,
     60
     61                        // Number of visible rows
     62                        size: 10,
     63
     64                        // Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js)
     65                        savePages: true,
     66
     67                        // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty
     68                        // table row set to a height to compensate; default is false
     69                        fixedHeight: false,
     70
     71                        // count child rows towards the set page size? (set true if it is a visible table row within the pager)
     72                        // if true, child row(s) may not appear to be attached to its parent row, may be split across pages or
     73                        // may distort the table if rowspan or cellspans are included.
     74                        countChildRows: false,
     75
     76                        // remove rows from the table to speed up the sort of large tables.
     77                        // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled.
     78                        removeRows: false, // removing rows in larger tables speeds up the sort
     79
     80                        // css class names of pager arrows
     81                        cssFirst: '.first', // go to first page arrow
     82                        cssPrev: '.prev', // previous page arrow
     83                        cssNext: '.next', // next page arrow
     84                        cssLast: '.last', // go to last page arrow
     85                        cssGoto: '.gotoPage', // go to page selector - select dropdown that sets the current page
     86                        cssPageDisplay: '.pagedisplay', // location of where the "output" is displayed
     87                        cssPageSize: '.pagesize', // page size selector - select dropdown that sets the "size" option
     88                        cssErrorRow: 'tablesorter-errorRow', // error information row
     89
     90                        // class added to arrows when at the extremes (i.e. prev/first arrows are "disabled" when on the first page)
     91                        cssDisabled: 'disabled', // Note there is no period "." in front of this class name
     92
     93                        // stuff not set by the user
     94                        totalRows: 0,
     95                        totalPages: 0,
     96                        filteredRows: 0,
     97                        filteredPages: 0,
     98                        ajaxCounter: 0,
     99                        currentFilters: [],
     100                        startRow: 0,
     101                        endRow: 0,
     102                        $size: null,
     103                        last: {}
     104
     105                };
     106
     107                var $this = this,
     108
     109                // hide arrows at extremes
     110                pagerArrows = function(p, disable) {
     111                        var a = 'addClass',
     112                        r = 'removeClass',
     113                        d = p.cssDisabled,
     114                        dis = !!disable,
     115                        tp = Math.min( p.totalPages, p.filteredPages );
     116                        if ( p.updateArrows ) {
     117                                p.$container.find(p.cssFirst + ',' + p.cssPrev)[ ( dis || p.page === 0 ) ? a : r ](d);
     118                                p.$container.find(p.cssNext + ',' + p.cssLast)[ ( dis || p.page === tp - 1 || p.totalPages === 0 ) ? a : r ](d);
     119                        }
     120                },
     121
     122                updatePageDisplay = function(table, p, flag) {
     123                        var i, pg, s, out,
     124                                c = table.config,
     125                                f = c.$table.hasClass('hasFilters') && !p.ajaxUrl,
     126                                t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove +
     127                                        (p.countChildRows ? '' : ',.' + c.cssChildRow),
     128                                sz = p.size || 10; // don't allow dividing by zero
     129                        p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method
     130                        p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t ).length : p.totalRows;
     131                        p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages;
     132                        if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) {
     133                                t = (p.size * p.page > p.filteredRows);
     134                                p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1);
     135                                p.page = (t) ? 0 : p.page;
     136                                p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) );
     137                                out = p.$container.find(p.cssPageDisplay);
     138                                // form the output string (can now get a new output string from the server)
     139                                s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || p.output : p.output )
     140                                        // {page} = one-based index; {page+#} = zero based index +/- value
     141                                        .replace(/\{page([\-+]\d+)?\}/gi, function(m,n){
     142                                                return p.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0;
     143                                        })
     144                                        // {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object)
     145                                        .replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){
     146                                                var str = m.replace(/[{}\s]/g,''),
     147                                                        extra = str.split(':'),
     148                                                        data = p.ajaxData,
     149                                                        // return zero for default page/row numbers
     150                                                        deflt = /(rows?|pages?)$/i.test(str) ? 0 : '';
     151                                                return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt;
     152                                        });
     153                                if (out.length) {
     154                                        out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s);
     155                                        if ( p.$goto.length ) {
     156                                                t = '';
     157                                                pg = Math.min( p.totalPages, p.filteredPages );
     158                                                for ( i = 1; i <= pg; i++ ) {
     159                                                        t += '<option>' + i + '</option>';
     160                                                }
     161                                                p.$goto.html(t).val( p.page + 1 );
     162                                        }
     163                                }
     164                        }
     165                        pagerArrows(p);
     166                        if (p.initialized && flag !== false) {
     167                                c.$table.trigger('pagerComplete', p);
     168                                // save pager info to storage
     169                                if (p.savePages && ts.storage) {
     170                                        ts.storage(table, 'tablesorter-pager', {
     171                                                page : p.page,
     172                                                size : p.size
     173                                        });
     174                                }
     175                        }
     176                },
     177
     178                fixHeight = function(table, p) {
     179                        var d, h,
     180                                c = table.config,
     181                                $b = c.$tbodies.eq(0);
     182                        if (p.fixedHeight) {
     183                                $b.find('tr.pagerSavedHeightSpacer').remove();
     184                                h = $.data(table, 'pagerSavedHeight');
     185                                if (h) {
     186                                        d = h - $b.height();
     187                                        if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) {
     188                                                $b.append('<tr class="pagerSavedHeightSpacer ' + c.selectorRemove.replace(/(tr)?\./g,'') + '" style="height:' + d + 'px;"></tr>');
     189                                        }
     190                                }
     191                        }
     192                },
     193
     194                changeHeight = function(table, p) {
     195                        var $b = table.config.$tbodies.eq(0);
     196                        $b.find('tr.pagerSavedHeightSpacer').remove();
     197                        $.data(table, 'pagerSavedHeight', $b.height());
     198                        fixHeight(table, p);
     199                        $.data(table, 'pagerLastSize', p.size);
     200                },
     201
     202                hideRows = function(table, p){
     203                        if (!p.ajaxUrl) {
     204                                var i,
     205                                c = table.config,
     206                                rows = c.$tbodies.eq(0).children(),
     207                                l = rows.length,
     208                                s = ( p.page * p.size ),
     209                                e =  s + p.size,
     210                                f = c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered',
     211                                j = 0; // size counter
     212                                for ( i = 0; i < l; i++ ){
     213                                        if ( !rows[i].className.match(f) ) {
     214                                                rows[i].style.display = ( j >= s && j < e ) ? '' : 'none';
     215                                                // don't count child rows
     216                                                j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !p.countChildRows ? 0 : 1;
     217                                        }
     218                                }
     219                        }
     220                },
     221
     222                hideRowsSetup = function(table, p){
     223                        p.size = parseInt( p.$size.val(), 10 ) || p.size;
     224                        $.data(table, 'pagerLastSize', p.size);
     225                        pagerArrows(p);
     226                        if ( !p.removeRows ) {
     227                                hideRows(table, p);
     228                                $(table).bind('sortEnd.pager filterEnd.pager', function(){
     229                                        hideRows(table, p);
     230                                });
     231                        }
     232                },
     233
     234                renderAjax = function(data, table, p, xhr, exception){
     235                        // process data
     236                        if ( typeof(p.ajaxProcessing) === "function" ) {
     237                                // ajaxProcessing result: [ total, rows, headers ]
     238                                var i, j, hsh, $f, $sh, t, th, d, l, $err, rr_count,
     239                                        c = table.config,
     240                                        $t = c.$table,
     241                                        tds = '',
     242                                        result = p.ajaxProcessing(data, table) || [ 0, [] ],
     243                                        hl = $t.find('thead th').length;
     244
     245                                $t.find('thead tr.' + p.cssErrorRow).remove(); // Clean up any previous error.
     246
     247                                if ( exception ) {
     248                                        if (c.debug) {
     249                                                ts.log('Ajax Error', xhr, exception);
     250                                        }
     251                                        $err = $('<tr class="' + p.cssErrorRow + '"><td style="text-align:center;" colspan="' + hl + '">' + (
     252                                                xhr.status === 0 ? 'Not connected, verify Network' :
     253                                                xhr.status === 404 ? 'Requested page not found [404]' :
     254                                                xhr.status === 500 ? 'Internal Server Error [500]' :
     255                                                exception === 'parsererror' ? 'Requested JSON parse failed' :
     256                                                exception === 'timeout' ? 'Time out error' :
     257                                                exception === 'abort' ? 'Ajax Request aborted' :
     258                                                'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']' ) + '</td></tr>')
     259                                        .click(function(){
     260                                                $(this).remove();
     261                                        })
     262                                        // add error row to thead instead of tbody, or clicking on the header will result in a parser error
     263                                        .appendTo( $t.find('thead:first') );
     264                                        c.$tbodies.eq(0).empty();
     265                                } else {
     266                                        // process ajax object
     267                                        if (!$.isArray(result)) {
     268                                                p.ajaxData = result;
     269                                                p.totalRows = result.total;
     270                                                th = result.headers;
     271                                                d = result.rows;
     272                                        } else {
     273                                                // allow [ total, rows, headers ]  or [ rows, total, headers ]
     274                                                t = isNaN(result[0]) && !isNaN(result[1]);
     275                                                //ensure a zero returned row count doesn't fail the logical ||
     276                                                rr_count = result[t ? 1 : 0];
     277                                                p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count;
     278                                                d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data
     279                                                th = result[2]; // headers
     280                                        }
     281                                        l = d.length;
     282                                        if (d instanceof jQuery) {
     283                                                // append jQuery object
     284                                                c.$tbodies.eq(0).empty().append(d);
     285                                        } else if (l) {
     286                                                // build table from array
     287                                                for ( i = 0; i < l; i++ ) {
     288                                                        tds += '<tr>';
     289                                                        for ( j = 0; j < d[i].length; j++ ) {
     290                                                                // build tbody cells; watch for data containing HTML markup - see #434
     291                                                                tds += /^\s*<td/.test(d[i][j]) ? $.trim(d[i][j]) : '<td>' + d[i][j] + '</td>';
     292                                                        }
     293                                                        tds += '</tr>';
     294                                                }
     295                                                // add rows to first tbody
     296                                                p.processAjaxOnInit ? c.$tbodies.eq(0).html( tds ) : p.processAjaxOnInit = true;
     297                                        }
     298                                        // only add new header text if the length matches
     299                                        if ( th && th.length === hl ) {
     300                                                hsh = $t.hasClass('hasStickyHeaders');
     301                                                $sh = hsh ? c.widgetOptions.$sticky.children('thead:first').children().children() : '';
     302                                                $f = $t.find('tfoot tr:first').children();
     303                                                // don't change td headers (may contain pager)
     304                                                c.$headers.filter('th').each(function(j){
     305                                                        var $t = $(this), icn;
     306                                                        // add new test within the first span it finds, or just in the header
     307                                                        if ( $t.find('.' + ts.css.icon).length ) {
     308                                                                icn = $t.find('.' + ts.css.icon).clone(true);
     309                                                                $t.find('.tablesorter-header-inner').html( th[j] ).append(icn);
     310                                                                if ( hsh && $sh.length ) {
     311                                                                        icn = $sh.eq(j).find('.' + ts.css.icon).clone(true);
     312                                                                        $sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn);
     313                                                                }
     314                                                        } else {
     315                                                                $t.find('.tablesorter-header-inner').html( th[j] );
     316                                                                if (hsh && $sh.length) {
     317                                                                        $sh.eq(j).find('.tablesorter-header-inner').html( th[j] );
     318                                                                }
     319                                                        }
     320                                                        $f.eq(j).html( th[j] );
    26321                                                });
    27322                                        }
    28                                         c.pagerPositionSet = true;
    29                                 }
    30                         }
    31                        
    32                         function moveToFirstPage(table) {
    33                                 var c = table.config;
    34                                 c.page = 0;
    35                                 moveToPage(table);
    36                         }
    37                        
    38                         function moveToLastPage(table) {
    39                                 var c = table.config;
    40                                 c.page = (c.totalPages-1);
    41                                 moveToPage(table);
    42                         }
    43                        
    44                         function moveToNextPage(table) {
    45                                 var c = table.config;
    46                                 c.page++;
    47                                 if(c.page >= (c.totalPages-1)) {
    48                                         c.page = (c.totalPages-1);
    49                                 }
    50                                 moveToPage(table);
    51                         }
    52                        
    53                         function moveToPrevPage(table) {
    54                                 var c = table.config;
    55                                 c.page--;
    56                                 if(c.page <= 0) {
    57                                         c.page = 0;
    58                                 }
    59                                 moveToPage(table);
    60                         }
    61                                                
    62                        
    63                         function moveToPage(table) {
    64                                 var c = table.config;
    65                                 if(c.page < 0 || c.page > (c.totalPages-1)) {
    66                                         c.page = 0;
    67                                 }
    68                                
    69                                 renderTable(table,c.rowsCopy);
    70                         }
    71                        
    72                         function renderTable(table,rows) {
    73                                
    74                                 var c = table.config;
    75                                 var l = rows.length;
    76                                 var s = (c.page * c.size);
    77                                 var e = (s + c.size);
    78                                 if(e > rows.length ) {
     323                                }
     324                                if (c.showProcessing) {
     325                                        ts.isProcessing(table); // remove loading icon
     326                                }
     327                                // make sure last pager settings are saved, prevents multiple server side calls with
     328                                // the same parameters
     329                                p.last.totalPages =  p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
     330                                p.last.currentFilters = p.currentFilters;
     331                                p.last.sortList = (c.sortList || []).join(',');
     332                                updatePageDisplay(table, p);
     333                                fixHeight(table, p);
     334                                // apply widgets after table has rendered
     335                                $t.trigger('applyWidgets');
     336                                $t.trigger('update', [false, function(){
     337                                        if (p.initialized) {
     338                                                $t.trigger('updateComplete');
     339                                                $t.trigger('pagerChange', p);
     340                                        }
     341                                }]);
     342                        }
     343                        if (!p.initialized) {
     344                                p.initialized = true;
     345                                $(table).trigger('pagerInitialized', p);
     346                        }
     347                },
     348
     349                getAjax = function(table, p){
     350                        var url = getAjaxUrl(table, p),
     351                        $doc = $(document),
     352                        counter,
     353                        c = table.config;
     354                        if ( url !== '' ) {
     355                                if (c.showProcessing) {
     356                                        ts.isProcessing(table, true); // show loading icon
     357                                }
     358                                $doc.bind('ajaxError.pager', function(e, xhr, settings, exception) {
     359                                        renderAjax(null, table, p, xhr, exception);
     360                                        $doc.unbind('ajaxError.pager');
     361                                });
     362
     363                                counter = ++p.ajaxCounter;
     364
     365                                p.ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl
     366                                p.ajaxObject.success = function(data) {
     367                                        // Refuse to process old ajax commands that were overwritten by new ones - see #443
     368                                        if (counter < p.ajaxCounter){
     369                                                return;
     370                                        }
     371                                        renderAjax(data, table, p);
     372                                        $doc.unbind('ajaxError.pager');
     373                                        if (typeof p.oldAjaxSuccess === 'function') {
     374                                                p.oldAjaxSuccess(data);
     375                                        }
     376                                };
     377                                if (c.debug) {
     378                                        ts.log('ajax initialized', p.ajaxObject);
     379                                }
     380                                $.ajax(p.ajaxObject);
     381                        }
     382                },
     383
     384                getAjaxUrl = function(table, p) {
     385                        var c = table.config,
     386                                url = (p.ajaxUrl) ? p.ajaxUrl
     387                                // allow using "{page+1}" in the url string to switch to a non-zero based index
     388                                .replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); })
     389                                .replace(/\{size\}/g, p.size) : '',
     390                        sl = c.sortList,
     391                        fl = p.currentFilters || $(table).data('lastSearch') || [],
     392                        sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/),
     393                        filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/),
     394                        arry = [];
     395                        if (sortCol) {
     396                                sortCol = sortCol[1];
     397                                $.each(sl, function(i,v){
     398                                        arry.push(sortCol + '[' + v[0] + ']=' + v[1]);
     399                                });
     400                                // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col"
     401                                url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol );
     402                                arry = [];
     403                        }
     404                        if (filterCol) {
     405                                filterCol = filterCol[1];
     406                                $.each(fl, function(i,v){
     407                                        if (v) {
     408                                                arry.push(filterCol + '[' + i + ']=' + encodeURIComponent(v));
     409                                        }
     410                                });
     411                                // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol"
     412                                url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol );
     413                                p.currentFilters = fl;
     414                        }
     415                        if ( typeof(p.customAjaxUrl) === "function" ) {
     416                                url = p.customAjaxUrl(table, url);
     417                        }
     418                        if (c.debug) {
     419                                ts.log('Pager ajax url: ' + url);
     420                        }
     421                        return url;
     422                },
     423
     424                renderTable = function(table, rows, p) {
     425                        var i, $tb,
     426                                l = rows && rows.length || 0, // rows may be undefined
     427                                s = ( p.page * p.size ),
     428                                e = ( s + p.size );
     429                        if ( l < 1 ) { return; } // empty table, abort!
     430                        if ( p.page >= p.totalPages ) {
     431                                // lets not render the table more than once
     432                                moveToLastPage(table, p);
     433                        }
     434                        p.isDisabled = false; // needed because sorting will change the page and re-enable the pager
     435                        if (p.initialized) { $(table).trigger('pagerChange', p); }
     436
     437                        if ( !p.removeRows ) {
     438                                hideRows(table, p);
     439                        } else {
     440                                if ( e > rows.length ) {
    79441                                        e = rows.length;
    80442                                }
    81                                
    82                                
    83                                 var tableBody = $(table.tBodies[0]);
    84                                
    85                                 // clear the table body
    86                                
    87                                 $.tablesorter.clearTableBody(table);
    88                                
    89                                 for(var i = s; i < e; i++) {
    90                                        
    91                                         //tableBody.append(rows[i]);
    92                                        
    93                                         var o = rows[i];
    94                                         var l = o.length;
    95                                         for(var j=0; j < l; j++) {
    96                                                
    97                                                 tableBody[0].appendChild(o[j]);
    98 
    99                                         }
    100                                 }
    101                                
    102                                 fixPosition(table,tableBody);
    103                                
    104                                 $(table).trigger("applyWidgets");
    105                                
    106                                 if( c.page >= c.totalPages ) {
    107                                 moveToLastPage(table);
    108                                 }
    109                                
    110                                 updatePageDisplay(c);
    111                         }
    112                        
    113                         this.appender = function(table,rows) {
    114                                
    115                                 var c = table.config;
    116                                
     443                                ts.clearTableBody(table);
     444                                $tb = ts.processTbody(table, table.config.$tbodies.eq(0), true);
     445                                for ( i = s; i < e; i++ ) {
     446                                        $tb.append(rows[i]);
     447                                }
     448                                ts.processTbody(table, $tb, false);
     449                        }
     450
     451                        updatePageDisplay(table, p);
     452                        if ( !p.isDisabled ) { fixHeight(table, p); }
     453                        $(table).trigger('applyWidgets');
     454                },
     455
     456                showAllRows = function(table, p){
     457                        if ( p.ajax ) {
     458                                pagerArrows(p, true);
     459                        } else {
     460                                p.isDisabled = true;
     461                                $.data(table, 'pagerLastPage', p.page);
     462                                $.data(table, 'pagerLastSize', p.size);
     463                                p.page = 0;
     464                                p.size = p.totalRows;
     465                                p.totalPages = 1;
     466                                $(table).addClass('pagerDisabled').find('tr.pagerSavedHeightSpacer').remove();
     467                                renderTable(table, table.config.rowsCopy, p);
     468                                if (table.config.debug) {
     469                                        ts.log('pager disabled');
     470                                }
     471                        }
     472                        // disable size selector
     473                        p.$size.add(p.$goto).each(function(){
     474                                $(this).addClass(p.cssDisabled)[0].disabled = true;
     475                        });
     476                },
     477
     478                moveToPage = function(table, p, flag) {
     479                        if ( p.isDisabled ) { return; }
     480                        var c = table.config,
     481                                l = p.last,
     482                                pg = Math.min( p.totalPages, p.filteredPages );
     483                        if ( p.page < 0 ) { p.page = 0; }
     484                        if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; }
     485                        // don't allow rendering multiple times on the same page/size/totalpages/filters/sorts
     486                        if ( l.page === p.page && l.size === p.size && l.totalPages === p.totalPages &&
     487                                (l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') &&
     488                                l.sortList === (c.sortList || []).join(',') ) { return; }
     489                        if (c.debug) {
     490                                ts.log('Pager changing to page ' + p.page);
     491                        }
     492                        p.last = {
     493                                page : p.page,
     494                                size : p.size,
     495                                // fixes #408; modify sortList otherwise it auto-updates
     496                                sortList : (c.sortList || []).join(','),
     497                                totalPages : p.totalPages,
     498                                currentFilters : p.currentFilters || []
     499                        };
     500                        if (p.ajax) {
     501                                getAjax(table, p);
     502                        } else if (!p.ajax) {
     503                                renderTable(table, table.config.rowsCopy, p);
     504                        }
     505                        $.data(table, 'pagerLastPage', p.page);
     506                        if (p.initialized && flag !== false) {
     507                                c.$table.trigger('pageMoved', p);
     508                                c.$table.trigger('applyWidgets');
     509                        }
     510                },
     511
     512                setPageSize = function(table, size, p) {
     513                        p.size = size || p.size || 10;
     514                        p.$size.val(p.size);
     515                        $.data(table, 'pagerLastPage', p.page);
     516                        $.data(table, 'pagerLastSize', p.size);
     517                        p.totalPages = Math.ceil( p.totalRows / p.size );
     518                        moveToPage(table, p);
     519                },
     520
     521                moveToFirstPage = function(table, p) {
     522                        p.page = 0;
     523                        moveToPage(table, p);
     524                },
     525
     526                moveToLastPage = function(table, p) {
     527                        p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 );
     528                        moveToPage(table, p);
     529                },
     530
     531                moveToNextPage = function(table, p) {
     532                        p.page++;
     533                        if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) {
     534                                p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 );
     535                        }
     536                        moveToPage(table, p);
     537                },
     538
     539                moveToPrevPage = function(table, p) {
     540                        p.page--;
     541                        if ( p.page <= 0 ) {
     542                                p.page = 0;
     543                        }
     544                        moveToPage(table, p);
     545                },
     546
     547                destroyPager = function(table, p){
     548                        showAllRows(table, p);
     549                        p.$container.hide(); // hide pager
     550                        table.config.appender = null; // remove pager appender function
     551                        p.initialized = false;
     552                        $(table).unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager');
     553                        if (ts.storage) {
     554                                ts.storage(table, 'tablesorter-pager', '');
     555                        }
     556                },
     557
     558                enablePager = function(table, p, triggered){
     559                        var pg = p.$size.removeClass(p.cssDisabled).removeAttr('disabled');
     560                        p.$goto.removeClass(p.cssDisabled).removeAttr('disabled');
     561                        p.isDisabled = false;
     562                        p.page = $.data(table, 'pagerLastPage') || p.page || 0;
     563                        p.size = $.data(table, 'pagerLastSize') || parseInt(pg.find('option[selected]').val(), 10) || p.size || 10;
     564                        pg.val(p.size); // set page size
     565                        p.totalPages = Math.ceil( Math.min( p.totalPages, p.filteredPages ) / p.size );
     566                        if ( triggered ) {
     567                                $(table).trigger('update');
     568                                setPageSize(table, p.size, p);
     569                                hideRowsSetup(table, p);
     570                                fixHeight(table, p);
     571                                if (table.config.debug) {
     572                                        ts.log('pager enabled');
     573                                }
     574                        }
     575                };
     576
     577                $this.appender = function(table, rows) {
     578                        var c = table.config,
     579                                p = c.pager;
     580                        if ( !p.ajax ) {
    117581                                c.rowsCopy = rows;
    118                                 c.totalRows = rows.length;
    119                                 c.totalPages = Math.ceil(c.totalRows / c.size);
    120                                
    121                                 renderTable(table,rows);
    122                         };
    123                        
    124                         this.defaults = {
    125                                 size: 10,
    126                                 offset: 0,
    127                                 page: 0,
    128                                 totalRows: 0,
    129                                 totalPages: 0,
    130                                 container: null,
    131                                 cssNext: '.next',
    132                                 cssPrev: '.prev',
    133                                 cssFirst: '.first',
    134                                 cssLast: '.last',
    135                                 cssPageDisplay: '.pagedisplay',
    136                                 cssPageSize: '.pagesize',
    137                                 seperator: "/",
    138                                 positionFixed: true,
    139                                 appender: this.appender
    140                         };
    141                        
    142                         this.construct = function(settings) {
    143                                
    144                                 return this.each(function() {   
    145                                        
    146                                         config = $.extend(this.config, $.tablesorterPager.defaults, settings);
    147                                        
    148                                         var table = this, pager = config.container;
    149                                
    150                                         $(this).trigger("appendCache");
    151                                        
    152                                         config.size = parseInt($(".pagesize",pager).val());
    153                                        
    154                                         $(config.cssFirst,pager).click(function() {
    155                                                 moveToFirstPage(table);
     582                                p.totalRows = p.countChildRows ? c.$tbodies.eq(0).children().length : rows.length;
     583                                p.size = $.data(table, 'pagerLastSize') || p.size || 10;
     584                                p.totalPages = Math.ceil( p.totalRows / p.size );
     585                                renderTable(table, rows, p);
     586                        }
     587                };
     588
     589                $this.construct = function(settings) {
     590                        return this.each(function() {
     591                                // check if tablesorter has initialized
     592                                if (!(this.config && this.hasInitialized)) { return; }
     593                                var t, ctrls, fxn,
     594                                        table = this,
     595                                        c = table.config,
     596                                        p = c.pager = $.extend( {}, $.tablesorterPager.defaults, settings ),
     597                                        $t = c.$table,
     598                                        // added in case the pager is reinitialized after being destroyed.
     599                                        pager = p.$container = $(p.container).addClass('tablesorter-pager').show();
     600                                if (c.debug) {
     601                                        ts.log('Pager initializing');
     602                                }
     603                                p.oldAjaxSuccess = p.oldAjaxSuccess || p.ajaxObject.success;
     604                                c.appender = $this.appender;
     605                                if (ts.filter && $.inArray('filter', c.widgets) >= 0) {
     606                                        // get any default filter settings (data-value attribute) fixes #388
     607                                        p.currentFilters = c.$table.data('lastSearch') || ts.filter.setDefaults(table, c, c.widgetOptions) || [];
     608                                        // set, but don't apply current filters
     609                                        ts.setFilters(table, p.currentFilters, false);
     610                                }
     611                                if (p.savePages && ts.storage) {
     612                                        t = ts.storage(table, 'tablesorter-pager') || {}; // fixes #387
     613                                        p.page = isNaN(t.page) ? p.page : t.page;
     614                                        p.size = ( isNaN(t.size) ? p.size : t.size ) || 10;
     615                                        $.data(table, 'pagerLastSize', p.size);
     616                                }
     617
     618                                $t
     619                                        .unbind('filterStart filterEnd sortEnd disable enable destroy update updateRows updateAll addRows pageSize '.split(' ').join('.pager '))
     620                                        .bind('filterStart.pager', function(e, filters) {
     621                                                p.currentFilters = filters;
     622                                                p.page = 0; // fixes #456
     623                                        })
     624                                        // update pager after filter widget completes
     625                                        .bind('filterEnd.pager sortEnd.pager', function() {
     626                                                if (p.initialized) {
     627                                                        moveToPage(table, p, false);
     628                                                        updatePageDisplay(table, p, false);
     629                                                        fixHeight(table, p);
     630                                                }
     631                                        })
     632                                        .bind('disable.pager', function(e){
     633                                                e.stopPropagation();
     634                                                showAllRows(table, p);
     635                                        })
     636                                        .bind('enable.pager', function(e){
     637                                                e.stopPropagation();
     638                                                enablePager(table, p, true);
     639                                        })
     640                                        .bind('destroy.pager', function(e){
     641                                                e.stopPropagation();
     642                                                destroyPager(table, p);
     643                                        })
     644                                        .bind('update updateRows updateAll addRows '.split(' ').join('.pager '), function(e){
     645                                                e.stopPropagation();
     646                                                hideRows(table, p);
     647                                        })
     648                                        .bind('pageSize.pager', function(e,v){
     649                                                e.stopPropagation();
     650                                                setPageSize(table, parseInt(v, 10) || 10, p);
     651                                                hideRows(table, p);
     652                                                updatePageDisplay(table, p, false);
     653                                                if (p.$size.length) { p.$size.val(p.size); } // twice?
     654                                        })
     655                                        .bind('pageSet.pager', function(e,v){
     656                                                e.stopPropagation();
     657                                                p.page = (parseInt(v, 10) || 1) - 1;
     658                                                if (p.$goto.length) { p.$goto.val(p.size); } // twice?
     659                                                moveToPage(table, p);
     660                                                updatePageDisplay(table, p, false);
     661                                        });
     662
     663                                // clicked controls
     664                                ctrls = [ p.cssFirst, p.cssPrev, p.cssNext, p.cssLast ];
     665                                fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ];
     666                                pager.find(ctrls.join(','))
     667                                        .unbind('click.pager')
     668                                        .bind('click.pager', function(e){
     669                                                e.stopPropagation();
     670                                                var i, $t = $(this), l = ctrls.length;
     671                                                if ( !$t.hasClass(p.cssDisabled) ) {
     672                                                        for (i = 0; i < l; i++) {
     673                                                                if ($t.is(ctrls[i])) {
     674                                                                        fxn[i](table, p);
     675                                                                        break;
     676                                                                }
     677                                                        }
     678                                                }
     679                                        });
     680
     681                                // goto selector
     682                                p.$goto = pager.find(p.cssGoto);
     683                                if ( p.$goto.length ) {
     684                                        p.$goto
     685                                                .unbind('change')
     686                                                .bind('change', function(){
     687                                                        p.page = $(this).val() - 1;
     688                                                        moveToPage(table, p);
     689                                                        updatePageDisplay(table, p, false);
     690                                                });
     691                                }
     692
     693                                // page size selector
     694                                p.$size = pager.find(p.cssPageSize);
     695                                if ( p.$size.length ) {
     696                                        p.$size.unbind('change.pager').bind('change.pager', function() {
     697                                                p.$size.val( $(this).val() ); // in case there are more than one pagers
     698                                                if ( !$(this).hasClass(p.cssDisabled) ) {
     699                                                        setPageSize(table, parseInt( $(this).val(), 10 ), p);
     700                                                        changeHeight(table, p);
     701                                                }
    156702                                                return false;
    157703                                        });
    158                                         $(config.cssNext,pager).click(function() {
    159                                                 moveToNextPage(table);
    160                                                 return false;
    161                                         });
    162                                         $(config.cssPrev,pager).click(function() {
    163                                                 moveToPrevPage(table);
    164                                                 return false;
    165                                         });
    166                                         $(config.cssLast,pager).click(function() {
    167                                                 moveToLastPage(table);
    168                                                 return false;
    169                                         });
    170                                         $(config.cssPageSize,pager).change(function() {
    171                                                 setPageSize(table,parseInt($(this).val()));
    172                                                 return false;
    173                                         });
    174                                 });
    175                         };
    176                        
    177                 }
    178         });
    179         // extend plugin scope
    180         $.fn.extend({
    181         tablesorterPager: $.tablesorterPager.construct
    182         });
    183        
    184 })(jQuery);                             
     704                                }
     705
     706                                // clear initialized flag
     707                                p.initialized = false;
     708                                // before initialization event
     709                                $t.trigger('pagerBeforeInitialized', p);
     710
     711                                enablePager(table, p, false);
     712
     713                                if ( typeof(p.ajaxUrl) === 'string' ) {
     714                                        // ajax pager; interact with database
     715                                        p.ajax = true;
     716                                        //When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side.
     717                                        c.widgetOptions.filter_serversideFiltering = true;
     718                                        c.serverSideSorting = true;
     719                                        moveToPage(table, p);
     720                                } else {
     721                                        p.ajax = false;
     722                                        // Regular pager; all rows stored in memory
     723                                        $(this).trigger("appendCache", true);
     724                                        hideRowsSetup(table, p);
     725                                }
     726
     727                                changeHeight(table, p);
     728
     729                                // pager initialized
     730                                if (!p.ajax) {
     731                                        p.initialized = true;
     732                                        $(table).trigger('pagerInitialized', p);
     733                                }
     734                        });
     735                };
     736
     737        }()
     738});
     739// extend plugin scope
     740$.fn.extend({
     741        tablesorterPager: $.tablesorterPager.construct
     742});
     743
     744})(jQuery);
  • extensions/UserAdvManager/branches/2.6/admin/template/uam.css

    r18461 r26948  
    3838  font-weight: bold;
    3939  color: yellow;
     40}
     41
     42table.table2 {
     43        border: 1px solid #111;
     44        margin: 1em auto;
     45        padding: 0;
     46  width: 97%;
    4047}
    4148
     
    8592
    8693/* jQuery tablesorter instructions */
    87 tr.throw
     94table.tablesorter tbody tr.normal-row td {
     95  background: #111111; 
     96  color: #666666;
     97}
     98table.tablesorter tbody tr.alt-row td {
     99  background: #222222;
     100  color: #666666;
     101}
     102
     103tr.tablesorter-headerRow, throw
    88104{
    89105cursor:pointer;
     
    91107}
    92108
    93 th.header
     109th.tablesorter-header
    94110{
    95111background-image:url("./icon/bg.png");
     
    99115}
    100116
    101 th.headerSortDown
     117th.tablesorter-headerDesc
    102118{
    103119background-image:url("./icon/desc.png");
    104120}
    105121
    106 th.headerSortUp
     122th.tablesorter-headerAsc
    107123{
    108124background-image:url("./icon/asc.png");
    109125}
    110126
     127th.sorter-false {
     128        background-image: none;
     129}
     130
     131/* jQuery tablesorter pager instructions */
    111132.pager{
    112133  text-align:center;
     134}
     135
     136/* pager wrapper, div */
     137.tablesorter-pager {
     138  padding: 5px;
     139}
     140/* pager wrapper, in thead/tfoot */
     141td.tablesorter-pager {
     142  background-color: #e6eeee;
     143  margin: 0; /* needed for bootstrap .pager gets a 18px bottom margin */
     144}
     145/* pager navigation arrows */
     146.tablesorter-pager img {
     147  vertical-align: middle;
     148  margin-right: 2px;
     149  cursor: pointer;
    113150}
    114151
     
    116153  margin-bottom: -3px;
    117154}
     155
     156/* pager output text */
     157.tablesorter-pager .pagedisplay {
     158  padding: 0 5px 0 5px;
     159  width: 50px;
     160  text-align: center;
     161}
     162
     163
     164/* pager element reset (needed for bootstrap) */
     165.tablesorter-pager select {
     166  margin: 0;
     167  padding: 0;
     168}
     169
     170/*** css used when "updateArrows" option is true ***/
     171/* the pager itself gets a disabled class when the number of rows is less than the size */
     172.tablesorter-pager.disabled {
     173  display: none;
     174}
     175/* hide or fade out pager arrows when the first or last row is visible */
     176.tablesorter-pager .disabled {
     177  /* visibility: hidden */
     178  opacity: 0.5;
     179  filter: alpha(opacity=50);
     180  cursor: default;
     181}
  • extensions/UserAdvManager/branches/2.6/admin/template/userlist.tpl

    r21747 r26948  
    22{combine_script id='jquery.cluetip' require='jquery' path='themes/default/js/plugins/jquery.cluetip.js'}
    33{combine_script id='jquery.tablesorter' require='jquery' path=$UAM_PATH|@cat:'admin/template/js/jquery.tablesorter.min.js'}
    4 {combine_script id='jquery.tablesorter.pager' require='jquery' path=$UAM_PATH|@cat:'admin/template/js/jquery.tablesorter.pager.js'}
     4{combine_script id='jquery.tablesorter.pager' require='jquery' path=$UAM_PATH|@cat:'admin/template/js/jquery.tablesorter.pager.min.js'}
    55
    66{combine_css path= $UAM_PATH|@cat:'admin/template/uam.css'}
     7
    78
    89<script type="text/javascript">
     
    2122      .tablesorter(
    2223      {ldelim}
    23           sortList:[[3,1]],
     24          sortList:[[3,0]],
    2425          // pass the headers argument and assing a object
    2526          headers:
    26           {ldelim}
    27               // assign the fourth column (we start counting zero)
     27          {ldelim}
     28              // assign the second column (we start counting zero)
     29              1:
     30              {ldelim}
     31                  // disable it by setting the property sorter to false
     32                  sorter: false
     33              {rdelim},
     34              // assign the fifth column (we start counting zero)
    2835              4:
    2936              {ldelim}
    3037                  // disable it by setting the property sorter to false
    31                   sorter: false 
     38                  sorter: false
    3239              {rdelim}
    33           {rdelim} 
     40          {rdelim}
    3441      {rdelim})
    35       .tablesorterPager({ldelim}container: $("#pager"), positionFixed: false, size: 20, totalPages: 0{rdelim});
     42      .tablesorterPager({ldelim}container: $("#pager"), page: 0, size: 20, output: '{ldelim}page{rdelim} / {ldelim}totalPages{rdelim}'{rdelim});
    3643    {rdelim}
    3744);
     
    4249</div>
    4350
    44 <form method="post" action="" class="general">
     51<form method="post" class="general">
    4552  <fieldset>
    4653        <legend class="cluetip" title="{'UAM_Tracking registered users'|translate}|{'UAM_userlistTitle_d'|translate}">{'UAM_Tracking registered users'|@translate}</legend>
    4754    {if count($users) > 0}
    48       <table id="sorting" class="table2" width="97%" summary="">
     55      <table id="sorting" class="table2">
    4956                        <thead>
    5057                        <tr class="throw">
     
    5966        {foreach from=$users item=user name=users_loop}
    6067          <tr class="{if $smarty.foreach.users_loop.index is odd}row1{else}row2{/if}">
    61             <td><label for="selection-{$user.ID}">{$user.USERNAME}</label></td>
    62             <td style="text-align:center;"><a href="./admin.php?page=profile&amp;user_id={$user.ID}" title="{'Profile'|@translate}" onclick="window.open(this.href); return false;"><img src="{$UAM_PATH}admin/template/icon/edit_s.png"/></a></td>
     68            <td><label>{$user.USERNAME}</label></td>
     69            <td style="text-align:center;"><a href="./admin.php?page=profile&amp;user_id={$user.ID}" title="{'Profile'|@translate}" onclick="window.open(this.href); return false;"><img src="{$UAM_PATH}admin/template/icon/edit_s.png" alt=""/></a></td>
    6370            <td>{$user.EMAIL}</td>
    6471            <td style="text-align:center;">{$user.LASTVISIT}</td>
     
    8087      </table>
    8188<div id="pager" class="pager">
    82         <form>
    83                 <img src="{$UAM_PATH}admin/template/icon/first.png" class="first"/>
    84                 <img src="{$UAM_PATH}admin/template/icon/prev.png" class="prev"/>
     89                <img src="{$UAM_PATH}admin/template/icon/first.png" class="first" alt=""/>
     90                <img src="{$UAM_PATH}admin/template/icon/prev.png" class="prev" alt=""/>
    8591                <input type="text" class="pagedisplay"/>
    86                 <img src="{$UAM_PATH}admin/template/icon/next.png" class="next"/>
    87                 <img src="{$UAM_PATH}admin/template/icon/last.png" class="last"/>
    88                 <select class="pagesize">
     92                <img src="{$UAM_PATH}admin/template/icon/next.png" class="next" alt=""/>
     93                <img src="{$UAM_PATH}admin/template/icon/last.png" class="last" alt=""/>
     94                <select class="pagesize" title="{'UAM_Select page size'|@translate}">
    8995                        <option  value="10">10</option>
    9096                        <option selected="selected" value="20">20</option>
     
    9298                        <option value="40">40</option>
    9399                </select>
    94         </form>
     100    <select class="gotoPage" title="{'UAM_Select page number'|@translate}"></select>
    95101</div>
    96102        <br/>
  • extensions/UserAdvManager/branches/2.6/admin/template/usermanager.tpl

    r21747 r26948  
    22{combine_script id='jquery.cluetip' require='jquery' path='themes/default/js/plugins/jquery.cluetip.js'}
    33{combine_script id='jquery.tablesorter' require='jquery' path=$UAM_PATH|@cat:'admin/template/js/jquery.tablesorter.min.js'}
    4 {combine_script id='jquery.tablesorter.pager' require='jquery' path=$UAM_PATH|@cat:'admin/template/js/jquery.tablesorter.pager.js'}
     4{combine_script id='jquery.tablesorter.pager' require='jquery' path=$UAM_PATH|@cat:'admin/template/js/jquery.tablesorter.pager.min.js'}
    55
    66{combine_css path= $UAM_PATH|@cat:'admin/template/uam.css'}
     
    1616{rdelim});
    1717
    18 $(document).ready(function() 
     18$(document).ready(function()
    1919    {ldelim}
    2020      $("#sorting")
    21       .tablesorter({ldelim}sortList:[[6,1]], headers: {ldelim} 0: {ldelim} sorter: false {rdelim}{rdelim}{rdelim})
    22       .tablesorterPager({ldelim}container: $("#pager"), positionFixed: false, size: 20, totalPages: 0{rdelim});
     21      .tablesorter({ldelim}sortList:[[6,0]], headers: {ldelim} 0: {ldelim} sorter: false {rdelim},2: {ldelim} sorter: false {rdelim}{rdelim}{rdelim})
     22      .tablesorterPager({ldelim}container: $("#pager"), page: 0, size: 20, output: '{ldelim}page{rdelim} / {ldelim}totalPages{rdelim}',{rdelim});
    2323    {rdelim}
    2424);
     
    2929</div>
    3030
    31 <form method="post" action="" class="general">
     31<form method="post" class="general">
     32    {if count($users) > 0}
    3233  <fieldset>
    3334        <legend class="cluetip" title="{'UAM_Tracking confirmations'|translate}|{'UAM_usermanTitle_d'|translate}">{'UAM_Tracking confirmations'|@translate}</legend>
    34     {if count($users) > 0}
    35       <table id="sorting" class="table2" width="97%" summary="">
     35      <table id="sorting" class="table2">
    3636                  <thead>
    3737                        <tr class="throw">
    38                         <th>&nbsp;</td>
     38                        <th>&nbsp;</th>
    3939                        <th>{'Username'|@translate}&nbsp;&nbsp;</th>
    4040            <th>{'Profile'|@translate}&nbsp;&nbsp;</th>
     
    5353            <td><input type="checkbox" name="selection[]" value="{$user.ID}" {$user.CHECKED} id="selection-{$user.ID}"/></td>
    5454            <td><label for="selection-{$user.ID}">{$user.USERNAME}</label></td>
    55             <td style="text-align:center;"><a href="./admin.php?page=profile&amp;user_id={$user.ID}" title="{'Profile'|@translate}" onclick="window.open(this.href); return false;"><img src="{$UAM_PATH}admin/template/icon/edit_s.png"/></a></td>
     55            <td style="text-align:center;"><a href="./admin.php?page=profile&amp;user_id={$user.ID}" title="{'Profile'|@translate}" onclick="window.open(this.href); return false;"><img src="{$UAM_PATH}admin/template/icon/edit_s.png" alt=""/></a></td>
    5656            <td>{$user.STATUS}</td>
    5757            <td>{$user.EMAIL}</td>
     
    7070      {if !empty($users)}
    7171        <div id="pager" class="pager">
    72           <form>
    73             <img src="{$UAM_PATH}admin/template/icon/first.png" class="first"/>
    74             <img src="{$UAM_PATH}admin/template/icon/prev.png" class="prev"/>
     72            <img src="{$UAM_PATH}admin/template/icon/first.png" class="first" alt=""/>
     73            <img src="{$UAM_PATH}admin/template/icon/prev.png" class="prev" alt=""/>
    7574            <input type="text" class="pagedisplay"/>
    76             <img src="{$UAM_PATH}admin/template/icon/next.png" class="next"/>
    77             <img src="{$UAM_PATH}admin/template/icon/last.png" class="last"/>
    78             <select class="pagesize">
     75            <img src="{$UAM_PATH}admin/template/icon/next.png" class="next" alt=""/>
     76            <img src="{$UAM_PATH}admin/template/icon/last.png" class="last" alt=""/>
     77            <select class="pagesize" title="{'UAM_Select page size'|@translate}">
    7978              <option  value="10">10</option>
    8079              <option selected="selected" value="20">20</option>
     
    8281              <option value="40">40</option>
    8382            </select>
    84           </form>
     83            <select class="gotoPage" title="{'UAM_Select page number'|@translate}"></select>
    8584        </div>
    8685      {/if}
     
    107106  </fieldset>
    108107    {else}
     108  <fieldset>
     109        <legend class="cluetip" title="{'UAM_Tracking confirmations'|translate}|{'UAM_usermanTitle_d'|translate}">{'UAM_Tracking confirmations'|@translate}</legend>
    109110      <div>
    110111        {'UAM_No_Usermanager'|@translate}
    111112                  </div>
     113  </fieldset>
    112114    {/if}
    113115</form>
  • extensions/UserAdvManager/branches/2.6/include/functions.inc.php

    r26077 r26948  
    142142
    143143    // --------------------------------------------------------------------------------------------------------------------
    144     // Workflow when admins have to validate registrations (CONFIRM_MAIL = local)
    145     // No validation needed when admins add users if ADMINCONFMAIL is set to OFF - users are considered as valid by default
    146     // Else a notification email with validation link is send to admins
    147     // Finally when a user registers himself, a notification email with validation link is send to admins
     144    // Workflow when admins have to validate registrations (CONFIRM_MAIL = local):
     145    // 1- A confirmation email with validation link is send to users added by admins
     146    // 2- Else no validation needed when admins add users if ADMINCONFMAIL is set to OFF - users are considered as valid by
     147    // default and no information email is send to user from UAM (Piwigo will do that by checking the option box)
     148    // 3- Finally when a user registers himself, a notification email with validation link is send to admins
    148149    // --------------------------------------------------------------------------------------------------------------------
    149150    if (isset($conf_UAM['CONFIRM_MAIL']) and $conf_UAM['CONFIRM_MAIL'] == 'local')
    150151    {
    151       if (is_admin() and isset($conf_UAM['ADMINCONFMAIL']) and $conf_UAM['ADMINCONFMAIL'] == 'true')
     152      if (is_admin() and (isset($conf_UAM['ADMINCONFMAIL']) and $conf_UAM['ADMINCONFMAIL'] == 'true'))
    152153      {
    153154        SendMail2User(1, $register_user['id'], $register_user['username'], $passwd, $register_user['email'], true);
    154155      }
    155       elseif (is_admin() and isset($conf_UAM['ADMINCONFMAIL']) and $conf_UAM['ADMINCONFMAIL'] == 'false')
     156      elseif (is_admin() and (isset($conf_UAM['ADMINCONFMAIL']) and $conf_UAM['ADMINCONFMAIL'] == 'false'))
    156157      {
    157158        SetValidated($register_user['id']);
    158         SendMail2User(1, $register_user['id'], $register_user['username'], $passwd, $register_user['email'], false);
     159        // Pending information email - Managed by Piwigo
     160        //SendMail2User(2, $register_user['id'], $register_user['username'], $passwd, $register_user['email'], false);
    159161      }
    160162      elseif (!is_admin())
     
    164166    }
    165167    // --------------------------------------------------------------------------------------------------------------------
    166     // Workflow when users have to validate their registration (CONFIRM_MAIL = true)
    167     // No validation needed when admins add users if ADMINCONFMAIL is set to OFF - users are considered as valid by default
    168     // Else an email with validation link is send to user
    169     // Finally when a user registers himself, an email with validation link is send to him
     168    // Workflow when users have to validate their registration (CONFIRM_MAIL = true):
     169    // 1- A confirmation email with validation link is send to users added by admins
     170    // 2- ELse no validation needed when admins add users if ADMINCONFMAIL is set to OFF - users are considered as valid by
     171    // default and no information email is send to user from UAM (Piwigo will do that by checking the option box)
     172    // 3- Finally when a user registers himself, an email with validation link is send to him
    170173    // --------------------------------------------------------------------------------------------------------------------
    171174    elseif (isset($conf_UAM['CONFIRM_MAIL']) and $conf_UAM['CONFIRM_MAIL'] == 'true')
    172175    {
    173       if (is_admin() and isset($conf_UAM['ADMINCONFMAIL']) and $conf_UAM['ADMINCONFMAIL'] == 'true')
     176      if (is_admin() and (isset($conf_UAM['ADMINCONFMAIL']) and $conf_UAM['ADMINCONFMAIL'] == 'true'))
    174177      {
    175178        SendMail2User(1, $register_user['id'], $register_user['username'], $passwd, $register_user['email'], true);
    176179      }
    177       elseif (is_admin() and isset($conf_UAM['ADMINCONFMAIL']) and $conf_UAM['ADMINCONFMAIL'] == 'false')
     180      elseif (is_admin() and (isset($conf_UAM['ADMINCONFMAIL']) and $conf_UAM['ADMINCONFMAIL'] == 'false'))
    178181      {
    179182        SetValidated($register_user['id']);
    180         SendMail2User(1, $register_user['id'], $register_user['username'], $passwd, $register_user['email'], false);
     183        // Pending information email - Managed by Piwigo
     184        //SendMail2User(2, $register_user['id'], $register_user['username'], $passwd, $register_user['email'], false);
    181185      }
    182186      elseif (!is_admin())
     
    287291    }
    288292
    289     $typemail = 3; // Only information email send to user on user profile update if checked
    290 
    291     if (!empty($_POST['use_new_pwd']))
    292     {
    293       $typemail = 2; // Confirmation email on user profile update - With information email
    294     }
    295 
    296293    // Sending registration confirmation by email
    297294    // ------------------------------------------
     
    310307        list($current_email) = pwg_db_fetch_row(pwg_query($query));
    311308
    312         // This is to send a new validation key
    313         // ------------------------------------
    314         if ($_POST['mail_address'] != $current_email and (isset($conf_UAM['CONFIRM_MAIL']) and $conf_UAM['CONFIRM_MAIL'] == 'true'))
     309        // This is to set the user to "waiting" group or status until validation
     310        // ---------------------------------------------------------------------
     311        if ($_POST['mail_address'] != $current_email and (isset($conf_UAM['CONFIRM_MAIL']) and ($conf_UAM['CONFIRM_MAIL'] == 'true' or $conf_UAM['CONFIRM_MAIL'] == 'local')))
    315312        {
    316313          SetPermission($user['id']);// Set to "waiting" group or status until user validation
     
    318315          $confirm_mail_need = true;
    319316        }
    320 
    321         // This is to set the user to "waiting" group or status until admin validation
    322         // ---------------------------------------------------------------------------
    323         elseif ($_POST['mail_address'] != $current_email and (isset($conf_UAM['CONFIRM_MAIL']) and $conf_UAM['CONFIRM_MAIL'] == 'local'))
     317        else
    324318        {
    325           SetPermission($user['id']);// Set to "waiting" group or status until admin validation
    326           SetUnvalidated($user['id']); // Set UAM_validated field to false in #_users table
    327319          $confirm_mail_need = false;
    328         }       
    329       }
    330 
    331       if (((!empty($_POST['use_new_pwd']) and (isset($conf_UAM['MAIL_INFO']) and $conf_UAM['MAIL_INFO'] == 'true')) or $confirm_mail_need))
     320        }
     321      }
     322
     323      // Send information email
     324      if ((isset($conf_UAM['MAIL_INFO']) and $conf_UAM['MAIL_INFO'] == 'true') or $confirm_mail_need)
    332325      {
    333326        $query = '
     
    338331
    339332        list($username) = pwg_db_fetch_row(pwg_query($query));
    340         SendMail2User($typemail, $user['id'], $username, $_POST['use_new_pwd'], $_POST['mail_address'], $confirm_mail_need);
     333
     334        SendMail2User(2, $user['id'], $username, $_POST['use_new_pwd'], $_POST['mail_address'], $confirm_mail_need);
    341335      }
    342336    }
     
    11091103      break;
    11101104
    1111     case 2: // Confirmation email on user profile update - With information email if modification done in user profile
    1112       if (isset($conf_UAM['CONFIRMMAIL_SUBJECT']) and !empty($conf_UAM['CONFIRMMAIL_SUBJECT']))
     1105    case 2: // Confirmation email on user profile update - Information email if modification done in user profile
     1106      if (isset($conf_UAM['INFOMAIL_SUBJECT']) and !empty($conf_UAM['INFOMAIL_SUBJECT']))
    11131107      {
    11141108        // Management of Extension flags ([username], [mygallery])
     
    11211115        if (function_exists('get_user_language_desc'))
    11221116        {
    1123           $subject = get_user_language_desc(preg_replace($patterns, $replacements, $conf_UAM['CONFIRMMAIL_SUBJECT']))."\n\n";
     1117          $subject = get_user_language_desc(preg_replace($patterns, $replacements, $conf_UAM['INFOMAIL_SUBJECT']))."\n\n";
    11241118        }
    1125         else $subject = l10n(preg_replace($patterns, $replacements, $conf_UAM['CONFIRMMAIL_SUBJECT']))."\n\n";
    1126       }
    1127 
    1128       $password = !empty($password) ? $password : l10n('UAM_empty_pwd');
     1119        else $subject = l10n(preg_replace($patterns, $replacements, $conf_UAM['INFOMAIL_SUBJECT']))."\n\n";
     1120      }
     1121
     1122      $password = !empty($password) ? $password : l10n('UAM_no_update_pwd');
    11291123
    11301124      if (isset($conf_UAM['MAILINFO_TEXT']) and !empty($conf_UAM['MAILINFO_TEXT']))
     
    11701164
    11711165      break;
    1172 
    1173     case 3: // Only information email send to user if checked
    1174       if (isset($conf_UAM['INFOMAIL_SUBJECT']) and !empty($conf_UAM['INFOMAIL_SUBJECT']))
    1175       {
    1176         // Management of Extension flags ([username], [mygallery])
    1177         // -------------------------------------------------------
    1178         $patterns[] = '#\[username\]#i';
    1179         $replacements[] = $username;
    1180         $patterns[] = '#\[mygallery\]#i';
    1181         $replacements[] = $conf['gallery_title'];
    1182 
    1183         if (function_exists('get_user_language_desc'))
    1184         {
    1185           $subject = get_user_language_desc(preg_replace($patterns, $replacements, $conf_UAM['INFOMAIL_SUBJECT']))."\n\n";
    1186         }
    1187         else $subject = l10n(preg_replace($patterns, $replacements, $conf_UAM['INFOMAIL_SUBJECT']))."\n\n";
    1188       }
    1189 
    1190       $password = !empty($password) ? $password : l10n('UAM_no_update_pwd');
    1191 
    1192       if (isset($conf_UAM['MAILINFO_TEXT']) and !empty($conf_UAM['MAILINFO_TEXT']))
    1193       {
    1194         // Management of Extension flags ([username], [mygallery], [myurl])
    1195         // ----------------------------------------------------------------
    1196         $patterns[] = '#\[username\]#i';
    1197         $replacements[] = $username;
    1198         $patterns[] = '#\[mygallery\]#i';
    1199         $replacements[] = $conf['gallery_title'];
    1200         $patterns[] = '#\[myurl\]#i';
    1201         $replacements[] = get_gallery_home_url();
    1202 
    1203         if (function_exists('get_user_language_desc'))
    1204         {
    1205           $infos1_perso = get_user_language_desc(preg_replace($patterns, $replacements, $conf_UAM['MAILINFO_TEXT']))."\n\n";
    1206         }
    1207         else $infos1_perso = l10n(preg_replace($patterns, $replacements, $conf_UAM['MAILINFO_TEXT']))."\n\n";
    1208       }
    1209 
    1210       if (isset($conf_UAM['MAIL_INFO']) and $conf_UAM['MAIL_INFO'] == 'true')
    1211       {
    1212         if (isset($conf_UAM['HIDEPASSW']) and $conf_UAM['HIDEPASSW'] == 'true') // Allow display of clear password in email
    1213         {
    1214           $infos1 = array(
    1215             get_l10n_args('UAM_infos_mail %s', stripslashes($username)),
    1216             get_l10n_args('UAM_User: %s', stripslashes($username)),
    1217             get_l10n_args('UAM_Password: %s', $password),
    1218             get_l10n_args('Email: %s', $email),
    1219             get_l10n_args('', ''),
    1220           );
    1221         }
    1222         else // Do not allow display of clear password in email
    1223         {
    1224           $infos1 = array(
    1225             get_l10n_args('UAM_infos_mail %s', stripslashes($username)),
    1226             get_l10n_args('UAM_User: %s', stripslashes($username)),
    1227             get_l10n_args('Email: %s', $email),
    1228             get_l10n_args('', ''),
    1229           );
    1230         }
    1231       }
    1232 
    1233       break;
    12341166  }
    12351167
     
    12661198  }
    12671199
     1200//       $converted_res = ($confirm) ? 'true' : 'false';
     1201//       UAMLog($typemail,$converted_res,$conf_UAM['CONFIRM_MAIL'],$subject);
     1202
    12681203// Sending the email with subject and contents
    12691204// -------------------------------------------
    1270   if ((isset($conf_UAM['CONFIRM_MAIL']) and $conf_UAM['CONFIRM_MAIL'] == 'local') and $confirm)
    1271         {
     1205
     1206  if ((isset($conf_UAM['CONFIRM_MAIL']) and $conf_UAM['CONFIRM_MAIL'] == 'local') and $confirm) // Confirmation email send to admins
     1207  {
    12721208    switch_lang_to(get_default_language());
    12731209
    1274         load_language('plugin.lang', UAM_PATH);
    1275         $subject = get_l10n_args('UAM_Subject admin validation for %s',$username);
    1276 
    1277                 $content = array(
    1278         get_l10n_args('UAM_Manual_validation_needed_for %s', stripslashes($username)),
    1279      get_l10n_args('', ''),
    1280      get_l10n_args('UAM_Link: %s', AddConfirmMail($id, $email)),
     1210    load_language('plugin.lang', UAM_PATH);
     1211    $subject_admin = get_l10n_args('UAM_Subject admin validation for %s',$username);
     1212
     1213    $content_admin = array(
     1214      get_l10n_args('UAM_Manual_validation_needed_for %s', stripslashes($username)),
     1215      get_l10n_args('', ''),
     1216      get_l10n_args('UAM_Link: %s', AddConfirmMail($id, $email)),
    12811217    );
    12821218
    1283     //UAM_mail_notification_admins($subject, $content);
    1284     pwg_mail_notification_admins($subject, $content, true);
    1285   }
    1286         elseif ((isset($conf_UAM['CONFIRM_MAIL']) and $conf_UAM['CONFIRM_MAIL'] == 'true') and $confirm)
     1219    pwg_mail_notification_admins($subject_admin, $content_admin, true);
     1220  }
     1221
     1222
     1223        if ((isset($conf_UAM['CONFIRM_MAIL']) and $conf_UAM['CONFIRM_MAIL'] == 'true') and $confirm) // Confirmation email send to users
    12871224        {
    12881225          // Adding gallery URL at the end of the email
    12891226    if (isset($conf_UAM['ADD_GALLERY_URL_TO_EMAILS']) and $conf_UAM['ADD_GALLERY_URL_TO_EMAILS'] == 'true')
    12901227    {
    1291       $content = (isset($infos1) ? $infos1_perso.l10n_args($infos1)."\n\n" : "").(isset($infos2) ? $infos2_perso.l10n_args($infos2)."\n\n" : "").get_absolute_root_url();
     1228      $content_confirmation = (isset($infos1) ? $infos1_perso.l10n_args($infos1)."\n\n" : "").(isset($infos2) ? $infos2_perso.l10n_args($infos2)."\n\n" : "").get_absolute_root_url();
    12921229
    12931230      pwg_mail(
     
    12971234          ),
    12981235        array(
    1299           'content' => $content,
     1236          'content' => $content_confirmation,
    13001237          'content_format' => 'text/plain',
    13011238          'subject' => $subject,
     
    13061243    elseif (isset($conf_UAM['ADD_GALLERY_URL_TO_EMAILS']) and $conf_UAM['ADD_GALLERY_URL_TO_EMAILS'] == 'false')
    13071244    {
    1308       $content = (isset($infos1) ? $infos1_perso.l10n_args($infos1)."\n\n" : "").(isset($infos2) ? $infos2_perso.l10n_args($infos2)."\n\n" : "");
     1245      $content_confirmation = (isset($infos1) ? $infos1_perso.l10n_args($infos1)."\n\n" : "").(isset($infos2) ? $infos2_perso.l10n_args($infos2)."\n\n" : "");
    13091246
    13101247      pwg_mail(
     
    13141251          ),
    13151252        array(
    1316           'content' => $content,
     1253          'content' => $content_confirmation,
    13171254          'content_format' => 'text/plain',
    13181255          'subject' => $subject,
     
    13231260    else
    13241261    {
    1325       $content = (isset($infos1) ? $infos1_perso.l10n_args($infos1)."\n\n" : "").(isset($infos2) ? $infos2_perso.l10n_args($infos2)."\n\n" : "");
     1262      $content_confirmation = (isset($infos1) ? $infos1_perso.l10n_args($infos1)."\n\n" : "").(isset($infos2) ? $infos2_perso.l10n_args($infos2)."\n\n" : "");
    13261263
    13271264      pwg_mail(
     
    13311268          ),
    13321269        array(
    1333           'content' => $content,
     1270          'content' => $content_confirmation,
     1271          'content_format' => 'text/plain',
     1272          'subject' => $subject,
     1273          )
     1274        );
     1275    }
     1276        }
     1277
     1278
     1279        if ((isset($conf_UAM['MAIL_INFO']) and $conf_UAM['MAIL_INFO'] == 'true') and $typemail <> 1) // Information email send to users
     1280        {
     1281          // Adding gallery URL at the end of the email
     1282    if (isset($conf_UAM['ADD_GALLERY_URL_TO_EMAILS']) and $conf_UAM['ADD_GALLERY_URL_TO_EMAILS'] == 'true')
     1283    {
     1284      $content_info = (isset($infos1) ? $infos1_perso.l10n_args($infos1)."\n\n" : "").(isset($infos2) ? $infos2_perso.l10n_args($infos2)."\n\n" : "").get_absolute_root_url();
     1285
     1286      pwg_mail(
     1287        array(
     1288          'name' => stripslashes($username),
     1289          'email' => $email,
     1290          ),
     1291        array(
     1292          'content' => $content_info,
     1293          'content_format' => 'text/plain',
     1294          'subject' => $subject,
     1295          )
     1296        );
     1297    }
     1298    // Do not add gallery URL at the end of the email
     1299    elseif (isset($conf_UAM['ADD_GALLERY_URL_TO_EMAILS']) and $conf_UAM['ADD_GALLERY_URL_TO_EMAILS'] == 'false')
     1300    {
     1301      $content_info = (isset($infos1) ? $infos1_perso.l10n_args($infos1)."\n\n" : "").(isset($infos2) ? $infos2_perso.l10n_args($infos2)."\n\n" : "");
     1302
     1303      pwg_mail(
     1304        array(
     1305          'name' => stripslashes($username),
     1306          'email' => $email,
     1307          ),
     1308        array(
     1309          'content' => $content_info,
     1310          'content_format' => 'text/plain',
     1311          'subject' => $subject,
     1312          )
     1313        );
     1314    }
     1315    // By default do not add gallery URL at the end of the email
     1316    else
     1317    {
     1318      $content_info = (isset($infos1) ? $infos1_perso.l10n_args($infos1)."\n\n" : "").(isset($infos2) ? $infos2_perso.l10n_args($infos2)."\n\n" : "");
     1319
     1320      pwg_mail(
     1321        array(
     1322          'name' => stripslashes($username),
     1323          'email' => $email,
     1324          ),
     1325        array(
     1326          'content' => $content_info,
    13341327          'content_format' => 'text/plain',
    13351328          'subject' => $subject,
  • extensions/UserAdvManager/branches/2.6/language/en_UK/plugin.lang.php

    r25101 r26948  
    268268            Goals: Inform the visitor that the registration is awaiting approval by displaying a personal block on the home page of the gallery, and this, as registration is not approved.
    269269            <br/><br/>
    270             <b>Recall: In standard operation, the "Guest" only sees the public categories, without information message.</b>
    271             </li><br/><br/>
    272             <li>
    273 Prerequisite:<br/>
    274 - A gallery with all or some private categories, visible only by registered users<br/>
    275 - At least 2 following Piwigo\'s users groups: "Waiting," without permission on private categories, and "Confirmed" with all the permissions on the private categories<br/>
    276 - UAM plugin<br/>
    277 - PWG Stuffs plugin, for adding a special UAM module<br/>
    278 - Optionally, the plugin Extended Description to support multi-languages<br/>
    279             </li><br/><br/>
    280             <li>
    281 Stages:<br/><br/>
     270            <b>Recall: In standard operation, the "Guest" only sees the public albums, without information message.</b>
     271            </li>
     272            <li style="list-style-type: none;"><br/></li>
     273            <li>
     274Prerequisite:
     275<li style="list-style-type: none;"></li>
     276<li style="list-style-type: none;">- A gallery with all or some private albums, visible only by registered users</li>
     277<li style="list-style-type: none;">- At least 2 following Piwigo\'s users groups: "Waiting," without permission on private albums, and "Confirmed" with all the permissions on the private albums</li>
     278<li style="list-style-type: none;">- UAM plugin</li>
     279<li style="list-style-type: none;">- PWG Stuffs plugin, for adding a special UAM module</li>
     280<li style="list-style-type: none;">- Optionally, the plugin Extended Description to support multi-languages</li>
     281            </li>
     282            <li style="list-style-type: none;"><br/></li>
     283            <li>
     284Stages:
     285<li style="list-style-type: none;"></li>
    282286A. In plugin UAM:
    283287              <ol>
     
    289293                <li>Save the plugin configuration</li>
    290294              </ol>
    291 <br/>
     295<li style="list-style-type: none;"><br/></li>
    292296B. In plugin PWG Stuffs :
    293297              <ol>
     
    306310            <li>
    307311            Goals: Inform the visitor that the registration is awaiting confirmation by posting an additional page replacing the standard index page gallery at each of these connections, and this, as registration is not approved.
    308             <br/><br/>
     312            <li style="list-style-type: none;"></li>
    309313            Advantages over the method with PWG_Stuffs: Allow formatting information and displaying the information immediately upon registration of visitors.
    310             </li><br/><br/>
    311             <li>
    312 Prerequisite:<br/>
    313 - A gallery with all or some private categories, visible only by registered users<br/>
    314 - At least 2 following Piwigo\'s users groups: "Waiting," without permission on private categories, and "Confirmed" with all the permissions on the private categories<br/>
    315 - UAM plugin<br/>
    316 - Additional Pages plugin for adding and managing an additional page to replace the default index page of the gallery<br/>
    317 - Optionally, the plugin Extended Description to support multi-languages<br/>
    318             </li><br/><br/>
    319             <li>
    320 Stages:<br/><br/>
     314            </li>
     315            <li style="list-style-type: none;"><br/></li>
     316            <li>
     317Prerequisite:
     318<li style="list-style-type: none;"></li>
     319<li style="list-style-type: none;">- A gallery with all or some private albums, visible only by registered users</li>
     320<li style="list-style-type: none;">- At least 2 following Piwigo\'s users groups: "Waiting," without permission on private albums, and "Confirmed" with all the permissions on the private albums</li>
     321<li style="list-style-type: none;">- UAM plugin</li>
     322<li style="list-style-type: none;">- Additional Pages plugin for adding and managing an additional page to replace the default index page of the gallery</li>
     323<li style="list-style-type: none;">- Optionally, the plugin Extended Description to support multi-languages</li>
     324            </li>
     325            <li style="list-style-type: none;"><br/></li>
     326            <li>
     327Stages:
     328<li style="list-style-type: none;"></li>
    321329A. In plugin UAM:
    322330              <ol>
     
    327335                <li>Save the plugin configuration</li>
    328336              </ol>
    329 <br/>
    330 B. In plugin Additional Pages:<br/>
     337<li style="list-style-type: none;"><br/></li>
     338B. In plugin Additional Pages:
     339<li style="list-style-type: none;"></li>
    331340                <b>NOTE : The management of access rights for groups on Additional Pages must be turned on (see plugin configuration settings).</b>
    332                 <br/>
     341                <li style="list-style-type: none;"></li>
    333342              <ol>
    334343                <li>Add a new page with at least the following parameters:</li>
     
    403412$lang['UAM_AddURL2Mail'] = 'Add the URL of the gallery at the end of emails (like a signature)';
    404413$lang['UAM_Follow this link to access the gallery'] = 'Please, follow this link to access the gallery';
     414
     415$lang['UAM_Select page size'] = 'Select page size';
     416$lang['UAM_Select page number'] = 'Select page number';
    405417?>
  • extensions/UserAdvManager/branches/2.6/language/fr_FR/plugin.lang.php

    r25101 r26948  
    105105$lang['UAM_GT_Init'] = 'Initialisation du Ghost Tracker';
    106106$lang['UAM_GhostTracker_Title'] = 'Gestion des visiteurs fantômes';
    107 $lang['UAM_GhostTracker_Init'] = 'A première activation de cette fonction, ou à sa réactivation après une longue période pendant laquelle de nouveaux visiteurs se sont inscrits, il convient d\'initialiser ou de réinitialiser le Ghost Tracker. Cette action n\'est à faire qu\'une seule fois après activation ou réactivation de l\'option; à cet effet, cliquez <u>une seule fois</u> sur le bouton d\'initialisation ci-dessous.</b>';
     107$lang['UAM_GhostTracker_Init'] = 'A la première activation de cette fonction, ou à sa réactivation après une longue période pendant laquelle de nouveaux visiteurs se sont inscrits, il convient d\'initialiser ou de réinitialiser le Ghost Tracker. Cette action n\'est à faire qu\'une seule fois après activation ou réactivation de l\'option; à cet effet, cliquez <u>une seule fois</u> sur le bouton d\'initialisation ci-dessous.';
    108108/* Mailing */
    109109$lang['UAM_Add of %s'] = 'Profil créé pour %s';
     
    262262            <br/><br/>
    263263            Avantages par rapport à la méthode avec PWG_Stuffs : Permettre une information mise en forme et moins austère et afficher immédiatement l\'information dès l\'inscription des visiteurs.
    264             </li><br/><br/>
    265             <li>
    266 Pré-requis:<br/>
    267 - Une galerie avec tout ou partie des catégories privées, visibles par les seuls utilisateurs inscrits<br/>
    268 - Au moins les 2 groupes d\'utilisateurs Piwigo suivants : "Attente", sans aucune permission sur les catégories privées, et "Confirmés", avec toutes les permissions sur les catégories privées<br/>
    269 - Le plugin UAM<br/>
    270 - Le plugin Additional Pages, pour l\'ajout et la gestion d\'une page additionnelle remplaçant la page d\'index par défaut de la galerie<br/>
    271 - En option, le plugin Extended Description, pour le support multi-langues<br/>
    272             </li><br/><br/>
    273             <li>
    274 Réalisation:<br/><br/>
    275 A. Dans le plugin UAM:<br/>
     264            </li>
     265            <li style="list-style-type: none;"><br/></li>
     266            <li>
     267Pré-requis:
     268<li style="list-style-type: none;"></li>
     269<li style="list-style-type: none;">- Une galerie avec tout ou partie des albums privées, visibles par les seuls utilisateurs inscrits</li>
     270<li style="list-style-type: none;">- Au moins les 2 groupes d\'utilisateurs Piwigo suivants : "Attente", sans aucune permission sur les albums privées, et "Confirmés", avec toutes les permissions sur les albums privées</li>
     271<li style="list-style-type: none;">- Le plugin UAM</li>
     272<li style="list-style-type: none;">- Le plugin Additional Pages, pour l\'ajout et la gestion d\'une page additionnelle remplaçant la page d\'index par défaut de la galerie</li>
     273<li style="list-style-type: none;">- En option, le plugin Extended Description, pour le support multi-langues</li>
     274            </li>
     275            <li style="list-style-type: none;"><br/></li>
     276            <li>
     277Réalisation:
     278<li style="list-style-type: none;"></li>
     279A. Dans le plugin UAM:
     280<li style="list-style-type: none;"></li>
    276281              <ol>
    277282                <li>Activer la confirmation d\'inscription</li>
     
    281286                <li>Enregistrer la configuration du plugin</li>
    282287              </ol>
    283 <br/>
    284 B. Dans le plugin Additional Pages:<br/>
     288<li style="list-style-type: none;"><br/></li>
     289B. Dans le plugin Additional Pages:
     290<li style="list-style-type: none;"></li>
    285291                <b>NOTE : La gestion des droits d\'accès aux pages additionelles pour les groupes doit être activée (voir configuration du plugin Additional Pages).</b>
    286292                <br/>
     
    312318            <li>
    313319            Objectifs : Informer le visiteur que son inscription est en attente de confirmation en affichant un bloc personnel sur la page d\'accueil de la galerie; et ce, tant que l\'inscription n\'est pas confirmée.<br/><br/>
    314             <b>Rappel: En fonctionnement standard, le "Guest" ne voit que les catégories publiques, sans message d\'information.</b>
    315             </li><br/><br/>
    316             <li>
    317 Pré-requis:<br/>
    318 - Une galerie avec tout ou partie des catégories privées, visibles par les seuls utilisateurs inscrits<br/>
    319 - Au moins les 2 groupes d\'utilisateurs Piwigo suivants : "Attente", sans aucune permission sur les catégories privées, et "Confirmés", avec toutes les permissions sur les catégories privées<br/>
    320 - Le plugin UAM<br/>
    321 - Le plugin PWG Stuffs, pour l\'ajout d\'un module spécial UAM<br/>
    322 - En option, le plugin Extended Description, pour le support multi-langues<br/>
    323             </li><br/><br/>
    324             <li>
    325 Réalisation:<br/><br/>
    326 A. Dans le plugin UAM:<br/>
     320            <b>Rappel: En fonctionnement standard, le "Guest" ne voit que les albums publics, sans message d\'information.</b>
     321            </li>
     322            <li style="list-style-type: none;"><br/></li>
     323            <li>
     324Pré-requis:
     325<li style="list-style-type: none;"></li>
     326<li style="list-style-type: none;">- Une galerie avec tout ou partie des albums privées, visibles par les seuls utilisateurs inscrits</li>
     327<li style="list-style-type: none;">- Au moins les 2 groupes d\'utilisateurs Piwigo suivants : "Attente", sans aucune permission sur les albums privées, et "Confirmés", avec toutes les permissions sur les albums privées</li>
     328<li style="list-style-type: none;">- Le plugin UAM</li>
     329<li style="list-style-type: none;">- Le plugin PWG Stuffs, pour l\'ajout d\'un module spécial UAM</li>
     330<li style="list-style-type: none;">- En option, le plugin Extended Description, pour le support multi-langues</li>
     331            </li>
     332            <li style="list-style-type: none;"><br/></li>
     333            <li>
     334Réalisation:
     335<li style="list-style-type: none;"></li>
     336A. Dans le plugin UAM:
     337<li style="list-style-type: none;"></li>
    327338              <ol>
    328339                <li>Activer la confirmation d\'inscription</li>
     
    333344                <li>Enregistrer la configuration du plugin</li>
    334345              </ol>
    335 <br/>
    336 B. Dans le plugin PWG Stuffs:<br/>
     346<li style="list-style-type: none;"><br/></li>
     347B. Dans le plugin PWG Stuffs:
     348<li style="list-style-type: none;"></li>
    337349              <ol>
    338350                <li>Aller dans l\'onglet "Ajouter un nouveau bloc"</li>
     
    403415$lang['UAM_AddURL2Mail'] = 'Ajouter l\'URL de la galerie à la fin des emails (comme une signature)';
    404416$lang['UAM_Follow this link to access the gallery'] = 'Veuillez utiliser ce lien pour vous connecter';
     417
     418$lang['UAM_Select page size'] = 'Choisir la taille des pages';
     419$lang['UAM_Select page number'] = 'Sélectionner une page';
    405420?>
  • extensions/UserAdvManager/branches/2.6/language/pt_BR/help.lang.php

    r25664 r26948  
    7777<br/><br/>
    7878Se esta opção e a opção "Lembrar os usuários não confirmados" são ativadas, novas opções aparecem em baixo nesta seção para permitir a automação de gestão de usuários não confirmados.';
     79$lang['UAM_miscTitle_d'] = '- Usuários registrados monitoramento <br/>
     80- Apelido obrigatório para comentário de visitantes <br/>
     81...';
     82$lang['UAM_infomailTitle_d'] = 'Esta opção permite automatizar o envio de um e-mail de informações de um usuário quando ele muda de senha ou endereço de e-mail em sua página de perfil. <br/><bb/>
     83O conteúdo da mensagem enviada é composta por uma parte customizável a introduzir uma pequena nota de boas-vindas e uma parte fixa, indicando o nome de login, senha e endereço de e-mail do usuário.';
     84$lang['UAM_confirmstatTitle'] = 'Estatutos';
     85$lang['UAM_confirmTitle_d'] = '- Informações geração email <br/>
     86- Registrar confirmação geração email <br/>
     87- Grupos, estado ou nível de privacidade auto registro <br/>
     88- Prazo para a confirmação do registro <br/>
     89- Geração email Lembrete
     90...';
     91$lang['UAM_USRAutoMailTitle_d'] = 'Quando ativada, esta função irá enviar automaticamente conteúdo personalizado em "Lembrete de e-mail com uma nova chave gerada" aos visitantes que correspondem aos critérios.';
     92$lang['UAM_Tracking registered users_d'] = 'Isso ativa uma tabela na guia "de rastreamento de usuários registrados", onde são listados os utilizadores registados na galeria com a data de sua última visita e tempo gasto (em dias) desde a sua última visita. O monitoramento é puramente informativo para o administrador da galeria.';
     93$lang['UAM_StuffsTitle_d'] = 'Isso permite que um bloco UAM adicional no PWG Stuffs plugin (se instalado), informar os visitantes que não confirmaram sua inscrição sobre suas condição.
     94<br/><br/>
     95Consulte <b>Dicas e Exemplos de Uso</b> na parte inferior desta página para mais detalhes.';
     96$lang['UAM_HidePasswTitle_d'] = 'Escolha esta opção se você deseja exibir a senha escolhida pelo visitante no e-mail informações. Se você ativar a opção, a senha irá aparecer em texto claro. Se você desativar a senha não vai aparecer.';
    7997?>
  • extensions/UserAdvManager/branches/2.6/language/pt_PT/description.txt

    r24203 r26948  
    1 Reforço das possibilidades de gerenciamento de utilizadores
     1Reforço das capacidades de gestão de utilizadores
  • extensions/UserAdvManager/branches/2.6/language/pt_PT/help.lang.php

    r25809 r26948  
    317317$lang['UAM_infomailTitle_d'] = 'Esta opção permite automatizar o envio de um e-mail informação para um utilizador quando ele altera a senha ou endereço de e-mail no seu perfil. <br/>
    318318O conteúdo da mensagem a enviar é composto por uma parte personalizável para introduzir uma pequena nota de boas-vindas e uma parte fixa, indicando o nome de login, senha e endereço de e-mail do utilizador.';
     319$lang['UAM_confirmstatTitle_d'] = '<b style=&quot;color: red;&quot;>AVISO : A utilização de estatutos na confirmação requer que tenha mantido o estatuto &quot;Guest (Visitante)&quot; como o estatuto por defeito (no modelo de utilizador) para os novos registos. Note que também pode definir outro tipo de utilizador como defeito. Por favor reveja a documentação do Piwigo para mais detalhes.</b><br/><br/>
     320Os estatutos são validados para utilização conjunta com a &quot;Confirmação de registo&quot;';
     321$lang['UAM_confirmlevelTitle_d'] = '<b style=&quot;color: red;&quot;>AVISO : A utilização do nível de privacidade requere que já o tenha utilizado com as suas imagens. Por favor reveja a documentação do Piwigo para mais detalhes.</b><br/><br/>
     322O nível de privacidade é validado para utilização conjunta com a &quot;Confirmação de registo&quot;';
     323$lang['UAM_confirmgrpTitle_d'] = '<b style=&quot;color: red;&quot;>AVISO : A utilização de grupos na confirmação requer que tenha criado pelo menos um grupo de utilizadores e que este esteja definido como grupo &quot;por defeito&quot; na gestão de grupos de utilizadores do Piwigo.</b><br/><br/>
     324os grupos são validados para utilização conjunta com a &quot;Confirmação de registo&quot;
     325';
     326$lang['UAM_carexcTitle_d'] = 'Pode ser interessante a proibição da utilização de certos caracteres nos nomes de utilizador (exemplo: recusar nomes de login que contenham @). Esta opção permite excluir caracteres ou sequências de caracteres.<br/>
     327NB: A opção pode também excluir palavras inteiras
     328<br/><br/>
     329<b style=&quot;color: red;&quot;>Aviso: esta opção não tem efeito sobre os nomes de utilizador criados à ativação da mesma.</b>';
    319330?>
Note: See TracChangeset for help on using the changeset viewer.