Current File : /home/inlingua/www/crm.vprotectindia.com/vendor/yajra/laravel-datatables-html/src/Html/Builder.php
<?php

namespace Yajra\DataTables\Html;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Collective\Html\HtmlBuilder;
use Illuminate\Support\Collection;
use Illuminate\Support\HtmlString;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Traits\Macroable;
use Illuminate\Contracts\Config\Repository;

class Builder
{
    use Macroable;

    /**
     * @var Collection
     */
    public $collection;

    /**
     * @var Repository
     */
    public $config;

    /**
     * @var Factory
     */
    public $view;

    /**
     * @var HtmlBuilder
     */
    public $html;

    /**
     * @var string|array
     */
    protected $ajax = '';

    /**
     * @var array
     */
    protected $tableAttributes = [];

    /**
     * @var string
     */
    protected $template = '';

    /**
     * @var array
     */
    protected $attributes = [];

    /**
     * Collection of Editors.
     *
     * @var null|Editor
     */
    protected $editors = [];

    /**
     * @param Repository $config
     * @param Factory $view
     * @param HtmlBuilder $html
     */
    public function __construct(Repository $config, Factory $view, HtmlBuilder $html)
    {
        $this->config          = $config;
        $this->view            = $view;
        $this->html            = $html;
        $this->collection      = new Collection;
        $this->tableAttributes = $this->config->get('datatables-html.table', []);
    }

    /**
     * Generate DataTable javascript.
     *
     * @param  null $script
     * @param  array $attributes
     * @return \Illuminate\Support\HtmlString
     */
    public function scripts($script = null, array $attributes = ['type' => 'text/javascript'])
    {
        $script     = $script ?: $this->generateScripts();
        $attributes = $this->html->attributes($attributes);

        return new HtmlString("<script{$attributes}>{$script}</script>\n");
    }

    /**
     * Get generated raw scripts.
     *
     * @return \Illuminate\Support\HtmlString
     */
    public function generateScripts()
    {
        $parameters = $this->generateJson();

        return new HtmlString(
            sprintf($this->template(), $this->getTableAttribute('id'), $parameters)
        );
    }

    /**
     * Get generated json configuration.
     *
     * @return string
     */
    public function generateJson()
    {
        $args = array_merge(
            $this->attributes, [
                'ajax'    => $this->ajax,
                'columns' => $this->collection->map(function (Column $column) {
                    $column = $column->toArray();
                    unset($column['attributes']);

                    return $column;
                })->toArray(),
            ]
        );

        return $this->parameterize($args);
    }

    /**
     * Generate DataTables js parameters.
     *
     * @param  array $attributes
     * @return string
     */
    public function parameterize($attributes = [])
    {
        $parameters = (new Parameters($attributes))->toArray();

        $values       = [];
        $replacements = [];

        foreach (array_dot($parameters) as $key => $value) {
            if ($this->isCallbackFunction($value, $key)) {
                $values[] = trim($value);
                array_set($parameters, $key, '%' . $key . '%');
                $replacements[] = '"%' . $key . '%"';
            }
        }

        foreach ($parameters as $key => $value) {
            array_set($new, $key, $value);
        }

        $json = json_encode($new);

        $json = str_replace($replacements, $values, $json);

        return $json;
    }

    /**
     * Check if given key & value is a valid callback js function.
     *
     * @param string $value
     * @param string $key
     * @return bool
     */
    protected function isCallbackFunction($value, $key)
    {
        if (empty($value)) {
            return false;
        }

        return Str::startsWith(trim($value),
                $this->config->get('datatables-html.callback', ['$', '$.', 'function'])) || Str::contains($key,
                'editor');
    }

    /**
     * Get javascript template to use.
     *
     * @return string
     */
    protected function template()
    {
        $template = $this->template ?: $this->config->get('datatables-html.script', 'datatables::script');

        return $this->view->make($template, ['editors' => $this->editors])->render();
    }

    /**
     * Retrieves HTML table attribute value.
     *
     * @param string $attribute
     * @return mixed
     * @throws \Exception
     */
    public function getTableAttribute($attribute)
    {
        if (! array_key_exists($attribute, $this->tableAttributes)) {
            throw new \Exception("Table attribute '{$attribute}' does not exist.");
        }

        return $this->tableAttributes[$attribute];
    }

    /**
     * Get table computed table attributes.
     *
     * @return array
     */
    public function getTableAttributes()
    {
        return $this->tableAttributes;
    }

    /**
     * Sets multiple HTML table attributes at once.
     *
     * @param array $attributes
     * @return $this
     */
    public function setTableAttributes(array $attributes)
    {
        foreach ($attributes as $attribute => $value) {
            $this->tableAttributes[$attribute] = $value;
        }

        return $this;
    }

