"Fossies" - the Fresh Open Source Software Archive

Member "grav/vendor/maximebf/debugbar/src/DebugBar/JavascriptRenderer.php" (1 Sep 2020, 35238 Bytes) of package /linux/www/grav-v1.6.27.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "JavascriptRenderer.php" see the Fossies "Dox" file reference documentation.

    1 <?php
    2 /*
    3  * This file is part of the DebugBar package.
    4  *
    5  * (c) 2013 Maxime Bouroumeau-Fuseau
    6  *
    7  * For the full copyright and license information, please view the LICENSE
    8  * file that was distributed with this source code.
    9  */
   10 
   11 namespace DebugBar;
   12 
   13 use DebugBar\DataCollector\AssetProvider;
   14 use DebugBar\DataCollector\Renderable;
   15 
   16 /**
   17  * Renders the debug bar using the client side javascript implementation
   18  *
   19  * Generates all the needed initialization code of controls
   20  */
   21 class JavascriptRenderer
   22 {
   23     const INITIALIZE_CONSTRUCTOR = 2;
   24 
   25     const INITIALIZE_CONTROLS = 4;
   26 
   27     const REPLACEABLE_TAG = "{--DEBUGBAR_OB_START_REPLACE_ME--}";
   28 
   29     const RELATIVE_PATH = 'path';
   30 
   31     const RELATIVE_URL = 'url';
   32 
   33     protected $debugBar;
   34 
   35     protected $baseUrl;
   36 
   37     protected $basePath;
   38 
   39     protected $cssVendors = array(
   40         'fontawesome' => 'vendor/font-awesome/css/font-awesome.min.css',
   41         'highlightjs' => 'vendor/highlightjs/styles/github.css'
   42     );
   43 
   44     protected $jsVendors = array(
   45         'jquery' => 'vendor/jquery/dist/jquery.min.js',
   46         'highlightjs' => 'vendor/highlightjs/highlight.pack.js'
   47     );
   48 
   49     protected $includeVendors = true;
   50 
   51     protected $cssFiles = array('debugbar.css', 'widgets.css', 'openhandler.css');
   52 
   53     protected $jsFiles = array('debugbar.js', 'widgets.js', 'openhandler.js');
   54 
   55     protected $additionalAssets = array();
   56 
   57     protected $javascriptClass = 'PhpDebugBar.DebugBar';
   58 
   59     protected $variableName = 'phpdebugbar';
   60 
   61     protected $enableJqueryNoConflict = true;
   62 
   63     protected $useRequireJs = false;
   64 
   65     protected $initialization;
   66 
   67     protected $controls = array();
   68 
   69     protected $ignoredCollectors = array();
   70 
   71     protected $ajaxHandlerClass = 'PhpDebugBar.AjaxHandler';
   72 
   73     protected $ajaxHandlerBindToFetch = false;
   74 
   75     protected $ajaxHandlerBindToJquery = true;
   76 
   77     protected $ajaxHandlerBindToXHR = false;
   78 
   79     protected $ajaxHandlerAutoShow = true;
   80 
   81     protected $openHandlerClass = 'PhpDebugBar.OpenHandler';
   82 
   83     protected $openHandlerUrl;
   84 
   85     /**
   86      * @param \DebugBar\DebugBar $debugBar
   87      * @param string $baseUrl
   88      * @param string $basePath
   89      */
   90     public function __construct(DebugBar $debugBar, $baseUrl = null, $basePath = null)
   91     {
   92         $this->debugBar = $debugBar;
   93 
   94         if ($baseUrl === null) {
   95             $baseUrl = '/vendor/maximebf/debugbar/src/DebugBar/Resources';
   96         }
   97         $this->baseUrl = $baseUrl;
   98 
   99         if ($basePath === null) {
  100             $basePath = __DIR__ . DIRECTORY_SEPARATOR . 'Resources';
  101         }
  102         $this->basePath = $basePath;
  103 
  104         // bitwise operations cannot be done in class definition :(
  105         $this->initialization = self::INITIALIZE_CONSTRUCTOR | self::INITIALIZE_CONTROLS;
  106     }
  107 
  108     /**
  109      * Sets options from an array
  110      *
  111      * Options:
  112      *  - base_path
  113      *  - base_url
  114      *  - include_vendors
  115      *  - javascript_class
  116      *  - variable_name
  117      *  - initialization
  118      *  - enable_jquery_noconflict
  119      *  - controls
  120      *  - disable_controls
  121      *  - ignore_collectors
  122      *  - ajax_handler_classname
  123      *  - ajax_handler_bind_to_jquery
  124      *  - ajax_handler_auto_show
  125      *  - open_handler_classname
  126      *  - open_handler_url
  127      *
  128      * @param array $options [description]
  129      */
  130     public function setOptions(array $options)
  131     {
  132         if (array_key_exists('base_path', $options)) {
  133             $this->setBasePath($options['base_path']);
  134         }
  135         if (array_key_exists('base_url', $options)) {
  136             $this->setBaseUrl($options['base_url']);
  137         }
  138         if (array_key_exists('include_vendors', $options)) {
  139             $this->setIncludeVendors($options['include_vendors']);
  140         }
  141         if (array_key_exists('javascript_class', $options)) {
  142             $this->setJavascriptClass($options['javascript_class']);
  143         }
  144         if (array_key_exists('variable_name', $options)) {
  145             $this->setVariableName($options['variable_name']);
  146         }
  147         if (array_key_exists('initialization', $options)) {
  148             $this->setInitialization($options['initialization']);
  149         }
  150         if (array_key_exists('enable_jquery_noconflict', $options)) {
  151             $this->setEnableJqueryNoConflict($options['enable_jquery_noconflict']);
  152         }
  153         if (array_key_exists('use_requirejs', $options)) {
  154             $this->setUseRequireJs($options['use_requirejs']);
  155         }
  156         if (array_key_exists('controls', $options)) {
  157             foreach ($options['controls'] as $name => $control) {
  158                 $this->addControl($name, $control);
  159             }
  160         }
  161         if (array_key_exists('disable_controls', $options)) {
  162             foreach ((array) $options['disable_controls'] as $name) {
  163                 $this->disableControl($name);
  164             }
  165         }
  166         if (array_key_exists('ignore_collectors', $options)) {
  167             foreach ((array) $options['ignore_collectors'] as $name) {
  168                 $this->ignoreCollector($name);
  169             }
  170         }
  171         if (array_key_exists('ajax_handler_classname', $options)) {
  172             $this->setAjaxHandlerClass($options['ajax_handler_classname']);
  173         }
  174         if (array_key_exists('ajax_handler_bind_to_jquery', $options)) {
  175             $this->setBindAjaxHandlerToJquery($options['ajax_handler_bind_to_jquery']);
  176         }
  177         if (array_key_exists('ajax_handler_auto_show', $options)) {
  178             $this->setAjaxHandlerAutoShow($options['ajax_handler_auto_show']);
  179         }
  180         if (array_key_exists('open_handler_classname', $options)) {
  181             $this->setOpenHandlerClass($options['open_handler_classname']);
  182         }
  183         if (array_key_exists('open_handler_url', $options)) {
  184             $this->setOpenHandlerUrl($options['open_handler_url']);
  185         }
  186     }
  187 
  188     /**
  189      * Sets the path which assets are relative to
  190      *
  191      * @param string $path
  192      */
  193     public function setBasePath($path)
  194     {
  195         $this->basePath = $path;
  196         return $this;
  197     }
  198 
  199     /**
  200      * Returns the path which assets are relative to
  201      *
  202      * @return string
  203      */
  204     public function getBasePath()
  205     {
  206         return $this->basePath;
  207     }
  208 
  209     /**
  210      * Sets the base URL from which assets will be served
  211      *
  212      * @param string $url
  213      */
  214     public function setBaseUrl($url)
  215     {
  216         $this->baseUrl = $url;
  217         return $this;
  218     }
  219 
  220     /**
  221      * Returns the base URL from which assets will be served
  222      *
  223      * @return string
  224      */
  225     public function getBaseUrl()
  226     {
  227         return $this->baseUrl;
  228     }
  229 
  230     /**
  231      * Whether to include vendor assets
  232      *
  233      * You can only include js or css vendors using
  234      * setIncludeVendors('css') or setIncludeVendors('js')
  235      *
  236      * @param boolean $enabled
  237      */
  238     public function setIncludeVendors($enabled = true)
  239     {
  240         if (is_string($enabled)) {
  241             $enabled = array($enabled);
  242         }
  243         $this->includeVendors = $enabled;
  244 
  245         if (!$enabled || (is_array($enabled) && !in_array('js', $enabled))) {
  246             // no need to call jQuery.noConflict() if we do not include our own version
  247             $this->enableJqueryNoConflict = false;
  248         }
  249 
  250         return $this;
  251     }
  252 
  253     /**
  254      * Checks if vendors assets are included
  255      *
  256      * @return boolean
  257      */
  258     public function areVendorsIncluded()
  259     {
  260         return $this->includeVendors !== false;
  261     }
  262 
  263     /**
  264      * Disable a specific vendor's assets.
  265      *
  266      * @param  string $name "jquery", "fontawesome", "highlightjs"
  267      *
  268      * @return void
  269      */
  270     public function disableVendor($name)
  271     {
  272         if (array_key_exists($name, $this->cssVendors)) {
  273             unset($this->cssVendors[$name]);
  274         }
  275         if (array_key_exists($name, $this->jsVendors)) {
  276             unset($this->jsVendors[$name]);
  277         }
  278     }
  279 
  280     /**
  281      * Sets the javascript class name
  282      *
  283      * @param string $className
  284      */
  285     public function setJavascriptClass($className)
  286     {
  287         $this->javascriptClass = $className;
  288         return $this;
  289     }
  290 
  291     /**
  292      * Returns the javascript class name
  293      *
  294      * @return string
  295      */
  296     public function getJavascriptClass()
  297     {
  298         return $this->javascriptClass;
  299     }
  300 
  301     /**
  302      * Sets the variable name of the class instance
  303      *
  304      * @param string $name
  305      */
  306     public function setVariableName($name)
  307     {
  308         $this->variableName = $name;
  309         return $this;
  310     }
  311 
  312     /**
  313      * Returns the variable name of the class instance
  314      *
  315      * @return string
  316      */
  317     public function getVariableName()
  318     {
  319         return $this->variableName;
  320     }
  321 
  322     /**
  323      * Sets what should be initialized
  324      *
  325      *  - INITIALIZE_CONSTRUCTOR: only initializes the instance
  326      *  - INITIALIZE_CONTROLS: initializes the controls and data mapping
  327      *  - INITIALIZE_CONSTRUCTOR | INITIALIZE_CONTROLS: initialize everything (default)
  328      *
  329      * @param integer $init
  330      */
  331     public function setInitialization($init)
  332     {
  333         $this->initialization = $init;
  334         return $this;
  335     }
  336 
  337     /**
  338      * Returns what should be initialized
  339      *
  340      * @return integer
  341      */
  342     public function getInitialization()
  343     {
  344         return $this->initialization;
  345     }
  346 
  347     /**
  348      * Sets whether to call jQuery.noConflict()
  349      *
  350      * @param boolean $enabled
  351      */
  352     public function setEnableJqueryNoConflict($enabled = true)
  353     {
  354         $this->enableJqueryNoConflict = $enabled;
  355         return $this;
  356     }
  357 
  358     /**
  359      * Checks if jQuery.noConflict() will be called
  360      *
  361      * @return boolean
  362      */
  363     public function isJqueryNoConflictEnabled()
  364     {
  365         return $this->enableJqueryNoConflict;
  366     }
  367 
  368     /**
  369      * Sets whether to use RequireJS or not
  370      *
  371      * @param boolean $enabled
  372      * @return $this
  373      */
  374     public function setUseRequireJs($enabled = true)
  375     {
  376         $this->useRequireJs = $enabled;
  377         return $this;
  378     }
  379 
  380     /**
  381      * Checks if RequireJS is used
  382      *
  383      * @return boolean
  384      */
  385     public function isRequireJsUsed()
  386     {
  387         return $this->useRequireJs;
  388     }
  389 
  390     /**
  391      * Adds a control to initialize
  392      *
  393      * Possible options:
  394      *  - icon: icon name
  395      *  - tooltip: string
  396      *  - widget: widget class name
  397      *  - title: tab title
  398      *  - map: a property name from the data to map the control to
  399      *  - default: a js string, default value of the data map
  400      *
  401      * "icon" or "widget" are at least needed
  402      *
  403      * @param string $name
  404      * @param array $options
  405      */
  406     public function addControl($name, array $options)
  407     {
  408         if (count(array_intersect(array_keys($options), array('icon', 'widget', 'tab', 'indicator'))) === 0) {
  409             throw new DebugBarException("Not enough options for control '$name'");
  410         }
  411         $this->controls[$name] = $options;
  412         return $this;
  413     }
  414 
  415     /**
  416      * Disables a control
  417      *
  418      * @param string $name
  419      */
  420     public function disableControl($name)
  421     {
  422         $this->controls[$name] = null;
  423         return $this;
  424     }
  425 
  426     /**
  427      * Returns the list of controls
  428      *
  429      * This does not include controls provided by collectors
  430      *
  431      * @return array
  432      */
  433     public function getControls()
  434     {
  435         return $this->controls;
  436     }
  437 
  438     /**
  439      * Ignores widgets provided by a collector
  440      *
  441      * @param string $name
  442      */
  443     public function ignoreCollector($name)
  444     {
  445         $this->ignoredCollectors[] = $name;
  446         return $this;
  447     }
  448 
  449     /**
  450      * Returns the list of ignored collectors
  451      *
  452      * @return array
  453      */
  454     public function getIgnoredCollectors()
  455     {
  456         return $this->ignoredCollectors;
  457     }
  458 
  459     /**
  460      * Sets the class name of the ajax handler
  461      *
  462      * Set to false to disable
  463      *
  464      * @param string $className
  465      */
  466     public function setAjaxHandlerClass($className)
  467     {
  468         $this->ajaxHandlerClass = $className;
  469         return $this;
  470     }
  471 
  472     /**
  473      * Returns the class name of the ajax handler
  474      *
  475      * @return string
  476      */
  477     public function getAjaxHandlerClass()
  478     {
  479         return $this->ajaxHandlerClass;
  480     }
  481 
  482     /**
  483      * Sets whether to call bindToFetch() on the ajax handler
  484      *
  485      * @param boolean $bind
  486      */
  487     public function setBindAjaxHandlerToFetch($bind = true)
  488     {
  489         $this->ajaxHandlerBindToFetch = $bind;
  490         return $this;
  491     }
  492 
  493     /**
  494      * Checks whether bindToFetch() will be called on the ajax handler
  495      *
  496      * @return boolean
  497      */
  498     public function isAjaxHandlerBoundToFetch()
  499     {
  500         return $this->ajaxHandlerBindToFetch;
  501     }
  502 
  503     /**
  504      * Sets whether to call bindToJquery() on the ajax handler
  505      *
  506      * @param boolean $bind
  507      */
  508     public function setBindAjaxHandlerToJquery($bind = true)
  509     {
  510         $this->ajaxHandlerBindToJquery = $bind;
  511         return $this;
  512     }
  513 
  514     /**
  515      * Checks whether bindToJquery() will be called on the ajax handler
  516      *
  517      * @return boolean
  518      */
  519     public function isAjaxHandlerBoundToJquery()
  520     {
  521         return $this->ajaxHandlerBindToJquery;
  522     }
  523 
  524     /**
  525      * Sets whether to call bindToXHR() on the ajax handler
  526      *
  527      * @param boolean $bind
  528      */
  529     public function setBindAjaxHandlerToXHR($bind = true)
  530     {
  531         $this->ajaxHandlerBindToXHR = $bind;
  532         return $this;
  533     }
  534 
  535     /**
  536      * Checks whether bindToXHR() will be called on the ajax handler
  537      *
  538      * @return boolean
  539      */
  540     public function isAjaxHandlerBoundToXHR()
  541     {
  542         return $this->ajaxHandlerBindToXHR;
  543     }
  544 
  545     /**
  546      * Sets whether new ajax debug data will be immediately shown.  Setting to false could be useful
  547      * if there are a lot of tracking events cluttering things.
  548      *
  549      * @param boolean $autoShow
  550      */
  551     public function setAjaxHandlerAutoShow($autoShow = true)
  552     {
  553         $this->ajaxHandlerAutoShow = $autoShow;
  554         return $this;
  555     }
  556 
  557     /**
  558      * Checks whether the ajax handler will immediately show new ajax requests.
  559      *
  560      * @return boolean
  561      */
  562     public function isAjaxHandlerAutoShow()
  563     {
  564         return $this->ajaxHandlerAutoShow;
  565     }
  566 
  567     /**
  568      * Sets the class name of the js open handler
  569      *
  570      * @param string $className
  571      */
  572     public function setOpenHandlerClass($className)
  573     {
  574         $this->openHandlerClass = $className;
  575         return $this;
  576     }
  577 
  578     /**
  579      * Returns the class name of the js open handler
  580      *
  581      * @return string
  582      */
  583     public function getOpenHandlerClass()
  584     {
  585         return $this->openHandlerClass;
  586     }
  587 
  588     /**
  589      * Sets the url of the open handler
  590      *
  591      * @param string $url
  592      */
  593     public function setOpenHandlerUrl($url)
  594     {
  595         $this->openHandlerUrl = $url;
  596         return $this;
  597     }
  598 
  599     /**
  600      * Returns the url for the open handler
  601      *
  602      * @return string
  603      */
  604     public function getOpenHandlerUrl()
  605     {
  606         return $this->openHandlerUrl;
  607     }
  608 
  609     /**
  610      * Add assets stored in files to render in the head
  611      *
  612      * @param array $cssFiles An array of filenames
  613      * @param array $jsFiles  An array of filenames
  614      * @param string $basePath Base path of those files
  615      * @param string $baseUrl  Base url of those files
  616      * @return $this
  617      */
  618     public function addAssets($cssFiles, $jsFiles, $basePath = null, $baseUrl = null)
  619     {
  620         $this->additionalAssets[] = array(
  621             'base_path' => $basePath,
  622             'base_url' => $baseUrl,
  623             'css' => (array) $cssFiles,
  624             'js' => (array) $jsFiles
  625         );
  626         return $this;
  627     }
  628 
  629     /**
  630      * Add inline assets to render inline in the head.  Ideally, you should store static assets in
  631      * files that you add with the addAssets function.  However, adding inline assets is useful when
  632      * integrating with 3rd-party libraries that require static assets that are only available in an
  633      * inline format.
  634      *
  635      * The inline content arrays require special string array keys:  they are used to deduplicate
  636      * content.  This is particularly useful if multiple instances of the same asset end up being
  637      * added.  Inline assets from all collectors are merged together into the same array, so these
  638      * content IDs effectively deduplicate the inline assets.
  639      *
  640      * @param array $inlineCss  An array map of content ID to inline CSS content (not including <style> tag)
  641      * @param array $inlineJs   An array map of content ID to inline JS content (not including <script> tag)
  642      * @param array $inlineHead An array map of content ID to arbitrary inline HTML content (typically
  643      *                          <style>/<script> tags); it must be embedded within the <head> element
  644      * @return $this
  645      */
  646     public function addInlineAssets($inlineCss, $inlineJs, $inlineHead)
  647     {
  648         $this->additionalAssets[] = array(
  649             'inline_css' => (array) $inlineCss,
  650             'inline_js' => (array) $inlineJs,
  651             'inline_head' => (array) $inlineHead
  652         );
  653         return $this;
  654     }
  655 
  656     /**
  657      * Returns the list of asset files
  658      *
  659      * @param string $type 'css', 'js', 'inline_css', 'inline_js', 'inline_head', or null for all
  660      * @param string $relativeTo The type of path to which filenames must be relative (path, url or null)
  661      * @return array
  662      */
  663     public function getAssets($type = null, $relativeTo = self::RELATIVE_PATH)
  664     {
  665         $cssFiles = $this->cssFiles;
  666         $jsFiles = $this->jsFiles;
  667         $inlineCss = array();
  668         $inlineJs = array();
  669         $inlineHead = array();
  670 
  671         if ($this->includeVendors !== false) {
  672             if ($this->includeVendors === true || in_array('css', $this->includeVendors)) {
  673                 $cssFiles = array_merge($this->cssVendors, $cssFiles);
  674             }
  675             if ($this->includeVendors === true || in_array('js', $this->includeVendors)) {
  676                 $jsFiles = array_merge($this->jsVendors, $jsFiles);
  677             }
  678         }
  679 
  680         if ($relativeTo) {
  681             $root = $this->getRelativeRoot($relativeTo, $this->basePath, $this->baseUrl);
  682             $cssFiles = $this->makeUriRelativeTo($cssFiles, $root);
  683             $jsFiles = $this->makeUriRelativeTo($jsFiles, $root);
  684         }
  685 
  686         $additionalAssets = $this->additionalAssets;
  687         // finds assets provided by collectors
  688         foreach ($this->debugBar->getCollectors() as $collector) {
  689             if (($collector instanceof AssetProvider) && !in_array($collector->getName(), $this->ignoredCollectors)) {
  690                 $additionalAssets[] = $collector->getAssets();
  691             }
  692         }
  693 
  694         foreach ($additionalAssets as $assets) {
  695             $basePath = isset($assets['base_path']) ? $assets['base_path'] : null;
  696             $baseUrl = isset($assets['base_url']) ? $assets['base_url'] : null;
  697             $root = $this->getRelativeRoot($relativeTo,
  698                 $this->makeUriRelativeTo($basePath, $this->basePath),
  699                 $this->makeUriRelativeTo($baseUrl, $this->baseUrl));
  700             if (isset($assets['css'])) {
  701                 $cssFiles = array_merge($cssFiles, $this->makeUriRelativeTo((array) $assets['css'], $root));
  702             }
  703             if (isset($assets['js'])) {
  704                 $jsFiles = array_merge($jsFiles, $this->makeUriRelativeTo((array) $assets['js'], $root));
  705             }
  706 
  707             if (isset($assets['inline_css'])) {
  708                 $inlineCss = array_merge($inlineCss, (array) $assets['inline_css']);
  709             }
  710             if (isset($assets['inline_js'])) {
  711                 $inlineJs = array_merge($inlineJs, (array) $assets['inline_js']);
  712             }
  713             if (isset($assets['inline_head'])) {
  714                 $inlineHead = array_merge($inlineHead, (array) $assets['inline_head']);
  715             }
  716         }
  717 
  718         // Deduplicate files
  719         $cssFiles = array_unique($cssFiles);
  720         $jsFiles = array_unique($jsFiles);
  721 
  722         return $this->filterAssetArray(array($cssFiles, $jsFiles, $inlineCss, $inlineJs, $inlineHead), $type);
  723     }
  724 
  725     /**
  726      * Returns the correct base according to the type
  727      *
  728      * @param string $relativeTo
  729      * @param string $basePath
  730      * @param string $baseUrl
  731      * @return string
  732      */
  733     protected function getRelativeRoot($relativeTo, $basePath, $baseUrl)
  734     {
  735         if ($relativeTo === self::RELATIVE_PATH) {
  736             return $basePath;
  737         }
  738         if ($relativeTo === self::RELATIVE_URL) {
  739             return $baseUrl;
  740         }
  741         return null;
  742     }
  743 
  744     /**
  745      * Makes a URI relative to another
  746      *
  747      * @param string|array $uri
  748      * @param string $root
  749      * @return string
  750      */
  751     protected function makeUriRelativeTo($uri, $root)
  752     {
  753         if (!$root) {
  754             return $uri;
  755         }
  756 
  757         if (is_array($uri)) {
  758             $uris = array();
  759             foreach ($uri as $u) {
  760                 $uris[] = $this->makeUriRelativeTo($u, $root);
  761             }
  762             return $uris;
  763         }
  764 
  765         if (substr($uri, 0, 1) === '/' || preg_match('/^([a-zA-Z]+:\/\/|[a-zA-Z]:\/|[a-zA-Z]:\\\)/', $uri)) {
  766             return $uri;
  767         }
  768         return rtrim($root, '/') . "/$uri";
  769     }
  770 
  771     /**
  772      * Filters a tuple of (css, js, inline_css, inline_js, inline_head) assets according to $type
  773      *
  774      * @param array $array
  775      * @param string $type 'css', 'js', 'inline_css', 'inline_js', 'inline_head', or null for all
  776      * @return array
  777      */
  778     protected function filterAssetArray($array, $type = null)
  779     {
  780         $types = array('css', 'js', 'inline_css', 'inline_js', 'inline_head');
  781         $typeIndex = array_search(strtolower($type), $types);
  782         return $typeIndex !== false ? $array[$typeIndex] : $array;
  783     }
  784 
  785     /**
  786      * Returns an array where all items are Assetic AssetCollection:
  787      *  - The first one contains the CSS files
  788      *  - The second one contains the JS files
  789      *  - The third one contains arbitrary inline HTML (typically composed of <script>/<style>
  790      *    elements); it must be embedded within the <head> element
  791      *
  792      * @param string $type Optionally return only 'css', 'js', or 'inline_head' collection
  793      * @return array|\Assetic\Asset\AssetCollection
  794      */
  795     public function getAsseticCollection($type = null)
  796     {
  797         $types = array('css', 'js', 'inline_head');
  798         $typeIndex = array_search(strtolower($type), $types);
  799 
  800         list($cssFiles, $jsFiles, $inlineCss, $inlineJs, $inlineHead) = $this->getAssets();
  801         $collections = array(
  802             $this->createAsseticCollection($cssFiles, $inlineCss),
  803             $this->createAsseticCollection($jsFiles, $inlineJs),
  804             $this->createAsseticCollection(null, $inlineHead)
  805         );
  806         return $typeIndex !== false ? $collections[$typeIndex] : $collections;
  807     }
  808 
  809     /**
  810      * Create an Assetic AssetCollection with the given content.
  811      * Filenames will be converted to absolute path using
  812      * the base path.
  813      *
  814      * @param array|null $files Array of asset filenames.
  815      * @param array|null $content Array of inline asset content.
  816      * @return \Assetic\Asset\AssetCollection
  817      */
  818     protected function createAsseticCollection($files = null, $content = null)
  819     {
  820         $assets = array();
  821         if ($files) {
  822             foreach ($files as $file) {
  823                 $assets[] = new \Assetic\Asset\FileAsset($file);
  824             }
  825         }
  826         if ($content) {
  827             foreach ($content as $item) {
  828                 $assets[] = new \Assetic\Asset\StringAsset($item);
  829             }
  830         }
  831         return new \Assetic\Asset\AssetCollection($assets);
  832     }
  833 
  834     /**
  835      * Write all CSS assets to standard output or in a file
  836      *
  837      * @param string $targetFilename
  838      */
  839     public function dumpCssAssets($targetFilename = null)
  840     {
  841         $this->dumpAssets($this->getAssets('css'), $this->getAssets('inline_css'), $targetFilename);
  842     }
  843 
  844     /**
  845      * Write all JS assets to standard output or in a file
  846      *
  847      * @param string $targetFilename
  848      */
  849     public function dumpJsAssets($targetFilename = null)
  850     {
  851         $this->dumpAssets($this->getAssets('js'), $this->getAssets('inline_js'), $targetFilename, $this->useRequireJs);
  852     }
  853 
  854     /**
  855      * Write all inline HTML header assets to standard output or in a file (only returns assets not
  856      * already returned by dumpCssAssets or dumpJsAssets)
  857      *
  858      * @param string $targetFilename
  859      */
  860     public function dumpHeadAssets($targetFilename = null)
  861     {
  862         $this->dumpAssets(null, $this->getAssets('inline_head'), $targetFilename);
  863     }
  864 
  865     /**
  866      * Write assets to standard output or in a file
  867      *
  868      * @param array|null $files Filenames containing assets
  869      * @param array|null $content Inline content to dump
  870      * @param string $targetFilename
  871      * @param bool $useRequireJs
  872      */
  873     protected function dumpAssets($files = null, $content = null, $targetFilename = null, $useRequireJs = false)
  874     {
  875         $dumpedContent = '';
  876         if ($files) {
  877             foreach ($files as $file) {
  878                 $dumpedContent .= file_get_contents($file) . "\n";
  879             }
  880         }
  881         if ($content) {
  882             foreach ($content as $item) {
  883                 $dumpedContent .= $item . "\n";
  884             }
  885         }
  886         if ($useRequireJs) {
  887             $dumpedContent = "define('debugbar', ['jquery'], function($){\r\n" . $dumpedContent . "\r\n return PhpDebugBar; \r\n});";
  888         }
  889         if ($targetFilename !== null) {
  890             file_put_contents($targetFilename, $dumpedContent);
  891         } else {
  892             echo $dumpedContent;
  893         }
  894     }
  895 
  896     /**
  897      * Renders the html to include needed assets
  898      *
  899      * Only useful if Assetic is not used
  900      *
  901      * @return string
  902      */
  903     public function renderHead()
  904     {
  905         list($cssFiles, $jsFiles, $inlineCss, $inlineJs, $inlineHead) = $this->getAssets(null, self::RELATIVE_URL);
  906         $html = '';
  907 
  908         foreach ($cssFiles as $file) {
  909             $html .= sprintf('<link rel="stylesheet" type="text/css" href="%s">' . "\n", $file);
  910         }
  911 
  912         foreach ($inlineCss as $content) {
  913             $html .= sprintf('<style type="text/css">%s</style>' . "\n", $content);
  914         }
  915 
  916         foreach ($jsFiles as $file) {
  917             $html .= sprintf('<script type="text/javascript" src="%s"></script>' . "\n", $file);
  918         }
  919 
  920         foreach ($inlineJs as $content) {
  921             $html .= sprintf('<script type="text/javascript">%s</script>' . "\n", $content);
  922         }
  923 
  924         foreach ($inlineHead as $content) {
  925             $html .= $content . "\n";
  926         }
  927 
  928         if ($this->enableJqueryNoConflict && !$this->useRequireJs) {
  929             $html .= '<script type="text/javascript">jQuery.noConflict(true);</script>' . "\n";
  930         }
  931 
  932         return $html;
  933     }
  934 
  935     /**
  936      * Register shutdown to display the debug bar
  937      *
  938      * @param boolean $here Set position of HTML. True if is to current position or false for end file
  939      * @param boolean $initialize Whether to render the de bug bar initialization code
  940      * @param bool $renderStackedData
  941      * @param bool $head
  942      * @return string Return "{--DEBUGBAR_OB_START_REPLACE_ME--}" or return an empty string if $here == false
  943      */
  944     public function renderOnShutdown($here = true, $initialize = true, $renderStackedData = true, $head = false)
  945     {
  946         register_shutdown_function(array($this, "replaceTagInBuffer"), $here, $initialize, $renderStackedData, $head);
  947 
  948         if (ob_get_level() === 0) {
  949             ob_start();
  950         }
  951 
  952         return ($here) ? self::REPLACEABLE_TAG : "";
  953     }
  954 
  955     /**
  956      * Same as renderOnShutdown() with $head = true
  957      *
  958      * @param boolean $here
  959      * @param boolean $initialize
  960      * @param boolean $renderStackedData
  961      * @return string
  962      */
  963     public function renderOnShutdownWithHead($here = true, $initialize = true, $renderStackedData = true)
  964     {
  965         return $this->renderOnShutdown($here, $initialize, $renderStackedData, true);
  966     }
  967 
  968     /**
  969      * Is callback function for register_shutdown_function(...)
  970      *
  971      * @param boolean $here Set position of HTML. True if is to current position or false for end file
  972      * @param boolean $initialize Whether to render the de bug bar initialization code
  973      * @param bool $renderStackedData
  974      * @param bool $head
  975      */
  976     public function replaceTagInBuffer($here = true, $initialize = true, $renderStackedData = true, $head = false)
  977     {
  978         $render = ($head ? $this->renderHead() : "")
  979                 . $this->render($initialize, $renderStackedData);
  980 
  981         $current = ($here && ob_get_level() > 0) ? ob_get_clean() : self::REPLACEABLE_TAG;
  982 
  983         echo str_replace(self::REPLACEABLE_TAG, $render, $current, $count);
  984 
  985         if ($count === 0) {
  986             echo $render;
  987         }
  988     }
  989 
  990     /**
  991      * Returns the code needed to display the debug bar
  992      *
  993      * AJAX request should not render the initialization code.
  994      *
  995      * @param boolean $initialize Whether or not to render the debug bar initialization code
  996      * @param boolean $renderStackedData Whether or not to render the stacked data
  997      * @return string
  998      */
  999     public function render($initialize = true, $renderStackedData = true)
 1000     {
 1001         $js = '';
 1002 
 1003         if ($initialize) {
 1004             $js = $this->getJsInitializationCode();
 1005         }
 1006 
 1007         if ($renderStackedData && $this->debugBar->hasStackedData()) {
 1008             foreach ($this->debugBar->getStackedData() as $id => $data) {
 1009                 $js .= $this->getAddDatasetCode($id, $data, '(stacked)');
 1010             }
 1011         }
 1012 
 1013         $suffix = !$initialize ? '(ajax)' : null;
 1014         $js .= $this->getAddDatasetCode($this->debugBar->getCurrentRequestId(), $this->debugBar->getData(), $suffix);
 1015 
 1016         if ($this->useRequireJs){
 1017             return "<script type=\"text/javascript\">\nrequire(['debugbar'], function(PhpDebugBar){ $js });\n</script>\n";
 1018         } else {
 1019             return "<script type=\"text/javascript\">\n$js\n</script>\n";
 1020         }
 1021 
 1022     }
 1023 
 1024     /**
 1025      * Returns the js code needed to initialize the debug bar
 1026      *
 1027      * @return string
 1028      */
 1029     protected function getJsInitializationCode()
 1030     {
 1031         $js = '';
 1032 
 1033         if (($this->initialization & self::INITIALIZE_CONSTRUCTOR) === self::INITIALIZE_CONSTRUCTOR) {
 1034             $js .= sprintf("var %s = new %s();\n", $this->variableName, $this->javascriptClass);
 1035         }
 1036 
 1037         if (($this->initialization & self::INITIALIZE_CONTROLS) === self::INITIALIZE_CONTROLS) {
 1038             $js .= $this->getJsControlsDefinitionCode($this->variableName);
 1039         }
 1040 
 1041         if ($this->ajaxHandlerClass) {
 1042             $js .= sprintf("%s.ajaxHandler = new %s(%s, undefined, %s);\n",
 1043                 $this->variableName,
 1044                 $this->ajaxHandlerClass,
 1045                 $this->variableName,
 1046                 $this->ajaxHandlerAutoShow ? 'true' : 'false'
 1047             );
 1048             if ($this->ajaxHandlerBindToFetch) {
 1049                 $js .= sprintf("%s.ajaxHandler.bindToFetch();\n", $this->variableName);
 1050             }
 1051             if ($this->ajaxHandlerBindToXHR) {
 1052                 $js .= sprintf("%s.ajaxHandler.bindToXHR();\n", $this->variableName);
 1053             } elseif ($this->ajaxHandlerBindToJquery) {
 1054                 $js .= sprintf("if (jQuery) %s.ajaxHandler.bindToJquery(jQuery);\n", $this->variableName);
 1055             }
 1056         }
 1057 
 1058         if ($this->openHandlerUrl !== null) {
 1059             $js .= sprintf("%s.setOpenHandler(new %s(%s));\n", $this->variableName,
 1060                 $this->openHandlerClass,
 1061                 json_encode(array("url" => $this->openHandlerUrl)));
 1062         }
 1063 
 1064         return $js;
 1065     }
 1066 
 1067     /**
 1068      * Returns the js code needed to initialized the controls and data mapping of the debug bar
 1069      *
 1070      * Controls can be defined by collectors themselves or using {@see addControl()}
 1071      *
 1072      * @param string $varname Debug bar's variable name
 1073      * @return string
 1074      */
 1075     protected function getJsControlsDefinitionCode($varname)
 1076     {
 1077         $js = '';
 1078         $dataMap = array();
 1079         $excludedOptions = array('indicator', 'tab', 'map', 'default', 'widget', 'position');
 1080 
 1081         // finds controls provided by collectors
 1082         $widgets = array();
 1083         foreach ($this->debugBar->getCollectors() as $collector) {
 1084             if (($collector instanceof Renderable) && !in_array($collector->getName(), $this->ignoredCollectors)) {
 1085                 if ($w = $collector->getWidgets()) {
 1086                     $widgets = array_merge($widgets, $w);
 1087                 }
 1088             }
 1089         }
 1090         $controls = array_merge($widgets, $this->controls);
 1091 
 1092         foreach (array_filter($controls) as $name => $options) {
 1093             $opts = array_diff_key($options, array_flip($excludedOptions));
 1094 
 1095             if (isset($options['tab']) || isset($options['widget'])) {
 1096                 if (!isset($opts['title'])) {
 1097                     $opts['title'] = ucfirst(str_replace('_', ' ', $name));
 1098                 }
 1099                 $js .= sprintf("%s.addTab(\"%s\", new %s({%s%s}));\n",
 1100                     $varname,
 1101                     $name,
 1102                     isset($options['tab']) ? $options['tab'] : 'PhpDebugBar.DebugBar.Tab',
 1103                     substr(json_encode($opts, JSON_FORCE_OBJECT), 1, -1),
 1104                     isset($options['widget']) ? sprintf('%s"widget": new %s()', count($opts) ? ', ' : '', $options['widget']) : ''
 1105                 );
 1106             } elseif (isset($options['indicator']) || isset($options['icon'])) {
 1107                 $js .= sprintf("%s.addIndicator(\"%s\", new %s(%s), \"%s\");\n",
 1108                     $varname,
 1109                     $name,
 1110                     isset($options['indicator']) ? $options['indicator'] : 'PhpDebugBar.DebugBar.Indicator',
 1111                     json_encode($opts, JSON_FORCE_OBJECT),
 1112                     isset($options['position']) ? $options['position'] : 'right'
 1113                 );
 1114             }
 1115 
 1116             if (isset($options['map']) && isset($options['default'])) {
 1117                 $dataMap[$name] = array($options['map'], $options['default']);
 1118             }
 1119         }
 1120 
 1121         // creates the data mapping object
 1122         $mapJson = array();
 1123         foreach ($dataMap as $name => $values) {
 1124             $mapJson[] = sprintf('"%s": ["%s", %s]', $name, $values[0], $values[1]);
 1125         }
 1126         $js .= sprintf("%s.setDataMap({\n%s\n});\n", $varname, implode(",\n", $mapJson));
 1127 
 1128         // activate state restoration
 1129         $js .= sprintf("%s.restoreState();\n", $varname);
 1130 
 1131         return $js;
 1132     }
 1133 
 1134     /**
 1135      * Returns the js code needed to add a dataset
 1136      *
 1137      * @param string $requestId
 1138      * @param array $data
 1139      * @param mixed $suffix
 1140      * @return string
 1141      */
 1142     protected function getAddDatasetCode($requestId, $data, $suffix = null)
 1143     {
 1144         $js = sprintf("%s.addDataSet(%s, \"%s\"%s);\n",
 1145             $this->variableName,
 1146             json_encode($data),
 1147             $requestId,
 1148             $suffix ? ", " . json_encode($suffix) : ''
 1149         );
 1150         return $js;
 1151     }
 1152 }