<?php

/**
* SelGridView class file.
* @author Vitaliy Potapov <noginsk@rambler.ru>
*/

Yii::import('zii.widgets.grid.CGridView');
Yii::import('zii.widgets.grid.CCheckBoxColumn');

/**
* SelGridView v1.1
* 
* SelGridView remembers selected rows of gridview when sorting or navigating by pages. Also it can select particular rows by GET param when page is loading
*
* Usage:
* Just put this file to extensions directory, create widget as usual and enjoy
*  
* <pre>
* $this->widget('application.extensions.SelGridView', array(
* ...
* ));
* </pre>
*/

class SelCheckboxColumn extends CCheckBoxColumn
{
    public function renderFilterCell()
    {
        //
    }
}

class SelGridView extends CGridView
{
    /**
    * GET param name to pass selected row(s)
    * 
    * @var mixed
    */
    public $selVar;
    
    /**
    * Creates column objects and initializes them. 
    * Automatically add hidden checkbox column.
    */
    protected function initColumns()
    {
         parent::initColumns();
         
         if($this->selectableRows == 0) return;

         if(empty($this->selVar)) {
             $this->selVar = $this->dataProvider->getId().'_sel';
         }
         
         //if gridview already has user defined Checkbox column --> exit
         foreach($this->columns as $col) {
             if($col instanceof CCheckBoxColumn) return;
         }
         
         if($this->dataProvider instanceOf CActiveDataProvider) {
             $primaryKey = '$data->primaryKey';
         } elseif($this->dataProvider instanceOf CArrayDataProvider) {
             $primaryKey = '$data["'.$this->dataProvider->keyField.'"]';
         } else {
             throw new CException('Unsupported DataProvider!'); 
         }

         //creating hidden checkbox column
         $checkboxColumn = new SelCheckboxColumn($this);   
         $checkboxColumn->checked = 'isset($_GET["'.$this->selVar.'"]) ? in_array('.$primaryKey.', is_array($_GET["'.$this->selVar.'"]) ? $_GET["'.$this->selVar.'"] : array($_GET["'.$this->selVar.'"])) : false;';
         $checkboxColumn->htmlOptions = array('style'=>'display:none');
         $checkboxColumn->headerHtmlOptions = array('style'=>'display:none');
         $checkboxColumn->footerHtmlOptions = array('style'=>'display:none');         
         $checkboxColumn->init(); 
                     
         $this->columns[] = $checkboxColumn;
    }
    
    /**
    * Registers necessary client scripts. 
    * Automaticlly prepend user's beforeajaxUpdate with needed code that will modify GET params when navigating and sorting
    */
    public function registerClientScript()
    {
        if($this->selectableRows > 0) $this->applyBeforeAjaxUpdate();
        parent::registerClientScript(); 
    }

    /**
    * prepending user's beforeajaxUpdate with needed code
    * 
    */
    protected function applyBeforeAjaxUpdate()
    {
         $funcChangeQuery = '
           function(id, options) {
               if(this.selectableRows == 0) return;

               //all GET params in current url
               var params = $.deparam.querystring(options.url);                   
               
               //GET param named "{ModelClass}_sel" showing selected rows in whole grid (not only current page)
               var selVar = "'.$this->selVar.'";
               var checkedInQuery = (params[selVar]) ? params[selVar] : [];
               if(!$.isArray(checkedInQuery)) checkedInQuery = [checkedInQuery];
               
               //rows selected on current page
               var checkedInPage = $.fn.yiiGridView.getSelection(id);

               //if only one row can be selected - logic is simple: if any row selected on current page, clear all previously selected
               if(this.selectableRows == 1) {
                    if(!checkedInPage.length) return;
                    params[selVar] = checkedInPage;
                    options.url = $.param.querystring(options.url, params); 
                    return;           
               } 
               
               //iterating all keys of this page
               $("#"+id).find(".keys span").each(function (i) {
                    var key = $(this).text();
                    
                    //row found in GET params
                    var indexInQuery = $.inArray(key, checkedInQuery);
                    
                    //row is checked on current page
                    var indexInChecked = $.inArray(key, checkedInPage);
                    
                    //row is selected and not in GET params --> adding to GET params
                    if(indexInChecked >= 0 && indexInQuery == -1) {
                       checkedInQuery.push(key); 
                    }
                    
                    //row not selected, but in GET params due to selected previously --> deleting from GET params
                    if(indexInChecked == -1 && indexInQuery >= 0) {
                       delete checkedInQuery[indexInQuery]; 
                    }
               });     
               
               if(!checkedInQuery) {
                   if(params[selVar]) delete params[selVar];
               } else {
                   params[selVar] = checkedInQuery;
               }
                         
               options.url = $.param.querystring( options.url, params);
           }
       ';

       if(empty($this->beforeAjaxUpdate)) {
           //no user defined ajaxUpdate handler. Simply use SelGridView's
           $this->beforeAjaxUpdate = $funcChangeQuery;
       } else {
                      
           //beforeAjaxUpdate = function or direct JS code                       
           if(preg_match('/(function.+\})/is', $this->beforeAjaxUpdate, $matches)) {
               $userScript = '('.$matches[0].')(id, options);';
           } else {
               $userScript =  $this->beforeAjaxUpdate; 
           }
           
           //merge to one beforeAjaxUpdate function
           $this->beforeAjaxUpdate = '
           function(id, options) {  
               ('.$funcChangeQuery.')(id, options); 
               '.$userScript.'
           }
          ';  
       }
    }
}