    /**
     * Sets HTML table "id" attribute.
     *
     * @param string $id
     * @return $this
     */
    public function setTableId($id)
    {
        return $this->setTableAttribute('id', $id);
    }

    /**
     * Sets HTML table attribute(s).
     *
     * @param string|array $attribute
     * @param mixed $value
     * @return $this
     */
    public function setTableAttribute($attribute, $value = null)
    {
        if (is_array($attribute)) {
            return $this->setTableAttributes($attribute);
        }

        $this->tableAttributes[$attribute] = $value;

        return $this;
    }

    /**
     * Add class names to the "class" attribute of HTML table.
     *
     * @param string|array $class
     * @return $this
     */
    public function addTableClass($class)
    {
        $class        = is_array($class) ? implode(' ', $class) : $class;
        $currentClass = Arr::get(array_change_key_case($this->tableAttributes), 'class');

        $classes = preg_split('#\s+#', $currentClass . ' ' . $class, null, PREG_SPLIT_NO_EMPTY);
        $class   = implode(' ', array_unique($classes));

        return $this->setTableAttribute('class', $class);
    }

    /**
     * Remove class names from the "class" attribute of HTML table.
     *
     * @param string|array $class
     * @return $this
     */
    public function removeTableClass($class)
    {
        $class        = is_array($class) ? implode(' ', $class) : $class;
        $currentClass = Arr::get(array_change_key_case($this->tableAttributes), 'class');

        $classes = array_diff(
            preg_split('#\s+#', $currentClass, null, PREG_SPLIT_NO_EMPTY),
            preg_split('#\s+#', $class, null, PREG_SPLIT_NO_EMPTY)
        );
        $class = implode(' ', array_unique($classes));

        return $this->setTableAttribute('class', $class);
    }

    /**
     * Add a column in collection usingsl attributes.
     *
     * @param  array $attributes
     * @return $this
     */
    public function addColumn(array $attributes)
    {
        $this->collection->push(new Column($attributes));

        return $this;
    }

    /**
     * Add a Column object at the beginning of collection.
     *
     * @param \Yajra\DataTables\Html\Column $column
     * @return $this
     */
    public function addBefore(Column $column)
    {
        $this->collection->prepend($column);

        return $this;
    }

    /**
     * Add a column at the beginning of collection using attributes.
     *
     * @param  array $attributes
     * @return $this
     */
    public function addColumnBefore(array $attributes)
    {
        $this->collection->prepend(new Column($attributes));

        return $this;
    }

    /**
     * Add a Column object in collection.
     *
     * @param \Yajra\DataTables\Html\Column $column
     * @return $this
     */
    public function add(Column $column)
    {
        $this->collection->push($column);

        return $this;
    }

    /**
     * Set datatables columns from array definition.
     *
     * @param array $columns
     * @return $this
     */
    public function columns(array $columns)
    {
        $this->collection = new Collection;

        foreach ($columns as $key => $value) {
            if (! is_a($value, Column::class)) {
                if (is_array($value)) {
                    $attributes = array_merge(
                        [
                            'name' => $value['name'] ?? $value['data'] ?? $key,
                            'data' => $value['data'] ?? $key,
                        ],
                        $this->setTitle($key, $value)
                    );
                } else {
                    $attributes = [
                        'name'  => $value,
                        'data'  => $value,
                        'title' => $this->getQualifiedTitle($value),
                    ];
                }

                $this->collection->push(new Column($attributes));
            } else {
                $this->collection->push($value);
            }
        }

        return $this;
    }

    /**
     * Set title attribute of an array if not set.
     *
     * @param string $title
     * @param array $attributes
     * @return array
     */
    public function setTitle($title, array $attributes)
    {
        if (! isset($attributes['title'])) {
            $attributes['title'] = $this->getQualifiedTitle($title);
        }

        return $attributes;
    }

    /**
     * Convert string into a readable title.
     *
     * @param string $title
     * @return string
     */
    public function getQualifiedTitle($title)
    {
        return Str::title(str_replace(['.', '_'], ' ', Str::snake($title)));
    }

    /**
     * Add a checkbox column.
     *
     * @param  array $attributes
     * @param  bool|int $position true to prepend, false to append or a zero-based index for positioning
     * @return $this
     */
    public function addCheckbox(array $attributes = [], $position = false)
    {
        $attributes = array_merge([
            'defaultContent' => '<input type="checkbox" ' . $this->html->attributes($attributes) . '/>',
            'title'          => '<input type="checkbox" ' . $this->html->attributes($attributes + ['id' => 'dataTablesCheckbox']) . '/>',
            'data'           => 'checkbox',
            'name'           => 'checkbox',
            'orderable'      => false,
            'searchable'     => false,
            'exportable'     => false,
            'printable'      => true,
            'width'          => '10px',
        ], $attributes);
        $column = new Column($attributes);

        if ($position === true) {
            $this->collection->prepend($column);
        } elseif ($position === false || $position >= $this->collection->count()) {
            $this->collection->push($column);
        } else {
            $this->collection->splice($position, 0, [$column]);
        }

        return $this;
    }

