'vendor/font-awesome/css/font-awesome.min.css',
        'highlightjs' => 'vendor/highlightjs/styles/github.css'
    );
    protected $jsVendors = array(
        'jquery' => 'vendor/jquery/dist/jquery.min.js',
        'highlightjs' => 'vendor/highlightjs/highlight.pack.js'
    );
    protected $includeVendors = true;
    protected $cssFiles = array('debugbar.css', 'widgets.css', 'openhandler.css');
    protected $jsFiles = array('debugbar.js', 'widgets.js', 'openhandler.js');
    protected $additionalAssets = array();
    protected $javascriptClass = 'PhpDebugBar.DebugBar';
    protected $variableName = 'phpdebugbar';
    protected $enableJqueryNoConflict = true;
    protected $useRequireJs = false;
    protected $hideEmptyTabs = null;
    protected $initialization;
    protected $controls = array();
    protected $ignoredCollectors = array();
    protected $ajaxHandlerClass = 'PhpDebugBar.AjaxHandler';
    protected $ajaxHandlerBindToFetch = false;
    protected $ajaxHandlerBindToJquery = false;
    protected $ajaxHandlerBindToXHR = true;
    protected $ajaxHandlerAutoShow = true;
    protected $ajaxHandlerEnableTab = false;
    protected $openHandlerClass = 'PhpDebugBar.OpenHandler';
    protected $openHandlerUrl;
    protected $cspNonce;
    /**
     * @param \DebugBar\DebugBar $debugBar
     * @param string $baseUrl
     * @param string $basePath
     */
    public function __construct(DebugBar $debugBar, $baseUrl = null, $basePath = null)
    {
        $this->debugBar = $debugBar;
        if ($baseUrl === null) {
            $baseUrl = '/vendor/maximebf/debugbar/src/DebugBar/Resources';
        }
        $this->baseUrl = $baseUrl;
        if ($basePath === null) {
            $basePath = __DIR__ . DIRECTORY_SEPARATOR . 'Resources';
        }
        $this->basePath = $basePath;
        // bitwise operations cannot be done in class definition :(
        $this->initialization = self::INITIALIZE_CONSTRUCTOR | self::INITIALIZE_CONTROLS;
    }
    /**
     * Sets options from an array
     *
     * Options:
     *  - base_path
     *  - base_url
     *  - include_vendors
     *  - javascript_class
     *  - variable_name
     *  - initialization
     *  - enable_jquery_noconflict
     *  - controls
     *  - disable_controls
     *  - ignore_collectors
     *  - ajax_handler_classname
     *  - ajax_handler_bind_to_jquery
     *  - ajax_handler_auto_show
     *  - open_handler_classname
     *  - open_handler_url
     *
     * @param array $options [description]
     */
    public function setOptions(array $options)
    {
        if (array_key_exists('base_path', $options)) {
            $this->setBasePath($options['base_path']);
        }
        if (array_key_exists('base_url', $options)) {
            $this->setBaseUrl($options['base_url']);
        }
        if (array_key_exists('include_vendors', $options)) {
            $this->setIncludeVendors($options['include_vendors']);
        }
        if (array_key_exists('javascript_class', $options)) {
            $this->setJavascriptClass($options['javascript_class']);
        }
        if (array_key_exists('variable_name', $options)) {
            $this->setVariableName($options['variable_name']);
        }
        if (array_key_exists('initialization', $options)) {
            $this->setInitialization($options['initialization']);
        }
        if (array_key_exists('enable_jquery_noconflict', $options)) {
            $this->setEnableJqueryNoConflict($options['enable_jquery_noconflict']);
        }
        if (array_key_exists('use_requirejs', $options)) {
            $this->setUseRequireJs($options['use_requirejs']);
        }
        if (array_key_exists('hide_empty_tabs', $options)) {
            $this->setHideEmptyTabs($options['hide_empty_tabs']);
        }
        if (array_key_exists('controls', $options)) {
            foreach ($options['controls'] as $name => $control) {
                $this->addControl($name, $control);
            }
        }
        if (array_key_exists('disable_controls', $options)) {
            foreach ((array) $options['disable_controls'] as $name) {
                $this->disableControl($name);
            }
        }
        if (array_key_exists('ignore_collectors', $options)) {
            foreach ((array) $options['ignore_collectors'] as $name) {
                $this->ignoreCollector($name);
            }
        }
        if (array_key_exists('ajax_handler_classname', $options)) {
            $this->setAjaxHandlerClass($options['ajax_handler_classname']);
        }
        if (array_key_exists('ajax_handler_bind_to_jquery', $options)) {
            $this->setBindAjaxHandlerToJquery($options['ajax_handler_bind_to_jquery']);
        }
        if (array_key_exists('ajax_handler_auto_show', $options)) {
            $this->setAjaxHandlerAutoShow($options['ajax_handler_auto_show']);
        }
        if (array_key_exists('ajax_handler_enable_tab', $options)) {
            $this->setAjaxHandlerEnableTab($options['ajax_handler_enable_tab']);
        }
        if (array_key_exists('open_handler_classname', $options)) {
            $this->setOpenHandlerClass($options['open_handler_classname']);
        }
        if (array_key_exists('open_handler_url', $options)) {
            $this->setOpenHandlerUrl($options['open_handler_url']);
        }
        if (array_key_exists('csp_nonce', $options)) {
            $this->setCspNonce($options['csp_nonce']);
        }
    }
    /**
     * Sets the path which assets are relative to
     *
     * @param string $path
     */
    public function setBasePath($path)
    {
        $this->basePath = $path;
        return $this;
    }
    /**
     * Returns the path which assets are relative to
     *
     * @return string
     */
    public function getBasePath()
    {
        return $this->basePath;
    }
    /**
     * Sets the base URL from which assets will be served
     *
     * @param string $url
     */
    public function setBaseUrl($url)
    {
        $this->baseUrl = $url;
        return $this;
    }
    /**
     * Returns the base URL from which assets will be served
     *
     * @return string
     */
    public function getBaseUrl()
    {
        return $this->baseUrl;
    }
    /**
     * Whether to include vendor assets
     *
     * You can only include js or css vendors using
     * setIncludeVendors('css') or setIncludeVendors('js')
     *
     * @param boolean $enabled
     */
    public function setIncludeVendors($enabled = true)
    {
        if (is_string($enabled)) {
            $enabled = array($enabled);
        }
        $this->includeVendors = $enabled;
        if (!$enabled || (is_array($enabled) && !in_array('js', $enabled))) {
            // no need to call jQuery.noConflict() if we do not include our own version
            $this->enableJqueryNoConflict = false;
        }
        return $this;
    }
    /**
     * Checks if vendors assets are included
     *
     * @return boolean
     */
    public function areVendorsIncluded()
    {
        return $this->includeVendors !== false;
    }
    /**
     * Disable a specific vendor's assets.
     *
     * @param  string $name "jquery", "fontawesome", "highlightjs"
     *
     * @return void
     */
    public function disableVendor($name)
    {
        if (array_key_exists($name, $this->cssVendors)) {
            unset($this->cssVendors[$name]);
        }
        if (array_key_exists($name, $this->jsVendors)) {
            unset($this->jsVendors[$name]);
        }
    }
    /**
     * Sets the javascript class name
     *
     * @param string $className
     */
    public function setJavascriptClass($className)
    {
        $this->javascriptClass = $className;
        return $this;
    }
    /**
     * Returns the javascript class name
     *
     * @return string
     */
    public function getJavascriptClass()
    {
        return $this->javascriptClass;
    }
    /**
     * Sets the variable name of the class instance
     *
     * @param string $name
     */
    public function setVariableName($name)
    {
        $this->variableName = $name;
        return $this;
    }
    /**
     * Returns the variable name of the class instance
     *
     * @return string
     */
    public function getVariableName()
    {
        return $this->variableName;
    }
    /**
     * Sets what should be initialized
     *
     *  - INITIALIZE_CONSTRUCTOR: only initializes the instance
     *  - INITIALIZE_CONTROLS: initializes the controls and data mapping
     *  - INITIALIZE_CONSTRUCTOR | INITIALIZE_CONTROLS: initialize everything (default)
     *
     * @param integer $init
     */
    public function setInitialization($init)
    {
        $this->initialization = $init;
        return $this;
    }
    /**
     * Returns what should be initialized
     *
     * @return integer
     */
    public function getInitialization()
    {
        return $this->initialization;
    }
    /**
     * Sets whether to call jQuery.noConflict()
     *
     * @param boolean $enabled
     */
    public function setEnableJqueryNoConflict($enabled = true)
    {
        $this->enableJqueryNoConflict = $enabled;
        return $this;
    }
    /**
     * Checks if jQuery.noConflict() will be called
     *
     * @return boolean
     */
    public function isJqueryNoConflictEnabled()
    {
        return $this->enableJqueryNoConflict;
    }
    /**
     * Sets whether to use RequireJS or not
     *
     * @param boolean $enabled
     * @return $this
     */
    public function setUseRequireJs($enabled = true)
    {
        $this->useRequireJs = $enabled;
        return $this;
    }
    /**
     * Checks if RequireJS is used
     *
     * @return boolean
     */
    public function isRequireJsUsed()
    {
        return $this->useRequireJs;
    }
    /**
     * Sets whether to hide empty tabs or not
     *
     * @param boolean $hide
     * @return $this
     */
    public function setHideEmptyTabs($hide = true)
    {
        $this->hideEmptyTabs = $hide;
        return $this;
    }
    /**
     * Checks if empty tabs are hidden or not
     *
     * @return boolean
     */
    public function areEmptyTabsHidden()
    {
        return $this->hideEmptyTabs;
    }
    /**
     * Adds a control to initialize
     *
     * Possible options:
     *  - icon: icon name
     *  - tooltip: string
     *  - widget: widget class name
     *  - title: tab title
     *  - map: a property name from the data to map the control to
     *  - default: a js string, default value of the data map
     *
     * "icon" or "widget" are at least needed
     *
     * @param string $name
     * @param array $options
     */
    public function addControl($name, array $options)
    {
        if (count(array_intersect(array_keys($options), array('icon', 'widget', 'tab', 'indicator'))) === 0) {
            throw new DebugBarException("Not enough options for control '$name'");
        }
        $this->controls[$name] = $options;
        return $this;
    }
    /**
     * Disables a control
     *
     * @param string $name
     */
    public function disableControl($name)
    {
        $this->controls[$name] = null;
        return $this;
    }
    /**
     * Returns the list of controls
     *
     * This does not include controls provided by collectors
     *
     * @return array
     */
    public function getControls()
    {
        return $this->controls;
    }
    /**
     * Ignores widgets provided by a collector
     *
     * @param string $name
     */
    public function ignoreCollector($name)
    {
        $this->ignoredCollectors[] = $name;
        return $this;
    }
    /**
     * Returns the list of ignored collectors
     *
     * @return array
     */
    public function getIgnoredCollectors()
    {
        return $this->ignoredCollectors;
    }
    /**
     * Sets the class name of the ajax handler
     *
     * Set to false to disable
     *
     * @param string $className
     */
    public function setAjaxHandlerClass($className)
    {
        $this->ajaxHandlerClass = $className;
        return $this;
    }
    /**
     * Returns the class name of the ajax handler
     *
     * @return string
     */
    public function getAjaxHandlerClass()
    {
        return $this->ajaxHandlerClass;
    }
    /**
     * Sets whether to call bindToFetch() on the ajax handler
     *
     * @param boolean $bind
     */
    public function setBindAjaxHandlerToFetch($bind = true)
    {
        $this->ajaxHandlerBindToFetch = $bind;
        return $this;
    }
    /**
     * Checks whether bindToFetch() will be called on the ajax handler
     *
     * @return boolean
     */
    public function isAjaxHandlerBoundToFetch()
    {
        return $this->ajaxHandlerBindToFetch;
    }
    /**
     * Sets whether to call bindToJquery() on the ajax handler
     *
     * @param boolean $bind
     * @deprecated use setBindAjaxHandlerToXHR
     */
    public function setBindAjaxHandlerToJquery($bind = true)
    {
        $this->ajaxHandlerBindToJquery = $bind;
        return $this;
    }
    /**
     * Checks whether bindToJquery() will be called on the ajax handler
     *
     * @return boolean
     * @deprecated use isAjaxHandlerBoundToXHR
     */
    public function isAjaxHandlerBoundToJquery()
    {
        return $this->ajaxHandlerBindToJquery;
    }
    /**
     * Sets whether to call bindToXHR() on the ajax handler
     *
     * @param boolean $bind
     */
    public function setBindAjaxHandlerToXHR($bind = true)
    {
        $this->ajaxHandlerBindToXHR = $bind;
        return $this;
    }
    /**
     * Checks whether bindToXHR() will be called on the ajax handler
     *
     * @return boolean
     */
    public function isAjaxHandlerBoundToXHR()
    {
        return $this->ajaxHandlerBindToXHR;
    }
    /**
     * Sets whether new ajax debug data will be immediately shown.  Setting to false could be useful
     * if there are a lot of tracking events cluttering things.
     *
     * @param boolean $autoShow
     */
    public function setAjaxHandlerAutoShow($autoShow = true)
    {
        $this->ajaxHandlerAutoShow = $autoShow;
        return $this;
    }
    /**
     * Checks whether the ajax handler will immediately show new ajax requests.
     *
     * @return boolean
     */
    public function isAjaxHandlerAutoShow()
    {
        return $this->ajaxHandlerAutoShow;
    }
    /**
     * Sets whether new ajax debug data will be shown in a separate tab instead of dropdown.
     *
     * @param boolean $enabled
     */
    public function setAjaxHandlerEnableTab($enabled = true)
    {
        $this->ajaxHandlerEnableTab = $enabled;
        return $this;
    }
    /**
     * Check if the Ajax Handler History tab is enabled
     *
     * @return boolean
     */
    public function isAjaxHandlerTabEnabled()
    {
        return $this->ajaxHandlerEnableTab;
    }
    /**
     * Sets the class name of the js open handler
     *
     * @param string $className
     */
    public function setOpenHandlerClass($className)
    {
        $this->openHandlerClass = $className;
        return $this;
    }
    /**
     * Returns the class name of the js open handler
     *
     * @return string
     */
    public function getOpenHandlerClass()
    {
        return $this->openHandlerClass;
    }
    /**
     * Sets the url of the open handler
     *
     * @param string $url
     */
    public function setOpenHandlerUrl($url)
    {
        $this->openHandlerUrl = $url;
        return $this;
    }
    /**
     * Returns the url for the open handler
     *
     * @return string
     */
    public function getOpenHandlerUrl()
    {
        return $this->openHandlerUrl;
    }
    /**
     * Sets the CSP Nonce (or remove it by setting to null)
     *
     * @param string|null $nonce
     * @return $this
     */
    public function setCspNonce($nonce = null)
    {
        $this->cspNonce = $nonce;
        return $this;
    }
    /**
     * Get the CSP Nonce
     *
     * @return string|null
     */
    public function getCspNonce()
    {
        return $this->cspNonce;
    }
    /**
     * Add assets stored in files to render in the head
     *
     * @param array $cssFiles An array of filenames
     * @param array $jsFiles  An array of filenames
     * @param string $basePath Base path of those files
     * @param string $baseUrl  Base url of those files
     * @return $this
     */
    public function addAssets($cssFiles, $jsFiles, $basePath = null, $baseUrl = null)
    {
        $this->additionalAssets[] = array(
            'base_path' => $basePath,
            'base_url' => $baseUrl,
            'css' => (array) $cssFiles,
            'js' => (array) $jsFiles
        );
        return $this;
    }
    /**
     * Add inline assets to render inline in the head.  Ideally, you should store static assets in
     * files that you add with the addAssets function.  However, adding inline assets is useful when
     * integrating with 3rd-party libraries that require static assets that are only available in an
     * inline format.
     *
     * The inline content arrays require special string array keys:  they are used to deduplicate
     * content.  This is particularly useful if multiple instances of the same asset end up being
     * added.  Inline assets from all collectors are merged together into the same array, so these
     * content IDs effectively deduplicate the inline assets.
     *
     * @param array $inlineCss  An array map of content ID to inline CSS content (not including ' . "\n", $content);
        }
        foreach ($jsFiles as $file) {
            $html .= sprintf('' . "\n", $file);
        }
        foreach ($inlineJs as $content) {
            $html .= sprintf('' . "\n", $nonce, $content);
        }
        foreach ($inlineHead as $content) {
            $html .= $content . "\n";
        }
        if ($this->enableJqueryNoConflict && !$this->useRequireJs) {
            $html .= '' . "\n";
        }
        return $html;
    }
    /**
     * Register shutdown to display the debug bar
     *
     * @param boolean $here Set position of HTML. True if is to current position or false for end file
     * @param boolean $initialize Whether to render the de bug bar initialization code
     * @param bool $renderStackedData
     * @param bool $head
     * @return string Return "{--DEBUGBAR_OB_START_REPLACE_ME--}" or return an empty string if $here == false
     */
    public function renderOnShutdown($here = true, $initialize = true, $renderStackedData = true, $head = false)
    {
        register_shutdown_function(array($this, "replaceTagInBuffer"), $here, $initialize, $renderStackedData, $head);
        if (ob_get_level() === 0) {
            ob_start();
        }
        return ($here) ? self::REPLACEABLE_TAG : "";
    }
    /**
     * Same as renderOnShutdown() with $head = true
     *
     * @param boolean $here
     * @param boolean $initialize
     * @param boolean $renderStackedData
     * @return string
     */
    public function renderOnShutdownWithHead($here = true, $initialize = true, $renderStackedData = true)
    {
        return $this->renderOnShutdown($here, $initialize, $renderStackedData, true);
    }
    /**
     * Is callback function for register_shutdown_function(...)
     *
     * @param boolean $here Set position of HTML. True if is to current position or false for end file
     * @param boolean $initialize Whether to render the de bug bar initialization code
     * @param bool $renderStackedData
     * @param bool $head
     */
    public function replaceTagInBuffer($here = true, $initialize = true, $renderStackedData = true, $head = false)
    {
        $render = ($head ? $this->renderHead() : "")
            . $this->render($initialize, $renderStackedData);
        $current = ($here && ob_get_level() > 0) ? ob_get_clean() : self::REPLACEABLE_TAG;
        echo str_replace(self::REPLACEABLE_TAG, $render, $current, $count);
        if ($count === 0) {
            echo $render;
        }
    }
    /**
     * Returns the code needed to display the debug bar
     *
     * AJAX request should not render the initialization code.
     *
     * @param boolean $initialize Whether or not to render the debug bar initialization code
     * @param boolean $renderStackedData Whether or not to render the stacked data
     * @return string
     */
    public function render($initialize = true, $renderStackedData = true)
    {
        $js = '';
        if ($initialize) {
            $js = $this->getJsInitializationCode();
        }
        if ($renderStackedData && $this->debugBar->hasStackedData()) {
            foreach ($this->debugBar->getStackedData() as $id => $data) {
                $js .= $this->getAddDatasetCode($id, $data, '(stacked)');
            }
        }
        $suffix = !$initialize ? '(ajax)' : null;
        $js .= $this->getAddDatasetCode($this->debugBar->getCurrentRequestId(), $this->debugBar->getData(), $suffix);
        $nonce = $this->getNonceAttribute();
        if ($nonce != '') {
            $js = preg_replace("/\n";
        } else {
            return "\n";
        }
    }
    /**
     * Returns the js code needed to initialize the debug bar
     *
     * @return string
     */
    protected function getJsInitializationCode()
    {
        $js = '';
        if (($this->initialization & self::INITIALIZE_CONSTRUCTOR) === self::INITIALIZE_CONSTRUCTOR) {
            $js .= sprintf("var %s = new %s();\n", $this->variableName, $this->javascriptClass);
        }
        if ($this->hideEmptyTabs !== null) {
            $js .= sprintf("%s.setHideEmptyTabs(%s);\n", $this->variableName,
                json_encode($this->hideEmptyTabs));
        }
        if (($this->initialization & self::INITIALIZE_CONTROLS) === self::INITIALIZE_CONTROLS) {
            $js .= $this->getJsControlsDefinitionCode($this->variableName);
        }
        if ($this->ajaxHandlerClass) {
            $js .= sprintf("%s.ajaxHandler = new %s(%s, undefined, %s);\n",
                $this->variableName,
                $this->ajaxHandlerClass,
                $this->variableName,
                $this->ajaxHandlerAutoShow ? 'true' : 'false'
            );
            if ($this->ajaxHandlerBindToFetch) {
                $js .= sprintf("%s.ajaxHandler.bindToFetch();\n", $this->variableName);
            }
            if ($this->ajaxHandlerBindToXHR) {
                $js .= sprintf("%s.ajaxHandler.bindToXHR();\n", $this->variableName);
            } elseif ($this->ajaxHandlerBindToJquery) {
                $js .= sprintf("if (jQuery) %s.ajaxHandler.bindToJquery(jQuery);\n", $this->variableName);
            }
        }
        if ($this->openHandlerUrl !== null) {
            $js .= sprintf("%s.setOpenHandler(new %s(%s));\n", $this->variableName,
                $this->openHandlerClass,
                json_encode(array("url" => $this->openHandlerUrl)));
        }
        return $js;
    }
    /**
     * Returns the js code needed to initialized the controls and data mapping of the debug bar
     *
     * Controls can be defined by collectors themselves or using {@see addControl()}
     *
     * @param string $varname Debug bar's variable name
     * @return string
     */
    protected function getJsControlsDefinitionCode($varname)
    {
        $js = '';
        $dataMap = array();
        $excludedOptions = array('indicator', 'tab', 'map', 'default', 'widget', 'position');
        // finds controls provided by collectors
        $widgets = array();
        foreach ($this->debugBar->getCollectors() as $collector) {
            if (($collector instanceof Renderable) && !in_array($collector->getName(), $this->ignoredCollectors)) {
                if ($w = $collector->getWidgets()) {
                    $widgets = array_merge($widgets, $w);
                }
            }
        }
        $controls = array_merge($widgets, $this->controls);
        foreach (array_filter($controls) as $name => $options) {
            $opts = array_diff_key($options, array_flip($excludedOptions));
            if (isset($options['tab']) || isset($options['widget'])) {
                if (!isset($opts['title'])) {
                    $opts['title'] = ucfirst(str_replace('_', ' ', $name));
                }
                $js .= sprintf("%s.addTab(\"%s\", new %s({%s%s}));\n",
                    $varname,
                    $name,
                    isset($options['tab']) ? $options['tab'] : 'PhpDebugBar.DebugBar.Tab',
                    substr(json_encode($opts, JSON_FORCE_OBJECT), 1, -1),
                    isset($options['widget']) ? sprintf('%s"widget": new %s()', count($opts) ? ', ' : '', $options['widget']) : ''
                );
            } elseif (isset($options['indicator']) || isset($options['icon'])) {
                $js .= sprintf("%s.addIndicator(\"%s\", new %s(%s), \"%s\");\n",
                    $varname,
                    $name,
                    isset($options['indicator']) ? $options['indicator'] : 'PhpDebugBar.DebugBar.Indicator',
                    json_encode($opts, JSON_FORCE_OBJECT),
                    isset($options['position']) ? $options['position'] : 'right'
                );
            }
            if (isset($options['map']) && isset($options['default'])) {
                $dataMap[$name] = array($options['map'], $options['default']);
            }
        }
        // creates the data mapping object
        $mapJson = array();
        foreach ($dataMap as $name => $values) {
            $mapJson[] = sprintf('"%s": ["%s", %s]', $name, $values[0], $values[1]);
        }
        $js .= sprintf("%s.setDataMap({\n%s\n});\n", $varname, implode(",\n", $mapJson));
        // activate state restoration
        $js .= sprintf("%s.restoreState();\n", $varname);
        if ($this->ajaxHandlerEnableTab) {
            $js .= sprintf("%s.enableAjaxHandlerTab();\n", $varname);
        }
        return $js;
    }
    /**
     * Returns the js code needed to add a dataset
     *
     * @param string $requestId
     * @param array $data
     * @param mixed $suffix
     * @return string
     */
    protected function getAddDatasetCode($requestId, $data, $suffix = null)
    {
        $js = sprintf("%s.addDataSet(%s, \"%s\"%s);\n",
            $this->variableName,
            json_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_INVALID_UTF8_IGNORE),
            $requestId,
            $suffix ? ", " . json_encode($suffix) : ''
        );
        return $js;
    }
    /**
     * If a nonce it set, create the correct attribute
     * @return string
     */
    protected function getNonceAttribute()
    {
        if ($nonce = $this->getCspNonce()) {
            return ' nonce="' . $nonce .'"';
        }
        return '';
    }
}