    /**
     * Add a action column.
     *
     * @param  array $attributes
     * @param  bool  $prepend
     * @return $this
     */
    public function addAction(array $attributes = [], $prepend = false)
    {
        $attributes = array_merge([
            'defaultContent' => '',
            'data'           => 'action',
            'name'           => 'action',
            'title'          => 'Action',
            'render'         => null,
            'orderable'      => false,
            'searchable'     => false,
            'exportable'     => false,
            'printable'      => true,
            'footer'         => '',
        ], $attributes);

        if ($prepend) {
            $this->collection->prepend(new Column($attributes));
        } else {
            $this->collection->push(new Column($attributes));
        }

        return $this;
    }

    /**
     * Add a index column.
     *
     * @param  array $attributes
     * @return $this
     */
    public function addIndex(array $attributes = [])
    {
        $indexColumn = $this->config->get('datatables.index_column', 'DT_RowIndex');
        $attributes  = array_merge([
            'defaultContent' => '',
            'data'           => $indexColumn,
            'name'           => $indexColumn,
            'title'          => '',
            'render'         => null,
            'orderable'      => false,
            'searchable'     => false,
            'exportable'     => false,
            'printable'      => true,
            'footer'         => '',
        ], $attributes);
        $this->collection->push(new Column($attributes));

        return $this;
    }

    /**
     * Setup ajax parameter for datatables pipeline plugin.
     *
     * @param  string $url
     * @param  string $pages
     * @return $this
     */
    public function pipeline($url, $pages)
    {
        $this->ajax = "$.fn.dataTable.pipeline({ url: '{$url}', pages: {$pages} })";

        return $this;
    }

    /**
     * Setup "ajax" parameter with POST method.
     *
     * @param  string|array $attributes
     * @return $this
     */
    public function postAjax($attributes = '')
    {
        if (! is_array($attributes)) {
            $attributes = ['url' => (string) $attributes];
        }

        unset($attributes['method']);
        Arr::set($attributes, 'type', 'POST');
        Arr::set($attributes, 'headers.X-HTTP-Method-Override', 'GET');

        return $this->ajax($attributes);
    }

    /**
     * Setup ajax parameter.
     *
     * @param  string|array $attributes
     * @return $this
     */
    public function ajax($attributes = '')
    {
        $this->ajax = $attributes;

        return $this;
    }

    /**
     * Generate DataTable's table html.
     *
     * @param array $attributes
     * @param bool $drawFooter
     * @param bool $drawSearch
     * @return \Illuminate\Support\HtmlString
     */
    public function table(array $attributes = [], $drawFooter = false, $drawSearch = false)
    {
        $this->setTableAttributes($attributes);

        $th       = $this->compileTableHeaders();
        $htmlAttr = $this->html->attributes($this->tableAttributes);

        $tableHtml  = '<table ' . $htmlAttr . '>';
        $searchHtml = $drawSearch ? '<tr class="search-filter">' . implode('',
                $this->compileTableSearchHeaders()) . '</tr>' : '';
        $tableHtml .= '<thead><tr>' . implode('', $th) . '</tr>' . $searchHtml . '</thead>';
        if ($drawFooter) {
            $tf = $this->compileTableFooter();
            $tableHtml .= '<tfoot><tr>' . implode('', $tf) . '</tr></tfoot>';
        }
        $tableHtml .= '</table>';

        return new HtmlString($tableHtml);
    }

    /**
     * Compile table headers and to support responsive extension.
     *
     * @return array
     */
    private function compileTableHeaders()
    {
        $th = [];
        foreach ($this->collection->toArray() as $row) {
            $thAttr = $this->html->attributes(array_merge(
                array_only($row, ['class', 'id', 'width', 'style', 'data-class', 'data-hide']),
                $row['attributes']
            ));
            $th[] = '<th ' . $thAttr . '>' . $row['title'] . '</th>';
        }

        return $th;
    }

    /**
     * Compile table search headers.
     *
     * @return array
     */
    private function compileTableSearchHeaders()
    {
        $search = [];
        foreach ($this->collection->all() as $key => $row) {
            $search[] = $row['searchable'] ? '<th>' . (isset($row->search) ? $row->search : '') . '</th>' : '<th></th>';
        }

        return $search;
    }

    /**
     * Compile table footer contents.
     *
     * @return array
     */
    private function compileTableFooter()
    {
        $footer = [];
        foreach ($this->collection->all() as $row) {
            if (is_array($row->footer)) {
                $footerAttr = $this->html->attributes(array_only($row->footer,
                    ['class', 'id', 'width', 'style', 'data-class', 'data-hide']));
                $title    = isset($row->footer['title']) ? $row->footer['title'] : '';
                $footer[] = '<th ' . $footerAttr . '>' . $title . '</th>';
            } else {
                $footer[] = '<th>' . $row->footer . '</th>';
            }
        }

        return $footer;
    }

    /**
     * Configure DataTable's parameters.
     *
     * @param  array $attributes
     * @return $this
     */
    public function parameters(array $attributes = [])
    {
        $this->attributes = array_merge($this->attributes, $attributes);

        return $this;
    }

    /**
     * Get collection of columns.
     *
     * @return \Illuminate\Support\Collection
     */
    public function getColumns()
    {
        return $this->collection;
    }

    /**
     * Remove column by name.
     *
     * @param array $names
     * @return $this
     */
    public function removeColumn(...$names)
    {
        foreach ($names as $name) {
            $this->collection = $this->collection->filter(function (Column $column) use ($name) {
                return $column->name !== $name;
            })->flatten();
        }

        return $this;
    }

    /**
     * Minify ajax url generated when using get request
     * by deleting unnecessary url params.
     *
     * @param string $url
     * @param string $script
     * @param array $data
     * @param array $ajaxParameters
     * @return $this
     */
    public function minifiedAjax($url = '', $script = null, $data = [], $ajaxParameters = [])
    {
        $this->ajax = [];
        $appendData = $this->makeDataScript($data);

        $this->ajax['url']  = $url;
        $this->ajax['type'] = 'GET';
        if (isset($this->attributes['serverSide']) ? $this->attributes['serverSide'] : true) {
            $this->ajax['data'] = 'function(data) {
            for (var i = 0, len = data.columns.length; i < len; i++) {
                if (!data.columns[i].search.value) delete data.columns[i].search;
                if (data.columns[i].searchable === true) delete data.columns[i].searchable;
                if (data.columns[i].orderable === true) delete data.columns[i].orderable;
                if (data.columns[i].data === data.columns[i].name) delete data.columns[i].name;
            }
            delete data.search.regex;';
        } else {
            $this->ajax['data'] = 'function(data){';
        }

        if ($appendData) {
            $this->ajax['data'] .= $appendData;
        }

        if ($script) {
            $this->ajax['data'] .= $script;
        }

        $this->ajax['data'] .= '}';

        $this->ajax = array_merge($this->ajax, $ajaxParameters);

        return $this;
    }

    /**
     * Make a data script to be appended on ajax request of dataTables.
     *
     * @param array $data
     * @return string
     */
    protected function makeDataScript(array $data)
    {
        $script = '';
        foreach ($data as $key => $value) {
            $script .= PHP_EOL . "data.{$key} = '{$value}';";
        }

        return $script;
    }

    /**
     * Attach multiple editors to builder.
     *
     * @param mixed ...$editors
     * @return $this
     */
    public function editors(...$editors)
    {
        foreach ($editors as $editor) {
            $this->editor($editor);
        }

        return $this;
    }

    /**
     * Integrate with DataTables Editor.
     *
     * @param array|Editor $fields
     * @return $this
     */
    public function editor($fields)
    {
        $this->setTemplate($this->config->get('datatables-html.editor', 'datatables::editor'));

        $editor = $this->newEditor($fields);

        $this->editors[] = $editor;

        return $this;
    }

    /**
     * Set custom javascript template.
     *
     * @param string $template
     * @return $this
     */
    public function setTemplate($template)
    {
        $this->template = $template;

        return $this;
    }

    /**
     * @param array|Editor $fields
     * @throws \Exception
     */
    protected function newEditor($fields)
    {
        if ($fields instanceof Editor) {
            $editor = $fields;
        } else {
            $editor = new Editor;
            $editor->fields($fields);
        }

        if (! $editor->table) {
            $editor->table($this->getTableAttribute('id'));
        }

        if (! $editor->ajax) {
            $editor->ajax($this->getAjaxUrl());
        }

        return $editor;
    }

    /**
     * Get ajax url.
     *
     * @return array|mixed|string
     */
    public function getAjaxUrl()
    {
        if (is_array($this->ajax)) {
            return $this->ajax['url'] ?: url()->current();
        }

        return $this->ajax ?: url()->current();
    }

    /**
     * Compile DataTable callback value.
     *
     * @param mixed $callback
     * @return mixed|string
     */
    private function compileCallback($callback)
    {
        if (is_callable($callback)) {
            return value($callback);
        } elseif ($this->view->exists($callback)) {
            return $this->view->make($callback)->render();
        }

        return $callback;
    }
}