Spade

Mini Shell

Directory:~$ /home/lmsyaran/www/css/
Upload File

[Home] [System Details] [Kill Me]
Current File:~$ /home/lmsyaran/www/css/gantry5.tar

preset/language/en-GB/en-GB.plg_gantry5_preset.sys.ini000064400000000723151166461500016552
0ustar00PLG_GANTRY5_PRESET="Gantry 5 - Presets"
PLG_GANTRY5_PRESET_DESCRIPTION="Enable Gantry 5 presets selection by
appending variable to the URL."

PLG_GANTRY5_OPTION_PRESET_LABEL="Preset action"
PLG_GANTRY5_OPTION_PRESET_DESC="Variable name for setting preset.
Defaults to '?presets=xxx', where xxx is the name of the
preset."
PLG_GANTRY5_OPTION_RESET_LABEL="Reset action"
PLG_GANTRY5_OPTION_RESET_DESC="Variable name for resetting preset.
Defaults to '?reset-settings'."
preset/MD5SUMS000064400000000322151166461500007070
0ustar00preset.php	375b207b98844b9d52b4568047dd0cf5
language/en-GB/en-GB.plg_gantry5_preset.sys.ini	027a921657ca1866f0578725b1e71eb5
MD5SUMS	d41d8cd98f00b204e9800998ecf8427e
preset.xml	993ec956bdf6abca05b3ec2d8655f613
preset/preset.php000064400000004360151166461500010071 0ustar00<?php
/**
 * @package   Gantry 5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */
defined('_JEXEC') or die;

class plgGantry5Preset extends JPlugin
{
    public function __construct(&$subject, $config)
    {
        // Do not load if Gantry libraries are not installed or
initialised.
        if (!class_exists('Gantry5\Loader')) return;

        parent::__construct($subject, $config);

        // Always load language.
        $lang = JFactory::getLanguage();
        $lang->load('com_gantry5.sys') ||
$lang->load('com_gantry5.sys', JPATH_ADMINISTRATOR .
'/components/com_gantry5');
        $this->loadLanguage('plg_quickicon_gantry5.sys');
    }

    public function onGantry5ThemeInit($theme)
    {
        $app = JFactory::getApplication();

        if ($app->isSite()) {
            $input = $app->input;

            $cookie = md5($theme->name);
            $presetVar = $this->params->get('preset',
'presets');
            $resetVar = $this->params->get('reset',
'reset-settings');

            if ($input->getCmd($resetVar) !== null) {
                $preset = false;
            } else {
                $preset = $input->getCmd($presetVar);
            }


            if ($preset !== null) {
                if ($preset === false) {
                    // Invalidate the cookie.
                    $this->updateCookie($cookie, false, time() - 42000);
                } else {
                    // Update the cookie.
                    $this->updateCookie($cookie, $preset, 0);
                }
            } else {
                $preset = $input->cookie->getString($cookie);
            }

            $theme->setPreset($preset);
        }
    }

    public function onGantry5UpdateCss($theme)
    {
        $cookie = md5($theme->name);

        $this->updateCookie($cookie, false, time() - 42000);
    }

    protected function updateCookie($name, $value, $expire = 0)
    {
        $app = JFactory::getApplication();
        $path   = $app->get('cookie_path', '/');
        $domain = $app->get('cookie_domain');

        $input = $app->input;
        $input->cookie->set($name, $value, $expire, $path, $domain);
    }
}
preset/preset.xml000064400000002613151166461500010101 0ustar00<?xml
version="1.0" encoding="UTF-8"
standalone="no"?>
<extension version="3.4" type="plugin"
group="gantry5" method="upgrade">
    <name>plg_gantry5_preset</name>
    <version>5.4.37</version>
    <creationDate>January 25, 2021</creationDate>
    <author>RocketTheme, LLC</author>
    <authorEmail>support@rockettheme.com</authorEmail>
    <authorUrl>http://www.rockettheme.com</authorUrl>
    <copyright>(C) 2005 - 2019 RocketTheme, LLC. All rights
reserved.</copyright>
    <license>http://www.gnu.org/licenses/gpl-2.0.html
GNU/GPL</license>
    <description>PLG_GANTRY5_PRESET_DESCRIPTION</description>

    <files>
        <filename
plugin="preset">preset.php</filename>
        <folder>language</folder>
    </files>

    <config>
        <fields name="params">
            <fieldset name="basic">
                <field name="preset"
                       type="text"
                       default="presets"
                      
description="PLG_GANTRY5_OPTION_PRESET_DESC"
                       label="PLG_GANTRY5_OPTION_PRESET_LABEL"
                        />
                <field name="reset"
                       type="text"
                       default="reset-settings"
                      
description="PLG_GANTRY5_OPTION_RESET_DESC"
                       label="PLG_GANTRY5_OPTION_RESET_LABEL"
                        />
            </fieldset>
        </fields>
    </config>
</extension>
bootstrap.php000064400000000665151166614510007307 0ustar00<?php

/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

include __DIR__ . '/Loader.php';

return \Gantry5\Loader::get();
classes/Gantry/Admin/Controller/Html/About.php000064400000001413151166614510015314
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Admin\ThemeList;
use Gantry\Component\Admin\HtmlController;

class About extends HtmlController
{
    public function index()
    {
        // TODO: Find better way:
        $this->params['info'] = (new
ThemeList)->getTheme($this->container['theme.name']);

        return
$this->render('@gantry-admin/pages/about/about.html.twig',
$this->params);
    }
}
classes/Gantry/Admin/Controller/Html/Cache.php000064400000002131151166614510015243
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Filesystem\Folder;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Cache extends HtmlController
{
    public function index()
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        Folder::delete($locator('gantry-cache://theme'), false);
        Folder::delete($locator('gantry-cache://admin'), false);

        // Make sure that PHP has the latest data of the files.
        clearstatcache();

        return new JsonResponse(['html' => 'Cache was
successfully cleared', 'title' => 'Cache
Cleared']);
    }
}
classes/Gantry/Admin/Controller/Html/Configurations/Assignments.php000064400000004435151166614510021536
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html\Configurations;

use Gantry\Component\Admin\HtmlController;
use Gantry\Framework\Assignments as AssignmentsObject;
use RocketTheme\Toolbox\Event\Event;

class Assignments extends HtmlController
{
    public function index()
    {
        $outline = $this->params['outline'];

        if ($this->hasAssignments($outline)) {
            $assignments = new AssignmentsObject($outline);

            $this->params['assignments'] =
$assignments->get();
            $this->params['options'] =
$assignments->assignmentOptions();
            $this->params['assignment'] =
$assignments->getAssignment();
        }

        return
$this->render('@gantry-admin/pages/configurations/assignments/assignments.html.twig',
$this->params);
    }

    public function store()
    {
        // Authorization.
        if (!$this->authorize('outline.assign')) {
            $this->forbidden();
        }

        $outline = $this->params['outline'];
        if (!$this->hasAssignments($outline)) {
            $this->undefined();
        }

        if (!$this->request->post->get('_end')) {
            throw new \OverflowException("Incomplete data received.
Please increase the value of 'max_input_vars' variable (in
php.ini or .htaccess)", 400);
        }

        // Save assignments.
        $assignments = new AssignmentsObject($outline);
       
$assignments->save($this->request->post->getArray('assignments'));

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->theme = $this->container['theme'];
        $event->controller = $this;
        $event->assignments = $assignments;
       
$this->container->fireEvent('admin.assignments.save',
$event);

        return '';
    }

    protected function hasAssignments($outline)
    {
        // Default outline and system outlines cannot have assignments.
        return $outline !== 'default' && $outline[0] !==
'_';
    }
}
classes/Gantry/Admin/Controller/Html/Configurations/Layout.php000064400000043155151166614510020522
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html\Configurations;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Layout\Layout as LayoutObject;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Outlines;
use RocketTheme\Toolbox\Event\Event;

class Layout extends HtmlController
{
    protected $httpVerbs = [
        'GET'    => [
            '/'         => 'index',
            '/create'   => 'create',
            '/create/*' => 'create',
            '/*'        => 'undefined',
            '/switch'   => 'listSwitches',
            '/switch/*' => 'switchLayout',
            '/preset'   => 'undefined',
            '/preset/*' => 'preset'
        ],
        'POST'   => [
            '/'                     => 'save',
            '/*'                    => 'undefined',
            '/*/*'                  => 'particle',
            '/switch'               => 'undefined',
            '/switch/*'             =>
'switchLayout',
            '/preset'               => 'undefined',
            '/preset/*'             => 'preset',
            '/particles'            => 'undefined',
            '/particles/*'          => 'undefined',
            '/particles/*/validate' => 'validate'
        ],
        'PUT'    => [
            '/*' => 'replace'
        ],
        'PATCH'  => [
            '/*' => 'update'
        ],
        'DELETE' => [
            '/*' => 'destroy'
        ]
    ];

    public function create($id = null)
    {
        if (!$id) {
            // TODO: we might want to display list of options here
            throw new \RuntimeException('Not Implemented', 404);
        }

        $layout = $this->getLayout("presets/{$id}");
        if (!$layout) {
            throw new \RuntimeException('Preset not found', 404);
        }
        $this->params['page_id'] = $id;
        $this->params['layout'] =
$layout->prepareWidths()->toArray();

        return
$this->render('@gantry-admin/pages/configurations/layouts/create.html.twig',
$this->params);
    }

    public function index()
    {
        $outline = $this->params['outline'];
        $layout = $this->getLayout($outline);
        if (!$layout) {
            throw new \RuntimeException('Layout not found', 404);
        }

        $groups = [
            'Positions' => ['position' => [],
'spacer' => [], 'system' => []],
            'Particles' => ['particle' => []]
        ];

        $particles = [
            'position'    => [],
            'spacer'      => [],
            'system' => [],
            'particle' => []
        ];

        $particles = array_replace($particles, $this->getParticles());
        foreach ($particles as &$group) {
            asort($group);
        }
        unset($group);

        foreach ($groups as $section => $children) {
            foreach ($children as $key => $child) {
                $groups[$section][$key] = $particles[$key];
            }
        }

        $this->params['page_id'] = $outline;
        $this->params['layout'] =
$layout->prepareWidths()->toArray();
        $this->params['preset'] = $layout->preset;
        $this->params['preset_title'] =
ucwords(trim(str_replace('_', ' ',
$layout->preset['name'])));
        $this->params['id'] =
ucwords(str_replace('_', ' ', ltrim($outline,
'_')));
        $this->params['particles'] = $groups;
        $this->params['switcher_url'] =
str_replace('.', '/',
"configurations.{$outline}.layout.switch");

        return
$this->render('@gantry-admin/pages/configurations/layouts/edit.html.twig',
$this->params);
    }

    public function save()
    {
        $layout = $this->request->post->get('layout');
        $layout = json_decode($layout);

        if (!isset($layout)) {
            throw new \RuntimeException('Error while saving layout:
Structure missing', 400);
        }

        $outline = $this->params['outline'];
        $preset =
$this->request->post->getJsonArray('preset');

        // Create layout from the data.
        $layout = new LayoutObject($outline, $layout, $preset);
        $layout->init(false, false);

        /** @var Outlines $outlines */
        $outlines = $this->container['outlines'];

        // Update layouts from all inheriting outlines.
        $elements = $layout->sections() + $layout->particles(false);
        foreach ($outlines->getInheritingOutlines($outline) as
$inheritedId => $inheritedName) {
           
LayoutObject::instance($inheritedId)->updateInheritance($outline,
$outline, $elements)->save()->saveIndex();
        }

        // Save layout and its index.
        $layout->save()->saveIndex();

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->theme = $this->container['theme'];
        $event->controller = $this;
        $event->layout = $layout;
        $this->container->fireEvent('admin.layout.save',
$event);
    }

    public function particle($type, $id)
    {
        if ($type === 'atom') { return ''; }

        $outline = $this->params['outline'];
        $layout = $this->getLayout($outline);
        if (!$layout) {
            throw new \RuntimeException('Layout not found', 404);
        }

        $item = $layout->find($id);
        $item->type    = $this->request->post['type'] ?:
$type;
        $item->subtype = $this->request->post['subtype']
?: $type;
        $item->title   = $this->request->post['title']
?: ucfirst($type);
        $parent   = $this->request->post['parent'] ?:
$layout->getParentId($id);
        if (!isset($item->attributes)) {
            $item->attributes = new \stdClass;
        }
        if (!isset($item->inherit)) {
            $item->inherit = new \stdClass;
        }

        $block =
$this->request->post->getArray('block');
        if (!empty($block)) {
            $item->block = (object) $block;
        }

        $attributes =
$this->request->post->getArray('options');
        $inherit =
$this->request->post->getArray('inherit');

        $particle = !$layout->isLayoutType($type);
        if (!$particle) {
            $name = $type;
            $section = ($type === 'section');
            $hasBlock = $section && !empty($block);
            $prefix = "particles.{$type}";
            $defaults = [];
            $attributes += (array) $item->attributes;
            $blueprints =
BlueprintForm::instance("layout/{$type}.yaml",
'gantry-admin://blueprints');

        } else {
            $name = $item->subtype;
            $hasBlock = true;
            $prefix = "particles.{$name}";
            $defaults = (array)
$this->container['config']->get($prefix);

            $blueprints =
$this->container['particles']->getBlueprintForm($name);
            $blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
        }

        if ($hasBlock) {
            $blockBlueprints =
BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');
        } else {
            $blockBlueprints = null;
        }

        $file =
"gantry-admin://blueprints/layout/inheritance/{$type}.yaml";
        if (file_exists($file)) {
            $inheritType = $particle ? 'particle' :
'section';

            /** @var Outlines $outlines */
            $outlines = $this->container['outlines'];

            if ($outline !== 'default') {
                if ($particle) {
                    $list =
$outlines->getOutlinesWithParticle($item->subtype, false);
                } else {
                    $list =
$outlines->getOutlinesWithSection($item->id, false);
                }
                unset($list[$outline]);
            } else {
                $list = [];
            }

            if (!empty($inherit['outline']) || (!($inheriting =
$outlines->getInheritingOutlines($outline, [$id, $parent])) &&
$list)) {
                $inheritable = true;
                $inheritance = BlueprintForm::instance($file,
'gantry-admin://blueprints');

               
$inheritance->set('form/fields/outline/filter',
array_keys($list));
                if (!$hasBlock) {
                   
$inheritance->undef('form/fields/include/options/block');
                }

                if ($particle) {
                   
$inheritance->set('form/fields/particle/particle', $name);
                }

            } elseif (!empty($inheriting)) {
                // Already inherited by other outlines.
                $inheritance =
BlueprintForm::instance('layout/inheritance/messages/inherited.yaml',
'gantry-admin://blueprints');
                $inheritance->set(
                    'form/fields/_note/content',
                   
sprintf($inheritance->get('form/fields/_note/content'),
$inheritType, ' <ul><li>' . implode('</li>
<li>', $inheriting) . '</li></ul>')
                );

            } elseif ($outline === 'default') {
                // Base outline.
                $inheritance =
BlueprintForm::instance('layout/inheritance/messages/default.yaml',
'gantry-admin://blueprints');

            } else {
                // Nothing to inherit from.
                $inheritance =
BlueprintForm::instance('layout/inheritance/messages/empty.yaml',
'gantry-admin://blueprints');
            }
        }

        $item->attributes = (object) $attributes;
        $item->inherit = (object) $inherit;

        $this->params['id'] = $name;
        $this->params += [
            'extra'         => $blockBlueprints,
            'inherit'       =>
!empty($inherit['outline']) ? $inherit['outline'] :
null,
            'inheritance'   => isset($inheritance) ?
$inheritance : null,
            'inheritable'   => !empty($inheritable),
            'item'          => $item,
            'data'          => ['particles' =>
[$name => $item->attributes]],
            'defaults'      => ['particles' =>
[$name => $defaults]],
            'prefix'        => "particles.{$name}.",
            'particle'      => $blueprints,
            'parent'        => 'settings',
            'route'         =>
"configurations.{$outline}.settings",
            'action'        => str_replace('.',
'/', 'configurations.' . $outline .
'.layout.' . $prefix . '.validate'),
            'skip'          => ['enabled'],
            'editable'      => $particle,
            'overrideable'  => $particle,
        ];

        if ($particle) {
            $result =
$this->render('@gantry-admin/pages/configurations/layouts/particle.html.twig',
$this->params);
        } else {
            $typeLayout = $type === 'container' ?
'container' : 'section';
            $result =
$this->render('@gantry-admin/pages/configurations/layouts/' .
$typeLayout . '.html.twig', $this->params);
        }

        return $result;
    }

    public function listSwitches()
    {
        $this->params['presets'] = LayoutObject::presets();
        $result =
$this->render('@gantry-admin/layouts/switcher.html.twig',
$this->params);

        return new JsonResponse(['html' => $result]);
    }

    public function switchLayout($id)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        $outline = $this->params['outline'];
        $layout = $this->getLayout($id);
        if (!$layout->toArray()) {
            // Layout hasn't been defined, return default layout
instead.
            $layout = $this->getLayout('default');
        }

        $input =
$this->request->post->getJson('layout');
        $deleted = isset($input) ?
$layout->clearSections()->copySections($input): [];
        if ($outline === 'default') {
            $layout->inheritNothing();
        } elseif (!$input &&
$this->request->post['inherit'] === '1') {
            $layout->inheritAll();
        }

        $message = $deleted
            ?
$this->render('@gantry-admin/ajax/particles-loss.html.twig',
['particles' => $deleted])
            : null;

        return new JsonResponse([
            'title' => ucwords(trim(str_replace('_',
' ', $layout->preset['name']))),
            'preset' => json_encode($layout->preset),
            'data' =>
$layout->prepareWidths()->toJson(),
            'deleted' => $deleted,
            'message' => $message
        ]);
    }

    public function preset($id)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        $preset = LayoutObject::preset($id);
        if (!$preset) {
            throw new \RuntimeException('Preset not found', 404);
        }

        $layout = new LayoutObject($id, $preset);

        $input =
$this->request->post->getJson('layout');
        $deleted = isset($input) ?
$layout->clearSections()->copySections($input): [];
        $message = $deleted
            ?
$this->render('@gantry-admin/ajax/particles-loss.html.twig',
['particles' => $deleted])
            : null;

        return new JsonResponse([
            'title' => ucwords(trim(str_replace('_',
' ', $id))),
            'preset' => json_encode($layout->preset),
            'data' =>
$layout->prepareWidths()->toJson(),
            'deleted' => $deleted,
            'message' => $message
        ]);
    }

    public function validate($particle)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        // Load particle blueprints and default settings.
        $validator = new BlueprintSchema;

        $name = $particle;
        if (in_array($particle, ['wrapper', 'section',
'container', 'grid', 'offcanvas'], true)) {
            $type = $particle;
            $particle = null;
            $file =
CompiledYamlFile::instance("gantry-admin://blueprints/layout/{$type}.yaml");
            $validator->embed('options',
(array)$file->content());
            $file->free();
        } else {
            $type = in_array($particle, ['spacer',
'system', 'position'], true) ? $particle : 
'particle';
            $validator->embed('options',
$this->container['particles']->get($particle));
        }

        // Create configuration from the defaults.
        $data = new Config(
            [
                'type'    => $type,
            ],
            function () use ($validator) {
                return $validator;
            }
        );

        // Join POST data.
        $data->join('options',
$this->request->post->getArray("particles." . $name));
        if ($particle) {
            $data->set('options.enabled', (int)
$data->get('options.enabled', 1));
        }

        if ($particle) {
            if ($type !== $particle) {
                $data->set('subtype', $particle);
            }

            $data->join('title',
$this->request->post['title'] ?: ucfirst($particle));
        }

        $block =
$this->request->post->getArray('block');
        if ($block) {
            // TODO: remove empty items in some other way:
            foreach ($block as $key => $param) {
                if ($param === '') {
                    unset($block[$key]);
                    continue;
                }
                if ($key === 'size') {
                    $param = round($param, 4);
                    if ($param < 5) {
                        $param = 5;
                    } elseif ($param > 100) {
                        $param = 100;
                    }
                    $block[$key] = $param;
                }
            }

            $data->join('block', $block);
        }

        $inherit =
$this->request->post->getArray('inherit');
        $clone = !empty($inherit['mode']) &&
$inherit['mode'] === 'clone';
        $inherit['include'] =
!empty($inherit['include']) ? explode(',',
$inherit['include']) : [];
        if (!$clone && !empty($inherit['outline'])
&& count($inherit['include'])) {
            // Clean up inherit and add it to the data.
            if (!$block) {
                $inherit['include'] =
array_values(array_diff($inherit['include'],
['block']));
            }

            unset($inherit['mode']);
            $data->join('inherit', $inherit);
        }

        // Optionally send children of the object.
        if (in_array('children', $inherit['include'],
true)) {
            $layout = LayoutObject::instance($inherit['outline']
?: $this->params['outline']);
            if ($clone) {
                $item = $layout->find($inherit['section']);
            } else {
                $item =
$layout->inheritAll()->find($inherit['section']);
            }
            $data->join('children', !empty($item->children)
? $item->children : []);
        }

        // TODO: validate

        return new JsonResponse(['data' =>
$data->toArray()]);
    }

    /**
     * @param string $name
     * @return LayoutObject
     */
    protected function getLayout($name)
    {
        return LayoutObject::instance($name);
    }

    protected function getParticles($onlyEnabled = false)
    {
        /** @var Config $config */
        $config = $this->container['config'];

        $particles = $this->container['particles']->all();

        $list = [];
        foreach ($particles as $name => $particle) {
            $type = isset($particle['type']) ?
$particle['type'] : 'particle';
            $particleName = isset($particle['name']) ?
$particle['name'] : $name;
            $particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;

            if (!$onlyEnabled ||
$config->get("particles.{$name}.enabled", true)) {
                $list[$type][$name] = ['name' =>
$particleName, 'icon' => $particleIcon];
            }
        }

        return $list;
    }
}
classes/Gantry/Admin/Controller/Html/Configurations/Page.php000064400000026514151166614510020121
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html\Configurations;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\Config;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Atoms;
use Gantry\Framework\Services\ConfigServiceProvider;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Page extends HtmlController
{
    protected $httpVerbs = [
        'GET'    => [
            '/' => 'index'
        ],
        'POST'   => [
            '/'                 => 'save',
            '/*'                => 'save',
            '/*/**'             => 'formfield',
            '/atoms'            => 'undefined',
            '/atoms/*'          => 'atom',
            '/atoms/*/validate' => 'atomValidate'
        ],
        'PUT'    => [
            '/' => 'save'
        ],
        'PATCH'  => [
            '/' => 'save'
        ],
        'DELETE' => [
            '/' => 'forbidden'
        ]
    ];

    public function index()
    {
        $outline = $this->params['outline'];

        if ($outline == 'default') {
            $this->params['overrideable'] = false;
            $data = $this->container['config'];
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] = $defaults =
$this->container['defaults'];
            $data = ConfigServiceProvider::load($this->container,
$outline, false, false);
        }

        $deprecated = $this->getDeprecatedAtoms();
        if ($deprecated) {
            $data->set('page.head.atoms', $deprecated);
        }

        if (isset($defaults)) {
            $currentAtoms = $data->get('page.head.atoms');
            if (!$currentAtoms) {
                // Make atoms to appear to be inherited in they are loaded
from defaults.
                $defaultAtoms = (array)
$defaults->get('page.head.atoms');
                $atoms = (new
Atoms($defaultAtoms))->inheritAll('default')->toArray();
                $defaults->set('page.head.atoms', $atoms);
            }
        }

        $this->params += [
            'data' => $data,
            'page' =>
$this->container['page']->group(),
            'route'  => "configurations.{$outline}",
            'page_id' => $outline,
            'atoms' => $this->getAtoms(),
            'atoms_deprecated' => $deprecated
        ];

        return
$this->render('@gantry-admin/pages/configurations/page/page.html.twig',
$this->params);
    }

    public function save($id = null)
    {
        $data = $id ? [$id => $this->request->post->getArray()]
: $this->request->post->getArray('page');

        foreach ($data as $name => $values) {
            $this->saveItem($name, $values);
        }

        // Fire save event.
        $event             = new Event;
        $event->gantry     = $this->container;
        $event->theme      = $this->container['theme'];
        $event->controller = $this;
        $event->data       = $data;
        $this->container->fireEvent('admin.page.save',
$event);

        return $id ? $this->display($id) : $this->index();
    }

    public function formfield($id)
    {
        $path = func_get_args();

        $end = end($path);
        if ($end === '') {
            array_pop($path);
        }
        if (end($path) == 'validate') {
            return call_user_func_array([$this, 'validate'],
$path);
        }

        // Load blueprints.
        $blueprints =
$this->container['page']->getBlueprintForm($id);

        list($fields, $path, $value) =
$blueprints->resolve(array_slice($path, 1), '/');

        if (!$fields) {
            throw new \RuntimeException('Page Not Found', 404);
        }

        $data =
$this->request->post->getJsonArray('data');

        $offset = "page.{$id}." . implode('.', $path);
        if ($value !== null) {
            $parent = $fields;
            $fields = ['fields' =>
$fields['fields']];
            $offset .= '.' . $value;
            $data = $data ?:
$this->container['config']->get($offset);
            $data = ['data' => $data];
            $scope = 'data.';
        } else {
            $data = $data ?:
$this->container['config']->get($offset);
            $scope = 'data';
        }

        $fields['is_current'] = true;

        array_pop($path);

        $outline = $this->params['outline'];
        $configuration = "configurations/{$outline}";
        $this->params = [
                'configuration' => $configuration,
                'blueprints' => $fields,
                'data' => $data,
                'prefix' => '',
                'scope' => $scope,
                'parent' => $path
                    ? "$configuration/settings/particles/{$id}/"
. implode('/', $path)
                    : "$configuration/settings/particles/{$id}",
                'route' =>
"configurations.{$outline}.{$offset}",
            ] + $this->params;

        if (isset($parent['key'])) {
            $this->params['key'] = $parent['key'];
        }
        if (isset($parent['value'])) {
            $this->params['title'] =
$parent['value'];
        }

        return
$this->render('@gantry-admin/pages/configurations/settings/field.html.twig',
$this->params);
    }

    public function validate($particle)
    {
        $path = implode('.', array_slice(func_get_args(), 1,
-1));

        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        // Load particle blueprints.
        $validator =
$this->container['particles']->get($particle);

        // Create configuration from the defaults.
        $data = new Config(
            [],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->join($path,
$this->request->post->getArray('data'));

        // TODO: validate

        return new JsonResponse(['data' =>
$data->get($path)]);
    }

    public function atom($name)
    {
        $outline = $this->params['outline'];
        $atoms = Atoms::instance($outline);

        $data = $this->request->post['data'];
        if ($data) {
            $data = json_decode($data, true);
        } else {
            $data = $this->request->post->getArray();
        }

        // Create atom and get its blueprint.
        $item = $atoms->createAtom($name, $data);
        $blueprint = $item->blueprint();

        // Load inheritance blueprint.
        $inheritance = $atoms->getInheritanceBlueprint($name,
$item->id);
        $inheritable = $inheritance &&
$inheritance->get('form/fields/outline/filter', []);

        $this->params += [
            'inherit'       =>
!empty($inherit['outline']) ? $inherit['outline'] :
null,
            'inheritance'   => $inheritance,
            'inheritable'   => $inheritable,
            'item'          => $item,
            'data'          => ['particles' =>
[$name => $item->attributes]],
            'blueprints'    => $blueprint,
            'parent'        => 'settings',
            'prefix'        => "particles.{$name}.",
            'route'         =>
"configurations.default.settings",
            'action'        =>
"configurations/{$outline}/page/atoms/{$name}/validate",
            'skip'          => ['enabled']
        ];

        return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/atom.html.twig',
$this->params)]);
    }

    /**
     * Validate data for the atom.
     *
     * @param string $name
     * @return JsonResponse
     */
    public function atomValidate($name)
    {
        // Load particle blueprints and default settings.
        $validator = new BlueprintSchema;
        $validator->embed('options',
$this->container['particles']->get($name));

        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Create configuration from the defaults.
        $data = new Config([],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->set('id',
$this->request->post['id']);
        $data->set('type', $name);
        $data->set('title',
$this->request->post['title'] ?:
$blueprints->get('name'));
        $data->set('attributes',
$this->request->post->getArray("particles.{$name}"));
        $data->def('attributes.enabled', 1);

        $block =
$this->request->post->getArray('block');
        foreach ($block as $key => $param) {
            if ($param === '') {
                unset($block[$key]);
            }
        }

        $inherit =
$this->request->post->getArray('inherit');
        $clone = !empty($inherit['mode']) &&
$inherit['mode'] === 'clone';
        $inherit['include'] =
!empty($inherit['include']) ? explode(',',
$inherit['include']) : [];
        if (!$clone && !empty($inherit['outline'])
&& count($inherit['include'])) {
            unset($inherit['mode']);
            $data->join('inherit', $inherit);
        }

        // TODO: validate

        // Fill parameters to be passed to the template file.
        $this->params['item'] = (object) $data->toArray();

        return new JsonResponse(['item' =>
$data->toArray()]);
    }

    protected function saveItem($id, $data)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        // Save layout into custom directory for the current theme.
        $outline = $this->params['outline'];

        // Move atoms out of layout.
        if ($id === 'head') {
            $layout = Layout::instance($outline);
            if (is_array($layout->atoms())) {
                $layout->save(false);
            }
            if (isset($data['atoms'])) {
                $atoms = new Atoms($data['atoms']);
                $data['atoms'] =
$atoms->update()->toArray();
            }
        }

        $save_dir      =
$locator->findResource("gantry-config://{$outline}/page",
true, true);
        $filename      = "{$save_dir}/{$id}.yaml";

        $file = YamlFile::instance($filename);
        if (!is_array($data)) {
            if ($file->exists()) {
                $file->delete();
            }
        } else {
            $blueprints =
$this->container['page']->getBlueprintForm($id);
            $config     = new Config($data, function () use ($blueprints) {
return $blueprints; });

            $file->save($config->toArray());
        }
        $file->free();
    }

    protected function getDeprecatedAtoms()
    {
        $id     = $this->params['outline'];
        $layout = Layout::instance($id);

        return $layout->atoms();
    }

    protected function getAtoms($onlyEnabled = false)
    {
        $config = $this->container['config'];

        $atoms = $this->container['particles']->all();

        $list = [];
        foreach ($atoms as $name => $atom) {
            $type     = isset($atom['type']) ?
$atom['type'] : 'atom';
            $atomName = isset($atom['name']) ?
$atom['name'] : $name;

            if (!$onlyEnabled ||
$config->get("particles.{$name}.enabled", true)) {
                $list[$type][$name] = $atomName;
            }
        }

        return $list['atom'];
    }
}
classes/Gantry/Admin/Controller/Html/Configurations/Settings.php000064400000021031151166614510021032
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html\Configurations;

use Gantry\Admin\Particles;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\Config;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Services\ConfigServiceProvider;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Settings extends HtmlController
{
    protected $httpVerbs = [
        'GET' => [
            '/'                 => 'index',
            '/particles'        => 'undefined',
            '/particles/*'      => 'display',
            '/particles/*/**'   => 'formfield',
        ],
        'POST' => [
            '/'                 => 'save',
            '/particles'        => 'forbidden',
            '/particles/*'      => 'save',
            '/particles/*/**'   => 'formfield'
        ],
        'PUT' => [
            '/'            => 'save',
            '/particles'   => 'forbidden',
            '/particles/*' => 'save'
        ],
        'PATCH' => [
            '/'            => 'save',
            '/particles'   => 'forbidden',
            '/particles/*' => 'save'
        ],
        'DELETE' => [
            '/'            => 'forbidden',
            '/particles'   => 'forbidden',
            '/particles/*' => 'reset'
        ]
    ];

    public function index()
    {
        $outline = $this->params['outline'];

        if ($outline === 'default') {
            $this->params['overrideable'] = false;
            $data = $this->container['config'];
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] =
$this->container['defaults'];
            $data = ConfigServiceProvider::load($this->container,
$outline, false, false);
        }

        /** @var Particles $particles */
        $particles = $this->container['particles'];
        $this->params += [
            'data' => $data,
            'particles' =>
$particles->group(['atom']),
            'route'  =>
"configurations.{$outline}.settings",
            'page_id' => $outline
        ];

        return
$this->render('@gantry-admin/pages/configurations/settings/settings.html.twig',
$this->params);
    }

    public function display($id)
    {
        $outline = $this->params['outline'];

        /** @var Particles $particles */
        $particles = $this->container['particles'];

        $blueprints = $particles->getBlueprintForm($id);
        $prefix = 'particles.' . $id;

        if($outline === 'default') {
            $this->params['overrideable'] = false;
            $data = $this->container['config'];
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] =
$this->container['defaults']->get($prefix);
            $data = ConfigServiceProvider::load($this->container,
$outline, false, false);
        }

        $this->params += [
            'scope' => 'particle.',
            'particle' => $blueprints,
            'data' =>  ['particle' =>
$data->get($prefix)],
            'id' => $id,
            'parent' =>
"configurations/{$outline}/settings",
            'route'  =>
"configurations.{$outline}.settings.{$prefix}",
            'skip' => ['enabled']
            ];

        return
$this->render('@gantry-admin/pages/configurations/settings/item.html.twig',
$this->params);
    }

    public function formfield($id)
    {
        $path = func_get_args();

        $end = end($path);
        if ($end === '') {
            array_pop($path);
        }
        if (end($path) === 'validate') {
            return call_user_func_array([$this, 'validate'],
$path);
        }

        /** @var Particles $particles */
        $particles = $this->container['particles'];

        // Load blueprints.
        $blueprints = $particles->getBlueprintForm($id);

        list($fields, $path, $value) =
$blueprints->resolve(array_slice($path, 1), '/');
        if (!$fields) {
            throw new \RuntimeException('Page Not Found', 404);
        }

        $data =
$this->request->post->getJsonArray('data');

        /** @var Config $config */
        $config = $this->container['config'];

        $offset = "particles.{$id}." . implode('.',
$path);
        if ($value !== null) {
            $parent = $fields;
            $fields = ['fields' =>
$fields['fields']];
            $offset .= '.' . $value;
            $data = $data ?: $config->get($offset);
            $data = ['data' => $data];
            $scope = 'data.';
        } else {
            $data = $data ?: $config->get($offset);
            $scope = 'data';
        }

        $fields['is_current'] = true;

        array_pop($path);

        $outline = $this->params['outline'];
        $configuration = "configurations/{$outline}";
        $this->params = [
                'configuration' => $configuration,
                'blueprints' => $fields,
                'data' => $data,
                'scope' => $scope,
                'parent' => $path
                    ?
"{$configuration}/settings/particles/{$id}/" .
implode('/', $path)
                    :
"{$configuration}/settings/particles/{$id}",
                'route' =>
"configurations.{$outline}.settings.{$offset}",
            ] + $this->params;

        if (isset($parent['key'])) {
            $this->params['key'] = $parent['key'];
        }
        if (isset($parent['value'])) {
            $this->params['title'] =
$parent['value'];
        }

        return
$this->render('@gantry-admin/pages/configurations/settings/field.html.twig',
$this->params);
    }

    public function validate($particle)
    {
        $path = implode('.', array_slice(func_get_args(), 1,
-1));

        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        /** @var Particles $particles */
        $particles = $this->container['particles'];

        // Load particle blueprints.
        $validator = $particles->get($particle);

        // Create configuration from the defaults.
        $data = new Config(
            [],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->join($path,
$this->request->post->getArray('data'));

        // TODO: validate

        return new JsonResponse(['data' =>
$data->get($path)]);
    }

    public function save($id = null)
    {
        if (!$this->request->post->get('_end')) {
            throw new \OverflowException("Incomplete data received.
Please increase the value of 'max_input_vars' variable (in
php.ini or .htaccess)", 400);
        }

        $data = $id ? [$id =>
$this->request->post->getArray('particle')] :
$this->request->post->getArray('particles');

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        // Save layout into custom directory for the current theme.
        $outline = $this->params['outline'];
        $save_dir =
$locator->findResource("gantry-config://{$outline}/particles",
true, true);

        foreach ($data as $name => $values) {
            $this->saveItem($name, $values, $save_dir);
        }
        @rmdir($save_dir);

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->theme = $this->container['theme'];
        $event->controller = $this;
        $event->data = $data;
        $this->container->fireEvent('admin.settings.save',
$event);

        return $id ? $this->display($id) : $this->index();
    }

    protected function saveItem($id, $data, $save_dir)
    {
        $filename = "{$save_dir}/{$id}.yaml";

        $file = YamlFile::instance($filename);
        if (!is_array($data)) {
            if ($file->exists()) {
                $file->delete();
            }
        } else {
            /** @var Particles $particles */
            $particles = $this->container['particles'];

            $blueprints = $particles->getBlueprintForm($id);
            $config = new Config($data, function() use ($blueprints) {
return $blueprints; });

            $file->save($config->toArray());
        }
        $file->free();
    }

    public function reset($id)
    {
        $this->params += [
            'data' => [],
        ];

        return $this->display($id);
    }
}
classes/Gantry/Admin/Controller/Html/Configurations/Styles.php000064400000020017151166614510020520
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html\Configurations;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\Config;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Services\ConfigServiceProvider;
use Gantry\Framework\Theme;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Styles extends HtmlController
{

    protected $httpVerbs = [
        'GET' => [
            '/'              => 'index',
            '/blocks'        => 'undefined',
            '/blocks/*'      => 'display',
            '/blocks/*/**'   => 'formfield'
        ],
        'POST' => [
            '/'          => 'save',
            '/blocks'    => 'forbidden',
            '/blocks/*'  => 'save',
            '/compile'   => 'compile'
        ],
        'PUT' => [
            '/'         => 'save',
            '/blocks'   => 'forbidden',
            '/blocks/*' => 'save'
        ],
        'PATCH' => [
            '/'         => 'save',
            '/blocks'   => 'forbidden',
            '/blocks/*' => 'save'
        ],
        'DELETE' => [
            '/'         => 'forbidden',
            '/blocks'   => 'forbidden',
            '/blocks/*' => 'reset'
        ]
    ];

    public function index()
    {
        $outline = $this->params['outline'];

        if($outline == 'default') {
            $this->params['overrideable'] = false;
            $this->params['data'] =
$this->container['config'];
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] =
$this->container['defaults'];
            $this->params['data'] =
ConfigServiceProvider::load($this->container, $outline, false, false);
        }

        $this->params['blocks'] =
$this->container['styles']->group();
        $this->params['route']  =
"configurations.{$outline}.styles";

        return
$this->render('@gantry-admin/pages/configurations/styles/styles.html.twig',
$this->params);
    }

    public function display($id)
    {
        $outline = $this->params['outline'];
        $blueprints =
$this->container['styles']->getBlueprintForm($id);
        $prefix = 'styles.' . $id;

        if($outline == 'default') {
            $this->params['overrideable'] = false;
            $this->params['data'] =
$this->container['config']->get($prefix);
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] =
$this->container['defaults']->get($prefix);
            $this->params['data'] =
ConfigServiceProvider::load($this->container, $outline, false,
false)->get($prefix);
        }

        $this->params += [
            'block' => $blueprints,
            'id' => $id,
            'parent' =>
"configurations/{$outline}/styles",
            'route'  =>
"configurations.{$outline}.styles.{$prefix}",
            'skip' => ['enabled']
        ];

        return
$this->render('@gantry-admin/pages/configurations/styles/item.html.twig',
$this->params);
    }

    public function formfield($id)
    {
        $path = func_get_args();

        $outline = $this->params['outline'];

        // Load blueprints.
        $blueprints =
$this->container['styles']->getBlueprintForm($id);

        list($fields, $path, $value) =
$blueprints->resolve(array_slice($path, 1), '/');

        if (!$fields) {
            throw new \RuntimeException('Page Not Found', 404);
        }

        $fields['is_current'] = true;

        // Get the prefix.
        $prefix = "styles.{$id}." . implode('.',
$path);
        if ($value !== null) {
            $parent = $fields;
            $fields = ['fields' =>
$fields['fields']];
            $prefix .= '.' . $value;
        }
        array_pop($path);

        if($outline == 'default') {
            $this->params['overrideable'] = false;
            $this->params['data'] =
$this->container['config']->get($prefix);
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] =
$this->container['defaults']->get($prefix);
            $this->params['data'] =
ConfigServiceProvider::load($this->container, $outline, false,
false)->get($prefix);
        }

        $this->params = [
                'blueprints' => $fields,
                'parent' => $path
                    ?
"configurations/{$outline}/styles/blocks/{$id}/" .
implode('/', $path)
                    :
"configurations/{$outline}/styles/blocks/{$id}",
                'route' => 'styles.' . $prefix
            ] + $this->params;

        if (isset($parent['key'])) {
            $this->params['key'] = $parent['key'];
        }

        return
$this->render('@gantry-admin/pages/configurations/styles/field.html.twig',
$this->params);
    }

    public function reset($id)
    {
        $this->params += [
            'data' => [],
        ];

        return $this->display($id);
    }


    public function compile()
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        $warnings = $this->compileSettings();

        if ($warnings) {
            $this->params += ['warnings' => $warnings];
            return new JsonResponse(
                [
                    'html'    =>
$this->render('@gantry-admin/layouts/css-warnings.html.twig',
$this->params),
                    'warning' => true,
                    'title'   => 'CSS Compiled With
Warnings',
                ]
            );
        } else {
            return new JsonResponse(['html' => 'The CSS
was successfully compiled', 'title' => 'CSS
Compiled']);
        }
    }

    public function save($id = null)
    {
        /** @var Config $config */
        $config = $this->container['config'];

        if ($id) {
            $data = (array) $config->get('styles');
            $data[$id] = $this->request->post->getArray();
        } else {
            $data =
$this->request->post->getArray('styles');
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        // Save layout into custom directory for the current theme.
        $outline = $this->params['outline'];
        $save_dir =
$locator->findResource("gantry-config://{$outline}", true,
true);
        $filename = "{$save_dir}/styles.yaml";

        $file = YamlFile::instance($filename);
        $file->save($data);
        $file->free();

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->theme = $this->container['theme'];
        $event->controller = $this;
        $event->data = $data;
        $this->container->fireEvent('admin.styles.save',
$event);

        // Compile CSS.
        $warnings = $this->compileSettings();

        if (empty($this->params['ajax'])) {
            // FIXME: HTML request: Output compiler warnings!!
            return $id ? $this->display($id) : $this->index();
        }

        if ($warnings) {
            $this->params += ['warnings' => $warnings];
            return new JsonResponse(
                [
                    'html'    =>
$this->render('@gantry-admin/layouts/css-warnings.html.twig',
$this->params),
                    'warning' => true,
                    'title'   => 'CSS Compiled With
Warnings',
                ]
            );
        } else {
            return new JsonResponse(['html' => 'The CSS
was successfully compiled', 'title' => 'CSS
Compiled']);
        }
    }

    /**
     * @returns array
     */
    protected function compileSettings()
    {
        /** @var Theme $theme */
        $theme = $this->container['theme'];
        $outline = $this->params['outline'];

        return $theme->updateCss($outline !== 'default' ?
[$outline => ucfirst($outline)] : null);
    }
}
classes/Gantry/Admin/Controller/Html/Configurations.php000064400000017126151166614510017244
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Response\RedirectResponse;
use Gantry\Component\Response\Response;
use Gantry\Component\Layout\Layout as LayoutObject;
use Gantry\Framework\Outlines as OutlinesObject;

class Configurations extends HtmlController
{
    protected $httpVerbs = [
        'GET' => [
            '/'                 => 'index',
            '/*'                => 'forward',
            '/*/delete'         =>
'confirmDeletion',
            '/*/**'             => 'forward',
        ],
        'POST' => [
            '/'                 => 'undefined',
            '/*'                => 'undefined',
            '/create'           => 'createForm',
            '/create/new'       => 'create',
            '/*/rename'         => 'rename',
            '/*/duplicate'      => 'duplicateForm',
            '/*/duplicate/new'  => 'duplicate',
            '/*/delete'         => 'undefined',
            '/*/delete/confirm' => 'delete',
            '/*/**'             => 'forward',
        ],
        'PUT'    => [
            '/'   => 'undefined',
            '/**' => 'forward'
        ],
        'PATCH'  => [
            '/'   => 'undefined',
            '/**' => 'forward'
        ]
    ];

    public function index()
    {
        return
$this->render('@gantry-admin/pages/configurations/configurations.html.twig',
$this->params);
    }

    public function createForm()
    {
        if (!$this->authorize('outline.create')) {
            $this->forbidden();
        }

        $params = [
            'presets' => LayoutObject::presets(),
            'outlines' =>
$this->container['outlines']
        ];

        $response = ['html' =>
$this->render('@gantry-admin/ajax/outline-new.html.twig',
$params)];

        return new JsonResponse($response);
    }

    public function create()
    {
        // Check if we want to duplicate outline instead.
        if ($this->request->post['from'] ===
'outline') {
            return
$this->duplicate($this->request->post['outline']);
        }

        if (!$this->authorize('outline.create')) {
            $this->forbidden();
        }

        /** @var OutlinesObject $outlines */
        $outlines = $this->container['outlines'];
        $title = $this->request->post['title'];
        $preset = $this->request->post['preset'];

        $id = $outlines->create(null, $title, $preset);

        $html =
$this->render('@gantry-admin/layouts/outline.html.twig',
['name' => $id, 'title' => $outlines[$id]]);

        return new JsonResponse(['html' => 'Outline
created.', 'id' => "outline-{$id}",
'outline' => $html]);
    }

    public function rename($outline)
    {
        if (!$this->authorize('outline.rename')) {
            $this->forbidden();
        }

        /** @var OutlinesObject $outlines */
        $outlines = $this->container['outlines'];
        $title = $this->request->post['title'];

        $id = $outlines->rename($outline, $title);

        $html =
$this->render('@gantry-admin/layouts/outline.html.twig',
['name' => $id, 'title' => $outlines[$id]]);

        return new JsonResponse(['html' => 'Outline
renamed.', 'id' => "outline-{$outline}",
'outline' => $html]);
    }

    public function duplicateForm($outline)
    {
        if (!$this->authorize('outline.create')) {
            $this->forbidden();
        }

        $params = [
            'outlines' =>
$this->container['outlines'],
            'outline' => $outline,
            'duplicate' => true
        ];

        $response = ['html' =>
$this->render('@gantry-admin/ajax/outline-new.html.twig',
$params)];

        return new JsonResponse($response);
    }

    public function duplicate($outline)
    {
        if (!$this->authorize('outline.create')) {
            $this->forbidden();
        }

        /** @var OutlinesObject $outlines */
        $outlines = $this->container['outlines'];
        $title = $this->request->post['title'];
        $inherit =
in_array($this->request->post['inherit'], ['1',
'true']);

        $id = $outlines->duplicate($outline, $title, $inherit);

        $html =
$this->render('@gantry-admin/layouts/outline.html.twig',
['name' => $id, 'title' => $outlines[$id]]);

        return new JsonResponse(['html' => 'Outline
duplicated.', 'id' => $id, 'outline' =>
$html]);
    }

    public function delete($outline)
    {
        if (!$this->authorize('outline.delete')) {
            $this->forbidden();
        }

        /** @var OutlinesObject $outlines */
        $outlines = $this->container['outlines'];
        $list = $outlines->user();

        if (!isset($list[$outline])) {
            $this->forbidden();
        }

        $outlines->delete($outline);

        return new JsonResponse(['html' => 'Outline
deleted.', 'outline' => $outline]);
    }

    /**
     * @return JsonResponse
     */
    public function confirmDeletion($id)
    {
        /** @var OutlinesObject $outlines */
        $outlines = $this->container['outlines'];

        $params = [
            'id' => $id,
            'page_type' => 'OUTLINE',
            'outline' => $outlines->title($id),
            'inherited' =>
$outlines->getInheritingOutlines($id)
        ];

        $html =
$this->render('@gantry-admin/pages/configurations/confirm-deletion.html.twig',
$params);

        return new JsonResponse(['html' => $html]);
    }

    /**
     * @return HtmlResponse|RedirectResponse|JsonResponse
     */
    public function forward()
    {
        $path = func_get_args();

        $outline = array_shift($path);
        $page = array_shift($path);

        $method = $this->params['method'];

        if ((!isset($outline) || !isset($page)) &&
$this->params['format'] !== 'json') {
            // Redirect path to the styles page of the selected outline.
            return new
RedirectResponse($this->container->route('configurations',
is_string($outline) ? $outline : 'default', 'styles'));
        }

        $outlines = $this->container['outlines'];

        if (!isset($outlines[$outline])) {
            throw new \RuntimeException('Outline not found.',
404);
        }

        $this->container['outline'] = $outline;
        $this->container['configuration'] = $outline;

        $resource = $this->params['location'] . '/'.
$page;

        $this->params['outline'] = $outline;
        $this->params['configuration'] = $outline;
        $this->params['location'] = $resource;
        $this->params['configuration_page'] = $page;
        $this->params['navbar'] =
!empty($this->request->get['navbar']);

        return $this->executeForward($resource, $method, $path,
$this->params);
    }

    protected function executeForward($resource, $method = 'GET',
$path, $params = [])
    {
        $class = '\\Gantry\\Admin\\Controller\\Html\\' .
strtr(ucwords(strtr($resource, '/', ' ')), '
', '\\');
        if (!class_exists($class)) {
            throw new \RuntimeException('Page not found', 404);
        }

        /** @var HtmlController $controller */
        $controller = new $class($this->container);

        // Execute action.
        $response = $controller->execute($method, $path, $params);

        if (!$response instanceof Response) {
            $response = new HtmlResponse($response);
        }

        return $response;
    }
}
classes/Gantry/Admin/Controller/Html/Export.php000064400000007505151166614510015533
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Framework\Exporter;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;

class Export extends HtmlController
{
    public function index()
    {
        if (!class_exists('Gantry\Framework\Exporter')) {
            $this->forbidden();
        }

        if (!class_exists('ZipArchive')) {
            throw new \RuntimeException('Please enable PHP ZIP
extension to use this feature.');
        }

        $exporter = new Exporter;
        $exported = $exporter->all();

        $zipname =
$exported['export']['theme']['name'] .
'-export.zip';
        $tmpname = tempnam(sys_get_temp_dir(), 'zip');

        $zip = new \ZipArchive();
        $zip->open($tmpname, \ZipArchive::CREATE);

        $zip->addFromString("export.yaml",
Yaml::dump($exported['export'], 10, 2));
        unset($exported['export']);

        foreach ($exported['positions'] as $key => $position)
{
            foreach ($position['items'] as $module => $data) {
               
$zip->addFromString("positions/{$key}/{$module}.yaml",
Yaml::dump($data, 10, 2));
            }

            $position['ordering'] =
array_keys($position['items']);
            unset($position['items']);

            $zip->addFromString("positions/{$key}.yaml",
Yaml::dump($position, 10, 2));
        }

        foreach ($exported['outlines'] as $outline =>
&$data) {
            if (!empty($data['config'])) {
                foreach ($data['config'] as $name => $config)
{
                    if (in_array($name, ['particles',
'page'])) {
                        foreach ($config as $sub => $subconfig) {
                           
$zip->addFromString("outlines/{$outline}/{$name}/{$sub}.yaml",
Yaml::dump($subconfig, 10, 2));
                        }
                    } else {
                       
$zip->addFromString("outlines/{$outline}/{$name}.yaml",
Yaml::dump($config, 10, 2));
                    }
                }
            }
            unset($data['config']);
        }
        $zip->addFromString("outlines/outlines.yaml",
Yaml::dump($exported['outlines'], 10, 2));

        foreach ($exported['menus'] as $menu => $data) {
            $zip->addFromString("menus/{$menu}.yaml",
Yaml::dump($data, 10, 2));
        }

        foreach ($exported['content'] as $id => $data) {
            $zip->addFromString("content/{$id}.yaml",
Yaml::dump($data, 10, 2));
        }

        if (!empty($exported['categories'])) {
            $zip->addFromString("content/categories.yaml",
Yaml::dump($exported['categories'], 10, 2));
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        foreach ($exported['files'] as $stream => $files) {
            foreach ($files as $path => $uri) {
                $filename = $locator->findResource($uri);
                
                if (file_exists($filename)) {
                    $zip->addFile($filename,
"files/{$stream}/{$path}");
                }
            }
            
        }

        $zip->close();

        header('Content-Type: application/zip');
        header('Content-Disposition: attachment; filename=' .
$zipname);
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($tmpname));

        @ob_end_clean();
        flush();

        readfile($tmpname);
        unlink($tmpname);

        exit;
    }
}
classes/Gantry/Admin/Controller/Html/Import.php000064400000005175151166614510015525
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Importer;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;

class Import extends HtmlController
{
    protected $httpVerbs = [
        'GET' => [
            '/'                 => 'index',
        ],
        'POST' => [
            '/'                 => 'import',
        ]
    ];

    public function index()
    {
        return
$this->render('@gantry-admin/pages/import/import.html.twig',
$this->params);
    }

    public function import()
    {
        if (!isset($_FILES['file']['error']) ||
is_array($_FILES['file']['error'])) {
            throw new \RuntimeException('No file sent', 400);
        }

        // Check $_FILES['file']['error'] value.
        switch ($_FILES['file']['error']) {
            case UPLOAD_ERR_OK:
                break;
            case UPLOAD_ERR_NO_FILE:
                throw new \RuntimeException('No file sent', 400);
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                throw new \RuntimeException('Exceeded filesize
limit.', 400);
            default:
                throw new \RuntimeException('Unkown errors',
400);
        }

        $filename = $_FILES['file']['tmp_name'];
        
        if (!is_uploaded_file($filename)) {
            throw new \RuntimeException('No file sent', 400);
        }

        $zip = new \ZipArchive;
        if ($zip->open($filename) !== true || !($export =
Yaml::parse($zip->getFromName('export.yaml'))) ||
!isset($export['gantry'])) {
            throw new \RuntimeException('Uploaded file is not Gantry 5
export file', 400);
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        $folder =
$locator->findResource('gantry-cache://import', true, true);
        if (is_dir($folder)) Folder::delete($folder);
        $zip->extractTo($folder);
        $zip->close();

        $importer = new Importer($folder);
        $importer->all();

        if (is_dir($folder)) Folder::delete($folder);

        $this->params['success'] = true;

        return
$this->render('@gantry-admin/pages/import/import.html.twig',
$this->params);
    }
}
classes/Gantry/Admin/Controller/Html/Install.php000064400000002167151166614510015657
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Framework\ThemeInstaller;

class Install extends HtmlController
{
    public function index()
    {
        if (!$this->authorize('updates.manage') ||
!class_exists('\Gantry\Framework\ThemeInstaller')) {
            $this->forbidden();
        }

        $installer = new ThemeInstaller();
        $installer->initialized = true;
       
$installer->loadExtension($this->container['theme.name']);
        $installer->installDefaults();
        $installer->installSampleData();
        $installer->finalize();

        $this->params['actions'] = $installer->actions;
        
        return
$this->render('@gantry-admin/pages/install/install.html.twig',
$this->params);
    }
}
classes/Gantry/Admin/Controller/Html/Menu.php000064400000043333151166614510015155
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\Menu\Item;
use Gantry\Component\Request\Input;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Response\Response;
use Gantry\Framework\Menu as MenuObject;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Menu extends HtmlController
{
    protected $httpVerbs = [
        'GET'    => [
            '/'                  => 'item',
            '/*'                 => 'item',
            '/*/**'              => 'item',
            '/particle'          => 'particle',
            '/particle/*'        =>
'validateParticle',
            '/select'            => 'undefined',
            '/select/particle'   =>
'selectParticle',
            '/select/module'     => 'selectModule',
            '/select/widget'     => 'selectWidget',
            '/edit'              => 'undefined',
            '/edit/*'            => 'edit',
            '/edit/*/**'         => 'editItem',
        ],
        'POST'   => [
            '/'                  => 'save',
            '/*'                 => 'save',
            '/*/**'              => 'item',
            '/particle'          => 'particle',
            '/particle/*'        =>
'validateParticle',
            '/select'            => 'undefined',
            '/select/particle'   =>
'selectParticle',
            '/select/module'     => 'selectModule',
            '/select/widget'     => 'selectWidget',
            '/widget'            => 'widget',
            '/edit'              => 'undefined',
            '/edit/*'            => 'edit',
            '/edit/*/**'         => 'editItem',
            '/edit/*/validate'   => 'validate',
        ],
        'PUT'    => [
            '/*' => 'replace'
        ],
        'PATCH'  => [
            '/*' => 'update'
        ],
        'DELETE' => [
            '/*' => 'destroy'
        ]
    ];

    public function execute($method, array $path, array $params)
    {
        if (!$this->authorize('menu.manage')) {
            $this->forbidden();
        }

        return parent::execute($method, $path, $params);
    }

    public function item($id = null)
    {
        // Load the menu.
        try {
            $resource = $this->loadResource($id,
$this->build($this->request->post));
        } catch (\Exception $e) {
            return
$this->render('@gantry-admin/pages/menu/menu.html.twig',
$this->params);
        }

        // All extra arguments become the path.
        $path = array_slice(func_get_args(), 1);

        // Get menu item and make sure it exists.
        $item = $resource[implode('/', $path)];
        if (!$item) {
            throw new \RuntimeException('Menu item not found',
404);
        }

        // Fill parameters to be passed to the template file.
        $this->params['id'] = $resource->name();
        $this->params['menus'] = $resource->getMenus();
        $this->params['default_menu'] =
$resource->hasDefaultMenu() ? $resource->getDefaultMenuName() :
false;
        $this->params['menu'] = $resource;
        $this->params['path'] = implode('/', $path);

        // Detect special case to fetch only single column group.
        $group = $this->request->get['group'];

        if (empty($this->params['ajax']) ||
empty($this->request->get['inline'])) {
            // Handle special case to fetch only one column group.
            if (count($path) > 0) {
                $this->params['columns'] =
$resource[$path[0]];
            }
            if (count($path) > 1) {
                $this->params['column'] = isset($group) ?
(int) $group : $resource[implode('/', array_slice($path, 0,
2))]->group;
                $this->params['override'] = $item;
            }

            return
$this->render('@gantry-admin/pages/menu/menu.html.twig',
$this->params);

        } else {
            // Get layout name.
            $layout = $this->layoutName(count($path) + (int)
isset($group));

            $this->params['item'] = $item;
            $this->params['group'] = isset($group) ? (int)
$group : $resource[implode('/', array_slice($path, 0,
2))]->group;

            return $this->render('@gantry-admin/menu/' .
$layout . '.html.twig', $this->params) ?:
'&nbsp;';
        }
    }

    public function edit($id)
    {
        $resource = $this->loadResource($id);
        $input = $this->build($this->request->post);
        if ($input) {
            $resource->config()->merge(['settings' =>
$input['settings']]);
        }

        // Fill parameters to be passed to the template file.
        $this->params['id'] = $resource->name();
        $this->params['blueprints'] =
$this->loadBlueprints();
        $this->params['data'] = ['settings' =>
$resource->settings()];

        return
$this->render('@gantry-admin/pages/menu/edit.html.twig',
$this->params);
    }

    public function save($id = null)
    {
        $resource = $this->loadResource($id);

        $data = $this->build($this->request->post);

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];
        $filename =
$locator->findResource("gantry-config://menu/{$resource->name()}.yaml",
true, true);

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->theme = $this->container['theme'];
        $event->controller = $this;
        $event->resource = $id;
        $event->menu = $data;
        $this->container->fireEvent('admin.menus.save',
$event);

        $file = YamlFile::instance($filename);
        $file->settings(['inline' => 99]);
        $file->save($data->toArray());
        $file->free();
    }

    public function editItem($id)
    {
        // All extra arguments become the path.
        $path = array_slice(func_get_args(), 1);
        $keyword = end($path);

        // Special case: validate instead of fetching menu item.
        if ($this->method == 'POST' && $keyword ==
'validate') {
            $params = array_slice(func_get_args(), 0, -1);
            return call_user_func_array([$this, 'validateitem'],
$params);
        }

        $path = html_entity_decode(implode('/', $path),
ENT_COMPAT | ENT_HTML5, 'UTF-8');

        // Load the menu.
        $resource = $this->loadResource($id);

        // Get menu item and make sure it exists.
        /** @var Item $item */
        $item = $resource[$path];
        if (!$item) {
            throw new \RuntimeException('Menu item not found',
404);
        }
        $data =
$this->request->post->getJsonArray('item');
        if ($data) {
            $item->update($data);
        }

        // Load blueprints for the menu item.
        $blueprints = $this->loadBlueprints('menuitem');

        $this->params = [
                'id'         => $resource->name(),
                'path'       => $path,
                'blueprints' => ['fields' =>
$blueprints['form/fields/items/fields']],
                'data'       => $item->toArray() +
['path' => $path],
            ] + $this->params;

        return
$this->render('@gantry-admin/pages/menu/menuitem.html.twig',
$this->params);
    }

    public function particle()
    {
        $data = $this->request->post['item'];
        if ($data) {
            $data = json_decode($data, true);
        } else {
            $data = $this->request->post->getArray();
        }

        $name = isset($data['particle']) ?
$data['particle'] : null;

        $block = BlueprintForm::instance('menu/block.yaml',
'gantry-admin://blueprints');
        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Load particle blueprints and default settings.
        $validator = $this->loadBlueprints('menu');
        $callable = function () use ($validator) {
            return $validator;
        };

        // Create configuration from the defaults.
        $item = new Config($data, $callable);
        $item->def('type', 'particle');
        $item->def('title',
$blueprints->get('name'));
        $item->def('options.type',
$blueprints->get('type', 'particle'));
        $item->def('options.particle', []);
        $item->def('options.block', []);

        $this->params += [
            'item'          => $item,
            'block'         => $block,
            'data'          => ['particles' =>
[$name => $item->options['particle']]],
            'particle'      => $blueprints,
            'parent'        => 'settings',
            'prefix'        => "particles.{$name}.",
            'route'         =>
"configurations.default.settings",
            'action'        =>
"menu/particle/{$name}"
        ];

        return
$this->render('@gantry-admin/pages/menu/particle.html.twig',
$this->params);
    }


    public function validateParticle($name)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        // Load particle blueprints and default settings.
        $validator = new BlueprintSchema;
        $validator->embed('options',
$this->container['particles']->get($name));

        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Create configuration from the defaults.
        $data = new Config([],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->set('type', 'particle');
        $data->set('particle', $name);
        $data->set('title',
$this->request->post['title'] ?:
$blueprints->post['name']);
        $data->set('options.particle',
$this->request->post->getArray("particles.{$name}"));
        $data->def('options.particle.enabled', 1);
        $data->set('enabled',
$data->get('options.particle.enabled'));

        $block =
$this->request->post->getArray('block');
        foreach ($block as $key => $param) {
            if ($param === '') {
                unset($block[$key]);
            }
        }

        $data->join('options.block', $block);

        // TODO: validate

        // Fill parameters to be passed to the template file.
        $this->params['item'] = (object) $data->toArray();

        $html =
$this->render('@gantry-admin/menu/item.html.twig',
$this->params);

        return new JsonResponse(['item' =>
$data->toArray(), 'html' => $html]);
    }

    public function selectModule()
    {
        return
$this->render('@gantry-admin/modals/module-picker.html.twig',
$this->params);
    }

    public function selectWidget()
    {
        $this->params['next'] = 'menu/widget';

        return
$this->render('@gantry-admin/modals/widget-picker.html.twig',
$this->params);
    }

    public function widget()
    {
        $data = $this->request->post->getJson('item');
        $path = [$data->widget];
        $this->params['scope'] = 'menu';

        return $this->executeForward('widget',
'POST', $path, $this->params);
    }

    public function selectParticle()
    {
        $groups = [
            'Particles' => ['particle' => []],
        ];

        $particles = [
            'position'    => [],
            'spacer'      => [],
            'system'      => [],
            'particle'    => [],
        ];

        $particles = array_replace($particles, $this->getParticles());
        unset($particles['atom'],
$particles['position']);

        foreach ($particles as &$group) {
            asort($group);
        }

        foreach ($groups as $section => $children) {
            foreach ($children as $key => $child) {
                $groups[$section][$key] = $particles[$key];
            }
        }

        $this->params += [
            'particles' => $groups,
            'route' => 'menu/particle',
        ];

        return
$this->render('@gantry-admin/modals/particle-picker.html.twig',
$this->params);
    }

    public function validate($id)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        // Load particle blueprints and default settings.
        $validator = $this->loadBlueprints('menu');
        $callable = function () use ($validator) {
            return $validator;
        };

        // Create configuration from the defaults.
        $data = new Config($this->request->post->getArray(),
$callable);

        // TODO: validate

        return new JsonResponse(['settings' => (array)
$data->get('settings')]);
    }

    public function validateitem($id)
    {
        // All extra arguments become the path.
        $path = array_slice(func_get_args(), 1);

        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        // Load the menu.
        $resource = $this->loadResource($id);

        // Load particle blueprints and default settings.
        $validator = $this->loadBlueprints('menuitem');
        $callable = function () use ($validator) {
            return $validator;
        };

        // Create configuration from the defaults.
        $data = new Config($this->request->post->getArray(),
$callable);

        // TODO: validate

        $item = $resource[implode('/', $path)];
        $item->update($data->toArray());

        // Fill parameters to be passed to the template file.
        $this->params['id'] = $resource->name();
        $this->params['item'] = $item;
        $this->params['group'] = isset($group) ? $group :
$resource[implode('/', array_slice($path, 0, 2))]->group;

        if (!$item->title) {
            throw new \RuntimeException('Title from the Menu Item
should not be empty', 400);
        }

        $html =
$this->render('@gantry-admin/menu/item.html.twig',
$this->params);

        return new JsonResponse(['path' =>
implode('/', $path), 'item' => $data->toArray(),
'html' => $html]);
    }

    protected function layoutName($level)
    {
        switch ($level) {
            case 0:
                return 'base';
            case 1:
                return 'columns';
            default:
                return 'list';
        }
    }

    /**
     * Load resource.
     *
     * @param string $id
     * @param Config $config
     *
     * @return \Gantry\Component\Menu\AbstractMenu
     * @throws \RuntimeException
     */
    protected function loadResource($id, Config $config = null)
    {
        /** @var MenuObject $menus */
        $menus = $this->container['menu'];

        return $menus->instance(['menu' => $id,
'admin' => true], $config);
    }

    /**
     * Load blueprints.
     *
     * @param string $name
     *
     * @return BlueprintForm
     */
    protected function loadBlueprints($name = 'menu')
    {
        return BlueprintForm::instance("menu/{$name}.yaml",
'gantry-admin://blueprints');
    }


    public function build(Input $input)
    {
        try {
            $items = $input->get('items');
            if ($items && $items[0] !== '{' &&
$items[0] !== '[') {
                $items = urldecode((string)base64_decode($items));
            }
            $items = json_decode($items, true);

            $settings = $input->getJsonArray('settings');
            $order = $input->getJsonArray('ordering');
        } catch (\Exception $e) {
            throw new \RuntimeException('Invalid menu structure',
400);
        }

        if (!$items && !$settings && !$order) {
            return null;
        }


        krsort($order);
        $ordering = ['' => []];
        foreach ($order as $path => $columns) {
            foreach ($columns as $column => $colitems) {
                $list = [];
                foreach ($colitems as $item) {
                    $name = trim(substr($item, strlen($path)),
'/');
                    if (isset($ordering[$item])) {
                        $list[$name] = $ordering[$item];
                        unset($ordering[$item]);
                    } else {
                        $list[$name] = '';
                    }
                }
                if (count($columns) > 1) {
                    $ordering[$path][$column] = $list;
                } else {
                    $ordering[$path] = $list;
                }
            }
        }

        $data = new Config([]);
        $data->set('settings', $settings);
        $data->set('ordering', $ordering['']);
        $data->set('items', $items);

        return $data;
    }

    protected function getParticles()
    {
        $particles = $this->container['particles']->all();

        $list = [];
        foreach ($particles as $name => $particle) {
            $type = isset($particle['type']) ?
$particle['type'] : 'particle';
            $particleName = isset($particle['name']) ?
$particle['name'] : $name;
            $particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;
            $list[$type][$name] = ['name' => $particleName,
'icon' => $particleIcon];
        }

        return $list;
    }

    protected function executeForward($resource, $method = 'GET',
$path, $params = [])
    {
        $class = '\\Gantry\\Admin\\Controller\\Json\\' .
strtr(ucwords(strtr($resource, '/', ' ')), '
', '\\');
        if (!class_exists($class)) {
            throw new \RuntimeException('Page not found', 404);
        }

        /** @var HtmlController $controller */
        $controller = new $class($this->container);

        // Execute action.
        $response = $controller->execute($method, $path, $params);

        if (!$response instanceof Response) {
            $response = new HtmlResponse($response);
        }

        return $response;
    }
}
classes/Gantry/Admin/Controller/Html/Positions.php000064400000025265151166614510016244
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\Position\Module;
use Gantry\Component\Position\Position;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Assignments;
use Gantry\Framework\Positions as PositionsObject;

class Positions extends HtmlController
{
    protected $httpVerbs = [
        'GET' => [
            '/'                   => 'index',
            '/*'                  => 'undefined',
            '/*/add'              =>
'selectParticle',
        ],
        'POST' => [
            '/'                   => 'save',
            '/create'             => 'create',
            '/*'                  => 'undefined',
            '/*/rename'           => 'rename',
            '/*/duplicate'        => 'duplicate',
            '/*/delete'           => 'delete',
            '/*/edit'             => 'undefined',
            '/*/edit/particle'    => 'particle',
            '/*/edit/particle/*'  =>
'validateParticle',
            '/edit'               => 'undefined',
            '/edit/particle'      => 'particle',
        ]
    ];

    public function index()
    {
        $this->params['positions'] =
$this->container['positions'];

        return
$this->render('@gantry-admin/pages/positions/positions.html.twig',
$this->params);
    }

    public function create()
    {
        // Create only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        /** @var PositionsObject $position */
        $positions = $this->container['positions'];

        $title = (string)
$this->request->post->get('title',
'Untitled');
        $key = (string) $this->request->post['key'];

        $id = $positions->create($title, $key);

        $html =
$this->render('@gantry-admin/layouts/position.html.twig',
['position' => ['name' => $id, 'title'
=> $title]]);

        return new JsonResponse(['html' =>
sprintf("Position '%s' created.", $id), 'id'
=> "position-{$id}", 'key' => $id,
'position' => $html]);
    }

    public function rename($old)
    {
        // Rename only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        $title = (string) $this->request->post['title'];
        $key = (string) $this->request->post['key'];
        $data = (string) $this->request->post['data'];

        /** @var PositionsObject $positions */
        $positions = $this->container['positions'];
        $position = $positions[$old];
        $exists = isset($position);

        if (!$exists) {
            if (!$data) {
                throw new \RuntimeException(sprintf("Position
'%s' not found", $old), 404);
            }

            $position = new Position($key ?: $old);
        }

        if (strlen($title)) {
            $position->title = (string) $title;
        }
        if ($exists && strlen($key)) {
            $position = $position->rename($key);
        } else {
            $position->save();
        }

        if ($data) {
            $data = ['title' => $position->title] +
json_decode($data, true);

            $position = new Position($position->name, $data);
        }

        $html =
$this->render('@gantry-admin/layouts/position.html.twig',
['position' => $position]);

        return new JsonResponse(['html' =>
sprintf("Position saved"), 'id' =>
"position-{$position->name}", 'key' =>
$position->name, 'position' => $html]);
    }

    public function duplicate($position)
    {
        // Duplicate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        /** @var PositionsObject $positions */
        $positions = $this->container['positions'];

        $id = $positions->duplicate($position);

        return new JsonResponse(['html' =>
sprintf("Position duplicated as '%s'.", $id)]);
    }

    public function delete($position)
    {
        // Delete only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        /** @var PositionsObject $positions */
        $positions = $this->container['positions'];

        $positions->delete($position);

        return new JsonResponse(['html' =>
sprintf("Position '%s' deleted.", $position),
'position' => $position]);
    }

    public function save()
    {
        // Save only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        $data =
$this->request->post->getJsonArray('positions');

        /** @var PositionsObject $position */
        $positions = $this->container['positions'];
        $positions->import($data);

        return new JsonResponse(['html' =>
sprintf("Positions saved.")]);
    }

    public function particle($position = null)
    {
        if (!$position) {
            $position = $this->request->post['position'];
        }
        $data = $this->request->post['item'];
        if ($data) {
            $data = json_decode($data, true);
        } else {
            $data = $this->request->post->getArray();
        }
        $name = isset($data['options']['type']) ?
$data['options']['type'] :
(isset($data['particle']) ? $data['particle'] : null);

        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        $chromeBlueprints =
BlueprintForm::instance('position/chrome.yaml',
'gantry-admin://blueprints');

        $data['title'] = isset($data['title']) ?
$data['title'] : $blueprints['name'];
        $data['chrome'] = isset($data['chrome']) ?
$data['chrome'] : [];
        $data['options'] = isset($data['options']) ?
$data['options'] : [];
        $data['options']['type'] = $name;
        $attributes =
isset($data['options']['attributes']) ?
$data['options']['attributes'] : [];
        $assignments = new Assignments();

        $this->params += [
            'item'          => $data,
            'data'          => [
                'particles' => [$name => $attributes],
                'chrome'    => $data['chrome'],
                'assignments' =>
isset($data['assignments']) ? $data['assignments'] :
'all'
            ],
            'particle'      => $blueprints,
            'chrome'        => $chromeBlueprints,
            'assignments'   => $assignments->get(),
            'parent'        => 'settings',
            'prefix'        => "particles.{$name}.",
            'route'         =>
"configurations.default.settings",
            'action'        =>
"positions/{$position}/edit/particle/{$name}"
        ];

        return
$this->render('@gantry-admin/pages/positions/particle.html.twig',
$this->params);
    }


    public function validateParticle($position, $name)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        if (!$this->request->post->get('_end')) {
            throw new \OverflowException("Incomplete data received.
Please increase the value of 'max_input_vars' variable (in
php.ini or .htaccess)", 400);
        }

        // Load particle blueprints and default settings.
        $validator = new BlueprintSchema;
        $validator->embed('options',
$this->container['particles']->get($name));

        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Create configuration from the defaults.
        $data = new Config([],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->set('position', $position);
        $data->set('id', $id =
$this->request->post['id']);
        $data->set('type', 'particle');
        $data->set('title',
$this->request->post['title'] ?:
$blueprints->post['name']);
        $data->set('chrome',
$this->request->post->getArray('chrome'));
        $data->set('options.type', $name);
        $data->set('options.attributes',
$this->request->post->getArray("particles.{$name}"));
        $data->def('options.attributes.enabled', 1);

        $assignments = (new
Assignments())->filter($this->request->post->getArray('assignments'),
true);

        if (!$assignments) {
            // Use special syntax for no assignments.
            $assignments = 'none';
        } elseif ($assignments === ['page' => [true]]) {
            // Use special syntax for assigned to all pages. This is a
special case and hardcoded for now.
            $assignments = 'all';
        }

        $data->set('assignments', $assignments);

        // TODO: validate

        // Fill parameters to be passed to the template file.
        $this->params['position'] = $position;
        $this->params['item'] = (object) $data->toArray();
        $this->params['module'] = new Module($id, $position,
$data->toArray());

        $html =
$this->render('@gantry-admin/pages/positions/item.html.twig',
$this->params);

        return new JsonResponse(['item' =>
$data->toArray(), 'html' => $html, 'position'
=> $position]);
    }

    public function selectParticle($position)
    {
        $groups = [
            'Particles' => ['particle' => []],
        ];

        $particles = [
            'position'    => [],
            'spacer'      => [],
            'system'      => [],
            'particle'    => [],
        ];

        $particles = array_replace($particles, $this->getParticles());
        unset($particles['atom'],
$particles['position']);

        foreach ($particles as &$group) {
            asort($group);
        }

        foreach ($groups as $section => $children) {
            foreach ($children as $key => $child) {
                $groups[$section][$key] = $particles[$key];
            }
        }

        $this->params += [
            'particles' => $groups,
            'route' =>
"positions/{$position}/edit/particle",
        ];

        return
$this->render('@gantry-admin/modals/particle-picker.html.twig',
$this->params);
    }

    protected function getParticles()
    {
        $particles = $this->container['particles']->all();

        $list = [];
        foreach ($particles as $name => $particle) {
            $type = isset($particle['type']) ?
$particle['type'] : 'particle';
            $particleName = isset($particle['name']) ?
$particle['name'] : $name;
            $particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;
            $list[$type][$name] = ['name' => $particleName,
'icon' => $particleIcon];
        }

        return $list;
    }
}
classes/Gantry/Admin/Controller/Html/Themes.php000064400000001321151166614510015465
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Admin\ThemeList;
use Gantry\Component\Admin\HtmlController;

class Themes extends HtmlController
{
    public function index()
    {
        $this->params['themes'] = (new
ThemeList)->getThemes();

        return
$this->render('@gantry-admin/pages/themes/themes.html.twig',
$this->params);
    }
}
classes/Gantry/Admin/Controller/Json/Atoms.php000064400000013066151166614510015341
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;

/**
 * Class Atoms
 * @package Gantry\Admin\Controller\Json
 */
class Atoms extends JsonController
{
    protected $httpVerbs = [
        'GET' => [
            '/' => 'index',
            '/*' => 'index',
            '/instance' => 'atom'
        ],
        'POST' => [
            '/' => 'index',
            '/*' => 'index',
            '/instance' => 'atom'
        ]
    ];
    
    public function index()
    {
        $path = implode('/', func_get_args());

        $post = $this->request->request;

        $outline = $post['outline'];
        $type = $post['subtype'];
        $inherit = $post['inherit'];
        $id = $post['id'];

        if (!$outline) {
            throw new \RuntimeException('Outline not given',
400);
        }

        $this->container['outline'] = $outline;
        $this->container['configuration'] = $outline;

        $atoms = new \Gantry\Framework\Atoms((array)
$this->container['config']->get('page.head.atoms'));
        if ($inherit) {
            $atoms->inheritAll($outline);
        }

        $item = (object) $atoms->id($id);

        if ($path === 'list') {
            $list = $atoms->type($type);
            if (empty($item->id)) {
                $item = (object)reset($list);
            }
        }
        $selected = !empty($item->id) ? $item->id : null;

        $type = isset($item->type) ? $item->type : $type;
        $item->attributes = isset($item->attributes) ? (array)
$item->attributes : [];

        $blueprints =
$this->container['particles']->getBlueprintForm($type);
        $blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);

        $params = [
            'gantry'        => $this->container,
            'parent'        => 'settings',
            'route'         =>
"configurations.{$outline}.settings",
            'inherit'       => $inherit ? $outline : null,
            'title'         => isset($item->title) ?
$item->title : '',
            'blueprints'    =>
$blueprints->get('form'),
            'item'          => $item,
            'data'          => ['particles' =>
[$type => $item->attributes]],
            'prefix'        => "particles.{$type}.",
            'editable'      => true,
            'overrideable'  => false,
            'skip'          => ['enabled']
        ];

        $html['g-settings-atom'] =
$this->render('@gantry-admin/pages/configurations/layouts/particle-card.html.twig',
 $params);
        if (isset($list)) {
            $html['g-inherit-atom'] =
$this->renderAtomsInput($inherit ? $outline : null, $type, $selected,
$list);
        }

        return new JsonResponse(['json' => $item,
'html' => $html]);
    }

    public function atom()
    {
        $post = $this->request->request;

        $outline = $post['outline'];
        $id = $post['id'];

        if (!$outline) {
            throw new \RuntimeException('Outline not given',
400);
        }

        $this->container['outline'] = $outline;
        $this->container['configuration'] = $outline;

        $atoms = new \Gantry\Framework\Atoms((array)
$this->container['config']->get('page.head.atoms'));
        $item = (object) $atoms->id($id);
        if (empty($item->id)) {
            throw new \RuntimeException('Atom was not found from the
outline', 404);
        }

        $name = $item->type;

        $prefix = "particles.{$name}";

        $blueprints =
$this->container['particles']->getBlueprintForm($name);
        $blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);

        $item->attributes = isset($item->attributes) ? (array)
$item->attributes : [];

        $this->params['id'] = $name;
        $this->params += [
            'item'          => $item,
            'data'          => ['particles' =>
[$name => $item->attributes]],
            'prefix'        => "particles.{$name}.",
            'particle'      => $blueprints,
            'parent'        => 'settings',
            'route'         =>
"configurations.{$outline}.settings",
            'action'        => str_replace('.',
'/', 'configurations.' . $outline .
'.layout.' . $prefix . '.validate'),
            'skip'          => ['enabled'],
            'editable'      => false,
            'overrideable'  => false,
        ];

        $html =
$this->render('@gantry-admin/modals/atom-preview.html.twig',
$this->params);

        return new JsonResponse(['html' => $html]);
    }

    /**
     * Render input field for particle picker.
     *
     * @param string $outline
     * @param string $type
     * @param string $selected
     * @param array $instances
     * @return string
     */
    protected function renderAtomsInput($outline, $type, $selected, array
$instances)
    {
        $params = [
            'layout' => 'input',
            'scope' => 'inherit.',
            'field' => [
                'name' => 'atom',
                'type' => 'gantry.atoms',
                'id' => 'g-inherit-atom',
                'outline' => $outline,
                'atoms' => $instances,
                'atom' => $type
            ],
            'value' => $selected
        ];

        return
$this->render('@gantry-admin/forms/fields/gantry/atoms.html.twig',
$params);
    }
}
classes/Gantry/Admin/Controller/Json/Changelog.php000064400000005746151166614510016153
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Remote\Response as RemoteResponse;
use Gantry\Component\Response\JsonResponse;

class Changelog extends JsonController
{
    protected $url =
'https://raw.githubusercontent.com/gantry/gantry5';
    protected $fullurl =
'https://github.com/gantry/gantry5/blob/develop';
    protected $issues =
'https://github.com/gantry/gantry5/issues';
    protected $contrib = 'https://github.com';
    protected $file = 'CHANGELOG.md';

    protected $platforms = ['common' => 'share-alt',
'joomla' => '', 'wordpress' =>
'', 'grav' => ''];

    protected $httpVerbs = [
        'POST' => [
            '/' => 'index'
        ]
    ];

    public function index()
    {
        $version = $this->request->post['version'];
        $lookup = $version;
        
        if ($version == '@version@') {
            $version = 'develop';
            $lookup  = '';
        }

        if (substr($version, 0, 4) == 'dev-') {
            $version = preg_replace('/^dev-/i', '',
$version);
            $lookup  = '';
        }

        $url       = $this->url . '/' . $version .
'/' . $this->file;
        $changelog = RemoteResponse::get($url);

        if ($changelog) {
            $found = preg_match("/(#\\s" . $lookup .
".+?\\n.*?)(?=\\n{1,}#|$)/uis", $changelog, $changelog);

            if ($found) {
                $changelog =
\Parsedown::instance()->parse($changelog[0]);

                // auto-link issues
                $changelog = preg_replace("/#(\\d{1,})/uis",
'<a target="_blank" rel="noopener"
href="' . $this->issues .
'/$1">#$1</a>', $changelog);

                // auto-link contributors
                $changelog = preg_replace("/@([\\w]+)[^\\w]/uis",
'<a target="_blank" rel="noopener"
href="' . $this->contrib . '/$1">@$1</a>
', $changelog);

                // add icons for platforms
                foreach($this->platforms as $platform => $icon) {
                    $changelog = preg_replace('/(<a
href="\#' . $platform . '">)/uis',
'$1<i class="fa fa-' . ($icon ?: $platform) .
'" aria-hidden="true"></i> ',
$changelog);
                }
            } else {
                $changelog = 'No changelog for version
<strong>' . $version . '</strong> was found.';
            }
        }

        $response = [
            'html' =>
$this->render('@gantry-admin/ajax/changelog.html.twig', [
                'changelog' => $changelog,
                'version'   => $version,
                'url'       => $url,
                'fullurl'   => $this->fullurl .
'/' . $this->file
            ])
        ];

        return new JsonResponse($response);
    }
}
classes/Gantry/Admin/Controller/Json/Confirmdeletion.php000064400000001631151166614510017372
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;

class Confirmdeletion extends JsonController
{
    protected $httpVerbs = [
        'GET' => [
            '/' => 'index'
        ]
    ];

    public function index()
    {
        $pageType =
$this->request->get->get('page_type',
'OUTLINE');
        $response = ['html' =>
$this->render('@gantry-admin/ajax/confirm-deletion.html.twig',
['page_type' => $pageType])];

        return new JsonResponse($response);
    }
}
classes/Gantry/Admin/Controller/Json/Devprod.php000064400000002275151166614510015661
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;
use RocketTheme\Toolbox\Event\Event;

class Devprod extends JsonController
{
    public function store()
    {
        $production =
intval((bool)$this->request->post['mode']);

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->controller = $this;
        $event->data = ['production' => $production];

        $this->container->fireEvent('admin.global.save',
$event);

        $response = [
            'mode' => $production,
            'title' => $production ? 'Production' :
'Development',
            'html' => $production ? 'Production mode
enabled' : 'Development mode enabled',
        ];
        return new JsonResponse($response);
    }
}
classes/Gantry/Admin/Controller/Json/Filepicker.php000064400000037254151166614510016340
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Response\JsonResponse;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;


class Filepicker extends JsonController
{
    protected $base = false;
    protected $value = false;
    protected $filter = false;
    protected $httpVerbs = [
        'GET'    => [
            '/'            => 'index',
            '/*'           => 'index',
            '/display'     => 'undefined',
            '/display/**'  => 'displayFile',
            '/download'    => 'undefined',
            '/download/**' => 'downloadFile',
        ],
        'POST'   => [
            '/'            => 'index',
            '/*'           => 'index',
            '/subfolder'   => 'subfolder',
            '/subfolder/*' => 'subfolder',
            '/upload'      => 'undefined',
            '/upload/**'   => 'upload'
        ],
        'DELETE' => [
            '/'   => 'undefined',
            '/**' => 'delete'
        ]
    ];

    public function index()
    {
        /** @var UniformResourceLocator $locator */
        $locator   = $this->container['locator'];
        $bookmarks = [];
        $drives    = ['/'];
        $subfolder = false;

        $this->base = $locator->base;

        if ($this->method == 'POST') {
            $root         = $this->request->post['root'];
            $drives       = isset($root) ? ($root !== 'false' ?
(array) $root : ['/']) : ['/'];
            $subfolder    =
$this->request->post['subfolder'] ? true : false;
            $filter       = $this->request->post['filter'];
            $this->filter = isset($filter) ? ($filter !==
'false' ? $filter : false) : false;
            $this->value  =
$this->request->post['value'] ?: '';
        }

        foreach ($drives as $drive) {
            // cleanup of the path so it's chrooted.
            $drive  = str_replace('..', '', $drive);

            $isStream = $locator->isStream($drive);
            $path     = rtrim($this->base, '/') .
'/' . ltrim($drive, '/');

            // It's a stream but the scheme doesn't exist. we
skip it.
            if (!$isStream && (strpos($drive, '://') ||
!file_exists($path))) {
                continue;
            }

            if ($isStream && !$locator->findResources($drive)) {
                continue;
            }

            $key = $isStream ? $drive : preg_replace('#/{2,}+#',
'/', $drive);

            if (!array_key_exists($key, $bookmarks)) {
                $bookmarks[$key] = $isStream
                    ? [$locator->getIterator($drive)]
                    : [rtrim(Folder::getRelativePath($path), '/')
. '/'];
            }
        }

        if (!count($bookmarks)) {
            throw new \RuntimeException(sprintf('%s "%s" not
found', count($drives) > 1 ? 'directories' :
'directory', implode('", "', $drives)), 404);
        }

        $folders = [];
        $active  = [];

        $index = 0;
        $activeFallback = '';

        // iterating the folder and collecting subfolders and files
        foreach ($bookmarks as $key => $bookmark) {
            $folders[$key] = [];

            if (!$index) {
                $activeFallback = $key;
            }

            foreach ($bookmark as $folder) {
                $isStream = $this->isStream($folder);

                if ($isStream) {
                    unset($bookmarks[$key]);
                    $iterator = new \IteratorIterator($folder);
                    $folder   = $key;
                } else {
                    $iterator = new \DirectoryIterator($this->base .
'/' . ltrim($folder, '/'));
                }

                $folders[$key][$folder] = new \ArrayObject();
                if (!$index && !$this->value) {
                    $active[] = $folder;
                }

                /** @var \SplFileInfo $info */
                foreach ($iterator as $info) {
                    // no dot files nor files beginning with dot
                    if ($info->isDot() ||
substr($info->getFilename(), 0, 1) == '.') {
                        continue;
                    }

                    $file = new \stdClass();
                    $this->attachData($file, $info, $folder);

                    if ($file->dir) {
                        if ($file->pathname == dirname($this->value))
{
                            $active[] = $file->pathname;
                        }

                        $folders[$key][$folder]->append($file);
                    } else {
                        /*if ($filter && !preg_match("/"
. $filter . "/i", $file->filename)) {
                            continue;
                        }
                        if ((!$index && !$this->value) ||
(in_array(dirname($file->pathname), $active))) {
                            $files->append($file);
                        }*/
                    }
                }

                if ($isStream) {
                    $bookmarks[$key][] = $key;
                }

                $index++;
            }
        }

        if (!count($active)) {
            $active[] = $activeFallback;
        }

        $lastItem = end($active);
        $files    = $this->listFiles($lastItem);
        $response = [];

        reset($active);
        if (!$subfolder) {
            $response['html'] = $this->render(
                '@gantry-admin/ajax/filepicker.html.twig', [
                    'active'    => $active,
                    'base'      => $this->base,
                    'bookmarks' => $bookmarks,
                    'folders'   => $folders,
                    'files'     => $files,
                    'filter'    => $this->filter,
                    'value'     => $this->value
                ]
            );
        } else {
            $response['subfolder'] =
!$folders[$key][$folder]->count()
                ? false
                : $this->render(
                   
'@gantry-admin/ajax/filepicker/subfolders.html.twig',
                    ['folder' => $folders[$key][$folder]]
                );
            $response['files']     = $this->render(
                '@gantry-admin/ajax/filepicker/files.html.twig',
                ['files' => $files, 'value' =>
$this->value]
            );
        }

        return new JsonResponse($response);
    }

    protected function attachData(&$node, $iteration, $folder)
    {
        foreach (
            ['getFilename', 'getExtension',
'getPerms', 'getMTime', 'getBasename',
'getPathname', 'getSize', 'getType',
'isReadable', 'isWritable',
             'isDir', 'isFile'] as $method
        ) {
            $keyMethod          =
strtolower(preg_replace("/^(is|get)/", '', $method));
            $node->{$keyMethod} = $iteration->{$method}();

            if ($method == 'getPathname') {
                $node->{$keyMethod} = $this->isStream($folder) ?
$iteration->getUrl() : Folder::getRelativePath($node->{$keyMethod});
            } else {
                if ($method == 'getExtension') {
                    $node->isImage =
in_array(strtolower($node->{$keyMethod}), ['jpg',
'jpeg', 'png', 'gif', 'ico',
'svg', 'bmp', 'webp']);
                }
            }
        }

    }

    protected function listFiles($folder)
    {
        $isStream = $this->isStream($folder);
        $locator  = $this->container['locator'];
        $iterator = $isStream ? new
\IteratorIterator($locator->getIterator($folder)) : new
\DirectoryIterator($this->base . '/' . ltrim($folder,
'/'));
        $files    = new \ArrayObject();

        /** @var \SplFileInfo $info */
        foreach ($iterator as $info) {
            // no dot files nor files beginning with dot
            if ($info->isDot() || substr($info->getFilename(), 0, 1)
== '.') {
                continue;
            }

            $file = new \stdClass();
            $this->attachData($file, $info, $folder);

            if (!$file->dir) {
                if ($this->filter && !preg_match("/" .
$this->filter . "/i", $file->filename)) {
                    continue;
                }

                $file->isInCustom = false;

                if ($isStream) {
                    $stream         = explode('://', $folder);
                    $stream         = array_shift($stream) .
'://';
                    $customLocation = $locator->findResource($stream,
true, true);
                    if (substr($info->getPathname(), 0,
strlen($customLocation)) === $customLocation) {
                        $file->isInCustom = true;
                    }
                }


                $files->append($file);
            }
        }

        $files->asort();
        return $files;

    }

    public function subfolder()
    {
        $response         = [];
        $response['html'] = 'subfolder';

        return new JsonResponse($response);

    }

    public function displayFile()
    {
        $path = implode('/', func_get_args());

        $this->doDownload($path, false);

    }

    protected function doDownload($path, $download)
    {
        if (!$path) {
            throw new \RuntimeException('No file specified',
400);
        }

        // TODO: handle streams
        $targetPath = GANTRY5_ROOT . '/' . $path;

        if (!file_exists($targetPath)) {
            throw new \RuntimeException(sprintf('File not found:
%s', $path), 404);
        }

        $hash = md5_file($path);

        // Handle 304 Not Modified
        if
(isset($this->request->server['HTTP_IF_NONE_MATCH'])) {
            $etag =
stripslashes($this->request->server['HTTP_IF_NONE_MATCH']);

            if ($etag == $hash) {
                header('Last-Modified: ' . gmdate('D, d M Y
H:i:s', filemtime($path)) . ' GMT', true, 304);

                // Give fast response.
                flush();
                exit();
            }
        }

        // Set file headers.
        header('ETag: ' . $hash);
        header('Pragma: public');
        header('Last-Modified: ' . gmdate('D, d M Y
H:i:s', filemtime($path)) . ' GMT');

        // Get the image file information.
        $info    = getimagesize($path);
        $isImage = (bool)$info;

        if (!$download && $isImage) {
            $fileType = $info['mime'];

            // Force re-validate.
            header('Expires: 0');
            header('Cache-Control: must-revalidate, post-check=0,
pre-check=0');
            header('Content-type: ' . $fileType);
            header('Content-Disposition: inline; filename="'
. basename($path) . '"');
        } else {
            // Force file download.
            header('Expires: 0');
            header('Cache-Control: must-revalidate, post-check=0,
pre-check=0');
            header('Content-Description: File Transfer');
            header('Content-Type: application/force-download');
            header('Content-Type: application/octet-stream');
            header('Content-Type: application/download');
            header('Content-Disposition: attachment;
filename="' . basename($path) . '"');
        }

        header('Content-Transfer-Encoding: binary');
        header('Content-Length: ' . filesize($path));
        flush();

        // Output the file contents.
        @readfile($path);
        flush();

        exit();

    }

    public function downloadFile()
    {
        $path = implode('/', func_get_args());

        $this->doDownload($path, true);

    }

    public function upload()
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];
        $path    = implode('/', func_get_args());

        if (base64_decode($path, true) !== false) {
            $path = urldecode(base64_decode($path));
        }

        $stream = explode('://', $path);
        $scheme = $stream[0];

        $isStream = $locator->schemeExists($scheme);
        if ($isStream) {
            $targetPath = dirname($locator->findResource($path, true,
true));
        } else {
            $targetPath = dirname(GANTRY5_ROOT . '/' . $path);
        }

        if (!isset($_FILES['file']['error']) ||
is_array($_FILES['file']['error'])) {
            throw new \RuntimeException('No file sent', 400);
        }

        // Check $_FILES['file']['error'] value.
        switch ($_FILES['file']['error']) {
            case UPLOAD_ERR_OK:
                break;
            case UPLOAD_ERR_NO_FILE:
                throw new \RuntimeException('No file sent', 400);
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                throw new \RuntimeException('Exceeded filesize
limit.', 400);
            default:
                throw new \RuntimeException('Unkown errors',
400);
        }

        $maxSize =
$this->returnBytes(min(ini_get('post_max_size'),
ini_get('upload_max_filesize')));
        if ($_FILES['file']['size'] > $maxSize) {
            throw new \RuntimeException('Exceeded filesize limit. File
is ' . $_FILES['file']['size'] . ', maximum
allowed is ' . $maxSize, 400);
        }

        // Check extension
        $fileParts = pathinfo($_FILES['file']['name']);
        $fileExt   = strtolower($fileParts['extension']);

        // TODO: check if download is of supported type.

        // Upload it
        $destination = sprintf('%s/%s', $targetPath,
$_FILES['file']['name']);
        $destination = preg_replace('#//#', '/',
$destination);

        Folder::create($targetPath);

        if
(!move_uploaded_file($_FILES['file']['tmp_name'],
$destination)) {
            throw new \RuntimeException('Failed to move uploaded
file.', 500);
        }

        $finfo = new \stdClass();
        $this->attachData($finfo, new \SplFileInfo($destination),
$targetPath);
        return new JsonResponse(['success' => 'File
uploaded successfully', 'finfo' => $finfo,
'url' => $path]);

    }

    protected function returnBytes($size_str)
    {
        switch (strtolower(substr($size_str, -1))) {
            case 'm':
            case 'mb':
                return (int)$size_str * 1048576;
            case 'k':
            case 'kb':
                return (int)$size_str * 1024;
            case 'g':
            case 'gb':
                return (int)$size_str * 1073741824;
            default:
                return $size_str;
        }

    }

    public function delete()
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];
        $path    = implode('/', func_get_args());

        if (base64_decode($path, true) !== false) {
            $path = urldecode(base64_decode($path));
        }

        $stream = explode('://', $path);
        $scheme = $stream[0];

        if (!$path) {
            throw new \RuntimeException('No file specified for
delete', 400);
        }

        $isStream = $locator->schemeExists($scheme);
        if ($isStream) {
            $targetPath = $locator->findResource($path, true, true);
        } else {
            $targetPath = GANTRY5_ROOT . '/' . $path;
        }

        $file = File::instance($targetPath);

        if (!$file->exists()) {
            throw new \RuntimeException(sprintf('File not found:
%s', $targetPath), 404);
        }

        try {
            $file->delete();
        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('File could not be
deleted: %s', $targetPath), 500);
        }
        $file->free();

        return new JsonResponse(['success', 'File deleted:
' . $targetPath]);
    }

    private function isStream($folder)
    {
        return $folder instanceof UniformResourceIterator ||
strpos($folder, '://');
    }
}
classes/Gantry/Admin/Controller/Json/Fontpicker.php000064400000006714151166614510016364
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;
use RocketTheme\Toolbox\File\JsonFile;

class Fontpicker extends JsonController
{
    protected $google_fonts =
'gantry-admin://js/google-fonts.json';

    protected $httpVerbs = [
        'GET' => [
            '/' => 'index'
        ]
    ];

    public function index()
    {
        $this->params['fonts'] = $this->loadGoogleFonts();
        $this->params['variantsMap'] =
$this->variantsMap();
        $response = [
            'html' =>
$this->render('@gantry-admin/ajax/fontpicker.html.twig',
$this->params)
        ];
        return new JsonResponse($response);
    }

    public function loadGoogleFonts()
    {
        $data = new \stdClass();
        $file = JsonFile::instance($this->google_fonts);
        $fonts = $file->content()['items'];
        $file->free();

        $data->categories = [];
        $data->subsets = [];

        // create list of unique categories and subsets
        array_walk($fonts, function (&$item) use ($data) {
            if (!in_array($item->category, $data->categories)) {
                $data->categories[] = $item->category;
            }
            $data->subsets = array_unique(array_merge($data->subsets,
$item->subsets));
        });

        asort($data->categories);
        asort($data->subsets);

        $data->families = $fonts;
        $data->local_families = $this->loadLocalFonts();

        if (count($data->local_families)) {
            array_unshift($data->categories, 'local-fonts');
        }

        $data->count = count($data->families);

        return $data;
    }

    public function loadLocalFonts()
    {
        $local_fonts =
$this->container['theme']->details()->get('configuration.fonts',
[]);
        $map = [];

        foreach ($local_fonts as $name => $variants) {
            if (is_array($variants)) {
                $list = array_keys($variants);
            } else {
                $list = ['regular'];
            }

            $map[] = ['family' => $name, 'variants'
=> $list, 'category' => 'local-fonts'];
        }

        return $map;
    }

    protected function variantsMap()
    {
        return [
            '100'       => 'Thin 100',
            '100italic' => 'Thin 100 Italic',
            '200'       => 'Extra-Light 200',
            '200italic' => 'Extra-Light 200 Italic',
            '300'       => 'Light 300',
            '300italic' => 'Light 300 Italic',
            '400'       => 'Normal 400',
            'regular'   => 'Normal 400',
            '400italic' => 'Normal 400 Italic',
            'italic'    => 'Normal 400 Italic',
            '500'       => 'Medium 500',
            '500italic' => 'Medium 500 Italic',
            '600'       => 'Semi-Bold 600',
            '600italic' => 'Semi-Bold 600 Italic',
            '700'       => 'Bold 700',
            '700italic' => 'Bold 700 Italic',
            '800'       => 'Extra-Bold 800',
            '800italic' => 'Extra-Bold 800 Italic',
            '900'       => 'Ultra-Bold 900',
            '900italic' => 'Ultra-Bold 900 Italic'
        ];
    }
}
classes/Gantry/Admin/Controller/Json/Icons.php000064400000043200151166614510015322
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;

class Icons extends JsonController
{
    public function index()
    {
        $response = [];

        // Font Awesome Icons list [v4.7.0 - 730 icons]
        // NOTE: To get an updated list of icons:
        //       1. Go to: http://fontawesome.io/icons/
        //       2. Open Console in Deveveloper Tools
        //       3. Type this JS snippet: var list = [];
document.querySelectorAll('.fontawesome-icon-list
i').forEach(function(icon, index){ var name =
icon.className.replace(/^fa\s/, ''); list.push(name); }); var
output = '$list = ["' + list.join('",
"') + '"];'; console.log(output); copy(output);
console.info('The output has been copied to the clipboard, you can now
CMD + V / CTRL + V to update the icons variable');
        //       4. Press Enter and replace the line below
        $list = ["fa-address-book",
"fa-address-book-o", "fa-address-card",
"fa-address-card-o", "fa-bandcamp",
"fa-bath", "fa-bathtub",
"fa-drivers-license", "fa-drivers-license-o",
"fa-eercast", "fa-envelope-open",
"fa-envelope-open-o", "fa-etsy",
"fa-free-code-camp", "fa-grav",
"fa-handshake-o", "fa-id-badge",
"fa-id-card", "fa-id-card-o", "fa-imdb",
"fa-linode", "fa-meetup", "fa-microchip",
"fa-podcast", "fa-quora", "fa-ravelry",
"fa-s15", "fa-shower", "fa-snowflake-o",
"fa-superpowers", "fa-telegram",
"fa-thermometer", "fa-thermometer-0",
"fa-thermometer-1", "fa-thermometer-2",
"fa-thermometer-3", "fa-thermometer-4",
"fa-thermometer-empty", "fa-thermometer-full",
"fa-thermometer-half", "fa-thermometer-quarter",
"fa-thermometer-three-quarters", "fa-times-rectangle",
"fa-times-rectangle-o", "fa-user-circle",
"fa-user-circle-o", "fa-user-o", "fa-vcard",
"fa-vcard-o", "fa-window-close",
"fa-window-close-o", "fa-window-maximize",
"fa-window-minimize", "fa-window-restore",
"fa-wpexplorer", "fa-address-book",
"fa-address-book-o", "fa-address-card",
"fa-address-card-o", "fa-adjust",
"fa-american-sign-language-interpreting", "fa-anchor",
"fa-archive", "fa-area-chart", "fa-arrows",
"fa-arrows-h", "fa-arrows-v",
"fa-asl-interpreting",
"fa-assistive-listening-systems", "fa-asterisk",
"fa-at", "fa-audio-description",
"fa-automobile", "fa-balance-scale",
"fa-ban", "fa-bank", "fa-bar-chart",
"fa-bar-chart-o", "fa-barcode", "fa-bars",
"fa-bath", "fa-bathtub", "fa-battery",
"fa-battery-0", "fa-battery-1",
"fa-battery-2", "fa-battery-3",
"fa-battery-4", "fa-battery-empty",
"fa-battery-full", "fa-battery-half",
"fa-battery-quarter", "fa-battery-three-quarters",
"fa-bed", "fa-beer", "fa-bell",
"fa-bell-o", "fa-bell-slash",
"fa-bell-slash-o", "fa-bicycle",
"fa-binoculars", "fa-birthday-cake",
"fa-blind", "fa-bluetooth", "fa-bluetooth-b",
"fa-bolt", "fa-bomb", "fa-book",
"fa-bookmark", "fa-bookmark-o", "fa-braille",
"fa-briefcase", "fa-bug", "fa-building",
"fa-building-o", "fa-bullhorn",
"fa-bullseye", "fa-bus", "fa-cab",
"fa-calculator", "fa-calendar",
"fa-calendar-check-o", "fa-calendar-minus-o",
"fa-calendar-o", "fa-calendar-plus-o",
"fa-calendar-times-o", "fa-camera",
"fa-camera-retro", "fa-car",
"fa-caret-square-o-down", "fa-caret-square-o-left",
"fa-caret-square-o-right", "fa-caret-square-o-up",
"fa-cart-arrow-down", "fa-cart-plus",
"fa-cc", "fa-certificate", "fa-check",
"fa-check-circle", "fa-check-circle-o",
"fa-check-square", "fa-check-square-o",
"fa-child", "fa-circle", "fa-circle-o",
"fa-circle-o-notch", "fa-circle-thin",
"fa-clock-o", "fa-clone", "fa-close",
"fa-cloud", "fa-cloud-download",
"fa-cloud-upload", "fa-code", "fa-code-fork",
"fa-coffee", "fa-cog", "fa-cogs",
"fa-comment", "fa-comment-o",
"fa-commenting", "fa-commenting-o",
"fa-comments", "fa-comments-o", "fa-compass",
"fa-copyright", "fa-creative-commons",
"fa-credit-card", "fa-credit-card-alt",
"fa-crop", "fa-crosshairs", "fa-cube",
"fa-cubes", "fa-cutlery", "fa-dashboard",
"fa-database", "fa-deaf", "fa-deafness",
"fa-desktop", "fa-diamond",
"fa-dot-circle-o", "fa-download",
"fa-drivers-license", "fa-drivers-license-o",
"fa-edit", "fa-ellipsis-h", "fa-ellipsis-v",
"fa-envelope", "fa-envelope-o",
"fa-envelope-open", "fa-envelope-open-o",
"fa-envelope-square", "fa-eraser",
"fa-exchange", "fa-exclamation",
"fa-exclamation-circle", "fa-exclamation-triangle",
"fa-external-link", "fa-external-link-square",
"fa-eye", "fa-eye-slash", "fa-eyedropper",
"fa-fax", "fa-feed", "fa-female",
"fa-fighter-jet", "fa-file-archive-o",
"fa-file-audio-o", "fa-file-code-o",
"fa-file-excel-o", "fa-file-image-o",
"fa-file-movie-o", "fa-file-pdf-o",
"fa-file-photo-o", "fa-file-picture-o",
"fa-file-powerpoint-o", "fa-file-sound-o",
"fa-file-video-o", "fa-file-word-o",
"fa-file-zip-o", "fa-film", "fa-filter",
"fa-fire", "fa-fire-extinguisher", "fa-flag",
"fa-flag-checkered", "fa-flag-o", "fa-flash",
"fa-flask", "fa-folder", "fa-folder-o",
"fa-folder-open", "fa-folder-open-o",
"fa-frown-o", "fa-futbol-o", "fa-gamepad",
"fa-gavel", "fa-gear", "fa-gears",
"fa-gift", "fa-glass", "fa-globe",
"fa-graduation-cap", "fa-group",
"fa-hand-grab-o", "fa-hand-lizard-o",
"fa-hand-paper-o", "fa-hand-peace-o",
"fa-hand-pointer-o", "fa-hand-rock-o",
"fa-hand-scissors-o", "fa-hand-spock-o",
"fa-hand-stop-o", "fa-handshake-o",
"fa-hard-of-hearing", "fa-hashtag",
"fa-hdd-o", "fa-headphones", "fa-heart",
"fa-heart-o", "fa-heartbeat", "fa-history",
"fa-home", "fa-hotel", "fa-hourglass",
"fa-hourglass-1", "fa-hourglass-2",
"fa-hourglass-3", "fa-hourglass-end",
"fa-hourglass-half", "fa-hourglass-o",
"fa-hourglass-start", "fa-i-cursor",
"fa-id-badge", "fa-id-card", "fa-id-card-o",
"fa-image", "fa-inbox", "fa-industry",
"fa-info", "fa-info-circle",
"fa-institution", "fa-key", "fa-keyboard-o",
"fa-language", "fa-laptop", "fa-leaf",
"fa-legal", "fa-lemon-o", "fa-level-down",
"fa-level-up", "fa-life-bouy",
"fa-life-buoy", "fa-life-ring",
"fa-life-saver", "fa-lightbulb-o",
"fa-line-chart", "fa-location-arrow",
"fa-lock", "fa-low-vision", "fa-magic",
"fa-magnet", "fa-mail-forward",
"fa-mail-reply", "fa-mail-reply-all",
"fa-male", "fa-map", "fa-map-marker",
"fa-map-o", "fa-map-pin", "fa-map-signs",
"fa-meh-o", "fa-microchip", "fa-microphone",
"fa-microphone-slash", "fa-minus",
"fa-minus-circle", "fa-minus-square",
"fa-minus-square-o", "fa-mobile",
"fa-mobile-phone", "fa-money", "fa-moon-o",
"fa-mortar-board", "fa-motorcycle",
"fa-mouse-pointer", "fa-music", "fa-navicon",
"fa-newspaper-o", "fa-object-group",
"fa-object-ungroup", "fa-paint-brush",
"fa-paper-plane", "fa-paper-plane-o",
"fa-paw", "fa-pencil", "fa-pencil-square",
"fa-pencil-square-o", "fa-percent",
"fa-phone", "fa-phone-square", "fa-photo",
"fa-picture-o", "fa-pie-chart", "fa-plane",
"fa-plug", "fa-plus", "fa-plus-circle",
"fa-plus-square", "fa-plus-square-o",
"fa-podcast", "fa-power-off", "fa-print",
"fa-puzzle-piece", "fa-qrcode",
"fa-question", "fa-question-circle",
"fa-question-circle-o", "fa-quote-left",
"fa-quote-right", "fa-random", "fa-recycle",
"fa-refresh", "fa-registered", "fa-remove",
"fa-reorder", "fa-reply", "fa-reply-all",
"fa-retweet", "fa-road", "fa-rocket",
"fa-rss", "fa-rss-square", "fa-s15",
"fa-search", "fa-search-minus",
"fa-search-plus", "fa-send", "fa-send-o",
"fa-server", "fa-share", "fa-share-alt",
"fa-share-alt-square", "fa-share-square",
"fa-share-square-o", "fa-shield", "fa-ship",
"fa-shopping-bag", "fa-shopping-basket",
"fa-shopping-cart", "fa-shower",
"fa-sign-in", "fa-sign-language",
"fa-sign-out", "fa-signal", "fa-signing",
"fa-sitemap", "fa-sliders", "fa-smile-o",
"fa-snowflake-o", "fa-soccer-ball-o",
"fa-sort", "fa-sort-alpha-asc",
"fa-sort-alpha-desc", "fa-sort-amount-asc",
"fa-sort-amount-desc", "fa-sort-asc",
"fa-sort-desc", "fa-sort-down",
"fa-sort-numeric-asc", "fa-sort-numeric-desc",
"fa-sort-up", "fa-space-shuttle",
"fa-spinner", "fa-spoon", "fa-square",
"fa-square-o", "fa-star", "fa-star-half",
"fa-star-half-empty", "fa-star-half-full",
"fa-star-half-o", "fa-star-o",
"fa-sticky-note", "fa-sticky-note-o",
"fa-street-view", "fa-suitcase", "fa-sun-o",
"fa-support", "fa-tablet", "fa-tachometer",
"fa-tag", "fa-tags", "fa-tasks",
"fa-taxi", "fa-television", "fa-terminal",
"fa-thermometer", "fa-thermometer-0",
"fa-thermometer-1", "fa-thermometer-2",
"fa-thermometer-3", "fa-thermometer-4",
"fa-thermometer-empty", "fa-thermometer-full",
"fa-thermometer-half", "fa-thermometer-quarter",
"fa-thermometer-three-quarters", "fa-thumb-tack",
"fa-thumbs-down", "fa-thumbs-o-down",
"fa-thumbs-o-up", "fa-thumbs-up",
"fa-ticket", "fa-times", "fa-times-circle",
"fa-times-circle-o", "fa-times-rectangle",
"fa-times-rectangle-o", "fa-tint",
"fa-toggle-down", "fa-toggle-left",
"fa-toggle-off", "fa-toggle-on",
"fa-toggle-right", "fa-toggle-up",
"fa-trademark", "fa-trash", "fa-trash-o",
"fa-tree", "fa-trophy", "fa-truck",
"fa-tty", "fa-tv", "fa-umbrella",
"fa-universal-access", "fa-university",
"fa-unlock", "fa-unlock-alt", "fa-unsorted",
"fa-upload", "fa-user", "fa-user-circle",
"fa-user-circle-o", "fa-user-o",
"fa-user-plus", "fa-user-secret",
"fa-user-times", "fa-users", "fa-vcard",
"fa-vcard-o", "fa-video-camera",
"fa-volume-control-phone", "fa-volume-down",
"fa-volume-off", "fa-volume-up",
"fa-warning", "fa-wheelchair",
"fa-wheelchair-alt", "fa-wifi",
"fa-window-close", "fa-window-close-o",
"fa-window-maximize", "fa-window-minimize",
"fa-window-restore", "fa-wrench",
"fa-american-sign-language-interpreting",
"fa-asl-interpreting",
"fa-assistive-listening-systems",
"fa-audio-description", "fa-blind",
"fa-braille", "fa-cc", "fa-deaf",
"fa-deafness", "fa-hard-of-hearing",
"fa-low-vision", "fa-question-circle-o",
"fa-sign-language", "fa-signing", "fa-tty",
"fa-universal-access", "fa-volume-control-phone",
"fa-wheelchair", "fa-wheelchair-alt",
"fa-hand-grab-o", "fa-hand-lizard-o",
"fa-hand-o-down", "fa-hand-o-left",
"fa-hand-o-right", "fa-hand-o-up",
"fa-hand-paper-o", "fa-hand-peace-o",
"fa-hand-pointer-o", "fa-hand-rock-o",
"fa-hand-scissors-o", "fa-hand-spock-o",
"fa-hand-stop-o", "fa-thumbs-down",
"fa-thumbs-o-down", "fa-thumbs-o-up",
"fa-thumbs-up", "fa-ambulance",
"fa-automobile", "fa-bicycle", "fa-bus",
"fa-cab", "fa-car", "fa-fighter-jet",
"fa-motorcycle", "fa-plane", "fa-rocket",
"fa-ship", "fa-space-shuttle", "fa-subway",
"fa-taxi", "fa-train", "fa-truck",
"fa-wheelchair", "fa-wheelchair-alt",
"fa-genderless", "fa-intersex", "fa-mars",
"fa-mars-double", "fa-mars-stroke",
"fa-mars-stroke-h", "fa-mars-stroke-v",
"fa-mercury", "fa-neuter", "fa-transgender",
"fa-transgender-alt", "fa-venus",
"fa-venus-double", "fa-venus-mars",
"fa-file", "fa-file-archive-o",
"fa-file-audio-o", "fa-file-code-o",
"fa-file-excel-o", "fa-file-image-o",
"fa-file-movie-o", "fa-file-o",
"fa-file-pdf-o", "fa-file-photo-o",
"fa-file-picture-o", "fa-file-powerpoint-o",
"fa-file-sound-o", "fa-file-text",
"fa-file-text-o", "fa-file-video-o",
"fa-file-word-o", "fa-file-zip-o",
"fa-circle-o-notch", "fa-cog", "fa-gear",
"fa-refresh", "fa-spinner",
"fa-check-square", "fa-check-square-o",
"fa-circle", "fa-circle-o",
"fa-dot-circle-o", "fa-minus-square",
"fa-minus-square-o", "fa-plus-square",
"fa-plus-square-o", "fa-square",
"fa-square-o", "fa-cc-amex",
"fa-cc-diners-club", "fa-cc-discover",
"fa-cc-jcb", "fa-cc-mastercard",
"fa-cc-paypal", "fa-cc-stripe", "fa-cc-visa",
"fa-credit-card", "fa-credit-card-alt",
"fa-google-wallet", "fa-paypal",
"fa-area-chart", "fa-bar-chart",
"fa-bar-chart-o", "fa-line-chart",
"fa-pie-chart", "fa-bitcoin", "fa-btc",
"fa-cny", "fa-dollar", "fa-eur",
"fa-euro", "fa-gbp", "fa-gg",
"fa-gg-circle", "fa-ils", "fa-inr",
"fa-jpy", "fa-krw", "fa-money",
"fa-rmb", "fa-rouble", "fa-rub",
"fa-ruble", "fa-rupee", "fa-shekel",
"fa-sheqel", "fa-try", "fa-turkish-lira",
"fa-usd", "fa-won", "fa-yen",
"fa-align-center", "fa-align-justify",
"fa-align-left", "fa-align-right", "fa-bold",
"fa-chain", "fa-chain-broken",
"fa-clipboard", "fa-columns", "fa-copy",
"fa-cut", "fa-dedent", "fa-eraser",
"fa-file", "fa-file-o", "fa-file-text",
"fa-file-text-o", "fa-files-o",
"fa-floppy-o", "fa-font", "fa-header",
"fa-indent", "fa-italic", "fa-link",
"fa-list", "fa-list-alt", "fa-list-ol",
"fa-list-ul", "fa-outdent", "fa-paperclip",
"fa-paragraph", "fa-paste", "fa-repeat",
"fa-rotate-left", "fa-rotate-right",
"fa-save", "fa-scissors", "fa-strikethrough",
"fa-subscript", "fa-superscript", "fa-table",
"fa-text-height", "fa-text-width", "fa-th",
"fa-th-large", "fa-th-list", "fa-underline",
"fa-undo", "fa-unlink",
"fa-angle-double-down", "fa-angle-double-left",
"fa-angle-double-right", "fa-angle-double-up",
"fa-angle-down", "fa-angle-left",
"fa-angle-right", "fa-angle-up",
"fa-arrow-circle-down", "fa-arrow-circle-left",
"fa-arrow-circle-o-down", "fa-arrow-circle-o-left",
"fa-arrow-circle-o-right", "fa-arrow-circle-o-up",
"fa-arrow-circle-right", "fa-arrow-circle-up",
"fa-arrow-down", "fa-arrow-left",
"fa-arrow-right", "fa-arrow-up", "fa-arrows",
"fa-arrows-alt", "fa-arrows-h",
"fa-arrows-v", "fa-caret-down",
"fa-caret-left", "fa-caret-right",
"fa-caret-square-o-down", "fa-caret-square-o-left",
"fa-caret-square-o-right", "fa-caret-square-o-up",
"fa-caret-up", "fa-chevron-circle-down",
"fa-chevron-circle-left", "fa-chevron-circle-right",
"fa-chevron-circle-up", "fa-chevron-down",
"fa-chevron-left", "fa-chevron-right",
"fa-chevron-up", "fa-exchange",
"fa-hand-o-down", "fa-hand-o-left",
"fa-hand-o-right", "fa-hand-o-up",
"fa-long-arrow-down", "fa-long-arrow-left",
"fa-long-arrow-right", "fa-long-arrow-up",
"fa-toggle-down", "fa-toggle-left",
"fa-toggle-right", "fa-toggle-up",
"fa-arrows-alt", "fa-backward",
"fa-compress", "fa-eject", "fa-expand",
"fa-fast-backward", "fa-fast-forward",
"fa-forward", "fa-pause", "fa-pause-circle",
"fa-pause-circle-o", "fa-play",
"fa-play-circle", "fa-play-circle-o",
"fa-random", "fa-step-backward",
"fa-step-forward", "fa-stop",
"fa-stop-circle", "fa-stop-circle-o",
"fa-youtube-play", "fa-500px", "fa-adn",
"fa-amazon", "fa-android", "fa-angellist",
"fa-apple", "fa-bandcamp", "fa-behance",
"fa-behance-square", "fa-bitbucket",
"fa-bitbucket-square", "fa-bitcoin",
"fa-black-tie", "fa-bluetooth",
"fa-bluetooth-b", "fa-btc", "fa-buysellads",
"fa-cc-amex", "fa-cc-diners-club",
"fa-cc-discover", "fa-cc-jcb",
"fa-cc-mastercard", "fa-cc-paypal",
"fa-cc-stripe", "fa-cc-visa", "fa-chrome",
"fa-codepen", "fa-codiepie",
"fa-connectdevelop", "fa-contao", "fa-css3",
"fa-dashcube", "fa-delicious",
"fa-deviantart", "fa-digg", "fa-dribbble",
"fa-dropbox", "fa-drupal", "fa-edge",
"fa-eercast", "fa-empire", "fa-envira",
"fa-etsy", "fa-expeditedssl", "fa-fa",
"fa-facebook", "fa-facebook-f",
"fa-facebook-official", "fa-facebook-square",
"fa-firefox", "fa-first-order", "fa-flickr",
"fa-font-awesome", "fa-fonticons",
"fa-fort-awesome", "fa-forumbee",
"fa-foursquare", "fa-free-code-camp",
"fa-ge", "fa-get-pocket", "fa-gg",
"fa-gg-circle", "fa-git", "fa-git-square",
"fa-github", "fa-github-alt",
"fa-github-square", "fa-gitlab", "fa-gittip",
"fa-glide", "fa-glide-g", "fa-google",
"fa-google-plus", "fa-google-plus-circle",
"fa-google-plus-official", "fa-google-plus-square",
"fa-google-wallet", "fa-gratipay", "fa-grav",
"fa-hacker-news", "fa-houzz", "fa-html5",
"fa-imdb", "fa-instagram",
"fa-internet-explorer", "fa-ioxhost",
"fa-joomla", "fa-jsfiddle", "fa-lastfm",
"fa-lastfm-square", "fa-leanpub",
"fa-linkedin", "fa-linkedin-square",
"fa-linode", "fa-linux", "fa-maxcdn",
"fa-meanpath", "fa-medium", "fa-meetup",
"fa-mixcloud", "fa-modx", "fa-odnoklassniki",
"fa-odnoklassniki-square", "fa-opencart",
"fa-openid", "fa-opera", "fa-optin-monster",
"fa-pagelines", "fa-paypal", "fa-pied-piper",
"fa-pied-piper-alt", "fa-pied-piper-pp",
"fa-pinterest", "fa-pinterest-p",
"fa-pinterest-square", "fa-product-hunt",
"fa-qq", "fa-quora", "fa-ra",
"fa-ravelry", "fa-rebel", "fa-reddit",
"fa-reddit-alien", "fa-reddit-square",
"fa-renren", "fa-resistance", "fa-safari",
"fa-scribd", "fa-sellsy", "fa-share-alt",
"fa-share-alt-square", "fa-shirtsinbulk",
"fa-simplybuilt", "fa-skyatlas", "fa-skype",
"fa-slack", "fa-slideshare", "fa-snapchat",
"fa-snapchat-ghost", "fa-snapchat-square",
"fa-soundcloud", "fa-spotify",
"fa-stack-exchange", "fa-stack-overflow",
"fa-steam", "fa-steam-square",
"fa-stumbleupon", "fa-stumbleupon-circle",
"fa-superpowers", "fa-telegram",
"fa-tencent-weibo", "fa-themeisle",
"fa-trello", "fa-tripadvisor", "fa-tumblr",
"fa-tumblr-square", "fa-twitch",
"fa-twitter", "fa-twitter-square", "fa-usb",
"fa-viacoin", "fa-viadeo",
"fa-viadeo-square", "fa-vimeo",
"fa-vimeo-square", "fa-vine", "fa-vk",
"fa-wechat", "fa-weibo", "fa-weixin",
"fa-whatsapp", "fa-wikipedia-w",
"fa-windows", "fa-wordpress",
"fa-wpbeginner", "fa-wpexplorer",
"fa-wpforms", "fa-xing", "fa-xing-square",
"fa-y-combinator", "fa-y-combinator-square",
"fa-yahoo", "fa-yc", "fa-yc-square",
"fa-yelp", "fa-yoast", "fa-youtube",
"fa-youtube-play", "fa-youtube-square",
"fa-ambulance", "fa-h-square", "fa-heart",
"fa-heart-o", "fa-heartbeat",
"fa-hospital-o", "fa-medkit",
"fa-plus-square", "fa-stethoscope",
"fa-user-md", "fa-wheelchair",
"fa-wheelchair-alt"];
        $options = [
            'fw' => 'Fixed Width',
            'spin' => 'Spinning',
            'larger' => ['' => '- Size -
', 'lg' => 'Large', '2x' =>
'2x', '3x' => '3x', '4x' =>
'4x', '5x' => '5x'],
            'rotation' => ['' => '- Rotation
-', 'flip-horizontal' => 'Horizontal Flip',
'flip-vertical' => 'Vertical Flip',
'rotate-90' => 'Rotate 90°', 'rotate-180'
=> 'Rotate 180°', 'rotate-270' => 'Rotate
270°']
        ];

        $list = array_unique($list);
        sort($list);

        $response['html'] =
$this->render('@gantry-admin/ajax/icons.html.twig',
['icons' => $list, 'options' => $options,
'total' => count($list)]);

        return new JsonResponse($response);
    }
}
classes/Gantry/Admin/Controller/Json/Layouts.php000064400000020114151166614510015706
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Response\JsonResponse;

/**
 * Class Layouts
 * @package Gantry\Admin\Controller\Json
 */
class Layouts extends JsonController
{
    protected $httpVerbs = [
        'GET' => [
            '/' => 'index',
            '/*' => 'index',
            '/particle' => 'particle'
        ],
        'POST' => [
            '/' => 'index',
            '/*' => 'index',
            '/particle' => 'particle'
        ]
    ];
    
    public function index()
    {
        $path = implode('/', func_get_args());

        $post = $this->request->request;

        $outline = $post['outline'];
        $type = $post['type'];
        $subtype = $post['subtype'];
        $inherit = $post['inherit'];
        $clone = $post['mode'] === 'clone';
        $id = $post['id'];

        $this->container['outline'] = $outline;
        $this->container['configuration'] = $outline;

        $layout = Layout::instance($outline);
        if ($inherit) {
            $layout->inheritAll();
        }

        if ($path == 'list' &&
!$layout->isLayoutType($type)) {
            $instance = $this->getParticleInstances($outline, $subtype,
null);
            $id = $instance['selected'];
        }

        $item = $layout->find($id);
        $type = isset($item->type) ? $item->type : $type;
        $subtype = isset($item->subtype) ? $item->subtype : $subtype;
        $item->attributes = isset($item->attributes) ? (array)
$item->attributes : [];
        $block = $layout->block($id);
        $block = isset($block->attributes) ? (array)
$block->attributes : [];

        $params = [
            'gantry'        => $this->container,
            'parent'        => 'settings',
            'route'         =>
"configurations.{$outline}.settings",
            'inherit'       => $inherit ? $outline : null,
        ];

        if ($layout->isLayoutType($type)) {
            $name = $type;
            $particle = false;
            $defaults = [];
            $blueprints =
BlueprintForm::instance("layout/{$name}.yaml",
'gantry-admin://blueprints');
        } else {
            $name = $subtype;
            $particle = true;
            $defaults =
$this->container['config']->get("particles.{$name}");
            $item->attributes = $item->attributes + $defaults;
            $blueprints =
$this->container['particles']->getBlueprintForm($name);
            $blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
        }

        $paramsParticle = [
            'title'         => isset($item->title) ?
$item->title : '',
            'blueprints'    =>
$blueprints->get('form'),
            'item'          => $item,
            'data'          => ['particles' =>
[$name => $item->attributes]],
            'defaults'      => ['particles' =>
[$name => $defaults]],
            'prefix'        => "particles.{$name}.",
            'editable'      => $particle,
            'overrideable'  => $particle,
            'skip'          => ['enabled']
        ] + $params;

        $html['g-settings-particle'] =
$this->render('@gantry-admin/pages/configurations/layouts/particle-card.html.twig',
 $paramsParticle);
        $html['g-settings-block-attributes'] =
$this->renderBlockFields($block, $params);
        if ($path == 'list') {
            $html['g-inherit-particle'] =
$this->renderParticlesInput($inherit || $clone ? $outline : null,
$subtype, $post['selected']);
        }

        return new JsonResponse(['json' => $item,
'html' => $html]);
    }

    public function particle()
    {
        $post = $this->request->request;

        $outline = $post['outline'];
        $id = $post['id'];

        $this->container['outline'] = $outline;
        $this->container['configuration'] = $outline;

        $layout = Layout::instance($outline);

        $particle = clone $layout->find($id);
        if (!isset($particle->type)) {
            throw new \RuntimeException('Particle was not found from
the outline', 404);
        }

        $particle->block = $layout->block($id);

        $name = $particle->subtype;
        $prefix = "particles.{$name}";
        $defaults = (array)
$this->container['config']->get($prefix);
        $attributes = (array) $particle->attributes + $defaults;

        $particleBlueprints =
$this->container['particles']->getBlueprintForm($name);
        $particleBlueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);

        $blockBlueprints =
BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');

        // TODO: Use blueprints to merge configuration.
        $particle->attributes = (object) $attributes;

        $this->params['id'] = $name;
        $this->params += [
            'extra'         => $blockBlueprints,
            'item'          => $particle,
            'data'          => ['particles' =>
[$name => $attributes]],
            'defaults'      => ['particles' =>
[$name => $defaults]],
            'prefix'        => "particles.{$name}.",
            'particle'      => $particleBlueprints,
            'parent'        => 'settings',
            'route'         =>
"configurations.{$outline}.settings",
            'action'        => str_replace('.',
'/', 'configurations.' . $outline .
'.layout.' . $prefix . '.validate'),
            'skip'          => ['enabled'],
            'editable'      => false,
            'overrideable'  => true,
        ];

        $html =
$this->render('@gantry-admin/pages/configurations/layouts/particle-preview.html.twig',
$this->params);

        return new JsonResponse(['html' => $html]);
    }

    /**
     * Render block settings.
     *
     * @param array $block
     * @param array $params
     * @return string
     */
     protected function renderBlockFields(array $block, array $params)
     {
         $blockBlueprints =
BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');

         $paramsBlock = [
                 'title' =>
$this->container['translator']->translate('GANTRY5_PLATFORM_BLOCK'),
                 'blueprints' => ['fields' =>
$blockBlueprints->get('form/fields/block_container/fields')],
                 'data' => ['block' => $block],
                 'prefix' => 'block.',
             ] + $params;

         return
$this->render('@gantry-admin/forms/fields.html.twig', 
$paramsBlock);
     }

    /**
     * Gets the list of available particle instances for an outline
     *
     * @param string $outline
     * @param string $particle
     * @param string $selected
     * @return string
     */

    protected function getParticleInstances($outline, $particle, $selected)
    {
        $list = $outline ?
$this->container['outlines']->getParticleInstances($outline,
$particle, false) : [];
        $selected = isset($list[$selected]) ? $selected : key($list);

        return ['list' => $list, 'selected' =>
$selected];
    }

    /**
     * Render input field for particle picker.
     *
     * @param string $outline
     * @param string $particle
     * @param string $selected
     * @return string
     */
    protected function renderParticlesInput($outline, $particle, $selected)
    {
        $instances = $this->getParticleInstances($outline, $particle,
$selected);

        $params = [
            'layout' => 'input',
            'scope' => 'inherit.',
            'field' => [
                'name' => 'particle',
                'type' => 'gantry.particles',
                'id' => 'g-inherit-particle',
                'outline' => $outline,
                'particles' => $instances['list'],
                'particle' => $particle
            ],
            'value' => $instances['selected']
        ];

        return
$this->render('@gantry-admin/forms/fields/gantry/particles.html.twig',
$params);
    }
}
classes/Gantry/Admin/Controller/Json/Particle.php000064400000014451151166614510016020
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\Response\JsonResponse;

class Particle extends JsonController
{
    protected $httpVerbs = [
        'GET'    => [
            '/'                  =>
'selectParticle',
            '/module'            => 'selectModule'
        ],
        'POST'   => [
            '/'                  => 'undefined',
            '/*'                 => 'particle',
            '/*/validate'        => 'validate',
        ],
        'PUT'    => [
            '/*' => 'replace'
        ],
        'PATCH'  => [
            '/*' => 'update'
        ],
        'DELETE' => [
            '/*' => 'destroy'
        ]
    ];

    /**
     * Return a modal for selecting a particle.
     *
     * @return string
     */
    public function selectParticle()
    {
        $groups = [
            'Particles' => ['particle' => []],
        ];

        $particles = [
            'position'    => [],
            'spacer'      => [],
            'system'      => [],
            'particle'    => [],
        ];

        $particles = array_replace($particles, $this->getParticles());
        unset($particles['atom'],
$particles['position']);

        foreach ($particles as &$group) {
            asort($group);
        }

        foreach ($groups as $section => $children) {
            foreach ($children as $key => $child) {
                $groups[$section][$key] = $particles[$key];
            }
        }

        $this->params['particles'] = $groups;
        return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/particle-picker.html.twig',
$this->params)]);
    }

    /**
     * Return a modal content for selecting module.
     *
     * @return mixed
     */
    public function selectModule()
    {
        return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/module-picker.html.twig',
$this->params)]);
    }

    /**
     * Return form for the particle (filled with data coming from POST).
     *
     * @param string $name
     * @return mixed
     */
    public function particle($name)
    {
        $data = $this->request->post['item'];
        if ($data) {
            $data = json_decode($data, true);
        } else {
            $data = $this->request->post->getArray();
        }

        // TODO: add support for other block types as well, like menu.
        // $block = BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');
        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Load particle blueprints and default settings.
        $validator = $this->loadBlueprints('menu');
        $callable = function () use ($validator) {
            return $validator;
        };

        // Create configuration from the defaults.
        $item = new Config($data, $callable);
        $item->def('type', 'particle');
        $item->def('title',
$blueprints->get('name'));
        $item->def('options.type',
$blueprints->get('type', 'particle'));
        $item->def('options.particle', []);
        $item->def('options.block', []);

        $this->params += [
            'item'          => $item,
            // 'block'         => $block,
            'data'          => ['particles' =>
[$name => $item->options['particle']]],
            'particle'      => $blueprints,
            'parent'        => 'settings',
            'prefix'        => "particles.{$name}.",
            'route'         =>
"configurations.default.settings",
            'action'        =>
"particle/{$name}/validate"
        ];

        return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/particle.html.twig',
$this->params)]);
    }

    /**
     * Validate data for the particle.
     *
     * @param string $name
     * @return JsonResponse
     */
    public function validate($name)
    {
        // Load particle blueprints and default settings.
        $validator = new BlueprintSchema;
        $validator->embed('options',
$this->container['particles']->get($name));

        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Create configuration from the defaults.
        $data = new Config([],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->set('type', 'particle');
        $data->set('particle', $name);
        $data->set('title',
$this->request->post['title'] ?:
$blueprints->get('name'));
        $data->set('options.particle',
$this->request->post->getArray("particles.{$name}"));
        $data->def('options.particle.enabled', 1);

        $block =
$this->request->post->getArray('block');
        foreach ($block as $key => $param) {
            if ($param === '') {
                unset($block[$key]);
            }
        }

        if ($block) {
            $data->join('options.block', $block);
        }

        // TODO: validate

        // Fill parameters to be passed to the template file.
        $this->params['item'] = (object) $data->toArray();

        return new JsonResponse(['item' =>
$data->toArray()]);
    }

    protected function getParticles()
    {
        $particles = $this->container['particles']->all();

        $list = [];
        foreach ($particles as $name => $particle) {
            $type = isset($particle['type']) ?
$particle['type'] : 'particle';
            $particleName = isset($particle['name']) ?
$particle['name'] : $name;
            $particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;
            $list[$type][$name] = ['name' => $particleName,
'icon' => $particleIcon];
        }

        return $list;
    }

    /**
     * Load blueprints.
     *
     * @param string $name
     *
     * @return BlueprintForm
     */
    protected function loadBlueprints($name = 'menu')
    {
        return BlueprintForm::instance("menu/{$name}.yaml",
'gantry-admin://blueprints');
    }
}
classes/Gantry/Admin/Controller/Json/Unsaved.php000064400000001445151166614510015661
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;

class Unsaved extends JsonController
{
    protected $httpVerbs = [
        'GET' => [
            '/' => 'index'
        ]
    ];

    public function index()
    {
        $response = ['html' =>
$this->render('@gantry-admin/ajax/unsaved.html.twig')];
        return new JsonResponse($response);
    }
}
classes/Gantry/Admin/EventListener.php000064400000017247151166614510014016
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Admin;

use Gantry\Component\Layout\Layout;
use Gantry\Joomla\CacheHelper;
use Gantry\Joomla\Manifest;
use Gantry\Joomla\StyleHelper;
use Joomla\Registry\Registry;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
use RocketTheme\Toolbox\File\IniFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class EventListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            'admin.init.theme'  =>
['onAdminThemeInit', 0],
            'admin.global.save' => ['onGlobalSave',
0],
            'admin.styles.save' => ['onStylesSave',
0],
            'admin.settings.save' =>
['onSettingsSave', 0],
            'admin.layout.save' => ['onLayoutSave',
0],
            'admin.assignments.save' =>
['onAssignmentsSave', 0],
            'admin.menus.save' => ['onMenusSave', 0]
        ];
    }

    public function onAdminThemeInit(Event $event)
    {
        \JPluginHelper::importPlugin('gantry5');

        // Trigger the onGantryThemeInit event.
        $dispatcher = \JEventDispatcher::getInstance();
        $dispatcher->trigger('onGantry5AdminInit',
['theme' => $event->theme]);
    }

    public function onGlobalSave(Event $event)
    {
        \JPluginHelper::importPlugin('gantry5');

        // Trigger the onGantryThemeUpdateCss event.
        $dispatcher = \JEventDispatcher::getInstance();
        $dispatcher->trigger('onGantry5SaveConfig',
[$event->data]);
    }

    public function onStylesSave(Event $event)
    {
        \JPluginHelper::importPlugin('gantry5');

        // Trigger the onGantryThemeUpdateCss event.
        $dispatcher = \JEventDispatcher::getInstance();
        $dispatcher->trigger('onGantry5UpdateCss',
['theme' => $event->theme]);
    }

    public function onSettingsSave(Event $event)
    {
    }

    public function onLayoutSave(Event $event)
    {
        /** @var Layout $layout */
        $layout = $event->layout;

        if ($layout->name[0] !== '_' &&
$layout->name !== 'default') {
            $preset = isset($layout->preset['name']) ?
$layout->preset['name'] : 'default';

            // Update Joomla template style.
            StyleHelper::update($layout->name, $preset);
        }

        $theme = $event->gantry['theme.name'];

        $positions =
$event->gantry['outlines']->positions();
        $positions['debug'] = 'Debug';

        $manifest = new Manifest($theme);
        $manifest->setPositions(array_keys($positions));
        $manifest->save();

        $translations = [];
        foreach ($positions as $key => $translation) {
            // Encode translation key in Joomla way.
            $key = preg_replace('/[^A-Z0-9_\-]/', '_',
strtoupper("TPL_{$theme}_POSITION_{$key}"));
            $translations[$key] = $translation;
        }

        /** @var UniformResourceLocator $locator */
        $locator = $event->gantry['locator'];

        $filename =
"gantry-theme://language/en-GB/en-GB.tpl_{$theme}_positions.ini";

        $ini = IniFile::instance($locator->findResource($filename, true,
true));
        $ini->save($translations);
        $ini->free();
    }

    public function onAssignmentsSave(Event $event)
    {
    }


    public function onMenusSave(Event $event)
    {
        $defaults = [
            'id' => 0,
            'layout' => 'list',
            'target' => '_self',
            'dropdown' => '',
            'icon' => '',
            'image' => '',
            'subtitle' => '',
            'icon_only' => false,
            'visible' => true,
            'group' => 0,
            'columns' => [],
            'link_title' => '',
            'hash' => '',
            'class' => ''
        ];

        $gantry = $event->gantry;
        $menu = $event->menu;

        // Save global menu settings into Joomla.
        /** @var \JTableMenuType $table */
        $menuType = \JTable::getInstance('MenuType');
        if (!$menuType->load(['menutype' =>
$event->resource])) {
            throw new \RuntimeException("Saving menu failed: Menu type
{$event->resource} not found.", 400);
        }
        $options = [
            'title' => $menu['settings.title'],
            'description' =>
$menu['settings.description']
        ];
        if ($gantry->authorize('menu.edit') &&
!$menuType->save($options)) {
            throw new \RuntimeException('Saving menu failed: '.
$menuType->getError(), 400);
        }

        unset($menu['settings']);

        /** @var \JTableMenu $table */
        $table = \JTable::getInstance('menu');

        foreach ($menu['items'] as $key => $item) {
            $id = !empty($item['id']) ? (int)
$item['id'] : 0;
            if ($id && $table->load($item['id'])) {
                $params = new Registry($table->params);

                // Menu item exists in Joomla, let's update it
instead.
                unset($item['type'], $item['link']);

                $item['id'] = (int) $id;

                $title = $menu["items.{$key}.title"];
                $browserNav = intval($menu["items.{$key}.target"]
=== '_blank');

                $options = [
                    // Disabled as the option has different meaning in
Joomla than in Gantry, see issue #1656.
                    // 'menu-anchor_css' =>
$menu["items.{$key}.class"],
                    'menu_image' =>
$menu["items.{$key}.image"],
                    'menu_text' =>
intval(!$menu["items.{$key}.icon_only"]),
                    'menu_show' =>
intval($menu["items.{$key}.enabled"]),
                ];

                $modified = false;

                if ($table->title != $title) {
                    $table->title = $title;
                    $modified = true;
                }

                if ($table->browserNav != $browserNav) {
                    $table->browserNav = $browserNav;
                    $modified = true;
                }

                foreach ($options as $var => $value) {
                    if ($params->get($var) !== $value) {
                        $params->set($var, $value);
                        $modified = true;
                    }
                }

                if ($modified &&
$gantry->authorize('menu.edit')) {
                    $table->params = (string) $params;
                    if (!$table->check() || !$table->store()) {
                        throw new \RuntimeException("Failed to save
/{$key}: {$table->getError()}", 400);
                    }
                }

                // Avoid saving values which are also stored in Joomla.
                unset($item['title'],
$item['anchor_class'], $item['image'],
$item['icon_only'], $item['target']);
                if (version_compare(JVERSION, '3.5.1',
'>=')) {
                    unset($item['enabled']);
                }

            }

            // Do not save default values.
            foreach ($defaults as $var => $value) {
                if (isset($item[$var]) && $item[$var] == $value) {
                    unset($item[$var]);
                }
            }

            // Do not save derived values.
            unset($item['path'], $item['alias'],
$item['parent_id'], $item['level'],
$item['group']);

            // Particles have no link.
            if (isset($item['type']) &&
$item['type'] === 'particle') {
                unset($item['link']);
            }

            // Because of ordering we need to save all menu items,
including those from Joomla which have no data except id.
            $event->menu["items.{$key}"] = $item;
        }

        // Clean the cache.
        CacheHelper::cleanMenu();
    }
}
classes/Gantry/Admin/Page.php000064400000007021151166614510012070
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin;

use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Theme as SiteTheme;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Page
{
    protected $container;
    protected $files;
    protected $blocks;

    public function __construct($container)
    {
        $this->container = $container;
    }

    public function all()
    {
        if (!$this->blocks)
        {
            $files = $this->locateBlocks();

            $this->blocks = [];
            foreach ($files as $key => $fileArray) {
                $filename = key($fileArray);
                $file = CompiledYamlFile::instance(GANTRY5_ROOT .
'/' . $filename);
                $this->blocks[$key] = $file->content();
                $file->free();
            }
        }

        return $this->blocks;
    }

    public function group()
    {
        $blocks = $this->all();

        $list = [];
        foreach ($blocks as $name => $setting) {
            $type = isset($setting['type']) ?
$setting['type'] : '';
            $list[$type][$name] = $setting;
        }

        return $this->sort($list);
    }

    public function get($id)
    {
        if ($this->blocks[$id]) {
            return $this->blocks[$id];
        }

        $files = $this->locateBlocks();

        if (empty($files[$id])) {
            throw new \RuntimeException(sprintf("Settings for
'%s' not found.", $id), 404);
        }

        $filename = key($files[$id]);
        $file = CompiledYamlFile::instance(GANTRY5_ROOT . '/' .
$filename);
        $setting = $file->content();
        $file->free();

        return $setting;
    }

    /**
     * @param string $id
     * @return BlueprintForm
     */
    public function getBlueprintForm($id)
    {
        return BlueprintForm::instance($id,
'gantry-blueprints://page');
    }

    protected function sort(array $blocks)
    {
        $list = [];

        /** @var SiteTheme $theme */
        $theme = $this->container['theme'];
        $ordering = (array) $theme->details()['admin.page'];
        if (!count($ordering)) {
            $ordering = ['global' => ['head',
'assets', 'body', 'generics']];
        }

        ksort($blocks);

        foreach ($ordering as $name => $order) {
            if (isset($blocks[$name])) {
                $list[$name] = $this->sortItems($blocks[$name], (array)
$order);
            }
        }
        $list += $blocks;

        return $list;
    }

    protected function sortItems(array $items, array $ordering)
    {
        $list = [];

        ksort($items);

        foreach ($ordering as $name) {
            if (isset($items[$name])) {
                $list[$name] = $items[$name];
            }
        }
        $list += $items;

        return $list;
    }

    protected function locateBlocks()
    {
        if (!$this->files) {
            /** @var UniformResourceLocator $locator */
            $locator = $this->container['locator'];
            $paths =
$locator->findResources('gantry-blueprints://page');

            $this->files = (new ConfigFileFinder)->listFiles($paths);
        }

        return $this->files;
    }
}
classes/Gantry/Admin/Particles.php000064400000011356151166614510013150
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin;

use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Theme as SiteTheme;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Particles
{
    protected $container;
    protected $files;
    protected $particles;

    public function __construct($container)
    {
        $this->container = $container;
    }

    public function overrides($outline, $particle = null)
    {
        if ($outline === 'default') {
            return true;
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        if ($particle) {
            // PHP 5.4
            $resource =
$locator->findResources("gantry-theme://config/{$outline}/particles/{$particle}.yaml");
            return !empty($resource);
        }

        // PHP 5.4
        $resource =
$locator->findResources("gantry-theme://config/{$outline}/particles");
        return !empty($resource);
    }

    public function all()
    {
        if (null === $this->particles) {
            $platform = $this->container['platform'];
            $files = $this->locateParticles();

            $this->particles = [];
            foreach ($files as $key => $fileArray) {
                $filename = key($fileArray);
                $file = CompiledYamlFile::instance(GANTRY5_ROOT .
'/' . $filename);
                $particle = $file->content();
                $file->free();

                if (!isset($particle['dependencies']) ||
$platform->checkDependencies($particle['dependencies'])) {
                    $this->particles[$key] = $particle;
                }
            }
        }

        return $this->particles;
    }

    public function group($exclude = [])
    {
        $particles = $this->all();

        $list = [];
        foreach ($particles as $name => $particle) {
            $type = isset($particle['type']) ?
$particle['type'] : 'particle';
            if (in_array($type, $exclude)) {
                continue;
            }
            if (in_array($type, ['spacer', 'system']))
{
                $type = 'position';
            }
            $list[$type][$name] = $particle;
        }

        return $this->sort($list);
    }

    public function get($id)
    {
        if (isset($this->particles[$id])) {
            return $this->particles[$id];
        }

        $files = $this->locateParticles();

        if (empty($files[$id])) {
            throw new \RuntimeException(sprintf("Settings for
'%s' not found.", $id), 404);
        }

        $filename = key($files[$id]);
        $file = CompiledYamlFile::instance(GANTRY5_ROOT . '/' .
$filename);
        $particle = $file->content();
        $particle['subtype'] = $id; // TODO: can this be done
better or is it fine like that?
        $file->free();

        return $particle;
    }

    /**
     * @param string $id
     * @return BlueprintForm
     */
    public function getBlueprintForm($id)
    {
        return BlueprintForm::instance($id,
'gantry-blueprints://particles');
    }

    protected function sort(array $blocks)
    {
        $list = [];

        /** @var SiteTheme $theme */
        $theme = $this->container['theme'];
        $ordering = (array)
$theme->details()['admin.settings'] ?: [
                'particle' => [],
                'position' => ['position',
'spacer', 'messages', 'content'],
                'atom' => []
            ];

        ksort($blocks);

        foreach ($ordering as $name => $order) {
            if (isset($blocks[$name])) {
                $list[$name] = $this->sortItems($blocks[$name], (array)
$order);
            }
        }
        $list += $blocks;

        return $list;
    }


    protected function sortItems(array $items, array $ordering)
    {
        $list = [];

        ksort($items);

        foreach ($ordering as $name) {
            if (isset($items[$name])) {
                $list[$name] = $items[$name];
            }
        }
        $list += $items;

        return $list;
    }

    protected function locateParticles()
    {
        if (!$this->files) {
            /** @var UniformResourceLocator $locator */
            $locator = $this->container['locator'];
            $paths =
$locator->findResources('gantry-blueprints://particles');

            $this->files = (new ConfigFileFinder)->listFiles($paths);
        }

        return $this->files;
    }
}
classes/Gantry/Admin/Router.php000064400000012062151166614510012475
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Admin;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Request\Request;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Response\Response;
use Gantry\Component\Router\Router as BaseRouter;
use Gantry\Joomla\StyleHelper;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Gantry administration router for Joomla.
 */
class Router extends BaseRouter
{
    public function boot()
    {
        \JHtml::_('behavior.keepalive');

        $app = \JFactory::getApplication();
        $input = $app->input;

        // TODO: Remove style variable.
        $style = $input->getInt('style');
        $theme = $input->getCmd('theme');
        $path = array_filter(explode('/',
$input->getString('view', '')), function($var) {
return $var !== ''; });

        $this->setTheme($theme, $style);

        /** @var Request $request */
        $request = $this->container['request'];

        $this->method = $request->getMethod();
        $this->path = $path ?:
(isset($this->container['theme.name']) ?
['configurations', true] : ['themes']);
        $this->resource = array_shift($this->path);
        $this->format = $input->getCmd('format',
'html');
        $ajax = ($this->format == 'json');

        $this->params = [
            'user' => \JFactory::getUser(),
            'ajax' => $ajax,
            'location' => $this->resource,
            'method' => $this->method,
            'format' => $this->format,
            'params' =>
$request->post->getJsonArray('params')
        ];

        return $this;
    }

    public function setTheme($theme, $style)
    {
        if ($style) {
            \JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');
            $table = \JTable::getInstance('Style',
'TemplatesTable');
            $table->load(['id' => $style,
'client_id' => 0]);

            $theme = $table->template;
        }
        if (!$theme) {
            $theme = StyleHelper::getDefaultStyle()->template;
        }

        $path = JPATH_SITE . '/templates/' . $theme;

        if (!is_file("{$path}/gantry/theme.yaml")) {
            $theme = null;
            $this->container['streams']->register();

            /** @var UniformResourceLocator $locator */
            $locator = $this->container['locator'];

            CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
            CompiledYamlFile::$defaultCaching =
$this->container['global']->get('compile_yaml',
1);
        }

        $this->container['base_url'] = \JUri::base(true) .
'/index.php?option=com_gantry5';

        $this->container['ajax_suffix'] =
'&format=json';

        $token = \JSession::getFormToken();

        $this->container['routes'] = [
            '1' =>
"&view=%s&theme={$theme}&{$token}=1",

            'themes' => '&view=themes',
            'picker/layouts' =>
"&view=layouts&theme={$theme}&{$token}=1",
        ];

        if (!$theme) {
            return $this;
        }

        $this->container['theme.path'] = $path;
        $this->container['theme.name'] = $theme;

        // Load language file for the template.
        $languageFile = 'tpl_' . $theme;
        $lang = \JFactory::getLanguage();
        $lang->load($languageFile, JPATH_SITE)
            || $lang->load($languageFile, $path)
            || $lang->load($languageFile, $path, 'en-GB');

        return $this;
    }

    protected function checkSecurityToken()
    {
        return \JSession::checkToken('get');
    }

    /**
     * Send response to the client.
     *
     * @param Response $response
     * @return string
     */
    protected function send(Response $response)
    {
        $app = \JFactory::getApplication();
        $document = \JFactory::getDocument();
        $document->setCharset($response->charset);
        $document->setMimeEncoding($response->mimeType);

        // Output HTTP header.
        $app->setHeader('Status', $response->getStatus());
        $app->setHeader('Content-Type', $response->mimeType
. '; charset=' . $response->charset);
        foreach ($response->getHeaders() as $key => $values) {
            $replace = true;
            foreach ($values as $value) {
                $app->setHeader($key, $value, $replace);
                $replace = false;
            }
        }

        if ($response instanceof JsonResponse) {
            $app->setHeader('Expires', 'Wed, 17 Aug 2005
00:00:00 GMT', true);
            $app->setHeader('Last-Modified', gmdate('D, d
M Y H:i:s') . ' GMT', true);
            $app->setHeader('Cache-Control', 'no-store,
no-cache, must-revalidate, post-check=0, pre-check=0', false);
            $app->setHeader('Pragma', 'no-cache');
            $app->sendHeaders();
        }

        // Output Gantry response.
        echo $response;

        if ($response instanceof JsonResponse) {
            $app->close();
        }
    }
}
classes/Gantry/Admin/Styles.php000064400000006637151166614510012513
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin;

use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Theme as SiteTheme;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Styles
{
    protected $container;
    protected $files;
    protected $blocks;

    public function __construct($container)
    {
        $this->container = $container;
    }

    public function all()
    {
        if (!$this->blocks)
        {
            $files = $this->locateBlocks();

            $this->blocks = [];
            foreach ($files as $key => $fileArray) {
                $filename = key($fileArray);
                $file = CompiledYamlFile::instance(GANTRY5_ROOT .
'/' . $filename);
                $this->blocks[$key] = $file->content();
                $file->free();
            }
        }

        return $this->blocks;
    }

    public function group()
    {
        $blocks = $this->all();

        $list = [];
        foreach ($blocks as $name => $style) {
            $type = isset($style['type']) ?
$style['type'] : 'block';
            $list[$type][$name] = $style;
        }

        return $this->sort($list);
    }

    public function get($id)
    {
        if ($this->blocks[$id]) {
            return $this->blocks[$id];
        }

        $files = $this->locateBlocks();

        if (empty($files[$id])) {
            throw new \RuntimeException(sprintf("Settings for
'%s' not found.", $id), 404);
        }

        $filename = key($files[$id]);
        $file = CompiledYamlFile::instance(GANTRY5_ROOT . '/' .
$filename);
        $particle = $file->content();
        $file->free();

        return $particle;
    }

    /**
     * @param string $id
     * @return BlueprintForm
     */
    public function getBlueprintForm($id)
    {
        return BlueprintForm::instance($id,
'gantry-blueprints://styles');
    }

    protected function sort(array $blocks)
    {
        $list = [];

        /** @var SiteTheme $theme */
        $theme = $this->container['theme'];
        $ordering = (array) $theme->details()['admin.styles'];

        ksort($blocks);

        foreach ($ordering as $name => $order) {
            if (isset($blocks[$name])) {
                $list[$name] = $this->sortItems($blocks[$name], (array)
$order);
            }
        }
        $list += $blocks;

        return $list;
    }

    protected function sortItems(array $items, array $ordering)
    {
        $list = [];

        ksort($items);

        foreach ($ordering as $name) {
            if (isset($items[$name])) {
                $list[$name] = $items[$name];
            }
        }
        $list += $items;

        return $list;
    }

    protected function locateBlocks()
    {
        if (!$this->files) {
            /** @var UniformResourceLocator $locator */
            $locator = $this->container['locator'];
            $paths =
$locator->findResources('gantry-blueprints://styles');

            $this->files = (new ConfigFileFinder)->listFiles($paths);
        }

        return $this->files;
    }
}
classes/Gantry/Admin/Theme.php000064400000007560151166614510012266
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin;

use Gantry\Component\Config\CompiledConfig;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Theme\AbstractTheme;
use Gantry\Framework\Platform;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Theme extends AbstractTheme
{
    /**
     * @see AbstractTheme::init()
     */
    protected function init()
    {
        $gantry = static::gantry();

        // Add particles, styles and defaults into DI.

        $gantry['particles'] = function ($c) {
            return new Particles($c);
        };

        $gantry['styles'] = function ($c) {
            return new Styles($c);
        };

        $gantry['page'] = function ($c) {
            return new Page($c);
        };

        $gantry['defaults'] = function($c) {
            /** @var UniformResourceLocator $locator */
            $locator = $c['locator'];

            $cache =
$locator->findResource('gantry-cache://theme/compiled/config',
true, true);
            $paths =
$locator->findResources('gantry-config://default');

            $files = (new ConfigFileFinder)->locateFiles($paths);

            $config = new CompiledConfig($cache, $files, GANTRY5_ROOT);
            $config->setBlueprints(function() use ($c) {
                return $c['blueprints'];
            });

            return $config->load(true);
        };

        // Initialize admin streams.

        /** @var Platform $patform */
        $patform = $gantry['platform'];

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $nucleus =
$patform->getEnginePaths('nucleus')[''];
        if (strpos($this->path, '://')) {
            $relpath = $this->path;
        } else {
            $relpath = Folder::getRelativePath($this->path);
        }
        $patform->set(
            'streams.gantry-admin.prefixes', [
                ''        =>
['gantry-theme://admin', $relpath, $relpath .
'/common', 'gantry-engine://admin'],
                'assets/' => array_merge([$relpath, $relpath .
'/common'], $nucleus, ['gantry-assets://'])
            ]
        );

        // Add admin paths.
        foreach
($patform->get('streams.gantry-admin.prefixes') as $prefix
=> $paths) {
            $locator->addPath('gantry-admin', $prefix,
$paths);
        }

        // Fire admin init event.
        $event = new Event;
        $event->gantry = $gantry;
        $event->theme = $this;
        $gantry->fireEvent('admin.init.theme', $event);
    }

    /**
     * @see AbstractTheme::getCachePath()
     *
     * @param string $path
     * @return string
     */
    protected function getCachePath($path = '')
    {
        $gantry = static::gantry();

        /** @var Platform $patform */
        $patform = $gantry['platform'];

        // Initialize theme cache stream.
        return $patform->getCachePath() . '/admin' . ($path ?
'/' . $path : '');
    }

    /**
     * @see AbstractTheme::setTwigLoaderPaths()
     *
     * @param \Twig_LoaderInterface $loader
     */
    protected function setTwigLoaderPaths(\Twig_LoaderInterface $loader)
    {
        if (!($loader instanceof \Twig_Loader_Filesystem)) {
            return;
        }

        $gantry = static::gantry();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

       
$loader->setPaths($locator->findResources('gantry-admin://templates'));
       
$loader->setPaths($locator->findResources('gantry-admin://templates'),
'gantry-admin');
    }
}
classes/Gantry/Admin/ThemeList.php000064400000010577151166614510013124
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Admin;

use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Theme\ThemeDetails;
use Gantry\Framework\Gantry;
use Joomla\Registry\Registry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;


class ThemeList
{
    /**
     * @var ThemeDetails[]
     */
    protected static $items;

    /**
     * @var array
     */
    protected static $styles;

    /**
     * @return array
     */
    public static function getThemes()
    {
        if (!is_array(static::$items)) {
            static::loadThemes();
        }

        $list = [];
        foreach (static::$items as $item) {
            $details = static::getTheme($item['name']);
            if ($details) {
                $list[$item['name']] = $details;
            }
        }

        return $list;
    }

    /**
     * @param string $name
     * @return mixed
     */
    public static function getTheme($name)
    {
        $styles = static::getStyles($name);

        return reset($styles);
    }

    /**
     * @param string $template
     * @return array
     */
    public static function getStyles($template = null, $force = false)
    {
        if ($force || !is_array(static::$styles)) {
            static::loadStyles();
        }

        if ($template) {
            return isset(static::$styles[$template]) ?
static::$styles[$template] : [];
        }

        $list = [];
        foreach (static::$styles as $styles) {
            $list += $styles;
        }

        ksort($list);

        return $list;
    }

    protected static function loadThemes()
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        /** @var array|ThemeDetails[] $list */
        $list = [];

        $files = Folder::all('gantry-themes://',
['recursive' => false, 'files' => false]);
        natsort($files);

        foreach ($files as $theme) {
            if ($locator('gantry-themes://' . $theme .
'/gantry/theme.yaml')) {
                $details = new ThemeDetails($theme);
                $details->addStreams();

                $details['name'] = $theme;
                $details['title'] =
$details['details.name'];
                $details['preview_url'] = null;
                $details['admin_url'] =
$gantry['platform']->getThemeAdminUrl($theme);
                $details['params'] = [];

                $list[$details->name] = $details;

            }
        }

        // Add Thumbnails links after adding all the paths to the locator.
        foreach ($list as $details) {
            $details['thumbnail'] =
$details->getUrl("details.images.thumbnail");
        }

        static::$items = $list;
    }

    protected static function loadStyles()
    {
        $gantry = Gantry::instance();
        $db = \JFactory::getDbo();

        $query = $db
            ->getQuery(true)
            ->select('s.id, e.extension_id, s.template AS name,
s.title, s.params')
            ->from('#__template_styles AS s')
            ->where('s.client_id = 0')
            ->where('e.enabled = 1')
            ->where('e.state = 0')
            ->leftJoin('#__extensions AS e ON e.element=s.template
AND e.type='
                . $db->quote('template') . ' AND
e.client_id=s.client_id')
            ->order('s.id');

        $db->setQuery($query);

        $styles = (array) $db->loadObjectList();

        if (!is_array(static::$items)) {
            static::loadThemes();
        }

        /** @var array|ThemeDetails[] $list */
        $list = [];

        foreach ($styles as $style)
        {
            $details = isset(static::$items[$style->name]) ?
static::$items[$style->name] : null;
            if (!$details) {
                continue;
            }

            $params = new Registry($style->params);

            $details = clone $details;
            $details['id'] = $style->id;
            $details['extension_id'] = $style->extension_id;
            $details['style'] = $style->title;
            $details['preview_url'] =
$gantry['platform']->getThemePreviewUrl($style->id);
            $details['params'] = $params->toArray();

            $list[$style->name][$style->id] = $details;
        }

        static::$styles = $list;
    }
}
classes/Gantry/Component/Admin/HtmlController.php000064400000001745151166614510016135
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Admin;

use Gantry\Component\Controller\HtmlController as BaseController;

abstract class HtmlController extends BaseController
{
    /**
     * @param string|array $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = [])
    {
        return
$this->container['admin.theme']->render($file, $context);
    }

    /**
     * @param string $action
     * @param string $id
     * @return boolean
     */
    public function authorize($action, $id = null)
    {
        return
$this->container['platform']->authorize($action, $id);
    }
}
classes/Gantry/Component/Admin/JsonController.php000064400000001745151166614510016142
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Admin;

use Gantry\Component\Controller\JsonController as BaseController;

abstract class JsonController extends BaseController
{
    /**
     * @param string|array $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = [])
    {
        return
$this->container['admin.theme']->render($file, $context);
    }

    /**
     * @param string $action
     * @param string $id
     * @return boolean
     */
    public function authorize($action, $id = null)
    {
        return
$this->container['platform']->authorize($action, $id);
    }
}
classes/Gantry/Component/Assignments/AbstractAssignments.php000064400000016752151166614510020413
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Assignments;

use Gantry\Component\Config\CompiledConfig;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class AbstractAssignments
{
    /**
     * @var string
     */
    protected $configuration;

    /**
     * @var string
     */
    protected $className =
'\Gantry\%s\Assignments\Assignments%s';

    /**
     * @var string
     */
    protected $platform;

    /**
     * @var AssignmentFilter
     */
    protected $filter;

    /**
     * @var array
     */
    protected $candidates;

    /**
     * @var array
     */
    protected $page;

    /** @var callable */
    protected $specialFilterMethod;

    /**
     * @param string $configuration
     */
    public function __construct($configuration = null)
    {
        $this->configuration = $configuration;
    }

    /**
     * Get list of assignment items.
     */
    public function get()
    {
        return $this->getTypes();
    }

    /**
     * Set (save) assignments.
     *
     * @param array $data
     */
    public function set(array $data)
    {
        $this->save($data);
    }

    /**
     * Select assigned outline.
     *
     * @param string $default
     * @return string
     */
    public function select($default = 'default')
    {
        $scores = $this->scores();

        return key($scores) ?: $default;
    }

    /**
     * List matching outlines sorted by score.
     *
     * @param array $candidates
     * @return array
     */
    public function scores(array $candidates = null)
    {
        $this->init();
        $candidates = $candidates ?: $this->candidates;
        return $this->filter->scores($candidates, $this->page,
$this->specialFilterMethod);
    }

    /**
     * List matching outlines with matched assignments.
     *
     * @param array $candidates
     * @return array
     */
    public function matches(array $candidates = null)
    {
        $this->init();
        $candidates = $candidates ?: $this->candidates;
        return $this->filter->matches($candidates, $this->page,
$this->specialFilterMethod);
    }

    /**
     * Load all assignments.
     *
     * @return array
     */
    public function loadAssignments()
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Find all the assignment files.
        $paths = $locator->findResources("gantry-config://");
        $files = (new
ConfigFileFinder)->locateFileInFolder('assignments', $paths);

        // Make sure that base or system outlines aren't in the list.
        foreach ($files as $key => $array) {
            $key = (string)$key;
            if ($key === 'default' || strpos($key, '_')
=== 0) {
                unset($files[$key]);
            }
        }

        $cache =
$locator->findResource('gantry-cache://theme/compiled/config',
true, true);

        $config = new CompiledConfig($cache, [$files], GANTRY5_ROOT);

        return $config->load()->toArray();
    }

    /**
     * Get all assignments for the current page.
     *
     * @return array
     */
    public function getPage()
    {
        $list = [];

        foreach($this->types() as $class => $type) {
            $class = is_numeric($class) ? sprintf($this->className,
$this->platform, ucfirst($type)) : $class;

            if (!class_exists($class)) {
                throw new \RuntimeException("Assignment type {$type}
is missing");
            }

            /** @var AssignmentsInterface $instance */
            $instance = new $class;
            $list[$type] = $instance->getRules();
            unset($instance);
        }

        return $list;
    }

    /**
     * Filter assignments data.
     *
     * @param array $data
     * @param bool $minimize
     * @return array
     */
    public function filter(array $data, $minimize = false)
    {
        $types = [];
        foreach ($this->types() as $type) {
            $types[$type] = [];
        }

        $data = array_replace($types, $data);
        foreach ($data as $tname => &$type) {
            if (is_array($type)) {
                foreach ($type as $gname => &$group) {
                    if (is_array($group)) {
                        foreach ($group as $key => $value) {
                            if (!$value) {
                                unset($group[$key]);
                            } else {
                                $group[$key] = (bool) $value;
                            }
                        }
                        if (empty($group)) {
                            unset($type[$gname]);
                        }
                    } else {
                        $group = (bool) $group;
                    }
                }
                unset($group);
                if ($minimize && empty($type)) {
                    unset($data[$tname]);
                }
            } else {
                $type = (bool) $type;
            }
        }

        return $data;
    }

    /**
     * Save assignments for the configuration.
     *
     * @param array $data
     */
    public function save(array $data)
    {
        $data = $this->filter($data);

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Save layout into custom directory for the current theme.
        $save_dir =
$locator->findResource("gantry-config://{$this->configuration}",
true, true);
        $filename = "{$save_dir}/assignments.yaml";

        $file = YamlFile::instance($filename);
        $file->save($data);
        $file->free();
    }

    /**
     * Get list of all assignment types for assignments form.
     *
     * @return array
     */
    public function getTypes()
    {
        $list = [];

        foreach ($this->types() as $class => $type) {
            $class = is_numeric($class) ? sprintf($this->className,
$this->platform, ucfirst($type)) : $class;

            if (!class_exists($class)) {
                throw new \RuntimeException("Assignment type
'{$type}' is missing");
            }

            /** @var AssignmentsInterface $instance */
            $instance = new $class;
            $list[$type] =
$instance->listRules($this->configuration);
            unset($instance);
        }

        return $list;
    }

    /**
     * Get selected assignment option.
     *
     * @return string
     */
    public function getAssignment()
    {
        return 'default';
    }

    /**
     * Set extra options for assignments.
     *
     * @param $value
     */
    public function setAssignment($value)
    {
    }

    /**
     * Get extra options for assignments.
     *
     * @return array
     */
    public function assignmentOptions()
    {
        return [];
    }

    protected function init()
    {
        if (!$this->filter) {
            $this->filter = new AssignmentFilter;
            $this->candidates = $this->loadAssignments();
            $this->page = $this->getPage();
        }
    }

    /**
     * Return list of assignment types.
     *
     * @return array
     */
    abstract public function types();
}
classes/Gantry/Component/Assignments/AssignmentFilter.php000064400000011457151166614510017707
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Assignments;

/**
 * Class AssignmentFilter
 * @package Gantry\Assignments
 */
class AssignmentFilter
{
    protected $method;

    /**
     * Return all matching candidates with their score. Candidates are
ordered by their scores.
     *
     * @param array $candidates  In format of
candidates[name][section][rule].
     * @param array $page        In format of page[section][rule].
     * @return array
     */
    public function scores(array &$candidates, array &$page,
callable $function = null)
    {
        $matches = $this->matches($candidates, $page, $function);

        $scores = [];
        foreach ($matches as $type => $candidate) {
            $scores[$type] = $this->getScore($candidate) +
(isset($candidate['language']) ? 0.01 : 0);
        }

        // Always return matches by score in alphabetical order.
        ksort($scores, SORT_STRING);
        arsort($scores, SORT_NUMERIC);

        return $scores;
    }

    /**
     * Returns all matching candidates with matching rules.
     *
     * @param array $candidates  In format of
candidates[name][section][rule].
     * @param array $page        In format of page[section][rule].
     * @return array
     */
    public function matches(array $candidates, array &$page, callable
$function = null)
    {
        $matches = [];
        foreach ($candidates as $type => $candidate) {
            if (!is_array($candidate)) {
                if ($candidate === true && $page) {
                    $matches[$type] = $page;
                }
                continue;
            }
            foreach ($candidate as $section => $list) {
                if (!is_array($list)) {
                    if ($list === true && !empty($page[$section]))
{
                        $matches[$type][$section] = $page[$section];
                    }
                    continue;
                }
                foreach ($list as $name => $rules) {
                    if (!empty($page[$section][$name])) {
                        if (!is_array($rules)) {
                            $match = $rules === true ?
$page[$section][$name] : [];
                        } else {
                            $match =
\array_intersect_key($page[$section][$name], $rules);
                        }
                        if ($match) {
                            $matches[$type][$section][$name] = $match;
                        }
                    }
                }
            }
            if (isset($matches[$type]) && $function &&
call_user_func($function, $candidate, $matches[$type], $page) === false) {
                unset($matches[$type]);
            }
        }

        return $matches;
    }

    /**
     * Returns the calculated score for the assignment.
     *
     * @param array $matches
     * @param string $method
     * @return int
     */
    public function getScore(array &$matches, $method =
'max')
    {
        $this->method = 'calc' . ucfirst($method);

        if (!method_exists($this, $this->method)) {
            $this->method = 'calcMax';
        }

        return $this->calcArray(0, $matches);
    }

    /**
     * @param float $carry
     * @param float|array $item
     * @return float
     * @internal
     */
    protected function calcArray($carry, $item)
    {
        if (is_array($item)) {
            return array_reduce($item, [$this, 'calcArray'],
$carry);
        }

        $method = $this->method;
        return $this->{$method}($carry, (float) $item);
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcOr($carry, $item)
    {
        return (float) ($carry || $item);
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcMin($carry, $item)
    {
        return $carry ? min($carry, $item) : $item;
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcMax($carry, $item)
    {
        return max($carry, $item);
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcSum($carry, $item)
    {
        return $carry + $item;
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcMul($carry, $item)
    {
        return $carry ? $carry * $item : $item;
    }
}
classes/Gantry/Component/Assignments/AssignmentsInterface.php000064400000001356151166614510020542
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Assignments;

interface AssignmentsInterface
{
    /**
     * Returns list of rules which apply to the current page.
     *
     * @return array
     */
    public function getRules();

    /**
     * List all the rules available.
     *
     * @param string $configuration
     * @return array
     */
    public function listRules($configuration);
}
classes/Gantry/Component/Collection/Collection.php000064400000002471151166614510016320
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Collection;

use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;

class Collection implements CollectionInterface
{
    use ArrayAccess, Countable, Export;

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

    public static function __set_state($variables)
    {
        $instance = new static();
        $instance->items = $variables['items'];
        return $instance;
    }

    /**
     *
     * Create a copy of this collection.
     *
     * @return static
     */
    public function copy()
    {
        return clone $this;
    }

    /**
     * @param $item
     * @return $this
     */
    public function add($item)
    {
        $this->items[] = $item;

        return $this;
    }

    /**
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->items);
    }
}
classes/Gantry/Component/Collection/CollectionInterface.php000064400000002176151166614510020143
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Collection;

interface CollectionInterface extends \IteratorAggregate, \ArrayAccess,
\Countable
{
    public function toArray();

    /**
     * @param $item
     */
    public function add($item);

    /**
     * @return \ArrayIterator
     */
    public function getIterator();

    /**
     * @param $offset
     *
     * @return bool
     */
    public function offsetExists($offset);

    /**
     * @param $offset
     * @param $value
     */
    public function offsetSet($offset, $value);

    /**
     * @param $offset
     *
     * @return mixed
     */
    public function offsetGet($offset);

    /**
     * @param $offset
     */
    public function offsetUnset($offset);

    /**
     * @return int
     */
    public function count();
}
classes/Gantry/Component/Config/BlueprintForm.php000064400000022071151166614510016125
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\Blueprints\BlueprintForm as BaseBlueprintForm;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * The Config class contains configuration information.
 *
 * @author RocketTheme
 */
class BlueprintForm extends BaseBlueprintForm
{
    /**
     * @var string
     */
    protected $context = 'gantry-blueprints://';

    /**
     * @var BlueprintSchema
     */
    protected $schema;

    /**
     * @param string $filename
     * @param string $context
     * @return BlueprintForm
     */
    public static function instance($filename, $context = null)
    {
        /** @var BlueprintForm $instance */
        $instance = new static($filename);
        if ($context) {
            $instance->setContext($context);
        }

        return $instance->load()->init();
    }

    /**
     * Get nested structure containing default values defined in the
blueprints.
     *
     * Fields without default value are ignored in the list.
     *
     * @return array
     */
    public function getDefaults()
    {
        return $this->schema()->getDefaults();
    }

    /**
     * Merge two arrays by using blueprints.
     *
     * @param  array $data1
     * @param  array $data2
     * @param  string $name         Optional
     * @param  string $separator    Optional
     * @return array
     */
    public function mergeData(array $data1, array $data2, $name = null,
$separator = '.')
    {
        return $this->schema()->mergeData($data1, $data2, $name,
$separator);
    }

    /**
     * Return data fields that do not exist in blueprints.
     *
     * @param  array  $data
     * @param  string $prefix
     * @return array
     */
    public function extra(array $data, $prefix = '')
    {
        return $this->schema()->extra($data, $prefix);
    }

    /**
     * Validate data against blueprints.
     *
     * @param  array $data
     * @throws \RuntimeException
     */
    public function validate(array $data)
    {
        $this->schema()->validate($data);
    }

    /**
     * Filter data by using blueprints.
     *
     * @param  array $data
     * @return array
     */
    public function filter(array $data)
    {
        return $this->schema()->filter($data);
    }

    /**
     * @return BlueprintSchema
     */
    public function schema()
    {
        if (!isset($this->schema)) {
            $this->schema = new BlueprintSchema;
            $this->schema->embed('', $this->items);
            $this->schema->init();
        }

        return $this->schema;
    }

    /**
     * @param string $filename
     * @return string
     */
    protected function loadFile($filename)
    {
        $file = CompiledYamlFile::instance($filename);
        $content = $file->content();
        $file->free();

        return $content;
    }

    /**
     * @param string|array $path
     * @param string $context
     * @return array
     */
    protected function getFiles($path, $context = null)
    {
        if (is_string($path) && !strpos($path, '://')) {
            // Resolve filename.
            if (isset($this->overrides[$path])) {
                $path = $this->overrides[$path];
            } else {
                if ($context === null) {
                    $context = $this->context;
                }
                if ($context && $context[strlen($context)-1] !==
'/') {
                    $context .= '/';
                }
                $path = $context . $path;

                if (!preg_match('/\.yaml$/', $path)) {
                    $path .= '.yaml';
                }
            }
        }

        if (is_string($path) && strpos($path, '://')) {
            /** @var UniformResourceLocator $locator */
            $locator = Gantry::instance()['locator'];

            $files = $locator->findResources($path);
        } else {
            $files = (array) $path;
        }

        return $files;
    }

    /**
     * @param array $field
     * @param string $property
     * @param array $call
     */
    protected function dynamicData(array &$field, $property, array
&$call)
    {
        $params = $call['params'];

        if (is_array($params)) {
            $function = array_shift($params);
        } else {
            $function = $params;
            $params = [];
        }

        list($o, $f) = preg_split('/::/', $function, 2);
        if (!$f) {
            if (function_exists($o)) {
                $data = call_user_func_array($o, $params);
            }
        } else {
            if (method_exists($o, $f)) {
                $data = call_user_func_array(array($o, $f), $params);
            }
        }

        // If function returns a value,
        if (isset($data)) {
            if (isset($field[$property]) &&
is_array($field[$property]) && is_array($data)) {
                // Combine field and @data-field together.
                $field[$property] += $data;
            } else {
                // Or create/replace field with @data-field.
                $field[$property] = $data;
            }
        }
    }

    /**
     * @param array $field
     * @param string $property
     * @param array $call
     */
    protected function dynamicConfig(array &$field, $property, array
&$call)
    {
        $value = $call['params'];

        $default = isset($field[$property]) ? $field[$property] : null;
        $config = Gantry::instance()['config']->get($value,
$default);

        if (!is_null($config)) {
            $field[$property] = $config;
        }
    }

    /**
     * Get blueprints by using slash notation for nested arrays/objects.
     *
     * @param array  $path
     * @param string  $separator
     * @return array
     */
    public function resolve(array $path, $separator = '/')
    {
        $fields = false;
        $parts = [];
        $current = $this['form/fields'];
        $result = [null, null, null];
        $prefix = '';

        while (($field = current($path)) !== false) {
            if (!$fields && isset($current['fields'])) {
                if (!empty($current['array'])) {
                    $result = [$current, $parts, $path ?
implode($separator, $path) : null];
                    // Skip item offset.
                    $parts[] = array_shift($path);
                }

                $current = $current['fields'];
                $prefix = '';
                $fields = true;

            } elseif (isset($current[$prefix . $field])) {
                $parts[] = array_shift($path);
                $current = $current[$prefix . $field];
                $prefix = '';
                $fields = false;

            } elseif (isset($current['.' . $prefix . $field])) {
                $parts[] = array_shift($path);
                $current = $current['.' . $prefix . $field];
                $prefix = '';
                $fields = false;

            } elseif ($field && $this->fieldExists($prefix .
$field, $current)) {
                $parts[] = array_shift($path);
                $prefix = "{$prefix}{$field}.";
                $fields = false;

            } else {
                // Check if there's a container with the field.
                $current = $this->resolveContainer($current, $prefix,
$field);

                // No containers with the field found.
                if (!$current) {
                    break;
                }

                $prefix = '';
                $fields = false;
            }
        }

        if (!$fields && !empty($current['array'])) {
            $result = [$current, $parts, $path ? implode($separator, $path)
: null];
        }

        return $result;
    }

    protected function resolveContainer($current, $prefix, $fieldName)
    {
        foreach ($current as $field) {
            $type = isset($field['type']) ?
$field['type'] : 'container._implicit';
            $container = (0 === strpos($type, 'container.'));

            if (!$container || !isset($field['fields'])) {
                continue;
            }

            $current_fields = $field['fields'];
            if (isset($current_fields[$prefix . $fieldName]) ||
isset($current_fields['.' . $prefix . $fieldName])
                || $this->fieldExists($prefix . $fieldName,
$current_fields)) {
                return $current_fields;
            }

            $current_fields = $this->resolveContainer($current_fields,
$prefix, $fieldName);
            if ($current_fields !== null) {
                return $current_fields;
            }
        }

        return null;
    }

    protected function fieldExists($prefix, $list)
    {
        foreach ($list as $field => $data) {
            $pos = strpos($field, $prefix);
            if ($pos === 0 || ($pos === 1 && $field[0] ===
'.')) {
                return true;
            }
        }

        return false;
    }
}
classes/Gantry/Component/Config/BlueprintSchema.php000064400000015361151166614510016426
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\Blueprints\BlueprintSchema as BlueprintSchemaBase;

/**
 * Blueprint schema handles the internal logic of blueprints.
 *
 * @author RocketTheme
 * @license MIT
 */
class BlueprintSchema extends BlueprintSchemaBase
{
    protected $configuration;

    protected $ignoreFormKeys = [
        'title' => true,
        'help' => true,
        'placeholder' => true,
        'fields' => true
    ];

    protected $types = [
        'container.set' => [
            'input@' => false
        ],
        'container.tabs' => [
            'input@' => false
        ],
        'separator.note' => [
            'input@' => false
        ],
        'separator.separator' => [
            'input@' => false
        ],
        'key' => [
            'input@' => false
        ],
        'collection.list' => [
            'array' => true
        ]
    ];

    /**
     * Constructor.
     *
     * @param array $serialized  Serialized content if available.
     */
    public function __construct($serialized = null)
    {
        parent::__construct($serialized);

        $this->configuration = new
Config(isset($serialized['configuration']) ?
$serialized['configuration'] : []);
    }

    /**
     * Convert object into an array.
     *
     * @return array
     */
    public function getState()
    {
        return parent::getState() + ['configuration' =>
$this->configuration->toArray()];
    }

    /**
     * Get nested structure containing default values defined in the
blueprints.
     *
     * Fields without default value are ignored in the list.
     *
     * @return array
     */
    public function getDefaults()
    {
        return array_merge_recursive($this->configuration->toArray(),
$this->buildDefaults($this->nested));
    }

    /**
     * Embed an array to the blueprint.
     *
     * @param $name
     * @param array $value
     * @param string $separator
     * @param bool $merge   Merge fields instead replacing them.
     * @return $this
     */
    public function embed($name, array $value, $separator = '.',
$merge = false)
    {
        parent::embed($name, $value, $separator, $merge);

        if (isset($value['configuration'])) {
            $this->configuration->set($name,
$value['configuration'], $separator);
        }

        return $this;
    }

    /**
     * Validate data against blueprints.
     *
     * @param  array $data
     * @throws \RuntimeException
     */
    public function validate(array $data)
    {
        try {
            $messages = $this->validateArray($data, $this->nested);

        } catch (\RuntimeException $e) {
            throw (new ValidationException($e->getMessage(),
$e->getCode(), $e))->setMessages();
        }

        if (!empty($messages)) {
            throw (new ValidationException())->setMessages($messages);
        }
    }

    /**
     * Filter data by using blueprints.
     *
     * @param  array $data
     * @return array
     */
    public function filter(array $data)
    {
        return $this->filterArray($data, $this->nested);
    }

    /**
     * @param array $data
     * @param array $rules
     * @returns array
     * @throws \RuntimeException
     * @internal
     */
    protected function validateArray(array $data, array $rules)
    {
        $messages = $this->checkRequired($data, $rules);

        foreach ($data as $key => $field) {
            $val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
            $rule = is_string($val) ? $this->items[$val] : null;

            if ($rule) {
                // Item has been defined in blueprints.
                $messages += Validation::validate($field, $rule);
            } elseif (is_array($field) && is_array($val)) {
                // Array has been defined in blueprints.
                $messages += $this->validateArray($field, $val);
            } elseif (isset($rules['validation']) &&
$rules['validation'] == 'strict') {
                // Undefined/extra item.
                throw new \RuntimeException(sprintf('%s is not defined
in blueprints', $key));
            }
        }

        return $messages;
    }

    /**
     * @param array $data
     * @param array $rules
     * @return array
     * @internal
     */
    protected function filterArray(array $data, array $rules)
    {
        $results = array();
        foreach ($data as $key => $field) {
            $val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
            $rule = is_string($val) ? $this->items[$val] : null;

            if ($rule) {
                // Item has been defined in blueprints.
                $field = Validation::filter($field, $rule);
            } elseif (is_array($field) && is_array($val)) {
                // Array has been defined in blueprints.
                $field = $this->filterArray($field, $val);
            } elseif (isset($rules['validation']) &&
$rules['validation'] == 'strict') {
                $field = null;
            }

            if (isset($field) && (!is_array($field) ||
!empty($field))) {
                $results[$key] = $field;
            }
        }

        return $results;
    }

    /**
     * @param array $data
     * @param array $fields
     * @return array
     */
    protected function checkRequired(array $data, array $fields)
    {
        $messages = [];

        foreach ($fields as $name => $field) {
            if (!is_string($field)) {
                continue;
            }
            $field = $this->items[$field];
            if (isset($field['validate']['required'])
                &&
$field['validate']['required'] === true
                && !isset($data[$name])) {
                $value = isset($field['label']) ?
$field['label'] : $field['name'];
                // TODO: translate
                $message  = sprintf("Please fill up required field
'%s'.", $value);
                $messages[$field['name']][] = $message;
            }
        }

        return $messages;
    }

    /**
     * @param array $field
     * @param string $property
     * @param array $call
     */
    protected function dynamicConfig(array &$field, $property, array
&$call)
    {
        $value = $call['params'];

        $default = isset($field[$property]) ? $field[$property] : null;
        $config = Gantry::instance()['config']->get($value,
$default);

        if (!is_null($config)) {
            $field[$property] = $config;
        }
    }
}
classes/Gantry/Component/Config/CompiledBase.php000064400000014215151166614510015665
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\File\PhpFile;

/**
 * The Compiled base class.
 */
abstract class CompiledBase
{
    /**
     * @var int Version number for the compiled file.
     */
    public $version = 1;

    /**
     * @var string  Filename (base name) of the compiled configuration.
     */
    public $name;

    /**
     * @var string|bool  Configuration checksum.
     */
    public $checksum;

    /**
     * @var string Cache folder to be used.
     */
    protected $cacheFolder;

    /**
     * @var array  List of files to load.
     */
    protected $files;

    /**
     * @var string
     */
    protected $path;

    /**
     * @var mixed  Configuration object.
     */
    protected $object;

    /**
     * @param  string $cacheFolder  Cache folder to be used.
     * @param  array  $files  List of files as returned from
ConfigFileFinder class.
     * @param string $path  Base path for the file list.
     * @throws \BadMethodCallException
     */
    public function __construct($cacheFolder, array $files, $path =
GANTRY5_ROOT)
    {
        if (!$cacheFolder) {
            throw new \BadMethodCallException('Cache folder not
defined.');
        }

        $this->cacheFolder = $cacheFolder;
        $this->files = $files;
        $this->path = $path ? rtrim($path, '\\/') .
'/' : '';
    }

    /**
     * Get filename for the compiled PHP file.
     *
     * @param string $name
     * @return $this
     */
    public function name($name = null)
    {
        if (!$this->name) {
            $this->name = $name ?:
md5(json_encode(array_keys($this->files)));
        }

        return $this;
    }

    /**
     * Function gets called when cached configuration is saved.
     */
    public function modified() {}

    /**
     * Load the configuration.
     *
     * @return mixed
     */
    public function load()
    {
        if ($this->object) {
            return $this->object;
        }

        $filename = $this->createFilename();
        if (!$this->loadCompiledFile($filename) &&
$this->loadFiles()) {
            $this->saveCompiledFile($filename);
        }

        return $this->object;
    }

    /**
     * Returns checksum from the configuration files.
     *
     * You can set $this->checksum = false to disable this check.
     *
     * @return bool|string
     */
    public function checksum()
    {
        if (!isset($this->checksum)) {
            $this->checksum = md5(json_encode($this->files) .
$this->version);
        }

        return $this->checksum;
    }

    protected function createFilename()
    {
        return
"{$this->cacheFolder}/{$this->name()->name}.php";
    }

    /**
     * Create configuration object.
     *
     * @param  array  $data
     */
    abstract protected function createObject(array $data = []);

    /**
     * Finalize configuration object.
     */
    abstract protected function finalizeObject();

    /**
     * Load single configuration file and append it to the correct
position.
     *
     * @param  string  $name  Name of the position.
     * @param  string  $filename  File to be loaded.
     */
    abstract protected function loadFile($name, $filename);

    /**
     * Load and join all configuration files.
     *
     * @return bool
     * @internal
     */
    protected function loadFiles()
    {
        $this->createObject();

        $list = array_reverse($this->files);
        foreach ($list as $files) {
            foreach ($files as $name => $item) {
                $this->loadFile($name, $this->path .
$item['file']);
            }
        }

        $this->finalizeObject();

        return true;
    }

    /**
     * Load compiled file.
     *
     * @param  string  $filename
     * @return bool
     * @internal
     */
    protected function loadCompiledFile($filename)
    {
        $gantry = Gantry::instance();

        /** @var Config $global */
        $global = $gantry['global'];

        if (!$global->get('compile_yaml', 1)) {
            return false;
        }

        if (!file_exists($filename)) {
            return false;
        }

        $cache = include $filename;
        if (
            !is_array($cache)
            || !isset($cache['checksum'])
            || !isset($cache['data'])
            || !isset($cache['@class'])
            || $cache['@class'] != get_class($this)
        ) {
            return false;
        }

        // Load real file if cache isn't up to date (or is invalid).
        if ($cache['checksum'] !== $this->checksum()) {
            return false;
        }

        $this->createObject($cache['data']);

        $this->finalizeObject();

        return true;
    }

    /**
     * Save compiled file.
     *
     * @param  string  $filename
     * @throws \RuntimeException
     * @internal
     */
    protected function saveCompiledFile($filename)
    {
        $gantry = Gantry::instance();

        /** @var Config $global */
        $global = $gantry['global'];

        if (!$global->get('compile_yaml', 1)) {
            return;
        }

        $file = PhpFile::instance($filename);

        // Attempt to lock the file for writing.
        try {
            $file->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }

        if ($file->locked() === false) {
            // File was already locked by another process.
            return;
        }

        $cache = [
            '@class' => get_class($this),
            'timestamp' => time(),
            'checksum' => $this->checksum(),
            'files' => $this->files,
            'data' => $this->getState()
        ];

        $file->save($cache);
        $file->unlock();
        $file->free();

        $this->modified();
    }

    protected function getState()
    {
        return $this->object->toArray();
    }
}
classes/Gantry/Component/Config/CompiledBlueprints.php000064400000004104151166614510017136
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

/**
 * The Compiled Blueprints class.
 */
class CompiledBlueprints extends CompiledBase
{
    /**
     * @var int Version number for the compiled file.
     */
    public $version = 3;

    /**
     * @var BlueprintSchema  Blueprints object.
     */
    protected $object;

    /**
     * Create configuration object.
     *
     * @param array  $data
     */
    protected function createObject(array $data = [])
    {
        $this->object = new BlueprintSchema($data);
    }

    /**
     * Finalize configuration object.
     */
    protected function finalizeObject()
    {
    }

    /**
     * Load single configuration file and append it to the correct
position.
     *
     * @param  string  $name  Name of the position.
     * @param  array   $files  Files to be loaded.
     */
    protected function loadFile($name, $files)
    {
        // Load blueprint file.
        $blueprint = new BlueprintForm($files);

        $this->object->embed($name,
$blueprint->load()->toArray(), '/', true);
    }

    /**
     * Load and join all configuration files.
     *
     * @return bool
     * @internal
     */
    protected function loadFiles()
    {
        $this->createObject();

        // Convert file list into parent list.
        $list = [];
        foreach ($this->files as $files) {
            foreach ($files as $name => $item) {
                $list[$name][] = $this->path . $item['file'];
            }
        }

        // Load files.
        foreach ($list as $name => $files) {
            $this->loadFile($name, $files);
        }

        $this->finalizeObject();

        return true;
    }

    protected function getState()
    {
        return $this->object->getState();
    }
}
classes/Gantry/Component/Config/CompiledConfig.php000064400000005024151166614510016216
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Component\File\CompiledYamlFile;

/**
 * The Compiled Configuration class.
 */
class CompiledConfig extends CompiledBase
{
    /**
     * @var int Version number for the compiled file.
     */
    public $version = 2;

    /**
     * @var Config  Configuration object.
     */
    protected $object;

    /**
     * @var callable  Blueprints loader.
     */
    protected $callable;

    /**
     * @var bool
     */
    protected $withDefaults;

    /**
     * Get filename for the compiled PHP file.
     *
     * @param string $name
     * @return $this
     */
    public function name($name = null)
    {
        if (!$this->name) {
            $this->name = $name ?:
md5(json_encode(array_keys($this->files)) . (int)
$this->withDefaults);
        }

        return $this;
    }

    /**
     * Set blueprints for the configuration.
     *
     * @param callable $blueprints
     * @return $this
     */
    public function setBlueprints(callable $blueprints)
    {
        $this->callable = $blueprints;

        return $this;
    }

    /**
     * @param bool $withDefaults
     * @return mixed
     */
    public function load($withDefaults = false)
    {
        $this->withDefaults = $withDefaults;

        return parent::load();
    }

    /**
     * Create configuration object.
     *
     * @param  array  $data
     */
    protected function createObject(array $data = [])
    {
        if ($this->withDefaults && empty($data) &&
is_callable($this->callable)) {
            $blueprints = $this->callable;
            $data = $blueprints()->getDefaults();
        }

        $this->object = new Config($data, $this->callable);
    }

    /**
     * Finalize configuration object.
     */
    protected function finalizeObject()
    {
    }

    /**
     * Load single configuration file and append it to the correct
position.
     *
     * @param  string  $name  Name of the position.
     * @param  string  $filename  File to be loaded.
     */
    protected function loadFile($name, $filename)
    {
        $file = CompiledYamlFile::instance($filename);
        $this->object->join($name, $file->content(),
'/');
        $file->free();
    }
}
classes/Gantry/Component/Config/Config.php000064400000016544151166614510014552
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;

/**
 * The Config class contains configuration information.
 *
 * @author RocketTheme
 */
class Config implements \ArrayAccess, \Countable, \Iterator,
ExportInterface
{
    use NestedArrayAccessWithGetters, Iterator, Export;

    /**
     * @var array
     */
    protected $items;

    /**
     * @var BlueprintSchema|BlueprintForm|callable
     */
    protected $blueprint;

    /**
     * Constructor to initialize array.
     *
     * @param  array  $items  Initial items inside the iterator.
     * @param  callable $blueprints  Function to load Blueprints for the
configuration.
     */
    public function __construct(array $items, callable $blueprint = null)
    {
        $this->items = $items;
        $this->blueprint = $blueprint;
    }

    /**
     * Join nested values together by using blueprints.
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $value      Value to be joined.
     * @param string  $separator  Separator, defaults to '.'
     * @return $this
     * @throws \RuntimeException
     */
    public function join($name, $value, $separator = '.')
    {
        $old = $this->get($name, null, $separator);
        if ($old !== null) {
            if (!is_array($old)) {
                throw new \RuntimeException("Value is not array in
{$name}: " . print_r($old, true));
            }
            if (is_object($value)) {
                $value = (array) $value;
            } elseif (!is_array($value)) {
                throw new \RuntimeException("Value is not array in
{$name}: " . print_r($value, true));
            }
            $value = $this->blueprint()->mergeData($old, $value,
$name, $separator);
        }

        $this->set($name, $value, $separator);

        return $this;
    }

    /**
     * Get nested structure containing default values defined in the
blueprints.
     *
     * Fields without default value are ignored in the list.

     * @return array
     */
    public function getDefaults()
    {
        return $this->blueprint()->getDefaults();
    }

    /**
     * Set default values by using blueprints.
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $value      Value to be joined.
     * @param string  $separator  Separator, defaults to '.'
     * @return $this
     */
    public function joinDefaults($name, $value, $separator = '.')
    {
        if (is_object($value)) {
            $value = (array) $value;
        }
        $old = $this->get($name, null, $separator);
        if ($old !== null) {
            $value = $this->blueprint()->mergeData($value, $old,
$name, $separator);
        }

        $this->set($name, $value, $separator);

        return $this;
    }

    /**
     * Get value from the configuration and join it with given data.
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param array   $value      Value to be joined.
     * @param string  $separator  Separator, defaults to '.'
     * @return array
     * @throws \RuntimeException
     */
    public function getJoined($name, $value, $separator = '.')
    {
        if (is_object($value)) {
            $value = (array) $value;
        } elseif (!is_array($value)) {
            throw new \RuntimeException("Value is not array in
'{$name}': " . print_r($value, true));
        }

        $old = $this->get($name, null, $separator);

        if ($old === null) {
            // No value set; no need to join data.
            return $value;
        }

        if (!is_array($old)) {
            throw new \RuntimeException("Value is not array in
'{$name}': " . print_r($value, true));
        }

        // Return joined data.
        return $this->blueprint()->mergeData($old, $value, $name,
$separator);
    }

    /**
     * Merge two configurations together.
     *
     * @param array $data
     * @return $this
     */
    public function merge(array $data)
    {
        $this->items =
$this->blueprint()->mergeData($this->items, $data);

        return $this;
    }

    /**
     * Set default values to the configuration if variables were not set.
     *
     * @param array $data
     * @return $this
     */
    public function setDefaults(array $data)
    {
        $this->items = $this->blueprint()->mergeData($data,
$this->items);

        return $this;
    }

    /**
     * Make a flat list from the configuration.
     *
     * @param string $name      Dot separated path to the requested value.
     * @param string $separator Separator, defaults to '.'
     * @param string $prefix    Name prefix.
     * @return array
     */
    public function flatten($name = null, $separator = '.',
$prefix = '')
    {
        $element = $name ? $this->offsetGet($name) : $this->items;

        if (!is_array($element)) {
            return [$name, $element];
        }

        if (strlen($separator) == 2 && in_array($separator,
['][', ')(', '}{'])) {
            $separator = [$separator[1], $separator[0]];
        }

        return $this->flattenNested('', $element, $separator,
$prefix);
    }

    /**
     * @param string $name
     * @param array  $element
     * @param string $separator
     * @param string $prefix
     * @return array
     * @internal
     */
    protected function flattenNested($name, &$element, $separator,
$prefix)
    {
        $list = [];
        foreach ($element as $key => $value) {
            $new = $name ? $name : $prefix;
            if (is_array($separator)) {
                $new .= $separator[0] . $key . $separator[1];
            } else {
                $new .= ($new ? $separator : '') . $key;
            }
            if (!is_array($value) || empty($value)) {
                $list[$new] = $value;
            } else {
                $list += $this->flattenNested($new, $value, $separator,
$prefix);
            }
        }

        return $list;
    }

    /**
     * Return blueprint.
     *
     * @return BlueprintSchema|BlueprintForm
     * @since 5.4.7
     */
    public function blueprint()
    {
        if (!$this->blueprint){
            $this->blueprint = new BlueprintSchema;
        } elseif (is_callable($this->blueprint)) {
            // Lazy load blueprints.
            $blueprint = $this->blueprint;
            $this->blueprint = $blueprint();
        }
        return $this->blueprint;
    }

    /**
     * Return blueprints.
     *
     * @return BlueprintSchema
     * @deprecated 5.4.7
     */
    public function blueprints()
    {
        return $this->blueprint();
    }

    /**
     * Count items in nested array.
     *
     * @param string $path
     * @param string $separator
     * @return int
     */
    public function count($path = null, $separator = '.')
    {
        $items = $path ? $this->get($path, null, $separator) :
$this->items;

        return is_array($items) ? count($items) : 0;
    }
}
classes/Gantry/Component/Config/ConfigFileFinder.php000064400000020534151166614510016474
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Component\Filesystem\Folder;

/**
 * The Configuration & Blueprints Finder class.
 */
class ConfigFileFinder
{
    protected $base = '';

    /**
     * @param string $base
     * @return $this
     */
    public function setBase($base)
    {
        $this->base = $base ? "{$base}/" : '';

        return $this;
    }

    /**
     * Return all locations for all the files with a timestamp.
     *
     * @param  array  $paths    List of folders to look from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     */
    public function locateFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
    {
        $list = [];
        foreach ($paths as $folder) {
            $list += $this->detectRecursive($folder, $pattern, $levels);
        }
        return $list;
    }

    /**
     * Return all locations for all the files with a timestamp.
     *
     * @param  array  $paths    List of folders to look from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     */
    public function getFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
    {
        $list = [];
        foreach ($paths as $folder) {
            $path = trim(Folder::getRelativePath($folder), '/');

            $files = $this->detectRecursive($folder, $pattern, $levels);

            $list += $files[trim($path, '/')];
        }
        return $list;
    }

    /**
     * Return all paths for all the files with a timestamp.
     *
     * @param  array  $paths    List of folders to look from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     */
    public function listFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
    {
        $list = [];
        foreach ($paths as $folder) {
            $list = array_merge_recursive($list,
$this->detectAll($folder, $pattern, $levels));
        }
        return $list;
    }

    /**
     * Find filename from a list of folders.
     *
     * Note: Only finds the last override.
     *
     * @param string $filename
     * @param array $folders
     * @return array
     */
    public function locateFileInFolder($filename, array $folders)
    {
        $list = [];
        foreach ($folders as $folder) {
            $list += $this->detectInFolder($folder, $filename);
        }
        return $list;
    }

    /**
     * Find filename from a list of folders.
     *
     * @param array $folders
     * @param string $filename
     * @return array
     */
    public function locateInFolders(array $folders, $filename = null)
    {
        $list = [];
        foreach ($folders as $folder) {
            $path = trim(Folder::getRelativePath($folder), '/');
            $list[$path] = $this->detectInFolder($folder, $filename);
        }
        return $list;
    }

    /**
     * Return all existing locations for a single file with a timestamp.
     *
     * @param  array  $paths   Filesystem paths to look up from.
     * @param  string $name    Configuration file to be located.
     * @param  string $ext     File extension (optional, defaults to
.yaml).
     * @return array
     */
    public function locateFile(array $paths, $name, $ext =
'.yaml')
    {
        $filename = preg_replace('|[.\/]+|', '/',
$name) . $ext;

        $list = [];
        foreach ($paths as $folder) {
            $path = trim(Folder::getRelativePath($folder), '/');

            if (is_file("{$folder}/{$filename}")) {
                $modified = filemtime("{$folder}/{$filename}");
            } else {
                $modified = 0;
            }
            $basename = $this->base . $name;
            $list[$path] = [$basename => ['file' =>
"{$path}/{$filename}", 'modified' => $modified]];
        }

        return $list;
    }

    /**
     * Detects all directories with a configuration file and returns them
with last modification time.
     *
     * @param  string $folder   Location to look up from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     * @internal
     */
    protected function detectRecursive($folder, $pattern, $levels)
    {
        $path = trim(Folder::getRelativePath($folder), '/');

        if (is_dir($folder)) {
            // Find all system and user configuration files.
            $options = [
                'levels'  => $levels,
                'compare' => 'Filename',
                'pattern' => $pattern,
                'filters' => [
                    'pre-key' => $this->base,
                    'key' => $pattern,
                    'value' => function
(\RecursiveDirectoryIterator $file) use ($path) {
                        return ['file' =>
"{$path}/{$file->getSubPathname()}", 'modified'
=> $file->getMTime()];
                    }
                ],
                'key' => 'SubPathname'
            ];

            $list = Folder::all($folder, $options);

            ksort($list);
        } else {
            $list = [];
        }

        return [$path => $list];
    }

    /**
     * Detects all directories with the lookup file and returns them with
last modification time.
     *
     * @param  string $folder Location to look up from.
     * @param  string $lookup Filename to be located (defaults to directory
name).
     * @return array
     * @internal
     */
    protected function detectInFolder($folder, $lookup = null)
    {
        $folder = rtrim($folder, '/');
        $path = trim(Folder::getRelativePath($folder), '/');
        $base = $path === $folder ? '' : ($path ? substr($folder,
0, -strlen($path)) : $folder . '/');

        $list = [];

        if (is_dir($folder)) {
            $iterator = new \DirectoryIterator($folder);

            /** @var \DirectoryIterator $directory */
            foreach ($iterator as $directory) {
                if (!$directory->isDir() || $directory->isDot()) {
                    continue;
                }

                $name = $directory->getBasename();
                $find = ($lookup ?: $name) . '.yaml';
                $filename = "{$path}/{$name}/{$find}";

                if (file_exists($base . $filename)) {
                    $basename = $this->base . $name;
                    $list[$basename] = ['file' => $filename,
'modified' => filemtime($base . $filename)];
                }
            }
        }

        return $list;
    }

    /**
     * Detects all plugins with a configuration file and returns them with
last modification time.
     *
     * @param  string $folder   Location to look up from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     * @internal
     */
    protected function detectAll($folder, $pattern, $levels)
    {
        $path = trim(Folder::getRelativePath($folder), '/');

        if (is_dir($folder)) {
            // Find all system and user configuration files.
            $options = [
                'levels'  => $levels,
                'compare' => 'Filename',
                'pattern' => $pattern,
                'filters' => [
                    'pre-key' => $this->base,
                    'key' => $pattern,
                    'value' => function
(\RecursiveDirectoryIterator $file) use ($path) {
                        return
["{$path}/{$file->getSubPathname()}" =>
$file->getMTime()];
                    }
                ],
                'key' => 'SubPathname'
            ];

            $list = Folder::all($folder, $options);

            ksort($list);
        } else {
            $list = [];
        }

        return $list;
    }
}
classes/Gantry/Component/Config/Validation.php000064400000053013151166614510015427
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;

/**
 * Data validation.
 *
 * @author RocketTheme
 * @license MIT
 */
class Validation
{
    /**
     * Validate value against a blueprint field definition.
     *
     * @param $value
     * @param array $field
     * @return array
     */
    public static function validate($value, array $field)
    {
        $messages = [];

        $validate = isset($field['validate']) ? (array)
$field['validate'] : [];

        // If value isn't required, we will stop validation if empty
value is given.
        if (empty($validate['required']) && ($value ===
null || $value === '')) {
            return $messages;
        }

        if (!isset($field['type'])) {
            $field['type'] = 'input.text';
        }

        // Special case for files, value is never empty and errors with
code 4 instead.
        if (empty($validate['required']) &&
$field['type'] === 'input.file' &&
isset($value['error'])
                && ($value['error'] ===
UPLOAD_ERR_NO_FILE || \in_array(UPLOAD_ERR_NO_FILE,
$value['error'], true))) {
            return $messages;
        }

        // Validate type with fallback type text.
        $type = (string)
isset($field['validate']['type']) ?
$field['validate']['type'] : $field['type'];
        $method = 'type_'.strtr($type, '-.',
'__');

        if (!method_exists(__CLASS__, $method)) {
            $method = 'type_Input_Text';
        }

        $name = ucfirst(isset($field['label']) ?
$field['label'] : $field['name']);
        // TODO: translate
        $message = (string)
isset($field['validate']['message'])
            ? sprintf($field['validate']['message'])
            : sprintf('Invalid input in field: ') . '
"' . $name . '"';

        $success = self::$method($value, $validate, $field);

        if (!$success) {
            $messages[$field['name']][] = $message;
        }

        // Check individual rules.
        foreach ($validate as $rule => $params) {
            $method = 'validate_' . ucfirst(strtr($rule,
'-.', '__'));

            if (method_exists(__CLASS__, $method)) {
                $success = self::$method($value, $params);

                if (!$success) {
                    $messages[$field['name']][] = $message;
                }
            }
        }

        return $messages;
    }

    /**
     * Filter value against a blueprint field definition.
     *
     * @param  mixed  $value
     * @param  array  $field
     * @return mixed  Filtered value.
     */
    public static function filter($value, array $field)
    {
        $validate = isset($field['validate']) ? (array)
$field['validate'] : [];

        // If value isn't required, we will return null if empty value
is given.
        if (($value === null || $value === '') &&
empty($validate['required'])) {
            return null;
        }

        if (!isset($field['type'])) {
            $field['type'] = 'input.text';
        }

        // Special case for files, value is never empty and errors with
code 4 instead.
        if (empty($validate['required']) &&
$field['type'] === 'input.file' &&
isset($value['error'])
            && ($value['error'] === UPLOAD_ERR_NO_FILE ||
\in_array(UPLOAD_ERR_NO_FILE, $value['error'], true))) {
            return null;
        }

        // Validate type with fallback type text.
        $type = (string)
isset($field['validate']['type']) ?
$field['validate']['type'] : $field['type'];
        $method = 'filter_' . ucfirst(str_replace('-',
'_', $type));

        if (!method_exists(__CLASS__, $method)) {
            $method = 'filter_Input_Text';
        }

        return self::$method($value, $validate, $field);
    }

    /**
     * HTML5 input: text
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Text($value, array $params, array
$field)
    {
        if (!\is_string($value) && !is_numeric($value)) {
            return false;
        }

        $value = (string)$value;

        if (isset($params['min']) && \strlen($value) <
$params['min']) {
            return false;
        }

        if (isset($params['max']) && \strlen($value) >
$params['max']) {
            return false;
        }

        $min = isset($params['min']) ? $params['min'] :
0;
        if (isset($params['step']) && (strlen($value) -
$min) % $params['step'] === 0) {
            return false;
        }

        if ((!isset($params['multiline']) ||
!$params['multiline']) && preg_match('/\R/um',
$value)) {
            return false;
        }

        return true;
    }

    protected static function filter_Input_Text($value, array $params,
array $field)
    {
        return (string) $value;
    }

    protected static function filter_Input_CommaList($value, array $params,
array $field)
    {
        return \is_array($value) ? $value :
preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
    }

    protected static function type_Input_CommaList($value, array $params,
array $field)
    {
        return \is_array($value) ? true : self::type_Input_Text($value,
$params, $field);
    }

    /**
     * HTML5 input: textarea
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Textarea_Textarea($value, array $params,
array $field)
    {
        if (!isset($params['multiline'])) {
            $params['multiline'] = true;
        }

        return self::type_Input_Text($value, $params, $field);
    }

    /**
     * HTML5 input: password
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Password($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field);
    }

    /**
     * HTML5 input: hidden
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Hidden($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field);
    }

    /**
     * Custom input: checkbox list
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Checkboxes_Checkboxes($value, array
$params, array $field)
    {
        // Set multiple: true so checkboxes can easily use min/max counts
to control number of options required
        $field['multiple'] = true;

        return self::typeArray((array) $value, $params, $field);
    }

    protected static function filter_Checkboxes_Checkboxes($value, array
$params, array $field)
    {
        return self::filterArray($value, $params, $field);
    }

    /**
     * HTML5 input: checkbox
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Checkbox($value, array $params, array
$field)
    {
        $value = (string) $value;

        if (!isset($field['value'])) {
            $field['value'] = 1;
        }

        if ($value && $value != $field['value']) {
            return false;
        }

        return true;
    }

    /**
     * HTML5 input: radio
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Radio($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    /**
     * Custom input: toggle
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Toggle_Toggle($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    /**
     * Custom input: file
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_File($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    protected static function filter_Input_File($value, array $params,
array $field)
    {
        if (isset($field['multiple']) &&
$field['multiple'] === true) {
            return (array) $value;
        }

        if (\is_array($value)) {
            return reset($value);
        }

        return $value;
    }

    /**
     * HTML5 input: select
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Select_Select($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    /**
     * HTML5 input: number
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Number($value, array $params, array
$field)
    {
        if (!is_numeric($value)) {
            return false;
        }

        if (isset($params['min']) && $value <
$params['min']) {
            return false;
        }

        if (isset($params['max']) && $value >
$params['max']) {
            return false;
        }

        $min = isset($params['min']) ? $params['min'] :
0;

        return !(isset($params['step']) && fmod($value -
$min, $params['step']) === 0);
    }

    protected static function filter_Input_Number($value, array $params,
array $field)
    {
        return (string)(int)$value !== (string)(float)$value ? (float)
$value : (int) $value;
    }

    protected static function filter_Input_DateTime($value, array $params,
array $field)
    {
        $converted = new \DateTime($value);
        return $converted->format('Y-m-d H:i:s');
    }


    /**
     * HTML5 input: range
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Range($value, array $params, array
$field)
    {
        return self::type_Input_Number($value, $params, $field);
    }

    protected static function filter_Input_Range($value, array $params,
array $field)
    {
        return self::filter_Input_Number($value, $params, $field);
    }

    /**
     * HTML5 input: color
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Color($value, array $params, array
$field)
    {
        return preg_match('/^\#[0-9a-fA-F]{3}[0-9a-fA-F]{3}?$/u',
$value);
    }

    /**
     * HTML5 input: email
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Email($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field) &&
filter_var($value, FILTER_VALIDATE_EMAIL);
    }

    /**
     * HTML5 input: url
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */

    public static function type_Input_Url($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field) &&
filter_var($value, FILTER_VALIDATE_URL);
    }

    /**
     * HTML5 input: datetime
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Datetime($value, array $params, array
$field)
    {
        if ($value instanceof \DateTime) {
            return true;
        }
        if (!\is_string($value)) {
            return false;
        }
        if (!isset($params['format'])) {
            return false !== strtotime($value);
        }

        $dateFromFormat =
\DateTime::createFromFormat($params['format'], $value);

        return $dateFromFormat && $value ===
date($params['format'], $dateFromFormat->getTimestamp());
    }

    /**
     * HTML5 input: datetime-local
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_DatetimeLocal($value, array $params,
array $field)
    {
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: date
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Date($value, array $params, array
$field)
    {
        if (!isset($params['format'])) {
            $params['format'] = 'Y-m-d';
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: time
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Time($value, array $params, array
$field)
    {
        if (!isset($params['format'])) {
            $params['format'] = 'H:i';
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: month
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Month($value, array $params, array
$field)
    {
        if (!isset($params['format'])) {
            $params['format'] = 'Y-m';
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: week
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Week($value, array $params, array
$field)
    {
        if (!isset($params['format']) &&
!preg_match('/^\d{4}-W\d{2}$/u', $value)) {
            return false;
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * Custom input: array
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function typeArray($value, array $params, array $field)
    {
        if (!\is_array($value)) {
            return false;
        }

        if (isset($field['multiple'])) {
            if (isset($params['min']) && \count($value)
< $params['min']) {
                return false;
            }

            if (isset($params['max']) && \count($value)
> $params['max']) {
                return false;
            }

            $min = isset($params['min']) ?
$params['min'] : 0;
            if (isset($params['step']) && (\count($value)
- $min) % $params['step'] === 0) {
                return false;
            }
        }

        $options = isset($field['options']) ?
array_keys($field['options']) : [];
        $values = isset($field['use']) &&
$field['use'] === 'keys' ? array_keys($value) : $value;

        return !($options && array_diff($values, $options));
    }

    protected static function filterArray($value, $params, $field)
    {
        $values = (array) $value;
        $options = isset($field['options']) ?
array_keys($field['options']) : [];
        $multi = isset($field['multiple']) ?
$field['multiple'] : false;

        if (\count($values) === 1 && isset($values[0]) &&
$values[0] === '') {
            return null;
        }

        if ($options) {
            $useKey = isset($field['use']) &&
$field['use'] === 'keys';
            foreach ($values as $key => $val) {
                $values[$key] = $useKey ? (bool) $val : $val;
            }
        }

        if ($multi) {
            foreach ($values as $key => $val) {
                if (\is_array($val)) {
                    $val = implode(',', $val);
                }

                $values[$key] =  array_map('trim',
explode(',', $val));
            }
        }

        return $values;
    }

    public static function type_Input_Yaml($value, $params)
    {
        try {
            Yaml::parse($value);
            return true;
        } catch (ParseException $e) {
            return false;
        }
    }

    public static function filter_Input_Yaml($value, $params)
    {
        try {
            return (array) Yaml::parse($value);
        } catch (ParseException $e) {
            return null;
        }
    }

    /**
     * Custom input: ignore (will not validate)
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Novalidate($value, array $params, array
$field)
    {
        return true;
    }

    public static function filter_Novalidate($value, array $params, array
$field)
    {
        return $value;
    }

    // HTML5 attributes (min, max and range are handled inside the types)

    public static function validate_Required($value, $params)
    {
        if (is_scalar($value)) {
            return (bool) $params !== true || $value !== '';
        }

        return (bool) $params !== true || !empty($value);
    }

    public static function validate_Pattern($value, $params)
    {
        return (bool) preg_match("`^{$params}$`u", $value);
    }


    // Internal types

    public static function validate_Alpha($value, $params)
    {
        return ctype_alpha($value);
    }

    public static function validate_Alnum($value, $params)
    {
        return ctype_alnum($value);
    }

    public static function type_Bool($value, $params)
    {
        return \is_bool($value) || $value == 1 || $value == 0;
    }

    public static function validate_Bool($value, $params)
    {
        return \is_bool($value) || $value == 1 || $value == 0;
    }

    protected static function filter_Bool($value, $params)
    {
        return (bool) $value;
    }

    public static function validate_Digit($value, $params)
    {
        return ctype_digit($value);
    }

    public static function validate_Float($value, $params)
    {
        return \is_float(filter_var($value, FILTER_VALIDATE_FLOAT));
    }

    protected static function filter_Float($value, $params)
    {
        return (float) $value;
    }

    public static function validate_Hex($value, $params)
    {
        return ctype_xdigit($value);
    }

    public static function validate_Int($value, $params)
    {
        return is_numeric($value) && (int) $value == $value;
    }

    protected static function filter_Int($value, $params)
    {
        return (int) $value;
    }

    public static function validate_Array($value, $params)
    {
        return \is_array($value)
            || ($value instanceof \ArrayAccess
                && $value instanceof \Traversable
                && $value instanceof \Countable);
    }

    public static function validate_Json($value, $params)
    {
        return (bool) (@json_decode($value));
    }
}
classes/Gantry/Component/Config/ValidationException.php000064400000002002151166614510017276
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

class ValidationException extends \RuntimeException
{
    protected $messages = [];

    public function setMessages(array $messages = []) {
        $this->messages = $messages;

        // TODO: add translation.
        $this->message = sprintf('Form validation failed:') .
' ' . $this->message;

        foreach ($messages as $variable => &$list) {
            $list = array_unique($list);
            foreach ($list as $message) {
                $this->message .= "<br/>$message";
            }
        }

        return $this;
    }

    public function getMessages()
    {
        return $this->messages;
    }
}
classes/Gantry/Component/Content/Block/ContentBlock.php000064400000013323151166614510017161
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

/**
 * Class to create nested blocks of content.
 *
 * $innerBlock = ContentBlock::create();
 * $innerBlock->setContent('my inner content');
 * $outerBlock = ContentBlock::create();
 * $outerBlock->setContent(sprintf('Inside my outer block I have
%s.', $innerBlock->getToken()));
 * $outerBlock->addBlock($innerBlock);
 * echo $outerBlock;
 *
 * @package Gantry\Component\Content\Block
 * @since 5.4.3
 */
class ContentBlock implements ContentBlockInterface
{
    protected $version = 1;
    protected $id;
    protected $tokenTemplate = '@@BLOCK-%s@@';
    protected $content = '';
    protected $blocks = [];

    /**
     * @param string $id
     * @return static
     * @since 5.4.3
     */
    public static function create($id = null)
    {
        return new static($id);
    }

    /**
     * @param array $serialized
     * @return ContentBlockInterface
     * @since 5.4.3
     */
    public static function fromArray(array $serialized)
    {
        try {
            $type = isset($serialized['_type']) ?
$serialized['_type'] : null;
            $id = isset($serialized['id']) ?
$serialized['id'] : null;

            if (!$type || !$id || !is_a($type,
'Gantry\Component\Content\Block\ContentBlockInterface', true)) {
                throw new \RuntimeException('Bad data');
            }

            /** @var ContentBlockInterface $instance */
            $instance = new $type($id);
            $instance->build($serialized);
        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Cannot unserialize
Block: %s', $e->getMessage()), $e->getCode(), $e);
        }

        return $instance;
    }

    /**
     * Block constructor.
     *
     * @param string $id
     * @since 5.4.3
     */
    public function __construct($id = null)
    {
        $this->id = $id ? (string) $id : $this->generateId();
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function getToken()
    {
        return sprintf($this->tokenTemplate, $this->getId());
    }

    /**
     * @return array
     * @since 5.4.3
     */
    public function toArray()
    {
        $blocks = [];
        /**
         * @var string $id
         * @var ContentBlockInterface $block
         */
        foreach ($this->blocks as $block) {
            $blocks[$block->getId()] = $block->toArray();
        }

        $array = [
            '_type' => get_class($this),
            '_version' => $this->version,
            'id' => $this->id,
        ];

        if ($this->content) {
            $array['content'] = $this->content;
        }

        if ($blocks) {
            $array['blocks'] = $blocks;
        }

        return $array;
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function toString()
    {
        if (!$this->blocks) {
            return (string) $this->content;
        }

        $tokens = [];
        $replacements = [];
        foreach ($this->blocks as $block) {
            $tokens[] = $block->getToken();
            $replacements[] = $block->toString();
        }

        return str_replace($tokens, $replacements, (string)
$this->content);
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function __toString()
    {
        try {
            return $this->toString();
        } catch (\Exception $e) {
            return sprintf('Error while rendering block: %s',
$e->getMessage());
        }
    }

    /**
     * @param array $serialized
     * @since 5.4.3
     */
    public function build(array $serialized)
    {
        $this->checkVersion($serialized);

        $this->id = isset($serialized['id']) ?
$serialized['id'] : $this->generateId();

        if (isset($serialized['content'])) {
            $this->setContent($serialized['content']);
        }

        $blocks = isset($serialized['blocks']) ? (array)
$serialized['blocks'] : [];
        foreach ($blocks as $block) {
            $this->addBlock(self::fromArray($block));
        }
    }

    /**
     * @param string $content
     * @return $this
     * @since 5.4.3
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * @param ContentBlockInterface $block
     * @return $this
     * @since 5.4.3
     */
    public function addBlock(ContentBlockInterface $block)
    {
        $this->blocks[$block->getId()] = $block;

        return $this;
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function serialize()
    {
        return serialize($this->toArray());
    }

    /**
     * @param string $serialized
     * @since 5.4.3
     */
    public function unserialize($serialized)
    {
        $array = unserialize($serialized);
        $this->build($array);
    }

    /**
     * @return string
     * @since 5.4.3
     */
    protected function generateId()
    {
        return uniqid('', true);
    }

    /**
     * @param array $serialized
     * @throws \RuntimeException
     * @since 5.4.3
     */
    protected function checkVersion(array $serialized)
    {
        $version = isset($serialized['_version']) ? (string)
$serialized['_version'] : 1;
        if ($version != $this->version) {
            throw new \RuntimeException(sprintf('Unsupported version
%s', $version));
        }
    }
}classes/Gantry/Component/Content/Block/ContentBlockInterface.php000064400000001570151166614510021003
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

/**
 * @since 5.4.3
 */
interface ContentBlockInterface extends \Serializable
{
    public static function create($id = null);
    public static function fromArray(array $serialized);

    public function __construct($id = null);

    public function getId();
    public function getToken();

    public function toArray();
    public function build(array $serialized);

    public function setContent($content);
    public function addBlock(ContentBlockInterface $block);
}classes/Gantry/Component/Content/Block/HtmlBlock.php000064400000031255151166614510016457
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Gantry\Framework\Theme;

/**
 * Class HtmlBlock
 * @package Gantry\Component\Content\Block
 * @since 5.4.3
 */
class HtmlBlock extends ContentBlock implements HtmlBlockInterface
{
    protected $version = 1;
    protected $frameworks = [];
    protected $styles = [];
    protected $scripts = [];
    protected $html = [];

    /**
     * @return array
     * @since 5.4.3
     */
    public function getAssets()
    {
        $assets = $this->getAssetsFast();

        $this->sortAssets($assets['styles']);
        $this->sortAssets($assets['scripts']);
        $this->sortAssets($assets['html']);

        return $assets;
    }

    /**
     * @return array
     * @since 5.4.3
     */
    public function getFrameworks()
    {
        $assets = $this->getAssetsFast();

        return array_keys($assets['frameworks']);
    }

    /**
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    public function getStyles($location = 'head')
    {
        $styles = $this->getAssetsInLocation('styles',
$location);

        if (!$styles) {
            return [];
        }

        $gantry = Gantry::instance();

        /** @var Theme $theme */
        $theme = isset($gantry['theme']) ?
$gantry['theme'] : null;

        /** @var Document $document */
        $document = $gantry['document'];

        foreach ($styles as $key => $style) {
            if (isset($style['href'])) {
                $url = $style['href'];
                if ($theme && preg_match('|\.scss$|',
$url)) {
                    // Compile SCSS files.
                    $url = $theme->css(basename($url,
'.scss'));
                }
                // Deal with streams and relative paths.
                $url = $document->url($url, false, null, false);

                $styles[$key]['href'] = $url;
            }
        }

        return $styles;
    }

    /**
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    public function getScripts($location = 'head')
    {
        $scripts = $this->getAssetsInLocation('scripts',
$location);

        if (!$scripts) {
            return [];
        }

        $gantry = Gantry::instance();

        /** @var Document $document */
        $document = $gantry['document'];

        foreach ($scripts as $key => $script) {
            if (isset($script['src'])) {
                // Deal with streams and relative paths.
                $scripts[$key]['src'] =
$document->url($script['src'], false, null, false);
            }
        }

        return $scripts;
    }

    /**
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    public function getHtml($location = 'bottom')
    {
        return $this->getAssetsInLocation('html', $location);
    }

    /**
     * @return array
     * @since 5.4.3
     */
    public function toArray()
    {
        $array = parent::toArray();

        if ($this->frameworks) {
            $array['frameworks'] = $this->frameworks;
        }
        if ($this->styles) {
            $array['styles'] = $this->styles;
        }
        if ($this->scripts) {
            $array['scripts'] = $this->scripts;
        }
        if ($this->html) {
            $array['html'] = $this->html;
        }

        return $array;
    }

    /**
     * @param array $serialized
     * @since 5.4.3
     */
    public function build(array $serialized)
    {
        parent::build($serialized);

        $this->frameworks = isset($serialized['frameworks']) ?
(array) $serialized['frameworks'] : [];
        $this->styles = isset($serialized['styles']) ? (array)
$serialized['styles'] : [];
        $this->scripts = isset($serialized['scripts']) ?
(array) $serialized['scripts'] : [];
        $this->html = isset($serialized['html']) ? (array)
$serialized['html'] : [];
    }

    /**
     * @param string $framework
     * @return $this
     * @since 5.4.3
     */
    public function addFramework($framework)
    {
        $this->frameworks[$framework] = 1;

        return $this;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     *
     * @example $block->addStyle('assets/js/my.js');
     * @example $block->addStyle(['href' =>
'assets/js/my.js', 'media' => 'screen']);
     * @since 5.4.3
     */
    public function addStyle($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['href' => (string) $element];
        }
        if (empty($element['href'])) {
            return false;
        }
        if (!isset($this->styles[$location])) {
            $this->styles[$location] = [];
        }

        $id = !empty($element['id']) ? ['id' =>
(string) $element['id']] : [];
        $href = $element['href'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/css';
        $media = !empty($element['media']) ? (string)
$element['media'] : null;
        unset($element['tag'], $element['id'],
$element['rel'], $element['content'],
$element['href'], $element['type'],
$element['media']);

        $this->styles[$location][md5($href) . sha1($href)] = [
                ':type' => 'file',
                ':priority' => (int) $priority,
                'href' => $href,
                'type' => $type,
                'media' => $media,
                'element' => $element
            ] + $id;

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addInlineStyle($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['content' => (string) $element];
        }
        if (empty($element['content'])) {
            return false;
        }
        if (!isset($this->styles[$location])) {
            $this->styles[$location] = [];
        }

        $content = (string) $element['content'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/css';

        $this->styles[$location][md5($content) . sha1($content)] = [
            ':type' => 'inline',
            ':priority' => (int) $priority,
            'content' => $content,
            'type' => $type
        ];

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addScript($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['src' => (string) $element];
        }
        if (empty($element['src'])) {
            return false;
        }
        if (!isset($this->scripts[$location])) {
            $this->scripts[$location] = [];
        }

        $src = $element['src'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/javascript';
        $defer = isset($element['defer']) ? true : false;
        $async = isset($element['async']) ? true : false;
        $handle = !empty($element['handle']) ? (string)
$element['handle'] : '';

        $this->scripts[$location][md5($src) . sha1($src)] = [
            ':type' => 'file',
            ':priority' => (int) $priority,
            'src' => $src,
            'type' => $type,
            'defer' => $defer,
            'async' => $async,
            'handle' => $handle
        ];

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addInlineScript($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['content' => (string) $element];
        }
        if (empty($element['content'])) {
            return false;
        }
        if (!isset($this->scripts[$location])) {
            $this->scripts[$location] = [];
        }

        $content = (string) $element['content'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/javascript';

        $this->scripts[$location][md5($content) . sha1($content)] = [
            ':type' => 'inline',
            ':priority' => (int) $priority,
            'content' => $content,
            'type' => $type
        ];

        return true;
    }

    /**
     * @param string $html
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addHtml($html, $priority = 0, $location =
'bottom')
    {
        if (empty($html) || !is_string($html)) {
            return false;
        }
        if (!isset($this->html[$location])) {
            $this->html[$location] = [];
        }

        $this->html[$location][md5($html) . sha1($html)] = [
            ':priority' => (int) $priority,
            'html' => $html
        ];

        return true;
    }

    /**
     * @param string $location
     * @deprecated Temporarily needed in WP
     * @since 5.4.3
     */
    public function clearStyles($location = 'head')
    {
        foreach ($this->blocks as $block) {
            if (method_exists($block, 'clearStyles')) {
                $block->clearStyles($location);
            }
        }
        unset($this->styles[$location]);
    }

    /**
     * @param string $location
     * @deprecated Temporarily needed in WP
     * @since 5.4.3
     */
    public function clearScripts($location = 'head')
    {
        foreach ($this->blocks as $block) {
            if (method_exists($block, 'clearScripts')) {
                $block->clearScripts($location);
            }
        }
        unset($this->scripts[$location]);
    }

    /**
     * @return array
     * @since 5.4.3
     */
    protected function getAssetsFast()
    {
        $assets = [
            'frameworks' => $this->frameworks,
            'styles' => $this->styles,
            'scripts' => $this->scripts,
            'html' => $this->html
        ];

        foreach ($this->blocks as $block) {
            if ($block instanceof HtmlBlock) {
                $blockAssets = $block->getAssetsFast();
                $assets['frameworks'] +=
$blockAssets['frameworks'];

                foreach ($blockAssets['styles'] as $location
=> $styles) {
                    if (!isset($assets['styles'][$location])) {
                        $assets['styles'][$location] = $styles;
                    } elseif ($styles) {
                        $assets['styles'][$location] += $styles;
                    }
                }

                foreach ($blockAssets['scripts'] as $location
=> $scripts) {
                    if (!isset($assets['scripts'][$location])) {
                        $assets['scripts'][$location] = $scripts;
                    } elseif ($scripts) {
                        $assets['scripts'][$location] +=
$scripts;
                    }
                }

                foreach ($blockAssets['html'] as $location =>
$htmls) {
                    if (!isset($assets['html'][$location])) {
                        $assets['html'][$location] = $htmls;
                    } elseif ($htmls) {
                        $assets['html'][$location] += $htmls;
                    }
                }
            }
        }

        return $assets;
    }

    /**
     * @param string $type
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    protected function getAssetsInLocation($type, $location)
    {
        $assets = $this->getAssetsFast();

        if (empty($assets[$type][$location])) {
            return [];
        }

        $styles = $assets[$type][$location];
        $this->sortAssetsInLocation($styles);

        return $styles;
    }

    /**
     * @param array $items
     * @since 5.4.3
     */
    protected function sortAssetsInLocation(array &$items)
    {
        $count = 0;
        foreach ($items as &$item) {
            $item[':order'] = ++$count;
        }
        uasort(
            $items,
            function ($a, $b) {
                return ($a[':priority'] ==
$b[':priority']) ? $a[':order'] -
$b[':order'] : $b[':priority'] -
$a[':priority'];
            }
        );
    }

    /**
     * @param array $array
     * @since 5.4.3
     */
    protected function sortAssets(array &$array)
    {
        foreach ($array as $location => &$items) {
            $this->sortAssetsInLocation($items);
        }
    }
}classes/Gantry/Component/Content/Block/HtmlBlockInterface.php000064400000001710151166614510020271
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

/**
 * @since 5.4.3
 */
interface HtmlBlockInterface extends ContentBlockInterface
{
    public function getAssets();

    public function addFramework($framework);
    public function addStyle($element, $priority = 0, $location =
'head');
    public function addInlineStyle($element, $priority = 0, $location =
'head');
    public function addScript($element, $priority = 0, $location =
'head');
    public function addInlineScript($element, $priority = 0, $location =
'head');
    public function addHtml($html, $priority = 0, $location =
'bottom');
}
classes/Gantry/Component/Content/Document/HtmlDocument.php000064400000064771151166614510017740
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Document;

use Gantry\Component\Content\Block\ContentBlock;
use Gantry\Component\Content\Block\HtmlBlock;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Url\Url;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class HtmlDocument
{
    use GantryTrait;

    public static $timestamp_age = 604800;
    public static $urlFilterParams;

    /**
     * @var array|HtmlBlock[]
     */
    protected static $stack;
    protected static $frameworks = [];
    protected static $scripts = [];
    protected static $styles = [];
    protected static $availableFrameworks = [
        'jquery' => 'registerJquery',
        'jquery.framework' => 'registerJquery',
        'jquery.ui.core' =>
'registerJqueryUiSortable',
        'jquery.ui.sortable' =>
'registerJqueryUiSortable',
        'bootstrap.2' => 'registerBootstrap2',
        'bootstrap.3' => 'registerBootstrap3',
        'mootools' => 'registerMootools',
        'mootools.framework' => 'registerMootools',
        'mootools.core' => 'registerMootools',
        'mootools.more' => 'registerMootoolsMore',
        'lightcase' => 'registerLightcase',
        'lightcase.init' => 'registerLightcaseInit',
    ];

    public function __construct()
    {
        static::$stack = [];
        static::push();
    }

    /**
     * Create new local instance of document allowing asset caching.
     */
    public static function push()
    {
        array_unshift(static::$stack, new HtmlBlock());
    }

    /**
     * Return local instance of document allowing it to be cached.
     *
     * @return HtmlBlock
     */
    public static function pop()
    {
        return array_shift(static::$stack);
    }

    /**
     * @param ContentBlock $block
     * @return $this
     */
    public function addBlock(ContentBlock $block)
    {
        static::$stack[0]->addBlock($block);

        return $this;
    }

    /**
     * @param string $framework
     * @return bool
     */
    public static function addFramework($framework)
    {
        if (!isset(static::$availableFrameworks[$framework])) {
            return false;
        }

        static::getObject();
        static::$stack[0]->addFramework($framework);

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addStyle($element, $priority = 0, $location =
'head')
    {
        static::getObject();
        return static::$stack[0]->addStyle($element, $priority,
$location);
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addInlineStyle($element, $priority = 0,
$location = 'head')
    {
        static::getObject();
        return static::$stack[0]->addInlineStyle($element, $priority,
$location);
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addScript($element, $priority = 0, $location =
'head')
    {
        static::getObject();
        return static::$stack[0]->addScript($element, $priority,
$location);
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addInlineScript($element, $priority = 0,
$location = 'head')
    {
        static::getObject();
        return static::$stack[0]->addInlineScript($element, $priority,
$location);
    }

    /**
     * @param string $html
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addHtml($html, $priority = 0, $location =
'bottom')
    {
        static::getObject();
        return static::$stack[0]->addHtml($html, $priority, $location);
    }

    /**
     * @param array $element
     * @param string $location
     * @param int $priority
     * @return bool
     */
    public static function addHeaderTag(array $element, $location =
'head', $priority = 0)
    {
        $success = false;

        switch ($element['tag']) {
            case 'link':
                if (!empty($element['rel']) &&
$element['rel'] === 'stylesheet') {
                    $success = static::addStyle($element, $priority,
$location);
                }

                break;

            case 'style':
                $success = static::addInlineStyle($element, $priority,
$location);

                break;

            case 'script':
                if (!empty($element['src'])) {
                    $success = static::addScript($element, $priority,
$location);
                } elseif (!empty($element['content'])) {
                    $success = static::addInlineScript($element, $priority,
$location);
                }

                break;
        }

        return $success;
    }

    public static function getStyles($location = 'head')
    {
        static::getObject();
        $styles = static::$stack[0]->getStyles($location);

        $output = [];

        foreach ($styles as $style) {
            switch ($style[':type']) {
                case 'file':
                    $attribs = '';
                    if ($style['media']) {
                        $attribs .= ' media="' .
static::escape($style['media']) . '"';
                    }
                    $output[] = sprintf(
                        '<link rel="stylesheet"
href="%s" type="%s"%s />',
                        static::escape($style['href']),
                        static::escape($style['type']),
                        $attribs
                    );
                    break;
                case 'inline':
                    $attribs = '';
                    if ($style['type'] !== 'text/css')
{
                        $attribs .= ' type="' .
static::escape($style['type']) . '"';
                    }
                    $output[] = sprintf(
                        '<style%s>%s</style>',
                        $attribs,
                        $style['content']
                    );
                    break;
            }
        }

        return $output;
    }

    public static function getScripts($location = 'head')
    {
        static::getObject();
        $scripts = static::$stack[0]->getScripts($location);

        $output = [];

        foreach ($scripts as $script) {
            switch ($script[':type']) {
                case 'file':
                    $attribs = '';
                    if ($script['async']) {
                        $attribs .= ' async="async"';
                    }
                    if ($script['defer']) {
                        $attribs .= ' defer="defer"';
                    }
                    $output[] = sprintf(
                        '<script type="%s"%s
src="%s"></script>',
                        static::escape($script['type']),
                        $attribs,
                        static::escape($script['src'])
                    );
                    break;
                case 'inline':
                    $output[] = sprintf(
                        '<script
type="%s">%s</script>',
                        static::escape($script['type']),
                        $script['content']
                    );
                    break;
            }
        }

        return $output;
    }

    public static function getHtml($location = 'bottom')
    {
        static::getObject();
        $htmls = static::$stack[0]->getHtml($location);
        $output = [];

        foreach ($htmls as $html) {
            $output[] = $html['html'];
        }

        return $output;
    }

    /**
     * Escape string (emulates twig filter).
     *
     * @param string|object $string
     * @param string $strategy
     * @return string
     */
    public static function escape($string, $strategy = 'html')
    {
        if (!is_string($string)) {
            if (is_object($string) && method_exists($string,
'__toString')) {
                $string = (string) $string;
            } elseif (in_array($strategy, ['html',
'js', 'css', 'html_attr', 'url']))
{
                return $string;
            }
        }

        switch ($strategy) {
            case 'html':
                return htmlspecialchars($string, ENT_QUOTES |
ENT_SUBSTITUTE, 'UTF-8');

            case 'js':
                if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
                    throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
                }

                $string = preg_replace_callback(
                    '#[^a-zA-Z0-9,\._]#Su',
                   
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_js_callback',
                    $string
                );

                return $string;

            case 'css':
                if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
                    throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
                }

                $string = preg_replace_callback(
                    '#[^a-zA-Z0-9]#Su',
                   
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_css_callback',
                    $string
                );

                return $string;

            case 'html_attr':
                if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
                    throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
                }

                $string = preg_replace_callback(
                    '#[^a-zA-Z0-9,\.\-_]#Su',
                   
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_html_attr_callback',
                    $string
                );

                return $string;

            case 'url':
                return rawurlencode($string);

            default:
                throw new \RuntimeException(sprintf('Invalid escaping
strategy "%s" (valid ones: html, js, css, html_attr, url).',
$strategy));
        }
    }

    /**
     * @param $framework
     * @return bool
     * @deprecated 5.3
     */
    public static function load($framework)
    {
        return static::addFramework($framework);
    }

    /**
     * Register assets.
     */
    public static function registerAssets()
    {
        static::registerFrameworks();
    }

    public static function siteUrl()
    {
        return static::rootUri();
    }

    /**
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @return string
     */
    public static function rootUri()
    {
        return '';
    }

    /**
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @param bool $addDomain
     * @return string
     */
    public static function domain($addDomain = false)
    {
        return '';
    }

    /**
     * Return URL to the resource.
     *
     * @example {{
url('gantry-theme://images/logo.png')|default('http://www.placehold.it/150x100/f4f4f4')
}}
     *
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @param  string $url         Resource to be located.
     * @param  bool $domain        True to include domain name.
     * @param  int $timestamp_age  Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                             Use value <= 0 to disable the
feature.
     * @param  bool $allowNull     True if non-existing files should return
null.
     * @return string|null         Returns url to the resource or null if
resource was not found.
     */
    public static function url($url, $domain = false, $timestamp_age =
null, $allowNull = true)
    {
        if (!is_string($url) || $url === '') {
            // Return null on invalid input.
            return null;
        }

        if ($url[0] === '#' || $url[0] === '?') {
            // Handle urls with query string or fragment only.
            return str_replace(' ', '%20', $url);
        }

        $parts = Url::parse($url);

        if (!is_array($parts)) {
            // URL could not be parsed.
            return $allowNull ? null : str_replace(' ',
'%20', $url);
        }

        // Make sure we always have scheme, host, port and path.
        $scheme = isset($parts['scheme']) ?
$parts['scheme'] : '';
        $host = isset($parts['host']) ? $parts['host']
: '';
        $port = isset($parts['port']) ? $parts['port']
: '';
        $path = isset($parts['path']) ? $parts['path']
: '';

        if ($scheme && !$port) {
            // If URL has a scheme, we need to check if it's one of
Gantry streams.
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];

            if (!$locator->schemeExists($scheme)) {
                // If scheme does not exists as a stream, assume it's
external.
                return str_replace(' ', '%20', $url);
            }

            // Attempt to find the resource (because of parse_url() we need
to put host back to path).
            $newPath =
$locator->findResource("{$scheme}://{$host}{$path}", false);

            if ($newPath === false) {
                if ($allowNull) {
                    return null;
                }

                // Return location where the file would be if it was saved.
                $path =
$locator->findResource("{$scheme}://{$host}{$path}", false,
true);
            } else {
                $path = $newPath;
            }

        } elseif ($host || $port) {
            // If URL doesn't have scheme but has host or port, it is
external.
            return str_replace(' ', '%20', $url);
        }

        // At this point URL is either relative or absolute path; let us
find if it is relative and not . or ..
        if ($path && '/' !== $path[0] &&
'.' !== $path[0]) {
            if ($timestamp_age === null) {
                $timestamp_age = static::$timestamp_age;
            }
            if ($timestamp_age > 0) {
                // We want to add timestamp to the URI: do it only for
existing files.
                $realPath = @realpath(GANTRY5_ROOT . '/' .
$path);
                if ($realPath && is_file($realPath)) {
                    $time = filemtime($realPath);
                    // Only append timestamp for files that are less than
the maximum age.
                    if ($time > time() - $timestamp_age) {
                        $parts['query'] =
(!empty($parts['query']) ?
"{$parts['query']}&" : '') .
sprintf('%x', $time);
                    }
                }
            }

            // We need absolute URI instead of relative.
            $path = rtrim(static::rootUri(), '/') . '/'
. $path;
        }

        // Set absolute URI.
        $uri = $path;

        // Add query string back.
        if (!empty($parts['query'])) {
            if (!$uri) $uri = static::rootUri();
            $uri .= '?' . $parts['query'];
        }

        // Add fragment back.
        if (!empty($parts['fragment'])) {
            if (!$uri) $uri = static::rootUri();
            $uri .= '#' . $parts['fragment'];
        }

        return static::domain($domain) . str_replace(' ',
'%20', $uri);
    }

    /**
     * Filter stream URLs from HTML.
     *
     * @param  string $html         HTML input to be filtered.
     * @param  bool $domain         True to include domain name.
     * @param  int $timestamp_age   Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                              Use value <= 0 to disable the
feature.
     * @param  bool $streamOnly     Only touch streams.
     * @return string               Returns modified HTML.
     */
    public static function urlFilter($html, $domain = false, $timestamp_age
= null, $streamOnly = false)
    {
        static::$urlFilterParams = [$domain, $timestamp_age, $streamOnly];

        // Tokenize all PRE, CODE and SCRIPT tags to avoid modifying any
src|href|url in them
        $tokens = [];

        $html =
preg_replace_callback('#<(pre|code|script)(\s[^>]+)?>.*?</\\1>#ius',
function($matches) use (&$tokens) {
            // Unfortunately uniqid() doesn't quite work in Windows,
so we need to work it around by adding some randomness.
            $token = '@@'. uniqid(mt_rand(), true) .
'@@';
            $match = $matches[0];

            $tokens[$token] = $match;

            return $token;
        }, $html);

        if ($streamOnly) {
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];
            $schemes = $locator->getSchemes();

            $list = [];
            foreach ($schemes as $scheme) {
                if (strpos($scheme, 'gantry-') === 0) {
                    $list[] = substr($scheme, 7);
                }
            }
            if (empty($list)) {
                return $html;
            }

            $match = '(gantry-(' . implode('|', $list).
')://.*?)';
        } else {
            $match = '(.*?)';
        }

        $html = preg_replace_callback('^(\s)(src|href)="' .
$match . '"^u', 'static::linkHandler', $html);
        $html = preg_replace_callback('^(\s)url\(' . $match .
'\)^u', 'static::urlHandler', $html);
        $html = static::replaceTokens($tokens, $html);

        return $html;
    }

    /**
     * @param array $matches
     * @return string
     * @internal
     */
    public static function linkHandler(array $matches)
    {
        list($domain, $timestamp_age) = static::$urlFilterParams;
        $url = trim($matches[3]);
        $url = static::url($url, $domain, $timestamp_age, false);

        return "{$matches[1]}{$matches[2]}=\"{$url}\"";
    }

    /**
     * @param array $matches
     * @return string
     * @internal
     */
    public static function urlHandler(array $matches)
    {
        list($domain, $timestamp_age) = static::$urlFilterParams;
        $url = trim($matches[2], '"\'');
        $url = static::url($url, $domain, $timestamp_age, false);

        return "{$matches[1]}url({$url})";
    }

    /**
     * This function is adapted from code coming from Twig.
     *
     * @param array $matches
     * @return string
     * @internal
     */
    public static function _escape_js_callback($matches)
    {
        $char = $matches[0];

        /*
         * A few characters have short escape sequences in JSON and
JavaScript.
         * Escape sequences supported only by JavaScript, not JSON, are
ommitted.
         * \" is also supported but omitted, because the resulting
string is not HTML safe.
         */
        static $shortMap = [
            '\\' => '\\\\',
            '/' => '\\/',
            "\x08" => '\b',
            "\x0C" => '\f',
            "\x0A" => '\n',
            "\x0D" => '\r',
            "\x09" => '\t',
        ];

        if (isset($shortMap[$char])) {
            return $shortMap[$char];
        }

        // \uHHHH
        $char = static::convert_encoding($char, 'UTF-16BE',
'UTF-8');
        $char = strtoupper(bin2hex($char));

        if (4 >= \strlen($char)) {
            return sprintf('\u%04s', $char);
        }

        return sprintf('\u%04s\u%04s', substr($char, 0, -4),
substr($char, -4));
    }

    /**
     * This function is adapted from code coming from Twig.
     *
     * @param $matches
     * @return string
     * @internal
     */
    public static function _escape_css_callback($matches)
    {
        $char = $matches[0];

        return sprintf('\\%X ', 1 === \strlen($char) ?
\ord($char) : static::ord($char));
    }

    /**
     * This function is adapted from code coming from Twig and Zend
Framework.
     *
     * @param array $matches
     * @return string
     *
     * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc.
(https://www.zend.com)
     * @license   https://framework.zend.com/license/new-bsd New BSD
License
     * @internal
     */
    public static function _escape_html_attr_callback($matches)
    {
        $chr = $matches[0];
        $ord = \ord($chr);

        /*
         * The following replaces characters undefined in HTML with the
         * hex entity for the Unicode replacement character.
         */
        if (($ord <= 0x1f && "\t" !== $chr &&
"\n" !== $chr && "\r" !== $chr) || ($ord >=
0x7f && $ord <= 0x9f)) {
            return '&#xFFFD;';
        }

        /*
         * Check if the current character to escape has a name entity we
should
         * replace it with while grabbing the hex value of the character.
         */
        if (1 === \strlen($chr)) {
            /*
             * While HTML supports far more named entities, the lowest
common denominator
             * has become HTML5's XML Serialisation which is
restricted to the those named
             * entities that XML supports. Using HTML entities would result
in this error:
             *     XML Parsing Error: undefined entity
             */
            static $entityMap = [
                34 => '&quot;', /* quotation mark */
                38 => '&amp;',  /* ampersand */
                60 => '&lt;',   /* less-than sign */
                62 => '&gt;',   /* greater-than sign */
            ];

            if (isset($entityMap[$ord])) {
                return $entityMap[$ord];
            }

            return sprintf('&#x%02X;', $ord);
        }

        /*
         * Per OWASP recommendations, we'll use hex entities for any
other
         * characters where a named entity does not exist.
         */
        return sprintf('&#x%04X;', static::ord($chr));
    }

    /**
     * Replace tokens with strings.
     *
     * @param array $tokens
     * @param $html
     * @return string|NUll
     */
    protected static function replaceTokens(array $tokens, $html)
    {
        foreach ($tokens as $token => $replacement) {
            // We need to use callbacks to turn off backreferences ($1,
\\1) in the replacement string.
            $callback = function() use ($replacement) { return
$replacement; };

            $html = preg_replace_callback('#' .
preg_quote($token, '#') . '#u', $callback, $html);
        }

        return $html;
    }

    /**
     * Register loaded frameworks.
     */
    protected static function registerFrameworks()
    {
        foreach (static::$stack[0]->getFrameworks() as $framework) {
            if (isset(static::$availableFrameworks[$framework])) {
                call_user_func([get_called_class(),
static::$availableFrameworks[$framework]]);
            }
        }
    }

    protected static function registerJquery()
    {
        static::addScript(
            [
                'src' =>
'https://code.jquery.com/jquery-2.2.2.min.js',
                'integrity' =>
'sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=',
                'crossorigin' => 'anonymous'
            ],
            11
        );
    }

    protected static function registerJqueryUiSortable()
    {
        static::registerJquery();

        static::addScript(
            [
                'src' =>
'https://code.jquery.com/ui/1.11.4/jquery-ui.min.js',
                'integrity' =>
'sha256-xNjb53/rY+WmG+4L6tTl9m6PpqknWZvRt0rO1SRnJzw=',
                'crossorigin' => 'anonymous'
            ],
            11
        );
    }

    protected static function registerBootstrap2()
    {
        static::registerJquery();

        static::addScript(['src' =>
'https://maxcdn.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js'],
11);
    }

    protected static function registerBootstrap3()
    {
        static::registerJquery();

        static::addScript(['src' =>
'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'],
11);
    }

    protected static function registerMootools()
    {
        static::addScript(['src' =>
'https://cdnjs.cloudflare.com/ajax/libs/mootools/1.5.2/mootools-core-compat.min.js'],
11);
    }

    protected static function registerMootoolsMore()
    {
        static::registerMootools();

        static::addScript(['src' =>
'https://cdnjs.cloudflare.com/ajax/libs/mootools-more/1.5.2/mootools-more-compat-compressed.js'],
11);
    }

    protected static function registerLightcase()
    {
        static::registerJquery();

        static::addScript(['src' =>
static::url('gantry-assets://js/lightcase.js', false, null,
false)], 11, 'footer');
        static::addStyle(['href' =>
static::url('gantry-assets://css/lightcase.css', false, null,
false)], 11);
    }

    protected static function registerLightcaseInit()
    {
        static::registerLightcase();

        static::addInlineScript(['content' =>
"jQuery(document).ready(function($) {
jQuery('[data-rel^=lightcase]').lightcase({maxWidth:
'100%', maxHeight: '100%', video: {width:
'1280', height: '720'}}); });"], 0,
'footer');
    }

    protected static function getObject()
    {
        static $object;

        if (!$object) {
            // We need to initialize document for backwards compatibility
(RokSprocket/RokGallery in WP).
            $object = Gantry::instance()['document'];
        }

        return $object;
    }

    /**
     * @param string $string
     * @param string $to
     * @param string $from
     * @return false|string|string[]|null
     * @internal
     */
    private static function convert_encoding($string, $to, $from)
    {
        if (\function_exists('mb_convert_encoding')) {
            return mb_convert_encoding($string, $to, $from);
        }
        if (\function_exists('iconv')) {
            return iconv($from, $to, $string);
        }

        throw new \RuntimeException('No suitable convert encoding
function (use UTF-8 as your encoding or install the iconv or mbstring
extension).');
    }

    /**
     * @param string $string
     * @return false|int|mixed
     * @internal
     */
    private static function ord($string)
    {
        if (\function_exists('mb_ord')) {
            return mb_ord($string, 'UTF-8');
        }

        $code = ($string = unpack('C*', substr($string, 0, 4))) ?
$string[1] : 0;
        if (0xF0 <= $code) {
            return (($code - 0xF0) << 18) + (($string[2] - 0x80)
<< 12) + (($string[3] - 0x80) << 6) + $string[4] - 0x80;
        }
        if (0xE0 <= $code) {
            return (($code - 0xE0) << 12) + (($string[2] - 0x80)
<< 6) + $string[3] - 0x80;
        }
        if (0xC0 <= $code) {
            return (($code - 0xC0) << 6) + $string[2] - 0x80;
        }

        return $code;
    }
}
classes/Gantry/Component/Controller/BaseController.php000064400000014771151166614510017201
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

use Gantry\Framework\Request;
use RocketTheme\Toolbox\DI\Container;
use RuntimeException;

abstract class BaseController implements RestfulControllerInterface
{
    /**
     * @var string Default HTTP method.
     */
    protected $method = 'GET';

    /**
     * @var Request
     */
    protected $request;

    /**
     * @var array List of HTTP verbs and their actions.
     */
    protected $httpVerbs = [
        'GET' => [
            '/'         => 'index',
            '/create'   => 'create',
            '/*'        => 'display',
            '/*/edit'   => 'edit'
        ],
        'POST' => [
            '/'  => 'store'
        ],
        'PUT' => [
            '/*' => 'replace'
        ],
        'PATCH' => [
            '/*' => 'update'
        ],
        'DELETE' => [
            '/*' => 'destroy'
        ]
    ];

    /**
     * @var array Parameters from router.
     */
    protected $params = [];

    /**
     * @var Container
     */
    protected $container;

    public function __construct(Container $container)
    {
        $this->container = $container;
        $this->request = $container['request'];
    }

    /**
     * Execute controller.
     *
     * @param string $method
     * @param array $path
     * @param array $params
     * @return mixed
     * @throws \RuntimeException
     */
    public function execute($method, array $path, array $params)
    {
        $this->method = $method;
        $this->setParams($params);
        list($action, $path) = $this->resolveHttpVerb($method, $path);

        if (!method_exists($this, $action)) {
            $action = 'undefined';
        }

        return call_user_func_array([$this, $action], $path);
    }

    /**
     * Set router parameters. Replaces the old parameters.
     *
     * @param array $params
     * @return $this
     */
    public function setParams(array $params)
    {
        $this->params = $params;

        return $this;
    }

    /**
     * @example GET /resources
     *
     * @return mixed
     */
    public function index()
    {
        return $this->undefined();
    }

    /**
     * @example GET /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function display($id)
    {
        return $this->undefined();
    }

    /**
     * Special sub-resource to create a new resource (returns a form).
     *
     * @example GET /resources/create
     *
     * @return mixed
     */
    public function create()
    {
        return $this->undefined();
    }

    /**
     * Special sub-resource to edit existing resource (returns a form).
     *
     * @example GET /resources/:id/edit
     *
     * @param string $id
     * @return mixed
     */
    public function edit($id)
    {
        return $this->undefined();
    }

    /**
     * @example POST /resources
     *
     * @return mixed
     */
    public function store()
    {
        return $this->undefined();
    }

    /**
     * @example PUT /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function replace($id)
    {
        return $this->undefined();
    }

    /**
     * @example PATCH /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function update($id)
    {
        return $this->undefined();
    }

    /**
     * @example DELETE /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function destroy($id)
    {
        return $this->undefined();
    }

    /**
     * Catch all action for all undefined actions.
     *
     * @return mixed
     * @throws RuntimeException
     */
    public function undefined()
    {
        if (in_array($this->method, ['HEAD',
'GET'])) {
            throw new RuntimeException('Page Not Found', 404);
        }

        throw new RuntimeException('Invalid Action', 405);
    }

    /**
     * Catch all action for forbidden actions.
     *
     * @return mixed
     * @throws RuntimeException
     */
    public function forbidden()
    {
        throw new RuntimeException('Forbidden', 403);
    }

    /**
     * Load resource.
     *
     * Function throws an exception if resource does not exist.
     *
     * @param string|int $id
     * @throws \RuntimeException
     */
    protected function loadResource($id)
    {
        throw new RuntimeException('Resource Not Found', 404);
    }

    /**
     * Resolve HTTP verb.
     *
     * @param string $method
     * @param array $items
     * @return array [function, parameters]
     */
    protected function resolveHttpVerb($method, array $items)
    {
        // HEAD has identical behavior to GET.
        $method = ($method == 'HEAD') ? 'GET' :
$method;

        if (!isset($this->httpVerbs[$method])) {
            // HTTP method is not defined.
            return ['undefined', $items];
        }

        $path = '';
        $remaining = $items;
        $variables = [];
        $actions = $this->httpVerbs[$method];

        // Build path for the verb and fetch all the variables.
        while (($current = array_shift($remaining)) !== null) {
            $test = "{$path}/{$current}";

            if (!isset($actions[$test])) {
                // Specific path not found, check if we have a variable.
                $test = "{$path}/*";

                if (isset($actions[$test])) {
                    // Variable found, save the value and move on.
                    $variables[] = $current;

                } elseif (isset($actions[$test . '*'])) {
                    // Wildcard found, pass through rest of the variables.
                    $path = $test . '*';
                    $variables = array_merge($variables, [$current],
$remaining);
                    break;

                } else {
                    // No matches; we are done here.
                    return ['undefined', $items];
                }
            }

            // Path was found.
            $path = $test;
        }

        // No matching path; check if we have verb for the root.
        if (!$path && isset($actions['/'])) {
            $path = '/';
        }

        // Get the action.
        $action = $path ? $actions[$path] : 'undefined';

        return [$action, $variables];
    }
}
classes/Gantry/Component/Controller/HtmlController.php000064400000002060151166614510017217
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\Response;

abstract class HtmlController extends BaseController
{
    /**
     * Execute controller and returns Response object, defaulting to
HtmlResponse.
     *
     * @param string $method
     * @param array $path
     * @param array $params
     * @return mixed
     * @throws \RuntimeException
     */
    public function execute($method, array $path, array $params)
    {
        $response = parent::execute($method, $path, $params);

        if (!$response instanceof Response) {
            $response = new HtmlResponse($response);
        }

        return $response;
    }
}
classes/Gantry/Component/Controller/JsonController.php000064400000001764151166614510017236
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

use Gantry\Component\Response\JsonResponse;

abstract class JsonController extends BaseController
{
    /**
     * Execute controller and returns JsonResponse object.
     *
     * @param string $method
     * @param array $path
     * @param array $params
     * @return mixed
     * @throws \RuntimeException
     */
    public function execute($method, array $path, array $params)
    {
        $response = parent::execute($method, $path, $params);

        if (!$response instanceof JsonResponse) {
            $response = new JsonResponse($response);
        }

        return $response;
    }
}
classes/Gantry/Component/Controller/RestfulControllerInterface.php000064400000003141151166614510021561
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

interface RestfulControllerInterface
{
    /**
     * @example GET /resources
     *
     * @return mixed
     */
    public function index();

    /**
     * @example GET /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function display($id);

    /**
     * Special sub-resource to create a new resource (returns a form).
     *
     * @example GET /resources/create
     *
     * @return mixed
     */
    public function create();

    /**
     * Special sub-resource to edit existing resource (returns a form).
     *
     * @example GET /resources/:id/edit
     *
     * @param string $id
     * @return mixed
     */
    public function edit($id);

    /**
     * @example POST /resources
     *
     * @return mixed
     */
    public function store();

    /**
     * @example PUT /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function replace($id);

    /**
     * @example PATCH /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function update($id);

    /**
     * @example DELETE /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function destroy($id);
}
classes/Gantry/Component/File/CompiledFile.php000064400000007774151166614510015360
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\File;

use RocketTheme\Toolbox\File\PhpFile;

/**
 * Class CompiledFile
 * @package Grav\Common\File
 *
 * @property string $filename
 * @property string $extension
 * @property string $raw
 * @property array|string $content
 */
trait CompiledFile
{
    protected $cachePath;
    protected $caching = true;

    /**
     * @param string $path
     * @return $this
     */
    public function setCachePath($path)
    {
        $this->cachePath = $path;

        return $this;
    }

    public function caching($enabled = null)
    {
        if (null !== $enabled) {
            $this->caching = (bool) $enabled;
        }

        return $this->caching;
    }

    /**
     * Get/set parsed file contents.
     *
     * @param mixed $var
     * @return string
     * @throws \BadMethodCallException
     */
    public function content($var = null)
    {
        if (!$this->cachePath) {
            throw new \BadMethodCallException("Cache path not defined
for compiled file ({$this->filename})!");
        }

        try {
            // If nothing has been loaded, attempt to get pre-compiled
version of the file first.
            if ($var === null && $this->raw === null &&
$this->content === null) {
                $modified = $this->modified();

                if (!$modified || !$this->caching) {
                    return $this->decode($this->raw());
                }

                $key = md5($this->filename);
                $file = PhpFile::instance($this->cachePath .
"/{$key}{$this->extension}.php");

                $class = get_class($this);

                $cache = $file->exists() ? $file->content() : null;

                // Load real file if cache isn't up to date (or is
invalid).
                if (!isset($cache['@class'])
                    || $cache['@class'] != $class
                    || $cache['modified'] != $modified
                    || $cache['filename'] != $this->filename
                ) {
                    // Attempt to lock the file for writing.
                    try {
                        $file->lock(false);
                    } catch (\Exception $e) {
                        // Another process has locked the file; we will
check this in a bit.
                    }

                    // Decode RAW file into compiled array.
                    $data = $this->decode($this->raw());
                    $cache = [
                        '@class' => $class,
                        'filename' => $this->filename,
                        'modified' => $modified,
                        'data' => $data
                    ];

                    // If compiled file wasn't already locked by
another process, save it.
                    if ($file->locked() !== false) {
                        $file->save($cache);
                        $file->unlock();

                        // Compile cached file into bytecode cache
                        if
(function_exists('opcache_invalidate')) {
                            // Silence error in case if
`opcache.restrict_api` directive is set.
                            @opcache_invalidate($file->filename(),
true);
                        } elseif
(function_exists('apc_compile_file')) {
                            // PHP 5.4
                            @apc_compile_file($file->filename());
                        }
                    }
                }
                $file->free();

                $this->content = $cache['data'];
            }

        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Failed to read %s:
%s', basename($this->filename), $e->getMessage()), 500, $e);
        }

        return parent::content($var);
    }
}
classes/Gantry/Component/File/CompiledYamlFile.php000064400000001502151166614510016162
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\File;

use RocketTheme\Toolbox\File\YamlFile;

class CompiledYamlFile extends YamlFile
{
    use CompiledFile;

    static public $defaultCachePath;
    static public $defaultCaching = true;

    protected function __construct()
    {
        parent::__construct();

        $this->caching(static::$defaultCaching);

        if (static::$defaultCachePath) {
            $this->setCachePath(static::$defaultCachePath);
        }
    }
}
classes/Gantry/Component/Filesystem/Folder.php000064400000025621151166614510015473
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Filesystem;

/**
 * Folder helper class.
 *
 * @author RocketTheme
 * @license MIT
 */
abstract class Folder
{
    /**
     * Recursively find the last modified time under given path.
     *
     * @param  string $path
     * @return int
     */
    public static function lastModifiedFolder($path)
    {
        $last_modified = 0;

        $directory = new \RecursiveDirectoryIterator($path,
\RecursiveDirectoryIterator::SKIP_DOTS);
        $iterator = new \RecursiveIteratorIterator($directory,
\RecursiveIteratorIterator::SELF_FIRST);

        /** @var \RecursiveDirectoryIterator $file */
        foreach ($iterator as $file) {
            $dir_modified = $file->getMTime();
            if ($dir_modified > $last_modified) {
                $last_modified = $dir_modified;
            }
        }

        return $last_modified;
    }

    /**
     * Get relative path between target and base path. If path isn't
relative, return full path.
     *
     * @param  string  $path
     * @param  string  $base
     * @return string
     */
    public static function getRelativePath($path, $base = GANTRY5_ROOT)
    {
        $base = preg_replace('![\\\/]+!', '/', $base);
        $path = preg_replace('![\\\/]+!', '/', $path);
        if (strpos($path, $base) === 0) {
            $path = ltrim(substr($path, strlen($base)), '/');
        }

        return $path;
    }

    /**
     * Get relative path between target and base path. If path isn't
relative, return full path.
     *
     * @param  string  $path
     * @param  string  $base
     * @return string
     */
    public static function getRelativePathDotDot($path, $base)
    {
        $base = preg_replace('![\\\/]+!', '/', $base);
        $path = preg_replace('![\\\/]+!', '/', $path);

        if ($path === $base) {
            return '';
        }

        $baseParts = explode('/', isset($base[0]) &&
'/' === $base[0] ? substr($base, 1) : $base);
        $pathParts = explode('/', isset($path[0]) &&
'/' === $path[0] ? substr($path, 1) : $path);

        array_pop($baseParts);
        $lastPart = array_pop($pathParts);
        foreach ($baseParts as $i => $directory) {
            if (isset($pathParts[$i]) && $pathParts[$i] ===
$directory) {
                unset($baseParts[$i], $pathParts[$i]);
            } else {
                break;
            }
        }
        $pathParts[] = $lastPart;
        $path = str_repeat('../', count($baseParts)) .
implode('/', $pathParts);

        return '' === $path
            || '/' === $path[0]
            || false !== ($colonPos = strpos($path, ':'))
&& ($colonPos < ($slashPos = strpos($path, '/')) ||
false === $slashPos)
            ? "./$path" : $path;
    }

    /**
     * Shift first directory out of the path.
     *
     * @param string $path
     * @return string
     */
    public static function shift(&$path)
    {
        $parts = explode('/', trim($path, '/'), 2);
        $result = array_shift($parts);
        $path = array_shift($parts);

        return $result ?: null;
    }

    /**
     * Return recursive list of all files and directories under given path.
     *
     * @param  string            $path
     * @param  array             $params
     * @return array
     * @throws \RuntimeException
     */
    public static function all($path, array $params = array())
    {
        if ($path === false) {
            throw new \RuntimeException("Path to {$path} doesn't
exist.");
        }

        $compare = isset($params['compare']) ? 'get' .
$params['compare'] : null;
        $pattern = isset($params['pattern']) ?
$params['pattern'] : null;
        $filters = isset($params['filters']) ?
$params['filters'] : null;
        $recursive = isset($params['recursive']) ?
$params['recursive'] : true;
        $levels = isset($params['levels']) ?
$params['levels'] : -1;
        $key = isset($params['key']) ? 'get' .
$params['key'] : null;
        $value = isset($params['value']) ? 'get' .
$params['value'] : ($recursive ? 'getSubPathname' :
'getFilename');
        $folders = isset($params['folders']) ?
$params['folders'] : true;
        $files = isset($params['files']) ?
$params['files'] : true;

        if ($recursive) {
            $directory = new \RecursiveDirectoryIterator($path,
                \RecursiveDirectoryIterator::SKIP_DOTS +
\FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
            $iterator = new \RecursiveIteratorIterator($directory,
\RecursiveIteratorIterator::SELF_FIRST);
            $iterator->setMaxDepth(max($levels, -1));
        } else {
            $iterator = new \FilesystemIterator($path);
        }

        $results = array();

        /** @var \RecursiveDirectoryIterator $file */
        foreach ($iterator as $file) {
            // Ignore hidden files.
            if ($file->getFilename()[0] == '.') {
                continue;
            }
            if (!$folders && $file->isDir()) {
                continue;
            }
            if (!$files && $file->isFile()) {
                continue;
            }
            if ($compare && $pattern &&
!preg_match($pattern, $file->{$compare}())) {
                continue;
            }
            $fileKey = $key ? $file->{$key}() : null;
            $filePath = $file->{$value}();
            if ($filters) {
                if (isset($filters['key'])) {
                    $filter = $filters['key'];
                    $pre = !empty($filters['pre-key']) ?
$filters['pre-key'] : '';
                    if (is_callable($filter)) {
                        $fileKey = $pre . call_user_func($filter,
$fileKey);
                    } else {
                        $fileKey = $pre . preg_replace($filter,
'', $fileKey);
                    }
                }
                if (isset($filters['value'])) {
                    $filter = $filters['value'];
                    if (is_callable($filter)) {
                        $filePath = call_user_func($filter, $file);
                    } else {
                        $filePath = preg_replace($filter, '',
$filePath);
                    }
                }
            }

            if ($fileKey !== null) {
                $results[$fileKey] = $filePath;
            } else {
                $results[] = $filePath;
            }
        }

        return $results;
    }

    /**
     * Recursively copy directory in filesystem.
     *
     * @param  string $source
     * @param  string $target
     * @param  string $ignore  Ignore files matching pattern (regular
expression).
     * @throws \RuntimeException
     */
    public static function copy($source, $target, $ignore = null)
    {
        $source = rtrim($source, '\\/');
        $target = rtrim($target, '\\/');

        if (!is_dir($source)) {
            throw new \RuntimeException('Cannot copy non-existing
folder.');
        }

        // Make sure that path to the target exists before copying.
        self::create($target);

        $success = true;

        // Go through all sub-directories and copy everything.
        $files = self::all($source);
        foreach ($files as $file) {
            if ($ignore && preg_match($ignore, $file)) {
                continue;
            }
            $src = $source .'/'. $file;
            $dst = $target .'/'. $file;

            if (is_dir($src)) {
                // Create current directory (if it doesn't exist).
                if (!is_dir($dst)) {
                    $success &= @mkdir($dst, 0777, true);
                }
            } else {
                // Or copy current file.
                $success &= @copy($src, $dst);
            }
        }

        if (!$success) {
            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }

        // Make sure that the change will be detected when caching.
        @touch(dirname($target));
    }

    /**
     * Move directory in filesystem.
     *
     * @param  string $source
     * @param  string $target
     * @throws \RuntimeException
     */
    public static function move($source, $target)
    {
        if (!is_dir($source)) {
            throw new \RuntimeException('Cannot move non-existing
folder.');
        }

        // Make sure that path to the target exists before moving.
        self::create(dirname($target));

        // Just rename the directory.
        $success = @rename($source, $target);

        if (!$success) {
            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }

        // Make sure that the change will be detected when caching.
        @touch(dirname($source));
        @touch(dirname($target));
    }

    /**
     * Recursively delete directory from filesystem.
     *
     * @param  string $target
     * @param  bool   $include_target
     * @throws \RuntimeException
     */
    public static function delete($target, $include_target = true)
    {
        if (!$target) { return; }

        if (!is_dir($target)) {
            throw new \RuntimeException('Cannot delete non-existing
folder.');
        }

        $success = self::doDelete($target, $include_target);

        if (!$success) {
            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }

        // Make sure that the change will be detected when caching.
        if ($include_target) {
            @touch(dirname($target));
        } else {
            @touch($target);
        }
    }

    /**
     * @param  string  $folder
     * @throws \RuntimeException
     */
    public static function create($folder)
    {
        if (is_dir($folder)) {
            return;
        }

        $success = @mkdir($folder, 0777, true);

        if (!$success) {
            // Take yet another look, make sure that the folder
doesn't exist.
            clearstatcache(true, $folder);
            if (is_dir($folder)) {
                return;
            }

            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }
    }

    /**
     * @param  string $folder
     * @param  bool   $include_target
     * @return bool
     * @internal
     */
    protected static function doDelete($folder, $include_target = true)
    {
        // Special case for symbolic links.
        if ($include_target && is_link($folder)) {
            return @unlink($folder);
        }

        // Go through all items in filesystem and recursively remove
everything.
        $files = array_diff(scandir($folder), array('.',
'..'));
        foreach ($files as $file) {
            $path = "{$folder}/{$file}";
            (is_dir($path)) ? self::doDelete($path) : @unlink($path);
        }

        return $include_target ? @rmdir($folder) : true;
    }
}
classes/Gantry/Component/Filesystem/Streams.php000064400000005415151166614510015675
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Filesystem;

use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\StreamWrapper\ReadOnlyStream;
use RocketTheme\Toolbox\StreamWrapper\Stream;

class Streams
{
    /**
     * @var array
     */
    protected $schemes = [];

    /**
     * @var array
     */
    protected $registered;

    /**
     * @var UniformResourceLocator
     */
    protected $locator;

    public function __construct(UniformResourceLocator $locator = null)
    {
        if ($locator) {
            $this->setLocator($locator);
        }
    }

    /**
     * @param UniformResourceLocator $locator
     */
    public function setLocator(UniformResourceLocator $locator)
    {
        $this->locator = $locator;

        // Set locator to both streams.
        Stream::setLocator($locator);
        ReadOnlyStream::setLocator($locator);
    }

    /**
     * @return UniformResourceLocator
     */
    public function getLocator()
    {
        return $this->locator;
    }

    public function add(array $schemes)
    {
        foreach ($schemes as $scheme => $config) {
            $force = !empty($config['force']);

            if (isset($config['paths'])) {
                $this->locator->addPath($scheme, '',
$config['paths'], false, $force);
            }
            if (isset($config['prefixes'])) {
                foreach ($config['prefixes'] as $prefix =>
$paths) {
                    $this->locator->addPath($scheme, $prefix, $paths,
false, $force);
                }
            }
            $type = !empty($config['type']) ?
$config['type'] : 'ReadOnlyStream';
            if ($type[0] != '\\') {
                $type = '\\Rockettheme\\Toolbox\\StreamWrapper\\'
. $type;
            }
            $this->schemes[$scheme] = $type;

            if (isset($this->registered)) {
                $this->doRegister($scheme, $type);
            }
        }
    }

    public function register()
    {
        $this->registered = stream_get_wrappers();

        foreach ($this->schemes as $scheme => $type) {
            $this->doRegister($scheme, $type);
        }
    }

    protected function doRegister($scheme, $type)
    {
        if (in_array($scheme, $this->registered)) {
            stream_wrapper_unregister($scheme);
        }

        if (!stream_wrapper_register($scheme, $type)) {
            throw new \InvalidArgumentException("Stream
'{$type}' could not be initialized.");
        }
    }
}
classes/Gantry/Component/Gantry/GantryTrait.php000064400000001527151166614520015650
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Gantry;

use Gantry\Framework\Gantry;

trait GantryTrait
{
    /**
     * @var Gantry
     */
    private static $gantry;

    /**
     * Get global Gantry instance.
     *
     * @return Gantry
     */
    public static function gantry()
    {
        // We cannot set variable directly for the trait as it doesn't
work in HHVM.
        if (!self::$gantry) {
            self::$gantry = Gantry::instance();
        }

        return self::$gantry;
    }
}
classes/Gantry/Component/Gettext/Gettext.php000064400000006016151166614520015202
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Gettext;

/**
 * Class Gettext
 * @package Gantry\Component\Gettext
 *
 * Examples on translating gettext in twig:
 *
 * {% trans string_var %}
 * http://twig.sensiolabs.org/doc/extensions/i18n.html
 *
 * {% trans %}Hello {{ author.name }}{% endtrans %}
 * http://symfony.com/doc/current/book/translation.html
 *
 * {{ 'Hello %name%'|trans({'%name%': name}) }}
 * {{ trans('Hello %name%', {'%name%': name}) }}
 */
class Gettext
{
    public $pos = 0;
    public $str;
    public $len;
    public $endian = 'V';

    public function parse($string)
    {
        $this->str = $string;
        $this->len = strlen($string);

        $magic = self::readInt() & 0xffffffff;

        if ($magic === 0x950412de) {
            // Low endian.
            $this->endian = 'V';
        } elseif ($magic === 0xde120495) {
            // Big endian.
            $this->endian = 'N';
        } else {
            throw new \Exception('Not a Gettext file (.mo)');
        }

        // Skip revision number.
        self::readInt();
        // Total count.
        $total = self::readInt();
        // Offset of original table.
        $originals = self::readInt();
        // Offset of translation table.
        $translations = self::readInt();

        $this->seek($originals);
        $table_originals = self::readIntArray($total * 2);
        $this->seek($translations);
        $table_translations = self::readIntArray($total * 2);

        $items = [];
        for ($i = 0; $i < $total; $i++) {
            $this->seek($table_originals[$i * 2 + 2]);
            $original = $this->read($table_originals[$i * 2 + 1]);

            if ($original) {
                $this->seek($table_translations[$i * 2 + 2]);
                $items[$original] = $this->read($table_translations[$i *
2 + 1]);
            }
        }

        return $items;
    }

    /**
     * @return int
     */
    protected function readInt()
    {
        $read = $this->read(4);

        if ($read === false) {
            return false;
        }

        $read = unpack($this->endian, $read);

        return array_shift($read);
    }

    /**
     * @param $count
     * @return array
     */
    protected function readIntArray($count)
    {
        return unpack($this->endian . $count, $this->read(4 *
$count));
    }

    /**
     * @param $bytes
     * @return string
     */
    private function read($bytes)
    {
        $data = substr($this->str, $this->pos, $bytes);
        $this->seek($this->pos + $bytes);
        return $data;
    }

    /**
     * @param $pos
     * @return mixed
     */
    private function seek($pos)
    {
        $this->pos = max($this->len, $pos);
        return $this->pos;
    }
}
classes/Gantry/Component/Layout/Layout.php000064400000102773151166614520014673
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout;

use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Outlines;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Layout
 */
class Layout implements \ArrayAccess, \Iterator, ExportInterface
{
    use ArrayAccess, Iterator, Export;

    const VERSION = 7;

    protected static $instances = [];
    protected static $indexes = [];
    protected $layout = ['wrapper', 'container',
'section', 'grid', 'block',
'offcanvas'];

    public $name;
    public $timestamp = 0;
    public $preset = [];
    public $equalized = [3 => 33.3, 6 => 16.7, 7 => 14.3, 8 =>
12.5, 9 => 11.1, 11 => 9.1, 12 => 8.3];

    protected $exists;
    protected $items;
    protected $references;
    protected $parents;
    protected $blocks;
    protected $types;
    protected $inherit;

    /**
     * @return array
     */
    public static function presets()
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        /** @var UniformResourceIterator $iterator */
        $iterator = $locator->getIterator(
            'gantry-layouts://',
            UniformResourceIterator::CURRENT_AS_SELF |
UniformResourceIterator::UNIX_PATHS | UniformResourceIterator::SKIP_DOTS
        );

        $files = [];
        /** @var UniformResourceIterator $info */
        foreach ($iterator as $info) {
            $name = $info->getBasename('.yaml');
            if (!$info->isFile() || $info->getExtension() !==
'yaml' || $name[0] === '.') {
                continue;
            }
            $files[] = $name;
        }

        sort($files);

        $results = ['user' => [], 'system' =>
[]];
        foreach ($files as $preset) {
            $scope = $preset && $preset[0] !== '_' ?
'user' : 'system';
            $results[$scope][$preset] =
ucwords(trim(preg_replace(['|_|', '|/|'], ['
', ' / '], $preset)));
        }

        return $results;
    }

    /**
     * @param string $name
     * @return array
     * @throws \RuntimeException
     */
    public static function preset($name)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $filename =
$locator->findResource("gantry-layouts://{$name}.yaml");

        if (!$filename) {
            throw new \RuntimeException(sprintf("Preset '%s'
not found", $name), 404);
        }

        $layout = LayoutReader::read($filename);
        $layout['preset']['name'] = $name;
        $layout['preset']['timestamp'] =
filemtime($filename);

        return $layout;
    }

    /**
     * @param  string $name
     * @return Layout
     */
    public static function instance($name)
    {
        if (!isset(static::$instances[$name])) {
            static::$instances[$name] = static::load($name);
        }

        return static::$instances[$name];
    }

    /**
     * @param  string $name
     * @return Layout
     */
    public static function index($name)
    {
        if (!isset(static::$indexes[$name])) {
            static::$indexes[$name] = static::loadIndex($name, true);
        }

        return static::$indexes[$name];
    }

    /**
     * @param string $name
     * @param array $items
     * @param array $preset
     */
    public function __construct($name, array $items = null, array $preset =
null)
    {
        $this->name = $name;
        $this->items = (array) $items;
        $this->exists = $items !== null;

        // Add preset data from the layout.
        if ($preset) {
            $this->preset = $preset;
        } elseif (isset($this->items['preset'])) {
            $this->preset = (array) $this->items['preset'];
        }

        unset($this->items['preset']);

        $this->preset += [
            'name' => '',
            'timestamp' => 0,
            'image' =>
'gantry-admin://images/layouts/default.png'
        ];
    }

    /**
     * @return bool
     */
    public function exists()
    {
        return $this->exists;
    }

    /**
     * Initialize layout.
     *
     * @param  bool  $force
     * @param  bool  $inherit
     * @return $this
     */
    public function init($force = false, $inherit = true)
    {
        if ($force || $this->references === null) {
            $this->initReferences();
            if ($inherit) {
                $this->initInheritance();
            }
        }

        return $this;
    }

    /**
     * Build separate meta-information from the layout.
     *
     * @return array
     */
    public function buildIndex()
    {
        return [
            'name' => $this->name,
            'timestamp' => $this->timestamp,
            'version' => static::VERSION,
            'preset' => $this->preset,
            'positions' => $this->positions(),
            'sections' => $this->sections(),
            'particles' => $this->particles(),
            'inherit' => $this->inherit()
        ];
    }

    /**
     * @return $this
     */
    public function clean()
    {
        $this->references = null;
        $this->types = null;
        $this->inherit = null;

        $this->cleanLayout($this->items);

        return $this;
    }

    /**
     * @param string $old
     * @param string $new
     * @param array  $ids
     * @return $this
     */
    public function updateInheritance($old, $new = null, $ids = null)
    {
        $this->init();

        $inherit = $this->inherit();

        if (!empty($inherit[$old])) {
            foreach ($inherit[$old] as $id => $inheritId) {
                $element = $this->find($id, false);
                if ($element) {
                    $inheritId = isset($element->inherit->particle) ?
$element->inherit->particle : $id;
                    if ($new && ($ids === null ||
isset($ids[$inheritId]))) {
                        // Add or modify inheritance.
                        if (!isset($element->inherit)) {
                            $element->inherit = new \stdClass;
                        }
                        $element->inherit->outline = $new;
                    } else {
                        // Remove inheritance.
                        $element->inherit = new \stdClass;
                        unset($this->inherit[$element->id]);
                    }
                } else {
                    // Element does not exist anymore, remove its
reference.
                    unset($this->inherit[$id]);
                }
            }
        }

        return $this;
    }


    /**
     * Save layout.
     *
     * @param bool $cascade
     * @return $this
     */
    public function save($cascade = true)
    {
        if (!$this->name) {
            throw new \LogicException('Cannot save unnamed
layout');
        }

        GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Saving layout for outline
{$this->name}");

        $name = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $this->name));

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // If there are atoms in the layout, copy them into outline
configuration.
        $atoms = $this->atoms();
        if (is_array($atoms) && $cascade) {
                // Save layout into custom directory for the current theme.
                $filename =
$locator->findResource("gantry-config://{$name}/page/head.yaml",
true, true);

                $file = YamlFile::instance($filename);
                $config = new Config($file->content());

                $file->save($config->set('atoms',
json_decode(json_encode($atoms), true))->toArray());
                $file->free();
        }

        // Remove atoms from the layout.
        foreach ($this->items as $key => $section) {
            if ($section->type === 'atoms') {
                unset ($this->items[$key]);
            }
        }

        // Make sure that base outline never uses inheritance.
        if ($name === 'default') {
            $this->inheritNothing();
        }

        $filename =
$locator->findResource("gantry-config://{$name}/layout.yaml",
true, true);
        $file = CompiledYamlFile::instance($filename);
        $file->settings(['inline' => 20]);
        $file->save(LayoutReader::store($this->preset,
$this->items));
        $file->free();

        $this->timestamp = $file->modified();
        $this->exists = true;

        static::$instances[$this->name] = $this;

        return $this;
    }

    public function export()
    {
        return LayoutReader::store($this->preset, $this->items);
    }

    /**
     * Save index.
     *
     * @return $this
     */
    public function saveIndex($index = null)
    {
        if (!$this->name) {
            throw new \LogicException('Cannot save unnamed
layout');
        }

        GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Saving layout index for outline
{$this->name}");

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $filename =
$locator->findResource("gantry-config://{$this->name}/index.yaml",
true, true);
        $cache =
$locator->findResource("gantry-cache://{$this->name}/compiled/yaml",
true, true);
        $file = CompiledYamlFile::instance($filename);

        // Attempt to lock the file for writing.
        try {
            $file->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }

        $index = $index ? $index : $this->buildIndex();

        // If file wasn't already locked by another process, save it.
        if ($file->locked() !== false) {
            $file->setCachePath($cache)->settings(['inline'
=> 20]);
            $file->save($index);
            $file->unlock();
        }
        $file->free();

        static::$indexes[$this->name] = $index;

        return $this;
    }

    /**
     * @return array
     */
    public function getLayoutTypes()
    {
        return $this->layout;
    }

    /**
     * @param string $type
     * @return bool
     */
    public function isLayoutType($type)
    {
        return in_array($type, $this->layout, true);
    }

    /**
     * @param $id
     * @return string|null
     */
    public function getParentId($id)
    {
        return isset($this->parents[$id]) ? $this->parents[$id] :
null;
    }

    /**
     * @return array
     */
    public function references()
    {
        $this->init();

        return $this->references;
    }

    /**
     * @param string $type
     * @param string $subtype
     * @return array
     */
    public function referencesByType($type = null, $subtype = null)
    {
        $this->init();

        if (!$type) {
            return $this->types;
        }

        if (!$subtype) {
            return isset($this->types[$type]) ? $this->types[$type] :
[];
        }

        return isset($this->types[$type][$subtype]) ?
$this->types[$type][$subtype] : [];
    }

    /**
     * Return list of positions (key) with their titles (value).
     *
     * @return array Array of position => title
     */
    public function positions()
    {
        $positions = $this->referencesByType('position',
'position');

        $list = [];
        foreach($positions as $position) {
            if (!isset($position->attributes->key)) {
                continue;
            }
            $list[$position->attributes->key] = $position->title;
        }

        return $list;
    }

    /**
     * Return list of positions (key) with their titles (value).
     *
     * @return array Array of position => title
     */
    public function sections()
    {
        $list = [];
        foreach ($this->referencesByType('section') as $type
=> $sections) {
            foreach ($sections as $id => $section) {
                $list[$id] = $section->title;
            }
        }

        foreach ($this->referencesByType('offcanvas') as $type
=> $sections) {
            foreach ($sections as $id => $section) {
                $list[$id] = $section->title;
            }
        }

        return $list;
    }

    /**
     * Return list of particles with their titles.
     *
     * @param  bool  $grouped  If true, group particles by type.
     * @return array Array of position => title
     */
    public function particles($grouped = true)
    {
        $blocks = $this->referencesByType('block',
'block');

        $list = [];
        foreach ($blocks as $blockId => $block) {
            if (!empty($block->children)) {
                foreach ($block->children as $id => $particle) {
                    if (!empty($particle->layout) ||
in_array($particle->type, $this->layout, true)) {
                        continue;
                    }
                    if ($grouped) {
                        $list[$particle->subtype][$particle->id] =
$particle->title;
                    } else {
                        $list[$particle->id] = $particle->title;
                    }
                }
            }
        }

        return $list;
    }

    /**
     * @param string $outline
     * @return array
     */
    public function inherit($outline = null)
    {
        $this->init();

        $list = [];
        foreach ($this->inherit as $name => $item) {
            if (isset($item->inherit->outline)) {
                if (isset($item->inherit->particle)) {
                    $list[$item->inherit->outline][$name] =
$item->inherit->particle;
                } else {
                    $list[$item->inherit->outline][$name] = $name;
                }
            }
        }

        return $outline ? (!empty($list[$outline]) ? $list[$outline] : [])
: $list;
    }

    /**
     * Return atoms from the layout.
     *
     * @return array|null
     * @deprecated
     */
    public function atoms()
    {
        $list   = null;

        $atoms = array_filter($this->items, function ($section) {
            return $section->type === 'atoms' &&
!empty($section->children);
        });
        $atoms = array_shift($atoms);

        if (!empty($atoms->children)) {
            $list = [];
            foreach ($atoms->children as $grid) {
                if (!empty($grid->children)) {
                    foreach ($grid->children as $block) {
                        if (isset($block->children[0])) {
                            $item = $block->children[0];
                            $list[] = ['title' =>
$item->title, 'type' => $item->subtype,
'attributes' => $item->attributes];
                        }
                    }
                }
            }
        }

        return $list;
    }

    /**
     * @param string $id
     * @param bool $createIfNotExists
     * @return object
     */
    public function find($id, $createIfNotExists = true)
    {
        $this->init();

        if (!isset($this->references[$id])) {
            return $createIfNotExists ? (object)['id' => $id,
'inherit' => new \stdClass] : null;
        }

        return $this->references[$id];
    }

    /**
     * @param string $id
     * @return null
     */
    public function block($id)
    {
        $this->init();

        return isset($this->blocks[$id]) ? $this->blocks[$id] : null;
    }

    public function clearSections()
    {
        $this->items = $this->clearChildren($this->items);

        return $this;
    }

    protected function clearChildren(&$items)
    {
        foreach ($items as $key => $item) {
            if (!empty($item->children)) {
                $this->children =
$this->clearChildren($item->children);
            }

            if (empty($item->children) &&
in_array($item->type, ['grid', 'block',
'particle', 'position', 'spacer',
'system'], true)) {
                unset($items[$key]);
            }
        }

        return array_values($items);
    }

    public function copySections(array $old)
    {
        $this->init();

        /** @var Layout $old */
        $old = new static('tmp', $old);

        $leftover = [];

        // Copy normal sections.
        $data = $old->referencesByType('section');

        if (isset($this->types['section'])) {
            $sections = $this->types['section'];

            $this->copyData($data, $sections, $leftover);
        }

        // Copy offcanvas.
        $data = $old->referencesByType('offcanvas');
        if (isset($this->types['offcanvas'])) {
            $offcanvas = $this->types['offcanvas'];

            $this->copyData($data, $offcanvas, $leftover);
        }

        // Copy atoms.
        $data = $old->referencesByType('atoms');
        if (isset($this->types['atoms'])) {
            $atoms = $this->types['atoms'];

            $this->copyData($data, $atoms, $leftover);
        }

        return $leftover;
    }

    public function inheritAll()
    {
        foreach ($this->references() as $item) {
            if (!empty($item->inherit->outline)) {
                continue;
            }
            if (!$this->isLayoutType($item->type)) {
                $item->inherit = (object) ['outline' =>
$this->name, 'include' => ['attributes',
'block']];
            } elseif ($item->type === 'section' ||
$item->type === 'offcanvas') {
                $item->inherit = (object) ['outline' =>
$this->name, 'include' => ['attributes',
'block', 'children']];
            }
        }

        $this->init(true);

        return $this;
    }

    public function inheritNothing()
    {
        foreach ($this->references() as $item) {
            unset($item->inherit);
        }

        $this->init(true);

        return $this;
    }

    protected function copyData(array $data, array $sections, array
&$leftover)
    {
        foreach ($data as $type => $items) {
            foreach ($items as $item) {
                $found = false;
                if (isset($sections[$type])) {
                    foreach ($sections[$type] as $section) {
                        if ($section->id === $item->id) {
                            $found = true;
                            $section->inherit =
$this->cloneData($item->inherit);
                            $section->children =
$this->cloneData($item->children);
                            break;
                        }
                    }
                }
                if (!$found && !empty($item->children)) {
                    $leftover[$item->id] = $item->title;
                }
            }
        }
    }

    /**
     * Clone data which consists mixed set of arrays and stdClass objects.
     *
     * @param mixed $data
     * @return mixed
     */
    protected function cloneData($data)
    {
        if (!($isObject = is_object($data)) && !is_array($data)) {
            return $data;
        }

        $clone = [];

        foreach((array) $data as $key => $value) {
            if (is_object($value) || is_array($value)) {
                $clone[$key] = $this->cloneData($value);
            } else {
                $clone[$key] = $value;
            }
        }

        return $isObject ? (object) $clone : $clone;
    }

    /**
     * @param array $items
     */
    protected function cleanLayout(array $items)
    {
        foreach ($items as $item) {
            if (!empty($item->inherit->include)) {
                $include = $item->inherit->include;
                foreach ($include as $part) {
                    switch ($part) {
                        case 'attributes':
                            $item->attributes = new \stdClass();
                            break;
                        case 'block':
                            break;
                        case 'children':
                            $item->children = [];
                            break;
                    }
                }
            }
            if (!empty($item->children)) {
                $this->cleanLayout($item->children);
            }
        }
    }

    protected function initInheritance()
    {
        $index = null;
        if ($this->name) {
            $index = static::loadIndexFile($this->name);
        }

        $inheriting = $this->inherit();

        if (GANTRY_DEBUGGER && $inheriting) {
            \Gantry\Debugger::addMessage(sprintf("Layout from outline
%s inherits %s", $this->name, implode(", ",
array_keys($inheriting))));
        }

        foreach ($inheriting as $outlineId => $list) {
            try {
                $outline = $this->instance($outlineId);
            } catch (\Exception $e) {
                // Outline must have been deleted.
                GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Outline {$outlineId} is missing /
deleted", 'error');
                $outline = null;
            }
            foreach ($list as $id => $inheritId) {
                $item = $this->find($id);

                $inheritId = !empty($item->inherit->particle) ?
$item->inherit->particle : $id;
                $inherited = $outline ? $outline->find($inheritId) :
null;
                $include = !empty($item->inherit->include) ? (array)
$item->inherit->include : [];

                foreach ($include as $part) {
                    switch ($part) {
                        case 'attributes':
                            // Deep clone attributes.
                            $item->attributes =
isset($inherited->attributes) ?
$this->cloneData($inherited->attributes) : new \stdClass();
                            break;
                        case 'block':
                            $block = $this->block($id);
                            if (isset($block->attributes)) {
                                $inheritBlock = $outline ?
$this->cloneData($outline->block($inheritId)) : null;
                                $blockAttributes = $inheritBlock ?
                                   
array_diff_key((array)$inheritBlock->attributes, ['fixed'
=> 1, 'size' => 1]) : [];
                                $block->attributes =
(object)($blockAttributes + (array)$block->attributes);
                            }
                            break;
                        case 'children':
                            if (!empty($inherited->children)) {
                                // Deep clone children.
                                $item->children =
$this->cloneData($inherited->children);
                               
$this->initReferences($item->children, $this->getParentId($id),
null,
                                    ['outline' => $outlineId,
'include' => ['attributes', 'block']],
$index);
                            } else {
                                $item->children = [];
                            }
                            break;
                    }
                }

                if (!$outline || !isset($inherited->attributes)) {
                    // Remove inheritance information if outline
doesn't exist.
                    $item->inherit = new \stdClass;
                    unset($this->inherit[$item->id]);
                }
            }
        }

    }

    /**
     * @param array $items
     * @param object $parent
     * @param object $block
     * @param string $inherit
     * @param array $index
     */
    protected function initReferences(array $items = null, $parent = null,
$block = null, $inherit = null, array $index = null)
    {
        if ($items === null) {
            $items = $this->items;
            $this->references = [];
            $this->types = [];
            $this->inherit = [];
        }

        foreach ($items as $item) {
            if (is_object($item)) {
                $type = $item->type;
                $subtype = !empty($item->subtype) ? $item->subtype :
$type;

                if ($block) {
                    $this->parents[$item->id] = $parent;
                }
                if ($block) {
                    $this->blocks[$item->id] = $block;
                }

                if ($inherit && !$this->isLayoutType($type)) {
                    $item->inherit = (object) $inherit;
                    $item->inherit->particle = $item->id;

                    if
(isset($index['inherit'][$item->inherit->outline])
&& ($newId = array_search($item->id,
$index['inherit'][$item->inherit->outline], true))) {
                        $item->id = $newId;
                    } else {
                        $item->id = $this->id($type, $subtype);
                    }
                }

                if (isset($item->id)) {
                    if (isset($this->references[$item->id])) {
                        if ($type === 'block' || $type ===
'grid') {
                            $item->id = $this->id($type, $subtype);
                        }
//                        elseif (null === $inherit) {
//                            throw new \RuntimeException('Layout
reference conflict on #' . $item->id);
//                        }
                    }
                    $this->references[$item->id] = $item;
                    $this->types[$type][$subtype][$item->id] = $item;

                    if (!empty($item->inherit->outline)) {
                        $this->inherit[$item->id] = $item;
                    }
                } else {
                    $this->types[$type][$subtype][] = $item;
                }

                if (isset($item->children) &&
is_array($item->children)) {
                    $this->initReferences($item->children, $type ===
'section' ? $item : $parent, $type === 'block' ? $item
: null, $inherit, $index);
                }
            }
        }
    }

    /**
     * @param string $type
     * @param string $subtype
     * @param string $id
     * @return string
     */
    protected function id($type, $subtype = null, $id = null)
    {
        $result = [];
        if ($type !== 'particle') {
            $result[] = $type;
        }
        if ($subtype && ($subtype !== $type || $subtype ===
'position')) {
            $result[] = $subtype;
        }
        $key = implode('-', $result);

        $key_id = $key . '-'. $id;
        if (!$id || isset($this->references[$key_id])) {
            while ($id = rand(1000, 9999)) {
                $key_id = $key . '-'. $id;
                if (!isset($this->references[$key_id])) {
                    break;
                }
            }
        }

        return $key_id;
    }

    /**
     * Prepare block width sizes.
     *
     * @return $this
     */
    public function prepareWidths()
    {
        $this->init();

        $this->calcWidths($this->items);

        return $this;
    }

    /**
     * Recalculate block widths.
     *
     * @param array $items
     * @internal
     */
    protected function calcWidths(array &$items)
    {
        foreach ($items as $i => $item) {
            if (empty($item->children)) {
                continue;
            }

            $this->calcWidths($item->children);

            $dynamicSize = 0;
            $fixedSize = 0;
            $childrenCount = 0;
            foreach ($item->children as $child) {
                if ($child->type !== 'block') {
                    continue;
                }
                $childrenCount++;
                if (!isset($child->attributes->size)) {
                    $child->attributes->size = 100 /
count($item->children);
                }
                if (empty($child->attributes->fixed)) {
                    $dynamicSize += $child->attributes->size;
                } else {
                    $fixedSize += $child->attributes->size;
                }
            }

            if (!$childrenCount) {
                continue;
            }

            $roundSize = round($dynamicSize, 1);
            $equalized = isset($this->equalized[$childrenCount]) ?
$this->equalized[$childrenCount] : 0;

            // force-casting string for testing comparison due to weird PHP
behavior that returns wrong result
            if ($roundSize !== 100 && (string) $roundSize !==
(string) ($equalized * $childrenCount)) {
                $fraction = 0;
                $multiplier = (100 - $fixedSize) / ($dynamicSize ?: 1);
                foreach ($item->children as $child) {
                    if ($child->type !== 'block') {
                        continue;
                    }
                    if (!empty($child->attributes->fixed)) {
                        continue;
                    }

                    // Calculate size for the next item by taking account
the rounding error from the last item.
                    // This will allow us to approximate cumulating error
and fix it when rounding error grows
                    // over the rounding treshold.
                    $size = ($child->attributes->size * $multiplier)
+ $fraction;
                    $newSize = round($size);
                    $fraction = $size - $newSize;
                    $child->attributes->size = $newSize;
                }
            }
        }
    }

    /**
     * @param  string $name
     * @param  string $preset
     * @return static
     */
    public static function load($name, $preset = null)
    {
        if (!$name) {
            throw new \BadMethodCallException('Layout needs to have a
name');
        }

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $layout = null;
        $filename =
$locator("gantry-config://{$name}/layout.yaml");

        // If layout file doesn't exists, figure out what preset was
used.
        if (!$filename) {

            // Attempt to load the index file.
            $indexFile =
$locator("gantry-config://{$name}/index.yaml");
            if ($indexFile || !$preset) {
                $index = static::loadIndex($name, true);
                $preset = $index['preset']['name'];
            }

            try {
                $layout = static::preset($preset);
            } catch (\Exception $e) {
                // Layout doesn't exist, do nothing.
            }
        } else {
            $layout = LayoutReader::read($filename);
        }

        return new static($name, $layout);
    }

    protected static function loadIndexFile($name)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Attempt to load the index file.
        $indexFile =
$locator("gantry-config://{$name}/index.yaml");
        if ($indexFile) {
            $file = CompiledYamlFile::instance($indexFile);
            $index = (array)$file->content();
            $file->free();
        } else {
            $index = [];
        }

        return $index;
    }

    /**
     * @param  string $name
     * @param  bool   $autoSave
     * @return array
     */
    public static function loadIndex($name, $autoSave = false)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $index = static::loadIndexFile($name);

        // Find out the currently used layout file.
        $layoutFile =
$locator("gantry-config://{$name}/layout.yaml");
        if (!$layoutFile) {
            /** @var Outlines $outlines */
            $outlines = $gantry['outlines'];

            $preset = isset($index['preset']['name']) ?
$index['preset']['name'] : $outlines->preset($name);
        }

        // Get timestamp for the layout file.
        $timestamp = $layoutFile ? filemtime($layoutFile) : 0;

        // If layout index file doesn't exist or is not up to date,
rebuild it.
        if (empty($index['timestamp']) ||
$index['timestamp'] != $timestamp ||
!isset($index['version']) || $index['version'] !=
static::VERSION) {
            $layout = isset($preset) ? new static($name,
static::preset($preset)) : static::instance($name);
            $layout->timestamp = $timestamp;

            if ($autoSave) {
                if (!$layout->timestamp) {
                    $layout->save();
                }
                $index = $layout->buildIndex();
                $layout->saveIndex($index);
            } else {
                $index = $layout->buildIndex();
            }
        }

        $index += [
            'name' => $name,
            'timestamp' => $timestamp,
            'preset' => [
                'name' => '',
                'image' =>
'gantry-admin://images/layouts/default.png'
            ],
            'positions' => [],
            'sections' => [],
            'inherit' => []
        ];

        return $index;
    }

    public function check(array $children = null)
    {
        if ($children === null) {
            $children = $this->items;
        }

        foreach ($children as $item) {
            if (!$item instanceof \stdClass) {
                throw new \RuntimeException('Invalid layout
element');
            }
            if (!isset($item->type)) {
                throw new \RuntimeException('Type missing');
            }
            if (!isset($item->subtype)) {
                throw new \RuntimeException('Subtype missing');
            }
            if (!isset($item->attributes)) {
                throw new \RuntimeException('Attributes
missing');
            }
            if (!is_object($item->attributes)) {
                throw new \RuntimeException('Attributes not
object');
            }
            if (isset($item->children)) {
                if (!is_array($item->children)) {
                    throw new \RuntimeException('Children not
array');
                }
                $this->check($item->children);
            }
        }
    }
}
classes/Gantry/Component/Layout/LayoutReader.php000064400000005011151166614520016001
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout;

use Gantry\Component\File\CompiledYamlFile;

/**
 * Read layout from yaml file.
 */
class LayoutReader
{
    /**
     * Get layout version.
     *
     * @param array $data
     * @return int
     */
    public static function version(array &$data)
    {
        if (isset($data['version'])) {
            return $data['version'];
        }

        return isset($data['children']) &&
is_array($data['children']) ? 0 : 1;
    }

    /**
     * Make layout from array data.
     *
     * @param array $data
     * @return array
     */
    public static function data(array $data)
    {
        $version = static::version($data);
        $reader = static::getClass($version, $data);
        $result = $reader->load();

        // Make sure that all preset values are set by defining defaults.
        $result['preset'] += [
            'name' => '',
            'image' =>
'gantry-admin://images/layouts/default.png'
        ];

        return $result;
    }

    /**
     * Read layout from yaml file and return parsed version of it.
     *
     * @param string $file
     * @return array
     */
    public static function read($file)
    {
        if (!$file) {
            return [];
        }

        $file = CompiledYamlFile::instance($file);
        $content = (array) $file->content();
        $file->free();

        return static::data($content);
    }

    /**
     * Convert layout into file format.
     *
     * @param array $preset
     * @param array $structure
     * @param int $version
     * @return mixed
     */
    public static function store(array $preset, array $structure, $version
= 2)
    {
        $reader = static::getClass($version);

        return $reader->store($preset, $structure);
    }

    /**
     * @param int $version
     * @param array $data
     * @return object
     */
    protected static function getClass($version, array $data = [])
    {
        $class =
"Gantry\\Component\\Layout\\Version\\Format{$version}";

        if (!class_exists($class)) {
            throw new \RuntimeException('Layout file cound not be
read: unsupported version {$version}.');
        }

        return new $class($data);

    }
}
classes/Gantry/Component/Layout/Version/Format0.php000064400000003601151166614520016341
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout\Version;

/**
 * Read layout from Layout Manager yaml file.
 */
class Format0 extends Format1
{
    /**
     * @return array
     */
    public function load()
    {
        $data = &$this->data;

        $preset = isset($data['preset']) &&
is_array($data['preset']) ? $data['preset'] : [];

        $result = is_array($data['children']) ?
$this->object($data['children']) : [];

        $invisible = [
            'offcanvas' =>
$this->parse('offcanvas', [], 0),
            'atoms' => $this->parse('atoms', [],
0)
        ];
        foreach ($result as $key => &$item) {
            if (isset($invisible[$item->type])) {
                $invisible[$item->type] = $item;
                unset($result[$key]);
            }
        }

        $result += $invisible;

        $result = array_values($result);

        return ['preset' => $preset] + $result;
    }

    protected function object(array $items, $container = true)
    {
        foreach ($items as &$item) {
            $item = (object) $item;

            if (isset($item->attributes) &&
(is_array($item->attributes) || is_object($item->attributes))) {
                $item->attributes = (object) $item->attributes;
            } else {
                $item->attributes = (object) [];
            }

            if (!empty($item->children) &&
is_array($item->children)) {
                $item->children = $this->object($item->children,
false);
            }

            $this->normalize($item, $container);
        }

        return $items;
    }
}
classes/Gantry/Component/Layout/Version/Format1.php000064400000021711151166614520016344
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout\Version;

/**
 * Read layout from simplified yaml file.
 */
class Format1
{
    protected $scopes = [0 => 'grid', 1 =>
'block'];

    protected $data;

    protected $keys = [];

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function load()
    {
        $data = &$this->data;

        // Check if we have preset.
        $preset = [];
        if (isset($data['preset']) &&
is_array($data['preset']) &&
isset($data['layout']) &&
is_array($data['layout'])) {
            $preset = $data['preset'];
            $data = $data['layout'];
        }

        // We have user entered file; let's build the layout.

        // Two last items are always offcanvas and atoms.
        $offcanvas = isset($data['offcanvas']) ?
$data['offcanvas'] : [];
        $atoms = isset($data['atoms']) ? $data['atoms']
: [];

        unset($data['offcanvas'], $data['atoms']);

        $data['offcanvas'] = $offcanvas;
        if ($atoms) {
            $data['atoms'] = $atoms;
        }

        $result = [];
        foreach ($data as $field => $params) {
            $child = $this->parse($field, (array) $params, 0);
            unset($child->size);

            $result[] = $child;
        }

        return ['preset' => $preset] + $result;
    }

    public function store(array $preset, array $structure)
    {
        return ['preset' => $preset, 'children'
=> $structure];
    }

    protected function normalize(&$item, $container = false)
    {
        if ($item->type === 'pagecontent') {
            // Update pagecontent to match the new standards.
            $item->type = 'system';
            if (!$item->subtype || $item->subtype ==
'pagecontent') {
                $item->subtype = 'content';
                $item->title = 'Page Content';
            } else {
                $item->subtype ='messages';
                $item->title = 'System Messages';
            }
        }

        if ($item->type === 'section') {
            // Update section to match the new standards.
            $section = strtolower($item->title);
            $item->id = $section;
            $item->subtype = (in_array($section, ['aside',
'nav', 'article', 'header',
'footer', 'main']) ? $section : 'section');
        } elseif ($item->type === 'offcanvas') {
            $item->id = $item->subtype = $item->type;
            unset ($item->attributes->name,
$item->attributes->boxed);
            return;
        } else {
            // Update all ids to match the new standards.
            $item->id = $this->id($item->type, $item->subtype);
        }

        if (!empty($item->attributes->extra)) {
            foreach ($item->attributes->extra as $i => $extra) {
                $v = reset($extra);
                $k = key($extra);
                if ($k === 'id') {
                    $item->id = preg_replace('/^g-/',
'', $v);
                    $item->attributes->id = $v;
                    unset ($item->attributes->extra[$i]);
                }
            }
            if (empty($item->attributes->extra)) {
                unset ($item->attributes->extra);
            }
        }

        $item->subtype = $item->subtype ?: $item->type;
        $item->layout = in_array($item->type, ['container',
'section', 'grid', 'block',
'offcanvas']);

        if (isset($item->attributes->boxed)) {
            // Boxed already set, just change boxed=0 to boxed=''
to use default settings.
            $item->attributes->boxed = $item->attributes->boxed
?: '';
            return;
        }

        if (!$container) {
            return;
        }

        // Update boxed model to match the new standards.
        if (isset($item->children) && count($item->children)
=== 1) {
            $child = reset($item->children);
            if ($item->type === 'container') {
                // Remove parent container only if the only child is a
section.
                if ($child->type === 'section') {
                    $child->attributes->boxed = 1;
                    $item = $child;
                }
                $item->attributes->boxed = '';
            } elseif ($child->type === 'container') {
                // Remove child container.
                $item->attributes->boxed = '';
                $item->children = $child->children;
            }
        }
    }

    /**
     * @param int|string $field
     * @param array $content
     * @param int $scope
     * @param bool|null $container
     * @return array
     */
    protected function parse($field, array $content, $scope, $container =
true)
    {
        if (is_numeric($field))  {
            // Row or block
            $type = $this->scopes[$scope];
            $result = (object) ['id' => null, 'type'
=> $type, 'subtype' => $type, 'layout' =>
true, 'attributes' => (object) []];
            $scope = ($scope + 1) % 2;
        } elseif (substr($field, 0, 9) == 'container') {
            // Container
            $type = 'container';
            $result = (object) ['id' => null, 'type'
=> $type, 'subtype' => $type, 'layout' =>
true, 'attributes' => (object) []];
            $id = substr($field, 10) ?: null;
            if ($id !== null) {
                $result->attributes->id = $id;
            }
        } else {
            // Section
            $list = explode(' ', $field, 2);
            $field = array_shift($list);
            $size = ((float) array_shift($list)) ?: null;
            $type = in_array($field, ['atoms',
'offcanvas']) ? $field : 'section';
            $subtype = in_array($field, ['aside',
'nav', 'article', 'header',
'footer', 'main']) ? $field : 'section';

            $result = (object) [
                'id' => null,
                'type' => $type,
                'subtype' => $subtype,
                'layout' => true,
                'title' => ucfirst($field),
                'attributes' => (object) ['id' =>
'g-' . $field]
            ];

            if ($size) {
                $result->size = $size;
            }
        }

        if (!empty($content)) {
            $result->children = [];
            foreach ($content as $child => $params) {
                if (is_array($params)) {
                    $child = $this->parse($child, $params, $scope,
false);
                } else {
                    $child = $this->resolve($params, $scope);
                }
                if (!empty($child->size)) {
                    $result->attributes->size = $child->size;
                }
                unset($child->size);
                $result->children[] = $child;
            }
        }

        $this->normalize($result, $container);

        return $result;
    }

    /**
     * @param string $field
     * @param int $scope
     * @return array
     */
    protected function resolve($field, $scope)
    {
        $list = explode(' ', $field, 2);
        $list2 = explode('-', array_shift($list), 2);
        $size = ((float) array_shift($list)) ?: null;
        $type = array_shift($list2);
        $subtype = array_shift($list2) ?: false;
        $title = ucfirst($subtype ?: $type);

        $attributes = new \stdClass;

        $attributes->enabled = 1;

        if ($subtype && $type === 'position') {
            $attributes->key = $subtype;
            $subtype = false;
        }

        $result = (object) ['id' => $this->id($type,
$subtype), 'title' => $title, 'type' => $type,
'subtype' => $subtype, 'attributes' =>
$attributes];
        $this->normalize($result);

        if ($scope > 1) {
            if ($size) {
                $result->attributes->size = $size;
            }
            return $result;
        }
        if ($scope <= 1) {
            $result = (object) ['id' =>
$this->id('block'), 'type' => 'block',
'subtype' => 'block', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
            if ($size) {
                $result->attributes->size = $size;
            }
        }
        if ($scope == 0) {
            $result = (object) ['id' =>
$this->id('grid'), 'type' => 'grid',
'subtype' => 'grid', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
        }

        return $result;
    }


    protected function id($type, $subtype = null)
    {
        if ($type === 'atoms') {
            return $type;
        }

        $result = [];
        if ($type !== 'particle' && $type !==
'atom') {
            $result[] = $type;
        }
        if ($subtype && $subtype !== $type) {
            $result[] = $subtype;
        }
        $key = implode('-', $result);

        while ($id = rand(1000, 9999)) {
            if (!isset($this->keys[$key][$id])) {
                break;
            }
        }

        $this->keys[$key][$id] = true;

        return $key . '-'. $id;
    }
}
classes/Gantry/Component/Layout/Version/Format2.php000064400000042706151166614520016354
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout\Version;

/**
 * Read layout from simplified yaml file.
 */
class Format2
{
    protected $scopes = [0 => 'grid', 1 =>
'block'];
    protected $sections = ['wrapper', 'container',
'section', 'grid', 'block',
'offcanvas'];
    protected $structures = ['div', 'section',
'aside', 'nav', 'article',
'header', 'footer', 'main'];

    protected $data;
    protected $structure;
    protected $content;
    protected $keys;

    /**
     * @param array $data
     */
    public function __construct(array $data = [])
    {
        $this->data = $data;
    }

    /**
     * @return array
     */
    public function load()
    {
        $data = &$this->data;

        // Parse layout.
        $result = [];
        foreach ($data['layout'] as $field => &$params) {
            if (!is_array($params)) {
                $params = [];
            }
            $child = $this->parse($field, $params);
            unset($child->size);

            $result[] = $child;
        }

        return ['preset' => $data['preset']] +
$result;
    }

    /**
     * @param array $preset
     * @param array $structure
     * @return array
     */
    public function store(array $preset, array $structure)
    {
        $this->structure = [];
        $this->content = [];

        $structure = ['children' =>
json_decode(json_encode($structure), true)];
        $structure = $this->build($structure);

        $result = [
            'version' => 2,
            'preset' => $preset,
            'layout' => $structure
        ];

        if ($this->structure) {
            $result['structure'] = $this->structure;
        }
        if ($this->content) {
            $result['content'] = $this->content;
        }

        return $result;
    }

    /**
     * @param int|string $field
     * @param array $content
     * @param int $scope
     * @param object $parent
     * @return array
     */
    protected function parse($field, array &$content, $scope = 0,
$parent = null)
    {
        if (is_numeric($field)) {
            // Row or block
            $result = (object)['id' =>
$this->id($this->scopes[$scope]), 'type' =>
$this->scopes[$scope], 'subtype' =>
$this->scopes[$scope], 'layout' => true,
'attributes' => (object)[]];
            $scope = ($scope + 1) % 2;

        } else {
            list ($type, $subtype, $id, $size, $section_id, $boxed) =
$this->parseSectionString($field);

            if ($type == 'grid') {
                $scope = 1;
            }
            if ($type == 'block') {
                $scope = 0;
            }

            // Build object.
            $result =
isset($this->data['structure'][$section_id]) ? (array)
$this->data['structure'][$section_id] : [];
            $result += [
                'id' => $section_id,
                'layout' => true,
                'type' => $type,
                'subtype' => $subtype,
                'title' => $this->getTitle($type, $subtype,
$id),
                'attributes' => []
            ];
            if (isset($boxed) &&
!isset($result['attributes']['boxed'])) {
                $result['attributes']['boxed'] =
$boxed;
            }
            if ($parent && $parent->type === 'block'
&& !empty($result['block'])) {
                $parent->attributes = (object)
($result['block'] + (array) $parent->attributes);
            }
            unset ($result['block']);

            $result = (object) $result;
            $result->attributes = (object) $result->attributes;
            if (isset($result->inherit)) {
                $result->inherit = (object) $result->inherit;
            }

            if ($size) {
                $result->size = $size;
            }
            if (($type === 'grid' || $type === 'block')
&& !isset($result->attributes->id)) {
                $result->attributes->id = $section_id;
            }
        }

        if (!empty($content)) {
            $result->children = [];
            foreach ($content as $child => &$params) {
                if (!$params && !is_array($params)) {
                    $params = [];
                }
                if (is_array($params)) {
                    $child = $this->parse($child, $params, $scope,
$result);
                } else {
                    $child = $this->resolve($params, $scope, $result);
                }
                if (!empty($child->size)) {
                    $result->attributes->size = $child->size;
                }
                unset($child->size);
                $result->children[] = $child;
            }
        }

        return $result;
    }

    /**
     * @param string $field
     * @param int $scope
     * @param object $parent
     * @return array
     */
    protected function resolve($field, $scope, $parent)
    {
        list ($type, $subtype, $id, $size, $content_id) =
$this->parseContentString($field);

        $title = $this->getTitle($type, $subtype, $id);

        $result = isset($this->data['content'][$content_id]) ?
(array) $this->data['content'][$content_id] : [];
        $result += ['id' => $this->id($type, $subtype,
$id), 'title' => $title, 'type' => $type,
'subtype' => $subtype, 'attributes' => []];

        $result['attributes'] = (object)
($result['attributes'] + ['enabled' => 1]);
        if (isset($result['inherit'])) {
            $result['inherit'] = (object)
$result['inherit'];
        }

        if (isset($result['block'])) {
            $block = $result['block'];
            unset ($result['block']);
        }

        $result = (object) $result;

        if ($type === 'position' &&
!isset($result->attributes->key) && !in_array($subtype,
['module', 'widget'])) {
            $result->attributes->key = $id;
        }
        if ($scope > 1) {
            if ($parent->type === 'block' &&
!empty($block)) {
                $parent->attributes = (object) ($block + (array)
$parent->attributes);
            }
            if ($size) {
                $result->attributes->size = $size;
            }
        }
        if ($scope <= 1) {
            $result = (object) ['id' =>
$this->id('block'), 'type' => 'block',
'subtype' => 'block', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
            if (!empty($block)) {
                $result->attributes = (object) $block;
            }
            if ($size) {
                $result->attributes->size = $size;
            }
        }
        if ($scope == 0) {
            $result = (object) ['id' =>
$this->id('grid'), 'type' => 'grid',
'subtype' => 'grid', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
        }

        return $result;
    }

    /**
     * @param array $content
     * @return array|null
     */
    protected function build(array &$content)
    {
        $result = [];
        $ctype = isset($content['type']) ?
$content['type'] : null;

        if (in_array($ctype, ['grid', 'block'])) {
            if (empty($content['attributes']['id']) ||
$content['attributes']['id'] ===
$content['id']) {
                unset ($content['attributes']['id']);
            }
        }
        if ($ctype === 'block') {
            if (empty($content['attributes']['extra']))
{
                unset
($content['attributes']['extra']);
            }
            if (empty($content['attributes']['fixed']))
{
                unset
($content['attributes']['fixed']);
            }
        }
        if ($ctype === 'section') {
            if (empty($content['attributes']['extra']))
{
                unset
($content['attributes']['extra']);
            }
        }

        if (!isset($content['children'])) {
            $content['children'] = [];
        }
        unset ($content['layout']);

        // Clean up all items for saving.
        foreach ($content['children'] as &$child) {
            $size = null;
            $id = $child['id'];
            $type = $child['type'];
            $subtype = $child['subtype'];
            $isSection = in_array($type, $this->sections);

            if (empty($child['inherit']['outline']) ||
empty($child['inherit']['include'])) {
                unset ($child['inherit']);
            } else {
                foreach ($child['inherit']['include']
as $include) {
                    switch ($include) {
                        case 'attributes':
                            unset($child['attributes']);
                            break;
                        case 'block':
                            if ($ctype === 'block') {
                                // Keep block size and fixed status.
                                $attributes =
!empty($content['attributes']) ? $content['attributes']
: [];
                                $content['attributes'] =
array_intersect_key($attributes, ['fixed' => 1,
'size' => 1]);
                            }
                            break;
                        case 'children':
                            $child['children'] = [];
                            break;
                    }
                }
            }

            if (!$isSection) {
                // Special handling for positions.
                if ($type === 'position') {
                    // TODO: we may want to simplify position id, but we
need to take into account multiple instances of the same position key.
/*
                    if (!$subtype || $subtype === 'position') {
                        $id = 'position-' .
(isset($child['attributes']['key']) ?
$child['attributes']['key'] : rand(1000,9999));
                        unset
($child['attributes']['key']);
                    }
*/
                    unset
($child['attributes']['title']);
                }

                $value = $id;
                if
(!empty($child['attributes']['enabled'])) {
                    unset
($child['attributes']['enabled']);
                }
            } else {
                // Recursively handle structure.
                $value = $this->build($child);
            }

            // Clean up defaults.
            if (empty($child['title']) ||
$child['title'] === 'Untitled' ||
$child['title'] === $this->getTitle($type, $subtype, $id)) {
                unset ($child['title']);
            }
            if (!$subtype || $subtype === $type) {
                unset ($child['subtype']);
            }

            // Remove id and children as we store data in flat structure
with id being the key.
            unset ($child['id'], $child['children']);

            if ($type === 'offcanvas' &&
isset($child['attributes']['name'])) {
                unset ($child['attributes']['name']);
            }

            if ($ctype === 'block') {
                // Embed size into array key/value.
                if
(isset($content['attributes']['size']) &&
$content['attributes']['size'] != 100) {
                    $size =
$content['attributes']['size'];
                }
                unset ($content['attributes']['size']);
                // Embed parent block.
                if (!empty($content['attributes'])) {
                    $child['block'] =
$content['attributes'];
                    unset ($content['attributes']);
                }
            }

            if (isset($child['attributes']['size'])) {
                if ($child['attributes']['size'] != 100
&& is_string($value)) {
                    $size =
$child['attributes']['size'];
                }
                unset ($child['attributes']['size']);
            }

            // Remove attributes if there aren't any.
            if (empty($child['attributes'])) {
                unset ($child['attributes']);
            }

            // Special handling for grid and block elements.
            if (in_array($type, ['grid', 'block'])
&& count($child) === 1 && isset($child['type']))
{
                $id = null;
            }

            // Check if type and subtype can be generated from the id.
            if ($subtype &&
(preg_match("/^{$type}-{$subtype}(-|$)/", $id))
                || (in_array($type, ['section',
'particle']) &&
preg_match("/^{$subtype}(-|$)/", $id))) {
                unset ($child['type'],
$child['subtype']);
            } elseif (preg_match("/^{$type}(-|$)/", $id)) {
                unset ($child['type']);
            }

            // Add item configuration if not empty.
            if ($id && !empty($child)) {
                if (!is_string($value)) {
                    $this->structure[$id] = $child;
                } else {
                    $this->content[$id] = $child;
                }
            }

            // Add item to the layout.
            if (!is_string($value)) {
                // Add structural item.
                if ($id) {
                    // Sections and other complex items.
                    $id =
isset($child['attributes']['boxed']) ?
"/{$id}/" : $id;
                    $result[trim("{$id} {$size}")] = $value;
                } elseif (!empty($value)) {
                    // Simple grid / block item.
                    $result[] = $value;
                }
            } else {
                // Add content item.
                $result[] = trim("{$value} {$size}");
            }
        }

        // TODO: maybe collapse grid as well?
        if ($ctype && in_array($ctype, ['block'])
&& count($result) <= 1 && key($result) === 0) {
            unset ($this->structure[$content['id']]);
            return reset($result) ?: null;
        }

        return $result;
    }

    /**
     * @param string $string
     * @return array
     */
    protected function parseSectionString($string)
    {
        // Extract: "[section-id] [size]".
        $list = explode(' ', $string, 2);
        $section_id = array_shift($list);
        $size = ((float) array_shift($list)) ?: null;

        // Extract slashes from "/[section-id]/".
        $boxedLeft = $section_id[0] === '/';
        $boxedRight = $section_id[strlen($section_id)-1] === '/';
        $boxed = ($boxedLeft && $boxedRight ? '' :
($boxedLeft ? '1' : ($boxedRight ? '0' : null)));
        $section_id = trim($section_id, '/');

        // Extract section id if it exists: "[section]-[id]".
        $list = explode('-', $section_id, 2);

        // Get section and its type.
        $section = reset($list);
        $type = (in_array($section, $this->sections)) ? $section :
'section';
        $subtype = ($type !== 'section' || in_array($section,
$this->structures)) ? $section : 'section';

        // Extract id.
        if ($type == 'section' && in_array($section,
$this->structures)) {
            $id = array_pop($list);
        } else {
            $id = $section_id;
        }

        return [$type, $subtype, $id, $size, $section_id, $boxed];
    }

    /**
     * @param string $string
     * @return array
     */
    protected function parseContentString($string)
    {
        // Extract: "[type-subtype] [size]".
        $list = explode(' ', $string, 2);
        $content_id = array_shift($list);
        $size = ((float) array_shift($list)) ?: null;

        // Extract sub-type if it exists:
"[type]-[subtype]-[id]".
        $list = explode('-', $content_id);

        // Get type, subtype and id.
        $type = reset($list);
        $test = end($list);
        $id = ((string)(int) $test === (string) $test) ? array_pop($list) :
null;
        if (in_array($type, ['system', 'position',
'particle', 'spacer'])) {
            array_shift($list);
        } else {
            $type = 'particle';
        }
        $subtype = implode('-', $list);

        if ($type === 'position' && !in_array($subtype,
['module', 'widget'])) {
            $id = ($subtype ?: $type) . ($id !== null ? "-{$id}"
: '');
            $subtype = 'position';
        }

        return [$type, $subtype ?: $type, $id, $size, $content_id];
    }

    /**
     * @param string $type
     * @param string $subtype
     * @param string $id
     * @return string
     */
    protected function getTitle($type, $subtype, $id)
    {
        if (in_array($type, $this->sections)) {
            if ($type === 'offcanvas') {
                return 'Offcanvas';
            }

            if ($type === 'grid' || $type === 'block')
{
                return null;
            }

            return ucfirst((string)(int) $id === (string) $id ? ($subtype
?: $type) . "-{$id}" : $id);
        }

        if ($type === 'position' && !in_array($subtype,
['module', 'widget'])) {
            return
ucfirst(preg_replace('/^position-(.*?[a-z])/ui', '\1',
$id));
        }

        if ($type === 'system') {
            if ($subtype === 'messages') {
                return 'System Messages';
            }
            if ($subtype === 'content') {
                return 'Page Content';
            }
        }

        return ucfirst($subtype ?: $type);
    }

    /**
     * @param string $type
     * @param string $subtype
     * @param string $id
     * @return string
     */
    protected function id($type, $subtype = null, $id = null)
    {
        $result = [];
        if ($type !== 'particle') {
            $result[] = $type;
        }
        if ($subtype && $subtype !== $type) {
            $result[] = $subtype;
        }
        $key = implode('-', $result);

        if (!$id || isset($this->keys[$key][$id])) {
            while ($id = rand(1000, 9999)) {
                if (!isset($this->keys[$key][$id])) {
                    break;
                }
            }
        }

        $this->keys[$key][$id] = true;

        return $key . '-'. $id;
    }
}
classes/Gantry/Component/Menu/AbstractMenu.php000064400000026431151166614520015431
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Menu;

use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Gantry\GantryTrait;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class AbstractMenu implements \ArrayAccess, \Iterator, \Countable
{
    use GantryTrait, ArrayAccessWithGetters, Iterator, Export, Countable;

    protected $default;
    protected $base;
    protected $active;
    protected $params;
    protected $override = false;
    protected $config;

    /**
     * @var array|Item[]
     */
    protected $items;

    /**
     * @var Config|null
     */
    protected $pathMap;

    protected $defaults = [
        'menu' => '',
        'base' => '/',
        'startLevel' => 1,
        'maxLevels' => 0,
        'showAllChildren' => true,
        'highlightAlias' => true,
        'highlightParentAlias' => true
    ];

    abstract public function __construct();

    /**
     * Return list of menus.
     *
     * @return array
     */
    abstract public function getMenus();


    /**
     * Return default menu.
     *
     * @return string
     */
    public function getDefaultMenuName()
    {
        return null;
    }

    /**
     * Returns true if the platform implements a Default menu.
     *
     * @return boolean
     */
    public function hasDefaultMenu()
    {
        return false;
    }

    /**
     * Return active menu.
     *
     * @return string
     */
    public function getActiveMenuName()
    {
        return null;
    }

    /**
     * Returns true if the platform implements an Active menu.
     *
     * @return boolean
     */
    public function hasActiveMenu()
    {
        return false;
    }

    /**
     * @param array $params
     * @param Config $menu
     * @return AbstractMenu
     */
    public function instance(array $params = [], Config $menu = null)
    {
        $params = $params + $this->defaults;

        $menus = $this->getMenus();

        if (!$menus) {
            throw new \RuntimeException('Site does not have
menus', 404);
        }
        if (empty($params['menu'])) {
            $params['menu'] = $this->getDefaultMenuName();
            if (!$params['menu'] &&
!empty($params['admin'])) {
                // In admin just select the first menu if there isn't
default menu to be selected.
                $params['menu'] = reset($menus);
            };
        } elseif ($params['menu'] == '-active-') {
            $params['menu'] = $this->getActiveMenuName();
        }
        if (!$params['menu']) {
            throw new \RuntimeException('No menu selected', 404);
        }
        if (!in_array($params['menu'], $menus)) {
            throw new \RuntimeException('Menu not found', 404);
        }

        $instance = clone $this;
        $instance->params = $params;

        if ($menu) {
            $instance->override = true;
            $instance->config = $menu;
        } else {
            $instance->config = null;
        }

        $config = $instance->config();
        $items = isset($config['items']) ?
$config['items'] : [];

        // Create menu structure.
        $instance->init($params);

        // Get menu items from the system (if not specified otherwise).
        if ($config->get('settings.type') !==
'custom') {
            $instance->getList($params, $items);
        }

        // Add custom menu items.
        $instance->addCustom($params, $items);

        // Sort menu items.
        $instance->sortAll();

        return $instance;
    }

    /**
     * Get menu configuration.
     *
     * @return Config
     */
    public function config()
    {
        if (!$this->config) {
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];

            $menu = $this->params['menu'];

            $file =
CompiledYamlFile::instance($locator("gantry-config://menu/{$menu}.yaml"));
            $this->config = new Config($file->content());
            $this->config->def('settings.title',
ucfirst($menu));
            $file->free();
        }

        return $this->config;
    }

    public function name()
    {
        return $this->params['menu'];
    }

    public function root()
    {
        return $this->offsetGet('');
    }

    public function ordering()
    {
        $list = [];
        foreach ($this->items as $name => $item) {
            $groups = $item->groups();
            if (count($groups) == 1 && empty($groups[0])) {
                continue;
            }

            $list[$name] = [];
            foreach ($groups as $col => $children) {
                $list[$name][$col] = [];
                foreach ($children as $child) {
                    $list[$name][$col][] = $child->path;
                }
            }
        }

        return $list;
    }

    public function items($withdefaults = true)
    {
        $list = [];
        foreach ($this->items as $key => $item) {
            if ($key !== '') {
                $list[$item->path] = $item->toArray($withdefaults);
            }
        }

        return $list;
    }

    public function settings()
    {
        return (array) $this->config()->get('settings');
    }

    /**
     * @return object
     */
    public function getBase()
    {
        return $this->offsetGet($this->base);
    }

    /**
     * @return object
     */
    public function getDefault()
    {
        return $this->offsetGet($this->default);
    }

    /**
     * @return object
     */
    public function getActive()
    {
        return $this->offsetGet($this->active);
    }

    /**
     * @return string|null
     */
    public function getCacheId()
    {
        return $this->active ?: '-inactive-';
    }

    public function isActive($item)
    {
        $active = $this->getActive();

        if ($active && $item && ($active->path ===
$item->path || strpos($active->path, $item->path . '/')
=== 0)) {
            return true;
        }

        return false;
    }

    public function isCurrent($item)
    {
        $active = $this->getActive();

        return $item && $active && $item->path ===
$active->path;
    }

    public function init(&$params)
    {
        $this->items = ['' => new Item($this, '',
['layout' => 'horizontal'])];
    }

    public function add(Item $item)
    {
        $this->items[$item->path] = $item;

        // If parent exists, assign menu item to its parent; otherwise
ignore menu item.
        if (isset($this->items[$item->parent_id])) {
            $this->items[$item->parent_id]->addChild($item);
        } elseif (!$this->items['']->count()) {
            $this->items[$item->parent_id] =
$this->items[''];
            $this->items[$item->parent_id]->addChild($item);
        }

        return $this;
    }

    /**
     * Get menu items from the platform.
     *
     * @param int $levels
     * @return array
     */
    abstract protected function getItemsFromPlatform($levels);

    /**
     * Get base menu item.
     *
     * If itemid is not specified or does not exist, return active menu
item.
     * If there is no active menu item, fall back to home page for the
current language.
     * If there is no home page, return null.
     *
     * @param   string  $path
     *
     * @return  string
     */
    abstract protected function calcBase($path);

    /**
     * Get a list of the menu items.
     *
     * @param  array  $params
     * @param  array  $items
     */
    abstract public function getList(array $params, array $items);

    /**
     * Add custom menu items.
     *
     * @param  array  $params
     * @param array $items
     */
    public function addCustom(array $params, array $items)
    {
        $start   = $params['startLevel'];
        $max     = $params['maxLevels'];
        $end     = $max ? $start + $max - 1 : 0;

        $config = $this->config();
        $type = $config->get('settings.type');

        // Add custom menu elements.
        foreach ($items as $route => $item) {
            if ($type !== 'custom' &&
(!isset($item['type']) || $item['type'] !==
'particle')) {
                continue;
            }

            $tree = explode('/', $route);
            $parentTree = $tree;
            array_pop($parentTree);

            // Enabled state should equal particle setting.
            $item['enabled'] =
!isset($item['options']['particle']['enabled'])
||
!empty($item['options']['particle']['enabled']);
            $item['level'] = $level = count($tree);
            $item['parent_id'] = implode('/',
$parentTree);
            if (($start && $start > $level)
                || ($end && $level > $end)
                // TODO: Improve. In the mean time Item::add() handles this
part.
                // || ($start > 1 && !in_array($tree[$start -
2], $tree))
            ) {
                continue;
            }
            $item = new Item($this, $route, $item);
            $this->add($item);
        }
    }

    /**
     * @param array $ordering
     * @param string $path
     * @param array $map
     */
    public function sortAll(array $ordering = null, $path = '',
$map = null)
    {
        if ($ordering === null) {
            $config = $this->config();
            $ordering = $config['ordering'] ?
$config['ordering'] : [];
        }

        if (!isset($this->items[$path]) ||
!$this->items[$path]->hasChildren()) {
            return;
        }

        if ($map === null) {
            $map = $this->pathMap ? $this->pathMap->toArray() :
[];
        }

        $order = [];
        $newMap = [];
        $item = $this->items[$path];
        if ($this->isAssoc($ordering)) {
            foreach ($ordering as $key => $value) {
                if ($map) {
                    $newMap = isset($map[$key]['children']) ?
$map[$key]['children'] : [];
                    $key = isset($map[$key]['path']) ?
basename($map[$key]['path']) : $key;
                    $order[$key] = $value;
                }

                if (is_array($value)) {
                    $this->sortAll($value, $path ? $path . '/'
. $key : $key, $newMap);
                }
            }

            $item->sortChildren($order ?: $ordering);
        } else {
            foreach ($ordering as $i => $group) {
                foreach ($group as $key => $value) {
                    if ($map) {
                        $newMap = isset($map[$key]['children']) ?
$map[$key]['children'] : [];
                        $key = isset($map[$key]['path']) ?
basename($map[$key]['path']) : $key;
                        $order[$i][$key] = $value;
                    }

                    if (is_array($value)) {
                        $this->sortAll($value, $path ? $path .
'/' . $key : $key, $newMap);
                    }
                }
            }

            $item->groupChildren($order ?: $ordering);
        }

    }

    protected function isAssoc(array $array)
    {
        return (array_values($array) !== $array);
    }
}
classes/Gantry/Component/Menu/Item.php000064400000023274151166614520013741
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Menu;

use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
use RocketTheme\Toolbox\ArrayTraits\Export;

/**
 * @property string $id
 * @property string $type
 * @property string $path
 * @property string $alias
 * @property string $title
 * @property string $link
 * @property string $parent_id
 * @property string $layout
 * @property int $browserNav
 * @property bool $menu_text
 * @property bool $visible
 * @property int $group
 * @property int $level
 */
class Item implements \ArrayAccess, \Iterator, \Serializable, \Countable
{
    use ArrayAccessWithGetters, Export;

    const VERSION = 1;

    protected $items;
    protected $menu;
    protected $groups = [];
    protected $children = [];
    protected $url;

    protected static $defaults = [
        'id' => 0,
        'type' => 'link',
        'path' => null,
        'alias' => null,
        'title' => null,
        'link' => null,
        'parent_id' => null,
        'layout' => 'list',
        'target' => '_self',
        'dropdown' => '',
        'icon' => '',
        'image' => '',
        'subtitle' => '',
        'hash' => '',
        'class' => '',
        'icon_only' => false,
        'enabled' => true,
        'visible' => true,
        'group' => 0,
        'columns' => [],
        'level' => 0,
        'link_title' => '',
        'anchor_class' => ''
    ];

    public function __construct(AbstractMenu $menu, $name, array $item =
[])
    {
        $this->menu = $menu;

        $tree = explode('/', $name);
        $alias = array_pop($tree);
        $parent = implode('/', $tree);

        // As we always calculate parent (it can change), prevent old one
from being inserted.
        unset($item['parent_id']);

        $this->items = $item + [
            'id' => preg_replace('|[^a-z0-9]|i',
'-', $name) ?: 'root',
            'path' => $name,
            'alias' => $alias,
            'title' => ucfirst($alias),
            'link' => $name,
            'parent_id' => $parent != '.' ? $parent
: '',
        ] + static::$defaults;
    }

    public function getDropdown()
    {
        if (!$this->items['dropdown']) {
            return count($this->groups()) > 1 ? 'fullwidth'
: 'standard';
        }

        return $this->items['dropdown'];
    }

    public function serialize()
    {
        // FIXME: need to create collection class to gather the sibling
data.
        return serialize([
            'version' => static::VERSION,
            'items' => $this->items,
            'groups' => $this->groups,
            'children' => $this->children,
            'url' => $this->url
        ]);
    }

    public function unserialize($serialized)
    {
        // FIXME: need to create collection class to gather the sibling
data.
        $data = unserialize($serialized);

        if (!isset($data['version']) &&
$data['version'] === static::VERSION) {
            throw new \UnexpectedValueException('Serialized data is
not valid');
        }

        $this->items = $data['items'];
        $this->groups =  $data['groups'];
        $this->children = $data['children'];
        $this->url = $data['url'];
    }

    /**
     * @param  string|null|bool $url
     * @return string
     */
    public function url($url = false)
    {
        if ($url !== false) {
            $this->url = $url;
        }
        return $this->url;
    }

    /**
     * @return AbstractMenu
     * @deprecated Need to break relationship to the menu and use a
collection instead.
     */
    protected function menu()
    {
        return $this->menu;
    }

    /**
     * @return Item
     */
    public function parent()
    {
        return $this->menu()[$this->items['parent_id']];
    }

    public function columnWidth($column)
    {
        if (isset($this->items['columns'][$column])) {
            return $this->items['columns'][$column];
        }

        return 100 / count($this->groups());
    }

    public function groups()
    {
        if ($this->groups) {
            $list = [];
            foreach ($this->groups as $i => $group) {
                $list[$i] = [];
                foreach ($group as $path) {
                    $list[$i][] = $this->menu()[$path];
                }
            }
            return $list;
        }
        return [$this->children()];
    }

    public function children()
    {
        $list = [];
        foreach ($this as $child) {
            $list[] = $child;
        }
        return $list;
    }

    public function hasChildren()
    {
        return !empty($this->children);
    }

    public function getGroup($i)
    {
        $groups = $this->groups();
        $i = (int) $i;

        return isset($groups[$i]) ? $groups[$i] : [];
    }

    public function update(array $data)
    {
        $this->items = array_replace($this->items, $data);

        return $this;
    }

    public function addChild(Item $child)
    {
        $child->level = $this->level + 1;
        $child->parent_id = $this->path;
        $this->children[$child->alias] = $child->path;

        return $this;
    }

    public function removeChild(Item $child)
    {
        unset($this->children[$child->alias]);

        return $this;
    }

    public function sortChildren($ordering)
    {
        // Array with keys that point to the items.
        $children =& $this->children;

        if ($children) {
            if (is_array($ordering)) {
                // Remove extra items from ordering and reorder.
                $children = array_replace(array_intersect_key($ordering,
$children), $children);
            } else {
                switch ((string) $ordering) {
                    case 'abc':
                        // Alphabetical ordering.
                        ksort($children, SORT_NATURAL);
                        break;
                    case 'cba':
                        // Reversed alphabetical ordering.
                        krsort($children, SORT_NATURAL);
                        break;
                }
            }
        }

        return $this;
    }


    public function reverse()
    {
        array_reverse($this->children, true);
        array_reverse($this->groups, true);

        return $this;
    }

    public function groupChildren(array $groups)
    {
        // Array with keys that point to the items.
        $children =& $this->children;

        if ($children) {
            $menu = $this->menu();
            $ordered = [];

            // Create empty groups.
            $this->groups = array_fill(0, max(1,
count($this->items['columns'])), []);

            foreach ($groups as $i => $ordering) {
                if (!is_array($ordering)) {
                    continue;
                }

                // Get the items for this group with proper ordering.
                $group = array_replace(
                    array_intersect_key($ordering, $children),
array_intersect_key($children, $ordering)
                );

                // Assign each menu items to the group.
                $group = array_map(
                    function($value) use ($i, $menu) {
                        $item = $menu[$value];
                        $item->group = $i;
                        return $value;
                    },
                    $group
                );

                // Update remaining children.
                $children = array_diff_key($children, $ordering);

                // Build child ordering.
                $ordered += $group;

                // Add items to the current group.
                $this->groups[$i] = $group;
            }

            if ($children) {
                // Add leftover children to the ordered list and to the
first group.
                $ordered += $children;
                $this->groups[0] += $children;
            }

            // Reorder children by their groups.
            $children = $ordered;
        }

        return $this;
    }

    // Implements \Iterator

    /**
     * Returns the current child.
     *
     * @return Item
     */
    public function current()
    {
        return $this->menu()[current($this->children)];
    }

    /**
     * Returns the key of the current child.
     *
     * @return mixed  Returns scalar on success, or NULL on failure.
     */
    public function key()
    {
        return key($this->children);
    }

    /**
     * Moves the current position to the next child.
     *
     * @return void
     */
    public function next()
    {
        next($this->children);
    }

    /**
     * Rewinds back to the first child.
     *
     * @return void
     */
    public function rewind()
    {
        reset($this->children);
    }

    /**
     * Count number of children.
     *
     * @return int
     */
    public function count()
    {
        return count($this->children);
    }

    /**
     * This method is called after Iterator::rewind() and Iterator::next()
to check if the current position is valid.
     *
     * @return bool  Returns TRUE on success or FALSE on failure.
     */
    public function valid()
    {
        return key($this->children) !== null;
    }

    /**
     * Convert object into an array.
     *
     * @return array
     */
    public function toArray($withDefaults = true)
    {
        $items = $this->items;

        if (!$withDefaults) {
            foreach (static::$defaults as $key => $value) {
                if ($items[$key] === $value) {
                    unset($items[$key]);
                }
            }
        }

        return $items;
    }
}
classes/Gantry/Component/Outline/OutlineCollection.php000064400000046107151166614520017211
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Outline;

use FilesystemIterator;
use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Layout\Layout;
use Gantry\Framework\Atoms;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class OutlineCollection extends Collection
{
    /**
     * @var Container
     */
    protected $container;

    /**
     * @var string
     */
    protected $path;

    /**
     * @param Container $container
     * @param array $items
     */
    public function __construct(Container $container, $items = [])
    {
        $this->container = $container;
        $this->items = $items;
    }

    /**
     * @param string $id
     * @return string|null
     */
    public function name($id)
    {
        return isset($this->items[$id]) ? $this->items[$id] : null;
    }

    /**
     * @param string $id
     * @return string
     */
    public function title($id)
    {
        return isset($this->items[$id]) ? $this->items[$id] : $id;
    }


    public function all()
    {
        return $this;
    }

    public function system()
    {
        foreach ($this->items as $key => $item) {
            if (substr($key, 0, 1) !== '_') {
                unset($this->items[$key]);
            }
        }

        return $this;
    }

    public function user()
    {
        foreach ($this->items as $key => $item) {
            if (substr($key, 0, 1) === '_' || $key ==
'default') {
                unset($this->items[$key]);
            }
        }

        return $this;
    }

    public function filter(array $include = null)
    {
        if ($include !== null) {
            foreach ($this->items as $key => $item) {
                if (!in_array($key, $include)) {
                    unset($this->items[$key]);
                }
            }
        }

        return $this;
    }

    /**
     * Returns list of all positions defined in all outlines.
     *
     * @return array
     */
    public function positions()
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);

                $list += $index['positions'];
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
            }
        }

        return $list;
    }

    /**
     * @param string $section
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlinesWithSection($section, $includeInherited =
true)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
                continue;
            }

            if (isset($index['sections'][$section])) {
                if (!$includeInherited) {
                    foreach ($index['inherit'] as $outline =>
$items) {
                        if (is_array($items) && in_array($section,
$items)) {
                            continue 2;
                        }
                    }
                }
                $list[$name] = $title;
            }
        }

        return $list;
    }

    /**
     * @param string $particle
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlinesWithParticle($particle, $includeInherited =
true)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
                continue;
            }

            if (isset($index['particles'][$particle])) {
                $ids = $index['particles'][$particle];
                if (!$includeInherited &&
!empty($index['inherit'])) {
                    foreach ($index['inherit'] as $items) {
                        foreach ((array) $items as $id => $inheritId) {
                            unset($ids[$id]);
                        }
                    }
                }
                if ($ids) {
                    $list[$name] = $title;
                }
            }
        }

        return $list;
    }

    /**
     * @param string $type
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlinesWithAtom($type, $includeInherited = true)
    {
        $list = [];

        foreach ($this->items as $name => $title) {
            $file =
CompiledYamlFile::instance("gantry-theme://config/{$name}/page/head.yaml");
            $index = $file->content();
            $file->free();
            if (isset($index['atoms'])) {
                foreach ($index['atoms'] as $atom) {
                    if (!empty($atom['id']) &&
$atom['type'] === $type && ($includeInherited ||
empty($atom['inherit']))) {
                        $list[$name] = $title;
                    }
                }
            }
        }

        return $list;
    }

    /**
     * @param string $particle
     * @param bool $includeInherited
     * @return array
     */
    public function getAllParticleInstances($particle, $includeInherited =
true)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            $list += $this->getParticleInstances($name, $particle,
$includeInherited);
        }

        return $list;
    }

    /**
     * @param string $outline
     * @param string $particle
     * @param bool $includeInherited
     * @return array
     */
    public function getParticleInstances($outline, $particle,
$includeInherited = true)
    {
        $list = [];
        $index = Layout::index($outline);
        if (isset($index['particles'][$particle])) {
            $list = $index['particles'][$particle];
            if (!$includeInherited &&
!empty($index['inherit'])) {
                foreach ($index['inherit'] as $items) {
                    foreach ((array) $items as $id => $inheritId) {
                        unset($list[$id]);
                    }
                }
            }
        }

        $layout = Layout::instance($outline);

        foreach ($list as $id => $title) {
            $item = clone $layout->find($id);
            $block = $layout->block($id);
            $item->block = isset($block->attributes) ?
$block->attributes : new \stdClass();
            $list[$id] = $item;
        }

        return $list;
    }


    /**
     * @param string $outline
     * @param string $type
     * @param bool $includeInherited
     * @return array
     */
    public function getAtomInstances($outline, $type, $includeInherited =
true)
    {
        $list = [];

        $file =
CompiledYamlFile::instance("gantry-theme://config/{$outline}/page/head.yaml");
        $head = $file->content();
        $file->free();
        if (isset($head['atoms'])) {
            foreach ($head['atoms'] as $atom) {
                if (!empty($atom['id']) &&
$atom['type'] === $type && ($includeInherited ||
empty($atom['inherit']['outline']))) {
                    $list[$atom['id']] = (object) $atom;
                }
            }
        }

        return $list;
    }

    /**
     * Return list of outlines which are inheriting the specified atom.
     *
     * @param string $outline
     * @param string $id
     * @return array
     */
    public function getInheritingOutlinesWithAtom($outline, $id = null)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            $file =
CompiledYamlFile::instance("gantry-theme://config/{$name}/page/head.yaml");
            $head = $file->content();
            $file->free();

            if (isset($head['atoms'])) {
                foreach ($head['atoms'] as $atom) {
                    if
(!empty($atom['inherit']['outline']) &&
$atom['inherit']['outline'] == $outline &&
(!$id || $atom['inherit']['atom'] == $id)) {
                        $list[$name] = $title;
                    }
                }
            }
        }

        return $list;
    }

    /**
     * Return list of outlines which are inheriting the specified outline.
     *
     * You can additionally pass section or particle id to filter the
results for only that type.
     *
     * @param string $outline
     * @param string|array $id
     * @return array
     */
    public function getInheritingOutlines($outline, $id = null)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
                continue;
            }

            if (!empty($index['inherit'][$outline]) &&
(!$id || array_intersect((array) $id,
$index['inherit'][$outline]))) {
                $list[$name] = $title;
            }
        }

        return $list;
    }

    /**
     * Return list of outlines inherited by the specified outline.
     *
     * You can additionally pass section or particle id to filter the
results for only that type.
     *
     * @param string $outline
     * @param string $id
     * @return array
     */
    public function getInheritedOutlines($outline, $id = null)
    {
        try {
            $index = Layout::index($outline);
        } catch (\Exception $e) {
            // Layout cannot be read. We will just return nothing instead
of throwing an exception.
            return [];
        }

        $list = [];
        foreach ($index['inherit'] as $name => $inherited) {
            if (!$id || array_intersect_key((array) $id, $inherited[$id]))
{
                $list[$name] = isset($this->items[$name]) ?
$this->items[$name] : $name;
            }
        }

        return $list;
    }

    /**
     * @param int|string $id
     * @return int|string
     */
    public function preset($id)
    {
        return $id;
    }

    /**
     * @param int|string $id
     * @return Layout
     */
    public function layout($id)
    {
        return Layout::load($id);
    }

    /**
     * @param int|string $id
     * @return array
     */
    public function layoutPreset($id)
    {
        $layout = Layout::load($id);
        $preset = $layout->preset;

        unset($layout);

        return $preset;
    }

    /**
     * @param string $path
     * @return $this
     * @throws \RuntimeException
     */
    public function load($path = 'gantry-config://')
    {
        $this->path = $path;

        $iterator = $this->getFilesystemIterator($path);

        $files = [];
        /** @var FilesystemIterator $info */
        foreach ($iterator as $name => $info) {
            if (!$info->isDir() || $name[0] == '.' ||
!is_file($info->getPathname() . '/index.yaml')) {
                continue;
            }
            $files[$name] = ucwords(trim(preg_replace(['|_|',
'|/|'], [' ', ' / '], $name)));
        }

        unset($files['default']);
        unset($files['menu']);

        asort($files);

        $this->items = $this->addDefaults($files);

        return $this;
    }

    /**
     * @param string|null $id
     * @param string $title
     * @param string|array $preset
     * @return string
     * @throws \RuntimeException
     */
    public function create($id, $title = null, $preset = null)
    {
        $title = $title ?: 'Untitled';
        $name = ltrim(strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $id ?: $title)), '_');

        if (!$name) {
            throw new \RuntimeException("Outline needs a name",
400);
        }

        if ($name === 'default') {
            throw new \RuntimeException("Outline cannot use reserved
name '{$name}'", 400);
        }

        $name = $this->findFreeName($name);
        if (!$id) {
            $title = ucwords(trim(preg_replace(['|_|',
'|/|'], [' ', ' / '], $name)));
        }

        if (!is_array($preset)) {
            // Load preset.
            $preset = Layout::preset($preset ?: 'default');
        }

        // Create layout and index for the new layout.
        $layout = new Layout($name, $preset);
        $layout->save()->saveIndex();

        $this->items[$name] = $title;

        return $name;
    }

    /**
     * @param string $id
     * @param string $title
     * @param bool $inherit
     * @return string
     * @throws \RuntimeException
     */
    public function duplicate($id, $title = null, $inherit = false)
    {
        if (!$this->canDuplicate($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be duplicated", 400);
        }

        $layout = Layout::load($id);
        if ($inherit) {
            $layout->inheritAll()->clean();
        }

        $new = $this->create(null, $title, $layout->toArray() +
['preset' => $layout->preset]);

        if ($id === 'default') {
            // For Base Outline we're done.
            return $new;
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        $path =
$locator->findResource("{$this->path}/{$id}");
        if (!$path) {
            // Nothing to copy.
            return $new;
        }

        $newPath =
$locator->findResource("{$this->path}/{$new}", true, true);

        try {
            // Copy everything over except index, layout and assignments.
            Folder::copy($path, $newPath,
'/^(index|layout|assignments)\..*$/');
        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Duplicating Outline
failed: ', $e->getMessage()), 500, $e);
        }

        return $new;
    }

    /**
     * @param string $id
     * @param string $title
     * @return string
     * @throws \RuntimeException
     */
    public function rename($id, $title)
    {
        if (!$this->canDelete($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be renamed", 400);
        }

        $gantry = $this->container;

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $path =
$locator->findResource("{$this->path}/{$id}", true, true);
        if (!$path || !is_dir($path)) {
            throw new \RuntimeException('Outline not found',
404);
        }

        $folder = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $title));

        if ($folder === 'default' || $folder[0] ===
'_') {
            throw new \RuntimeException("Outline cannot use reserved
name '{$folder}'", 400);
        }

        $newPath =
$locator->findResource("{$this->path}/{$folder}", true,
true);
        if (is_dir($newPath)) {
            throw new \RuntimeException("Outline '$id'
already exists.", 400);
        }

        try {
            foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
                $this->layout($outline)->updateInheritance($id,
$folder)->save()->saveIndex();
            }
            foreach ($this->getInheritingOutlinesWithAtom($id) as
$outline => $title) {
                Atoms::instance($outline)->updateInheritance($id,
$folder)->save();
            }

            Folder::move($path, $newPath);

        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Renaming Outline
failed: %s', $e->getMessage()), 500, $e);
        }

        $this->items[$id] = $title;

        return $folder;
    }

    /**
     * @param string $id
     * @throws \RuntimeException
     */
    public function delete($id)
    {
        if (!$this->canDelete($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be deleted", 400);
        }

        $gantry = $this->container;

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $path =
$locator->findResource("{$this->path}/{$id}", true, true);
        if (!is_dir($path)) {
            throw new \RuntimeException('Outline not found',
404);
        }

        foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
           
$this->layout($outline)->updateInheritance($id)->save()->saveIndex();
        }
        foreach ($this->getInheritingOutlinesWithAtom($id) as $outline
=> $title) {
           
Atoms::instance($outline)->updateInheritance($id)->save();
        }

        if (file_exists($path)) {
            Folder::delete($path);
        }

        unset($this->items[$id]);
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function canDuplicate($id)
    {
        if (!isset($this->items[$id])) {
            return false;
        }

        return true;
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function canDelete($id)
    {
        if (!$id || $id[0] === '_' || $id ===
'default') {
            return false;
        }

        return true;
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function isDefault($id)
    {
        return $id === 'default';
    }

    /**
     * @param array $outlines
     * @return array
     */
    protected function addDefaults(array $outlines)
    {
        return [
            'default' => 'Base Outline',
            '_body_only' => 'Body Only',
            '_error' => 'Error',
            '_offline' => 'Offline'
        ] + $outlines;
    }

    /**
     * Find unused name with number appended to it when duplicating an
outline.
     *
     * @param string $id
     * @return string
     */
    protected function findFreeName($id)
    {
        if (!isset($this->items[$id])) {
            return $id;
        }

        $name = $id;
        $count = 0;
        if (preg_match('|^(?:_)?(.*?)(?:_(\d+))?$|ui', $id,
$matches)) {
            $matches += ['', '', ''];
            list (, $name, $count) = $matches;
        }

        $count = max(1, $count);

        do {
            $count++;
        } while (isset($this->items["{$name}_{$count}"]));

        return "{$name}_{$count}";
    }

    protected function getFilesystemIterator($path)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        $custom = $locator->findResource($path, true, true);
        if (is_dir($custom)) {
            /** @var FilesystemIterator $iterator */
            $iterator = new FilesystemIterator(
                $custom,
                FilesystemIterator::CURRENT_AS_SELF |
FilesystemIterator::KEY_AS_FILENAME |
                FilesystemIterator::UNIX_PATHS |
FilesystemIterator::SKIP_DOTS
            );
        } else {
            /** @var UniformResourceIterator $iterator */
            $iterator = $locator->getIterator(
                $path,
                UniformResourceIterator::CURRENT_AS_SELF |
UniformResourceIterator::KEY_AS_FILENAME |
                UniformResourceIterator::UNIX_PATHS |
UniformResourceIterator::SKIP_DOTS
            );
        }

        return $iterator;
    }
}
classes/Gantry/Component/Position/Module.php000064400000010215151166614520015157
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Position;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Module implements \ArrayAccess
{
    use NestedArrayAccessWithGetters, Export;

    public $name;
    public $position;
    public $assigned;
    protected $items;

    /**
     * Module constructor.
     *
     * @param string $name
     * @param string $position
     * @param array $data
     */
    public function __construct($name, $position = null, array $data =
null)
    {
        $this->name = $name;
        $this->position = $position;

        if ($data) {
            $this->init($data);
        } else {
            $this->load();
        }
    }

    public function update(array $data)
    {
        $this->init($data);

        return $this;
    }

    /**
     * Save module.
     *
     * @param string $position
     * @param string $name
     * @return $this
     */
    public function save($name = null, $position = null)
    {
        $this->name = $name ?: $this->name;
        $this->position = $position ?: $this->position;

        $items = $this->toArray();
        unset($items['position'], $items['id']);

        $file = $this->file(true);
        $file->save($items);

        return $this;
    }

    /**
     * Delete module.
     *
     * @return $this
     */
    public function delete()
    {
        $file = $this->file(true);
        if ($file->exists()) {
            $file->delete();
        }

        return $this;
    }

    /**
     * Return true if module exists.
     *
     * @return bool
     */
    public function exists()
    {
        return $this->name ? $this->file()->exists() : false;
    }

    public function toArray()
    {
        return  ['position' => $this->position,
'id' => $this->name] + $this->items;
    }

    protected function load()
    {
        $file = $this->file();
        $this->init($file->content());
        $file->free();
    }

    protected function init($data)
    {
        unset($data['id'], $data['position']);

        $this->items = $data;

        if (isset($this->items['assignments'])) {
            $assignments = $this->items['assignments'];
            if (is_array($assignments)) {
                $this->assigned = 'some';
            } elseif ($assignments !== 'all') {
                $this->assigned = 'none';
            } else {
                $this->assigned = 'all';
            }
        } else {
            $this->assigned = 'all';
        }
    }

    protected function file($save = false)
    {
        $position = $this->position ?: '_unassigned_';

        $this->name = $this->name ?: ($save ?
$this->findFreeName() : null);
        $name = $this->name ?: '_untitled_';

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        return
CompiledYamlFile::instance($locator->findResource("gantry-positions://{$position}/{$name}.yaml",
true, $save));
    }

    /**
     * Find unused name with number appended.
     */
    protected function findFreeName()
    {
        $position = $this->position ?: '_unassigned_';
        $name = $this->get('type');
        $name = $name == 'particle' ?
$this->get('options.type') : $name;

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        if
(!file_exists($locator->findResource("gantry-positions://{$position}/{$name}.yaml",
true, true))) {
            return $name;
        }

        $count = 1;

        do {
            $count++;
        } while
(file_exists($locator->findResource("gantry-positions://{$position}/{$name}_{$count}.yaml",
true, true)));

        return "{$name}_{$count}";
    }
}
classes/Gantry/Component/Position/Position.php000064400000021451151166614520015542
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Position;

use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;

class Position extends Collection
{
    public $name;
    public $title;
    protected $modules = [];

    /**
     * Position constructor.
     *
     * @param string $name
     * @param array $items
     */
    public function __construct($name, array $items = null)
    {
        $this->name = $name;

        $this->load($items);
    }

    /**
     * Save position.
     *
     * @return $this
     */
    public function save()
    {
        $file = $this->file(true);
        $file->save($this->toArray());

        return $this;
    }

    /**
     * Clone position together with its modules. Returns new position.
     *
     * @param string $name
     * @return Position
     */
    public function duplicate($name)
    {
        $new = clone $this;
        $new->name = $name;
        $new->save();

        foreach ($this as $module) {
            $clone = clone $module;
            $clone->position = $name;
            $clone->save();
        }

        return $new;
    }

    /**
     * Raname module key
     *
     * @param string $name
     * @return static
     */
    public function rename($name)
    {
        $new = $this->duplicate($name);
        $this->delete();

        return $new;
    }

    /**
     * Delete position.
     *
     * @return $this
     */
    public function delete()
    {
        $file = $this->file(true);
        if ($file->exists()) {
            $file->delete();
        }

        $folder = $this->folder(true);
        if (is_dir($folder)) {
            Folder::delete($folder);
        }

        return $this;
    }

    /**
     * Update modules in the position.
     *
     * @param array $items
     * @return $this
     */
    public function update(array $items)
    {
        $list = [];
        foreach ($items as $item) {
            $name = ($item instanceof Module) ? $item->name : $item;

            $list[] = $name;
            if (!in_array($name, $this->items)) {
                $this->add($item);
            }
        }

        $remove = array_diff($this->items, $list);
        foreach ($remove as $item) {
            $module = $this->get($item);
            if ($module->position === $this->name) {
                $module->delete();
            }
        }

        $this->items = $list;

        return $this;
    }
    
    /**
     * @param Module|string $item
     * @param string        $name  Temporary name for the module.
     * @return $this
     */
    public function add($item, $name = null)
    {
        if ($item instanceof Module) {
            $this->modules[$name ?: $item->name] = $item;
            $item = $name ?: $item->name;
        }

        $this->items[] = $item;

        return $this;
    }

    public function remove($item)
    {
        if ($item instanceof Module) {
            $item = $item->name;
        }

        unset($this->modules[$item]);

        $this->items = array_diff($this->items, $item);

        return $this;
    }

    /**
     * @param $name
     * @return Module
     */
    public function get($name)
    {
        if (!isset($this->modules[$name])) {
            $this->modules[$name] = $this->loadModule($name);
        }

        return $this->modules[$name];
    }

    /**
     * Returns the value at specified offset.
     *
     * @param string $offset  The offset to retrieve.
     * @return Module
     */
    public function offsetGet($offset)
    {
        if (!isset($this->items[$offset])) {
            return null;
        }

        $name = $this->items[$offset];

        if (!isset($this->modules[$name])) {
            $this->modules[$name] = $this->loadModule($name);
        }

        return $this->modules[$name];
    }

    /**
     * Assigns a value to the specified offset.
     *
     * @param mixed $offset  The offset to assign the value to.
     * @param mixed $value   The value to set.
     */
    public function offsetSet($offset, $value)
    {
        if (!$value instanceof Position) {
            throw new \InvalidArgumentException('Value has to be
instance of Position');
        }
        if (is_null($offset)) {
            $this->items[] = $value->name;
            $this->modules[$value->name] = $value;
        } else {
            $this->items[$offset] = $value->name;
            $this->modules[$value->name] = $value;
        }
    }

    /**
     * Unsets an offset.
     *
     * @param mixed $offset  The offset to unset.
     */
    public function offsetUnset($offset)
    {
        parent::offsetUnset($offset);

        if (!isset($this->items[$offset])) {
            return;
        }

        $name = $this->items[$offset];
        if (isset($this->modules[$name])) {
            unset($this->modules[$name]);
        }
    }

    /**
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        $items = [];
        foreach ($this->items as $key => $name) {
            $items[] = $this->offsetGet($key);
        }

        return new \ArrayIterator($items);
    }

    /**
     * @return array
     */
    public function toArray($includeModules = false)
    {
        $array = [
            'name' => $this->name,
            'title' => $this->title,
        ];

        if (!$includeModules) {
            $array['ordering'] = $this->items;

        } else {
            $list = [];
            foreach ($this->getIterator() as $key => $module) {
                $list[$key] = $module->toArray();
            }
            $array['modules'] = $list;
        }

        return $array;
    }

    /**
     * @param int $inline
     * @param int $indent
     * @param bool $includeModules
     * @return string
     */
    public function toYaml($inline = 3, $indent = 2, $includeModules =
false)
    {
        return Yaml::dump($this->toArray($includeModules), $inline,
$indent, true, false);
    }

    /**
     * @param bool $includeModules
     * @return string
     */
    public function toJson($includeModules = false)
    {
        return json_encode($this->toArray($includeModules));
    }

    /**
     * @return array
     */
    public function listModules()
    {
        $list = [];
        foreach ($this->items as $name) {
            $list[] = "{$this->name}/{$name}";
        }

        return $list;
    }

    /**
     * @param bool $save
     * @return string
     */
    public function folder($save = false)
    {
        return $this->locator()->findResource($this->path(), true,
$save);
    }

    /**
     * @param $data
     */
    protected function load($data)
    {
        if ($data === null) {
            $file = $this->file();
            $data = $file->content();
            $file->free();
        }

        $this->title = isset($data['title']) ?
$data['title'] : $this->name;

        if (isset($data['modules'])) {
            foreach ($data['modules'] as $array) {
                $this->add(new Module($array['id'],
$this->name, $array), $array['id'] ?: rand());
            }

            return;
        }

        // Sort modules by ordering, if items are not listed in ordering,
use alphabetical order.
        $ordering = isset($data['ordering']) ?
array_flip($data['ordering']) : [];
        $path = $this->locator()->findResource($this->path());
        $files = $path ? Folder::all(
            $path,
            [
                'compare' => 'Filename',
                'pattern' => '|\.yaml$|',
                'folders' => false,
                'recursive' => false,
                'key' => 'Filename',
                'filters' => ['key' =>
'|\.yaml$|']
            ]
        ) : [];
        ksort($files);
        $this->items = array_keys($ordering + $files);
    }

    /**
     * @param  string $name
     * @return $this
     */
    protected function loadModule($name)
    {
        return new Module($name, $this->name);
    }

    /**
     * @param bool $save
     * @return CompiledYamlFile
     */
    protected function file($save = false)
    {
        return
CompiledYamlFile::instance($this->locator()->findResource($this->path()
. '.yaml', true, $save));
    }

    /**
     * @return UniformResourceLocator
     */
    protected function locator()
    {
        return Gantry::instance()['locator'];
    }

    /**
     * @return string
     */
    protected function path()
    {
        return "gantry-positions://{$this->name}";
    }

}
classes/Gantry/Component/Position/Positions.php000064400000014456151166614520015734
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Position;

use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\DI\Container;

class Positions extends Collection
{
    /**
     * @var array|Position[]
     */
    protected $items;

    /**
     * @var string
     */
    protected $path;

    /**
     * @var Container
     */
    protected $container;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    /**
     * @param string $path
     *
     * @return $this
     * @throws \RuntimeException
     */
    public function load($path = 'gantry-positions://')
    {
        $this->path = $path;
        $positions = [];

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];
        if ($locator->findResource($path)) {
            /** @var UniformResourceIterator $iterator */
            $iterator = $locator->getIterator($path);

            /** @var UniformResourceIterator $info */
            foreach ($iterator as $info) {
                if (!$info->isFile() || $info->getExtension() !==
'yaml') {
                    continue;
                }

                $name = $info->getBasename('.yaml');
                $position =
CompiledYamlFile::instance($info->getPathname())->content();

                // Only use filesystem position if it it is properly set
up.
                if ($position) {
                    $positions[$name] = new Position($name, $position);
                }
            }
        }

        // Add empty positions from the layouts.
        foreach ($this->container['outlines']->positions()
as $name => $title) {
            if (!isset($positions[$name])) {
                $positions[$name] = new Position($name, ['title'
=> $title]);
            }
        }

        ksort($positions);

        $this->items = $positions;

        return $this;
    }

    /**
     * Updates all positions with their modules from an array and saves
them.
     *
     * @param array $data
     * @return $this
     */
    public function import(array $data)
    {
        foreach ($data as $pos) {
            $list = [];
            $position = $pos['name'];
            foreach ($pos['modules'] as $item) {
                $name = !empty($item['id']) ?
$item['id'] : '';

                if ($name && !empty($item['position'])) {
                    $module =
$this[$item['position']]->get($name);

                    if ($position !== $item['position']) {
                        $module->delete();
                    }
                } else {
                    $module = new Module($name, $position);
                }
                $module->update($item)->save($name, $position);

                $list[] = $module;
            }

            $this[$pos['name']]->update($list)->save();
        }

        return $this;
    }

    /**
     * @param Position $item
     * @return $this
     */
    public function add($item)
    {
        if ($item instanceof Position) {
            $this->items[$item->name] = $item;
        }

        return $this;
    }

    /**
     * @param string $title
     * @param string $id
     *
     * @return string
     * @throws \RuntimeException
     */
    public function create($title = 'Untitled', $id = null)
    {
        $name = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $id ?: $title));

        if (!$name) {
            throw new \RuntimeException("Position needs a name",
400);
        }

        $name = $this->findFreeName($name);

        $position = new Position($name, ['title' => $title]);
        $position->save();

        return $name;
    }

    /**
     * @param string $id
     * @param string $new
     *
     * @return string
     * @throws \RuntimeException
     */
    public function duplicate($id, $new = null)
    {
        if (!isset($this->items[$id])) {
            throw new \RuntimeException(sprintf("Duplicating Position
failed: '%s' not found.", $id), 400);
        }

        $new = $this->findFreeName($new ?
strtolower(preg_replace('|[^a-z\d_-]|ui', '_', $new)) :
$id);

        $position = $this->items[$id];
        $new = $position->duplicate($new);

        return $new->name;
    }

    /**
     * @param string $id
     * @param string $new
     *
     * @return string
     * @throws \RuntimeException
     */
    public function rename($id, $new)
    {
        if (!isset($this->items[$id])) {
            throw new \RuntimeException(sprintf("Renaming Position
failed: '%s' not found.", $id), 400);
        }

        $newId = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $new));

        if (isset($this->items[$newId])) {
            throw new \RuntimeException(sprintf("Renaming Position
failed: '%s' already exists.", $newId), 400);
        }

        $position = $this->items[$id];
        $position->rename($new);

        return $position->name;
    }

    /**
     * @param string $id
     *
     * @throws \RuntimeException
     */
    public function delete($id)
    {
        if (!isset($this->items[$id])) {
            throw new \RuntimeException(sprintf("Deleting Position
failed: '%s' not found.", $id), 400);
        }

        $position = $this->items[$id];
        $position->delete();
    }

    /**
     * Find unused name with number appended to it when duplicating an
position.
     *
     * @param string $id
     *
     * @return string
     */
    protected function findFreeName($id)
    {
        if (!isset($this->items[$id])) {
            return $id;
        }

        $name  = $id;
        $count = 0;
        if (preg_match('|^(?:_)?(.*?)(?:_(\d+))?$|ui', $id,
$matches)) {
            $matches += ['', '', ''];
            list (, $name, $count) = $matches;
        }

        $count = max(1, $count);

        do {
            $count++;
        } while (isset($this->items["{$name}_{$count}"]));

        return "{$name}_{$count}";
    }
}
classes/Gantry/Component/Remote/Response.php000064400000015615151166614520015170
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Remote;

class Response
{
    /**
     * The callback for the progress
     *
     * @var callable    Either a function or callback in array notation
     */
    public static $callback = null;

    /**
     * Which method to use for HTTP calls, can be 'curl',
'fopen' or 'auto'. Auto is default and fopen is the
preferred method
     *
     * @var string
     */
    private static $method = 'auto';

    /**
     * Default parameters for `curl` and `fopen`
     *
     * @var array
     */
    private static $defaults = [

        'curl'  => [
            CURLOPT_REFERER        => 'Gantry5 Response',
            CURLOPT_USERAGENT      => 'Gantry5 Response',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_TIMEOUT        => 15,
            CURLOPT_HEADER         => false,
            /**
             * Example of callback parameters from within your own class
             */
            //CURLOPT_NOPROGRESS     => false,
            //CURLOPT_PROGRESSFUNCTION => [$this, 'progress']
        ],
        'fopen' => [
            'method'          => 'GET',
            'user_agent'      => 'Gantry5 Response',
            'max_redirects'   => 5,
            'follow_location' => 1,
            'timeout'         => 15,
            /**
             * Example of callback parameters from within your own class
             */
            //'notification' => [$this, 'progress']
        ]
    ];

    /**
     * Sets the preferred method to use for making HTTP calls.
     *
     * @param string $method Default is `auto`
     *
     * @return Response
     */
    public static function setMethod($method = 'auto')
    {
        if (!in_array($method, ['auto', 'curl',
'fopen'])) {
            $method = 'auto';
        }

        self::$method = $method;

        return new self();
    }

    /**
     * Makes a request to the URL by using the preferred method
     *
     * @param  string   $uri     URL to call
     * @param  array    $options An array of parameters for both `curl` and
`fopen`
     * @param  callable $callback
     *
     * @return string The response of the request
     */
    public static function get($uri = '', $options = [],
$callback = null)
    {
        if (!self::isCurlAvailable() && !self::isFopenAvailable())
{
            throw new \RuntimeException('Could not start an HTTP
request. `allow_url_open` is disabled and `cURL` is not available');
        }

        $options = array_replace_recursive(self::$defaults, $options);
        $method  = 'get' . ucfirst(strtolower(self::$method));

        self::$callback = $callback;
        return static::$method($uri, $options, $callback);
    }

    /**
     * Checks if cURL is available
     *
     * @return boolean
     */
    public static function isCurlAvailable()
    {
        return function_exists('curl_version');
    }

    /**
     * Checks if the remote fopen request is enabled in PHP
     *
     * @return boolean
     */
    public static function isFopenAvailable()
    {
        return preg_match('/1|yes|on|true/i',
ini_get('allow_url_fopen'));
    }

    /**
     * Progress normalized for cURL and fopen
     *
     * @return array Normalized array with useful data.
     *               Format: ['code' => int|false,
'filesize' => bytes, 'transferred' => bytes,
'percent' => int]
     */
    public static function progress()
    {
        static $filesize = null;

        $args           = func_get_args();
        $isCurlResource = is_resource($args[0]) &&
get_resource_type($args[0]) == 'curl';

        $notification_code = !$isCurlResource ? $args[0] : false;
        $bytes_transferred = $isCurlResource ? $args[2] : $args[4];

        if ($isCurlResource) {
            $filesize = $args[1];
        } elseif ($notification_code == STREAM_NOTIFY_FILE_SIZE_IS) {
            $filesize = $args[5];
        }

        if ($bytes_transferred > 0) {
            if ($notification_code == STREAM_NOTIFY_PROGRESS |
STREAM_NOTIFY_COMPLETED || $isCurlResource) {

                $progress = [
                    'code'        => $notification_code,
                    'filesize'    => $filesize,
                    'transferred' => $bytes_transferred,
                    'percent'     => $filesize <= 0 ?
'-' : round(($bytes_transferred * 100) / $filesize, 1)
                ];

                if (self::$callback !== null) {
                    call_user_func_array(self::$callback, [$progress]);
                }
            }
        }
    }

    /**
     * Automatically picks the preferred method
     *
     * @return string The response of the request
     */
    private static function getAuto()
    {
        if (self::isFopenAvailable()) {
            return self::getFopen(func_get_args());
        }

        if (self::isCurlAvailable()) {
            return self::getCurl(func_get_args());
        }

        return '';
    }

    /**
     * Starts a HTTP request via fopen
     *
     * @return string The response of the request
     */
    private static function getFopen()
    {
        if (count($args = func_get_args()) == 1) {
            $args = $args[0];
        }

        $uri      = $args[0];
        $options  = $args[1];
        $callback = $args[2];

        if ($callback) {
            $options['fopen']['notification'] =
['self', 'progress'];
        }

        $stream  = stream_context_create(['http' =>
$options['fopen']], $options['fopen']);
        $content = @file_get_contents($uri, false, $stream);

        if ($content === false) {
            throw new \RuntimeException("Error while trying to
download '$uri'");
        }

        return $content;
    }

    /**
     * Starts a HTTP request via cURL
     *
     * @return string The response of the request
     */
    private static function getCurl()
    {
        $args = func_get_args();
        $args = count($args) > 1 ? $args : array_shift($args);

        $uri      = $args[0];
        $options  = $args[1];
        $callback = $args[2];

        $ch = curl_init($uri);
        curl_setopt_array($ch, $options['curl']);

        if ($callback) {
            curl_setopt_array(
                $ch,
                [
                    CURLOPT_NOPROGRESS       => false,
                    CURLOPT_PROGRESSFUNCTION => ['self',
'progress']
                ]
            );
        }

        $response = curl_exec($ch);

        if ($errno = curl_errno($ch)) {
            $error_message = curl_strerror($errno);
            throw new \RuntimeException("cURL error ({$errno}):\n
{$error_message}");
        }

        curl_close($ch);

        return $response;
    }
}
classes/Gantry/Component/Request/Input.php000064400000007016151166614520014662
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Request;

use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;

class Input implements \ArrayAccess, \Iterator, ExportInterface
{
    use NestedArrayAccessWithGetters, Iterator, Export;

    /**
     * @var array
     */
    protected $items;

    /**
     * Constructor to initialize array.
     *
     * @param  array  $items  Initial items inside the iterator.
     */
    public function __construct(array &$items = [])
    {
        $this->items = &$items;
    }

    /**
     * Returns input array. If there are any JSON encoded fields (key:
_json), those will be decoded as well.
     *
     * @param string  $path       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return array
     */
    public function getArray($path = null, $default = null, $separator =
'.')
    {
        $data = $path ? $this->get($path, $default, $separator) :
$this->items;

        return (array) $this->getChildren($data);
    }

    /**
     * Returns JSON decoded input array.
     *
     * @param string  $path       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return array
     */
    public function getJsonArray($path = null, $default = null, $separator
= '.')
    {
        return (array) $this->getJson($path, $default, $separator,
true);
    }

    /**
     * Returns JSON decoded input. Accosiative arrays become objects.
     *
     * @param string|null  $path       Dot separated path to the requested
value.
     * @param mixed        $default    Default value (or null).
     * @param string       $separator  Separator, defaults to '.'
     * @param bool         $assoc      True to return associative arrays
instead of objects.
     * @return mixed
     */
    public function getJson($path = null, $default = null, $separator =
'.', $assoc = false)
    {
        $data = $this->get($path, null, $separator);

        if (!isset($data)) {
            return $default;
        }

        if (!is_string($data)) {
            throw new \RuntimeException(sprintf('%s::%s(%s) expects
input to be JSON encoded string', __CLASS__, __FUNCTION__, $path));
        }

        $data = json_decode($data, $assoc);
        if (!isset($data)) {
            throw new \RuntimeException(sprintf('%s::%s(): %s',
__CLASS__, __FUNCTION__, json_last_error_msg()));
        }

        return $data;
    }

    /**
     * @param $current
     * @return array|mixed
     * @internal
     */
    protected function getChildren(&$current)
    {
        if (!is_array($current)) {
            return $current;
        }
        $array = [];
        foreach ($current as $key => &$value) {
            if ($key === '_json') {
                $array += json_decode($value, true);
            } else {
                $array[$key] = $this->getChildren($value);
            }
        }

        return $array;
    }
}
classes/Gantry/Component/Request/Request.php000064400000002740151166614520015212
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Request;

class Request
{
    /**
     * @var string
     */
    protected $method;

    /**
     * @var Input
     */
    public $get;

    /**
     * @var Input
     */
    public $post;

    /**
     * @var Input
     */
    public $cookie;

    /**
     * @var Input
     */
    public $server;

    /**
     * @var Input
     */
    public $request;

    public function __construct()
    {
        $this->init();
    }

    public function getMethod()
    {
        if (!$this->method) {
            $method = $this->server['REQUEST_METHOD'] ?:
'GET';
            if ('POST' === $method) {
                $method =
$this->server['X-HTTP-METHOD-OVERRIDE'] ?: $method;
                $method = $this->post['METHOD'] ?: $method;
            }
            $this->method = strtoupper($method);
        }

        return $this->method;
    }

    protected function init()
    {
        $this->get = new Input($_GET);
        $this->post = new Input($_POST);
        $this->cookie = new Input($_COOKIE);
        $this->server = new Input($_SERVER);
        $this->request = new Input($_REQUEST);
    }
}
classes/Gantry/Component/Response/HtmlResponse.php000064400000000701151166614520016346
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class HtmlResponse extends Response
{
}
classes/Gantry/Component/Response/JsonResponse.php000064400000007147151166614520016366
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class JsonResponse extends Response
{
    public $mimeType = 'application/json';

    protected $success = true;
    protected $message;
    protected $exceptions = [];
    protected $messages = [];
    protected $content = [];

    /**
     * @param string $content
     * @param bool $success
     * @return $this
     */
    public function setContent($content, $success = true)
    {
        $this->success = (bool) $success;

        if (is_array($content)) {
            foreach ($content as $key => $value) {
                $this->parseValue($key, $value);
            }
        } else {
            $this->parseValue(null, $content);
        }

        return $this;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        // Empty output buffer to make sure that the response is clean and
valid.
        while (($output = ob_get_clean()) !== false) {
            // In debug mode send also output buffers (debug dumps, PHP
notices and warnings).
            if ($output && (GANTRY5_DEBUG || headers_sent())) {
                $this->messages['php'][] = $output;
            }
        }

        $json = [
            'code' => $this->code,
            'success' => $this->success
        ];
        if ($this->messages) {
            $json['messages'] = $this->messages;
        }
        if ($this->exceptions) {
            $json['exceptions'] = $this->exceptions;
        }
        if (GANTRY5_DEBUG) {
            $json['memory'] = ['peak' =>
memory_get_peak_usage(), 'current' => memory_get_usage()];
        }
        $json += $this->content;

        return (string) json_encode($json);
    }

    protected function parseValue($key, $value)
    {
        if ($value instanceof \Exception) {
            // Prepare the error response if we are dealing with an error.
            $this->success = false;
            $this->exceptions = $this->parseException($value);

        } elseif ($value instanceof HtmlResponse) {
            // Add HTML response (numeric keys are always integers).
            $key =  !$key || is_int($key) ? 'html' : $key;
            $this->content[$key] = trim((string) $value);

        } elseif (is_null($key)) {
            // If the returned value was not an array, put the contents
into data variable.
            $this->content['data'] = $value;

        } elseif (is_int($key)) {
            // If the key was an integer, also put the contents into data
variable.
            $this->content['data'][$key] = $value;

        } else {
            $this->content[$key] = $value;
        }
    }

    protected function parseException(\Exception $e)
    {
        $this->code = $e->getCode();

        // Build data from exceptions.
        $exceptions = [];

        do {
            $exception = [
                'code' => $e->getCode(),
                'message' => $e->getMessage()
            ];

            if (GANTRY5_DEBUG) {
                $exception += [
                    'type' => get_class($e),
                    'file' => $e->getFile(),
                    'line' => $e->getLine()
                ];
            }

            $exceptions[] = $exception;
            $e = $e->getPrevious();
        }
        while (GANTRY5_DEBUG && $e);

        return $exceptions;
    }
}
classes/Gantry/Component/Response/RedirectResponse.php000064400000001467151166614520017215
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class RedirectResponse extends Response
{
    public function __construct($content = '', $status = 303)
    {
        parent::__construct('', $status);

        $this->setHeader('Location', $content);
    }

    public function getContent()
    {
        return (string) $this->getHeaders()['Location'];
    }

    public function setContent($content)
    {
        $this->setHeader('Location', $content);
    }
}
classes/Gantry/Component/Response/Response.php000064400000010313151166614520015521
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class Response
{
    public $charset = 'utf-8';
    public $mimeType = 'text/html';

    protected $code = 200;
    protected $message = 'OK';
    protected $lifetime = 0;
    protected $etag;

    /**
     * @var array Response headers.
     */
    protected $headers = [];

    /**
     * @var string Response body.
     */
    protected $content;

    protected $responseCodes = [
        200 => 'OK',
        400 => 'Bad Request',
        401 => 'Unauthorized',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        410 => 'Gone',
        500 => 'Internal Server Error',
        501 => 'Not Implemented',
        503 => 'Service Temporarily Unavailable'
    ];

    public function __construct($content = '', $status = 200)
    {
        if ($content) {
            $this->setContent($content);
        }

        if ($status != 200) {
            $this->setStatusCode($status);
        }
    }

    /**
     * @param int $seconds
     * @return $this
     */
    public function setLifetime($seconds)
    {
        $this->lifetime = $seconds;

        return $this;
    }

    /**
     * @param mixed $key
     * @return $this
     */
    public function setKey($key)
    {
        $this->etag = md5(json_encode($key));

        return $this;
    }

    /**
     * @return int
     */
    public function getStatusCode()
    {
        return $this->code;
    }

    /**
     * @param int $code
     * @param string $message
     * @return $this
     */
    public function setStatusCode($code, $message = null)
    {
        if ($message) {
            $this->code = $code;
            $this->message = $message;
        } else {
            $this->code = isset($this->responseCodes[$code]) ? (int)
$code : 500;
            $this->message = $this->responseCodes[$this->code];
        }

        return $this;
    }

    /**
     * @return string
     */
    public function getStatus()
    {
        $code = $this->getStatusCode();

        return $code . ' ' .
(isset($this->responseCodes[$code]) ? $this->responseCodes[$code] :
'Unknown error');
    }

    /**
     * @return array
     */
    public function getHeaders()
    {
        return $this->headers;
    }

    /**
     * @param array $headers
     * @param bool $replace
     * @return $this
     */
    public function setHeaders(array $headers, $replace = false)
    {
        foreach ($headers as $key => $values) {
            $act = $replace;
            foreach ((array) $values as $value) {
                $this->setHeader($key, $value, $act);
                $act = false;
            }
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function clearHeaders()
    {
        $this->headers = [];

        return $this;
    }

    /**
     * @param $name
     * @param $value
     * @param bool $replace
     * @return $this
     */
    public function setHeader($name, $value, $replace = false)
    {
        if ($replace) {
            $this->headers[$name] = [$value];
        } else {
            $this->headers[$name][] = $value;
        }

        return $this;
    }

    /**
     * @return string
     */
    public function getContent()
    {
        return (string) $this->content;
    }

    /**
     * @param string $content
     * @return Response
     * @throws \UnexpectedValueException
     */
    public function setContent($content) {
        if ($content !== null && !is_string($content) &&
!is_numeric($content) && !is_callable([$content,
'__toString'])) {
            throw new \UnexpectedValueException(
                sprintf('Content must be a string or object
implementing __toString()')
            );
        }
        $this->content = $content;

        return $this;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return (string) $this->content;
    }
}
classes/Gantry/Component/Router/Router.php000064400000012471151166614520014674
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Router;

use Gantry\Admin\EventListener;
use Gantry\Admin\Theme;
use Gantry\Component\Controller\BaseController;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\Response;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\Event\EventDispatcher;
use Whoops\Exception\ErrorException;

abstract class Router implements RouterInterface
{
    /**
     * @var Container
     */
    protected $container;

    protected $format;
    protected $resource;
    protected $method;
    protected $path;
    protected $params;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    public function dispatch()
    {
        $this->boot();

        $this->load();

        // Render the page or execute the task.
        try {
            $response = static::execute($this->resource,
$this->method, $this->path, $this->params, $this->format);

        } catch (ErrorException $e) {
            throw $e;

        } catch (\Exception $e) {
            // Handle errors.
            if ($this->container->debug()) {
                throw $e;
            }
            $response = $this->getErrorResponse($e, $this->format ==
'json');
        }

        return $this->send($response);
    }

    public function execute($resource, $method = 'GET', $path,
$params = [], $format = 'html')
    {
        $class = '\\Gantry\\Admin\\Controller\\' .
ucfirst($format) . '\\' . strtr(ucwords(strtr($resource,
'/', ' ')), ' ', '\\');

        // Protect against CSRF Attacks.
        if (!in_array($method, ['GET', 'HEAD'], true)
&& !$this->checkSecurityToken()) {
            throw new \RuntimeException('Invalid security token;
please reload the page and try again.', 403);
        }

        if (!class_exists($class)) {
            if ($format === 'json') {
                // Special case: All HTML requests can be returned also as
JSON.
                $response = $this->execute($resource, $method, $path,
$params, 'html');
                return $response instanceof JsonResponse ? $response : new
JsonResponse($response);
            }

            throw new \RuntimeException('Page Not Found', 404);
        }

        /** @var BaseController $controller */
        $controller = new $class($this->container);

        // Execute action.
        $response = $controller->execute($method, $path, $params);

        if (!$response instanceof Response) {
            $response = new HtmlResponse($response);
        }

        return $response;
    }

    /**
     * @return $this
     */
    abstract protected function boot();

    abstract protected function checkSecurityToken();

    /**
     * @return $this
     */
    public function load()
    {
        static $loaded = false;

        if ($loaded) {
            return $this;
        }

        $loaded = true;

        if (isset($this->container['theme.path']) &&
file_exists($this->container['theme.path'] .
'/includes/gantry.php')) {
            include $this->container['theme.path'] .
'/includes/gantry.php';
        }

        if (isset($this->container['theme'])) {
            // Initialize current theme if it is set.
            $this->container['theme'];
        } else {
            // Otherwise initialize streams and error handler manually.
            $this->container['streams']->register();
            $this->container->register(new ErrorServiceProvider);
        }

        $this->container['admin.theme'] = function () {
            return new Theme(GANTRYADMIN_PATH);
        };

        // Add event listener.
        if (class_exists('Gantry\\Admin\\EventListener')) {
            $listener = new EventListener;

            /** @var EventDispatcher $events */
            $events = $this->container['events'];
            $events->addSubscriber($listener);
        }

        // Boot the service.
        $this->container['admin.theme'];

        return $this;
    }

    protected function getErrorResponse(\Exception $e, $json = false)
    {
        $response = new HtmlResponse;
        $response->setStatusCode($e->getCode());

        $params = [
            'ajax' => $json,
            'title' => $response->getStatus(),
            'error' => $e,
        ];
       
$response->setContent($this->container['admin.theme']->render('@gantry-admin/error.html.twig',
$params));

        if ($json) {
            return new JsonResponse([$e, $response]);
        }

        return $response;
    }

    protected function send(Response $response) {
        // Output HTTP header.
        header("HTTP/1.1 {$response->getStatus()}", true,
$response->getStatusCode());
        header("Content-Type: {$response->mimeType};
charset={$response->charset}");
        foreach ($response->getHeaders() as $key => $values) {
            foreach ($values as $value) {
                header("{$key}: {$value}");
            }
        }

        echo $response;

        return true;
    }
}
classes/Gantry/Component/Router/RouterInterface.php000064400000000725151166614520016514
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Router;

interface RouterInterface
{
    public function dispatch();
}
classes/Gantry/Component/Stylesheet/CssCompiler.php000064400000023161151166614520016506
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet;

use Gantry\Component\Config\Config;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Colors;
use RocketTheme\Toolbox\File\PhpFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class CssCompiler implements CssCompilerInterface
{
    use GantryTrait;

    protected $type;

    protected $name;

    protected $debug = false;

    protected $warnings = [];

    /**
     * @var array
     */
    protected $fonts;

    /**
     * @var array
     */
    protected $variables;

    /**
     * @var string
     */
    protected $target = 'gantry-theme://css-compiled';

    /**
     * @var string
     */
    protected $configuration = 'default';

    /**
     * @var array
     */
    protected $paths;

    /**
     * @var array
     */
    protected $files;

    /**
     * @var mixed
     */
    protected $compiler;

    /**
     * @var bool
     */
    protected $production;

    public function __construct()
    {
        $gantry = static::gantry();

        /** @var Config $global */
        $global = $gantry['global'];

        // In production mode we do not need to do any other checks.
        $this->production = (bool)
$global->get('production');
    }

    public function getWarnings()
    {
        return $this->warnings;
    }

    /**
     * @return string
     */
    public function getTarget()
    {
        return $this->target;
    }

    /**
     * @param string $target
     * @return $this
     */
    public function setTarget($target = null)
    {
        if ($target !== null) {
            $this->target = (string) $target;
        }

        return $this;
    }

    /**
     * @param string $configuration
     * @return $this
     */
    public function setConfiguration($configuration = null)
    {
        if ($configuration !== null) {
            $this->configuration = $configuration;
        }

        return $this;
    }

    /**
     * @param array $fonts
     * @return $this
     */
    public function setFonts(array $fonts = null)
    {
        if ($fonts !== null) {
            // Normalize font data.
            $list = [];
            foreach ($fonts as $family => $data) {
                $family = strtolower($family);

                if (is_array($data)) {
                    // font: [400: url1, 500: url2, 700: url3]
                    $list[$family] = $data;
                } else {
                    // font: url
                    $list[$family] = [400 => (string) $data];
                }
            }
            $this->compiler->setFonts($list);
        }

        return $this;
    }

    /**
     * @param array $paths
     * @return $this
     */
    public function setPaths(array $paths = null)
    {
        if ($paths !== null) {
            $this->paths = $paths;
        }

        return $this;
    }

    /**
     * @param array $files
     * @return $this
     */
    public function setFiles(array $files = null)
    {
        if ($files !== null) {
            $this->files = $files;
        }

         return $this;
    }


    /**
     * @param string $name
     * @return string
     */
    public function getCssUrl($name)
    {
        $out = $name . ($this->configuration !== 'default' ?
'_'. $this->configuration : '');

        return "{$this->target}/{$out}.css";
    }

    /**
     * @return $this
     */
    public function compileAll()
    {
        foreach ($this->files as $file) {
            $this->compileFile($file);
        }

        return $this;
    }

    public function needsCompile($in, $variables)
    {
        $gantry = static::gantry();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $out = $this->getCssUrl($in);
        $path = $locator->findResource($out);

        // Check if CSS file exists at all.
        if (!$path) {
            $this->setVariables($variables());

            return true;
        }

        if ($this->production) {
            // Open the file to see if it contains development comment in
the beginning of the file.
            $handle = fopen($path, 'rb');
            $contents = fread($handle, 36);
            fclose($handle);

            if ($contents === '/* GANTRY5 DEVELOPMENT MODE
ENABLED.') {
                $this->setVariables($variables());
                return true;
            }

            // Compare checksum comment in the file.
            if ($contents !== $this->checksum()) {
                $this->setVariables($variables());
                return true;
            }

            // In production mode we do not need to do any other checks.
            return false;
        }

        $uri = basename($out);
        $metaFile =
PhpFile::instance($locator->findResource("gantry-cache://theme/scss/{$uri}.php",
true, true));

        // Check if meta file exists.
        if (!$metaFile->exists()) {
            $this->setVariables($variables());
            return true;
        }

        $content = $metaFile->content();
        $metaFile->free();

        // Check if filename in meta file matches.
        if (empty($content['file']) || $content['file']
!== $out) {
            $this->setVariables($variables());
            return true;
        }

        // Check if meta timestamp matches to CSS file.
        if (filemtime($path) !== $content['timestamp']) {
            $this->setVariables($variables());
            return true;
        }

        $this->setVariables($variables());

        // Check if variables have been changed.
        $oldVariables = isset($content['variables']) ?
$content['variables'] : [];
        if ($oldVariables != $this->getVariables()) {
            return true;
        }

        // Preload all CSS files to locator cache.
        foreach ($this->paths as $path) {
            $locator->fillCache($path);
        }

        // Check if any of the imported files have been changed.
        $imports = isset($content['imports']) ?
$content['imports'] : [];

        if (!$imports) {
            return $this->findImport($in) !== null;
        }

        foreach ($imports as $resource => $timestamp) {
            $import = $locator->isStream($resource) ?
$locator->findResource($resource) : realpath($resource);
            if (!$import || filemtime($import) !== $timestamp) {
                return true;
            }
        }

        return false;
    }

    public function setVariables(array $variables)
    {
        $this->variables = array_filter($variables);

        foreach($this->variables as &$value) {
            // Check variable against colors and units.
            /* Test regex against these:
             * Should only match the ones marked as +
             *      - family=Aguafina+Script
             *      - #zzzzzz
             *      - #fff
             *      + #ffaaff
             *      + 33em
             *      + 0.5px
             *      - 50 rem
             *      - rgba(323,323,2323)
             *      + rgba(125,200,100,0.3)
             *      - rgb(120,12,12)
             */
            if
(preg_match('/(^(#([a-fA-F0-9]{6})|(rgba\(\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*((0.[0-9]+)|[01])\s*\)))|(\d+(\.\d+){0,1}(rem|em|ex|ch|vw|vh|vmin|vmax|%|px|cm|mm|in|pt|pc))$)/i',
$value)) {
                continue;
            }

            // Check variable against predefined color names (we use Leafo
SCSS Color class to do that).
            if (isset(Colors::$cssColors[strtolower($value)])) {
                continue;
            }

            // All the unknown values need to be quoted.
            $value = "'{$value}'";
        }

        return $this;
    }

    public function getVariables()
    {
        return $this->variables;
    }

    public function reset()
    {
        $this->compiler->reset();

        return $this;
    }

    /**
     * @param string $url
     * @return null|string
     */
    abstract public function findImport($url);

    protected function checksum($len = 36)
    {
        static $checksum;

        if (!$checksum) {
            $checksum = md5(GANTRY5_VERSION . ' ' .
Gantry::instance()['theme']->version);
        }

        return '/*' . substr($checksum, 0, $len - 4) .
'*/';
    }

    protected function createMeta($out, $md5)
    {
        $gantry = Gantry::instance();

        if ($this->production) {
            return;
        }

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $uri = basename($out);
        $metaFile =
PhpFile::instance($locator->findResource("gantry-cache://theme/scss/{$uri}.php",
true, true));
        $data = [
            'file' => $out,
            'timestamp' =>
filemtime($locator->findResource($out)),
            'md5' => $md5,
            'variables' => $this->getVariables(),
            'imports' =>
$this->compiler->getParsedFiles()
        ];

        // Attempt to lock the file for writing.
        try {
            $metaFile->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }
        // If meta file wasn't already locked by another process, save
it.
        if ($metaFile->locked() !== false) {
            $metaFile->save($data);
            $metaFile->unlock();
        }
        $metaFile->free();
    }
}
classes/Gantry/Component/Stylesheet/CssCompilerInterface.php000064400000003324151166614520020326
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet;

interface CssCompilerInterface
{
    /**
     * @return array
     */
    public function getWarnings();

    /**
     * @return string
     */
    public function getTarget();

    /**
     * @param string $target
     * @return $this
     */
    public function setTarget($target = null);

    /**
     * @param string $configuration
     * @return $this
     */
    public function setConfiguration($configuration = null);

    /**
     * @param array $paths
     * @return $this
     */
    public function setPaths(array $paths = null);

    /**
     * @param array $files
     * @return $this
     */
    public function setFiles(array $files = null);

    /**
     * @param array $fonts
     * @return $this
     */
    public function setFonts(array $fonts);

    /**
     * @param string $name
     * @return string
     */
    public function getCssUrl($name);

    public function getVariables();
    public function setVariables(array $variables);
    public function registerFunction($name, callable $callback);
    public function unregisterFunction($name);
    public function needsCompile($in, $variables);
    public function compileFile($in);

    /**
     * @return $this
     */
    public function reset();

    /**
     * @return $this
     */
    public function compileAll();

    public function resetCache();
}
classes/Gantry/Component/Stylesheet/Scss/Compiler.php000064400000026204151166614520016751
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet\Scss;

use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Compiler as BaseCompiler;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\Parser;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Compiler extends BaseCompiler
{
    protected $basePath;
    protected $fonts;
    protected $usedFonts;
    protected $streamNames;
    protected $parsedFiles = [];

    public function __construct()
    {
        parent::__construct();

        $this->registerFunction('get-font-url', [$this,
'userGetFontUrl']);
        $this->registerFunction('get-font-family', [$this,
'userGetFontFamily']);
        $this->registerFunction('get-local-fonts', [$this,
'userGetLocalFonts']);
        $this->registerFunction('get-local-font-weights',
[$this, 'userGetLocalFontWeights']);
        $this->registerFunction('get-local-font-url', [$this,
'userGetLocalFontUrl']);
    }

    /**
     * @param $basePath
     */
    public function setBasePath($basePath)
    {
        /** @var Document $document */
        $document = Gantry::instance()['document'];
        $this->basePath = rtrim($document->rootUri(), '/')
. '/' . Folder::getRelativePath($basePath);
    }

    /**
     * @param array $fonts
     */
    public function setFonts(array $fonts)
    {
        $this->fonts = $fonts;
    }

    /**
     * @param $args
     * @return mixed
     */
    public function compileArgs($args)
    {
        foreach ($args as &$arg) {
            $arg = $this->compileValue($arg);
        }

        return $args;
    }

    /**
     * Get variable
     *
     * @api
     *
     * @param string    $name
     * @param boolean   $shouldThrow
     * @param BaseCompiler\Environment $env
     * @param bool $unreduced
     *
     * @return mixed
     */
    public function get($name, $shouldThrow = true,
BaseCompiler\Environment $env = null, $unreduced = false)
    {
        try {
            return parent::get($name, $shouldThrow, $env, $unreduced);
        } catch (\Exception $e) {
            echo $e->getMessage() . "\n";
            return ['string', '', ['']];
        }
    }

    /**
     * @param array $args
     * @return string
     * @throws \Leafo\ScssPhp\Exception\CompilerException
     */
    public function libUrl(array $args)
    {
        // Function has a single parameter.
        $parsed = reset($args);
        if (!$parsed) {
            $this->throwError('url() is missing parameter');
        }

        // Compile parsed value to string.
        $url = trim($this->compileValue($parsed),
'\'"');

        // Handle ../ inside CSS files (points to current theme).
        if (strpos($url, '../') === 0 && strpos($url,
'../', 3) === false) {
            $url = 'gantry-theme://' . substr($url, 3);
        }

        // Generate URL, failed streams will be transformed to 404 URLs.
        $url = Gantry::instance()['document']->url($url, null,
null, false);

        // Changes absolute URIs to relative to make the path to work even
if the site gets moved.
        if ($url && $url[0] === '/' &&
$this->basePath) {
            $url = Folder::getRelativePathDotDot($url, $this->basePath);
        }

        // Make sure that all the URLs inside CSS are https compatible by
replacing http:// protocol with //.
        if (strpos($url, 'http://') === 0) {
            $url = str_replace('http://', '//', $url);
        }

        // Return valid CSS.
        return "url('{$url}')";
    }

    /**
     * get-font-url($my-font-variable);
     *
     * @param array $args
     * @return string
     */
    public function userGetFontUrl($args)
    {
        $value = trim($this->compileValue(reset($args)),
'\'"');

        // It's a google font
        if (0 === strpos($value, 'family=')) {
            $fonts = $this->decodeFonts($value);
            $font = reset($fonts);

            // Only return url once per font.
            if ($font && !isset($this->usedFonts[$font])) {
                $this->usedFonts[$font] = true;
                return
"url('//fonts.googleapis.com/css?{$value}')";
            }
        }

        return false;
    }

    /**
     * font-family: get-font-family($my-font-variable);
     *
     * @param array $args
     * @return string
     */
    public function userGetFontFamily($args)
    {
        $value = trim($this->compileValue(reset($args)),
'\'"');

        return $this->encodeFonts($this->decodeFonts($value));
    }

    /**
     * get-local-fonts($my-font-variable, $my-font-variable2, ...);
     *
     * @param array $args
     * @return array
     */
    public function userGetLocalFonts($args)
    {
        $args = $this->compileArgs($args);

        $fonts = [];
        foreach ($args as $value) {
            // It's a local font, we need to load any of the mapped
fonts from the theme
            $fonts = array_merge($fonts, $this->decodeFonts($value,
true));
        }

        $fonts = $this->getLocalFonts($fonts);

        // Create a basic list of strings so that SCSS parser can parse the
list.
        $list = [];
        foreach ($fonts as $font => $data) {
            $list[] = ['string', '"', [$font]];
        }

        return ['list', ',', $list];
    }

    /**
     * get-local-font-weights(roboto);
     *
     * @param array $args
     * @return array
     */
    public function userGetLocalFontWeights($args)
    {
        $name = trim($this->compileValue(reset($args)),
'\'"');

        $weights = isset($this->fonts[$name]) ?
array_keys($this->fonts[$name]) : [];

        // Create a list of numbers so that SCSS parser can parse the list.
        $list = [];
        foreach ($weights as $weight) {
            $list[] = ['string', '', [(int) $weight]];
        }

        return ['list', ',', $list];
    }

    /**
     * get-local-font-url(roboto, 400);
     *
     * @param array $args
     * @return string
     */
    public function userGetLocalFontUrl($args)
    {
        $args = $this->compileArgs($args);

        $name = isset($args[0]) ? trim($args[0], '\'"')
: '';
        $weight = isset($args[1]) ? $args[1] : 400;

        // Only return url once per font.
        $weightName = $name . '-' . $weight;
        if (isset($this->fonts[$name][$weight]) &&
!isset($this->usedFonts[$weightName])) {
            $this->usedFonts[$weightName] = true;

            return $this->fonts[$name][$weight];
        }

        return false;
    }

    /**
     * Get local font data.
     *
     * @param array $fonts
     * @return array
     */
    protected function getLocalFonts(array $fonts)
    {
        $list = [];
        foreach ($fonts as $family) {
            $family = strtolower($family);

            if (isset($this->fonts[$family])) {
                $list[$family] = $this->fonts[$family];
            }
        }

        return $list;
    }

    /**
     * Convert array of fonts into a CSS parameter string.
     *
     * @param array $fonts
     * @return string
     */
    protected function encodeFonts(array $fonts)
    {
        array_walk($fonts, function(&$val) {
            // Check if font family is one of the 4 default ones, otherwise
add quotes.
            if (!\in_array($val, ['cursive', 'serif',
'sans-serif', 'monospace'], true)) {
                $val = '"' . $val . '"';
            }
        });

        return implode(', ', $fonts);
    }

    /**
     * Convert string into array of fonts.
     *
     * @param  string $string
     * @param  bool   $localOnly
     * @return array
     */
    protected function decodeFonts($string, $localOnly = false)
    {
        if (0 === strpos($string, 'family=')) {
            if ($localOnly) {
                // Do not return external fonts.
                return [];
            }

            // Matches google font family name
            preg_match('/^family=([^&:]+).*$/ui', $string,
$matches);
            return [urldecode($matches[1])];
        }

        // Filter list of fonts and quote them.
        $list = (array) explode(',', $string);
        array_walk($list, function(&$val) {
            $val = trim($val, "'\" \t\n\r\0\x0B");
        });
        array_filter($list);

        return $list;
    }

    public function reset()
    {
        $this->usedFonts = [];

        return $this;
    }

    /**
     * Instantiate parser
     *
     * @param string $path
     *
     * @return \Leafo\ScssPhp\Parser
     */
    protected function parserFactory($path)
    {
        $parser = new Parser($path, count($this->sourceNames),
$this->encoding);

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        $this->sourceNames[] = $locator->isStream($path) ?
$locator->findResource($path, false) : $path;
        $this->streamNames[] = $path;
        $this->addParsedFile($path);
        return $parser;
    }

    /**
     * Adds to list of parsed files
     *
     * @api
     *
     * @param string $path
     */
    public function addParsedFile($path)
    {
        if ($path && file_exists($path)) {
            $this->parsedFiles[$path] = filemtime($path);
        }
    }

    /**
     * Returns list of parsed files
     *
     * @api
     *
     * @return array
     */
    public function getParsedFiles()
    {
        return $this->parsedFiles;
    }

    /**
     * Clean parset files.
     *
     * @api
     */
    public function cleanParsedFiles()
    {
        $this->parsedFiles = [];
    }

    /**
     * Handle import loop
     *
     * @param string $name
     *
     * @throws \Exception
     */
    protected function handleImportLoop($name)
    {
        for ($env = $this->env; $env; $env = $env->parent) {
            $file = $this->streamNames[$env->block->sourceIndex];

            if (realpath($file) === $name) {
                $this->throwError('An @import loop has been found:
%s imports %s', $file, basename($file));
                break;
            }
        }
    }

    /**
     * Override function to improve the logic.
     *
     * @param string $path
     * @param OutputBlock  $out
     *
     * @throws \Exception
     */
    protected function importFile($path, OutputBlock $out)
    {
        $this->addParsedFile($path);

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        // see if tree is cached
        $realPath = $locator($path);

        if (isset($this->importCache[$realPath])) {
            $this->handleImportLoop($realPath);

            $tree = $this->importCache[$realPath];
        } else {
            $code   = file_get_contents($realPath);
            $parser = $this->parserFactory($path);
            $tree   = $parser->parse($code);

            $this->importCache[$realPath] = $tree;
        }

        $dirname = dirname($path);
        array_unshift($this->importPaths, $dirname);
        $this->compileChildrenNoReturn($tree->children, $out);
        array_shift($this->importPaths);
    }
}
classes/Gantry/Component/Stylesheet/ScssCompiler.php000064400000014766151166614520016704
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet;

use Gantry\Component\Stylesheet\Scss\Compiler;
use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Exception\CompilerException;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\File\JsonFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class ScssCompiler extends CssCompiler
{
    /**
     * @var string
     */
    public $type = 'scss';

    /**
     * @var string
     */
    public $name = 'SCSS';

    /**
     * @var Compiler
     */
    protected $compiler;

    /**
     * Constructor.
     */
    public function __construct()
    {
        parent::__construct();

        $this->compiler = new Compiler();

        if ($this->production) {
           
$this->compiler->setFormatter('Leafo\ScssPhp\Formatter\Crunched');
        } else {
           
$this->compiler->setFormatter('Leafo\ScssPhp\Formatter\Expanded');
            // Work around bugs in SCSS compiler.
            // TODO: Pass our own SourceMapGenerator instance instead.
           
$this->compiler->setSourceMap(Compiler::SOURCE_MAP_INLINE);
            $this->compiler->setSourceMapOptions([
                'sourceMapBasepath' => '/',
                'sourceRoot'        => '/',
            ]);
           
$this->compiler->setLineNumberStyle(Compiler::LINE_COMMENTS);
        }
    }

    public function compile($in)
    {
        return $this->compiler->compile($in);
    }

    public function resetCache()
    {
    }

    /**
     * @param string $in    Filename without path or extension.
     * @return bool         True if the output file was saved.
     * @throws \RuntimeException
     */
    public function compileFile($in)
    {
        // Buy some extra time as compilation may take a lot of time in
shared environments.
        @set_time_limit(30);
        @set_time_limit(60);
        @set_time_limit(90);
        @set_time_limit(120);
        ob_start();

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $out = $this->getCssUrl($in);
        $path = $locator->findResource($out, true, true);
        $file = File::instance($path);

        // Attempt to lock the file for writing.
        try {
            $file->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }

        if ($file->locked() === false) {
            // File was already locked by another process, lets avoid
compiling the same file twice.
            return false;
        }

        // Set the lookup paths.
        $this->compiler->setBasePath($path);
        $this->compiler->setImportPaths([[$this,
'findImport']]);

        // Run the compiler.
        $this->compiler->setVariables($this->getVariables());
        $scss = '@import "' . $in . '.scss"';
        try {
            $css = $this->compiler->compile($scss);
        } catch (CompilerException $e) {
            throw new \RuntimeException("CSS Compilation on file
'{$in}.scss' failed on error: {$e->getMessage()}", 500,
$e);
        }
        if (strpos($css, $scss) === 0) {
            $css = '/* ' . $scss . ' */';
        }

        // Extract map from css and save it as separate file.
        if ($pos = strrpos($css, '/*# sourceMappingURL=')) {
            $map = json_decode(urldecode(substr($css, $pos + 43, -3)),
true);

            /** @var Document $document */
            $document = $gantry['document'];

            foreach ($map['sources'] as &$source) {
                $source = $document->url($source, null, -1);
            }
            unset($source);

            $mapFile = JsonFile::instance($path . '.map');
            $mapFile->save($map);
            $mapFile->free();

            $css = substr($css, 0, $pos) . '/*#
sourceMappingURL=' . basename($out) . '.map */';
        }

        $warnings = trim(ob_get_clean());
        if ($warnings) {
            $this->warnings[$in] = explode("\n", $warnings);
        }

        if (!$this->production) {
            $warning = <<<WARN
/* GANTRY5 DEVELOPMENT MODE ENABLED.

   WARNING: This file is automatically generated by Gantry5. Any
modifications to this file will be lost!

   For more information on modifying CSS, please read:

   http://docs.gantry.org/gantry5/configure/styles
   http://docs.gantry.org/gantry5/tutorials/adding-a-custom-style-sheet
 */
WARN;
            $css = $warning . "\n\n" . $css;
        } else {
            $css = "{$this->checksum()}\n{$css}";
        }

        $file->save($css);
        $file->unlock();
        $file->free();

        $this->createMeta($out, md5($css));
        $this->compiler->cleanParsedFiles();

        return true;
    }

    /**
     * @param string   $name       Name of function to register to the
compiler.
     * @param callable $callback   Function to run when called by the
compiler.
     * @return $this
     */
    public function registerFunction($name, callable $callback)
    {
        $this->compiler->registerFunction($name, $callback);

        return $this;
    }

    /**
     * @param string $name       Name of function to unregister.
     * @return $this
     */
    public function unregisterFunction($name)
    {
        $this->compiler->unregisterFunction($name);

        return $this;
    }

    /**
     * @param string $url
     * @return null|string
     * @internal
     */
    public function findImport($url)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Ignore vanilla css and external requests.
        if (preg_match('/\.css$|^https?:\/\//', $url)) {
            return null;
        }

        // Try both normal and the _partial filename.
        $files = array($url, preg_replace('/[^\/]+$/',
'_\0', $url));

        foreach ($this->paths as $base) {
            foreach ($files as $file) {
                if (!preg_match('|\.scss$|', $file)) {
                    $file .= '.scss';
                }
                if ($locator->findResource($base . '/' .
$file)) {
                    return $base . '/' . $file;
                }
            }
        }

        return null;
    }
}
classes/Gantry/Component/System/Messages.php000064400000001175151166614520015166
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Component\System;

class Messages
{
    protected $messages = [];

    public function add($message, $type = 'warning')
    {
        $this->messages[] = ['type' => $type,
'message' => $message];

        return $this;
    }

    public function get()
    {
        return $this->messages;
    }

    public function clean()
    {
        $this->messages = [];

        return $this;
    }
}
classes/Gantry/Component/Theme/AbstractTheme.php000064400000016062151166614520015724
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Twig\TwigCacheFilesystem;
use Gantry\Component\Twig\TwigExtension;
use Gantry\Framework\Platform;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class AbstractTheme
 * @package Gantry\Component
 *
 * @property string $path
 * @property string $layout
 */
abstract class AbstractTheme
{
    use GantryTrait;

    /**
     * @var string
     */
    public $name;

    /**
     * @var string
     */
    public $path;

    /**
     * @var \Twig_Environment
     */
    protected $renderer;

    /**
     * Construct theme object.
     *
     * @param string $path
     * @param string $name
     */
    public function __construct($path, $name = null)
    {
        if (!is_dir($path)) {
            throw new \LogicException('Theme not found!');
        }

        $this->name = $name ? $name : basename($path);
        $this->path = $path;

        $this->init();
    }

    /**
     * Get context for render().
     *
     * @param array $context
     * @return array
     */
    public function getContext(array $context)
    {
        $context['theme'] = $this;

        return $context;
    }

    /**
     * Define twig environment.
     *
     * @param \Twig_Environment $twig
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Environment
     */
    public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
    {
        if
($twig->hasExtension('Gantry\Component\Twig\TwigExtension')) {
            return $twig;
        }

        if (!$loader) {
            $loader = $twig->getLoader();
        }

        $this->setTwigLoaderPaths($loader);

        $twig->addExtension(new TwigExtension);

        if (method_exists($this, 'toGrid')) {
            $filter = new \Twig_SimpleFilter('toGrid', [$this,
'toGrid']);
            $twig->addFilter($filter);
        }

        return $twig;
    }

    /**
     * Return renderer.
     *
     * @return \Twig_Environment
     */
    public function renderer()
    {
        if (!$this->renderer) {
            $gantry = static::gantry();

            /** @var Config $global */
            $global = $gantry['global'];

            $cachePath = $global->get('compile_twig', 1) ?
$this->getCachePath('twig') : null;
            $cache = $cachePath ? new TwigCacheFilesystem($cachePath,
\Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION) : null;
            $debug = $gantry->debug();
            $production = (bool) $global->get('production',
1);
            $loader = new \Twig_Loader_Filesystem();
            $params = [
                'cache' => $cache,
                'debug' => $debug,
                'auto_reload' => !$production,
                'autoescape' => 'html'
            ];

            $twig = new \Twig_Environment($loader, $params);

            $this->setTwigLoaderPaths($loader);

            if ($debug) {
                $twig->addExtension(new \Twig_Extension_Debug());
            }

            $this->renderer = $this->extendTwig($twig, $loader);
        }

        return $this->renderer;
    }

    /**
     * Render a template file by using given context.
     *
     * @param string $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = [])
    {
        // Include Gantry specific things to the context.
        $context = $this->getContext($context);

        return $this->renderer()->render($file, $context);
    }

    /**
     * Compile and render twig string.
     *
     * @param string $string
     * @param array $context
     * @return string
     */
    public function compile($string, array $context = [])
    {
        $renderer = $this->renderer();
        $template = $renderer->createTemplate($string);

        // Include Gantry specific things to the context.
        $context = $this->getContext($context);

        return $template->render($context);
    }

    /**
     * Initialize theme.
     */
    protected function init()
    {
        $gantry = static::gantry();
        $gantry['streams']->register();

        // Only add error service if development or debug mode has been
enabled or user is admin.
        if (!$gantry['global']->get('production', 0)
|| $gantry->debug() || $gantry->admin()) {
            $gantry->register(new ErrorServiceProvider);
        }

        // Initialize theme cache stream.
        $cachePath = $this->getCachePath();

        Folder::create($cachePath);

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $locator->addPath('gantry-cache', 'theme',
[$cachePath], true, true);

        CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
        CompiledYamlFile::$defaultCaching =
$gantry['global']->get('compile_yaml', 1);
    }

    /**
     * Set twig lookup paths to the loader.
     *
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Loader_Filesystem|null
     * @internal
     */
    protected function setTwigLoaderPaths(\Twig_LoaderInterface $loader)
    {
        if ($loader instanceof \Twig_Loader_Chain) {
            $new = new \Twig_Loader_Filesystem();
            $loader->addLoader($new);
            $loader = $new;
        } elseif (!($loader instanceof \Twig_Loader_Filesystem)) {
            return null;
        }

        $gantry = static::gantry();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

       
$loader->setPaths($locator->findResources('gantry-engine://templates'),
'nucleus');
       
$loader->setPaths($locator->findResources('gantry-particles://'),
'particles');

        return $loader;
    }

    /**
     * Get path to Twig cache.
     *
     * @param string $path
     * @return string
     */
    protected function getCachePath($path = '')
    {
        $gantry = static::gantry();

        /** @var Platform $patform */
        $patform = $gantry['platform'];

        // Initialize theme cache stream.
        return $patform->getCachePath() . '/' . $this->name
. ($path ? '/' . $path : '');
    }

    /**
     * @deprecated 5.0.2
     */
    public function debug()
    {
        return static::gantry()->debug();
    }

    /**
     * @deprecated 5.1.5
     */
    public function add_to_context(array $context)
    {
        return $this->getContext($context);
    }

    /**
     * @deprecated 5.1.5
     */
    public function add_to_twig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
    {
        return $this->extendTwig($twig, $loader);
    }
}
classes/Gantry/Component/Theme/ThemeDetails.php000064400000013737151166614520015554
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Streams;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class ThemeDetails
 * @package Gantry\Component\Theme
 */
class ThemeDetails implements \ArrayAccess
{
    use NestedArrayAccessWithGetters, Export;

    protected $items;
    protected $parent;

    /**
     * Create new theme details.
     *
     * @param string $theme
     */
    public function __construct($theme)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $filename =
$locator->findResource("gantry-themes://{$theme}/gantry/theme.yaml");
        if (!$filename) {
            throw new \RuntimeException(sprintf('Theme %s not
found', $theme), 404);
        }

        $cache =
$locator->findResource("gantry-cache://{$theme}/compiled/yaml",
true, true);

        $file = CompiledYamlFile::instance($filename);
        $this->items = $file->setCachePath($cache)->content();
        $file->free();

        $this->offsetSet('name', $theme);

        $parent = (string)
$this->get('configuration.theme.parent', $theme);
        $parent = $parent != $theme ? $parent : null;

        $this->offsetSet('parent', $parent);
    }

    /**
     * @return string
     */
    public function addStreams()
    {
        $gantry = Gantry::instance();

        // Initialize theme stream.
        $streamName =
$this->addStream($this->offsetGet('name'),
$this->getPaths());

        // Initialize parent theme streams.
        $loaded = [$this->offsetGet('name')];
        $details = $this;

        while ($details = $details->parent()) {
            if (in_array($details->name, $loaded)) {
                break;
            }
            $this->addStream($details->name,
$details->getPaths(false));
            $loaded[] = $details->name;
        }

        /** @var Streams $streams */
        $streams = $gantry['streams'];
        $streams->register();

        return $streamName;
    }

    /**
     * Get parent theme details if theme has a parent.
     *
     * @return ThemeDetails|null
     * @throws \RuntimeException
     */
    public function parent()
    {
        $parent = $this->offsetGet('parent');

        if (!$this->parent && $parent) {
            try {
                $this->parent = new ThemeDetails($parent);
            } catch (\RuntimeException $e) {
                throw new \RuntimeException(sprintf('Parent theme %s
not found', $parent), 404);
            }
        }

        return $this->parent;
    }

    /**
     * Get all possible paths to the theme.
     *
     * @return array
     */
    public function getPaths($overrides = true)
    {
        $paths = array_merge(
            $overrides ? (array)
$this->get('configuration.theme.overrides',
'gantry-theme://custom') : [],
            ['gantry-theme://'],
            (array) $this->get('configuration.theme.base',
'gantry-theme://common')
        );

        $parent = $this->offsetGet('parent');
        if ($parent) {
            // Stream needs to be valid URL.
            $streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $parent);
            $paths[] = "{$streamName}://";
        }

        return $this->parsePaths($paths);
    }

    /**
     * Convert theme path into stream URI.
     *
     * @param string $path
     * @return string
     */
    public function getUrl($path)
    {
        $uri = (string) $this->offsetGet($path);

        if (strpos($uri, 'gantry-theme://') === 0) {
            list (, $uri) = explode('://', $uri, 2);
        }
        if (!strpos($uri, '://')) {
            $name = $this->offsetGet('name');

            // Stream needs to be valid URL.
            $streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $name);
            $uri = "{$streamName}://{$uri}";
        }

        return $uri;
    }

    /**
     * Turn list of theme paths to be universal, so they can be used
outside of the theme.
     *
     * @param array $items
     * @return array
     */
    public function parsePaths(array $items)
    {
        foreach ($items as &$item) {
            $item = $this->parsePath($item);
        }

        return $items;
    }

    /**
     * Convert theme paths to be universal, so they can be used outside of
the theme.
     *
     * @param string $path
     * @return string
     */
    public function parsePath($path)
    {
        if (strpos($path, 'gantry-theme://') === 0) {
            list (, $path) = explode('://', $path, 2);
        }
        if (!strpos($path, '://')) {
            $name = $this->offsetGet('name');
            $path = "gantry-themes://{$name}/{$path}";
        }

        return $path;
    }

    /**
     * @return string|null
     * @deprecated 5.1.5
     */
    public function getParent()
    {
        return $this->offsetGet('parent');
    }

    /**
     * @param string $name
     * @param array $paths
     * @return string|null
     */
    protected function addStream($name, $paths)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        /** @var Streams $streams */
        $streams = $gantry['streams'];

        // Add theme stream.
        $streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $name);
        if (!$locator->schemeExists($streamName)) {
            $streams->add([$streamName => ['paths' =>
$paths]]);
        }

        return $streamName;
    }
}
classes/Gantry/Component/Theme/ThemeInstaller.php000064400000022755151166614520016124
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Layout\Layout;
use Gantry\Framework\Gantry;
use Gantry\Framework\Platform;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class ThemeInstaller
{
    /**
     * Set to true if in Gantry.
     *
     * @var bool
     */
    public $initialized = false;
    public $actions = [];

    protected $name;
    protected $outlines;
    protected $script;

    public function __construct($extension = null)
    {
        if ($extension) {
            $this->name = $extension;
        }
    }

    abstract public function getPath();

    /**
     * Get list of available outlines.
     *
     * @param array $filter
     * @return array
     */
    public function getOutlines(array $filter = null)
    {
        if (!isset($this->outlines)) {
            $this->outlines = [];
            $path = $this->getPath();

            // If no outlines are given, try loading outlines.yaml file.
            $file = YamlFile::instance($path .
'/install/outlines.yaml');

            if ($file->exists()) {
                // Load the list from the yaml file.
                $this->outlines = (array) $file->content();
                $file->free();

            } elseif (is_dir($path . '/install/outlines')) {
                // Build the list from the install folder.
                // recurse = false, full=true
                $folders = Folder::all($path .
'/install/outlines', ['folders' => true,
'recursive' => false]);
                foreach ($folders as $folder) {
                    $this->outlines[basename($folder)] = [];
                }
            }

            // Always include system outlines.
            $this->outlines += ['default' => [],
'_body_only' => [], '_error' => [],
'_offline' => []];

        }

        return is_array($filter) ? array_intersect_key($this->outlines,
array_flip($filter)) : $this->outlines;
    }

    public function getOutline($name)
    {
        $list = $this->getOutlines([$name]);

        return reset($list);
    }

    public function installDefaults()
    {
        $installerScript = $this->getInstallerScript();

        if ($installerScript && method_exists($installerScript,
'installDefaults')) {
            $installerScript->installDefaults($this);
        } else {
            $this->createDefaults();
        }
    }

    public function installSampleData()
    {
        $installerScript = $this->getInstallerScript();

        if ($installerScript && method_exists($installerScript,
'installSampleData')) {
            $installerScript->installSampleData($this);
        } else {
            $this->createSampleData();
        }
    }

    public function createDefaults()
    {
        $this->createOutlines();
    }

    public function createSampleData()
    {
    }

    public function render($template, $context = [])
    {
        try {
            $loader = new \Twig_Loader_Filesystem();
            $loader->setPaths([$this->getPath() .
'/install/templates']);

            $params = [
                'cache' => null,
                'debug' => false,
                'autoescape' => 'html'
            ];

            $twig = new \Twig_Environment($loader, $params);

            $name = $this->name;
            $context += [
                'name' => $this->translate($name),
                'actions' => $this->actions
            ];

            return $twig->render($template, $context);
        } catch (\Exception $e) {
            return '';
        }
    }

    /**
     * Set available outlines.
     *
     * @param array $outlines If parameter isn't provided, outlines
list get reloaded from the disk.
     * @return $this
     */
    public function setOutlines(array $outlines = null)
    {
        $this->outlines = $outlines;

        return $this;
    }

    /**
     * @param array $filter
     */
    public function createOutlines(array $filter = null)
    {
        $outlines = $this->getOutlines($filter);

        foreach ($outlines as $folder => $params) {
            $this->createOutline($folder, $params);
        }
    }

    /**
     * @param string $folder
     * @param array $params
     * @return string|bool
     */
    public function createOutline($folder, array $params = [])
    {
        if (!$folder) {
            throw new \RuntimeException('Cannot create outline without
folder name');
        }

        $this->initialize();

        $created = false;

        $params += [
            'preset' => null,
            'title' => null
        ];

        $title = $params['title'] ?: ucwords(trim(strtr($folder,
['_' => ' '])));
        $preset = $params['preset'] ?: 'default';

        // Copy configuration for the new layout.
        if (($this->copyCustom($folder, $folder) || $created)) {
            // Update layout and save it.
            $layout = Layout::load($folder, $preset);
            $layout->save()->saveIndex();

            if ($created) {
                $this->actions[] = ['action' =>
'outline_created', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_CREATED',
$title)];
            } else {
                $this->actions[] = ['action' =>
'outline_updated', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_UPDATED',
$title)];
            }
        }

        return $folder;
    }

    public function initialize()
    {
        if ($this->initialized) {
            return;
        }

        $name = $this->name;
        $path = $this->getPath();

        // Remove compiled CSS files if they exist.
        $cssPath = $path . '/custom/css-compiled';
        if (is_dir($cssPath)) {
            Folder::delete($cssPath);
        } elseif (is_file($cssPath)) {
            @unlink($cssPath);
        }

        // Remove wrongly named file if it exists.
        $md5path = $path . '/MD5SUM';
        if (is_file($md5path)) {
            @unlink($md5path);
        }

        // Restart Gantry and initialize it.
        $gantry = Gantry::restart();
        $gantry['theme.name'] = $name;
        $gantry['streams']->register();

        // Only add error service if debug mode has been enabled.
        if ($gantry->debug()) {
            $gantry->register(new ErrorServiceProvider);
        }

        /** @var Platform $patform */
        $patform = $gantry['platform'];

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Initialize theme stream.
        $details = new ThemeDetails($name);
        $locator->addPath('gantry-theme', '',
$details->getPaths(), false, true);

        // Initialize theme cache stream and clear theme cache.
        $cachePath = $patform->getCachePath() . '/' . $name;
        if (is_dir($cachePath)) {
            Folder::delete($cachePath);
        }
        Folder::create($cachePath);
        $locator->addPath('gantry-cache', 'theme',
[$cachePath], true, true);

        CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
        CompiledYamlFile::$defaultCaching =
$gantry['global']->get('compile_yaml', 1);

        $this->initialized = true;
    }

    public function finalize()
    {
        // Copy standard outlines if they haven't been copied already.
        $this->copyCustom('default', 'default');
        $this->copyCustom('_body_only',
'_body_only');
        $this->copyCustom('_error', '_error');
        $this->copyCustom('_offline', '_offline');

        $this->initialize();
    }

    /**
     * @param string $layout
     * @param string $id
     * @return bool True if files were copied over.
     */
    protected function copyCustom($layout, $id)
    {
        $path = $this->getPath();

        // Only copy files if the target id doesn't exist.
        $dst = $path . '/custom/config/' . $id;
        if (!$layout || !$id || is_dir($dst)) {
            return false;
        }

        // New location for G5.3.2+
        $src = $path . '/install/outlines/' . $layout;
        if (!is_dir($src)) {
            // Old and deprecated location.
            $src = $path . '/install/layouts/' . $layout;
        }

        try {
            is_dir($src) ? Folder::copy($src, $dst) : Folder::create($dst);
        } catch (\Exception $e) {
            throw new \RuntimeException("Creating configuration for
outline '{$layout}' failed: {$e->getMessage()}", 500,
$e);
        }

        return true;
    }

    protected function translate($text)
    {
        $translator = Gantry::instance()['translator'];

        $args = func_get_args();

        return call_user_func_array([$translator, 'translate'],
$args);
    }


    protected function getInstallerScript()
    {
        if (!$this->script) {
            $className = ucfirst($this->name) .
'InstallerScript';

            if (!class_exists($className)) {

                $path = "{$this->getPath()}/install.php";
                if (is_file($path)) {
                    require_once $path;
                }
            }

            if (class_exists($className)) {
                $this->script = new $className;
            }
        }

        return $this->script;
    }
}
classes/Gantry/Component/Theme/ThemeInterface.php000064400000007372151166614520016065
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\Config\Config;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Stylesheet\CssCompilerInterface;

/**
 * Class ThemeTrait
 * @package Gantry\Framework\Base
 *
 * @property string $path
 * @property string $layout
 */
interface ThemeInterface
{
    // AbstractTheme class

    /**
     * Get context for render().
     *
     * @param array $context
     * @return array
     */
    public function getContext(array $context);

    /**
     * Define twig environment.
     *
     * @param \Twig_Environment $twig
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Environment
     */
    public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null);

    /**
     * Returns renderer.
     *
     * @return \Twig_Environment
     */
    public function renderer();

    /**
     * Render a template file.
     *
     * @param string $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = array());

    // ThemeTrait class

    /**
     * Update all CSS files in the theme.
     *
     * @param array $outlines
     * @return array List of CSS warnings.
     */
    public function updateCss(array $outlines = null);

    /**
     * Set current layout.
     *
     * @param string $name
     * @param bool $force
     * @return $this
     */
    public function setLayout($name = null, $force = false);

    /**
     * Get current preset.
     *
     * @param  bool $forced     If true, return only forced preset or null.
     * @return string|null $preset
     */
    public function preset($forced = false);

    /**
     * Set preset to be used.
     *
     * @param string $name
     * @return $this
     */
    public function setPreset($name = null);

    /**
     * Return CSS compiler used in the theme.
     *
     * @return CssCompilerInterface
     * @throws \RuntimeException
     */
    public function compiler();

    /**
     * Returns URL to CSS file.
     *
     * If file does not exist, it will be created by using CSS compiler.
     *
     * @param string $name
     * @return string
     */
    public function css($name);

    /**
     * Return all CSS variables.
     *
     * @return array
     */
    public function getCssVariables();

    /**
     * Returns style presets for the theme.
     *
     * @return Config
     */
    public function presets();

    /**
     * Return name of the used layout preset.
     *
     * @return string
     * @throws \RuntimeException
     */
    public function type();

    /**
     * Load current layout and its configuration.
     *
     * @param string $name
     * @return Layout
     * @throws \LogicException
     */
    public function loadLayout($name = null);

    /**
     * Check whether layout has content bock.
     *
     * @return bool
     */
    public function hasContent();

    /**
     * Returns all non-empty segments from the layout.
     *
     * @return array
     */
    public function segments();

    /**
     * Returns details of the theme.
     *
     * @return ThemeDetails
     */
    public function details();

    /**
     * Returns configuration of the theme.
     *
     * @return array
     */
    public function configuration();

    /**
     * Function to convert block sizes into CSS classes.
     *
     * @param $text
     * @return string
     */
    public function toGrid($text);
}
classes/Gantry/Component/Theme/ThemeTrait.php000064400000055354151166614520015253
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\Config\Config;
use Gantry\Component\Content\Block\ContentBlock;
use Gantry\Component\Content\Block\HtmlBlock;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Stylesheet\CssCompilerInterface;
use Gantry\Framework\Document;
use Gantry\Framework\Menu;
use Gantry\Framework\Services\ConfigServiceProvider;
use RocketTheme\Toolbox\File\PhpFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class ThemeTrait
 * @package Gantry\Component
 *
 * @property string $path
 * @property string $layout
 */
trait ThemeTrait
{
    use GantryTrait;

    protected $layoutObject;
    protected $atoms;
    protected $segments;
    protected $preset;
    protected $cssCache;
    /**
     * @var CssCompilerInterface
     */
    protected $compiler;
    protected $equalized = [3 => 33.3, 6 => 16.7, 7 => 14.3, 8
=> 12.5, 9 => 11.1, 11 => 9.1, 12 => 8.3];

    /**
     * @var ThemeDetails
     */
    protected $details;

    /**
     * Register Theme stream.
     *
     * @param string $savePath
     */
    public function registerStream($savePath = null)
    {
        $streamName = $this->details()->addStreams();

        /** @var UniformResourceLocator $locator */
        $locator = self::gantry()['locator'];
        $locator->addPath('gantry-theme', '',
array_merge((array) $savePath, [[$streamName, '']]));
    }

    /**
     * Update all CSS files in the theme.
     *
     * @param array $outlines
     * @return array List of CSS warnings.
     */
    public function updateCss(array $outlines = null)
    {
        $gantry = static::gantry();
        $compiler = $this->compiler();

        if (is_null($outlines)) {
            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];
            $path = $locator->findResource($compiler->getTarget(),
true, true);

            // Make sure that all the CSS files get deleted.
            if (is_dir($path)) {
                Folder::delete($path, false);
            }

            $outlines = $gantry['outlines'];
        }

        // Make sure that PHP has the latest data of the files.
        clearstatcache();

        $warnings = [];
        foreach ($outlines as $outline => $title) {
            $config = ConfigServiceProvider::load($gantry, $outline);

           
$compiler->reset()->setConfiguration($outline)->setVariables($config->flatten('styles',
'-'));

            $results = $compiler->compileAll()->getWarnings();
            if ($results) {
                $warnings[$outline] = $results;
            }
        }

        return $warnings;
    }

    /**
     * Set layout to be used.
     *
     * @param string $name
     * @param bool $force
     * @return $this
     */
    public function setLayout($name = null, $force = false)
    {
        $gantry = static::gantry();

        // Force new layout to be set.
        if ($force) {
            unset($gantry['configuration']);
        }

        // Set default name only if configuration has not been set before.
        if ($name === null &&
!isset($gantry['configuration'])) {
            $name = 'default';
        }

        $outline = isset($gantry['configuration']) ?
$gantry['configuration'] : null;

        // Set configuration if given.
        if ($name && $name != $outline) {
            GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Using Gantry outline {$name}");

            $gantry['configuration'] = $name;
            unset($gantry['config']);
            $gantry['config'] =
ConfigServiceProvider::load($gantry, $name);
        }

        return $this;
    }

    /**
     * Get current preset.
     *
     * @param  bool $forced     If true, return only forced preset or null.
     * @return string|null $preset
     */
    public function preset($forced = false)
    {
        $presets = $this->presets()->toArray();

        $preset = $this->preset;

        if (!$preset && !$forced) {
            $preset =
static::gantry()['config']->get('styles.preset',
'-undefined-');
        }

        if ($preset && !isset($presets[$preset])) {
            $preset = null;
        }

        return $preset;
    }

    /**
     * Set preset to be used.
     *
     * @param string $name
     * @return $this
     */
    public function setPreset($name = null)
    {
        // Set preset if given.
        if ($name) {
            $this->preset = $name;
        }

        return $this;
    }

    /**
     * Return CSS compiler used in the theme.
     *
     * @return CssCompilerInterface
     * @throws \RuntimeException
     */
    public function compiler()
    {
        if (!$this->compiler) {
            $compilerClass = (string)
$this->details()->get('configuration.css.compiler',
'\Gantry\Component\Stylesheet\ScssCompiler');

            if (!class_exists($compilerClass)) {
                throw new \RuntimeException('CSS compiler used by the
theme not found');
            }

            $details = $this->details();

            /** @var CssCompilerInterface $compiler */
            $this->compiler = new $compilerClass();
            $this->compiler
               
->setTarget($details->get('configuration.css.target'))
               
->setPaths($details->get('configuration.css.paths'))
               
->setFiles($details->get('configuration.css.files'))
               
->setFonts($details->get('configuration.fonts'));
        }

        $preset = $this->preset(true);
        if ($preset) {
            $this->compiler->setConfiguration($preset);
        } else {
            $gantry = static::gantry();
           
$this->compiler->setConfiguration(isset($gantry['configuration'])
? $gantry['configuration'] : 'default');
        }

        return $this->compiler->reset();
    }

    /**
     * Returns URL to CSS file.
     *
     * If file does not exist, it will be created by using CSS compiler.
     *
     * @param string $name
     * @return string
     */
    public function css($name)
    {
        if (!isset($this->cssCache[$name])) {
            $compiler = $this->compiler();

            if ($compiler->needsCompile($name, [$this,
'getCssVariables'])) {
                GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer("css-{$name}", "Compiling CSS:
{$name}") && \Gantry\Debugger::addMessage("Compiling CSS:
{$name}");

                $compiler->compileFile($name);

                GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer("css-{$name}");
            }

            $this->cssCache[$name] = $compiler->getCssUrl($name);
        }

        return $this->cssCache[$name];
    }

    public function getCssVariables()
    {
        if ($this->preset) {
            $variables = $this->presets()->flatten($this->preset .
'.styles', '-');
        } else {
            $gantry = self::gantry();
            $variables =
$gantry['config']->flatten('styles', '-');
        }

        return $variables;
    }

    /**
     * Returns style presets for the theme.
     *
     * @return Config
     */
    public function presets()
    {
        static $presets;

        if (!$presets) {
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];

            $filename =
$locator->findResource("gantry-theme://gantry/presets.yaml");
            $file = CompiledYamlFile::instance($filename);
            $presets = new Config($file->content());
            $file->free();
        }

        return $presets;
    }

    /**
     * Return name of the used layout preset.
     *
     * @return string
     * @throws \RuntimeException
     */
    public function type()
    {
        if (!$this->layoutObject) {
            throw new \RuntimeException('Function called too
early');
        }
        $name = isset($this->layoutObject->preset['name'])
? $this->layoutObject->preset['name'] :
'unknown';

        return $name;
    }

    /**
     * Load current layout and its configuration.
     *
     * @param string $name
     * @return Layout
     * @throws \LogicException
     */
    public function loadLayout($name = null)
    {
        if (!$name) {
            try {
                $name = static::gantry()['configuration'];
            } catch (\Exception $e) {
                throw new \LogicException('Gantry: Outline has not
been defined yet', 500);
            }
        }

        if (!isset($this->layoutObject) ||
$this->layoutObject->name != $name) {
            $layout = Layout::instance($name);

            if (!$layout->exists()) {
                $layout = Layout::instance('default');
            }

            // TODO: Optimize
            $this->layoutObject = $layout->init();
        }

        return $this->layoutObject;
    }

    /**
     * Check whether layout has content bock.
     *
     * @return bool
     */
    public function hasContent()
    {
        $layout = $this->loadLayout();
        $content = $layout->referencesByType('system',
'content');

        return !empty($content);
    }

    /**
     * Load atoms and assets from the page settings.
     *
     * @since 5.4.9
     */
    public function loadAtoms()
    {
        if (!isset($this->atoms)) {
            $this->atoms = true;

            GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('atoms', "Preparing
atoms");

            $gantry = static::gantry();

            /** @var Config $config */
            $config = $gantry['config'];

            /** @var \Gantry\Framework\Document $document */
            $document = $gantry['document'];

            $atoms = (array) $config->get('page.head.atoms');

            foreach ($atoms as $data) {
                $atom = [
                    'type' => 'atom',
                    'subtype' => $data['type'],
                ] + $data;

                try {
                    $block = $this->getContent($atom);
                    $document->addBlock($block);

                } catch (\Exception $e) {
                    if ($gantry->debug()) {
                        throw new \RuntimeException("Rendering Atom
'{$atom['subtype']}' failed on error:
{$e->getMessage()}", 500, $e);
                    }
                }
            }

            $assets = (array) $config->get('page.assets');

            if ($assets) {
                $atom = [
                    'id' => 'page-assets',
                    'title' => 'Page Assets',
                    'type' => 'atom',
                    'subtype' => 'assets',
                    'attributes' => $assets +
['enabled' => 1]
                ];

                try {
                    $block = $this->getContent($atom);
                    $document->addBlock($block);

                } catch (\Exception $e) {
                    if ($gantry->debug()) {
                        throw new \RuntimeException("Rendering CSS/JS
Assets failed on error: {$e->getMessage()}", 500, $e);
                    }
                }
            }

            GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('atoms');
        }
    }

    /**
     * Returns all non-empty segments from the layout.
     *
     * @return array
     */
    public function segments()
    {
        if (!isset($this->segments)) {
            $this->segments = $this->loadLayout()->toArray();

            GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('segments', "Preparing
layout");

            $this->prepareLayout($this->segments);

            GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('segments');
        }

        return $this->segments;
    }

    /**
     * Prepare layout for rendering. Initializes all CSS/JS in particles.
     */
    public function prepare()
    {
        $this->segments();
    }

    /**
     * Returns details of the theme.
     *
     * @return ThemeDetails
     */
    public function details()
    {
        if (!$this->details) {
            $this->details = new ThemeDetails($this->name);
        }
        return $this->details;
    }

    /**
     * Returns configuration of the theme.
     *
     * @return array
     */
    public function configuration()
    {
        return (array) $this->details()['configuration'];
    }

    /**
     * Function to convert block sizes into CSS classes.
     *
     * @param $text
     * @return string
     */
    public function toGrid($text)
    {
        if (!$text) {
            return '';
        }

        $number = round($text, 1);
        $number = max(5, $number);
        $number = (string) ($number == 100 ? 100 : min(95, $number));

        static $sizes = array(
            '33.3' => 'size-33-3',
            '16.7' => 'size-16-7',
            '14.3' => 'size-14-3',
            '12.5' => 'size-12-5',
            '11.1' => 'size-11-1',
            '9.1'  => 'size-9-1',
            '8.3'  => 'size-8-3'
        );

        return isset($sizes[$number]) ? ' ' . $sizes[$number] :
'size-' . (int) $number;
    }

    /**
     * Magic setter method
     *
     * @param mixed $offset Asset name value
     * @param mixed $value  Asset value
     */
    public function __set($offset, $value)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        $this->details()->offsetSet('details.' . $offset,
$value);
    }

    /**
     * Magic getter method
     *
     * @param  mixed $offset Asset name value
     * @return mixed         Asset value
     */
    public function __get($offset)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        $value = $this->details()->offsetGet('details.' .
$offset);

        if ($offset == 'version' && is_int($value)) {
            $value .= '.0';
        }

        return $value;
    }

    /**
     * Magic method to determine if the attribute is set
     *
     * @param  mixed   $offset Asset name value
     * @return boolean         True if the value is set
     */
    public function __isset($offset)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        return $this->details()->offsetExists('details.' .
$offset);
    }

    /**
     * Magic method to unset the attribute
     *
     * @param mixed $offset The name value to unset
     */
    public function __unset($offset)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        $this->details()->offsetUnset('details.' .
$offset);
    }

    /**
     * Prepare layout by loading all the positions and particles.
     *
     * Action is needed before displaying the layout as it recalculates
block widths based on the visible content.
     *
     * @param array $items
     * @param bool  $temporary
     * @param bool  $sticky
     * @internal
     */
    protected function prepareLayout(array &$items, $temporary = false,
$sticky = false)
    {
        foreach ($items as $i => &$item) {
            // Non-numeric items are meta-data which should be ignored.
            if (((string)(int) $i !== (string) $i) || !is_object($item)) {
                continue;
            }

            if (!empty($item->children)) {
                $fixed = true;
                foreach ($item->children as $child) {
                    $fixed &= !empty($child->attributes->fixed);
                }

                $this->prepareLayout($item->children, $fixed,
$temporary);
            }

            // TODO: remove hard coded types.
            switch ($item->type) {
                case 'system':
                    break;

                case 'atom':
                case 'particle':
                case 'position':
                case 'spacer':
                    GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer($item->id, "Rendering
{$item->id}");

                    $item->content = $this->renderContent($item,
['prepare_layout' => true]);
                    // Note that content can also be null (postpone
rendering).
                    if ($item->content === '') {
                        unset($items[$i]);
                    }

                    GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer($item->id);

                    break;

                default:
                    if ($sticky) {
                        $item->attributes->sticky = 1;
                        break;
                    }

                    if (empty($item->children)) {
                        unset($items[$i]);
                        break;
                    }

                    $dynamicSize = 0;
                    $fixedSize = 0;
                    $childrenCount = count($item->children);
                    foreach ($item->children as $child) {
                        if (!isset($child->attributes->size)) {
                            $child->attributes->size = 100 /
count($item->children);
                        }
                        if (empty($child->attributes->fixed)) {
                            $dynamicSize += $child->attributes->size;
                        } else {
                            $fixedSize += $child->attributes->size;
                        }
                    }

                    $roundSize = round($dynamicSize, 1);
                    $equalized = isset($this->equalized[$childrenCount])
? $this->equalized[$childrenCount] : 0;

                    // force-casting string for testing comparison due to
weird PHP behavior that returns wrong result
                    if ($roundSize != 100 && (string) $roundSize !=
(string) ($equalized * $childrenCount)) {
                        $fraction = 0;
                        $multiplier = (100 - $fixedSize) / ($dynamicSize ?:
1);
                        foreach ($item->children as $child) {
                            if (!empty($child->attributes->fixed)) {
                                continue;
                            }

                            // Calculate size for the next item by taking
account the rounding error from the last item.
                            // This will allow us to approximate cumulating
error and fix it when rounding error grows
                            // over the rounding treshold.
                            $size = ($child->attributes->size *
$multiplier) + $fraction;
                            $newSize = round($size);
                            $fraction = $size - $newSize;
                            $child->attributes->size = $newSize;
                        }
                    }
            }
        }
    }

    /**
     * Renders individual content block, like particle or position.
     *
     * Function is used to pre-render content.
     *
     * @param object|array $item
     * @param array $options
     * @return string|null
     */
    public function renderContent($item, $options = [])
    {
        $gantry = static::gantry();

        $content = $this->getContent($item, $options);

        /** @var Document $document */
        $document = $gantry['document'];
        $document->addBlock($content);

        $html = $content->toString();
        return !strstr($html, '@@DEFERRED@@') ? $html : null;
    }

    /**
     * Renders individual content block, like particle or position.
     *
     * Function is used to pre-render content.
     *
     * @param object|array $item
     * @param array $options
     * @return ContentBlock
     * @since 5.4.3
     */
    public function getContent($item, $options = [])
    {
        if (is_array($item)) {
            $item = (object) $item;
        }

        $gantry = static::gantry();

        /** @var Config $global */
        $global = $gantry['global'];

        $production = (bool) $global->get('production');
        $subtype = $item->subtype;
        $enabled =
$gantry['config']->get("particles.{$subtype}.enabled",
1);

        if (!$enabled) {
            return new HtmlBlock;
        }

        $attributes = isset($item->attributes) ? $item->attributes :
[];
        $particle =
$gantry['config']->getJoined("particles.{$subtype}",
$attributes);

        $cached = false;
        $cacheKey = [];

        // Enable particle caching only in production mode.
        if ($production && isset($particle['caching'])) {
            $caching = $particle['caching'] + ['type'
=> 'dynamic'];

            switch ($caching['type']) {
                case 'static':
                    $cached = true;
                    break;
                case 'config_matches':
                    if
(isset($particle['caching']['values'])) {
                        $values = (array)
$particle['caching']['values'];
                        $compare = array_intersect_key($particle, $values);
                        $cached = ($values === $compare);
                    }
                    break;
                case 'menu':
                    /** @var Menu $menu */
                    $menu = $gantry['menu'];
                    $cacheId = $menu->getCacheId();

                    // FIXME: menu caching needs to handle dynamic modules
inside menu: turning it off for now.
                    if (false && $cacheId !== null) {
                        $cached = true;
                        $cacheKey['menu_cache_key'] = $cacheId;
                    }
                    break;
            }
        }

        if ($cached) {
            $cacheKey['language'] =
$gantry['page']->language;
            $cacheKey['attributes'] = $particle;
            $cacheKey += (array) $item;

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];
            $key = md5(json_encode($cacheKey));

            $filename =
$locator->findResource("gantry-cache://theme/html/{$key}.php",
true, true);
            $file = PhpFile::instance($filename);
            if ($file->exists()) {
                try {
                    return ContentBlock::fromArray((array)
$file->content());
                } catch (\Exception $e) {
                    // Invalid cache, continue to rendering.
                    GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage(sprintf('Failed to load %s %s
cache', $item->type, $item->id), 'debug');
                }
            }
        }

        // Create new document context for assets.
        $context = $this->getContext(['segment' => $item,
'enabled' => 1, 'particle' => $particle] +
$options);

        /** @var Document $document */
        $document = $gantry['document'];
        $document->push();
        $html =
trim($this->render("@nucleus/content/{$item->type}.html.twig",
$context));
        $content = $document->pop()->setContent($html);

        if (isset($file)) {
            // Save HTML and assets into the cache.
            GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage(sprintf('Caching %s %s',
$item->type, $item->id), 'debug');
            $file->save($content->toArray());
        }

        return $content;
    }
}
classes/Gantry/Component/Translator/Translator.php000064400000006704151166614520016420
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Translator;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Translator implements TranslatorInterface
{
    protected $default = 'en';
    protected $active = 'en';
    protected $sections = [];
    protected $translations = [];
    protected $untranslated = [];

    public function translate($string)
    {
        if (preg_match('|^GANTRY5(_[A-Z0-9]+){2,}$|', $string)) {
            list(, $section, $code) = explode('_', $string, 3);

            $string = ($this->find($this->active, $section, $string)
?: $this->find($this->default, $section, $string)) ?: $string;
        }

        if (func_num_args() === 1) {
            return $string;
        }

        $args = func_get_args();
        $args[0] = $string;

        return call_user_func_array('sprintf', $args);
    }

    /**
     * Set new active language if given and return previous active
language.
     *
     * @param  string  $language  Language code. If not given, current
language is kept.
     * @return string  Previously active language.
     */
    public function active($language = null)
    {
        $previous = $this->active;

        if ($language) {
            $this->active = $language;
        }

        return $previous;
    }

    public function untranslated()
    {
        return $this->untranslated;
    }

    protected function find($language, $section, $string)
    {
        if (!isset($this->sections[$language][$section])) {
            $translations = $this->load($language, $section);

            if (isset($this->translations[$language])) {
                $this->translations[$language] += $translations;
            } else {
                $this->translations[$language] = $translations;
            }

            $this->sections[$language][$section] =
!empty($translations);
        }

        if (!isset($this->translations[$language][$string])) {
            $this->untranslated[$language][$section][$string] = null;

            return null;
        }

        return $this->translations[$language][$string];
    }

    protected function load($language, $section)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $section = strtolower($section);
        if ($section === 'engine') {
            // TODO: add support for other engines than nucleus.
            $section = 'nucleus';
        }

        $filename = 'gantry-admin://translations/' . $language .
'/' . $section . '.yaml';
        $file = CompiledYamlFile::instance($filename);

        if (!$file->exists() && ($pos = strpos($language,
'-')) > 0) {
            $filename = 'gantry-admin://translations/' .
substr($language, 0, $pos) . '/' . $section . '.yaml';
            $file = CompiledYamlFile::instance($filename);
        }

        $cachePath =
$locator->findResource('gantry-cache://translations', true,
true);
        $translations = (array)
$file->setCachePath($cachePath)->content();
        $file->free();

        return $translations;
    }
}
classes/Gantry/Component/Translator/TranslatorInterface.php000064400000001507151166614520020235
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Translator;

interface TranslatorInterface
{
    /**
     * @param string $string
     * @return string
     */
    public function translate($string);

    /**
     * Set new active language if given and return previous active
language.
     *
     * @param  string  $language  Language code. If not given, current
language is kept.
     * @return string  Previously active language.
     */
    public function active($language = null);
}
classes/Gantry/Component/Twig/Node/TwigNodeAssets.php000064400000004267151166614520016642
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeAssets extends \Twig_Node implements
\Twig_NodeCaptureInterface
{
    protected $tagName = 'assets';

    public function __construct(\Twig_Node $body = null,
\Twig_Node_Expression $location = null, \Twig_Node_Expression $variables =
null, $lineno = 0, $tag = null)
    {
        parent::__construct(['body' => $body,
'location' => $location, 'variables' =>
$variables], [], $lineno, $tag);
    }
    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this)
            ->write("\$assetFunction =
\$this->env->getFunction('parse_assets')->getCallable();\n")
            ->write('$assetVariables = ')
            ->subcompile($this->getNode('variables'))
            ->raw(";\n")
            ->write("if (\$assetVariables &&
!is_array(\$assetVariables)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} with x %}: x is not an array');\n")
            ->outdent()
            ->write("}\n")
            ->write('$location = ')
            ->subcompile($this->getNode('location'))
            ->raw(";\n")
            ->write("if (\$location &&
!is_string(\$location)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} in x %}: x is not a string');\n")
            ->outdent()
            ->write("}\n")
            ->write("\$priority =
isset(\$assetVariables['priority']) ?
\$assetVariables['priority'] : 0;\n")
            ->write("ob_start();\n")
            ->subcompile($this->getNode('body'))
            ->write("\$content = ob_get_clean();\n")
            ->write("\$assetFunction(\$content, \$location,
\$priority);\n");
    }
}
classes/Gantry/Component/Twig/Node/TwigNodeMarkdown.php000064400000002701151166614520017151
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeMarkdown extends \Twig_Node implements
\Twig_NodeOutputInterface
{
    public function __construct(\Twig_Node $body, $lineno, $tag =
'markdown')
    {
        parent::__construct(['body' => $body], [], $lineno,
$tag);
    }
    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('ob_start();' . PHP_EOL)
            ->subcompile($this->getNode('body'))
            ->write('$content = ob_get_clean();' . PHP_EOL)
            ->write('preg_match("/^\s*/", $content,
$matches);' . PHP_EOL)
            ->write('$lines = explode("\n",
$content);' . PHP_EOL)
            ->write('$content = preg_replace(\'/^\' .
$matches[0]. \'/\', "", $lines);' . PHP_EOL)
            ->write('$content = join("\n",
$content);' . PHP_EOL)
            ->write('echo
$this->env->getExtension(\'Gantry\Component\Twig\TwigExtension\')->markdownFunction($content);'
. PHP_EOL);
    }
}
classes/Gantry/Component/Twig/Node/TwigNodePageblock.php000064400000004226151166614520017262
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodePageblock extends \Twig_Node implements
\Twig_NodeCaptureInterface
{
    protected $tagName = 'pageblock';

    public function __construct(\Twig_Node $body = null,
\Twig_Node_Expression $location = null, \Twig_Node_Expression $variables =
null, $lineno = 0, $tag = null)
    {
        parent::__construct(['body' => $body,
'location' => $location, 'variables' =>
$variables], [], $lineno, $tag);
    }
    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this)
            ->write('$pageblockVariables = ')
            ->subcompile($this->getNode('variables'))
            ->raw(";\n")
            ->write("if (\$pageblockVariables &&
!is_array(\$pageblockVariables)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} with x %}: x is not an array');\n")
            ->outdent()
            ->write("}\n")
            ->write('$location = ')
            ->subcompile($this->getNode('location'))
            ->raw(";\n")
            ->write("if (\$location &&
!is_string(\$location)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} in x %}: x is not a string');\n")
            ->outdent()
            ->write("}\n")
            ->write("\$priority =
isset(\$pageblockVariables['priority']) ?
\$pageblockVariables['priority'] : 0;\n")
            ->write("ob_start();\n")
            ->subcompile($this->getNode('body'))
            ->write("\$content = ob_get_clean();\n")
           
->write("Gantry\Framework\Gantry::instance()['document']->addHtml(\$content,
\$priority, \$location);\n");
    }
}
classes/Gantry/Component/Twig/Node/TwigNodeScripts.php000064400000000757151166614520017027
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeScripts extends TwigNodeAssets
{
    protected $tagName = 'scripts';
}
classes/Gantry/Component/Twig/Node/TwigNodeStyles.php000064400000000756151166614520016662
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeStyles extends TwigNodeScripts
{
    protected $tagName = 'styles';
}
classes/Gantry/Component/Twig/Node/TwigNodeSwitch.php000064400000004011151166614520016624
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeSwitch extends \Twig_Node
{
    public function __construct(\Twig_Node $value, \Twig_Node $cases,
\Twig_Node $default = null, $lineno = 0, $tag = null)
    {
        parent::__construct(array('value' => $value,
'cases' => $cases, 'default' => $default),
array(), $lineno, $tag);
    }

    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('switch (')
            ->subcompile($this->getNode('value'))
            ->raw(") {\n")
            ->indent();

        foreach ($this->getNode('cases') as $case) {
            if (!$case->hasNode('body')) {
                continue;
            }

            foreach ($case->getNode('values') as $value) {
                $compiler
                    ->write('case ')
                    ->subcompile($value)
                    ->raw(":\n");
            }

            $compiler
                ->write("{\n")
                ->indent()
                ->subcompile($case->getNode('body'))
                ->write("break;\n")
                ->outdent()
                ->write("}\n");
        }

        if ($this->hasNode('default') &&
$this->getNode('default') !== null) {
            $compiler
                ->write("default:\n")
                ->write("{\n")
                ->indent()
                ->subcompile($this->getNode('default'))
                ->outdent()
                ->write("}\n");
        }

        $compiler
            ->outdent()
            ->write("}\n");
    }
}
classes/Gantry/Component/Twig/Node/TwigNodeThrow.php000064400000002221151166614520016467
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeThrow extends \Twig_Node
{
    public function __construct(
        $code,
        \Twig_Node $message,
        $lineno = 0,
        $tag = null
    )
    {
        parent::__construct(['message' => $message],
['code' => $code], $lineno, $tag);
    }

    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     * @throws \LogicException
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        $compiler
            ->write('throw new \RuntimeException(')
            ->subcompile($this->getNode('message'))
            ->write(', ')
            ->write($this->getAttribute('code') ?: 500)
            ->write(");\n");
    }
}
classes/Gantry/Component/Twig/Node/TwigNodeTryCatch.php000064400000003233151166614520017111
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeTryCatch extends \Twig_Node
{
    public function __construct(\Twig_Node $try, \Twig_Node $catch = null,
$lineno = 0, $tag = null)
    {
        parent::__construct(array('try' => $try,
'catch' => $catch), array(), $lineno, $tag);
    }

    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        $compiler
            ->write('try {')
        ;

        $compiler
            ->indent()
            ->subcompile($this->getNode('try'))
        ;

        if ($this->hasNode('catch') && null !==
$this->getNode('catch')) {
            $compiler
                ->outdent()
                ->write('} catch (\Exception $e) {' .
"\n")
                ->indent()
                ->write('if
($context[\'gantry\']->debug()) throw $e;' .
"\n")
                ->write('GANTRY_DEBUGGER &&
method_exists(\'Gantry\\Debugger\', \'addException\')
&& \Gantry\Debugger::addException($e);' . "\n")
                ->write('$context[\'e\'] = $e;' .
"\n")
                ->subcompile($this->getNode('catch'))
            ;
        }

        $compiler
            ->outdent()
            ->write("}\n");
    }
}
classes/Gantry/Component/Twig/TokenParser/TokenParserAssets.php000064400000005221151166614520020716
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeScripts;

/**
 * Adds javascript / style assets to head/footer/custom location.
 *
 * {% assets in 'head' with { priority: 2 } %}
 *   <script type="text/javascript" src="{{
url('gantry-theme://js/my.js') }}"></script>
 *   <link rel="stylesheet" href="{{
url('gantry-assets://css/font-awesome.min.css') }}"
type="text/css"/>
 * {% endassets -%}
 */
class TokenParserAssets extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        list($location, $variables) = $this->parseArguments($token);

        $content = $this->parser->subparse([$this,
'decideBlockEnd'], true);
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeScripts($content, $location, $variables,
$lineno, $this->getTag());
    }

    /**
     * @param \Twig_Token $token
     * @return array
     */
    protected function parseArguments(\Twig_Token $token)
    {
        $stream = $this->parser->getStream();
        $location = null;
        if ($stream->nextIf(\Twig_Token::OPERATOR_TYPE, 'in'))
{
            $location =
$this->parser->getExpressionParser()->parseExpression();
        } else {
            $lineno = $token->getLine();
            $location = new
\Twig_Node_Expression_Constant('head', $lineno);
        }

        if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'with')) {
            $variables =
$this->parser->getExpressionParser()->parseExpression();
        } else {
            $lineno = $token->getLine();
            $variables = new \Twig_Node_Expression_Array([], $lineno);
            $variables->setAttribute('priority', 0);
        }
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return [$location, $variables];
    }

    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endassets');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'assets';
    }
}
classes/Gantry/Component/Twig/TokenParser/TokenParserMarkdown.php000064400000002767151166614520021252
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeMarkdown;

/**
 * Adds ability to inline markdown between tags.
 *
 * {% markdown %}
 * This is **bold** and this _underlined_
 *
 * 1. This is a bullet list
 * 2. This is another item in that same list
 * {% endmarkdown %}
 */
class TokenParserMarkdown extends \Twig_TokenParser
{
    /**
     * {@inheritdoc}
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
       
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse(array($this,
'decideMarkdownEnd'), true);
       
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        return new TwigNodeMarkdown($body, $lineno, $this->getTag());
    }

    /**
     * Decide if current token marks end of Markdown block.
     *
     * @param \Twig_Token $token
     * @return bool
     */
    public function decideMarkdownEnd(\Twig_Token $token)
    {
        return $token->test('endmarkdown');
    }

    /**
     * {@inheritdoc}
     */
    public function getTag()
    {
        return 'markdown';
    }
}
classes/Gantry/Component/Twig/TokenParser/TokenParserPageblock.php000064400000004534151166614520021351
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodePageblock;

/**
 * Adds javascript / style assets to head/footer/custom location.
 *
 * {% pageblock in 'bottom' with { priority: 0 } %}
 *   <div>Bottom HTML</div>
 * {% endpageblock -%}
 */
class TokenParserPageblock extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        list($location, $variables) = $this->parseArguments($token);

        $content = $this->parser->subparse([$this,
'decideBlockEnd'], true);
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodePageblock($content, $location, $variables,
$lineno, $this->getTag());
    }

    /**
     * @param \Twig_Token $token
     * @return array
     */
    protected function parseArguments(\Twig_Token $token)
    {
        $stream = $this->parser->getStream();
        $lineno = $token->getLine();
        $location = new
\Twig_Node_Expression_Constant($stream->expect(\Twig_Token::NAME_TYPE)->getValue(),
$lineno);

        if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'with')) {
            $variables =
$this->parser->getExpressionParser()->parseExpression();
        } else {
            $lineno = $token->getLine();
            $variables = new \Twig_Node_Expression_Array([], $lineno);
            $variables->setAttribute('priority', 0);
        }
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return [$location, $variables];
    }

    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endpageblock');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'pageblock';
    }
}
classes/Gantry/Component/Twig/TokenParser/TokenParserScripts.php000064400000001731151166614520021105
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

/**
 * Adds scripts to head/footer/custom location.
 *
 * {% scripts in 'head' with { priority: 2 } %}
 *   <script type="text/javascript" src="{{
url('gantry-theme://js/my.js') }}"></script>
 * {% endscripts -%}
 */
class TokenParserScripts extends TokenParserAssets
{
    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endscripts');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'scripts';
    }
}
classes/Gantry/Component/Twig/TokenParser/TokenParserStyles.php000064400000001715151166614520020743
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

/**
 * Adds stylesheets to document.
 *
 * {% styles with { priority: 2 } %}
 *   <link rel="stylesheet" href="{{
url('gantry-assets://css/font-awesome.min.css') }}"
type="text/css"/>
 * {% endstyles -%}
 */
class TokenParserStyles extends TokenParserAssets
{
    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endstyles');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'styles';
    }
}
classes/Gantry/Component/Twig/TokenParser/TokenParserSwitch.php000064400000007354151166614520020726
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeSwitch;

/**
 * Adds ability use elegant switch instead of ungainly if statements
 *
 * {% switch type %}
 *   {% case 'foo' %}
 *      {{ my_data.foo }}
 *   {% case 'bar' %}
 *      {{ my_data.bar }}
 *   {% default %}
 *      {{ my_data.default }}
 * {% endswitch %}
 */
class TokenParserSwitch extends \Twig_TokenParser
{
    /**
     * {@inheritdoc}
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $name =
$this->parser->getExpressionParser()->parseExpression();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        // There can be some whitespace between the {% switch %} and first
{% case %} tag.
        while ($stream->getCurrent()->getType() ===
\Twig_Token::TEXT_TYPE &&
trim($stream->getCurrent()->getValue()) === '') {
            $stream->next();
        }

        $stream->expect(\Twig_Token::BLOCK_START_TYPE);

        $expressionParser = $this->parser->getExpressionParser();

        $default = null;
        $cases = [];
        $end = false;

        while (!$end) {
            $next = $stream->next();

            switch ($next->getValue()) {
                case 'case':
                    $values = [];

                    while (true) {
                        $values[] =
$expressionParser->parsePrimaryExpression();
                        // Multiple allowed values?
                        if ($stream->test(\Twig_Token::OPERATOR_TYPE,
'or')) {
                            $stream->next();
                        } else {
                            break;
                        }
                    }

                    $stream->expect(\Twig_Token::BLOCK_END_TYPE);
                    $body = $this->parser->subparse(array($this,
'decideIfFork'));
                    $cases[] = new \Twig_Node([
                        'values' => new \Twig_Node($values),
                        'body' => $body
                    ]);
                    break;

                case 'default':
                    $stream->expect(\Twig_Token::BLOCK_END_TYPE);
                    $default = $this->parser->subparse(array($this,
'decideIfEnd'));
                    break;

                case 'endswitch':
                    $end = true;
                    break;

                default:
                    throw new \Twig_Error_Syntax(sprintf('Unexpected
end of template. Twig was looking for the following tags "case",
"default", or "endswitch" to close the
"switch" block started at line %d)', $lineno), -1);
            }
        }

        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeSwitch($name, new \Twig_Node($cases), $default,
$lineno, $this->getTag());
    }

    /**
     * Decide if current token marks switch logic.
     *
     * @param \Twig_Token $token
     * @return bool
     */
    public function decideIfFork(\Twig_Token $token)
    {
        return $token->test(array('case', 'default',
'endswitch'));
    }

    /**
     * Decide if current token marks end of swtich block.
     *
     * @param \Twig_Token $token
     * @return bool
     */
    public function decideIfEnd(\Twig_Token $token)
    {
        return $token->test(array('endswitch'));
    }

    /**
     * {@inheritdoc}
     */
    public function getTag()
    {
        return 'switch';
    }
}
classes/Gantry/Component/Twig/TokenParser/TokenParserThrow.php000064400000002564151166614520020566
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeThrow;

/**
 * Handles try/catch in template file.
 *
 * <pre>
 * {% throw 404 'Not Found' %}
 * </pre>
 */
class TokenParserThrow extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $code =
$stream->expect(\Twig_Token::NUMBER_TYPE)->getValue();
        $message =
$this->parser->getExpressionParser()->parseExpression();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeThrow($code, $message, $lineno,
$this->getTag());
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'throw';
    }
}
classes/Gantry/Component/Twig/TokenParser/TokenParserTryCatch.php000064400000003514151166614520021200
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeTryCatch;

/**
 * Handles try/catch in template file.
 *
 * <pre>
 * {% try %}
 *    <li>{{ user.get('name') }}</li>
 * {% catch %}
 *    {{ e.message }}
 * {% endcatch %}
 * </pre>
 */
class TokenParserTryCatch extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
        $try = $this->parser->subparse([$this,
'decideCatch']);
        $stream->next();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
        $catch = $this->parser->subparse([$this,
'decideEnd']);
        $stream->next();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeTryCatch($try, $catch, $lineno,
$this->getTag());
    }

    public function decideCatch(\Twig_Token $token)
    {
        return $token->test(array('catch'));
    }

    public function decideEnd(\Twig_Token $token)
    {
        return $token->test(array('endtry')) ||
$token->test(array('endcatch'));
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'try';
    }
}
classes/Gantry/Component/Twig/TwigCacheFilesystem.php000064400000005435151166614520016753
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig;

/**
 * Class TwigCacheFilesystem
 * @package Gantry\Component\Twig
 *
 * Replaces \Twig_Cache_Filesystem, needed for being able to change PHP
versions on fly.
 */
class TwigCacheFilesystem implements \Twig_CacheInterface
{
    const FORCE_BYTECODE_INVALIDATION = 1;
    private $directory;
    private $options;
    /**
     * @param $directory string The root cache directory
     * @param $options   int    A set of options
     */
    public function __construct($directory, $options = 0)
    {
        $this->directory = rtrim($directory,
'\/').'/';
        $this->options = $options;
    }
    /**
     * {@inheritdoc}
     */
    public function generateKey($name, $className)
    {
        $hash = hash('sha256', $className . '-' .
PHP_VERSION);
        return
$this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
    }
    /**
     * {@inheritdoc}
     */
    public function load($key)
    {
        @include_once $key;
    }
    /**
     * {@inheritdoc}
     */
    public function write($key, $content)
    {
        $dir = dirname($key);
        if (!is_dir($dir)) {
            if (false === @mkdir($dir, 0777, true) &&
!is_dir($dir)) {
                throw new \RuntimeException(sprintf('Unable to create
the cache directory (%s).', $dir));
            }
        } elseif (!is_writable($dir)) {
            throw new \RuntimeException(sprintf('Unable to write in
the cache directory (%s).', $dir));
        }
        $tmpFile = tempnam($dir, basename($key));
        if (false !== @file_put_contents($tmpFile, $content) &&
@rename($tmpFile, $key)) {
            @chmod($key, 0666 & ~umask());
            if (self::FORCE_BYTECODE_INVALIDATION == ($this->options
& self::FORCE_BYTECODE_INVALIDATION)) {
                // Compile cached file into bytecode cache
                if (function_exists('opcache_invalidate')) {
                    // Silence error in case if `opcache.restrict_api`
directive is set.
                    @opcache_invalidate($key, true);
                } elseif (function_exists('apc_compile_file')) {
                    @apc_compile_file($key);
                }
            }
            return;
        }
        throw new \RuntimeException(sprintf('Failed to write cache
file "%s".', $key));
    }
    /**
     * {@inheritdoc}
     */
    public function getTimestamp($key)
    {
        if (!file_exists($key)) {
            return 0;
        }
        return (int) @filemtime($key);
    }
}
classes/Gantry/Component/Twig/TwigExtension.php000064400000050667151166614520015666
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig;

use Gantry\Component\Content\Document\HtmlDocument;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Translator\TranslatorInterface;
use Gantry\Component\Twig\TokenParser\TokenParserPageblock;
use Gantry\Component\Twig\TokenParser\TokenParserAssets;
use Gantry\Component\Twig\TokenParser\TokenParserScripts;
use Gantry\Component\Twig\TokenParser\TokenParserStyles;
use Gantry\Component\Twig\TokenParser\TokenParserTryCatch;
use Gantry\Component\Twig\TokenParser\TokenParserMarkdown;
use Gantry\Component\Twig\TokenParser\TokenParserSwitch;
use Gantry\Component\Twig\TokenParser\TokenParserThrow;
use Gantry\Framework\Gantry;
use Gantry\Framework\Markdown\Parsedown;
use Gantry\Framework\Markdown\ParsedownExtra;
use Gantry\Framework\Request;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;

class TwigExtension extends \Twig_Extension implements
\Twig_Extension_GlobalsInterface
{
    use GantryTrait;

    /**
     * Register some standard globals
     *
     * @return array
     */
    public function getGlobals()
    {
        return [
            'gantry' => static::gantry(),
        ];
    }

    /**
     * Return a list of all filters.
     *
     * @return array
     */
    public function getFilters()
    {
        $filters = [
            new \Twig_SimpleFilter('html', [$this,
'htmlFilter']),
            new \Twig_SimpleFilter('url', [$this,
'urlFunc']),
            new \Twig_SimpleFilter('trans_key', [$this,
'transKeyFilter']),
            new \Twig_SimpleFilter('trans', [$this,
'transFilter']),
            new \Twig_SimpleFilter('repeat', [$this,
'repeatFilter']),
            new \Twig_SimpleFilter('values', [$this,
'valuesFilter']),
            new \Twig_SimpleFilter('base64',
'base64_encode'),
            new \Twig_SimpleFilter('imagesize', [$this,
'imageSize']),
            new \Twig_SimpleFilter('truncate_text', [$this,
'truncateText']),
            new \Twig_SimpleFilter('attribute_array', [$this,
'attributeArrayFilter'], ['is_safe' =>
['html']]),
        ];

        if (1 || GANTRY5_PLATFORM !== 'grav') {
            $filters = array_merge($filters, [
                new \Twig_SimpleFilter('fieldName', [$this,
'fieldNameFilter']),
                new \Twig_SimpleFilter('json_decode', [$this,
'jsonDecodeFilter']),
                new \Twig_SimpleFilter('truncate_html', [$this,
'truncateHtml']),
                new \Twig_SimpleFilter('markdown', [$this,
'markdownFunction'], ['is_safe' =>
['html']]),
                new \Twig_SimpleFilter('nicetime', [$this,
'nicetimeFilter']),

                // Casting values
                new \Twig_SimpleFilter('string', [$this,
'stringFilter']),
                new \Twig_SimpleFilter('int', [$this,
'intFilter'], ['is_safe' => ['all']]),
                new \Twig_SimpleFilter('bool', [$this,
'boolFilter']),
                new \Twig_SimpleFilter('float', [$this,
'floatFilter'], ['is_safe' => ['all']]),
                new \Twig_SimpleFilter('array', [$this,
'arrayFilter']),
            ]);
        }

        return $filters;
    }

    /**
     * Return a list of all functions.
     *
     * @return array
     */
    public function getFunctions()
    {
        $functions = [
            new \Twig_SimpleFunction('nested', [$this,
'nestedFunc']),
            new \Twig_SimpleFunction('parse_assets', [$this,
'parseAssetsFunc']),
            new \Twig_SimpleFunction('colorContrast', [$this,
'colorContrastFunc']),
            new \Twig_SimpleFunction('get_cookie', [$this,
'getCookie']),
            new \Twig_SimpleFunction('preg_match', [$this,
'pregMatch']),
            new \Twig_SimpleFunction('imagesize', [$this,
'imageSize']),
            new \Twig_SimpleFunction('is_selected', [$this,
'is_selectedFunc']),
            new \Twig_SimpleFunction('url', [$this,
'urlFunc']),
        ];

        if (1 || GANTRY5_PLATFORM !== 'grav') {
            $functions = array_merge($functions, [
                new \Twig_SimpleFunction('array', [$this,
'arrayFilter']),
                new \Twig_SimpleFunction('json_decode', [$this,
'jsonDecodeFilter']),
            ]);
        }

        return $functions;
    }

    /**
     * @return array
     */
    public function getTokenParsers()
    {
        return [
            new TokenParserPageblock(),
            new TokenParserAssets(),
            new TokenParserScripts(),
            new TokenParserStyles(),
            new TokenParserThrow(),
            new TokenParserTryCatch(),
            new TokenParserMarkdown(),
            new TokenParserSwitch()
        ];
    }

    /**
     * Filters field name by changing dot notation into array notation.
     *
     * @param  string  $str
     * @return string
     */
    public function fieldNameFilter($str)
    {
        $path = explode('.', $str);

        return array_shift($path) . ($path ? '[' .
implode('][', $path) . ']' : '');
    }

    /**
     * Translate by using key, default on original string.
     *
     * @param $str
     * @return string
     */
    public function transKeyFilter($str)
    {
        $params = \func_get_args();
        array_shift($params);

        $key = preg_replace('|[^A-Z0-9]+|', '_',
strtoupper(implode('_', $params)));

        $translation = $this->transFilter($key);

        return $translation === $key ? $str : $translation;
    }

    /**
     * Translate string.
     *
     * @param  string  $str
     * @return string
     */
    public function transFilter($str)
    {
        /** @var TranslatorInterface $translator */
        static $translator;

        $params = \func_get_args();

        if (!$translator) {
            $translator = self::gantry()['translator'];
        }

        return \call_user_func_array([$translator, 'translate'],
$params);
    }

    /**
     * Repeat string x times.
     *
     * @param  string  $str
     * @param  int  $count
     * @return string
     */
    public function repeatFilter($str, $count)
    {
        return str_repeat($str, max(0, (int) $count));
    }


    /**
     * Decodes string from JSON.
     *
     * @param  string  $str
     * @param  bool  $assoc
     * @param int $depth
     * @param int $options
     * @return array
     */
    public function jsonDecodeFilter($str, $assoc = false, $depth = 512,
$options = 0)
    {
        return json_decode(html_entity_decode($str), $assoc, $depth,
$options);
    }

    public function imageSize($src, $attrib = true, $remote = false)
    {
        // TODO: need to better handle absolute and relative paths
        //$url =
Gantry::instance()['document']->url(trim((string) $src),
false, false);
        $width = $height = null;
        $sizes = ['width' => $width, 'height' =>
$height];
        $attr = '';

        if (@is_file($src) || $remote) {
            try {
                list($width, $height,, $attr) = @getimagesize($src);
            } catch (\Exception $e) {}

            $sizes['width'] = $width;
            $sizes['height'] = $height;
        }

        return $attrib ? $attr : $sizes;
    }

    /**
     * Reindexes values in array.
     *
     * @param array $array
     * @return array
     */
    public function valuesFilter(array $array)
    {
        return array_values($array);
    }

    /**
     * Casts input to string.
     *
     * @param mixed $input
     * @return string
     */
    public function stringFilter($input)
    {
        return (string) $input;
    }


    /**
     * Casts input to int.
     *
     * @param mixed $input
     * @return int
     */
    public function intFilter($input)
    {
        return (int) $input;
    }

    /**
     * Casts input to bool.
     *
     * @param mixed $input
     * @return bool
     */
    public function boolFilter($input)
    {
        return (bool) $input;
    }

    /**
     * Casts input to float.
     *
     * @param mixed $input
     * @return float
     */
    public function floatFilter($input)
    {
        return (float) $input;
    }

    /**
     * Casts input to array.
     *
     * @param mixed $input
     * @return array
     */
    public function arrayFilter($input)
    {
        return (array) $input;
    }

    /**
     * Takes array of attribute keys and values and converts it to properly
escaped HTML attributes.
     *
     * @example ['data-id' => 'id',
'data-key' => 'key'] => '
data-id="id" data-key="key"'
     * @example [['data-id' => 'id'],
['data-key' => 'key']] => '
data-id="id" data-key="key"'
     *
     * @param string|string[] $input
     * @return string
     */
    public function attributeArrayFilter($input)
    {
        if (\is_string($input)) {
            return $input;
        }

        $array = [];
        foreach ((array) $input as $key => $value) {
            if (\is_array($value)) {
                foreach ((array) $value as $key2 => $value2) {
                    $array[] = HtmlDocument::escape($key2) .
'="' . HtmlDocument::escape($value2, 'html_attr')
. '"';
                }
            } elseif ($key) {
                $array[] = HtmlDocument::escape($key) . '="'
. HtmlDocument::escape($value, 'html_attr') . '"';
            }
        }
        return $array ? ' ' . implode(' ', $array) :
'';
    }

    public function is_selectedFunc($a, $b)
    {
        $b = (array) $b;
        array_walk(
            $b,
            function (&$item) {
                if (\is_bool($item)) {
                    $item = (int) $item;
                }
                $item = (string) $item;
            }
        );

        return \in_array((string) $a, $b, true);
    }

    /**
     * Truncate text by number of characters but can cut off words. Removes
html tags.
     *
     * @param  string $string
     * @param  int    $limit       Max number of characters.
     *
     * @return string
     */
    public function truncateText($string, $limit = 150)
    {
        $platform = Gantry::instance()['platform'];

        return $platform->truncate($string, (int) $limit, false);
    }

    /**
     * Truncate text by number of characters but can cut off words.
     *
     * @param  string $string
     * @param  int    $limit       Max number of characters.
     *
     * @return string
     */
    public function truncateHtml($string, $limit = 150)
    {
        $platform = Gantry::instance()['platform'];

        return $platform->truncate($string, (int) $limit, true);
    }

    /**
     * @param string $string
     * @param bool $block  Block or Line processing
     * @param array $settings
     * @return mixed|string
     */
    public function markdownFunction($string, $block = true, array
$settings = null)
    {
        // Initialize the preferred variant of Parsedown
        if (!empty($settings['extra'])) {
            $parsedown = new ParsedownExtra($settings);
        } else {
            $parsedown = new Parsedown($settings);
        }

        if ($block) {
            $string = $parsedown->text($string);
        } else {
            $string = $parsedown->line($string);
        }

        return $string;
    }

    /**
     * Get value by using dot notation for nested arrays/objects.
     *
     * @example {{ nested(array,
'this.is.my.nested.variable')|json_encode }}
     *
     * @param array   $items      Array of items.
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return mixed  Value.
     */
    public function nestedFunc($items, $name, $default = null, $separator =
'.')
    {
        if ($items instanceof NestedArrayAccess) {
            return $items->get($name, $default, $separator);
        }
        $path = explode($separator, $name);
        $current = $items;
        foreach ($path as $field) {
            if (\is_object($current) &&
isset($current->{$field})) {
                $current = $current->{$field};
            } elseif (\is_array($current) &&
isset($current[$field])) {
                $current = $current[$field];
            } else {
                return $default;
            }
        }

        return $current;
    }

    /**
     * Return URL to the resource.
     *
     * @example {{
url('theme://images/logo.png')|default('http://www.placehold.it/150x100/f4f4f4')
}}
     *
     * @param  string $input       Resource to be located.
     * @param  bool $domain        True to include domain name.
     * @param  int $timestamp_age  Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                             Use value <= 0 to disable the
feature.
     * @return string|null         Returns url to the resource or null if
resource was not found.
     */
    public function urlFunc($input, $domain = false, $timestamp_age = null)
    {
        $gantry = Gantry::instance();

        return $gantry['document']->url(trim((string) $input),
$domain, $timestamp_age);
    }

    /**
     * Filter stream URLs from HTML input.
     *
     * @param  string $str          HTML input to be filtered.
     * @param  bool $domain         True to include domain name.
     * @param  int $timestamp_age   Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                              Use value <= 0 to disable the
feature.
     * @return string               Returns modified HTML.
     */
    public function htmlFilter($str, $domain = false, $timestamp_age =
null)
    {
        $gantry = Gantry::instance();

        return $gantry['document']->urlFilter($str, $domain,
$timestamp_age);
    }

    /**
     * @param \libXMLError $error
     * @param string $input
     * @throws \RuntimeException
     */
    protected function dealXmlError(\libXMLError $error, $input)
    {
        switch ($error->level) {
            case LIBXML_ERR_WARNING:
                $level = 1;
                $message = "DOM Warning {$error->code}: ";
                break;
            case LIBXML_ERR_ERROR:
                $level = 2;
                $message = "DOM Error {$error->code}: ";
                break;
            case LIBXML_ERR_FATAL:
                $level = 3;
                $message = "Fatal DOM Error {$error->code}: ";
                break;
            default:
                $level = 3;
                $message = "Unknown DOM Error {$error->code}:
";
        }
        $message .= "{$error->message} while
parsing:\n{$input}\n";

        if ($level <= 2 && !Gantry::instance()->debug()) {
            return;
        }

        throw new \RuntimeException($message, 500);
    }

    /**
     * Move supported document head elements into platform document object,
return all
     * unsupported tags in a string.
     *
     * @param string $input
     * @param string $location
     * @param int $priority
     * @return string
     */
    public function parseAssetsFunc($input, $location = 'head',
$priority = 0)
    {
        if ($location === 'head') {
            $scope = 'head';
            $html = "<!doctype
html>\n<html><head>{$input}</head><body></body></html>";
        } else {
            $scope = 'body';
            $html = "<!doctype
html>\n<html><head></head><body>{$input}</body></html>";
        }

        libxml_clear_errors();

        $internal = libxml_use_internal_errors(true);

        $doc = new \DOMDocument();
        $doc->loadHTML($html);
        foreach (libxml_get_errors() as $error) {
            $this->dealXmlError($error, $html);
        }

        libxml_clear_errors();

        libxml_use_internal_errors($internal);

        $raw = [];
        /** @var \DomElement $element */
        foreach
($doc->getElementsByTagName($scope)->item(0)->childNodes as
$element) {
            if (empty($element->tagName)) {
                continue;
            }
            $result = ['tag' => $element->tagName,
'content' => $element->textContent];
            foreach ($element->attributes as $attribute) {
                $result[$attribute->name] = $attribute->value;
            }
            $success =
Gantry::instance()['document']->addHeaderTag($result,
$location, (int) $priority);
            if (!$success) {
                $raw[] = $doc->saveHTML($element);
            }
        }

        return implode("\n", $raw);
    }

    public function colorContrastFunc($value)
    {
        $value = str_replace(' ', '', $value);
        $rgb = new \stdClass;
        $opacity = 1;

        if (0 !== strpos($value, 'rgb')) {
            $value = str_replace('#', '', $value);
            if (\strlen($value) === 3) {
                $h0 = str_repeat(substr($value, 0, 1), 2);
                $h1 = str_repeat(substr($value, 1, 1), 2);
                $h2 = str_repeat(substr($value, 2, 1), 2);
                $value = $h0 . $h1 . $h2;
            }

            $rgb->r = hexdec(substr($value, 0, 2));
            $rgb->g = hexdec(substr($value, 2, 2));
            $rgb->b = hexdec(substr($value, 4, 2));
        } else {
           
preg_match("/(\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*(1\\.|0?\\.?[0-9]?+))?/uim",
$value, $matches);
            $rgb->r = $matches[1];
            $rgb->g = $matches[2];
            $rgb->b = $matches[3];
            $opacity = isset($matches[4]) ? $matches[4] : 1;
            $opacity = substr($opacity, 0, 1) === '.' ?
'0' . $opacity : $opacity;
        }

        $yiq = ((($rgb->r * 299) + ($rgb->g * 587) + ($rgb->b *
114)) / 1000) >= 128;
        $contrast = $yiq || (!$opacity || (float) $opacity < 0.35);

        return $contrast;
    }

    /**
     * Displays a facebook style 'time ago' formatted date/time.
     *
     * @param string|int $date
     * @param bool $long_strings
     *
     * @return string
     */
    public function nicetimeFilter($date, $long_strings = true)
    {
        static $lengths = [60, 60, 24, 7, 4.35, 12, 10];
        static $periods_long = [
            'GANTRY5_ENGINE_NICETIME_SECOND',
            'GANTRY5_ENGINE_NICETIME_MINUTE',
            'GANTRY5_ENGINE_NICETIME_HOUR',
            'GANTRY5_ENGINE_NICETIME_DAY',
            'GANTRY5_ENGINE_NICETIME_WEEK',
            'GANTRY5_ENGINE_NICETIME_MONTH',
            'GANTRY5_ENGINE_NICETIME_YEAR',
            'GANTRY5_ENGINE_NICETIME_DECADE'
        ];
        static $periods_short = [
            'GANTRY5_ENGINE_NICETIME_SEC',
            'GANTRY5_ENGINE_NICETIME_MIN',
            'GANTRY5_ENGINE_NICETIME_HR',
            'GANTRY5_ENGINE_NICETIME_DAY',
            'GANTRY5_ENGINE_NICETIME_WK',
            'GANTRY5_ENGINE_NICETIME_MO',
            'GANTRY5_ENGINE_NICETIME_YR',
            'GANTRY5_ENGINE_NICETIME_DEC'
        ];

        if (empty($date)) {
            return
$this->transFilter('GANTRY5_ENGINE_NICETIME_NO_DATE_PROVIDED');
        }

        $periods = $long_strings ? $periods_long : $periods_short;

        $now = time();

        // check if unix timestamp
        if ((string)(int)$date === (string)$date) {
            $unix_date = (int)$date;
        } else {
            $unix_date = strtotime($date);
        }

        // check validity of date
        if (!$unix_date) {
            return
$this->transFilter('GANTRY5_ENGINE_NICETIME_BAD_DATE');
        }

        // is it future date or past date
        if ($now > $unix_date) {
            $difference = $now - $unix_date;
            $tense      =
$this->transFilter('GANTRY5_ENGINE_NICETIME_AGO');

        } else if ($now === $unix_date) {
            $difference = $now - $unix_date;
            $tense      =
$this->transFilter('GANTRY5_ENGINE_NICETIME_JUST_NOW');

        } else {
            $difference = $unix_date - $now;
            $tense      =
$this->transFilter('GANTRY5_ENGINE_NICETIME_FROM_NOW');
        }


        for ($j = 0; $difference >= $lengths[$j] && $j <
\count($lengths) - 1; $j++) {
            $difference /= $lengths[$j];
        }
        $period = $periods[$j];

        $difference = round($difference);

        if ($difference !== 1) {
            $period .= '_PLURAL';
        }

        $period = $this->transFilter($period);

        if ($now === $unix_date) {
            return $tense;
        }

        return "{$difference} {$period} {$tense}";
    }

    public function getCookie($name)
    {
        $gantry = Gantry::instance();

        /** @var Request $request */
        $request = $gantry['request'];

        return $request->cookie[$name];
    }

    public function pregMatch($pattern, $subject, &$matches = [])
    {
        preg_match($pattern, $subject, $matches);

        return $matches ?: false;
    }
}
classes/Gantry/Component/Url/Url.php000064400000007731151166614520013443
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Url;

class Url
{
    /**
     * UTF8 aware parse_url().
     *
     * @param  string $url
     * @param  bool   $queryArray
     * @return array|bool
     */
    public static function parse($url, $queryArray = false)
    {
        $encodedUrl = preg_replace_callback(
            '%[^:/@?&=#]+%usD',
            function ($matches) { return rawurlencode($matches[0]); },
            $url
        );

        // PHP versions below 5.4.7 have troubles with URLs without scheme,
so lets help by fixing that.
        // TODO: This is not needed in PHP >= 5.4.7, but for now we need
to test if the function works.
        if ('/' === $encodedUrl[0] && false !==
strpos($encodedUrl, '://')) {
            $schemeless = true;

            // Fix the path so that parse_url() will not return false.
            $parts = parse_url('fake://fake.com' . $encodedUrl);

            // Remove the fake values.
            unset($parts['scheme'], $parts['host']);

        } else {
            $parts = parse_url($encodedUrl);
        }

        if (!$parts) {
            return false;
        }

        // PHP versions below 5.4.7 do not understand schemeless URLs
starting with // either.
        if (isset($schemeless) && !isset($parts['host'])
&& 0 === strpos($encodedUrl, '//')) {
            // Path is stored in format: //[host]/[path], so let's fix
it.
            list($parts['host'], $path) = explode('/',
substr($parts['path'], 2), 2);
            $parts['path'] = "/{$path}";
        }

        foreach($parts as $name => $value) {
            $parts[$name] = rawurldecode($value);
        }

        // Return query string also as an array if requested.
        if ($queryArray) {
            $parts['vars'] = isset($parts['query']) ?
static::parseQuery($parts['query']) : [];
        }

        return $parts;
    }

    /**
     * Parse query string and return array.
     *
     * @param $query
     * @return mixed
     */
    public static function parseQuery($query)
    {
        parse_str($query, $vars);

        return $vars;
    }

    /**
     * Build parsed URL array.
     *
     * @param array $parsed_url
     * @return string
     */
    public static function build(array $parsed_url)
    {
        // Build query string from variables if they are set.
        if (isset($parsed_url['vars'])) {
            $parsed_url['query'] =
static::buildQuery($parsed_url['vars']);
        }

        // Build individual parts of the url.
        $scheme   = isset($parsed_url['scheme']) ?
$parsed_url['scheme'] . '://' : '';
        $host     = isset($parsed_url['host']) ?
$parsed_url['host'] : '';
        $port     = isset($parsed_url['port']) ? ':' .
$parsed_url['port'] : '';
        $user     = isset($parsed_url['user']) ?
$parsed_url['user'] : '';
        $pass     = isset($parsed_url['pass']) ? ':' .
$parsed_url['pass']  : '';
        $pass     = ($user || $pass) ? "{$pass}@" : '';
        $path     = isset($parsed_url['path']) ?
$parsed_url['path'] : '';
        $query    = isset($parsed_url['query']) ? '?' .
$parsed_url['query'] : '';
        $fragment = isset($parsed_url['fragment']) ?
'#' . $parsed_url['fragment'] : '';
        $scheme   = $host && !$scheme ? '//' : $scheme;

        return
"{$scheme}{$user}{$pass}{$host}{$port}{$path}{$query}{$fragment}";
    }

    /**
     * Build query string from variables.
     *
     * @param array $vars
     * @return null|string
     */
    public static function buildQuery(array $vars)
    {
        $list = [];
        foreach ($vars as $key => $var) {
            $list[] = $key . '=' . rawurlencode($var);
        }

        return $list ? implode('&', $list) : null;
    }
}
classes/Gantry/Component/Whoops/SystemFacade.php000064400000011101151166614520015750
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Whoops;

class SystemFacade extends \Whoops\Util\SystemFacade
{
    protected $registeredPatterns;
    protected $whoopsErrorHandler;
    protected $whoopsExceptionHandler;
    protected $whoopsShutdownHandler;
    protected $platformExceptionHandler;

    /**
     * @param  array|string $patterns List or a single regex pattern to
match for silencing errors in particular files.
     */
    public function __construct($patterns = [])
    {
        $this->registeredPatterns = array_map(
            function ($pattern) {
                return["pattern" => $pattern];
            },
            (array) $patterns
        );
    }

    /**
     * @param callable $handler
     * @param int|string $types
     *
     * @return callable|null
     */
    public function setErrorHandler(callable $handler, $types =
'use-php-defaults')
    {
        // Workaround for PHP 5.5
        if ($types === 'use-php-defaults') {
            $types = E_ALL | E_STRICT;
        }

        $this->whoopsErrorHandler = $handler;

        return parent::setErrorHandler([$this, 'handleError'],
$types);
    }

    /**
     * @param callable $function
     *
     * @return void
     */
    public function registerShutdownFunction(callable $function)
    {
        $this->whoopsShutdownHandler = $function;
        register_shutdown_function([$this, 'handleShutdown']);
    }

    /**
     * @param callable $handler
     *
     * @return callable|null
     */
    public function setExceptionHandler(callable $handler)
    {
        $this->whoopsExceptionHandler = $handler;
        $this->platformExceptionHandler =
parent::setExceptionHandler([$this, 'handleException']);

        return $this->platformExceptionHandler;
    }

    /**
     * Converts generic PHP errors to \ErrorException instances, before
passing them off to be handled.
     *
     * This method MUST be compatible with set_error_handler.
     *
     * @param int    $level
     * @param string $message
     * @param string $file
     * @param int    $line
     *
     * @return bool
     * @throws \ErrorException
     */
    public function handleError($level, $message, $file = null, $line =
null)
    {
        $handler = $this->whoopsErrorHandler;

        if (!$this->registeredPatterns) {
            // Just forward to parent function is there aren't no
registered patterns.
            return $handler($level, $message, $file, $line);

        }

        // If there are registered patterns, only handle errors if error
matches one of the patterns.
        if ($level & error_reporting()) {
            foreach ($this->registeredPatterns as $entry) {
                $pathMatches = $file &&
preg_match($entry["pattern"], $file);
                if ($pathMatches) {
                    return $handler($level, $message, $file, $line);
                }
            }
        }

        // Propagate error to the next handler, allows error_get_last() to
work on silenced errors.
        return false;
    }

    /**
     * Handles an exception, ultimately generating a Whoops error page.
     *
     * @param  \Throwable $exception
     * @return void
     */
    public function handleException($exception)
    {
        $handler = $this->whoopsExceptionHandler;

        // If there are registered patterns, only handle errors if error
matches one of the patterns.
        if ($this->registeredPatterns) {
            foreach ($this->registeredPatterns as $entry) {
                $file = $exception->getFile();
                $pathMatches = $file &&
preg_match($entry["pattern"], $file);
                if ($pathMatches) {
                    $handler($exception);
                    return;
                }
            }
        }

        // Propagate error to the next handler.
        if ($this->platformExceptionHandler) {
            call_user_func_array($this->platformExceptionHandler,
[&$exception]);
        }
    }

    /**
     * Special case to deal with Fatal errors and the like.
     */
    public function handleShutdown()
    {
        $handler = $this->whoopsShutdownHandler;

        $error = $this->getLastError();

        // Ignore core warnings and errors.
        if ($error && !($error['type'] &
(E_CORE_WARNING | E_CORE_ERROR))) {
            $handler();
        }
    }
}
classes/Gantry/Framework/Assignments.php000064400000013503151166614520014417
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Assignments\AbstractAssignments;
use Gantry\Joomla\CacheHelper;
use Gantry\Joomla\StyleHelper;
use Joomla\Utilities\ArrayHelper;

class Assignments extends AbstractAssignments
{
    protected $platform = 'Joomla';

    /**
     * Load all assignments.
     *
     * @return array
     */
    public function loadAssignments()
    {
        $app = \JFactory::getApplication();

        if (!$app->isSite()) {
            return [];
        }

        // Get current template, style id and rules.
        $template = $app->getTemplate();
        $active = $app->getMenu()->getActive();
        if ($active) {
            $style = (int) $active->template_style_id;
            $rules = [$active->menutype => [$active->id =>
true]];
        } else {
            $style = 0;
            $rules = [];
        }

        // Load saved assignments.
        $assignments = parent::loadAssignments();

        // Add missing template styles from Joomla.
        $styles = StyleHelper::loadStyles($template);
        $assignments += array_fill_keys(array_keys($styles), []);

        foreach ($assignments as $id => &$assignment) {
            // Add current menu item if it has been assigned to the style.
            $assignment['menu'] = $style === $id ? $rules : [];

            // Always add the current template style.
            $assignment['style'] =  ['id' => [$id
=> true]];
        }

        return $assignments;
    }

    /**
     * Save assignments for the configuration.
     *
     * @param array $data
     */
    public function save(array $data)
    {
        $data += ['assignment' => 0, 'menu' =>
[]];

        // Joomla stores language and menu assignments by its own.
        $this->saveAssignment($data['assignment']);
        $this->saveMenu($data['menu']);
        unset($data['assignment'], $data['menu'],
$data['style']);

        // Continue saving rest of the assignments.
        parent::save($data);
    }

    public function types()
    {
        return ['menu', 'style'];
    }

    public function saveMenu($data)
    {
        $active = [];
        foreach ($data as $menutype => $items) {
            $active += array_filter($items, function($value) {return $value
> 0; });

        }
        $active = array_keys($active);

        // Detect disabled template.
        $extension = \JTable::getInstance('Extension');

        $template = Gantry::instance()['theme.name'];
        if ($extension->load(array('enabled' => 0,
'type' => 'template', 'element' =>
$template, 'client_id' => 0))) {
            throw new
\RuntimeException(\JText::_('COM_TEMPLATES_ERROR_SAVE_DISABLED_TEMPLATE'));
        }

        \JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');
        $style = \JTable::getInstance('Style',
'TemplatesTable');
        if (!$style->load($this->configuration) ||
$style->client_id != 0) {
            throw new \RuntimeException('Template style does not
exist');
        }

        $user = \JFactory::getUser();
        $n = 0;

        if ($user->authorise('core.edit',
'com_menus')) {
            $db   = \JFactory::getDbo();
            $user = \JFactory::getUser();

            if (!empty($active)) {
                ArrayHelper::toInteger($active);

                // Update the mapping for menu items that this style IS
assigned to.
                $query = $db->getQuery(true)
                    ->update('#__menu')
                    ->set('template_style_id = ' . (int)
$style->id)
                    ->where('id IN (' . implode(',',
$active) . ')')
                    ->where('template_style_id != ' . (int)
$style->id)
                    ->where('checked_out IN (0,' . (int)
$user->id . ')');
                $db->setQuery($query);
                $db->execute();
                $n += $db->getAffectedRows();
            }

            // Remove style mappings for menu items this style is NOT
assigned to.
            // If unassigned then all existing maps will be removed.
            $query = $db->getQuery(true)
                ->update('#__menu')
                ->set('template_style_id = 0');

            if (!empty($active)) {
                $query->where('id NOT IN (' .
implode(',', $active) . ')');
            }

            $query->where('template_style_id = ' . (int)
$style->id)
                ->where('checked_out IN (0,' . (int)
$user->id . ')');
            $db->setQuery($query);
            $db->execute();

            $n += $db->getAffectedRows();
        }

        // Clean the cache.
        CacheHelper::cleanTemplates();

        return ($n > 0);
    }

    public function getAssignment()
    {
        $style = StyleHelper::getStyle($this->configuration);

        return $style->home;
    }

    public function saveAssignment($value)
    {
        $options = $this->assignmentOptions();

        if (!isset($options[$value])) {
            throw new \RuntimeException('Invalid value for default
assignment!', 400);
        }

        $style = StyleHelper::getStyle($this->configuration);
        $style->home = $value;

        if (!$style->check() || !$style->store()) {
            throw new \RuntimeException($style->getError());
        }

        // Clean the cache.
        CacheHelper::cleanTemplates();
    }

    public function assignmentOptions()
    {
        if ((string)(int) $this->configuration !== (string)
$this->configuration) {
            return [];
        }

        $languages = \JHtml::_('contentlanguage.existing');

        $options = ['- Make Default -', 'All
Languages'];
        foreach ($languages as $language) {
            $options[$language->value] = $language->text;
        }

        return $options;
    }
}
classes/Gantry/Framework/Atoms.php000064400000022345151166614520013213
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework;

use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Atoms implements \ArrayAccess, \Iterator, ExportInterface
{
    use ArrayAccess, Iterator, Export;

    /**
     * @var  string
     */
    protected $name;

    /**
     * @var array
     */
    protected $items;

    /**
     * @var array
     */
    protected $ids;

    /**
     * @var array|static[]
     */
    protected static $instances;

    protected $inherit = false;

    /**
     * @param string $outline
     * @return static
     */
    public static function instance($outline)
    {
        if (!isset(static::$instances[$outline])) {
            $file =
CompiledYamlFile::instance("gantry-theme://config/{$outline}/page/head.yaml");
            $head = $file->content();
            static::$instances[$outline] = new
static(isset($head['atoms']) ? $head['atoms'] : [],
$outline);
            $file->free();

            static::$instances[$outline]->init();
        }

        return static::$instances[$outline];
    }

    /**
     * Atoms constructor.
     * @param array $atoms
     * @param string $name
     */
    public function __construct(array $atoms = [], $name = null)
    {
        $this->name = $name;
        $this->items = array_filter($atoms);
        $this->inherit =
file_exists('gantry-admin://blueprints/layout/inheritance/atom.yaml');

        foreach ($this->items as &$item) {
            if (!empty($item['id'])) {
                $this->ids[$item['id']] = $item;
            }
        }
    }

    public function init()
    {
        foreach ($this->items as &$item) {
            if (!empty($item['inherit']['outline'])
&& !empty($item['inherit']['atom'])) {
                $inherited =
static::instance($item['inherit']['outline']);
                $test =
$inherited->id($item['inherit']['atom']);
                if (isset($test['attributes'])) {
                    $item['attributes'] =
$test['attributes'];
                } else {
                    unset($item['inherit']);
                }
            }
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function update()
    {
        foreach ($this->items as &$item) {
            if (empty($item['id'])) {
                $item['id'] = $this->createId($item);
            }
            if (!empty($item['inherit']['outline'])
&& !empty($item['inherit']['atom'])) {
                unset($item['attributes']);
            } else {
                unset($item['inherit']);
            }
        }

        return $this;
    }

    /**
     * @param string $outline
     * @return $this
     */
    public function inheritAll($outline)
    {
        foreach ($this->items as &$item) {
            if (!empty($item['id'])) {
                $item['inherit'] = [
                    'outline' => $outline,
                    'atom' => $item['id'],
                    'include' => ['attributes']
                ];
            }
        }

        return $this;
    }

    /**
     * @param string $old
     * @param string $new
     * @param array  $ids
     * @return $this
     */
    public function updateInheritance($old, $new = null, $ids = null)
    {
        $this->init();

        foreach ($this->items as &$item) {
            if (!empty($item['inherit']['outline'])
&& $item['inherit']['outline'] == $old
&& isset($item['inherit']['atom'])) {
                if ($new && ($ids === null ||
isset($ids[$item['inherit']['atom']]))) {
                    $item['inherit']['outline'] = $new;
                } else {
                    unset($item['inherit']);
                }
            }
        }

        return $this;
    }

    public function save()
    {
        if ($this->name) {
            /** @var UniformResourceLocator $locator */
            $locator = Gantry::instance()['locator'];

            $loadPath =
$locator->findResource("gantry-theme://config/{$this->name}/page/head.yaml");
            $savePath =
$locator->findResource("gantry-theme://config/{$this->name}/page/head.yaml",
true, true);

            if ($loadPath && $savePath) {
                $file = CompiledYamlFile::instance($loadPath);
                $head = $file->content();
                $head['atoms'] =
$this->update()->toArray();
                $file->free();

                $file = CompiledYamlFile::instance($savePath);
                $file->save($head);
                $file->free();
            }
        }
    }

    /**
     * @param string $id
     * @return array
     */
    public function id($id)
    {
        return isset($this->ids[$id]) ? $this->ids[$id] : [];
    }

    /**
     * @param string $type
     * @return array
     */
    public function type($type)
    {
        $list = [];
        foreach ($this->items as $item) {
            if ($item['type'] === $type) {
                $list[] = $item;
            }
        }

        return $list;
    }

    /**
     * @param string $type
     * @param array $data
     * @return Config
     */
    public function createAtom($type, array $data = [])
    {
        $self = $this;

        $callable = function () use ($self, $type) {
            return $self->getBlueprint($type);
        };

        // Create configuration from the data.
        $item = new Config($data, $callable);
        $item->def('id', null);
        $item->def('type', $type);
        if (!isset($item['title'])) {
            $item->def('title',
$item->blueprint()->get('name'));
        }
        $item->def('attributes', []);
        $item->def('inherit', []);

        return $item;
    }

    /**
     * @param string $type
     * @return BlueprintForm
     */
    public function getBlueprint($type)
    {
        $blueprint = BlueprintForm::instance($type,
'gantry-blueprints://particles');

        if ($this->inherit) {
            $blueprint->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
        }

        return $blueprint;
    }

    /**
     * @param string $type
     * @param string $id
     * @param bool $force
     * @return BlueprintForm|null
     */
    public function getInheritanceBlueprint($type, $id = null, $force =
false)
    {
        if (!$this->inherit) {
            return null;
        }

        $inheriting = $id ? $this->getInheritingOutlines($id) : [];
        $list = $this->getOutlines($type, false);

        if ($force || (empty($inheriting) && $list)) {
            $inheritance =
BlueprintForm::instance('layout/inheritance/atom.yaml',
'gantry-admin://blueprints');
            $inheritance->set('form/fields/outline/filter',
array_keys($list));
            $inheritance->set('form/fields/atom/atom', $type);

        } elseif (!empty($inheriting)) {
            // Already inherited by other outlines.
            $inheritance =
BlueprintForm::instance('layout/inheritance/messages/inherited.yaml',
'gantry-admin://blueprints');
            $inheritance->set(
                'form/fields/_note/content',
               
sprintf($inheritance->get('form/fields/_note/content'),
'atom', ' <ul><li>' .
implode('</li> <li>', $inheriting) .
'</li></ul>')
            );

        } elseif ($this->name === 'default') {
            // Base outline.
            $inheritance =
BlueprintForm::instance('layout/inheritance/messages/default.yaml',
'gantry-admin://blueprints');

        } else {
            // Nothing to inherit from.
            $inheritance =
BlueprintForm::instance('layout/inheritance/messages/empty.yaml',
'gantry-admin://blueprints');
        }

        return $inheritance;
    }

    /**
     * @param string $id
     * @return array
     */
    public function getInheritingOutlines($id = null)
    {
        /** @var Outlines $outlines */
        $outlines = Gantry::instance()['outlines'];

        return $outlines->getInheritingOutlinesWithAtom($this->name,
$id);
    }

    /**
     * @param string $type
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlines($type, $includeInherited = true)
    {
        if ($this->name !== 'default') {
            /** @var Outlines $outlines */
            $outlines = Gantry::instance()['outlines'];

            $list = $outlines->getOutlinesWithAtom($type,
$includeInherited);
            unset($list[$this->name]);
        } else {
            $list = [];
        }

        return $list;
    }

    /**
     * @param array $item
     * @return string
     */
    protected function createId(array &$item)
    {
        $type = $item['type'];

        while ($num = rand(1000, 9999)) {
            if (!isset($this->ids["{$type}-{$num}"])) {
                break;
            }
        }

        $id = "{$type}-{$num}";

        $this->ids[$id] = $item;

        return $id;
    }
}
classes/Gantry/Framework/Base/Gantry.php000064400000022264151166614520014246
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Base;

use Gantry\Component\Config\Config;
use Gantry\Component\System\Messages;
use Gantry\Framework\Document;
use Gantry\Framework\Menu;
use Gantry\Framework\Outlines;
use Gantry\Framework\Page;
use Gantry\Framework\Platform;
use Gantry\Framework\Positions;
use Gantry\Framework\Request;
use Gantry\Framework\Services\ConfigServiceProvider;
use Gantry\Framework\Services\StreamsServiceProvider;
use Gantry\Framework\Site;
use Gantry\Framework\Translator;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventDispatcher;

abstract class Gantry extends Container
{
    /**
     * @var static
     */
    protected static $instance;
    protected $wrapper;

    public static function instance()
    {
        if (!self::$instance) {
            self::$instance = static::init();

            if (!defined('GANTRY5_DEBUG')) {
                define('GANTRY5_DEBUG',
self::$instance->debug());
            }
        }

        return self::$instance;
    }

    public static function restart()
    {
        self::$instance = null;

        return static::instance();
    }

    /**
     * Returns true if debug mode has been enabled.
     *
     * @return boolean
     */
    public function debug()
    {
        return $this['global']->get('debug', false);
    }

    /**
     * Returns true if we are in administration.
     *
     * @return boolean
     */
    public function admin()
    {
        return defined('GANTRYADMIN_PATH');
    }


    /**
     * @return string
     */
    public function siteUrl()
    {
        $gantry = Gantry::instance();

        return $gantry['document']->siteUrl();
    }

    /**
     * @param string $location
     * @return array
     */
    public function styles($location = 'head')
    {
        return $this['document']->getStyles($location);
    }

    /**
     * @param string $location
     * @return array
     */
    public function scripts($location = 'head')
    {
        return $this['document']->getScripts($location);
    }

    /**
     * Load Javascript framework / extension in platform independent way.
     *
     * @param string $framework
     * @return bool
     */
    public function load($framework)
    {
        return $this['document']->addFramework($framework);
    }

    /**
     * Lock the variable against modification and return the value.
     *
     * @param string $id
     * @return mixed
     */
    public function lock($id)
    {
        $value = $this[$id];

        try {
            // Create a dummy service.
            $this[$id] = function () use ($value) {
                return $value;
            };
        } catch (\RuntimeException $e) {
            // Services are already locked, so ignore the error.
        }

        // Lock the service and return value.
        return $this[$id];
    }

    /**
     * Fires an event with optional parameters.
     *
     * @param  string $eventName
     * @param  Event  $event
     * @return Event
     */
    public function fireEvent($eventName, Event $event = null)
    {
        /** @var EventDispatcher $events */
        $events = $this['events'];
        return $events->dispatch($eventName, $event);
    }

    public function route($path)
    {
        $routes = $this->offsetGet('routes');
        $route = isset($routes[$path]) ? $routes[$path] : $routes[1];

        if (!$route) {
            // TODO: need to implement back to root in Prime..
            return $this->offsetGet('base_url');
        }

        $path = implode('/', array_filter(func_get_args(),
function($var) { return isset($var) && $var !== ''; }));

        // rawurlencode() the whole path, but keep the slashes.
        $path = preg_replace(['|%2F|', '|%25|'],
['/', '%'], rawurlencode($path));

        return preg_replace('|/+|', '/', '/'
. $this->offsetGet('base_url') . sprintf($route, $path));
    }

    public function authorize($action, $id = null)
    {
        return $this['platform']->authorize($action, $id);
    }

    public function wrapper($value = null)
    {
        if ($value !== null ) {
            $this->wrapper = $value;
        }

        return $this->wrapper;
    }

    protected static function init()
    {
        /** @var Gantry $instance */
        $instance = new static();

        if (GANTRY_DEBUGGER) {
            $instance['debugger'] = \Gantry\Debugger::instance();
        }

        $instance['loader'] = \Gantry5\Loader::get();

        $instance->register(new ConfigServiceProvider);
        $instance->register(new StreamsServiceProvider);

        $instance['request'] = function () {
            return new Request;
        };

        $instance['events'] = function () {
            return new EventDispatcher;
        };

        $instance['platform'] = function ($c) {
            return new Platform($c);
        };

        $instance['translator'] = function () {
            return new Translator;
        };

        $instance['site'] = function () {
            return new Site;
        };

        $instance['menu'] = function () {
            return new Menu;
        };

        $instance['messages'] = function () {
            return new Messages;
        };

        $instance['page'] = function ($c) {
            return new Page($c);
        };

        $instance['document'] = function () {
            return new Document;
        };

        // Make sure that nobody modifies the original collection by making
it a factory.
        $instance['outlines'] = $instance->factory(function
($c) {
            static $collection;
            if (!$collection) {
                $collection = (new Outlines($c))->load();
            }

            return $collection->copy();
        });

        // @deprecated 5.3
        $instance['configurations'] =
$instance->factory(function ($c) {
            GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Depredated call:
gantry.configurations");

            static $collection;
            if (!$collection) {
                $collection = (new Outlines($c))->load();
            }

            return $collection->copy();
        });

        $instance['positions'] = $instance->factory(function
($c) {
            static $collection;
            if (!$collection) {
                $collection = (new Positions($c))->load();
            }

            return $collection->copy();
        });

        $instance['global'] = function ($c) {
            $data = $c->loadGlobal() + [
                    'debug' => false,
                    'production' => true,
                    'use_media_folder' => false,
                    'asset_timestamps' => true,
                    'asset_timestamps_period' => 7,
                    'compile_yaml' => true,
                    'compile_twig' => true,
                    'offline_message'  => ''
                ];

            return new Config($data);
        };

        return $instance;
    }

    /**
     * Check if Gantry is compatible with your theme / extension.
     *
     * This function can be used to make sure that user has installed
Gantry version
     * that has been tested to work with your extension. All existing
functions should
     * be backwards compatible, but each release can add some new
functionality, which
     * you may want to use.
     *
     * <code>
     * if ($gantry->isCompatible('5.0.1')) {
     *      // You can do it in the new way.
     * } else {
     *     // Revert to the old way to display an error message.
     * }
     * </code>
     *
     * @param string $version Minimum required version.
     *
     * @return boolean Yes, if it is safe to use Gantry Framework.
     */
    public function isCompatible($version)
    {
        // If requested version is smaller than 5.0-rc, it's not
compatible.
        if (version_compare($version, '5.0-rc',
'<')) {
            return false;
        }

        // Development version support.
        if ($version === '5.3' || static::isDev()) {
            return true;
        }

        // Check if future version is needed.
        if (version_compare($version, GANTRY5_VERSION, '>')) {
            return false;
        }

        return true;
    }

    /**
     * Check if Gantry is running from a Git repository or is a CI build.
     *
     * Developers tend to do their work directly in the Git repositories
instead of
     * creating and installing new builds after every change. This function
can be
     * used to check the condition and make sure we do not break users
repository
     * by replacing files during upgrade.
     *
     * @return boolean True if Git repository or CI build is detected.
     */
    public function isDev()
    {
        if ('@version@' == GANTRY5_VERSION) {
            return true;
        }
        if ('dev-' === substr(GANTRY5_VERSION, 0, 4)) {
            return true;
        }

        return false;
    }

    /**
     * @return array
     */
    protected function loadGlobal()
    {
        return [];
    }
}
classes/Gantry/Framework/Base/Page.php000064400000004115151166614520013651
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Base;

abstract class Page
{
    protected $container;
    protected $config;

    public function __construct($container)
    {
        $this->container = $container;
        $this->config = $container['config'];
    }

    public function doctype()
    {
        return $this->config->get('page.doctype',
'html');
    }

    abstract public function url(array $args = []);

    public function preset()
    {
        /** @var Theme $theme */
        $theme = $this->container['theme'];
        return 'g-' . preg_replace('/[^a-z0-9-]/',
'', $theme->type());
    }

    public function htmlAttributes()
    {
        return
$this->getAttributes($this->config->get('page.html'));
    }

    public function bodyAttributes($attributes = [])
    {
        return
$this->getAttributes($this->config->get('page.body.attribs'),
$attributes);
    }

    protected function getAttributes($params, $extra = [])
    {
        $params = array_merge_recursive($params, $extra);

        $list = [];
        foreach ($params as $param => $value) {
            if (!$value) { continue; }
            if (!is_array($value) || !count(array_filter($value,
'is_array'))) {
                $value = array_filter(array_unique((array) $value));
                $list[] = $param . '="' . implode('
', $value) . '"';
            } else {
                $values = new \RecursiveIteratorIterator(new
\RecursiveArrayIterator($value));
                foreach ($values as $iparam => $ivalue) {
                    $ivalue = array_filter(array_unique((array) $ivalue));
                    $list[] = $iparam . '="' .
implode(' ', $ivalue) . '"';
                }
            }

        }

        return $list ? ' ' . implode(' ', $list) :
'';
    }
}
classes/Gantry/Framework/Base/Platform.php000064400000014730151166614520014565
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Base;

use Gantry\Component\Filesystem\Folder;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;
use RocketTheme\Toolbox\DI\Container;

/**
 * The Platform Configuration class contains configuration information.
 *
 * @author RocketTheme
 * @license MIT
 */

abstract class Platform
{
    use NestedArrayAccess, Export;

    protected $name;
    protected $features = [];
    protected $settings_key;
    protected $items;
    protected $container;

    public function __construct(Container $container)
    {
        $this->container = $container;

        //Make sure that cache folder exists, otherwise it will be removed
from the lookup.
        $cachePath = $this->getCachePath();
        Folder::create($cachePath);

        $this->items = [
            'streams' => [
                // Cached files.
                'gantry-cache' => [
                    'type' => 'Stream',
                    'force' => true,
                    'prefixes' => ['' =>
[$cachePath]]
                ],
                // Container for all frontend themes.
                'gantry-themes' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getThemesPaths()
                ],
                // Selected frontend theme.
                'gantry-theme' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getThemePaths()
                ],
                // System defined media files.
                'gantry-assets' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getAssetsPaths()
                ],
                // User defined media files.
                'gantry-media' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getMediaPaths()
                ],
                // Container for all Gantry engines.
                'gantry-engines' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getEnginesPaths()
                ],
                // Gantry engine used to render the selected theme.
                'gantry-engine' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getEnginePaths()
                ],
                // Layout definitions for the blueprints.
                'gantry-layouts' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => ['' =>
['gantry-theme://layouts', 'gantry-engine://layouts']]
                ],
                // Gantry particles.
                'gantry-particles' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => ['' =>
['gantry-theme://particles',
'gantry-engine://particles']]
                ],
                // Gantry administration.
                'gantry-admin' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => []
                ],
                // Blueprints for the configuration.
                'gantry-blueprints' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => [
                        '' =>
['gantry-theme://blueprints',
'gantry-engine://blueprints'],
                        'particles' =>
['gantry-particles://']
                    ]
                ],
                // Configuration from the selected theme.
                'gantry-config' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => ['' =>
['gantry-theme://config']]
                ]
            ]
        ];
    }

    abstract public function getCachePath();
    abstract public function getThemesPaths();
    abstract public function getAssetsPaths();
    abstract public function getMediaPaths();

    public function init()
    {
        return $this;
    }

    public function has($feature)
    {
        return !empty($this->features[$feature]);
    }

    public function getThemePaths()
    {
        return ['' => []];
    }

    public function getEnginePaths($name = 'nucleus')
    {
        return ['' => ['gantry-theme://engine',
"gantry-engines://{$name}"]];
    }

    public function getEnginesPaths()
    {
        return ['' => []];
    }

    public function errorHandlerPaths()
    {
        return [];
    }

    /**
     * Get preview url for individual theme.
     *
     * @param string $theme
     * @return string|null
     */
    abstract public function getThemePreviewUrl($theme);

    /**
     * Get administrator url for individual theme.
     *
     * @param string $theme
     * @return string|null
     */
    abstract public function getThemeAdminUrl($theme);

    public function settings()
    {
        return null;
    }

    public function settings_key()
    {
        return $this->settings_key;
    }

    public function listModules()
    {
        return false;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getEditor($name, $content = '', $width =
null, $height = null)
    {
        return null;
    }

    public function filter($text)
    {
        return $text;
    }

    public function finalize()
    {
        $gantry = Gantry::instance();

        $gantry['document']->registerAssets();
    }

    public function call()
    {
        $args = func_get_args();
        $callable = array_shift($args);
        return is_callable($callable) ? call_user_func_array($callable,
$args) : null;
    }

    public function authorize($action)
    {
        return true;
    }

    /**
     * @param array|string $dependencies
     * @return bool|null
     * @since 5.4.3
     */
    public function checkDependencies($dependencies)
    {
        if (is_string($dependencies) && $dependencies !==
$this->name) {
            return false;
        }

        if (isset($dependencies['platform'])) {
            if (is_string($dependencies['platform']) &&
$dependencies['platform'] !== $this->name) {
                return false;
            }
            if
(!isset($dependencies['platform'][$this->name])) {
                return false;
            }
        }

        return true;
    }
}
classes/Gantry/Framework/Base/Site.php000064400000000644151166614520013704
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Base;

class Site
{
}
classes/Gantry/Framework/Base/Theme.php000064400000001107151166614520014035
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Base;

use Gantry\Component\Theme\AbstractTheme;
use Gantry\Component\Theme\ThemeTrait;

/**
 * @deprecated 5.1.5
 */
abstract class Theme extends AbstractTheme
{
    use ThemeTrait;
}
classes/Gantry/Framework/Configurations.php000064400000000727151166614520015122
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework;

/**
 * @deprecated 5.1.1
 */
class Configurations extends Outlines
{
}
classes/Gantry/Framework/Document.php000064400000016143151166614520013705
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Content\Document\HtmlDocument;

class Document extends HtmlDocument
{
    protected static $availableFrameworks = [
        'jquery' => 'registerJquery',
        'jquery.framework' => 'registerJquery',
        'jquery.ui.core' => 'registerJqueryUiCore',
        'jquery.ui.sortable' =>
'registerJqueryUiSortable',
        'bootstrap.2' => 'registerBootstrap2',
        'mootools' => 'registerMootools',
        'mootools.framework' => 'registerMootools',
        'mootools.core' => 'registerMootools',
        'mootools.more' => 'registerMootoolsMore',
        'lightcase' => 'registerLightcase',
        'lightcase.init' => 'registerLightcaseInit',
    ];

    public static function registerAssets()
    {
        static::registerFrameworks();
        static::registerStyles();
        static::registerScripts();
    }

    /**
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @param bool $addDomain
     * @return string
     */
    public static function domain($addDomain = false)
    {
        if (!$addDomain) {
            return '';
        }

        $absolute = \JUri::root(false);
        $relative = \JUri::root(true);

        return substr($absolute, 0, -strlen($relative));
    }

    public static function rootUri()
    {
        return rtrim(\JUri::root(true), '/') ?: '/';
    }

    public static function errorPage($new = null)
    {
        static $error = false;

        if (isset($new)) {
            $error = (bool) $new;
        }

        return $error;
    }

    protected static function registerStyles()
    {
        if (static::errorPage()) {
            return;
        }

        $doc = \JFactory::getDocument();

        $styles = static::$stack[0]->getStyles();

        foreach ($styles as $style) {
            switch ($style[':type']) {
                case 'file':
                    $doc->addStyleSheet($style['href'],
$style['type'], $style['media'],
$style['element']);
                    break;
                case 'inline':
                   
$doc->addStyleDeclaration($style['content'],
$style['type']);
                    break;
            }
        }
    }

    protected static function registerScripts()
    {
        if (static::errorPage()) {
            return;
        }

        $doc = \JFactory::getDocument();

        $scripts = static::$stack[0]->getScripts();

        foreach ($scripts as $script) {
            switch ($script[':type']) {
                case 'file':
                    $doc->addScript($script['src'],
$script['type'], $script['defer'],
$script['async']);
                    break;
                case 'inline':
                   
$doc->addScriptDeclaration($script['content'],
$script['type']);
                    break;
            }
        }
    }

    protected static function registerJquery()
    {
        if (!static::errorPage()) {
            \JHtml::_('jquery.framework');

            return;
        }

        // Workaround for error document type.
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery.min.js'
            ],
            'head',
            100
        );
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery-noconflict.js'
            ],
            'head',
            100
        );
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery-migrate.min.js'
            ],
            'head',
            100
        );
    }

    protected static function registerJqueryUiCore()
    {
        if (!static::errorPage()) {
            \JHtml::_('jquery.ui', ['core']);

            return;
        }

        // Workaround for error document type.
        static::registerJquery();
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery.ui.core.min.js'
            ],
            'head',
            100
        );

    }

    protected static function registerJqueryUiSortable()
    {
        if (!static::errorPage()) {
            \JHtml::_('jquery.ui', ['sortable']);

            return;
        }

        // Workaround for error document type.
        static::registerJqueryUiCore();
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery.ui.sortable.min.js'
            ],
            'head',
            100
        );
    }

    protected static function registerBootstrap2()
    {
        Gantry::instance()['theme']->joomla(true);

        if (!static::errorPage()) {
            \JHtml::_('bootstrap.framework');

            return;
        }

        // Workaround for error document type.
        static::registerJquery();
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/bootstrap.min.js'
            ],
            'head',
            100
        );
    }

    protected static function registerMootools()
    {
        if (!static::errorPage()) {
            \JHtml::_('behavior.framework');

            return;
        }

        // Workaround for error document type.
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/system/js/mootools-core.js'
            ],
            'head',
            99
        );
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/system/js/core.js'
            ],
            'head',
            99
        );
    }

    protected static function registerMootoolsMore()
    {
        if (!static::errorPage()) {
            \JHtml::_('behavior.framework', true);

            return;
        }

        // Workaround for error document type.
        static::registerMootools();
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/system/js/mootools-more.js'
            ],
            'head',
            99
        );
    }

    /**
     * Override to support index.php?Itemid=xxx.
     *
     * @param array $matches
     * @return string
     * @internal
     */
    public static function linkHandler(array $matches)
    {
        $url = trim($matches[3]);
        if (strpos($url, 'index.php?') !== 0) {
            list($domain, $timestamp_age) = static::$urlFilterParams;
            $url = static::url(trim($matches[3]), $domain, $timestamp_age);
        }

        return "{$matches[1]}{$matches[2]}=\"{$url}\"";
    }
}
classes/Gantry/Framework/Exception.php000064400000002030151166614520014053
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework;

class Exception extends \RuntimeException
{
    protected $responseCodes = [
        200 => '200 OK',
        400 => '400 Bad Request',
        401 => '401 Unauthorized',
        403 => '403 Forbidden',
        404 => '404 Not Found',
        410 => '410 Gone',
        500 => '500 Internal Server Error',
        501 => '501 Not Implemented',
        503 => '503 Service Temporarily Unavailable'
    ];

    public function getResponseCode() {
        return isset($this->responseCodes[$this->code]) ? (int)
$this->code : 500;
    }

    public function getResponseStatus() {
        return $this->responseCodes[$this->getResponseCode()];
    }
}
classes/Gantry/Framework/Exporter.php000064400000025761151166614520013745
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Layout\Layout;
use Gantry\Framework\Services\ConfigServiceProvider;
use Gantry\Joomla\Category\CategoryFinder;
use Gantry\Joomla\Content\ContentFinder;
use Gantry\Joomla\Module\ModuleFinder;
use Gantry\Joomla\StyleHelper;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Exporter
{
    protected $files = [];

    public function all()
    {
        $theme = Gantry::instance()['theme']->details();

        return [
            'export' => [
                'gantry' => [
                    'version' => GANTRY5_VERSION !==
'@version@' ? GANTRY5_VERSION : 'GIT',
                    'format' => 1
                ],
                'platform' => [
                    'name' => 'joomla',
                    'version' => JVERSION
                ],
                'theme' => [
                    'name' =>
$theme->get('name'),
                    'title' =>
$theme->get('details.name'),
                    'version' =>
$theme->get('details.version'),
                    'date' =>
$theme->get('details.date'),
                    'author' =>
$theme->get('details.author'),
                    'copyright' =>
$theme->get('details.copyright'),
                    'license' =>
$theme->get('details.license'),
                ]
            ],
            'outlines' => $this->outlines(),
            'positions' => $this->positions(),
            'menus' => $this->menus(),
            'content' => $this->articles(),
            'categories' => $this->categories(),
            'files' => $this->files,
        ];
    }

    public function outlines()
    {
        $gantry = Gantry::instance();
        $styles = StyleHelper::loadStyles($gantry['theme.name']);

        $list = [
            'default' => ['title' =>
'Default'],
            '_error' => ['title' =>
'Error'],
            '_offline' => ['title' =>
'Offline'],
            '_body_only' => ['title' =>
'Body Only'],
        ];
        $inheritance = [];

        foreach ($styles as $style) {
            $name = $base =
strtolower(trim(preg_replace('|[^a-z\d_-]+|ui', '_',
$style->title), '_'));
            $i = 0;
            while (isset($list[$name])) {
                $i++;
                $name = "{$base}-{$i}";
            };
            $inheritance[$style->id] = $name;
            $list[$name] = [
                'id' => (int) $style->id,
                'title' => $style->title,
                'home' => $style->home,
            ];
            if (!$style->home) {
                unset($list[$name]['home']);
            }
        }

        foreach ($list as $name => &$style) {
            $id = isset($style['id']) ? $style['id'] :
$name;
            $config = ConfigServiceProvider::load($gantry, $id, false,
false);

            // Update layout inheritance.
            $layout = Layout::instance($id);
            $layout->name = $name;
            foreach ($inheritance as $from => $to) {
                $layout->updateInheritance($from, $to);
            }
            $style['preset'] =
$layout->preset['name'];
            $config['index'] = $layout->buildIndex();
            $config['layout'] = $layout->export();

            // Update atom inheritance.
            $atoms = $config->get('page.head.atoms');
            if (is_array($atoms)) {
                $atoms = new Atoms($atoms);
                foreach ($inheritance as $from => $to) {
                    $atoms->updateInheritance($from, $to);
                }
                $config->set('page.head.atoms',
$atoms->update()->toArray());
            }

            // Add assignments.
            if (is_numeric($id)) {
                $assignments = $this->getOutlineAssignments($id);
                if ($assignments) {
                    $config->set('assignments',
$this->getOutlineAssignments($id));
                }
            }
            
            $style['config'] = $config->toArray();
        }

        return $list;
    }

    public function positions($all = true)
    {
        $gantry = Gantry::instance();
        $positions = $gantry['outlines']->positions();
        $positions['debug'] = 'Debug';

        $finder = new ModuleFinder();
        if (!$all) {
            $finder->particle();
        }
        $modules = $finder->find()->export();
        $list = [];
        foreach ($modules as $position => &$items) {
            if (!isset($positions[$position])) {
                continue;
            }
            foreach ($items as &$item) {
                $func = 'module' .
$item['options']['type'];
                if (method_exists($this, $func)) {
                    $item = $this->{$func}($item);
                }
            }
            $list[$position] = [
                'title' => $positions[$position],
                'items' => $items,
            ];
        }

        return $list;
    }

    public function menus()
    {
        $gantry = Gantry::instance();
        $db = \JFactory::getDbo();

        $query = $db->getQuery(true)
            ->select('id, menutype, title, description')
            ->from('#__menu_types');
        $db->setQuery($query);
        $menus = $db->loadObjectList('id');

        $list = [];
        foreach ($menus as $menu) {
            $items =
$gantry['menu']->instance(['menu' =>
$menu->menutype])->items(false);

            array_walk(
                $items,
                function (&$item) {
                    $item['id'] = (int) $item['id'];
                    if (in_array($item['type'],
['component', 'alias'])) {
                        $item['type'] =
"joomla.{$item['type']}";
                    }

                    unset($item['alias'],
$item['path'], $item['parent_id'],
$item['level']);
                }
            );

            $list[$menu->menutype] = [
                'id' => (int) $menu->id,
                'title' => $menu->title,
                'description' => $menu->description,
                'items' => $items
            ];
        }

        return $list;
    }

    public function articles()
    {
        $finder = new ContentFinder();

        $articles = $finder->limit(0)->find();

        $list = [];
        foreach ($articles as $article) {
            $exported = $article->toArray();

            // Convert images to use streams.
            $exported['introtext'] =
$this->urlFilter($exported['introtext']);
            $exported['fulltext'] =
$this->urlFilter($exported['fulltext']);

            $list[$article->id . '-' . $article->alias] =
$exported;
        }

        return $list;
    }

    public function categories()
    {
        $finder = new CategoryFinder();

        $categories = $finder->limit(0)->find();

        $list = [];
        foreach ($categories as $category) {
            $list[$category->id] = $category->toArray();
        }

        return $list;
    }


    /**
     * List all the rules available.
     *
     * @param string $configuration
     * @return array
     */
    public function getOutlineAssignments($configuration)
    {
        require_once JPATH_ADMINISTRATOR .
'/components/com_menus/helpers/menus.php';
        $app = \JApplicationCms::getInstance('site');
        $menu = $app->getMenu();
        $data = \MenusHelper::getMenuLinks();

        $items = [];
        foreach ($data as $item) {
            foreach ($item->links as $link) {
                if ($link->template_style_id == $configuration) {
                    $items[$menu->getItem($link->value)->route] =
1;
                }
            }
        }

        if ($items) {
            return ['page' => [$items]];
        }

        return [];
    }

    /**
     * Filter stream URLs from HTML.
     *
     * @param  string $html         HTML input to be filtered.
     * @return string               Returns modified HTML.
     */
    public function urlFilter($html)
    {
        // Tokenize all PRE and CODE tags to avoid modifying any
src|href|url in them
        $tokens = [];
        $html =
preg_replace_callback('#<(pre|code).*?>.*?<\\/\\1>#is',
function($matches) use (&$tokens) {
            $token = uniqid('__g5_token');
            $tokens['#' . $token . '#'] = $matches[0];

            return $token;
        }, $html);

        $html =
preg_replace_callback('^(\s)(src|href)="(.*?)"^',
[$this, 'linkHandler'], $html);
        $html = preg_replace_callback('^(\s)url\((.*?)\)^',
[$this, 'urlHandler'], $html);
        $html = preg_replace(array_keys($tokens), array_values($tokens),
$html); // restore tokens

        return $html;
    }

    public function url($url)
    {
        // Only process local urls.
        if ($url === '' || $url[0] === '/' || $url[0]
=== '#') {
            return $url;
        }

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        // Handle URIs.
        if (strpos($url, '://')) {
            if ($locator->isStream($url)) {
                // File is a stream, include it to files list.
                list ($stream, $path) = explode('://', $url);
                $this->files[$stream][$path] = $url;
            }

            return $url;
        }

        // Try to convert local paths to streams.
        $paths = $locator->getPaths();

        $found = false;
        $stream = $path = '';
        foreach ($paths as $stream => $prefixes) {
            foreach ($prefixes as $prefix => $paths) {
                foreach ($paths as $path) {
                    if (is_string($path) && strpos($url, $path) ===
0) {
                        $path = ($prefix ? "{$prefix}/" :
'') . substr($url, strlen($path) + 1);
                        $found = true;
                        break 3;
                    }
                }
            }
        }

        if ($found) {
            $url = "{$stream}://{$path}";
            $this->files[$stream][$path] = $url;
        }

        return $url;
    }

    /**
     * @param array $matches
     * @return string
     * @internal
     */
    public function linkHandler(array $matches)
    {
        $url = $this->url(trim($matches[3]));

        return "{$matches[1]}{$matches[2]}=\"{$url}\"";
    }

    /**
     * @param array $matches
     * @return string
     * @internal
     */
    public function urlHandler(array $matches)
    {
        $url = $this->url(trim($matches[2], '"\''));

        return "{$matches[1]}url({$url})";
    }

    protected function moduleMod_Custom(array $data)
    {
        // Convert to particle...
        $data['type'] = 'particle';
        $data['joomla'] = $data['options'];
        $data['options'] = [
            'type' => 'custom',
            'attributes' => [
                'enabled' =>
$data['joomla']['published'],
                'html' =>
$this->urlFilter($data['joomla']['content']),
                'filter' =>
$data['joomla']['params']['prepare_content']
            ]
        ];

        unset($data['joomla']['content'],
$data['joomla']['params']['prepare_content']);

        return $data;
    }
}
classes/Gantry/Framework/Gantry.php000064400000002733151166614520013373
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

class Gantry extends Base\Gantry
{
    /**
     * @return boolean
     */
    public function debug()
    {
        return JDEBUG;
    }

    /**
     * @return boolean
     */
    public function admin()
    {
        return \JFactory::getApplication()->isAdmin();
    }

    /**
     * @param string $location
     * @param bool   $force
     * @return array
     */
    public function styles($location = 'head', $force = false)
    {
        // Do not display head, Joomla will take care of it (most of the
time).
        return (!$force && $location == 'head') ? [] :
parent::styles($location);
    }

    /**
     * @param string $location
     * @param bool $force
     * @return array
     */
    public function scripts($location = 'head', $force = false)
    {
        // Do not display head, Joomla will take care of it (most of the
time).
        return (!$force && $location == 'head') ? [] :
parent::scripts($location);
    }

    /**
     * @return array
     */
    protected function loadGlobal()
    {
        $global = null;

        // Trigger the event.
        $dispatcher = \JEventDispatcher::getInstance();
        $dispatcher->trigger('onGantryGlobalConfig',
['global' => &$global]);

        return $global;
    }
}
classes/Gantry/Framework/Markdown/Parsedown.php000064400000001227151166614520015650
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Markdown;

class Parsedown extends \Parsedown
{
    use ParsedownTrait;

    /**
     * Parsedown constructor.
     *
     * @param array $defaults
     */
    public function __construct(array $defaults = null)
    {
        $this->init($defaults ?: []);
    }

}
classes/Gantry/Framework/Markdown/ParsedownExtra.php000064400000001337151166614520016656
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Markdown;

class ParsedownExtra extends \ParsedownExtra
{
    use ParsedownTrait;

    /**
     * ParsedownExtra constructor.
     *
     * @param array $defaults
     * @throws \Exception
     */
    public function __construct(array $defaults = null)
    {
        parent::__construct();

        $this->init($defaults ?: []);
    }
}
classes/Gantry/Framework/Markdown/ParsedownTrait.php000064400000010342151166614520016652
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Markdown;

use Gantry\Framework\Gantry;

trait ParsedownTrait
{
    protected $special_chars;
    protected $twig_link_regex =
'/\!*\[(?:.*)\]\((\{([\{%#])\s*(.*?)\s*(?:\2|\})\})\)/';

    /**
     * Initialization function to setup key variables needed by the
MarkdownGravLinkTrait
     *
     * @param $defaults
     */
    protected function init(array $defaults)
    {
        $defaults += [
            'auto_line_breaks' => false,
            'auto_url_links' => false,
            'escape_markup' => false,
            'special_chars' => false
        ];

        $this->BlockTypes['{'][] = 'TwigTag';
        $this->special_chars = ['>' => 'gt',
'<' => 'lt', '"' =>
'quot'];

       
$this->setBreaksEnabled($defaults['auto_line_breaks']);
        $this->setUrlsLinked($defaults['auto_url_links']);
        $this->setMarkupEscaped($defaults['escape_markup']);
        $this->setSpecialChars($defaults['special_chars']);
    }

    /**
     * Setter for special chars
     *
     * @param $special_chars
     *
     * @return $this
     */
    public function setSpecialChars($special_chars)
    {
        $this->special_chars = $special_chars;

        return $this;
    }

    /**
     * Ensure Twig tags are treated as block level items with no
<p></p> tags
     *
     * @param array $line
     * @return array|null
     */
    protected function blockTwigTag($line)
    {
        if (preg_match('/(?:{{|{%|{#)(.*)(?:}}|%}|#})/',
$line['body'], $matches)) {
            return ['markup' => $line['body']];
        }

        return null;
    }

    protected function inlineSpecialCharacter($excerpt)
    {
        if ($excerpt['text'][0] === '&' &&
!preg_match('/^&#?\w+;/', $excerpt['text'])) {
            return [
                'markup' => '&amp;',
                'extent' => 1,
            ];
        }

        if (isset($this->special_chars[$excerpt['text'][0]]))
{
            return [
                'markup' => '&' .
$this->special_chars[$excerpt['text'][0]] . ';',
                'extent' => 1,
            ];
        }

        return null;
    }

    protected function inlineImage($excerpt)
    {
        if (preg_match($this->twig_link_regex,
$excerpt['text'], $matches)) {
            $excerpt['text'] = str_replace($matches[1],
'/', $excerpt['text']);
            $excerpt = parent::inlineImage($excerpt);
           
$excerpt['element']['attributes']['src'] =
$matches[1];
            $excerpt['extent'] = $excerpt['extent'] +
\strlen($matches[1]) - 1;

            return $excerpt;
        }

        $excerpt['type'] = 'image';
        $excerpt = parent::inlineImage($excerpt);

        // if this is an image process it
        if
(isset($excerpt['element']['attributes']['src']))
{
            $gantry = Gantry::instance();

           
$excerpt['element']['attributes']['src'] =
$gantry['document']->url($excerpt['element']['attributes']['src']);
        }

        return $excerpt;
    }

    protected function inlineLink($excerpt)
    {
        if (!isset($excerpt['type'])) {
            $excerpt['type'] = 'link';
        }

        // do some trickery to get around Parsedown requirement for valid
URL if its Twig in there
        if (preg_match($this->twig_link_regex,
$excerpt['text'], $matches)) {
            $excerpt['text'] = str_replace($matches[1],
'/', $excerpt['text']);
            $excerpt = parent::inlineLink($excerpt);
           
$excerpt['element']['attributes']['href'] =
$matches[1];
            $excerpt['extent'] = $excerpt['extent'] +
\strlen($matches[1]) - 1;

            return $excerpt;
        }

        $excerpt = parent::inlineLink($excerpt);

        // if this is a link
        if
(isset($excerpt['element']['attributes']['href']))
{
            $gantry = Gantry::instance();

           
$excerpt['element']['attributes']['href'] =
$gantry['document']->url($excerpt['element']['attributes']['href']);
        }

        return $excerpt;
    }
}
classes/Gantry/Framework/Menu.php000064400000031100151166614520013021
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Config\Config;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Menu\AbstractMenu;
use Gantry\Component\Menu\Item;

class Menu extends AbstractMenu
{
    use GantryTrait;

    /**
     * @var \JApplicationCms
     */
    protected $app;

    /**
     * @var \JMenu
     */
    protected $menu;

    public function __construct()
    {
        $this->app = \JApplicationCms::getInstance('site');

        $lang = \JFactory::getLanguage();
        $tag = \JLanguageMultilang::isEnabled() ? $lang->getTag() :
'*';

        $this->menu = $this->app->getMenu();
        $this->default = $this->menu->getDefault($tag);
        $this->active  = $this->menu->getActive();
    }

    public function init(&$params)
    {
        parent::init($params);

        if (!empty($params['admin'])) {
            /** @var \JTableMenuType $table */
            $menuType = \JTable::getInstance('MenuType');
            $menuType->load(['menutype' =>
$params['menu']]);

            $config = $this->config();
            $config->set('settings.title',
$menuType->title);
            $config->set('settings.description',
$menuType->description);
        }
    }

    /**
     * Return list of menus.
     *
     * @return array
     * @throws \RuntimeException
     */
    public function getMenus()
    {
        static $items;

        if ($items === null) {
            require_once JPATH_ADMINISTRATOR .
'/components/com_menus/helpers/menus.php';
            $items = (array) \MenusHelper::getMenuTypes();
        }

        return $items;
    }

    public function getGroupedItems()
    {
        $groups = array();

        // Get the menu items.
        $items = \MenusHelper::getMenuLinks();

        // Build the groups arrays.
        foreach ($items as $item) {
            // Initialize the group.
            $groups[$item->menutype] = [];

            // Build the options array.
            foreach ($item->links as $link) {
                $groups[$item->menutype][$link->value] = [
                    'spacing' => str_repeat('&nbsp;
', max(0, $link->level-1)),
                    'label' => $link->text
                ];
            }
        }

        return $groups;
    }

    /**
     * Return default menu.
     *
     * @return string|null
     */
    public function getDefaultMenuName()
    {
        return $this->default ? $this->default->menutype : null;
    }

    /**
     * Returns true if the platform implements a Default menu.
     *
     * @return boolean
     */
    public function hasDefaultMenu()
    {
        return true;
    }

    /**
     * Return active menu.
     *
     * @return string|null
     */
    public function getActiveMenuName()
    {
        return $this->active ? $this->active->menutype : null;
    }

    /**
     * Returns true if the platform implements an Active menu.
     *
     * @return boolean
     */
    public function hasActiveMenu()
    {
        return true;
    }

    /**
     * @return string|null
     */
    public function getCacheId()
    {
        if (!\JFactory::getUser()->guest) {
            return null;
        }

        return $this->active ? $this->active->id : 0;
    }

    public function isActive($item)
    {
        $tree = $this->base->tree;

        if (in_array($item->id, $tree)) {
            return true;
        } elseif ($item->type == 'alias') {
            $aliasToId = $item->link_id;

            if (count($tree) > 0 && $aliasToId ==
$tree[count($tree) - 1]) {
                return (bool) $this->params['highlightAlias'];
            } elseif (in_array($aliasToId, $tree)) {
                return (bool)
$this->params['highlightParentAlias'];
            }
        }

        return false;
    }

    public function isCurrent($item)
    {
        return $item->id == $this->active->id
        || ($item->type == 'alias' &&
$item->params->get('aliasoptions') ==
$this->active->id);
    }

    /**
     * Get menu items from the platform.
     *
     * @param array $params
     * @return array    List of routes to the pages.
     */
    protected function getItemsFromPlatform($params)
    {
        $attributes = ['menutype'];
        $values = [$params['menu']];

        // Items are already filtered by access and language, in admin we
need to work around that.
        if (\JFactory::getApplication()->isAdmin()) {
            $attributes[] = 'access';
            $values[] = null;

            $attributes[] = 'language';
            $values[] = null;
        }

        return $this->menu->getItems($attributes, $values);
    }

    /**
     * Get base menu item.
     *
     * If itemid is not specified or does not exist, return active menu
item.
     * If there is no active menu item, fall back to home page for the
current language.
     * If there is no home page, return null.
     *
     * @param   int  $itemid
     *
     * @return  object|null
     */
    protected function calcBase($itemid = null)
    {
        $menu = $this->app->getMenu();

        // Get base menu item.
        $base = $itemid ? $menu->getItem($itemid) : null;

        if (!$base) {
            // Use active menu item or fall back to default menu item.
            $base = $this->active ?: $this->default;
        }

        // Return base menu item.
        return $base;
    }

    /**
     * Get a list of the menu items.
     *
     * Logic was originally copied from Joomla 3.4 mod_menu/helper.php
(joomla-cms/staging, 2014-11-12).
     * We should keep the contents of the function similar to Joomla in
order to review it against any changes.
     *
     * @param  array  $params
     * @param  array  $items
     */
    public function getList(array $params, array $items)
    {
        // Get base menu item for this menu (defaults to active menu item).
        $this->base = $this->calcBase($params['base']);

        // Make sure that the menu item exists.
        if (!$this->base &&
!\JFactory::getApplication()->isAdmin()) {
            return;
        }

        $levels = \JFactory::getUser()->getAuthorisedViewLevels();
        asort($levels);

        // FIXME: need to create collection class to gather the sibling
data, otherwise caching cannot work.
        //$key = 'gantry_menu_items.' . json_encode($params) .
'.' . json_encode($levels) . '.' .
$this->base->id;
        //$cache = \JFactory::getCache('mod_menu', '');
        //try {
        //    $this->items = $cache->get($key);
        //} catch (\Exception $e) {
        //    $this->items = false;
        //}

        if (1) {
            $tree    = isset($this->base->tree) ?
$this->base->tree : [];
            $start   = $params['startLevel'];
            $max     = $params['maxLevels'];
            $end     = $max ? $start + $max - 1 : 0;

            $menuItems = $this->getItemsFromPlatform($params);

            $itemMap = [];
            foreach ($items as $path => &$itemRef) {
                if (isset($itemRef['id']) &&
is_numeric($itemRef['id'])) {
                    $itemRef['path'] = $path;
                    $itemMap[$itemRef['id']] = &$itemRef;
                }
            }

            foreach ($menuItems as $menuItem) {
                if (($start && $start > $menuItem->level)
                    || ($end && $menuItem->level > $end)
                    || ($start > 1 &&
!in_array($menuItem->tree[$start - 2], $tree))) {
                    continue;
                }

                // These params always come from Joomla and cannot be
overridden.
                $itemParams = [
                    'id' => $menuItem->id,
                    'type' => $menuItem->type,
                    'alias' => $menuItem->alias,
                    'path' => $menuItem->route,
                    'link' => $menuItem->link,
                    'link_title' =>
$menuItem->params->get('menu-anchor_title', ''),
                    'rel' =>
$menuItem->params->get('menu-anchor_rel', ''),
                    'enabled' => (bool)
$menuItem->params->get('menu_show', 1),
                ];

                // Rest of the items will come from saved configuration.
                if (isset($itemMap[$menuItem->id])) {
                    // ID found, use it.
                    $itemParams += $itemMap[$menuItem->id];

                    // Store new path for the menu item into path map.
                    if ($itemParams['path'] !==
$itemMap[$menuItem->id]['path']) {
                        if (!$this->pathMap) {
                            $this->pathMap = new Config([]);
                        }
                       
$this->pathMap->set(preg_replace('|/|u',
'/children/', $itemMap[$menuItem->id]['path']) .
'/path', $itemParams['path'], '/');
                    }
                } elseif (isset($items[$menuItem->route])) {
                    // ID not found, try to use route.
                    $itemParams += $items[$menuItem->route];
                }

                // Get default target from Joomla.
                switch ($menuItem->browserNav)
                {
                    default:
                    case 0:
                        // Target window: Parent.
                        $target = '_self';
                        break;
                    case 1:
                    case 2:
                        // Target window: New with navigation.
                        $target = '_blank';
                        break;
                }

                // And if not available in configuration, default to
Joomla.
                $itemParams += [
                    'title' => $menuItem->title,
                    'anchor_class' =>
$menuItem->params->get('menu-anchor_css', ''),
                    'image' =>
$menuItem->params->get('menu_image', ''),
                    'icon_only' =>
!$menuItem->params->get('menu_text', 1),
                    'target' => $target
                ];

                $item = new Item($this, $menuItem->route, $itemParams);
                $this->add($item);

                $link  = $item->link;

                switch ($item->type) {
                    case 'separator':
                    case 'heading':
                        // These types have no link.
                        $link = null;
                        break;

                    case 'url':
                        if ((strpos($item->link, 'index.php?')
=== 0) && (strpos($item->link, 'Itemid=') === false))
{
                            // If this is an internal Joomla link, ensure
the Itemid is set.
                            $link = $item->link .
'&Itemid=' . $item->id;
                        }
                        break;

                    case 'alias':
                        // If this is an alias use the item id stored in
the parameters to make the link.
                        $link = 'index.php?Itemid=' .
$menuItem->params->get('aliasoptions', 0);
                        break;

                    default:
                        $app = $this->app;
                        $router = $app::getRouter();

                        if ($router->getMode() == JROUTER_MODE_SEF) {
                            $link = 'index.php?Itemid=' .
$item->id;

                            if
(isset($menuItem->query['format']) &&
$app->get('sef_suffix')) {
                                $link .= '&format=' .
$menuItem->query['format'];
                            }
                        } else {
                            $link .= '&Itemid=' .
$item->id;
                        }
                        break;
                }

                if (!$link) {
                    $item->url(false);
                } elseif (strcasecmp(substr($link, 0, 4), 'http')
&& (strpos($link, 'index.php?') !== false)) {
                    $item->url(\JRoute::_($link, false,
$menuItem->params->get('secure')));
                } else {
                    $item->url(\JRoute::_($link, false));
                }

                if ($item->type == 'url') {
                    // Moved from modules/mod_menu/tmpl/default_url.php,
not sure why Joomla had application logic in there.
                    // Keep compatibility to Joomla menu module, but we
need non-encoded version of the url.
                    $item->url(
                       
htmlspecialchars_decode(\JFilterOutput::ampReplace(htmlspecialchars($item->link,
ENT_COMPAT|ENT_SUBSTITUTE, 'UTF-8')))
                    );
                }
            }

            // FIXME: need to create collection class to gather the sibling
data, otherwise caching cannot work.
            // $cache->store($this->items, $key);
        }
    }
}
classes/Gantry/Framework/Outlines.php000064400000020730151166614520013726
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Admin\ThemeList;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Outline\OutlineCollection;
use Gantry\Joomla\StyleHelper;
use Gantry\Joomla\TemplateInstaller;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Outlines extends OutlineCollection
{
    protected $createId;

    public function preset($id)
    {
        if (is_numeric($id)) {
            $style = StyleHelper::getStyle($id);
            $params = json_decode($style->params, true);

            $id = isset($params['preset']) ?
$params['preset'] : 'default';
        }

        return $id;
    }

    public function current($template = null)
    {
        if (!is_object($template)) {
            // Get the template style.
            $template = \JFactory::getApplication()->getTemplate(true);
        }

        $preset = $template->params->get('preset',
'default');
        $outline = $template->params->get('configuration',
!empty($template->id) ? $template->id : null);

        GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage('Template Style:') &&
\Gantry\Debugger::addMessage($template);

        if (JDEBUG && !$outline) {
            static $shown = false;

            if (!$shown) {
                $shown = true;
               
\JFactory::getApplication()->enqueueMessage('[DEBUG]
JApplicationSite::getTemplate() was overridden with no specified Gantry 5
outline.', 'notice');
            }
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        return ($outline &&
is_dir($locator("{$this->path}/{$outline}"))) ? $outline :
$preset;
    }

    /**
     * @param string $path
     * @return $this
     */
    public function load($path = 'gantry-config://')
    {
        $this->path = $path;

        $gantry = $this->container;

        $theme = isset($gantry['theme.name']) ?
$gantry['theme.name'] : null;

        $styles = ThemeList::getStyles($theme);

        $installer = new
TemplateInstaller($this->container['theme.name']);
        $title = $installer->getStyleName('%s - ');

        $outlines = [];
        foreach ($styles as $style) {
            $preset = isset($style->params['preset']) ?
$style->params['preset'] : null;
            $outline = isset($style->params['configuration'])
? $style->params['configuration'] : $preset;

            if ($outline && $outline != $style->id) {
                // New style generated by Joomla.
                StyleHelper::copy($style, $outline, $style->id);
            }
            $outlines[$style->id] = preg_replace('|^' .
preg_quote($title) . '|', '', $style->style);
        }

        asort($outlines);

        $this->items = $this->addDefaults($outlines);

        return $this;
    }

    /**
     * @param string|null $id
     * @param string $title
     * @param string|array $preset
     * @return string
     * @throws \RuntimeException
     */
    public function create($id, $title = null, $preset = null)
    {
        if ($this->createId) {
            // Workaround Joomla wanting to use different logic for style
duplication.
            $new = parent::create($this->createId, $title, $preset);

            $this->createId = null;

            return $new;
        }

        $title = $title ? "%s - {$title}" : '%s -
Untitled';

        $installer = new
TemplateInstaller($this->container['theme.name']);
        $title = $installer->getStyleName($title);
        $style = $installer->addStyle($title);

        $error = $style->getError();

        if ($error) {
            throw new \RuntimeException($error, 400);
        }

        $presetId = (string)
(isset($preset['preset']['name']) ?
$preset['preset']['name'] : ($preset ?:
'default'));

        StyleHelper::update($style->id, $presetId);

        // Create configuration folder.
        $id = parent::create($style->id, $title, $preset);

        if ($id != $style->id) {
            throw new \RuntimeException(sprintf("Creating outline:
folder '%s' already exists!", $style->id));
        }

        return $style->id;
    }

    public function duplicate($id, $title = null, $inherit = false)
    {
        if (!$this->canDuplicate($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be duplicated", 400);
        }

        // Handle special case of duplicating system outlines.
        if ((string)(int) $id !== (string) $id) {
            return parent::duplicate($id, $title, $inherit);
        }

        // Use Joomla logic to duplicate the style.
        $model = StyleHelper::loadModel();
        $pks = [$id];

        if (!$model->duplicate($pks)) {
            throw new \RuntimeException($model->getError(), 400);
        }

        // Seek the newly generated style ID since Joomla doesn't
return one on duplication.
        $theme = $this->container['theme.name'];
        $styles = ThemeList::getStyles($theme, true);
        $style = end($styles);

        if ($title) {
            // Change the title.
            $installer = new TemplateInstaller($theme);
            $title = $installer->getStyleName("%s -
{$title}");
            $this->rename($style->id, $title);
        } else {
            $title = $style->style;
        }

        $this->createId = $style->id;

        return parent::duplicate($id, $title, $inherit);
    }

    public function rename($id, $title)
    {
        $model = StyleHelper::loadModel();

        $item = $model->getTable();
        $item->load($id);

        if (!$item->id) {
            throw new \RuntimeException('Outline not found',
404);
        }

        $theme = $this->container['theme.name'];
        $installer = new TemplateInstaller($theme);

        $title = $title ? "%s - {$title}" : '%s -
Untitled';
        $title = $installer->getStyleName($title);

        $item->title = $title;

        if (!$item->check()) {
            throw new \RuntimeException($item->getError(), 400);
        }

        if (!$item->store()) {
            throw new \RuntimeException($item->getError(), 500);
        }

        if (isset($this->items[$id])) {
            $this->items[$id] = $title;
        }

        return $id;
    }

    public function delete($id, $deleteModel = true)
    {
        if (!$this->canDelete($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be deleted", 400);
        }

        $model = StyleHelper::loadModel();

        $item = $model->getTable();
        $item->load($id);

        try {
            foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
               
$this->layout($outline)->updateInheritance($id)->save()->saveIndex();
            }
            foreach ($this->getInheritingOutlinesWithAtom($id) as
$outline => $title) {
               
Atoms::instance($outline)->updateInheritance($id)->save();
            }

            if ($deleteModel && !$model->delete($id)) {
                $error = $model->getError();
                // Well, Joomla can always send enqueue message instead!
                if (!$error) {
                    $messages =
\JFactory::getApplication()->getMessageQueue();
                    $message = reset($messages);
                    $error = $message ? $message['message'] :
'Unknown error';
                }
                throw new \RuntimeException($error);
            }
        } catch (\Exception $e) {
            throw new \RuntimeException('Deleting outline failed:
' . $e->getMessage(), 400, $e);
        }

        // Remove configuration directory.
        $gantry = $this->container;

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $path =
$locator->findResource("{$this->path}/{$item->id}",
true, true);
        if ($path) {
            if (file_exists($path)) {
                Folder::delete($path);
            }
        }

        unset($this->items[$item->id]);
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function canDelete($id)
    {
        $model = StyleHelper::loadModel();

        $item = $model->getTable();
        $item->load($id);

        return !$item->id || $item->home ? false : true;
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function isDefault($id)
    {
        $model = StyleHelper::loadModel();

        $item = $model->getTable();
        $item->load($id);

        return (bool) $item->home;
    }
}
classes/Gantry/Framework/Page.php000064400000007214151166614520013002
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

class Page extends Base\Page
{
    public $home;
    public $outline;
    public $language;
    public $direction;

    // Joomla specific properties.
    public $theme;
    public $baseUrl;
    public $title;
    public $description;

    public function __construct($container)
    {
        parent::__construct($container);

        $app = \JFactory::getApplication();
        $document = \JFactory::getDocument();
        $input = $app->input;

        $this->tmpl     = $input->getCmd('tmpl',
'');
        $this->option   = $input->getCmd('option',
'');
        $this->view     = $input->getCmd('view',
'');
        $this->layout   = $input->getCmd('layout',
'');
        $this->task     = $input->getCmd('task',
'');
        $this->itemid   = $input->getInt('Itemid', 0);
        $this->printing = $input->getCmd('print',
'');

        $this->class = '';
        if ($this->itemid) {
            $menuItem = $app->getMenu()->getActive();
            if ($menuItem && $menuItem->id) {
                $this->home = (bool) $menuItem->home;
                $this->class =
$menuItem->params->get('pageclass_sfx', '');
            }
        }
        $templateParams = $app->getTemplate(true);
        $this->outline = Gantry::instance()['configuration'];
        $this->sitename = $app->get('sitename');
        $this->theme = $templateParams->template;
        $this->baseUrl = \JUri::base(true);
        $this->title = $document->title;
        $this->description = $document->description;

        // Document has lower case language code, which causes issues with
some JS scripts (Snipcart). Use tag instead.
        $code = explode('-', $document->getLanguage(), 2);
        $language =  array_shift($code);
        $country = strtoupper(array_shift($code));
        $this->language = $language . ($country ? '-' .
$country : '');
        $this->direction = $document->direction;
    }

    public function url(array $args = [])
    {
        $url = \JUri::getInstance();

        foreach ($args as $key => $val) {
            $url->setVar($key, $val);
        }

        return $url->toString();
    }

    public function htmlAttributes()
    {
        $attributes = [
                'lang' => $this->language,
                'dir' => $this->direction
            ]
            + (array) $this->config->get('page.html', []);

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

    public function bodyAttributes($attributes = [])
    {
        if ($this->tmpl == 'component') {
            $classes = ['contentpane', 'modal'];
        } else {
            $classes = ['site', $this->option,
"view-{$this->view}"];
            $classes[] = $this->layout ? 'layout-' .
$this->layout : 'no-layout';
            $classes[] = $this->task ? 'task-' .
$this->task : 'no-task';
        }
        $classes[] = 'dir-' . $this->direction;
        if ($this->class) $classes[] = $this->class;
        if ($this->printing) $classes[] = 'print-mode';
        if ($this->itemid) $classes[] = 'itemid-' .
$this->itemid;
        if ($this->outline) $classes[] = 'outline-' .
$this->outline;

        $baseAttributes = (array)
$this->config->get('page.body.attribs', []);
        if (!empty($baseAttributes['class'])) {
            $baseAttributes['class'] = array_merge((array)
$baseAttributes['class'], $classes);
        } else {
            $baseAttributes['class'] = $classes;
        }

        return $this->getAttributes($baseAttributes, $attributes);
    }
}
classes/Gantry/Framework/Platform.php000064400000041205151166614520013710
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Admin\ThemeList;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Base\Platform as BasePlatform;
use Gantry\Joomla\Category\CategoryFinder;
use Gantry\Joomla\Content\Content;
use Gantry\Joomla\Content\ContentFinder;

/**
 * The Platform Configuration class contains configuration information.
 *
 * @author RocketTheme
 * @license MIT
 */

class Platform extends BasePlatform
{
    public $no_base_layout = false;
    public $module_wrapper = '<div
class="platform-content">%s</div>';
    public $component_wrapper = '<div class="platform-content
row-fluid"><div
class="span12">%s</div></div>';

    protected $name = 'joomla';
    protected $features = ['modules' => true];
    protected $settings_key = 'return';
    protected $modules;


    public function setModuleWrapper($html)
    {
        $this->module_wrapper = $html;
    }

    public function setComponentWrapper($html)
    {
        $this->component_wrapper = $html;
    }

    public function init()
    {
        // Support linked sample data.
        $theme = isset($this->container['theme.name']) ?
$this->container['theme.name'] : null;
        if ($theme && is_dir(JPATH_ROOT .
"/media/gantry5/themes/{$theme}/media-shared")) {
            $custom = JPATH_ROOT .
"/media/gantry5/themes/{$theme}/custom";
            if (!is_dir($custom)) {
                // First run -- copy configuration into a single location.
                $shared = JPATH_ROOT .
"/media/gantry5/themes/{$theme}/template-shared";
                $demo = JPATH_ROOT .
"/media/gantry5/themes/{$theme}/template-demo";

                try {
                    Folder::create($custom);
                } catch (\Exception $e) {
                    throw new \RuntimeException(sprintf("Failed to
create folder '%s'.", $custom), 500, $e);
                }

                if (is_dir("{$shared}/custom/config")) {
                    Folder::copy("{$shared}/custom/config",
"{$custom}/config");
                }
                if (is_dir("{$demo}/custom/config")) {
                    Folder::copy("{$demo}/custom/config",
"{$custom}/config");
                }
            }
           
array_unshift($this->items['streams']['gantry-theme']['prefixes'][''],
"media/gantry5/themes/{$theme}/template-shared");
           
array_unshift($this->items['streams']['gantry-theme']['prefixes'][''],
"media/gantry5/themes/{$theme}/template-demo");
           
array_unshift($this->items['streams']['gantry-theme']['prefixes'][''],
"media/gantry5/themes/{$theme}/custom");
        }

        return parent::init();
    }

    public function getCachePath()
    {
        $path = \JFactory::getConfig()->get('cache_path',
JPATH_SITE . '/cache');
        if (!is_dir($path)) {
            throw new \RuntimeException('Joomla cache path does not
exist!');
        }

        return $path . '/gantry5';
    }

    public function getThemesPaths()
    {
        return ['' => ['templates']];
    }

    public function getMediaPaths()
    {
        $paths = ['images'];

        // Support linked sample data.
        $theme = isset($this->container['theme.name']) ?
$this->container['theme.name'] : null;
        if ($theme && is_dir(JPATH_ROOT .
"/media/gantry5/themes/{$theme}/media-shared")) {
            array_unshift($paths,
"media/gantry5/themes/{$theme}/media-shared");
            array_unshift($paths,
"media/gantry5/themes/{$theme}/media-demo");
        }

        if
($this->container['global']->get('use_media_folder',
false)) {
            array_push($paths, 'gantry-theme://images');
        } else {
            array_unshift($paths, 'gantry-theme://images');
        }

        return ['' => $paths];
    }

    public function getEnginesPaths()
    {
        if (is_link(GANTRY5_ROOT . '/media/gantry5/engines')) {
            // Development environment.
            return ['' =>
["media/gantry5/engines/{$this->name}",
'media/gantry5/engines/common']];
        }
        return ['' => ['media/gantry5/engines']];
    }

    public function getAssetsPaths()
    {
        if (is_link(GANTRY5_ROOT . '/media/gantry5/assets')) {
            // Development environment.
            return ['' => ['gantry-theme://',
"media/gantry5/assets/{$this->name}",
'media/gantry5/assets/common']];
        }

        return ['' => ['gantry-theme://',
'media/gantry5/assets']];
    }

    /**
     * Get preview url for individual theme.
     *
     * @param string $theme
     * @return string
     */
    public function getThemePreviewUrl($theme)
    {
        return (string)(int) $theme === (string) $theme ?
\JUri::root(false) . 'index.php?templateStyle=' . $theme : null;
    }

    /**
     * Get administrator url for individual theme.
     *
     * @param string $theme
     * @return string
     */
    public function getThemeAdminUrl($theme)
    {
        $token = \JSession::getFormToken();
        return
\JRoute::_("index.php?option=com_gantry5&view=configurations/default/styles&theme={$theme}&{$token}=1"
, false);
    }

    public function filter($text)
    {
        \JPluginHelper::importPlugin('content');
        return \JHtml::_('content.prepare', $text, '',
'mod_custom.content');
    }

    public function countModules($position)
    {
        $document = \JFactory::getDocument();
        return ($document instanceof \JDocumentHTML) ?
$document->countModules($position) : 0;
    }

    public function getModules($position)
    {
        // TODO:
        return [];
    }

    public function displayModule($id, $attribs = [])
    {
        $document = \JFactory::getDocument();
        if (!$document instanceof \JDocumentHTML) {
            return '';
        }

        $module = is_object($id) ? $id : $this->getModule($id);

        // Make sure that module really exists.
        if (!is_object($module)) {
            return '';
        }

        $isGantry = \strpos($module->module, 'gantry5') !==
false;
        $content = isset($module->content) ? $module->content : null;

        $renderer = $document->loadRenderer('module');

        $html = trim($renderer->render($module, $attribs));

        // Add frontend editing feature as it has only been defined for
module positions.
        $app = \JFactory::getApplication();
        $user = \JFactory::getUser();

        $frontEditing = ($app->isSite() &&
$app->get('frontediting', 1) && !$user->guest);
        $menusEditing = ($app->get('frontediting', 1) == 2)
&& $user->authorise('core.edit',
'com_menus');

        if (!$isGantry && $frontEditing && $html &&
$user->authorise('module.edit.frontend',
'com_modules.module.' . $module->id)) {
            $displayData = [
                'moduleHtml' => &$html,
                'module' => $module,
                'position' =>
isset($attribs['position']) ? $attribs['position'] :
$module->position,
                'menusediting' => $menusEditing
            ];
           
\JLayoutHelper::render('joomla.edit.frontediting_modules',
$displayData);
        }

        // Work around Joomla "issue" which corrupts content of
custom html module (last checked J! 3.6.5).
        $module->content = $content;

        if ($html && !$isGantry) {
            $this->container['theme']->joomla(true);
            return sprintf($this->module_wrapper, $html);
        }

        return $html;
    }

    public function displayModules($position, $attribs = [])
    {
        $document = \JFactory::getDocument();
        if (!$document instanceof \JDocumentHTML) {
            return '';
        }

        $html = '';
        foreach (\JModuleHelper::getModules($position) as $module) {
            $html .= $this->displayModule($module, $attribs);
        }

        return $html;
    }

    public function displaySystemMessages($params = [])
    {
        // We cannot use JDocument renderer here as it fires too early to
display any messages.
        return '<jdoc:include type="message" />';
    }

    public function displayContent($content, $params = [])
    {
        $document = \JFactory::getDocument();
        if (!$document instanceof \JDocumentHTML) {
            return $content;
        }

        $renderer = $document->loadRenderer('component');

        $html = trim($renderer->render(null, $params, $content ?:
$document->getBuffer('component')));

        $isGantry =
\strpos(\JFactory::getApplication()->input->getCmd('option'),
'gantry5') !== false;

        if ($html && !$isGantry) {
            $this->container['theme']->joomla(true);
            return sprintf($this->component_wrapper, $html);
        }

        return $html;
    }

    public function getModule($id)
    {
        $modules = $this->getModuleList();
        return $id && isset($modules[$id]) ? $modules[$id] : null;
    }

    protected function &getModuleList()
    {
        if ($this->modules === null) {
            $modules = \JModuleHelper::getModuleList();

            $this->modules = [];
            foreach ($modules as $module) {
                $this->modules[$module->id] = $module;
            }
        }
        return $this->modules;
    }

    public function listModules()
    {
        $db = \JFactory::getDbo();
        $query = $db->getQuery(true);

        $query->select('a.id, a.title, a.position, a.module,
a.published AS enabled')
            ->from('#__modules AS a');

        // Join on the asset groups table.
        $query->select('ag.title AS access')
            ->join('LEFT', '#__viewlevels AS ag ON ag.id
= a.access')
            ->where('a.published >= 0')
            ->where('a.client_id = 0')
            ->order('a.position, a.module, a.ordering');

        $db->setQuery($query);

        try {
            $result = $db->loadObjectList();
        } catch (\RuntimeException $e) {
            return false;
        }

        return $result;
    }

    public function getEditor($name, $content = '', $width =
null, $height = null)
    {
        $conf = \JFactory::getConfig();
        $editor = \JEditor::getInstance($conf->get('editor'));
        if (!$height) {
            $height = 250;
        }

        return $editor->display($name, $content, $width, $height, 50, 8,
false, null, null, null, ['html_height' => $height]);
    }

    public function errorHandlerPaths()
    {
        return ['|gantry5|'];
    }

    public function settings()
    {
        if (!$this->authorize('platform.settings.manage')) {
            return '';
        }

        return
\JRoute::_('index.php?option=com_config&view=component&component=com_gantry5',
false);
    }

    public function update()
    {
        return
\JRoute::_('index.php?option=com_installer&view=update',
false);
    }

    public function updates()
    {
        if (!$this->authorize('updates.manage')) {
            return [];
        }

        $styles = ThemeList::getThemes();

        $extension_ids = array_unique(array_map(
            function($item) {
                return (int) $item->extension_id;
            },
            $styles));

        $extension_ids = $extension_ids ? implode(',',
$extension_ids) : '-1';

        $db = \JFactory::getDbo();
        $query = $db->getQuery(true);
        $query
            ->select('*')
            ->from('#__updates')
            ->where("element='pkg_gantry5' OR
extension_id IN ($extension_ids)");

        $db->setQuery($query);

        $updates = $db->loadObjectList();

        $list = [];
        foreach ($updates as $update) {
            if ($update->element === 'pkg_gantry5') {
                // Rename Gantry 5 package.
                $update->name = 'Gantry';
                // Ignore git and CI installs and if the Gantry version is
the same or higher than in the updates.
                if (version_compare(GANTRY5_VERSION, 0) < 0 ||
version_compare($update->version, GANTRY5_VERSION) <= 0) {
                    continue;
                }
            } else {
                // Check if templates need to be updated.
                $version = isset($styles[$update->element]) ?
$styles[$update->element]->get('details.version') : null;
                if (version_compare($version, 0) < 0 ||
version_compare($update->version, $version) <= 0) {
                    continue;
                }
            }
            $list[] = $update->name . ' ' .
$update->version;
        }

        return $list;
    }

    public function factory()
    {
        $args = func_get_args();
        $method = ['JFactory', 'get'. ucfirst((string)
array_shift($args))];
        return method_exists($method[0], $method[1]) ?
call_user_func_array($method, $args) : null;
    }

    public function instance()
    {
        $args = func_get_args();
        $class = ucfirst((string) array_shift($args));
        if (!$class) {
            return null;
        }
        if (class_exists('J'. $class)) {
            $class = 'J'. $class;
        }
        $method = [$class, 'getInstance'];
        return method_exists($method[0], $method[1]) ?
call_user_func_array($method, $args) : null;
    }

    public function route()
    {
        return call_user_func_array(['JRoute', '_'],
func_get_args());
    }

    public function html()
    {
        $args = func_get_args();
        if (isset($args[0]) && method_exists('JHtml',
$args[0])) {
            return call_user_func_array(['JHtml',
array_shift($args)], $args);
        }
        return call_user_func_array(['JHtml', '_'],
$args);
    }

    public function article($keys)
    {
        return Content::getInstance($keys);
    }

    public function finder($domain, $options = null)
    {
        $options = (array) $options;
        switch ($domain) {
            case 'article':
            case 'articles':
            case 'content':
                $finder = new ContentFinder($options);

                return \JFactory::getApplication()->isSite() ?
$finder->authorised() : $finder;
            case 'category':
            case 'categories':
                $finder = (new
CategoryFinder($options))->extension('content');

                return \JFactory::getApplication()->isSite() ?
$finder->authorised() : $finder;
        }

        return null;
    }

    public function truncate($text, $length, $html = false)
    {
        return \JHtml::_('string.truncate', $text, $length, true,
$html);
    }

    public function authorize($action, $id = null)
    {
        $user = \JFactory::getUser();

        switch ($action) {
            case 'platform.settings.manage':
                return $user->authorise('core.admin',
'com_templates') || $user->authorise('core.admin',
'com_gantry5');
            case 'menu.manage':
                return $user->authorise('core.manage',
'com_menus') &&
$user->authorise('core.edit', 'com_menus');
            case 'menu.edit':
                if ($id) {
                    $db = \JFactory::getDbo();
                    $userId = \JFactory::getUser()->id;

                    // Verify that no items are checked out.
                    $query = $db->getQuery(true)
                        ->select('id')
                        ->from('#__menu')
                        ->where('menutype=' .
$db->quote($id))
                        ->where('checked_out !=' . (int)
$userId)
                        ->where('checked_out !=0');
                    $db->setQuery($query);

                    if ($db->loadRowList()) {
                        return false;
                    }

                    // Verify that no module for this menu are checked out.
                    $query->clear()
                        ->select('id')
                        ->from('#__modules')
                        ->where('module=' .
$db->quote('mod_menu'))
                        ->where('params LIKE ' .
$db->quote('%"menutype":' . json_encode($id) .
'%'))
                        ->where('checked_out !=' . (int)
$userId)
                        ->where('checked_out !=0');
                    $db->setQuery($query);

                    if ($db->loadRowList()) {
                        return false;
                    }
                }
                return $user->authorise('core.edit',
'com_menus');
            case 'updates.manage':
                return $user->authorise('core.manage',
'com_installer');
            case 'outline.create':
                return $user->authorise('core.create',
'com_templates');
            case 'outline.delete':
                 return $user->authorise('core.delete',
'com_templates');
            case 'outline.rename':
                return $user->authorise('core.edit',
'com_templates');
            case 'outline.assign':
                return $user->authorise('core.edit.state',
'com_templates') &&
$user->authorise('core.edit', 'com_menu');
            case 'outline.edit':
                return true;
        }

        return true;
    }
}
classes/Gantry/Framework/Positions.php000064400000000765151166614520014121
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework;

use Gantry\Component\Position\Positions as BasePositions;

class Positions extends BasePositions
{
}
classes/Gantry/Framework/Request.php000064400000000753151166614520013557
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework;

use Gantry\Component\Request\Request as BaseRequest;

class Request extends BaseRequest {}
classes/Gantry/Framework/Services/ConfigServiceProvider.php000064400000007551151166614520020156
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Services;

use Gantry\Component\Config\CompiledBlueprints;
use Gantry\Component\Config\CompiledConfig;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Framework\Atoms;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class ConfigServiceProvider implements ServiceProviderInterface
{
    public function register(Container $gantry)
    {
        $gantry['blueprints'] = function($c) {
            GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('blueprints', 'Loading
blueprints');

            $blueprints = static::blueprints($c);

            GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('blueprints');

            return $blueprints;
        };

        $gantry['config'] = function($c) {
            // Make sure configuration has been set.
            if (!isset($c['configuration'])) {
                throw new \LogicException('Gantry: Please set current
configuration before using $gantry["config"]', 500);
            }

            GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('config', 'Loading
configuration');

            // Get the current configuration and lock the value from
modification.
            $outline = $c->lock('configuration');

            $config = static::load($c, $outline);

            GANTRY_DEBUGGER &&
\Gantry\Debugger::setConfig($config)->stopTimer('config');

            return $config;
        };
    }

    public static function blueprints(Container $container)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $container['locator'];

        $cache =
$locator->findResource('gantry-cache://theme/compiled/blueprints',
true, true);

        $files = [];
        $paths =
$locator->findResources('gantry-particles://');
        $files += (new
ConfigFileFinder)->setBase('particles')->locateFiles($paths);
        $paths =
$locator->findResources('gantry-blueprints://');
        $files += (new ConfigFileFinder)->locateFiles($paths);

        $config = new CompiledBlueprints($cache, $files, GANTRY5_ROOT);

        return $config->load();
    }

    public static function load(Container $container, $name =
'default', $combine = true, $withDefaults = true)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $container['locator'];

        $combine = $combine && $name !== 'default';

        // Merge current configuration with the default.
        $uris = $combine ? ["gantry-config://{$name}",
'gantry-config://default'] :
["gantry-config://{$name}"];

        $paths = [];
        foreach ($uris as $uri) {
            $paths = array_merge($paths, $locator->findResources($uri));
        }

        // Locate all configuration files to be compiled.
        $files = (new ConfigFileFinder)->locateFiles($paths);

        $cache =
$locator->findResource('gantry-cache://theme/compiled/config',
true, true);

        if (!$cache) {
            throw new \RuntimeException('Who just removed Gantry 5
cache folder? Try reloading the page if it fixes the issue');
        }

        $compiled = new CompiledConfig($cache, $files, GANTRY5_ROOT);
        $compiled->setBlueprints(function() use ($container) {
            return $container['blueprints'];
        });

        $config = $compiled->load($withDefaults);

        // Set atom inheritance.
        $atoms = $config->get('page.head.atoms');
        if (is_array($atoms)) {
            $config->set('page.head.atoms', (new
Atoms($atoms))->init()->toArray());
        }

        return $config;
    }
}
classes/Gantry/Framework/Services/ErrorServiceProvider.php000064400000004351151166614520020035
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Services;

use Gantry\Component\Whoops\SystemFacade;
use Gantry\Framework\Platform;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Whoops\Handler\JsonResponseHandler;
use Whoops\Handler\PrettyPageHandler;
use Whoops\Run;
use Whoops\Util\Misc;

class ErrorServiceProvider implements ServiceProviderInterface
{
    protected $format;

    public function __construct($format = 'html')
    {
        $this->format = $format;
    }

    public function register(Container $container)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $container['locator'];

        /** @var Platform $platform */
        $platform = $container['platform'];

        // Setup Whoops-based error handler
        $system = new SystemFacade($platform->errorHandlerPaths());
        $errors = new Run($system);

        $error_page = new PrettyPageHandler;
        $error_page->setPageTitle('Crikey! There was an
error...');
        $error_page->setEditor('sublime');
        foreach
($locator->findResources('gantry-assets://css/whoops.css') as
$path) {
            $error_page->addResourcePath(dirname($path));
        }
        $error_page->addCustomCss('whoops.css');

        $errors->pushHandler($error_page);

        $jsonRequest = $this->format === 'json' || ($_SERVER
&& isset($_SERVER['HTTP_ACCEPT']) &&
$_SERVER['HTTP_ACCEPT'] == 'application/json');
        if (Misc::isAjaxRequest() || $jsonRequest) {
            $json_handler = new JsonResponseHandler;
            //$json_handler->setJsonApi(true);

            $errors->pushHandler($json_handler);
        }

        $errors->register();

        $container['errors'] = $errors;

        if (GANTRY_DEBUGGER &&
method_exists('Gantry\Debugger', 'setErrorHandler')) {
            \Gantry\Debugger::setErrorHandler();
        }
    }
}
classes/Gantry/Framework/Services/StreamsServiceProvider.php000064400000002464151166614520020365
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Services;

use Gantry\Component\Filesystem\Streams;
use Pimple\Container;
use RocketTheme\Toolbox\DI\ServiceProviderInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class StreamsServiceProvider implements ServiceProviderInterface
{
    public function register(Container $gantry)
    {
        $sp = $this;

        $gantry['locator'] = function() use ($sp) {
            return new UniformResourceLocator(GANTRY5_ROOT);
        };
        $gantry['streams'] = function($c) use ($sp) {
            $schemes = (array)
$c['platform']->init()->get('streams');

            /** @var UniformResourceLocator $locator */
            $locator = $c['locator'];

            $streams = new Streams($locator);
            $streams->add($schemes);

            GANTRY_DEBUGGER &&
method_exists('Gantry\Debugger', 'setLocator')
&& \Gantry\Debugger::setLocator($locator);

            return $streams;
        };
    }
}
classes/Gantry/Framework/Site.php000064400000001155151166614520013030
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

class Site
{
    public function __construct()
    {
        $document = \JFactory::getDocument();

        if ($document instanceof \JDocumentHTML) {
            $this->theme = $document->template;
            $this->url = $document->baseurl;
            $this->title = $document->title;
            $this->description = $document->description;
        }
    }
}
classes/Gantry/Framework/Theme.php000064400000014207151166614520013170
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Theme\AbstractTheme;
use Gantry\Component\Theme\ThemeTrait;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class Theme
 * @package Gantry\Framework
 */
class Theme extends AbstractTheme
{
    use ThemeTrait;

    /**
     * @var bool
     */
    protected $joomla = false;

    /**
     * If parameter is set to true, loads bootstrap. Returns true if
bootstrap has been loaded.
     *
     * @param bool|null $enable
     * @return bool
     */
    public function joomla($enable = null)
    {
        if ($enable && !$this->joomla) {
            $this->joomla = true;

            // Workaround for Joomla! not loading bootstrap when it needs
it.
            $this->gantry()->load('bootstrap.2');
        }

        return $this->joomla;
    }

    /**
     * @see AbstractTheme::extendTwig()
     *
     * @param \Twig_Environment $twig
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Environment
     */
    public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
    {
        parent::extendTwig($twig, $loader);

        /** @var \Twig_Extension_Core $core */
        $core = $twig->getExtension('Twig_Extension_Core');

        // Get user timezone and if not set, use Joomla default.
        $timezone = \JFactory::getUser()->getParam('timezone',
\JFactory::getConfig()->get('offset', 'UTC'));
        $core->setTimezone(new \DateTimeZone($timezone));

        // Set locale for dates and numbers.
        $core->setDateFormat(\JText::_('DATE_FORMAT_LC2'),
\JText::_('GANTRY5_X_DAYS'));
        $core->setNumberFormat(0,
\JText::_('DECIMALS_SEPARATOR'),
\JText::_('THOUSANDS_SEPARATOR'));

        $filter = new \Twig_SimpleFilter('date', [$this,
'twig_dateFilter'], array('needs_environment' =>
true));
        $twig->addFilter($filter);

        return $twig;
    }

    /**
     * Converts a date to the given format.
     *
     * <pre>
     *   {{ post.published_at|date("m/d/Y") }}
     * </pre>
     *
     * @param \Twig_Environment                                 $env
     * @param \DateTime|\DateTimeInterface|\DateInterval|string $date     A
date
     * @param string|null                                       $format  
The target format, null to use the default
     * @param \DateTimeZone|string|null|false                   $timezone
The target timezone, null to use the default, false to leave unchanged
     *
     * @return string The formatted date
     */
    public function twig_dateFilter(\Twig_Environment $env, $date, $format
= null, $timezone = null)
    {
        if (null === $format) {
            $formats =
$env->getExtension('Twig_Extension_Core')->getDateFormat();
            $format = $date instanceof \DateInterval ? $formats[1] :
$formats[0];
        }

        if ($date instanceof \DateInterval) {
            return $date->format($format);
        }

        if (!($date instanceof \JDate)) {
            // Create localized JDate object.
            $twig_date = \twig_date_converter($env, $date, $timezone);

            $date = new \JDate($twig_date->getTimestamp());
            $date->setTimezone($twig_date->getTimezone());
        } elseif ($timezone) {
            $date->setTimezone($timezone);
        }

        return $date->format($format, true);
    }

    /**
     * @see AbstractTheme::getContext()
     *
     * @param array $context
     * @return array
     */
    public function getContext(array $context)
    {
        $gantry = static::gantry();

        $context = parent::getContext($context);
        $context['site'] = $gantry['site'];
        $context['joomla'] = $gantry['platform'];

        return $context;
    }

    /**
     * @see AbstractTheme::init()
     */
    protected function init()
    {
        parent::init();

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $lang = \JFactory::getLanguage();

        // FIXME: Do not hardcode this file.
        $lang->load('files_gantry5_nucleus', JPATH_SITE);

        if (\JFactory::getApplication()->isSite()) {
            // Load our custom positions file as frontend requires the
strings to be there.
            $filename =
$locator("gantry-theme://language/en-GB/en-GB.tpl_{$this->name}_positions.ini");

            if ($filename) {
                $lang->load("tpl_{$this->name}_positions",
\dirname(\dirname(\dirname($filename))), 'en-GB');
            }

            // Load template language files, including overrides.
            $paths =
$locator->findResources('gantry-theme://language');
            foreach (array_reverse($paths) as $path) {
                $lang->load("tpl_{$this->name}",
\dirname($path));
            }
        }

        $doc = \JFactory::getDocument();
        if ($doc instanceof \JDocumentHtml) {
            $doc->setHtml5(true);
        }
        $this->language = $doc->language;
        $this->direction = $doc->direction;
        $this->url = \JUri::root(true) . '/templates/' .
$this->name;

        \JPluginHelper::importPlugin('gantry5');

        // Trigger the onGantryThemeInit event.
        $dispatcher = \JEventDispatcher::getInstance();
        $dispatcher->trigger('onGantry5ThemeInit',
['theme' => $this]);
    }

    /**
     * Get list of twig paths.
     *
     * @return array
     */
    public static function getTwigPaths()
    {
        /** @var UniformResourceLocator $locator */
        $locator = static::gantry()['locator'];

        return
$locator->mergeResources(['gantry-theme://twig',
'gantry-engine://twig']);
    }

    /**
     * @see AbstractTheme::setTwigLoaderPaths()
     *
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Loader_Filesystem
     */
    protected function setTwigLoaderPaths(\Twig_LoaderInterface $loader)
    {
        $loader = parent::setTwigLoaderPaths($loader);

        if ($loader) {
            $loader->setPaths($this->getTwigPaths());
        }

        return $loader;
    }
}
classes/Gantry/Framework/ThemeInstaller.php000064400000042612151166614520015047
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Layout\Layout;
use Gantry\Component\Theme\ThemeInstaller as AbstractInstaller;
use Gantry\Joomla\Manifest;
use RocketTheme\Toolbox\File\YamlFile;

class ThemeInstaller extends AbstractInstaller
{
    protected $extension;
    protected $manifest;

    public function __construct($extension = null)
    {
        parent::__construct();

        jimport('joomla.filesystem.folder');

        \JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');
        if ($extension instanceof \JInstallerAdapterTemplate) {
            $this->setInstaller($extension);
        } elseif ($extension) {
            $this->loadExtension($extension);
        }
    }

    public function setInstaller(\JInstallerAdapterTemplate $install)
    {
        // We need access to a protected variable $install->extension.
        $reflectionClass = new \ReflectionClass($install);
        $property =
$reflectionClass->getProperty('extension');
        $property->setAccessible(true);
        $this->extension = $property->getValue($install);
        $this->name = $this->extension->name;

        $this->manifest = new Manifest($this->extension->name,
$install->getManifest());

        return $this;
    }

    public function loadExtension($id)
    {
        if ((string) intval($id) !== (string) $id) {
            $id = ['type' => 'template',
'element' => (string) $id, 'client_id' => 0];
        }
        $this->extension = \JTable::getInstance('extension');
        $this->extension->load($id);
        $this->name = $this->extension->name;
    }

    public function getPath()
    {
        return JPATH_SITE . '/templates/' .
$this->extension->name;
    }

    public function getStyleName($title)
    {
        return \JText::sprintf($title,
\JText::_($this->extension->name));
    }

    public function getStyle($name = null)
    {
        if (is_numeric($name)) {
            $field = 'id';
        } else {
            $field = 'title';
            $name = $this->getStyleName($name);
        }

        $style = $this->createStyle();
        $style->load([
            'template' => $this->extension->element,
            'client_id' => $this->extension->client_id,
            $field => $name
        ]);

        return $style;
    }

    public function getDefaultStyle()
    {
        $style = \JTable::getInstance('Style',
'TemplatesTable');
        $style->load(['home' => 1, 'client_id'
=> 0]);

        return $style;
    }

    /**
     * @param string $type
     * @return \JTableMenu
     */
    public function getMenu($type)
    {
        /** @var \JTableMenuType $table */
        $table = \JTable::getInstance('MenuType');
        $table->load(['menutype' => $type]);

        return $table;
    }

    public function createSampleData()
    {
        $this->updateStyle('JLIB_INSTALLER_DEFAULT_STYLE', [],
1);
        $this->installMenus();
    }

    public function render($template, $context = [])
    {
        $token = \JSession::getFormToken();
        $manifest = $this->getManifest();
        $context += [
            'description' => $this->translate((string)
$manifest->get('description')),
            'version' => (string)
$manifest->get('version'),
            'date' => (string)
$manifest->get('creationDate'),
            'author' => [
                'name' => (string)
$manifest->get('author'),
                'email' => (string)
$manifest->get('authorEmail'),
                'url' => (string)
$manifest->get('authorUrl')
            ],
            'copyright' => (string)
$manifest->get('copyright'),
            'license' => (string)
$manifest->get('license'),
            'install_url' =>
\JRoute::_("index.php?option=com_gantry5&view=install&theme={$this->name}&{$token}=1",
false),
            'edit_url' =>
\JRoute::_("index.php?option=com_gantry5&view=configurations/default/styles&theme={$this->name}&{$token}=1",
false),
        ];

        return parent::render($template, $context);
    }

    public function createStyle()
    {
        $style = \JTable::getInstance('Style',
'TemplatesTable');
        $style->reset();
        $style->template = $this->extension->element;
        $style->client_id = $this->extension->client_id;

        return $style;
    }

    public function addStyle($title, array $configuration = [], $home = 0)
    {
        // Make sure language debug is turned off.
        $lang = \JFactory::getLanguage();
        $debug = $lang->setDebug(false);

        // Translate title.
        $title = $this->getStyleName($title);

        // Turn language debug back on.
        $lang->setDebug($debug);

        $data = [
            'home' => (int) $home,
            'title' => $title,
            'params' => json_encode($configuration),
        ];

        $style = $this->createStyle();
        $style->save($data);

        if ($home) {
            $this->actions[] = ['action' =>
'default_style_assigned', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_DEFAULT_STYLE_ASSIGNED',
$title)];
        }

        return $style;
    }

    public function updateStyle($name, array $configuration, $home = null)
    {
        $style = $this->getStyle($name);

        if ($style->id) {
            $home = ($home !== null ? $home : $style->home);
            $params = (array) json_decode($style->params, true);

            $data = [
                'params' => json_encode($configuration +
$params),
                'home' => $home
            ];

            if ($home && !$style->home) {
                $this->actions[] = ['action' =>
'default_style_assigned', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_DEFAULT_STYLE_ASSIGNED',
$style->title)];
            }

            $style->save($data);
        }

        return $style;
    }

    public function assignHomeStyle($style)
    {
        // Update the mapping for menu items that this style IS assigned
to.
        $db = \JFactory::getDbo();

        $query = $db->getQuery(true)
            ->update('#__menu')
            ->set('template_style_id=' . (int) $style->id)
            ->where('home=1')
            ->where('client_id=0');
        $db->setQuery($query);
        $db->execute();

        if ($db->getAffectedRows()) {
            $this->actions[] = ['action' =>
'home_style_assigned', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_HOME_STYLE_ASSIGNED',
$style->title)];
        }
    }

    /**
     * @param string $folder
     * @param array $params
     * @return string|bool
     */
    public function createOutline($folder, array $params = [])
    {
        if (!$folder) {
            throw new \RuntimeException('Cannot create outline without
folder name');
        }

        $this->initialize();

        $created = false;

        $params += [
            'preset' => null,
            'title' => null
        ];

        $title = $params['title'] ?: ucwords(trim(strtr($folder,
['_' => ' '])));
        $preset = $params['preset'] ?: 'default';

        if ($folder[0] !== '_') {
            $title = $this->getStyleName($title !== 'Default'
? "%s - {$title}" : 'JLIB_INSTALLER_DEFAULT_STYLE');
            $style = $this->getStyle($title);

            if (!$style->id) {
                // Only add style if it doesn't exist.
                $style = $this->addStyle($title, ['preset'
=> $preset]);
                $created = true;
            }

            $id = $style->id;

        } else {
            $id = $folder;
        }

        $target = $folder !== 'default' ? $id : $folder;

        // Copy configuration for the new layout.
        if (($this->copyCustom($folder, $target) || $created) &&
isset($style)) {
            // Update layout and save it.
            $layout = Layout::load($target, $preset);
            $layout->save()->saveIndex();

            if ($id !== $target) {
                // Default outline: Inherit everything from the base.
                $layout->inheritAll()->name = $id;
                $layout->save()->saveIndex();

                $this->actions[] = ['action' =>
'base_outline_created', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_BASE_OUTLINE_CREATED',
$title)];
            }

            if ($created) {
                $this->actions[] = ['action' =>
'outline_created', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_CREATED',
$title)];
            } else {
                $this->actions[] = ['action' =>
'outline_updated', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_UPDATED',
$title)];
            }

            // Update preset in Joomla table.
            $this->updateStyle($title, ['preset' =>
$layout['preset']['name']]);
        }

        return $id;
    }

    /**
     * @param  array $item       [menutype, title, alias, link,
template_style_id, params]
     * @param  int   $parent_id  Parent menu id.
     * @param  bool  $load       True if updating existing items.
     * @return int
     * @throws \Exception
     */
    public function addMenuItem(array $item, $parent_id = 1, $load = false)
    {
        $component_id = $this->getComponent();

        $table = \JTable::getInstance('menu');
        $date = new \JDate();
        $update = false;

        // Defaults for the item.
        $item += [
            'menutype' => 'mainmenu',
            'title' => 'Home',
            'alias' => 'gantry5',
            'note' => '',
            'link' =>
'index.php?option=com_gantry5&view=custom',
            'type' => 'component',
            'published' => 1,
            'parent_id' => $parent_id,
            'component_id' => $component_id,
            'checked_out' => 0,
            'checked_out_time' => $date->toSql(),
            'browserNav' => 0,
            'access' => 1,
            'img' => '',
            'template_style_id' => 0,
            'params' => '{}',
            'home' => 0,
            'language' => '*',
            'client_id' => 0
        ];

        if (in_array($item['type'], ['separator',
'heading'])) {
            $item['link'] = '';
        }

        if ($item['type'] !== 'component') {
            $item['component_id'] = 0;
        }

        if ($load) {
            $update = $table->load([
                'menutype' => $item['menutype'],
                'alias' => $item['alias'],
                'parent_id' => $item['parent_id']
            ]);
        }

        $table->setLocation($parent_id, 'last-child');

        if (!$table->bind($item) || !$table->check() ||
!$table->store()) {
            throw new \Exception($table->getError());
        }

        /** @var \JCache|\JCacheController $cache */
        $cache = \JFactory::getCache();
        $cache->clean('mod_menu');

        $menu = \JTable::getInstance('menuType');
        $menu->load(['menutype' =>
$item['menutype']]);

        if
(!isset($this->actions["menu_{$item['menutype']}_created"]))
{
            $postfix = $item['home'] ? '_HOME' :
'';
            if ($update) {
                $this->actions[] = ['action' =>
'menu_item_updated', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_ITEM_UPDATED' .
$postfix, $table->title, $table->path, $menu->title)];
            } else {
                $this->actions[] = ['action' =>
'menu_item_created', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_ITEM_CREATED' .
$postfix, $table->title, $table->path, $menu->title)];
            }
        } elseif ($item['home']) {
            $this->actions[] = ['action' =>
'menu_item_updated', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_ITEM_HOME',
$table->title, $table->path, $menu->title)];
        }

        return $table->id;
    }

    public function installMenus(array $menus = null, $parent = 1)
    {
        if ($menus === null) {
            $path = $this->getPath();

            $file = YamlFile::instance($path .
'/install/menus.yaml');
            $menus = (array) $file->content();
            $file->free();
        }

        foreach ($menus as $menutype => $menu) {
            $title = !empty($menu['title']) ?
$menu['title'] : ucfirst($menutype);
            $description = !empty($menu['description']) ?
$menu['description'] : '';

            $exists = $this->getMenu($menutype)->id;

            // If $parent = 0, do dry run.
            if ((int) $parent && !$exists) {
                $this->deleteMenu($menutype, true);
                $this->createMenu($menutype, $title, $description);
            }

            if (!empty($menu['items'])) {
                $this->addMenuItems($menutype, $menu['items'],
(int) $parent);
            }
        }
    }

    /**
     * @param string $type
     * @param string $title
     * @param string $description
     * @throws \Exception
     */
    public function createMenu($type, $title, $description)
    {
        /** @var \JTableMenuType $table */
        $table = \JTable::getInstance('MenuType');
        $data  = array(
            'menutype'    => $type,
            'title'       => $title,
            'description' => $description
        );

        if (!$table->bind($data) || !$table->check()) {
            // Menu already exists, do nothing
            return;
        }

        if (!$table->store()) {
            throw new \Exception($table->getError());
        }

        $this->actions["menu_{$type}_created"] =
['action' => 'menu_created', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_CREATED',
$title)];
    }

    /**
     * @param string $type
     * @param bool $force
     */
    public function deleteMenu($type, $force = false)
    {
        if ($force) {
            $this->unsetHome($type);
        }

        $table = \JTable::getInstance('MenuType');
        $table->load(array('menutype' => $type));

        if ($table->id) {
            $success = $table->delete();

            if (!$success) {
               
\JFactory::getApplication()->enqueueMessage($table->getError(),
'error');
            } else {
                $this->actions["menu_{$type}_deleted"] =
['action' => 'menu_delete', 'text' =>
\JText::_('GANTRY5_INSTALLER_ACTION_MENU_DELETED',
$table->title)];
            }
        }

        /** @var \JCache|\JCacheController $cache */
        $cache = \JFactory::getCache();
        $cache->clean('mod_menu');
    }

    public function unsetHome($type)
    {
        // Update the mapping for menu items that this style IS assigned
to.
        $db = \JFactory::getDbo();

        $query = $db->getQuery(true)
            ->update('#__menu')
            ->set('home=0')
            ->where('menutype=' . $db->quote($type))
            ->where('client_id=0');
        $db->setQuery($query);
        $db->execute();
    }

    /**
     * @deprecated 5.3.2
     */
    public function cleanup()
    {
        $this->initialize();
        $this->finalize();
    }

    public function finalize()
    {
        parent::finalize();

        $gantry = Gantry::instance();

        /** @var Outlines $outlines */
        $outlines = $gantry['outlines'];
        $name = $this->extension->name;

        // Update positions in manifest file.
        $positions = $outlines->positions();

        $manifest = new Manifest($name);
        $manifest->setPositions(array_keys($positions));
        $manifest->save();
    }

    protected function addMenuItems($menutype, array $items, $parent)
    {
        foreach ($items as $alias => $item) {
            $item = (array) $item;
            $item += [
                'menutype' => $menutype,
                'title' => ucfirst($alias),
                'alias' => $alias
            ];

            $outline = isset($item['outline']) ?
$item['outline'] : (isset($item['layout']) ?
$item['layout'] : null);
            $params = $this->getOutline($outline);
            if (!is_array($params)) {
                $params = [
                    'preset' =>
isset($item['preset']) ? $item['preset'] :
(isset($item['layout']) ? $item['layout'] : null),
                    'title' => isset($item['style'])
? $item['style'] : null
                ];
            }

            $id = $outline ? $this->createOutline($outline, $params) :
0;
            $item['template_style_id'] = (string)(int) $id ===
(string) $id ? $id : 0;

            // If $parent = 0, do dry run.
            $itemId = $parent ? $this->addMenuItem($item, $parent, true)
: 0;
            if (!empty($item['items'])) {
                $this->addMenuItems($menutype, $item['items'],
$itemId);
            }
        }
    }

    protected function getInstallerScript()
    {
        if (!$this->script) {
            $className = $this->extension->name .
'InstallerScript';

            if (!class_exists($className)) {
                $manifest = new Manifest($this->extension->name);
                $file = $manifest->getScriptFile();

                $path = "{$this->getPath()}/{$file}";
                if ($file && is_file($path)) {
                    require_once $path;
                }
            }

            if (class_exists($className)) {
                $this->script = new $className;
            }
        }

        return $this->script;
    }

    protected function getManifest()
    {
        if (!$this->manifest) {
            $this->manifest = new
Manifest($this->extension->name);
        }

        return $this->manifest;
    }

    protected function getComponent()
    {
        static $component_id;

        if (!$component_id) {
            // Get Gantry component id.
            $component_id =
\JComponentHelper::getComponent('com_gantry5')->id;
        }

        return $component_id;
    }
}
classes/Gantry/Framework/Translator.php000064400000001134151166614520014252
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Translator\Translator as BaseTranslator;

class Translator extends BaseTranslator
{
    public function translate($string)
    {
        if (\func_num_args() === 1) {
            return \JText::_($string);
        }

        $args = \func_get_args();
        return \call_user_func_array(['JText',
'sprintf'], $args);
    }
}
classes/Gantry/Joomla/Assignments/AssignmentsMenu.php000064400000004063151166614520017024
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Assignments;

use Gantry\Component\Assignments\AssignmentsInterface;

class AssignmentsMenu implements AssignmentsInterface
{
    public $type = 'menu';
    public $priority = 1;

    /**
     * Returns list of rules which apply to the current page.
     *
     * @return array
     */
    public function getRules()
    {
        $rules = [];

        $app = \JFactory::getApplication();
        if ($app->isSite()) {
            $active = $app->getMenu()->getActive();
            if ($active) {
                $menutype = $active->menutype;
                $id = $active->id;
                $rules = [$menutype => [$id => $this->priority]];
            }
        }

        return $rules;
    }

    /**
     * List all the rules available.
     *
     * @param string $configuration
     * @return array
     */
    public function listRules($configuration)
    {
        require_once JPATH_ADMINISTRATOR .
'/components/com_menus/helpers/menus.php';
        $data = \MenusHelper::getMenuLinks();

        $userid = \JFactory::getUser()->id;

        $list = [];

        foreach ($data as $menu) {
            $items = [];
            foreach ($menu->links as $link) {
                $items[] = [
                    'name' => $link->value,
                    'field' => ['id',
'link' . $link->value],
                    'value' => $link->template_style_id ==
$configuration,
                    'disabled' => $link->type !=
'component' || $link->checked_out &&
$link->checked_out != $userid,
                    'label' => str_repeat('—',
max(0, $link->level-1)) . ' ' . $link->text
                ];
            }
            $group = [
                'label' => $menu->title ?:
$menu->menutype,
                'items' => $items
            ];

            $list[$menu->menutype] = $group;
        }

        return $list;
    }
}
classes/Gantry/Joomla/Assignments/AssignmentsStyle.php000064400000003621151166614520017217
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Assignments;

use Gantry\Component\Assignments\AssignmentsInterface;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class AssignmentsStyle implements AssignmentsInterface
{
    public $type = 'style';
    public $priority = 2;

    /**
     * Returns list of rules which apply to the current page.
     *
     * @return array
     */
    public function getRules()
    {
        static $rules;

        if (!isset($rules)) {
            $rules = [];

            $template = \JFactory::getApplication()->getTemplate(true);

            $theme = $template->template;
            $outline =
$template->params->get('configuration',
!empty($template->id) ? $template->id :
$template->params->get('preset', null));

            if (JDEBUG) {
                GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage('Template Style:',
'debug') && \Gantry\Debugger::addMessage($template,
'debug');

                if (!$outline) {
                   
\JFactory::getApplication()->enqueueMessage('JApplicationSite::getTemplate()
was overridden with no specified Gantry 5 outline.',
'debug');
                }
            }

            /** @var UniformResourceLocator $locator */
            $locator = Gantry::instance()['locator'];

            if ($outline &&
is_dir($locator("gantry-themes://{$theme}/custom/config/{$outline}")))
{
                $rules = ['id' => [$outline =>
$this->priority]];
            }
        }

        return $rules;
    }

    /**
     * List all the rules available.
     *
     * @param string $configuration
     * @return array
     */
    public function listRules($configuration)
    {
        return [];
    }
}
classes/Gantry/Joomla/CacheHelper.php000064400000003007151166614520013551
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla;

class CacheHelper
{
    public static function cleanTemplates()
    {
        self::cleanByType('com_templates');
        self::cleanByType('_system');
    }

    public static function cleanMenu()
    {
        self::cleanByType('mod_menu');
        self::cleanByType('_system');
    }

    public static function cleanPlugin()
    {
        self::cleanByType('_system', 0);
        self::cleanByType('_system', 1);
        self::cleanByType('com_plugins', 0);
        self::cleanByType('com_plugins', 1);
    }

    private static function cleanByType($group = null, $client_id = 0,
$event = 'onContentCleanCache')
    {
        $conf = \JFactory::getConfig();
        $dispatcher = \JEventDispatcher::getInstance();

        $options = array(
            'defaultgroup' => $group,
            'cachebase' => ($client_id) ? JPATH_ADMINISTRATOR
. '/cache' : $conf->get('cache_path', JPATH_SITE .
'/cache'),
            'result' => true
        );

        try {
            $cache = \JCache::getInstance('callback', $options);
            $cache->clean();
        } catch (\Exception $e) { // TODO: Joomla 3.7 uses JCacheException
            $options['result'] = false;
        }

        // Trigger the onContentCleanCache event.
        $dispatcher->trigger($event, $options);
    }
}
classes/Gantry/Joomla/Category/Category.php000064400000003302151166614520014736
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Category;

use Gantry\Framework\Gantry;
use Gantry\Joomla\Object\AbstractObject;

class Category extends AbstractObject
{
    static protected $instances = [];

    static protected $table = 'Category';
    static protected $order = 'lft';

    public function initialize()
    {
        if (!parent::initialize()) {
            return false;
        }

        $this->params = json_decode($this->params);
        $this->metadata = json_decode($this->metadata);

        return true;
    }

    public function parent()
    {
        if ($this->alias != $this->path)
        {
            $parent = Category::getInstance($this->parent_id);
        }

        return isset($parent) && $parent->extension ==
$this->extension ? $parent : null;
    }

    public function parents()
    {
        $parent = $this->parent();

        return $parent ? array_merge($parent->parents(), [$parent]) :
[];
    }

    public function route()
    {
        require_once JPATH_SITE .
'/components/com_content/helpers/route.php';

        return
\JRoute::_(\ContentHelperRoute::getCategoryRoute($this->id .
':' . $this->alias), false);
    }

    public function render($file)
    {
        return Gantry::instance()['theme']->render($file,
['category' => $this]);
    }

    public function compile($string)
    {
        return Gantry::instance()['theme']->compile($string,
['category' => $this]);
    }

    public function toArray()
    {
        return $this->getProperties(true);
    }
}
classes/Gantry/Joomla/Category/CategoryFinder.php000064400000010073151166614520016071
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Category;

use Gantry\Joomla\Object\Finder;

class CategoryFinder extends Finder
{
    protected $table = '#__categories';
    protected $extension = 'com_content';
    protected $readonly = true;

    /**
     * Makes all created objects as readonly.
     *
     * @return $this
     */
    public function readonly($readonly = true)
    {
        $this->readonly = (bool)$readonly;

        return $this;
    }

    public function find($object = true)
    {
        $ids = parent::find();

        if (!$object) {
            return $ids;
        }

        return Category::getInstances($ids, $this->readonly);
    }

    public function id($ids, $levels = 0)
    {
        if ($ids && $levels) {
            $ids = (array) $ids;

            $db = $this->db;
            array_walk($ids, function (&$item) use ($db) { $item =
$db->quote($item); });
            $idList = implode(',', $ids);

            // Create a subquery for the subcategory list
            $subQuery = $this->db->getQuery(true)
                ->select('sub.id')
                ->from('#__categories AS sub')
                ->join('INNER', '#__categories AS this ON
sub.lft > this.lft AND sub.rgt < this.rgt')
                ->where("this.id IN ({$idList})");

            if (is_numeric($levels)) {
                $subQuery->where('sub.level <= this.level +
' . (int) $levels);
            }

            // Add the subquery to the main query
            $this->query->where("(a.id IN ({$idList}) OR a.id IN
({$subQuery->__toString()}))");
        } else {
            $this->where('a.id', 'IN', $ids);
        }

        return $this;
    }

    public function language($language = true)
    {
        if (!$language) {
            return $this;
        }
        if ($language === true || is_numeric($language)) {
            $language = \JFactory::getLanguage()->getTag();
        }
        return $this->where('a.language', 'IN',
[$language, '*']);
    }

    public function published($published = 1)
    {
        if (!is_array($published)) {
            $published = (array) intval($published);
        }
        return $this->where('a.published', 'IN',
$published);
    }

    public function authorised($authorised = true)
    {
        if (!$authorised) {
            return $this;
        }

        // Ignore unpublished categories.
        $unpublished = $this->getUnpublished($this->extension);

        if ($unpublished) {
            $this->where('a.id', 'NOT IN',
$unpublished);
        }

        // Check authorization.
        $user = \JFactory::getUser();
        $groups = $user->getAuthorisedViewLevels();

        return $this->where('a.access', 'IN',
$groups);
    }

    public function extension($extension)
    {
        $this->extension = static::getExtension($extension);

        return $this->where('a.extension', '=',
$this->extension);
    }

    public static function getExtension($extension)
    {
        static $map = [
            'article' => 'com_content',
            'articles' => 'com_content',
            'content' => 'com_content',
        ];

        if (isset($map[$extension])) {
            $extension = $map[$extension];
        }

        return $extension;
    }

    public static function getUnpublished($extension)
    {
        static $list;

        if ($list === null) {
            $db = \JFactory::getDbo();

            $query = $db->getQuery(true)
                ->select('cat.id AS id')
                ->from('#__categories AS cat')
                ->join('LEFT', '#__categories AS parent
ON cat.lft BETWEEN parent.lft AND parent.rgt')
                ->where('parent.extension = ' .
$db->quote(static::getExtension($extension)))
                ->where('parent.published != 1 AND cat.published
< 1')
                ->group('cat.id');

            $db->setQuery($query);
            $list = $db->loadColumn();
        }

        return $list;
    }
}
classes/Gantry/Joomla/Content/Content.php000064400000006227151166614520014441
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Content;

use Gantry\Framework\Gantry;
use Gantry\Joomla\Category\Category;
use Gantry\Joomla\Object\AbstractObject;

class Content extends AbstractObject
{
    static protected $instances = [];

    static protected $table = 'Content';
    static protected $order = 'id';

    public function initialize()
    {
        if (!parent::initialize()) {
            return false;
        }

        $this->images = json_decode($this->images);
        $this->urls = json_decode($this->urls);
        $this->attribs = json_decode($this->attribs);
        $this->metadata = json_decode($this->metadata);

        $nullDate = \JFactory::getDbo()->getNullDate();
        if ($this->modified === $nullDate) {
            $this->modified = $this->created;
        }
        if ($this->publish_up === $nullDate) {
            $this->publish_up = $this->created;
        }

        return true;
    }

    public function author()
    {
        return \JUser::getInstance($this->created_by);
    }

    public function category()
    {
        return Category::getInstance($this->catid);
    }

    public function categories()
    {
        $category = $this->category();

        return array_merge($category->parents(), [$category]);
    }

    public function text()
    {
        return $this->introtext . ' ' . $this->fulltext;
    }

    public function preparedText()
    {
        return \JHtml::_('content.prepare', $this->text());
    }

    public function preparedIntroText()
    {
        return \JHtml::_('content.prepare', $this->introtext);
    }

    public function readmore()
    {
        return (bool)strlen($this->fulltext);
    }

    public function route()
    {
        require_once JPATH_SITE .
'/components/com_content/helpers/route.php';

        $category = $this->category();

        return \JRoute::_(\ContentHelperRoute::getArticleRoute($this->id
. ':' . $this->alias, $category->id . ':' .
$category->alias), false);
    }

    public function edit()
    {
        $user = \JFactory::getUser();
        $asset = "com_content.article.{$this->id}";

        if ($user->authorise('core.edit', $asset) ||
$user->authorise('core.edit.own', $asset)) {
            return
"index.php?option=com_content&task=article.edit&a_id={$this->id}&tmpl=component";
        }

        return false;
    }

    public function render($file)
    {
        return Gantry::instance()['theme']->render($file,
['article' => $this]);
    }

    public function compile($string)
    {
        return Gantry::instance()['theme']->compile($string,
['article' => $this]);
    }

    public function toArray()
    {
        return $this->getProperties(true) + [
            'category' => [
                'alias' => $this->category()->alias,
                'title' => $this->category()->title
            ],
            'author' => [
                'username' => $this->author()->username,
                'fullname' => $this->author()->name
            ],
        ];
    }
}
classes/Gantry/Joomla/Content/ContentFinder.php000064400000007673151166614520015577
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Content;

use Gantry\Joomla\Category\Category;
use Gantry\Joomla\Category\CategoryFinder;
use Gantry\Joomla\Object\Collection;
use Gantry\Joomla\Object\Finder;

class ContentFinder extends Finder
{
    protected $table = '#__content';
    protected $readonly = true;
    protected $state = [];

    /**
     * Makes all created objects as readonly.
     *
     * @return $this
     */
    public function readonly($readonly = true)
    {
        $this->readonly = (bool)$readonly;

        return $this;
    }

    public function find($object = true)
    {
        $ids = parent::find();

        if (!$object) {
            return $ids;
        }

        return Content::getInstances($ids, $this->readonly);
    }

    public function id($ids, $include = true)
    {
        return $this->addToGroup('a.id', $ids, $include);
    }

    public function author($ids, $include = true)
    {
        return $this->addToGroup('a.created_by', $ids,
$include);
    }

    public function category($ids, $include = true)
    {
        if ($ids instanceof Collection) {
            $ids = $ids->toArray();
        } else {
            $ids = (array)$ids;
        }

        array_walk($ids, function (&$item) { $item = $item instanceof
Category ? $item->id : (int) $item; });

        return $this->addToGroup('a.catid', $ids, $include);
    }

    public function featured($featured = true)
    {
        $featured = intval((bool)$featured);
        $this->where('a.featured', '=', $featured);

        return $this;
    }

    public function language($language = true)
    {
        if (!$language) {
            return $this;
        }
        if ($language === true || is_numeric($language)) {
            $language = \JFactory::getLanguage()->getTag();
        }
        return $this->where('a.language', 'IN',
[$language, '*']);
    }

    public function published($published = 1)
    {
        if (!is_array($published)) {
            $published = (array) intval($published);
        }
        return $this->where('a.state', 'IN',
$published);
    }

    public function authorised($authorised = true)
    {
        if (!$authorised) {
            return $this;
        }

        $unpublished = CategoryFinder::getUnpublished('content');
        if ($unpublished) {
            $this->where('a.catid', 'NOT IN',
$unpublished);
        }

        $user = \JFactory::getUser();

        // Define null and now dates
        $nullDate = $this->db->quote($this->db->getNullDate());
        $nowDate =
$this->db->quote(\JFactory::getDate()->toSql());

        // Filter by start and end dates.
        if (!$user->authorise('core.edit.state',
'com_content') &&
!$user->authorise('core.edit', 'com_content')) {
            $this->query
                ->where("(a.publish_up = {$nullDate} OR
a.publish_up <= {$nowDate})")
                ->where("(a.publish_down = {$nullDate} OR
a.publish_down >= {$nowDate})")
                ->where("a.state >= 1")
            ;
        }

        $groups = $user->getAuthorisedViewLevels();

        $this->query->join('INNER', '#__categories AS
c ON c.id = a.catid');

        return $this->where('a.access', 'IN',
$groups)->where('c.access', 'IN', $groups);
    }

    protected function addToGroup($key, $ids, $include = true)
    {
        $op = $include ? 'IN' : 'NOT IN';

        if (isset($this->state[$key][$op])) {
            $this->state[$key][$op] =
array_merge($this->state[$key][$op], $ids);
        } else {
            $this->state[$key][$op] = $ids;
        }

        return $this;
    }

    protected function prepare()
    {
        foreach ($this->state as $key => $list) {
            foreach ($list as $op => $group) {
                $this->where($key, $op, array_unique($group));
            }
        }
    }
}
classes/Gantry/Joomla/Manifest.php000064400000004324151166614520013157
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla;

/**
 * Joomla manifest file modifier.
 */
class Manifest
{
    protected $theme;
    protected $path;
    protected $xml;

    /**
     * @param string $theme
     * @param \SimpleXMLElement $manifest
     * @throws \RuntimeException
     */
    public function __construct($theme, \SimpleXMLElement $manifest = null)
    {
        $this->theme = $theme;
        $this->path = JPATH_SITE .
"/templates/{$theme}/templateDetails.xml";

        if (!is_file($this->path)) {
            throw new \RuntimeException(sprintf('Template %s does not
exist.', $theme));
        }
        $this->xml = $manifest ?: simplexml_load_file($this->path);
    }

    /**
     * @param string $variable
     * @return string
     */
    public function get($variable)
    {
        return (string) $this->xml->{$variable};
    }

    /**
     * @return \SimpleXMLElement
     */
    public function getXml()
    {
        return $this->xml;
    }

    public function getScriptFile()
    {
        return (string) $this->xml->scriptfile;
    }

    public function setPositions(array $positions)
    {
        sort($positions);

        // Get the positions.
        $target =
current($this->xml->xpath('//positions'));

        $xml = "<positions>\n        <position>" .
implode("</position>\n        <position>",
$positions) . "</position>\n    </positions>";
        $insert = new \SimpleXMLElement($xml);

        // Replace all positions.
        $targetDom = dom_import_simplexml($target);
        $insertDom =
$targetDom->ownerDocument->importNode(dom_import_simplexml($insert),
true);
        $targetDom->parentNode->replaceChild($insertDom, $targetDom);
    }


    public function save()
    {
        // Do not save manifest if template has been symbolically linked.
        if (is_link(dirname($this->path))) {
            return;
        }

        if (!$this->xml->asXML($this->path)) {
            throw new \RuntimeException(sprintf('Saving manifest for
%s template failed', $this->theme));
        }
    }
}
classes/Gantry/Joomla/Module/Module.php000064400000012244151166614520014063
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Module;

use Gantry\Framework\Gantry;
use Gantry\Joomla\Object\AbstractObject;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;

\JTable::addIncludePath(JPATH_LIBRARIES . '/legacy/table/');

class Module extends AbstractObject implements ExportInterface
{
    use Export;

    static protected $instances = [];

    static protected $table = 'Module';
    static protected $order = 'id';
    
    protected $_assignments;

    public function assignments($assignments = null)
    {
        if (is_array($assignments)) {
            $this->_assignments = array_map('intval',
array_values($assignments));

        } elseif (!isset($this->_assignments)) {
            $db = \JFactory::getDbo();
            $query = $db->getQuery(true);
           
$query->select('menuid')->from('#__modules_menu')->where('moduleid
= ' . $this->id);
            $db->setQuery($query);

            $this->_assignments = array_map('intval', (array)
$db->loadColumn());
        }
        
        return $this->_assignments;
    }

    public function initialize()
    {
        if (!parent::initialize()) {
            return false;
        }

        $this->params = json_decode($this->params);

        return true;
    }

    public function toArray()
    {
        $particle = $this->module === 'mod_gantry5_particle';

        // Convert params to array.
        $params = json_decode(json_encode($this->params), true);

        $array = [
            'id' => $this->id,
            'position' => $this->position,
            'ordering' => (int) $this->ordering,
            'type' => $particle ? 'particle' :
'joomla',
            'title' => $this->title,
            'chrome' => [
                'display_title' => (bool) $this->showtitle,
                'class' =>
!empty($params['moduleclass_sfx']) ?
$params['moduleclass_sfx'] : ''
            ],
            'options' => null,
            'assignments' => $this->assignments()
        ];

        $options = array_filter(
            [
                'type' => !$particle ? $this->module :
null,
                'note' => $this->note ?: null,
                'published' => (bool) $this->published,
                'content' => $this->content ?: null,
                'params' => &$params,
                'language' => $this->language !==
'*' ? $this->language : null,
            ],
            [$this, 'is_not_null']
        );

        if ($particle) {
            $array['joomla'] = $options;
            $options = !empty($params['particle']) ?
json_decode($params['particle'], true) : [];
            $options['type'] =
isset($options['particle']) ? $options['particle'] :
null;
            $options['attributes'] =
isset($options['options']['particle']) ?
$options['options']['particle'] : [];

            unset($options['particle'],
$options['options']);

            $array['options'] = $options;

            unset($params['particle']);
        } else {
            $array['options'] = $options;
        }

        return array_filter($array, [$this, 'is_not_null']);
    }

    public function create(array $array)
    {
        $type = $array['type'];

        if ($type === 'particle') {
            $particle = isset($array['options']) ?
$array['options'] : [];
            $array['options'] = isset($array['joomla'])
? $array['joomla'] : [];
            $array['options']['type'] =
'mod_gantry5_particle';
           
$array['options']['params']['particle'] =
$particle;

        } elseif ($type !== 'joomla') {
            return null;
        }

        $options = $array['options'];

        $properties = [
            'title' => $array['title'],
            'note' => isset($options['note']) ?
$options['note'] : '',
            'content' => isset($options['content'])
? $options['content'] : '',
            'position' => $array['position'],
            'ordering' => (int) $array['ordering'],
            'published' => (int)
!empty($options['published']),
            'module' => $options['type'],
            'showtitle' => (int)
!empty($array['chrome']['display_title']),
            'params' => isset($options['params']) ?
json_decode(json_encode($options['params'])) : [],
            'language' =>
isset($options['language']) ? $options['language'] :
'*',
            '_assignments' =>
isset($array['assignments']) ? $array['assignments'] :
[],
        ];

        $object = new static();
        $object->bind($properties);

        return $object;
    }

    public function render($file)
    {
        return Gantry::instance()['theme']->render($file,
['particle' => $this]);
    }

    public function compile($string)
    {
        return Gantry::instance()['theme']->compile($string,
['particle' => $this]);
    }

    // Internal functions

    /**
     * @param $val
     * @return bool
     * @internal
     */
    public function is_not_null($val)
    {
        return !is_null($val);
    }

    static protected function collection($items)
    {
        return new ModuleCollection($items);
    }
}
classes/Gantry/Joomla/Module/ModuleCollection.php000064400000006353151166614520016103
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Module;

use Gantry\Joomla\Object\Collection;

class ModuleCollection extends Collection
{
    public function toArray()
    {
        return $this->__call('toArray', []);
    }

    public function export()
    {
        $assignments = $this->assignments();
        $paths =
$this->getAssignmentPath($this->values($assignments));

        $items = $this->toArray();
        $positions = [];

        // Convert assignments to our format.
        foreach ($items as $item) {
            $position = $item['position'];
            $name = $item['options']['type'] .
'-' . $item['id'];

            if ($position === '') {
                continue;
            }
            if (empty($item['assignments'])) {
                $item['assignments'] = [];
            } elseif (in_array(0, $item['assignments'])) {
                $item['assignments'] = ['page' =>
true];
            } else {
                $list = [];
                foreach ($item['assignments'] as $assignment) {
                    $key = abs($assignment);
                    if (isset($paths[$key])) {
                        $list[$paths[$key]] = $assignment > 0 ? 1 : -1;
                    }
                }
                $item['assignments'] = ['page' =>
[$list]];
            }
            unset($item['position'], $item['id'],
$item['ordering']);

            $positions[$position][$name] = $item;
        }

        return $positions;
    }

    public function assignments()
    {
        $this->loadAssignments();

        return $this->__call('assignments', []);
    }

    public function loadAssignments()
    {
        $ids = $this->defined('assignments', false);
        $ids = array_filter($ids);

        if (!$ids) {
            return;
        }

        $idlist = implode(',', array_keys($ids));

        $db = \JFactory::getDbo();
        $query = $db->getQuery(true);
        $query->select('moduleid,
menuid')->from('#__modules_menu')->where("moduleid
IN ($idlist)");
        $db->setQuery($query);

        $assignments = $db->loadRowList();

        $list = [];
        foreach ($assignments as $value) {
            $list[$value[0]][] = (int) $value[1];
        }

        foreach ($this as $module) {
            $module->assignments(isset($list[$module->id]) ?
$list[$module->id] : []);
        }
    }

    protected function getAssignmentPath(array $ids)
    {
        if (!$ids) {
            return [];
        }

        $idlist = implode(',', array_map('intval',
$ids));

        $db = \JFactory::getDbo();
        $query = $db->getQuery(true);
        $query->select('id,
path')->from('#__menu')->where("id IN
($idlist)");
        $db->setQuery($query);

        $paths = $db->loadRowList();

        $list = [];
        foreach ($paths as $value) {
            $list[$value[0]] = $value[1];
        }

        return $list;
    }

    protected function values($values)
    {
        $list = [];
        foreach ($values as $array) {
            $list = array_merge($list, (array) $array);
        }

        return array_unique($list);
    }
}
classes/Gantry/Joomla/Module/ModuleFinder.php000064400000005110151166614520015205
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Module;

use Gantry\Joomla\Object\Finder;

class ModuleFinder extends Finder
{
    protected $table = '#__modules';
    protected $readonly = true;
    protected $state = [];
    protected $published = [0, 1];
    protected $limit = 0;

    /**
     * Makes all created objects as readonly.
     *
     * @return $this
     */
    public function readonly($readonly = true)
    {
        $this->readonly = (bool)$readonly;

        return $this;
    }

    public function find($object = true)
    {
        $ids = parent::find();

        if (!$object) {
            return $ids;
        }

        return Module::getInstances($ids, $this->readonly);
    }

    public function id($ids, $include = true)
    {
        return $this->addToGroup('a.id', $ids, $include);
    }

    public function language($language = true)
    {
        if (!$language) {
            return $this;
        }
        if ($language === true || is_numeric($language)) {
            $language = \JFactory::getLanguage()->getTag();
        }
        return $this->where('a.language', 'IN',
[$language, '*']);
    }

    public function published($published = 1)
    {
        if (!is_array($published)) {
            $published = (array) intval($published);
        }

        $this->published = $published;

        return $this;
    }

    public function particle()
    {
        return $this->where('a.module', '=',
'mod_gantry5_particle');
    }

    public function authorised($authorised = true)
    {
        if (!$authorised) {
            return $this;
        }

        $groups = \JFactory::getUser()->getAuthorisedViewLevels();

        return $this->where('a.access', 'IN',
$groups);
    }

    protected function addToGroup($key, $ids, $include = true)
    {
        $op = $include ? 'IN' : 'NOT IN';

        if (isset($this->state[$key][$op])) {
            $this->state[$key][$op] =
array_merge($this->state[$key][$op], $ids);
        } else {
            $this->state[$key][$op] = $ids;
        }

        return $this;
    }

    protected function prepare()
    {
        $this->where('client_id', '=',
0)->where('published', 'IN',
$this->published)->order('position')->order('ordering');
        foreach ($this->state as $key => $list) {
            foreach ($list as $op => $group) {
                $this->where($key, $op, array_unique($group));
            }
        }
    }
}
classes/Gantry/Joomla/Object/AbstractObject.php000064400000031372151166614520015514
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Object;

/**
 * Abstract base class for database objects.
 *
 *
 */
abstract class AbstractObject extends \JObject
{
    /**
     * If you don't have global instance ids, override this in
extending class.
     * @var array
     */
    static protected $instances = [];

    /**
     * Override table class in your own class.
     * @var string
     */
    static protected $table;

    /**
     * JTable class prefix, override if needed.
     * @var string
     */
    static protected $tablePrefix = 'JTable';

    /**
     * Override table in your own class.
     * @var string
     */
    static protected $order;

    /**
     * @var int
     */
    public $id;

    /**
     * Is object stored into database?
     * @var boolean
     */
    protected $_exists = false;

    /**
     * Readonly object.
     * @var bool
     */
    protected $_readonly = false;

    /**
     * @var bool
     */
    protected $_initialized = false;

    /**
     * Class constructor, overridden in descendant classes.
     *
     * @param int $identifier Identifier.
     */
    public function __construct($identifier = null)
    {
        parent::__construct();

        if ($identifier) {
            $this->load($identifier);
        }
    }

    /**
     * Override this function if you need to initialize object right after
creating it.
     *
     * Can be used for example if the database fields need to be converted
to array or JRegistry.
     *
     * @return bool True if initialization was done, false if object was
already initialized.
     */
    public function initialize()
    {
        $initialized = $this->_initialized;
        $this->_initialized = true;

        return !$initialized;
    }

    /**
     * Make instance as read only object.
     */
    public function readonly()
    {
        $this->_readonly = true;
    }

    /**
     * Returns the global instance to the object.
     *
     * Note that using array of fields will always make a query to the
database, but it's very useful feature if you want to search
     * one item by using arbitrary set of matching fields. If there are
more than one matching object, first one gets returned.
     *
     * @param  int|array  $keys        An optional primary key value to
load the object by, or an array of fields to match.
     * @param  boolean    $reload      Force object reload from the
database.
     *
     * @return  Object
     */
    static public function getInstance($keys = null, $reload = false)
    {
        // If we are creating or loading a new item or we load instance by
alternative keys,
        // we need to create a new object.
        if (!$keys || is_array($keys) || !isset(static::$instances[(int)
$keys])) {
            $c = get_called_class();
            $instance = new $c($keys);
            /** @var Object $instance */
            if (!$instance->exists()) return $instance;

            // Instance exists: make sure that we return the global
instance.
            $keys = $instance->id;
        }

        // Return global instance from the identifier, possibly reloading
it first.
        $instance = static::$instances[(int) $keys];
        if ($reload) $instance->load($keys);

        return $instance;
    }

    /**
     * Removes all or selected instances from the object cache.
     *
     * @param null|int|array  $ids
     */
    static public function freeInstances($ids = null)
    {
        if ($ids === null) {
            $ids = array_keys(static::$instances);
        }
        $ids = (array) $ids;

        foreach ($ids as $id) {
            unset(static::$instances[$id]);
        }
    }

    /**
     * Returns true if the object exists in the database.
     *
     * @param   boolean  $exists  Internal parameter to change state.
     *
     * @return  boolean  True if object exists in database.
     */
    public function exists($exists = null)
    {
        $return = $this->_exists;
        if ($exists !== null) $this->_exists = (bool) $exists;
        return $return;
    }

    /**
     * Tests if dynamically defined property has been defined.
     *
     * @param string $property
     * @param bool   $defined
     * @return bool
     */
    public function defined($property, $defined = true)
    {
        $property = '_' . $property;

        return $defined ? isset($this->{$property}) :
!isset($this->{$property});
    }

    /**
     * Returns an associative array of object properties.
     *
     * @param   boolean  $public  If true, returns only the public
properties.
     *
     * @return  array
     */
    public function getProperties($public = true)
    {
        if ($public) {
            $getProperties = function($obj) { return get_object_vars($obj);
};
            return $getProperties($this);
        }

        return get_object_vars($this);
    }

    /**
     * Method to bind an associative array to the instance.
     *
     * This method optionally takes an array of properties to ignore or
allow when binding.
     *
     * @param   array    $src     An associative array or object to bind to
the JTable instance.
     * @param   array    $fields  An optional array list of properties to
ignore / include only while binding.
     * @param   boolean  $include  True to include only listed fields,
false to ignore listed fields.
     *
     * @return  boolean  True on success.
     */
    public function bind(array $src = null, array $fields = null, $include
= false)
    {
        if (empty($src)) return false;

        if (!empty($fields)) {
            $src = $include ? array_intersect_key($src,
array_flip($fields)) : array_diff_key($src, array_flip($fields));
        }
        $this->setProperties ( $src );
        return true;
    }

    /**
     * Method to load object from the database.
     *
     * @param   mixed    $keys   An optional primary key value to load the
object by, or an array of fields to match. If not
     *                           set the instance key value is used.
     *
     * @return  boolean  True on success, false if the object doesn't
exist.
     */
    public function load($keys = null)
    {
        if ($keys !== null && !is_array($keys)) {
            $keys = array('id'=>(int) $keys);
        }

        // Create the table object.
        $table = static::getTable ();

        // Make sure we set the given keys to the object even if it is not
loaded.
        $table->reset();
        if ($keys !== null) $table->bind($keys);

        // Load the object based on the keys.
        $this->_exists = $table->load($keys, false);

        // Work around Joomla 3.1.1 bug on load() returning true if keys
didn't exist.
        if ($table->id == 0) $this->_exists = false;

        // Assuming all is well at this point lets bind the data.
        $this->setProperties($table->getProperties());

        if ($this->id) {
            if (!isset(static::$instances[$this->id])) {
                static::$instances[$this->id] = $this;
            }
        }
        $this->initialize();

        return $this->_exists;
    }

    /**
     * Method to save the object to the database.
     *
     * Before saving the object, this method checks if object can be safely
saved.
     * It will also trigger onContentBeforeSave and onContentAfterSave
events.
     *
     * @return  boolean  True on success.
     */
    public function save()
    {
        // Check the object.
        if ($this->_readonly || !$this->check()) {
            return false;
        }

        $isNew = !$this->_exists;

        // Initialize table object.
        $table = static::getTable ();
        $table->bind($this->getProperties());

        // Check the table object.
        if (!$table->check()) {
            $this->setError($table->getError());
            return false;
        }

        // Include the content plugins for the on save events.
        $dispatcher = \JEventDispatcher::getInstance();
        \JPluginHelper::importPlugin('content');

        // Trigger the onContentBeforeSave event.
        $result = $dispatcher->trigger('onContentBeforeSave',
array("com_gantry5.".get_called_class(), $table, $isNew));
        if (in_array(false, $result, true)) {
            $this->setError($table->getError());
            return false;
        }

        // Store the data.
        if (!$table->store()) {
            $this->setError($table->getError());
            return false;
        }

        // If item was created, load the object.
        if ($isNew) {
            $this->load($table->id);

            if (!isset(static::$instances[$this->id])) {
                static::$instances[$this->id] = $this;
            }
        }

        // Trigger the onContentAfterSave event.
        $dispatcher->trigger('onContentAfterSave',
array("com_gantry5.".get_called_class(), $table, $isNew));

        return true;
    }

    /**
     * Method to delete the object from the database.
     *
     * @return	boolean	True on success.
     */
    public function delete()
    {
        if ($this->_readonly) {
            return false;
        }

        if (!$this->_exists) {
            return true;
        }

        // Initialize table object.
        $table = static::getTable();
        $table->bind($this->getProperties());

        // Include the content plugins for the on save events.
        $dispatcher = \JEventDispatcher::getInstance();
        \JPluginHelper::importPlugin('content');

        // Trigger the onContentBeforeDelete event.
        $result =
$dispatcher->trigger('onContentBeforeDelete',
array("com_gantry5.".get_called_class(), $table));
        if (in_array(false, $result, true)) {
            $this->setError($table->getError());
            return false;
        }

        if (!$table->delete()) {
            $this->setError($table->getError());
            return false;
        }
        $this->_exists = false;

        // Trigger the onContentAfterDelete event.
        $dispatcher->trigger('onContentAfterDelete',
array("com_gantry5.".get_called_class(), $table));

        return true;
    }

    /**
     * Method to perform sanity checks on the instance properties to ensure
     * they are safe to store in the database.
     *
     * Child classes should override this method to make sure the data they
are storing in
     * the database is safe and as expected before storage.
     *
     * @return  boolean  True if the instance is sane and able to be stored
in the database.
     */
    public function check()
    {
        return true;
    }

    static public function getAvailableInstances()
    {
        return static::collection(static::$instances);
    }

    static public function getInstances(array $ids, $readonly = true)
    {
        if (!$ids) {
            return array();
        }

        $results = array();
        $list = array();

        foreach ($ids as $id) {
            if (!isset(static::$instances[$id])) {
                $list[] = $id;
            }
        }

        if ($list) {
            $query = static::getQuery();
            $query->where('id IN (' . implode(',',
$list) . ')');
            static::loadInstances($query);
        }

        foreach ($ids as $id) {
            if (isset(static::$instances[$id])) {
                if ($readonly) {
                    $results[$id] = clone static::$instances[$id];
                } else {
                    $results[$id] = static::$instances[$id];
                }
            }
        }

        return static::collection($results);
    }

    // Internal functions

    static protected function collection($items)
    {
        return new Collection($items);
    }

    /**
     * Method to get the table object.
     *
     * @return  \JTable  The table object.
     */
    static protected function getTable()
    {
        return \JTable::getInstance(static::$table, static::$tablePrefix);
    }

    /**
     * @return \JDatabaseQuery
     */
    static protected function getQuery()
    {
        $table = static::getTable();
        $db = \JFactory::getDbo();
        $query = $db->getQuery(true);
       
$query->select('a.*')->from($table->getTableName().'
AS a')->order(static::$order);

        return $query;
    }

    /**
     * @param \JDatabaseQuery|string $query
     */
    static protected function loadInstances($query = null)
    {
        if (!$query) {
            $query = static::getQuery();
        }

        $db = \JFactory::getDbo();
        $db->setQuery($query);

        /** @var Object[] $items */
        $items = (array) $db->loadObjectList('id',
get_called_class());

        foreach ($items as $item) {
            if (!isset(static::$instances[$item->id])) {
                $item->exists(true);
                $item->initialize();
            }
        }

        static::$instances += $items;
    }
}
classes/Gantry/Joomla/Object/Collection.php000064400000001770151166614520014714
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Object;

use \Gantry\Component\Collection\Collection as BaseCollection;

class Collection extends BaseCollection
{
    public function __construct(array $items)
    {
        $this->items = $items;
    }

    public function get($property)
    {
        $list = [];

        if ($property === 'id') {
            return array_keys($this->items);
        }

        foreach ($this as $object) {
            $list[$object->id] = $object->{$property};
        }

        return $list;
    }

    public function __call($name, $arguments)
    {
        $list = [];

        foreach ($this as $object) {
            $list[$object->id] = method_exists($object, $name) ?
call_user_func_array([$object, $name], $arguments) : null;
        }

        return $list;
    }
}
classes/Gantry/Joomla/Object/Finder.php000064400000013102151166614520014020
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Object;

/**
 * Class Finder
 * @package Gantry\Joomla\Object
 */
abstract class Finder
{
    /**
     * Table associated with the model.
     *
     * @var string
     */
    protected $table;

    /**
     * @var string
     */
    protected $primaryKey = 'id';

    /**
     * @var \JDatabaseQuery
     */
    protected $query;

    /**
     * @var \JDatabase
     */
    protected $db;

    protected $start = 0;

    protected $limit = 20;

    protected $skip = false;

    /**
     * Finder constructor.
     *
     * @param array $options
     */
    public function __construct(array $options = [])
    {
        if (!$this->table) {
            throw new \DomainException('Table name missing from '
. get_class($this));
        }

        $this->db = \JFactory::getDbo();
        $this->query = $this->db->getQuery(true);
        $this->query->from($this->table . ' AS a');

        if ($options) {
            $this->parse($options);
        }
    }

    public function parse(array $options)
    {
        foreach ($options as $func => $params) {
            if (method_exists($this, $func)) {
                call_user_func_array([$this, $func], (array) $params);
            }
        }

        return $this;
    }

    /**
     * Set limitstart for the query.
     *
     * @param int $limitstart
     *
     * @return $this
     */
    public function start($limitstart = 0)
    {
        $this->start = $limitstart;

        return $this;
    }

    /**
     * Set limit to the query.
     *
     * @param int $limit
     *
     * @return $this
     */
    public function limit($limit = null)
    {
        if (!is_null($limit))
        {
            $this->limit = $limit;
        }

        return $this;
    }

    /**
     * Set order by field and direction.
     *
     * This function can be used more than once to chain order by.
     *
     * @param  string $by
     * @param  int $direction
     * @param  string $alias
     *
     * @return $this
     */
    public function order($by, $direction = 1, $alias = 'a')
    {
        if (is_numeric($direction)) {
            $direction = $direction > 0 ? 'ASC' :
'DESC';
        } else {
            $direction = strtolower((string)$direction) == 'desc'
? 'DESC' : 'ASC';
        }
        $by = (string)$alias . '.' .
$this->db->quoteName($by);
        $this->query->order("{$by} {$direction}");

        return $this;
    }

    /**
     * Filter by field.
     *
     * @param  string        $field       Field name.
     * @param  string        $operation   Operation
(>|>=|<|<=|=|IN|NOT IN)
     * @param  string|array  $value       Value.
     *
     * @return $this
     */
    public function where($field, $operation, $value)
    {
        $db = $this->db;
        $operation = strtoupper($operation);
        switch ($operation)
        {
            case '>':
            case '>=':
            case '<':
            case '<=':
            case '=':
                // Quote all non integer values.
                $value = (string)(int)$value === (string)$value ?
(int)$value : $db->quote($value);
               
$this->query->where("{$this->db->quoteName($field)}
{$operation} {$value}");
                break;
            case 'BETWEEN':
            case 'NOT BETWEEN':
                list($a, $b) = (array) $value;
                // Quote all non integer values.
                $a = (string)(int)$a === (string)$a ? (int)$a :
$db->quote($a);
                $b = (string)(int)$b === (string)$b ? (int)$b :
$db->quote($b);
               
$this->query->where("{$this->db->quoteName($field)}
{$operation} {$a} AND {$b}");
                break;
            case 'IN':
            case 'NOT IN':
                $value = (array) $value;
                if (empty($value)) {
                    // WHERE field IN (nothing).
                    $this->query->where('0');
                } else {
                    // Quote all non integer values.
                    array_walk($value, function (&$value) use ($db) {
$value = (string)(int)$value === (string)$value ? (int)$value :
$db->quote($value); });
                    $list = implode(',', $value);
                   
$this->query->where("{$this->db->quoteName($field)}
{$operation} ({$list})");
                }
                break;
        }

        return $this;
    }

    /**
     * Get items.
     *
     * Derived classes should generally override this function to return
correct objects.
     *
     * @return array
     */
    public function find()
    {
        if ($this->skip)
        {
            return array();
        }

        $baseQuery = clone $this->query;
        $this->prepare();
        $query = $this->query;
        $this->query = $baseQuery;

        $query->select('a.' . $this->primaryKey);
        $this->db->setQuery($query, $this->start,
$this->limit);
        $results = (array) $this->db->loadColumn();

        return $results;
    }

    /**
     * Count items.
     *
     * @return int
     */
    public function count()
    {
        $baseQuery = clone $this->query;
        $this->prepare();
        $query = $this->query;
        $this->query = $baseQuery;

        $query->select('COUNT(*)');
        $this->db->setQuery($query);
        $count = (int) $this->db->loadResult();

        return $count;
    }

    /**
     * Override to include common where rules.
     *
     * @return void
     */
    protected function prepare()
    {
    }
}
classes/Gantry/Joomla/StyleHelper.php000064400000006676151166614520013665
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla;

use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Joomla style helper.
 */
class StyleHelper
{
    public static function getStyle($id)
    {
        \JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');

        $style = \JTable::getInstance('Style',
'TemplatesTable');
        $style->load($id);

        return $style;
    }

    public static function loadStyles($template)
    {
        $db = \JFactory::getDbo();

        $query = $db
            ->getQuery(true)
            ->select('s.id, s.template, s.home, s.title AS
long_title, s.params')
            ->from('#__template_styles AS s')
            ->where('s.client_id = 0')
            ->where("s.template = {$db->quote($template)}")
            ->order('s.id');

        $db->setQuery($query);

        $list = (array) $db->loadObjectList('id');

        foreach ($list as $id => &$style) {
            $style->title = preg_replace('/' .
preg_quote(\JText::_($style->template), '/') .
'\s*-\s*/u', '', $style->long_title);
            $style->home = $style->home && $style->home
!== '1' ? $style->home : (bool)$style->home;
        }

        return $list;
    }

    public static function getDefaultStyle()
    {
        return static::getStyle(['home' => 1,
'client_id' => 0]);
    }

    public static function copy($style, $old, $new)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $oldPath = $locator->findResource('gantry-config://' .
$old, true, true);
        $newPath = $locator->findResource('gantry-config://' .
$new, true, true);

        if (file_exists($oldPath)) {
            Folder::copy($oldPath, $newPath);
        }

        $extension = !empty($style->extension_id) ?
$style->extension_id : $style->template;

        $installer = new TemplateInstaller($extension);
        $installer->updateStyle($new, ['configuration' =>
$new]);
    }

    public static function update($id, $preset)
    {
        $style = static::getStyle($id);

        $extension = !empty($style->extension_id) ?
$style->extension_id : $style->template;

        $installer = new TemplateInstaller($extension);
        $installer->updateStyle($id, ['configuration' =>
$id, 'preset' => $preset]);
    }

    public static function delete($id)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $path = $locator->findResource('gantry-config://' .
$id, true, true);

        if (is_dir($path)) {
            Folder::delete($path, true);
        }
    }

    /**
     * @return \TemplatesModelStyle
     */
    public static function loadModel()
    {
        static $model;

        if (!$model) {
            $path = JPATH_ADMINISTRATOR .
'/components/com_templates/';

            \JTable::addIncludePath("{$path}/tables");
            require_once "{$path}/models/style.php";

            // Load language strings.
            $lang = \JFactory::getLanguage();
            $lang->load('com_templates');

            $model = new \TemplatesModelStyle;
        }

        return $model;
    }
}
classes/Gantry/Joomla/TemplateInstaller.php000064400000000650151166614520015040
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla;

use Gantry\Framework\ThemeInstaller;

/**
 * Class TemplateInstaller
 * @package Gantry\Joomla
 * @deprecated 5.3.2
 */
class TemplateInstaller extends ThemeInstaller {}
classes/Leafo/ScssPhp/Compiler.php000064400000554424151166614520013122
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

use Leafo\ScssPhp\Base\Range;
use Leafo\ScssPhp\Block;
use Leafo\ScssPhp\Cache;
use Leafo\ScssPhp\Colors;
use Leafo\ScssPhp\Compiler\Environment;
use Leafo\ScssPhp\Exception\CompilerException;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\Node;
use Leafo\ScssPhp\SourceMap\SourceMapGenerator;
use Leafo\ScssPhp\Type;
use Leafo\ScssPhp\Parser;
use Leafo\ScssPhp\Util;

/**
 * The scss compiler and parser.
 *
 * Converting SCSS to CSS is a three stage process. The incoming file is
parsed
 * by `Parser` into a syntax tree, then it is compiled into another tree
 * representing the CSS structure by `Compiler`. The CSS tree is fed into a
 * formatter, like `Formatter` which then outputs CSS as a string.
 *
 * During the first compile, all values are *reduced*, which means that
their
 * types are brought to the lowest form before being dump as strings. This
 * handles math equations, variable dereferences, and the like.
 *
 * The `compile` function of `Compiler` is the entry point.
 *
 * In summary:
 *
 * The `Compiler` class creates an instance of the parser, feeds it SCSS
code,
 * then transforms the resulting tree to a CSS tree. This class also holds
the
 * evaluation context, such as all available mixins and variables at any
given
 * time.
 *
 * The `Parser` class is only concerned with parsing its input.
 *
 * The `Formatter` takes a CSS tree, and dumps it to a formatted string,
 * handling things like indentation.
 */

/**
 * SCSS compiler
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Compiler
{
    const LINE_COMMENTS = 1;
    const DEBUG_INFO    = 2;

    const WITH_RULE     = 1;
    const WITH_MEDIA    = 2;
    const WITH_SUPPORTS = 4;
    const WITH_ALL      = 7;

    const SOURCE_MAP_NONE   = 0;
    const SOURCE_MAP_INLINE = 1;
    const SOURCE_MAP_FILE   = 2;

    /**
     * @var array
     */
    static protected $operatorNames = [
        '+'   => 'add',
        '-'   => 'sub',
        '*'   => 'mul',
        '/'   => 'div',
        '%'   => 'mod',

        '=='  => 'eq',
        '!='  => 'neq',
        '<'   => 'lt',
        '>'   => 'gt',

        '<='  => 'lte',
        '>='  => 'gte',
        '<=>' => 'cmp',
    ];

    /**
     * @var array
     */
    static protected $namespaces = [
        'special'  => '%',
        'mixin'    => '@',
        'function' => '^',
    ];

    static public $true         = [Type::T_KEYWORD, 'true'];
    static public $false        = [Type::T_KEYWORD, 'false'];
    static public $null         = [Type::T_NULL];
    static public $nullString   = [Type::T_STRING, '', []];
    static public $defaultValue = [Type::T_KEYWORD, ''];
    static public $selfSelector = [Type::T_SELF];
    static public $emptyList    = [Type::T_LIST, '', []];
    static public $emptyMap     = [Type::T_MAP, [], []];
    static public $emptyString  = [Type::T_STRING, '"', []];
    static public $with         = [Type::T_KEYWORD, 'with'];
    static public $without      = [Type::T_KEYWORD, 'without'];

    protected $importPaths = [''];
    protected $importCache = [];
    protected $importedFiles = [];
    protected $userFunctions = [];
    protected $registeredVars = [];
    protected $registeredFeatures = [
        'extend-selector-pseudoclass' => false,
        'at-error'                    => true,
        'units-level-3'               => false,
        'global-variable-shadowing'   => false,
    ];

    protected $encoding = null;
    protected $lineNumberStyle = null;

    protected $sourceMap = self::SOURCE_MAP_NONE;
    protected $sourceMapOptions = [];

    /**
     * @var string|\Leafo\ScssPhp\Formatter
     */
    protected $formatter = 'Leafo\ScssPhp\Formatter\Nested';

    protected $rootEnv;
    protected $rootBlock;

    /**
     * @var \Leafo\ScssPhp\Compiler\Environment
     */
    protected $env;
    protected $scope;
    protected $storeEnv;
    protected $charsetSeen;
    protected $sourceNames;

    protected $cache;

    protected $indentLevel;
    protected $extends;
    protected $extendsMap;
    protected $parsedFiles;
    protected $parser;
    protected $sourceIndex;
    protected $sourceLine;
    protected $sourceColumn;
    protected $stderr;
    protected $shouldEvaluate;
    protected $ignoreErrors;

    protected $callStack = [];

    /**
     * Constructor
     */
    public function __construct($cacheOptions = null)
    {
        $this->parsedFiles = [];
        $this->sourceNames = [];

        if ($cacheOptions) {
            $this->cache = new Cache($cacheOptions);
        }
    }

    public function getCompileOptions()
    {
        $options = [
            'importPaths'        => $this->importPaths,
            'registeredVars'     => $this->registeredVars,
            'registeredFeatures' =>
$this->registeredFeatures,
            'encoding'           => $this->encoding,
            'sourceMap'          =>
serialize($this->sourceMap),
            'sourceMapOptions'   =>
$this->sourceMapOptions,
            'formatter'          => $this->formatter,
        ];

        return $options;
    }

    /**
     * Compile scss
     *
     * @api
     *
     * @param string $code
     * @param string $path
     *
     * @return string
     */
    public function compile($code, $path = null)
    {
        if ($this->cache) {
            $cacheKey = ($path ? $path : "(stdin)") .
":" . md5($code);
            $compileOptions = $this->getCompileOptions();
            $cache = $this->cache->getCache("compile",
$cacheKey, $compileOptions);

            if (is_array($cache)
                && isset($cache['dependencies'])
                && isset($cache['out'])
            ) {
                // check if any dependency file changed before accepting
the cache
                foreach ($cache['dependencies'] as $file =>
$mtime) {
                    if (! file_exists($file)
                        || filemtime($file) !== $mtime
                    ) {
                        unset($cache);
                        break;
                    }
                }

                if (isset($cache)) {
                    return $cache['out'];
                }
            }
        }


        $this->indentLevel    = -1;
        $this->extends        = [];
        $this->extendsMap     = [];
        $this->sourceIndex    = null;
        $this->sourceLine     = null;
        $this->sourceColumn   = null;
        $this->env            = null;
        $this->scope          = null;
        $this->storeEnv       = null;
        $this->charsetSeen    = null;
        $this->shouldEvaluate = null;
        $this->stderr         = fopen('php://stderr',
'w');

        $this->parser = $this->parserFactory($path);
        $tree = $this->parser->parse($code);
        $this->parser = null;

        $this->formatter = new $this->formatter();
        $this->rootBlock = null;
        $this->rootEnv   = $this->pushEnv($tree);

        $this->injectVariables($this->registeredVars);
        $this->compileRoot($tree);
        $this->popEnv();

        $sourceMapGenerator = null;

        if ($this->sourceMap) {
            if (is_object($this->sourceMap) &&
$this->sourceMap instanceof SourceMapGenerator) {
                $sourceMapGenerator = $this->sourceMap;
                $this->sourceMap = self::SOURCE_MAP_FILE;
            } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) {
                $sourceMapGenerator = new
SourceMapGenerator($this->sourceMapOptions);
            }
        }

        $out = $this->formatter->format($this->scope,
$sourceMapGenerator);

        if (! empty($out) && $this->sourceMap &&
$this->sourceMap !== self::SOURCE_MAP_NONE) {
            $sourceMap    = $sourceMapGenerator->generateJson();
            $sourceMapUrl = null;

            switch ($this->sourceMap) {
                case self::SOURCE_MAP_INLINE:
                    $sourceMapUrl =
sprintf('data:application/json,%s',
Util::encodeURIComponent($sourceMap));
                    break;

                case self::SOURCE_MAP_FILE:
                    $sourceMapUrl =
$sourceMapGenerator->saveMap($sourceMap);
                    break;
            }

            $out .= sprintf('/*# sourceMappingURL=%s */',
$sourceMapUrl);
        }

        if ($this->cache && isset($cacheKey) &&
isset($compileOptions)) {
            $v = [
                'dependencies' => $this->getParsedFiles(),
                'out' => &$out,
            ];

            $this->cache->setCache("compile", $cacheKey,
$v, $compileOptions);
        }

        return $out;
    }

    /**
     * Instantiate parser
     *
     * @param string $path
     *
     * @return \Leafo\ScssPhp\Parser
     */
    protected function parserFactory($path)
    {
        $parser = new Parser($path, count($this->sourceNames),
$this->encoding, $this->cache);

        $this->sourceNames[] = $path;
        $this->addParsedFile($path);

        return $parser;
    }

    /**
     * Is self extend?
     *
     * @param array $target
     * @param array $origin
     *
     * @return boolean
     */
    protected function isSelfExtend($target, $origin)
    {
        foreach ($origin as $sel) {
            if (in_array($target, $sel)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Push extends
     *
     * @param array     $target
     * @param array     $origin
     * @param \stdClass $block
     */
    protected function pushExtends($target, $origin, $block)
    {
        if ($this->isSelfExtend($target, $origin)) {
            return;
        }

        $i = count($this->extends);
        $this->extends[] = [$target, $origin, $block];

        foreach ($target as $part) {
            if (isset($this->extendsMap[$part])) {
                $this->extendsMap[$part][] = $i;
            } else {
                $this->extendsMap[$part] = [$i];
            }
        }
    }

    /**
     * Make output block
     *
     * @param string $type
     * @param array  $selectors
     *
     * @return \Leafo\ScssPhp\Formatter\OutputBlock
     */
    protected function makeOutputBlock($type, $selectors = null)
    {
        $out = new OutputBlock;
        $out->type         = $type;
        $out->lines        = [];
        $out->children     = [];
        $out->parent       = $this->scope;
        $out->selectors    = $selectors;
        $out->depth        = $this->env->depth;

        if ($this->env->block instanceof Block) {
            $out->sourceName   = $this->env->block->sourceName;
            $out->sourceLine   = $this->env->block->sourceLine;
            $out->sourceColumn =
$this->env->block->sourceColumn;
        } else {
            $out->sourceName   = null;
            $out->sourceLine   = null;
            $out->sourceColumn = null;
        }

        return $out;
    }

    /**
     * Compile root
     *
     * @param \Leafo\ScssPhp\Block $rootBlock
     */
    protected function compileRoot(Block $rootBlock)
    {
        $this->rootBlock = $this->scope =
$this->makeOutputBlock(Type::T_ROOT);

        $this->compileChildrenNoReturn($rootBlock->children,
$this->scope);
        $this->flattenSelectors($this->scope);
        $this->missingSelectors();
    }

    /**
     * Report missing selectors
     */
    protected function missingSelectors()
    {
        foreach ($this->extends as $extend) {
            if (isset($extend[3])) {
                continue;
            }

            list($target, $origin, $block) = $extend;

            // ignore if !optional
            if ($block[2]) {
                continue;
            }

            $target = implode(' ', $target);
            $origin = $this->collapseSelectors($origin);

            $this->sourceLine = $block[Parser::SOURCE_LINE];
            $this->throwError("\"$origin\" failed to
@extend \"$target\". The selector \"$target\" was not
found.");
        }
    }

    /**
     * Flatten selectors
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
     * @param string                               $parentKey
     */
    protected function flattenSelectors(OutputBlock $block, $parentKey =
null)
    {
        if ($block->selectors) {
            $selectors = [];

            foreach ($block->selectors as $s) {
                $selectors[] = $s;

                if (! is_array($s)) {
                    continue;
                }

                // check extends
                if (! empty($this->extendsMap)) {
                    $this->matchExtends($s, $selectors);

                    // remove duplicates
                    array_walk($selectors, function (&$value) {
                        $value = serialize($value);
                    });

                    $selectors = array_unique($selectors);

                    array_walk($selectors, function (&$value) {
                        $value = unserialize($value);
                    });
                }
            }

            $block->selectors = [];
            $placeholderSelector = false;

            foreach ($selectors as $selector) {
                if ($this->hasSelectorPlaceholder($selector)) {
                    $placeholderSelector = true;
                    continue;
                }

                $block->selectors[] =
$this->compileSelector($selector);
            }

            if ($placeholderSelector && 0 ===
count($block->selectors) && null !== $parentKey) {
                unset($block->parent->children[$parentKey]);

                return;
            }
        }

        foreach ($block->children as $key => $child) {
            $this->flattenSelectors($child, $key);
        }
    }

    /**
     * Glue parts of :not( or :nth-child( ... that are in general splitted
in selectors parts
     *
     * @param array $parts
     *
     * @return array
     */
    protected function glueFunctionSelectors($parts)
    {
        $new = [];

        foreach ($parts as $part) {
            if (is_array($part)) {
                $part = $this->glueFunctionSelectors($part);
                $new[] = $part;
            } else {
                // a selector part finishing with a ) is the last part of a
:not( or :nth-child(
                // and need to be joined to this
                if (count($new) && is_string($new[count($new) - 1])
                    && strlen($part) && substr($part, -1)
=== ')' && strpos($part, '(') === false
                ) {
                    $new[count($new) - 1] .= $part;
                } else {
                    $new[] = $part;
                }
            }
        }

        return $new;
    }

    /**
     * Match extends
     *
     * @param array   $selector
     * @param array   $out
     * @param integer $from
     * @param boolean $initial
     */
    protected function matchExtends($selector, &$out, $from = 0,
$initial = true)
    {
        static $partsPile = [];

        $selector = $this->glueFunctionSelectors($selector);

        foreach ($selector as $i => $part) {
            if ($i < $from) {
                continue;
            }

            // check that we are not building an infinite loop of
extensions
            // if the new part is just including a previous part don't
try to extend anymore
            if (count($part) > 1) {
                foreach ($partsPile as $previousPart) {
                    if (! count(array_diff($previousPart, $part))) {
                        continue 2;
                    }
                }
            }

            if ($this->matchExtendsSingle($part, $origin)) {
                $partsPile[] = $part;
                $after       = array_slice($selector, $i + 1);
                $before      = array_slice($selector, 0, $i);

                list($before, $nonBreakableBefore) =
$this->extractRelationshipFromFragment($before);

                foreach ($origin as $new) {
                    $k = 0;

                    // remove shared parts
                    if (count($new) > 1) {
                        while ($k < $i && isset($new[$k])
&& $selector[$k] === $new[$k]) {
                            $k++;
                        }
                    }

                    $replacement = [];
                    $tempReplacement = $k > 0 ? array_slice($new, $k) :
$new;

                    for ($l = count($tempReplacement) - 1; $l >= 0;
$l--) {
                        $slice = [];

                        foreach ($tempReplacement[$l] as $chunk) {
                            if (! in_array($chunk, $slice)) {
                                $slice[] = $chunk;
                            }
                        }

                        array_unshift($replacement, $slice);

                        if (!
$this->isImmediateRelationshipCombinator(end($slice))) {
                            break;
                        }
                    }

                    $afterBefore = $l != 0 ? array_slice($tempReplacement,
0, $l) : [];

                    // Merge shared direct relationships.
                    $mergedBefore =
$this->mergeDirectRelationships($afterBefore, $nonBreakableBefore);

                    $result = array_merge(
                        $before,
                        $mergedBefore,
                        $replacement,
                        $after
                    );

                    if ($result === $selector) {
                        continue;
                    }

                    $out[] = $result;

                    // recursively check for more matches
                    $startRecurseFrom = count($before) +
min(count($nonBreakableBefore), count($mergedBefore));
                    $this->matchExtends($result, $out,
$startRecurseFrom, false);

                    // selector sequence merging
                    if (! empty($before) && count($new) > 1) {
                        $preSharedParts = $k > 0 ? array_slice($before,
0, $k) : [];
                        $postSharedParts = $k > 0 ? array_slice($before,
$k) : $before;

                        list($betweenSharedParts, $nonBreakable2) =
$this->extractRelationshipFromFragment($afterBefore);

                        $result2 = array_merge(
                            $preSharedParts,
                            $betweenSharedParts,
                            $postSharedParts,
                            $nonBreakable2,
                            $nonBreakableBefore,
                            $replacement,
                            $after
                        );

                        $out[] = $result2;
                    }
                }

                array_pop($partsPile);
            }
        }
    }

    /**
     * Match extends single
     *
     * @param array $rawSingle
     * @param array $outOrigin
     *
     * @return boolean
     */
    protected function matchExtendsSingle($rawSingle, &$outOrigin)
    {
        $counts = [];
        $single = [];

        // simple usual cases, no need to do the whole trick
        if (in_array($rawSingle,
[['>'],['+'],['~']])) {
            return false;
        }

        foreach ($rawSingle as $part) {
            // matches Number
            if (! is_string($part)) {
                return false;
            }

            if (! preg_match('/^[\[.:#%]/', $part) &&
count($single)) {
                $single[count($single) - 1] .= $part;
            } else {
                $single[] = $part;
            }
        }

        $extendingDecoratedTag = false;

        if (count($single) > 1) {
            $matches = null;
            $extendingDecoratedTag = preg_match('/^[a-z0-9]+$/i',
$single[0], $matches) ? $matches[0] : false;
        }

        foreach ($single as $part) {
            if (isset($this->extendsMap[$part])) {
                foreach ($this->extendsMap[$part] as $idx) {
                    $counts[$idx] = isset($counts[$idx]) ? $counts[$idx] +
1 : 1;
                }
            }
        }

        $outOrigin = [];
        $found = false;

        foreach ($counts as $idx => $count) {
            list($target, $origin, /* $block */) = $this->extends[$idx];

            $origin = $this->glueFunctionSelectors($origin);

            // check count
            if ($count !== count($target)) {
                continue;
            }

            $this->extends[$idx][3] = true;

            $rem = array_diff($single, $target);

            foreach ($origin as $j => $new) {
                // prevent infinite loop when target extends itself
                if ($this->isSelfExtend($single, $origin)) {
                    return false;
                }

                $replacement = end($new);

                // Extending a decorated tag with another tag is not
possible.
                if ($extendingDecoratedTag && $replacement[0] !=
$extendingDecoratedTag &&
                    preg_match('/^[a-z0-9]+$/i', $replacement[0])
                ) {
                    unset($origin[$j]);
                    continue;
                }

                $combined = $this->combineSelectorSingle($replacement,
$rem);

                if (count(array_diff($combined,
$origin[$j][count($origin[$j]) - 1]))) {
                    $origin[$j][count($origin[$j]) - 1] = $combined;
                }
            }

            $outOrigin = array_merge($outOrigin, $origin);

            $found = true;
        }

        return $found;
    }

    /**
     * Extract a relationship from the fragment.
     *
     * When extracting the last portion of a selector we will be left with
a
     * fragment which may end with a direction relationship combinator.
This
     * method will extract the relationship fragment and return it along
side
     * the rest.
     *
     * @param array $fragment The selector fragment maybe ending with a
direction relationship combinator.
     *
     * @return array The selector without the relationship fragment if any,
the relationship fragment.
     */
    protected function extractRelationshipFromFragment(array $fragment)
    {
        $parents = [];
        $children = [];
        $j = $i = count($fragment);

        for (;;) {
            $children = $j != $i ? array_slice($fragment, $j, $i - $j) :
[];
            $parents = array_slice($fragment, 0, $j);
            $slice = end($parents);

            if (empty($slice) || !
$this->isImmediateRelationshipCombinator($slice[0])) {
                break;
            }

            $j -= 2;
        }

        return [$parents, $children];
    }

    /**
     * Combine selector single
     *
     * @param array $base
     * @param array $other
     *
     * @return array
     */
    protected function combineSelectorSingle($base, $other)
    {
        $tag = [];
        $out = [];
        $wasTag = true;

        foreach ([$base, $other] as $single) {
            foreach ($single as $part) {
                if (preg_match('/^[\[.:#]/', $part)) {
                    $out[] = $part;
                    $wasTag = false;
                } elseif (preg_match('/^[^_-]/', $part)) {
                    $tag[] = $part;
                    $wasTag = true;
                } elseif ($wasTag) {
                    $tag[count($tag) - 1] .= $part;
                } else {
                    $out[count($out) - 1] .= $part;
                }
            }
        }

        if (count($tag)) {
            array_unshift($out, $tag[0]);
        }

        return $out;
    }

    /**
     * Compile media
     *
     * @param \Leafo\ScssPhp\Block $media
     */
    protected function compileMedia(Block $media)
    {
        $this->pushEnv($media);

        $mediaQueries =
$this->compileMediaQuery($this->multiplyMedia($this->env));

        if (! empty($mediaQueries) && $mediaQueries) {
            $previousScope = $this->scope;
            $parentScope = $this->mediaParent($this->scope);

            foreach ($mediaQueries as $mediaQuery) {
                $this->scope = $this->makeOutputBlock(Type::T_MEDIA,
[$mediaQuery]);

                $parentScope->children[] = $this->scope;
                $parentScope = $this->scope;
            }

            // top level properties in a media cause it to be wrapped
            $needsWrap = false;

            foreach ($media->children as $child) {
                $type = $child[0];

                if ($type !== Type::T_BLOCK &&
                    $type !== Type::T_MEDIA &&
                    $type !== Type::T_DIRECTIVE &&
                    $type !== Type::T_IMPORT
                ) {
                    $needsWrap = true;
                    break;
                }
            }

            if ($needsWrap) {
                $wrapped = new Block;
                $wrapped->sourceName = $media->sourceName;
                $wrapped->sourceIndex = $media->sourceIndex;
                $wrapped->sourceLine = $media->sourceLine;
                $wrapped->sourceColumn = $media->sourceColumn;
                $wrapped->selectors = [];
                $wrapped->comments = [];
                $wrapped->parent = $media;
                $wrapped->children = $media->children;

                $media->children = [[Type::T_BLOCK, $wrapped]];
                if (isset($this->lineNumberStyle)) {
                    $annotation =
$this->makeOutputBlock(Type::T_COMMENT);
                    $annotation->depth = 0;

                    $file = $this->sourceNames[$media->sourceIndex];
                    $line = $media->sourceLine;

                    switch ($this->lineNumberStyle) {
                        case static::LINE_COMMENTS:
                            $annotation->lines[] = '/* line '
. $line
                                                 . ($file ? ', '
. $file : '')
                                                 . ' */';
                            break;

                        case static::DEBUG_INFO:
                            $annotation->lines[] = '@media
-sass-debug-info{'
                                                 . ($file ?
'filename{font-family:"' . $file . '"}' :
'')
                                                 .
'line{font-family:' . $line . '}}';
                            break;
                    }

                    $this->scope->children[] = $annotation;
                }
            }

            $this->compileChildrenNoReturn($media->children,
$this->scope);

            $this->scope = $previousScope;
        }

        $this->popEnv();
    }

    /**
     * Media parent
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     *
     * @return \Leafo\ScssPhp\Formatter\OutputBlock
     */
    protected function mediaParent(OutputBlock $scope)
    {
        while (! empty($scope->parent)) {
            if (! empty($scope->type) && $scope->type !==
Type::T_MEDIA) {
                break;
            }

            $scope = $scope->parent;
        }

        return $scope;
    }

    /**
     * Compile directive
     *
     * @param \Leafo\ScssPhp\Block $block
     */
    protected function compileDirective(Block $block)
    {
        $s = '@' . $block->name;

        if (! empty($block->value)) {
            $s .= ' ' . $this->compileValue($block->value);
        }

        if ($block->name === 'keyframes' ||
substr($block->name, -10) === '-keyframes') {
            $this->compileKeyframeBlock($block, [$s]);
        } else {
            $this->compileNestedBlock($block, [$s]);
        }
    }

    /**
     * Compile at-root
     *
     * @param \Leafo\ScssPhp\Block $block
     */
    protected function compileAtRoot(Block $block)
    {
        $env     = $this->pushEnv($block);
        $envs    = $this->compactEnv($env);
        $without = isset($block->with) ?
$this->compileWith($block->with) : static::WITH_RULE;

        // wrap inline selector
        if ($block->selector) {
            $wrapped = new Block;
            $wrapped->sourceName   = $block->sourceName;
            $wrapped->sourceIndex  = $block->sourceIndex;
            $wrapped->sourceLine   = $block->sourceLine;
            $wrapped->sourceColumn = $block->sourceColumn;
            $wrapped->selectors    = $block->selector;
            $wrapped->comments     = [];
            $wrapped->parent       = $block;
            $wrapped->children     = $block->children;
            $wrapped->selfParent   = $block->selfParent;

            $block->children = [[Type::T_BLOCK, $wrapped]];
            $block->selector = null;
        }

        $selfParent = $block->selfParent;

        if (! $block->selfParent->selectors &&
isset($block->parent) && $block->parent &&
            isset($block->parent->selectors) &&
$block->parent->selectors
        ) {
            $selfParent = $block->parent;
        }

        $this->env = $this->filterWithout($envs, $without);

        $saveScope   = $this->scope;
        $this->scope = $this->filterScopeWithout($saveScope,
$without);

        // propagate selfParent to the children where they still can be
useful
        $this->compileChildrenNoReturn($block->children,
$this->scope, $selfParent);

        $this->scope = $this->completeScope($this->scope,
$saveScope);
        $this->scope = $saveScope;
        $this->env   = $this->extractEnv($envs);

        $this->popEnv();
    }

    /**
     * Filter at-root scope depending of with/without option
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     * @param mixed                                $without
     *
     * @return mixed
     */
    protected function filterScopeWithout($scope, $without)
    {
        $filteredScopes = [];

        if ($scope->type === TYPE::T_ROOT) {
            return $scope;
        }

        // start from the root
        while ($scope->parent && $scope->parent->type !==
TYPE::T_ROOT) {
            $scope = $scope->parent;
        }

        for (;;) {
            if (! $scope) {
                break;
            }

            if (! $this->isWithout($without, $scope)) {
                $s = clone $scope;
                $s->children = [];
                $s->lines = [];
                $s->parent = null;

                if ($s->type !== Type::T_MEDIA && $s->type
!== Type::T_DIRECTIVE) {
                    $s->selectors = [];
                }

                $filteredScopes[] = $s;
            }

            if ($scope->children) {
                $scope = end($scope->children);
            } else {
                $scope = null;
            }
        }

        if (! count($filteredScopes)) {
            return $this->rootBlock;
        }

        $newScope = array_shift($filteredScopes);
        $newScope->parent = $this->rootBlock;

        $this->rootBlock->children[] = $newScope;

        $p = &$newScope;

        while (count($filteredScopes)) {
            $s = array_shift($filteredScopes);
            $s->parent = $p;
            $p->children[] = &$s;
            $p = $s;
        }

        return $newScope;
    }

    /**
     * found missing selector from a at-root compilation in the previous
scope
     * (if at-root is just enclosing a property, the selector is in the
parent tree)
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $previousScope
     *
     * @return mixed
     */
    protected function completeScope($scope, $previousScope)
    {
        if (! $scope->type && (! $scope->selectors || !
count($scope->selectors)) && count($scope->lines)) {
            $scope->selectors =
$this->findScopeSelectors($previousScope, $scope->depth);
        }

        if ($scope->children) {
            foreach ($scope->children as $k => $c) {
                $scope->children[$k] = $this->completeScope($c,
$previousScope);
            }
        }

        return $scope;
    }

    /**
     * Find a selector by the depth node in the scope
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     * @param integer                              $depth
     *
     * @return array
     */
    protected function findScopeSelectors($scope, $depth)
    {
        if ($scope->depth === $depth && $scope->selectors) {
            return $scope->selectors;
        }

        if ($scope->children) {
            foreach (array_reverse($scope->children) as $c) {
                if ($s = $this->findScopeSelectors($c, $depth)) {
                    return $s;
                }
            }
        }

        return [];
    }

    /**
     * Compile @at-root's with: inclusion / without: exclusion into
filter flags
     *
     * @param array $with
     *
     * @return integer
     */
    protected function compileWith($with)
    {
        static $mapping = [
            'rule'     => self::WITH_RULE,
            'media'    => self::WITH_MEDIA,
            'supports' => self::WITH_SUPPORTS,
            'all'      => self::WITH_ALL,
        ];

        // exclude selectors by default
        $without = static::WITH_RULE;

        if ($this->libMapHasKey([$with, static::$with])) {
            $without = static::WITH_ALL;

            $list = $this->coerceList($this->libMapGet([$with,
static::$with]));

            foreach ($list[2] as $item) {
                $keyword =
$this->compileStringContent($this->coerceString($item));

                if (array_key_exists($keyword, $mapping)) {
                    $without &= ~($mapping[$keyword]);
                }
            }
        }

        if ($this->libMapHasKey([$with, static::$without])) {
            $without = 0;

            $list = $this->coerceList($this->libMapGet([$with,
static::$without]));

            foreach ($list[2] as $item) {
                $keyword =
$this->compileStringContent($this->coerceString($item));

                if (array_key_exists($keyword, $mapping)) {
                    $without |= $mapping[$keyword];
                }
            }
        }

        return $without;
    }

    /**
     * Filter env stack
     *
     * @param array   $envs
     * @param integer $without
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function filterWithout($envs, $without)
    {
        $filtered = [];

        foreach ($envs as $e) {
            if ($e->block && $this->isWithout($without,
$e->block)) {
                $ec = clone $e;
                $ec->block = null;
                $ec->selectors = [];
                $filtered[] = $ec;
            } else {
                $filtered[] = $e;
            }
        }

        return $this->extractEnv($filtered);
    }

    /**
     * Filter WITH rules
     *
     * @param integer                                                  
$without
     * @param \Leafo\ScssPhp\Block|\Leafo\ScssPhp\Formatter\OutputBlock
$block
     *
     * @return boolean
     */
    protected function isWithout($without, $block)
    {
        if (isset($block->type)) {
            if ($block->type === Type::T_MEDIA) {
                return ($without & static::WITH_MEDIA) ? true : false;
            }

            if ($block->type === Type::T_DIRECTIVE) {
                if (isset($block->name) && $block->name ===
'supports') {
                    return ($without & static::WITH_SUPPORTS) ? true :
false;
                }

                if (isset($block->selectors) &&
strpos(serialize($block->selectors), '@supports') !== false) {
                    return ($without & static::WITH_SUPPORTS) ? true :
false;
                }
            }
        }

        if ((($without & static::WITH_RULE) &&
isset($block->selectors))) {
            return true;
        }

        return false;
    }

    /**
     * Compile keyframe block
     *
     * @param \Leafo\ScssPhp\Block $block
     * @param array                $selectors
     */
    protected function compileKeyframeBlock(Block $block, $selectors)
    {
        $env = $this->pushEnv($block);

        $envs = $this->compactEnv($env);

        $this->env = $this->extractEnv(array_filter($envs, function
(Environment $e) {
            return ! isset($e->block->selectors);
        }));

        $this->scope = $this->makeOutputBlock($block->type,
$selectors);
        $this->scope->depth = 1;
        $this->scope->parent->children[] = $this->scope;

        $this->compileChildrenNoReturn($block->children,
$this->scope);

        $this->scope = $this->scope->parent;
        $this->env   = $this->extractEnv($envs);

        $this->popEnv();
    }

    /**
     * Compile nested block
     *
     * @param \Leafo\ScssPhp\Block $block
     * @param array                $selectors
     */
    protected function compileNestedBlock(Block $block, $selectors)
    {
        $this->pushEnv($block);

        $this->scope = $this->makeOutputBlock($block->type,
$selectors);
        $this->scope->parent->children[] = $this->scope;

        // wrap assign children in a block
        // except for @font-face
        if ($block->type !== Type::T_DIRECTIVE || $block->name !==
"font-face") {
            // need wrapping?
            $needWrapping = false;

            foreach ($block->children as $child) {
                if ($child[0] === Type::T_ASSIGN) {
                    $needWrapping = true;
                    break;
                }
            }

            if ($needWrapping) {
                $wrapped = new Block;
                $wrapped->sourceName = $block->sourceName;
                $wrapped->sourceIndex = $block->sourceIndex;
                $wrapped->sourceLine = $block->sourceLine;
                $wrapped->sourceColumn = $block->sourceColumn;
                $wrapped->selectors = [];
                $wrapped->comments = [];
                $wrapped->parent = $block;
                $wrapped->children = $block->children;
                $wrapped->selfParent = $block->selfParent;

                $block->children = [[Type::T_BLOCK, $wrapped]];
            }
        }

        $this->compileChildrenNoReturn($block->children,
$this->scope);

        $this->scope = $this->scope->parent;

        $this->popEnv();
    }

    /**
     * Recursively compiles a block.
     *
     * A block is analogous to a CSS block in most cases. A single SCSS
document
     * is encapsulated in a block when parsed, but it does not have parent
tags
     * so all of its children appear on the root level when compiled.
     *
     * Blocks are made up of selectors and children.
     *
     * The children of a block are just all the blocks that are defined
within.
     *
     * Compiling the block involves pushing a fresh environment on the
stack,
     * and iterating through the props, compiling each one.
     *
     * @see Compiler::compileChild()
     *
     * @param \Leafo\ScssPhp\Block $block
     */
    protected function compileBlock(Block $block)
    {
        $env = $this->pushEnv($block);
        $env->selectors = $this->evalSelectors($block->selectors);

        $out = $this->makeOutputBlock(null);

        if (isset($this->lineNumberStyle) &&
count($env->selectors) && count($block->children)) {
            $annotation = $this->makeOutputBlock(Type::T_COMMENT);
            $annotation->depth = 0;

            $file = $this->sourceNames[$block->sourceIndex];
            $line = $block->sourceLine;

            switch ($this->lineNumberStyle) {
                case static::LINE_COMMENTS:
                    $annotation->lines[] = '/* line ' . $line
                                         . ($file ? ', ' . $file
: '')
                                         . ' */';
                    break;

                case static::DEBUG_INFO:
                    $annotation->lines[] = '@media
-sass-debug-info{'
                                         . ($file ?
'filename{font-family:"' . $file . '"}' :
'')
                                         . 'line{font-family:' .
$line . '}}';
                    break;
            }

            $this->scope->children[] = $annotation;
        }

        $this->scope->children[] = $out;

        if (count($block->children)) {
            $out->selectors = $this->multiplySelectors($env,
$block->selfParent);

            // propagate selfParent to the children where they still can be
useful
            $selfParentSelectors = null;

            if (isset($block->selfParent->selectors)) {
                $selfParentSelectors = $block->selfParent->selectors;
                $block->selfParent->selectors = $out->selectors;
            }

            $this->compileChildrenNoReturn($block->children, $out,
$block->selfParent);

            // and revert for the following childs of the same block
            if ($selfParentSelectors) {
                $block->selfParent->selectors = $selfParentSelectors;
            }
        }

        $this->formatter->stripSemicolon($out->lines);

        $this->popEnv();
    }

    /**
     * Compile root level comment
     *
     * @param array $block
     */
    protected function compileComment($block)
    {
        $out = $this->makeOutputBlock(Type::T_COMMENT);
        $out->lines[] = is_string($block[1]) ? $block[1] :
$this->compileValue($block[1]);

        $this->scope->children[] = $out;
    }

    /**
     * Evaluate selectors
     *
     * @param array $selectors
     *
     * @return array
     */
    protected function evalSelectors($selectors)
    {
        $this->shouldEvaluate = false;

        $selectors = array_map([$this, 'evalSelector'],
$selectors);

        // after evaluating interpolates, we might need a second pass
        if ($this->shouldEvaluate) {
            $selectors = $this->revertSelfSelector($selectors);
            $buffer = $this->collapseSelectors($selectors);
            $parser = $this->parserFactory(__METHOD__);

            if ($parser->parseSelector($buffer, $newSelectors)) {
                $selectors = array_map([$this, 'evalSelector'],
$newSelectors);
            }
        }

        return $selectors;
    }

    /**
     * Evaluate selector
     *
     * @param array $selector
     *
     * @return array
     */
    protected function evalSelector($selector)
    {
        return array_map([$this, 'evalSelectorPart'], $selector);
    }

    /**
     * Evaluate selector part; replaces all the interpolates, stripping
quotes
     *
     * @param array $part
     *
     * @return array
     */
    protected function evalSelectorPart($part)
    {
        foreach ($part as &$p) {
            if (is_array($p) && ($p[0] === Type::T_INTERPOLATE ||
$p[0] === Type::T_STRING)) {
                $p = $this->compileValue($p);

                // force re-evaluation
                if (strpos($p, '&') !== false || strpos($p,
',') !== false) {
                    $this->shouldEvaluate = true;
                }
            } elseif (is_string($p) && strlen($p) >= 2
&&
                ($first = $p[0]) && ($first === '"'
|| $first === "'") &&
                substr($p, -1) === $first
            ) {
                $p = substr($p, 1, -1);
            }
        }

        return $this->flattenSelectorSingle($part);
    }

    /**
     * Collapse selectors
     *
     * @param array   $selectors
     * @param boolean $selectorFormat
     *   if false return a collapsed string
     *   if true return an array description of a structured selector
     *
     * @return string
     */
    protected function collapseSelectors($selectors, $selectorFormat =
false)
    {
        $parts = [];

        foreach ($selectors as $selector) {
            $output = [];
            $glueNext = false;

            foreach ($selector as $node) {
                $compound = '';

                array_walk_recursive(
                    $node,
                    function ($value, $key) use (&$compound) {
                        $compound .= $value;
                    }
                );

                if ($selectorFormat &&
$this->isImmediateRelationshipCombinator($compound)) {
                    if (count($output)) {
                        $output[count($output) - 1] .= ' ' .
$compound;
                    } else {
                        $output[] = $compound;
                    }
                    $glueNext = true;
                } elseif ($glueNext) {
                    $output[count($output) - 1] .= ' ' .
$compound;
                    $glueNext = false;
                } else {
                    $output[] = $compound;
                }
            }

            if ($selectorFormat) {
                foreach ($output as &$o) {
                    $o = [Type::T_STRING, '', [$o]];
                }
                $output = [Type::T_LIST, ' ', $output];
            } else {
                $output = implode(' ', $output);
            }

            $parts[] = $output;
        }

        if ($selectorFormat) {
            $parts = [Type::T_LIST, ',', $parts];
        } else {
            $parts = implode(', ', $parts);
        }

        return $parts;
    }

    /**
     * Parse down the selector and revert [self] to "&"
before a reparsing
     *
     * @param array $selectors
     *
     * @return array
     */
    protected function revertSelfSelector($selectors)
    {
        foreach ($selectors as &$part) {
            if (is_array($part)) {
                if ($part === [Type::T_SELF]) {
                    $part = '&';
                } else {
                    $part = $this->revertSelfSelector($part);
                }
            }
        }

        return $selectors;
    }

    /**
     * Flatten selector single; joins together .classes and #ids
     *
     * @param array $single
     *
     * @return array
     */
    protected function flattenSelectorSingle($single)
    {
        $joined = [];

        foreach ($single as $part) {
            if (empty($joined) ||
                ! is_string($part) ||
                preg_match('/[\[.:#%]/', $part)
            ) {
                $joined[] = $part;
                continue;
            }

            if (is_array(end($joined))) {
                $joined[] = $part;
            } else {
                $joined[count($joined) - 1] .= $part;
            }
        }

        return $joined;
    }

    /**
     * Compile selector to string; self(&) should have been replaced by
now
     *
     * @param string|array $selector
     *
     * @return string
     */
    protected function compileSelector($selector)
    {
        if (! is_array($selector)) {
            return $selector; // media and the like
        }

        return implode(
            ' ',
            array_map(
                [$this, 'compileSelectorPart'],
                $selector
            )
        );
    }

    /**
     * Compile selector part
     *
     * @param array $piece
     *
     * @return string
     */
    protected function compileSelectorPart($piece)
    {
        foreach ($piece as &$p) {
            if (! is_array($p)) {
                continue;
            }

            switch ($p[0]) {
                case Type::T_SELF:
                    $p = '&';
                    break;

                default:
                    $p = $this->compileValue($p);
                    break;
            }
        }

        return implode($piece);
    }

    /**
     * Has selector placeholder?
     *
     * @param array $selector
     *
     * @return boolean
     */
    protected function hasSelectorPlaceholder($selector)
    {
        if (! is_array($selector)) {
            return false;
        }

        foreach ($selector as $parts) {
            foreach ($parts as $part) {
                if (strlen($part) && '%' === $part[0]) {
                    return true;
                }
            }
        }

        return false;
    }

    protected function pushCallStack($name = '')
    {
        $this->callStack[] = [
          'n' => $name,
          Parser::SOURCE_INDEX => $this->sourceIndex,
          Parser::SOURCE_LINE => $this->sourceLine,
          Parser::SOURCE_COLUMN => $this->sourceColumn
        ];

        // infinite calling loop
        if (count($this->callStack) > 25000) {
            // not displayed but you can var_dump it to deep debug
            $msg = $this->callStackMessage(true, 100);
            $msg = "Infinite calling loop";
            $this->throwError($msg);
        }
    }

    protected function popCallStack()
    {
        array_pop($this->callStack);
    }

    /**
     * Compile children and return result
     *
     * @param array                                $stms
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     * @param string                               $traceName
     *
     * @return array|null
     */
    protected function compileChildren($stms, OutputBlock $out, $traceName
= '')
    {
        $this->pushCallStack($traceName);

        foreach ($stms as $stm) {
            $ret = $this->compileChild($stm, $out);

            if (isset($ret)) {
                return $ret;
            }
        }

        $this->popCallStack();

        return null;
    }

    /**
     * Compile children and throw exception if unexpected @return
     *
     * @param array                                $stms
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     * @param \Leafo\ScssPhp\Block                 $selfParent
     * @param string                               $traceName
     *
     * @throws \Exception
     */
    protected function compileChildrenNoReturn($stms, OutputBlock $out,
$selfParent = null, $traceName = '')
    {
        $this->pushCallStack($traceName);

        foreach ($stms as $stm) {
            if ($selfParent && isset($stm[1]) &&
is_object($stm[1]) && $stm[1] instanceof Block) {
                $stm[1]->selfParent = $selfParent;
                $ret = $this->compileChild($stm, $out);
                $stm[1]->selfParent = null;
            } elseif ($selfParent && $stm[0] === TYPE::T_INCLUDE) {
                $stm['selfParent'] = $selfParent;
                $ret = $this->compileChild($stm, $out);
                unset($stm['selfParent']);
            } else {
                $ret = $this->compileChild($stm, $out);
            }

            if (isset($ret)) {
                $this->throwError('@return may only be used within
a function');

                return;
            }
        }

        $this->popCallStack();
    }


    /**
     * evaluate media query : compile internal value keeping the structure
inchanged
     *
     * @param array $queryList
     *
     * @return array
     */
    protected function evaluateMediaQuery($queryList)
    {
        foreach ($queryList as $kql => $query) {
            foreach ($query as $kq => $q) {
                for ($i = 1; $i < count($q); $i++) {
                    $value = $this->compileValue($q[$i]);

                    // the parser had no mean to know if media type or
expression if it was an interpolation
                    if ($q[0] == Type::T_MEDIA_TYPE &&
                        (strpos($value, '(') !== false ||
                        strpos($value, ')') !== false ||
                        strpos($value, ':') !== false)
                    ) {
                        $queryList[$kql][$kq][0] =
Type::T_MEDIA_EXPRESSION;

                        if (strpos($value, 'and') !== false) {
                            $values = explode('and', $value);
                            $value = trim(array_pop($values));

                            while ($v = trim(array_pop($values))) {
                                $type = Type::T_MEDIA_EXPRESSION;

                                if (strpos($v, '(') === false
&&
                                    strpos($v, ')') === false
&&
                                    strpos($v, ':') === false
                                ) {
                                    $type = Type::T_MEDIA_TYPE;
                                }

                                if (substr($v, 0, 1) === '('
&& substr($v, -1) === ')') {
                                    $v = substr($v, 1, -1);
                                }

                                $queryList[$kql][] =
[$type,[Type::T_KEYWORD, $v]];
                            }
                        }

                        if (substr($value, 0, 1) === '('
&& substr($value, -1) === ')') {
                            $value = substr($value, 1, -1);
                        }
                    }

                    $queryList[$kql][$kq][$i] = [Type::T_KEYWORD, $value];
                }
            }
        }

        return $queryList;
    }

    /**
     * Compile media query
     *
     * @param array $queryList
     *
     * @return array
     */
    protected function compileMediaQuery($queryList)
    {
        $start = '@media ';
        $default = trim($start);
        $out = [];
        $current = "";

        foreach ($queryList as $query) {
            $type = null;
            $parts = [];

            $mediaTypeOnly = true;

            foreach ($query as $q) {
                if ($q[0] !== Type::T_MEDIA_TYPE) {
                    $mediaTypeOnly = false;
                    break;
                }
            }

            foreach ($query as $q) {
                switch ($q[0]) {
                    case Type::T_MEDIA_TYPE:
                        $newType = array_map([$this,
'compileValue'], array_slice($q, 1));
                        // combining not and anything else than media type
is too risky and should be avoided
                        if (! $mediaTypeOnly) {
                            if (in_array(Type::T_NOT, $newType) || ($type
&& in_array(Type::T_NOT, $type) )) {
                                if ($type) {
                                    array_unshift($parts, implode('
', array_filter($type)));
                                }

                                if (! empty($parts)) {
                                    if (strlen($current)) {
                                        $current .=
$this->formatter->tagSeparator;
                                    }

                                    $current .= implode(' and ',
$parts);
                                }

                                if ($current) {
                                    $out[] = $start . $current;
                                }

                                $current = "";
                                $type = null;
                                $parts = [];
                            }
                        }

                        if ($newType === ['all'] &&
$default) {
                            $default = $start . 'all';
                        }

                        // all can be safely ignored and mixed with
whatever else
                        if ($newType !== ['all']) {
                            if ($type) {
                                $type = $this->mergeMediaTypes($type,
$newType);

                                if (empty($type)) {
                                    // merge failed : ignore this query
that is not valid, skip to the next one
                                    $parts = [];
                                    $default = ''; // if
everything fail, no @media at all
                                    continue 3;
                                }
                            } else {
                                $type = $newType;
                            }
                        }
                        break;

                    case Type::T_MEDIA_EXPRESSION:
                        if (isset($q[2])) {
                            $parts[] = '('
                                . $this->compileValue($q[1])
                                . $this->formatter->assignSeparator
                                . $this->compileValue($q[2])
                                . ')';
                        } else {
                            $parts[] = '('
                                . $this->compileValue($q[1])
                                . ')';
                        }
                        break;

                    case Type::T_MEDIA_VALUE:
                        $parts[] = $this->compileValue($q[1]);
                        break;
                }
            }

            if ($type) {
                array_unshift($parts, implode(' ',
array_filter($type)));
            }

            if (! empty($parts)) {
                if (strlen($current)) {
                    $current .= $this->formatter->tagSeparator;
                }

                $current .= implode(' and ', $parts);
            }
        }

        if ($current) {
            $out[] = $start . $current;
        }

        // no @media type except all, and no conflict?
        if (! $out && $default) {
            $out[] = $default;
        }

        return $out;
    }

    /**
     * Merge direct relationships between selectors
     *
     * @param array $selectors1
     * @param array $selectors2
     *
     * @return array
     */
    protected function mergeDirectRelationships($selectors1, $selectors2)
    {
        if (empty($selectors1) || empty($selectors2)) {
            return array_merge($selectors1, $selectors2);
        }

        $part1 = end($selectors1);
        $part2 = end($selectors2);

        if (! $this->isImmediateRelationshipCombinator($part1[0])
&& $part1 !== $part2) {
            return array_merge($selectors1, $selectors2);
        }

        $merged = [];

        do {
            $part1 = array_pop($selectors1);
            $part2 = array_pop($selectors2);

            if (! $this->isImmediateRelationshipCombinator($part1[0])
&& $part1 !== $part2) {
                if
($this->isImmediateRelationshipCombinator(reset($merged)[0])) {
                    array_unshift($merged, [$part1[0] . $part2[0]]);
                    $merged = array_merge($selectors1, $selectors2,
$merged);
                } else {
                    $merged = array_merge($selectors1, [$part1],
$selectors2, [$part2], $merged);
                }

                break;
            }

            array_unshift($merged, $part1);
        } while (! empty($selectors1) && ! empty($selectors2));

        return $merged;
    }

    /**
     * Merge media types
     *
     * @param array $type1
     * @param array $type2
     *
     * @return array|null
     */
    protected function mergeMediaTypes($type1, $type2)
    {
        if (empty($type1)) {
            return $type2;
        }

        if (empty($type2)) {
            return $type1;
        }

        $m1 = '';
        $t1 = '';

        if (count($type1) > 1) {
            $m1= strtolower($type1[0]);
            $t1= strtolower($type1[1]);
        } else {
            $t1 = strtolower($type1[0]);
        }

        $m2 = '';
        $t2 = '';

        if (count($type2) > 1) {
            $m2 = strtolower($type2[0]);
            $t2 = strtolower($type2[1]);
        } else {
            $t2 = strtolower($type2[0]);
        }

        if (($m1 === Type::T_NOT) ^ ($m2 === Type::T_NOT)) {
            if ($t1 === $t2) {
                return null;
            }

            return [
                $m1 === Type::T_NOT ? $m2 : $m1,
                $m1 === Type::T_NOT ? $t2 : $t1,
            ];
        }

        if ($m1 === Type::T_NOT && $m2 === Type::T_NOT) {
            // CSS has no way of representing "neither screen nor
print"
            if ($t1 !== $t2) {
                return null;
            }

            return [Type::T_NOT, $t1];
        }

        if ($t1 !== $t2) {
            return null;
        }

        // t1 == t2, neither m1 nor m2 are "not"
        return [empty($m1)? $m2 : $m1, $t1];
    }

    /**
     * Compile import; returns true if the value was something that could
be imported
     *
     * @param array                                $rawPath
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     * @param boolean                              $once
     *
     * @return boolean
     */
    protected function compileImport($rawPath, OutputBlock $out, $once =
false)
    {
        if ($rawPath[0] === Type::T_STRING) {
            $path = $this->compileStringContent($rawPath);

            if ($path = $this->findImport($path)) {
                if (! $once || ! in_array($path, $this->importedFiles))
{
                    $this->importFile($path, $out);
                    $this->importedFiles[] = $path;
                }

                return true;
            }

            return false;
        }

        if ($rawPath[0] === Type::T_LIST) {
            // handle a list of strings
            if (count($rawPath[2]) === 0) {
                return false;
            }

            foreach ($rawPath[2] as $path) {
                if ($path[0] !== Type::T_STRING) {
                    return false;
                }
            }

            foreach ($rawPath[2] as $path) {
                $this->compileImport($path, $out);
            }

            return true;
        }

        return false;
    }

    /**
     * Compile child; returns a value to halt execution
     *
     * @param array                                $child
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     *
     * @return array
     */
    protected function compileChild($child, OutputBlock $out)
    {
        if (isset($child[Parser::SOURCE_LINE])) {
            $this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ?
$child[Parser::SOURCE_INDEX] : null;
            $this->sourceLine = isset($child[Parser::SOURCE_LINE]) ?
$child[Parser::SOURCE_LINE] : -1;
            $this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ?
$child[Parser::SOURCE_COLUMN] : -1;
        } elseif (is_array($child) &&
isset($child[1]->sourceLine)) {
            $this->sourceIndex = $child[1]->sourceIndex;
            $this->sourceLine = $child[1]->sourceLine;
            $this->sourceColumn = $child[1]->sourceColumn;
        } elseif (! empty($out->sourceLine) && !
empty($out->sourceName)) {
            $this->sourceLine = $out->sourceLine;
            $this->sourceIndex = array_search($out->sourceName,
$this->sourceNames);

            if ($this->sourceIndex === false) {
                $this->sourceIndex = null;
            }
        }

        switch ($child[0]) {
            case Type::T_SCSSPHP_IMPORT_ONCE:
                $rawPath = $this->reduce($child[1]);

                if (! $this->compileImport($rawPath, $out, true)) {
                    $out->lines[] = '@import ' .
$this->compileValue($rawPath) . ';';
                }
                break;

            case Type::T_IMPORT:
                $rawPath = $this->reduce($child[1]);

                if (! $this->compileImport($rawPath, $out)) {
                    $out->lines[] = '@import ' .
$this->compileValue($rawPath) . ';';
                }
                break;

            case Type::T_DIRECTIVE:
                $this->compileDirective($child[1]);
                break;

            case Type::T_AT_ROOT:
                $this->compileAtRoot($child[1]);
                break;

            case Type::T_MEDIA:
                $this->compileMedia($child[1]);
                break;

            case Type::T_BLOCK:
                $this->compileBlock($child[1]);
                break;

            case Type::T_CHARSET:
                if (! $this->charsetSeen) {
                    $this->charsetSeen = true;

                    $out->lines[] = '@charset ' .
$this->compileValue($child[1]) . ';';
                }
                break;

            case Type::T_ASSIGN:
                list(, $name, $value) = $child;

                if ($name[0] === Type::T_VARIABLE) {
                    $flags = isset($child[3]) ? $child[3] : [];
                    $isDefault = in_array('!default', $flags);
                    $isGlobal = in_array('!global', $flags);

                    if ($isGlobal) {
                        $this->set($name[1], $this->reduce($value),
false, $this->rootEnv, $value);
                        break;
                    }

                    $shouldSet = $isDefault &&
                        (($result = $this->get($name[1], false)) ===
null
                        || $result === static::$null);

                    if (! $isDefault || $shouldSet) {
                        $this->set($name[1], $this->reduce($value),
true, null, $value);
                    }
                    break;
                }

                $compiledName = $this->compileValue($name);

                // handle shorthand syntax: size / line-height
                if ($compiledName === 'font' || $compiledName ===
'grid-row' || $compiledName === 'grid-column') {
                    if ($value[0] === Type::T_VARIABLE) {
                        // if the font value comes from variable, the
content is already reduced
                        // (i.e., formulas were already calculated), so we
need the original unreduced value
                        $value = $this->get($value[1], true, null,
true);
                    }

                    $fontValue=&$value;

                    if ($value[0] === Type::T_LIST &&
$value[1]==',') {
                        // this is the case if more than one font is given:
example: "font: 400 1em/1.3 arial,helvetica"
                        // we need to handle the first list element
                        $fontValue=&$value[2][0];
                    }

                    if ($fontValue[0] === Type::T_EXPRESSION &&
$fontValue[1] === '/') {
                        $fontValue = $this->expToString($fontValue);
                    } elseif ($fontValue[0] === Type::T_LIST) {
                        foreach ($fontValue[2] as &$item) {
                            if ($item[0] === Type::T_EXPRESSION &&
$item[1] === '/') {
                                $item = $this->expToString($item);
                            }
                        }
                    }
                }

                // if the value reduces to null from something else then
                // the property should be discarded
                if ($value[0] !== Type::T_NULL) {
                    $value = $this->reduce($value);

                    if ($value[0] === Type::T_NULL || $value ===
static::$nullString) {
                        break;
                    }
                }

                $compiledValue = $this->compileValue($value);

                $out->lines[] = $this->formatter->property(
                    $compiledName,
                    $compiledValue
                );
                break;

            case Type::T_COMMENT:
                if ($out->type === Type::T_ROOT) {
                    $this->compileComment($child);
                    break;
                }

                $out->lines[] = $child[1];
                break;

            case Type::T_MIXIN:
            case Type::T_FUNCTION:
                list(, $block) = $child;

                $this->set(static::$namespaces[$block->type] .
$block->name, $block);
                break;

            case Type::T_EXTEND:
                foreach ($child[1] as $sel) {
                    $results = $this->evalSelectors([$sel]);

                    foreach ($results as $result) {
                        // only use the first one
                        $result = current($result);

                        $this->pushExtends($result, $out->selectors,
$child);
                    }
                }
                break;

            case Type::T_IF:
                list(, $if) = $child;

                if ($this->isTruthy($this->reduce($if->cond,
true))) {
                    return $this->compileChildren($if->children,
$out);
                }

                foreach ($if->cases as $case) {
                    if ($case->type === Type::T_ELSE ||
                        $case->type === Type::T_ELSEIF &&
$this->isTruthy($this->reduce($case->cond))
                    ) {
                        return
$this->compileChildren($case->children, $out);
                    }
                }
                break;

            case Type::T_EACH:
                list(, $each) = $child;

                $list =
$this->coerceList($this->reduce($each->list));

                $this->pushEnv();

                foreach ($list[2] as $item) {
                    if (count($each->vars) === 1) {
                        $this->set($each->vars[0], $item, true);
                    } else {
                        list(,, $values) = $this->coerceList($item);

                        foreach ($each->vars as $i => $var) {
                            $this->set($var, isset($values[$i]) ?
$values[$i] : static::$null, true);
                        }
                    }

                    $ret = $this->compileChildren($each->children,
$out);

                    if ($ret) {
                        if ($ret[0] !== Type::T_CONTROL) {
                            $this->popEnv();

                            return $ret;
                        }

                        if ($ret[1]) {
                            break;
                        }
                    }
                }

                $this->popEnv();
                break;

            case Type::T_WHILE:
                list(, $while) = $child;

                while ($this->isTruthy($this->reduce($while->cond,
true))) {
                    $ret = $this->compileChildren($while->children,
$out);

                    if ($ret) {
                        if ($ret[0] !== Type::T_CONTROL) {
                            return $ret;
                        }

                        if ($ret[1]) {
                            break;
                        }
                    }
                }
                break;

            case Type::T_FOR:
                list(, $for) = $child;

                $start = $this->reduce($for->start, true);
                $end   = $this->reduce($for->end, true);

                if (! ($start[2] == $end[2] || $end->unitless())) {
                    $this->throwError('Incompatible units:
"%s" and "%s".', $start->unitStr(),
$end->unitStr());

                    break;
                }

                $unit  = $start[2];
                $start = $start[1];
                $end   = $end[1];

                $d = $start < $end ? 1 : -1;

                for (;;) {
                    if ((! $for->until && $start - $d == $end)
||
                        ($for->until && $start == $end)
                    ) {
                        break;
                    }

                    $this->set($for->var, new Node\Number($start,
$unit));
                    $start += $d;

                    $ret = $this->compileChildren($for->children,
$out);

                    if ($ret) {
                        if ($ret[0] !== Type::T_CONTROL) {
                            return $ret;
                        }

                        if ($ret[1]) {
                            break;
                        }
                    }
                }
                break;

            case Type::T_BREAK:
                return [Type::T_CONTROL, true];

            case Type::T_CONTINUE:
                return [Type::T_CONTROL, false];

            case Type::T_RETURN:
                return $this->reduce($child[1], true);

            case Type::T_NESTED_PROPERTY:
                list(, $prop) = $child;

                $prefixed = [];
                $prefix = $this->compileValue($prop->prefix) .
'-';

                foreach ($prop->children as $child) {
                    switch ($child[0]) {
                        case Type::T_ASSIGN:
                            array_unshift($child[1][2], $prefix);
                            break;

                        case Type::T_NESTED_PROPERTY:
                            array_unshift($child[1]->prefix[2],
$prefix);
                            break;
                    }

                    $prefixed[] = $child;
                }

                $this->compileChildrenNoReturn($prefixed, $out);
                break;

            case Type::T_INCLUDE:
                // including a mixin
                list(, $name, $argValues, $content) = $child;

                $mixin =
$this->get(static::$namespaces['mixin'] . $name, false);

                if (! $mixin) {
                    $this->throwError("Undefined mixin
$name");
                    break;
                }

                $callingScope = $this->getStoreEnv();

                // push scope, apply args
                $this->pushEnv();
                $this->env->depth--;

                $storeEnv = $this->storeEnv;
                $this->storeEnv = $this->env;

                // Find the parent selectors in the env to be able to know
what '&' refers to in the mixin
                // and assign this fake parent to childs
                $selfParent = null;

                if (isset($child['selfParent']) &&
isset($child['selfParent']->selectors)) {
                    $selfParent = $child['selfParent'];
                } else {
                    $parentSelectors =
$this->multiplySelectors($this->env);

                    if ($parentSelectors) {
                        $parent = new Block();
                        $parent->selectors = $parentSelectors;

                        foreach ($mixin->children as $k => $child) {
                            if (isset($child[1]) &&
is_object($child[1]) && $child[1] instanceof Block) {
                                $mixin->children[$k][1]->parent =
$parent;
                            }
                        }
                    }
                }

                // clone the stored content to not have its scope spoiled
by a further call to the same mixin
                // i.e., recursive @include of the same mixin
                if (isset($content)) {
                    $copyContent = clone $content;
                    $copyContent->scope = $callingScope;

                   
$this->setRaw(static::$namespaces['special'] .
'content', $copyContent, $this->env);
                }

                if (isset($mixin->args)) {
                    $this->applyArguments($mixin->args, $argValues);
                }

                $this->env->marker = 'mixin';

                $this->compileChildrenNoReturn($mixin->children,
$out, $selfParent, $this->env->marker . " " . $name);

                $this->storeEnv = $storeEnv;

                $this->popEnv();
                break;

            case Type::T_MIXIN_CONTENT:
                $env = isset($this->storeEnv) ? $this->storeEnv :
$this->env;
                $content =
$this->get(static::$namespaces['special'] .
'content', false, $env);

                if (! $content) {
                    $content = new \stdClass();
                    $content->scope = new \stdClass();
                    $content->children =
$this->storeEnv->parent->block->children;
                    break;
                }

                $storeEnv = $this->storeEnv;
                $this->storeEnv = $content->scope;
                $this->compileChildrenNoReturn($content->children,
$out);

                $this->storeEnv = $storeEnv;
                break;

            case Type::T_DEBUG:
                list(, $value) = $child;

                $fname = $this->sourceNames[$this->sourceIndex];
                $line = $this->sourceLine;
                $value = $this->compileValue($this->reduce($value,
true));
                fwrite($this->stderr, "File $fname on line $line
DEBUG: $value\n");
                break;

            case Type::T_WARN:
                list(, $value) = $child;

                $fname = $this->sourceNames[$this->sourceIndex];
                $line = $this->sourceLine;
                $value = $this->compileValue($this->reduce($value,
true));
                fwrite($this->stderr, "File $fname on line $line
WARN: $value\n");
                break;

            case Type::T_ERROR:
                list(, $value) = $child;

                $fname = $this->sourceNames[$this->sourceIndex];
                $line = $this->sourceLine;
                $value = $this->compileValue($this->reduce($value,
true));
                $this->throwError("File $fname on line $line ERROR:
$value\n");
                break;

            case Type::T_CONTROL:
                $this->throwError('@break/@continue not permitted
in this scope');
                break;

            default:
                $this->throwError("unknown child type:
$child[0]");
        }
    }

    /**
     * Reduce expression to string
     *
     * @param array $exp
     *
     * @return array
     */
    protected function expToString($exp)
    {
        list(, $op, $left, $right, /* $inParens */, $whiteLeft,
$whiteRight) = $exp;

        $content = [$this->reduce($left)];

        if ($whiteLeft) {
            $content[] = ' ';
        }

        $content[] = $op;

        if ($whiteRight) {
            $content[] = ' ';
        }

        $content[] = $this->reduce($right);

        return [Type::T_STRING, '', $content];
    }

    /**
     * Is truthy?
     *
     * @param array $value
     *
     * @return boolean
     */
    protected function isTruthy($value)
    {
        return $value !== static::$false && $value !==
static::$null;
    }

    /**
     * Is the value a direct relationship combinator?
     *
     * @param string $value
     *
     * @return boolean
     */
    protected function isImmediateRelationshipCombinator($value)
    {
        return $value === '>' || $value === '+' ||
$value === '~';
    }

    /**
     * Should $value cause its operand to eval
     *
     * @param array $value
     *
     * @return boolean
     */
    protected function shouldEval($value)
    {
        switch ($value[0]) {
            case Type::T_EXPRESSION:
                if ($value[1] === '/') {
                    return $this->shouldEval($value[2]) ||
$this->shouldEval($value[3]);
                }

                // fall-thru
            case Type::T_VARIABLE:
            case Type::T_FUNCTION_CALL:
                return true;
        }

        return false;
    }

    /**
     * Reduce value
     *
     * @param array   $value
     * @param boolean $inExp
     *
     * @return array|\Leafo\ScssPhp\Node\Number
     */
    protected function reduce($value, $inExp = false)
    {

        switch ($value[0]) {
            case Type::T_EXPRESSION:
                list(, $op, $left, $right, $inParens) = $value;

                $opName = isset(static::$operatorNames[$op]) ?
static::$operatorNames[$op] : $op;
                $inExp = $inExp || $this->shouldEval($left) ||
$this->shouldEval($right);

                $left = $this->reduce($left, true);

                if ($op !== 'and' && $op !==
'or') {
                    $right = $this->reduce($right, true);
                }

                // special case: looks like css shorthand
                if ($opName == 'div' && ! $inParens
&& ! $inExp && isset($right[2])
                    && (($right[0] !== Type::T_NUMBER &&
$right[2] != '')
                    || ($right[0] === Type::T_NUMBER && !
$right->unitless()))
                ) {
                    return $this->expToString($value);
                }

                $left = $this->coerceForExpression($left);
                $right = $this->coerceForExpression($right);

                $ltype = $left[0];
                $rtype = $right[0];

                $ucOpName = ucfirst($opName);
                $ucLType  = ucfirst($ltype);
                $ucRType  = ucfirst($rtype);

                // this tries:
                // 1. op[op name][left type][right type]
                // 2. op[left type][right type] (passing the op as first
arg
                // 3. op[op name]
                $fn = "op${ucOpName}${ucLType}${ucRType}";

                if (is_callable([$this, $fn]) ||
                    (($fn = "op${ucLType}${ucRType}") &&
                        is_callable([$this, $fn]) &&
                        $passOp = true) ||
                    (($fn = "op${ucOpName}") &&
                        is_callable([$this, $fn]) &&
                        $genOp = true)
                ) {
                    $coerceUnit = false;

                    if (! isset($genOp) &&
                        $left[0] === Type::T_NUMBER && $right[0]
=== Type::T_NUMBER
                    ) {
                        $coerceUnit = true;

                        switch ($opName) {
                            case 'mul':
                                $targetUnit = $left[2];

                                foreach ($right[2] as $unit => $exp) {
                                    $targetUnit[$unit] =
(isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) + $exp;
                                }
                                break;

                            case 'div':
                                $targetUnit = $left[2];

                                foreach ($right[2] as $unit => $exp) {
                                    $targetUnit[$unit] =
(isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) - $exp;
                                }
                                break;

                            case 'mod':
                                $targetUnit = $left[2];
                                break;

                            default:
                                $targetUnit = $left->unitless() ?
$right[2] : $left[2];
                        }

                        if (! $left->unitless() && !
$right->unitless()) {
                            $left = $left->normalize();
                            $right = $right->normalize();
                        }
                    }

                    $shouldEval = $inParens || $inExp;

                    if (isset($passOp)) {
                        $out = $this->$fn($op, $left, $right,
$shouldEval);
                    } else {
                        $out = $this->$fn($left, $right, $shouldEval);
                    }

                    if (isset($out)) {
                        if ($coerceUnit && $out[0] ===
Type::T_NUMBER) {
                            $out = $out->coerce($targetUnit);
                        }

                        return $out;
                    }
                }

                return $this->expToString($value);

            case Type::T_UNARY:
                list(, $op, $exp, $inParens) = $value;

                $inExp = $inExp || $this->shouldEval($exp);
                $exp = $this->reduce($exp);

                if ($exp[0] === Type::T_NUMBER) {
                    switch ($op) {
                        case '+':
                            return new Node\Number($exp[1], $exp[2]);

                        case '-':
                            return new Node\Number(-$exp[1], $exp[2]);
                    }
                }

                if ($op === 'not') {
                    if ($inExp || $inParens) {
                        if ($exp === static::$false || $exp ===
static::$null) {
                            return static::$true;
                        }

                        return static::$false;
                    }

                    $op = $op . ' ';
                }

                return [Type::T_STRING, '', [$op, $exp]];

            case Type::T_VARIABLE:
                return $this->reduce($this->get($value[1]));

            case Type::T_LIST:
                foreach ($value[2] as &$item) {
                    $item = $this->reduce($item);
                }

                return $value;

            case Type::T_MAP:
                foreach ($value[1] as &$item) {
                    $item = $this->reduce($item);
                }

                foreach ($value[2] as &$item) {
                    $item = $this->reduce($item);
                }

                return $value;

            case Type::T_STRING:
                foreach ($value[2] as &$item) {
                    if (is_array($item) || $item instanceof \ArrayAccess) {
                        $item = $this->reduce($item);
                    }
                }

                return $value;

            case Type::T_INTERPOLATE:
                $value[1] = $this->reduce($value[1]);
                if ($inExp) {
                    return $value[1];
                }

                return $value;

            case Type::T_FUNCTION_CALL:
                return $this->fncall($value[1], $value[2]);

            case Type::T_SELF:
                $selfSelector = $this->multiplySelectors($this->env);
                $selfSelector = $this->collapseSelectors($selfSelector,
true);
                return $selfSelector;

            default:
                return $value;
        }
    }

    /**
     * Function caller
     *
     * @param string $name
     * @param array  $argValues
     *
     * @return array|null
     */
    protected function fncall($name, $argValues)
    {
        // SCSS @function
        if ($this->callScssFunction($name, $argValues, $returnValue)) {
            return $returnValue;
        }

        // native PHP functions
        if ($this->callNativeFunction($name, $argValues, $returnValue))
{
            return $returnValue;
        }

        // for CSS functions, simply flatten the arguments into a list
        $listArgs = [];

        foreach ((array) $argValues as $arg) {
            if (empty($arg[0])) {
                $listArgs[] = $this->reduce($arg[1]);
            }
        }

        return [Type::T_FUNCTION, $name, [Type::T_LIST, ',',
$listArgs]];
    }

    /**
     * Normalize name
     *
     * @param string $name
     *
     * @return string
     */
    protected function normalizeName($name)
    {
        return str_replace('-', '_', $name);
    }

    /**
     * Normalize value
     *
     * @param array $value
     *
     * @return array
     */
    public function normalizeValue($value)
    {
        $value = $this->coerceForExpression($this->reduce($value));

        switch ($value[0]) {
            case Type::T_LIST:
                $value = $this->extractInterpolation($value);

                if ($value[0] !== Type::T_LIST) {
                    return [Type::T_KEYWORD,
$this->compileValue($value)];
                }

                foreach ($value[2] as $key => $item) {
                    $value[2][$key] = $this->normalizeValue($item);
                }

                return $value;

            case Type::T_STRING:
                return [$value[0], '"',
[$this->compileStringContent($value)]];

            case Type::T_NUMBER:
                return $value->normalize();

            case Type::T_INTERPOLATE:
                return [Type::T_KEYWORD, $this->compileValue($value)];

            default:
                return $value;
        }
    }

    /**
     * Add numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opAddNumberNumber($left, $right)
    {
        return new Node\Number($left[1] + $right[1], $left[2]);
    }

    /**
     * Multiply numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opMulNumberNumber($left, $right)
    {
        return new Node\Number($left[1] * $right[1], $left[2]);
    }

    /**
     * Subtract numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opSubNumberNumber($left, $right)
    {
        return new Node\Number($left[1] - $right[1], $left[2]);
    }

    /**
     * Divide numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return array|\Leafo\ScssPhp\Node\Number
     */
    protected function opDivNumberNumber($left, $right)
    {
        if ($right[1] == 0) {
            return [Type::T_STRING, '', [$left[1] . $left[2] .
'/' . $right[1] . $right[2]]];
        }

        return new Node\Number($left[1] / $right[1], $left[2]);
    }

    /**
     * Mod numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opModNumberNumber($left, $right)
    {
        return new Node\Number($left[1] % $right[1], $left[2]);
    }

    /**
     * Add strings
     *
     * @param array $left
     * @param array $right
     *
     * @return array|null
     */
    protected function opAdd($left, $right)
    {
        if ($strLeft = $this->coerceString($left)) {
            if ($right[0] === Type::T_STRING) {
                $right[1] = '';
            }

            $strLeft[2][] = $right;

            return $strLeft;
        }

        if ($strRight = $this->coerceString($right)) {
            if ($left[0] === Type::T_STRING) {
                $left[1] = '';
            }

            array_unshift($strRight[2], $left);

            return $strRight;
        }

        return null;
    }

    /**
     * Boolean and
     *
     * @param array   $left
     * @param array   $right
     * @param boolean $shouldEval
     *
     * @return array|null
     */
    protected function opAnd($left, $right, $shouldEval)
    {
        $truthy = ($left === static::$null || $right === static::$null) ||
                  ($left === static::$false || $left === static::$true)
&&
                  ($right === static::$false || $right === static::$true);

        if (! $shouldEval) {
            if (! $truthy) {
                return null;
            }
        }

        if ($left !== static::$false && $left !== static::$null) {
            return $this->reduce($right, true);
        }

        return $left;
    }

    /**
     * Boolean or
     *
     * @param array   $left
     * @param array   $right
     * @param boolean $shouldEval
     *
     * @return array|null
     */
    protected function opOr($left, $right, $shouldEval)
    {
        $truthy = ($left === static::$null || $right === static::$null) ||
                  ($left === static::$false || $left === static::$true)
&&
                  ($right === static::$false || $right === static::$true);

        if (! $shouldEval) {
            if (! $truthy) {
                return null;
            }
        }

        if ($left !== static::$false && $left !== static::$null) {
            return $left;
        }

        return $this->reduce($right, true);
    }

    /**
     * Compare colors
     *
     * @param string $op
     * @param array  $left
     * @param array  $right
     *
     * @return array
     */
    protected function opColorColor($op, $left, $right)
    {
        $out = [Type::T_COLOR];

        foreach ([1, 2, 3] as $i) {
            $lval = isset($left[$i]) ? $left[$i] : 0;
            $rval = isset($right[$i]) ? $right[$i] : 0;

            switch ($op) {
                case '+':
                    $out[] = $lval + $rval;
                    break;

                case '-':
                    $out[] = $lval - $rval;
                    break;

                case '*':
                    $out[] = $lval * $rval;
                    break;

                case '%':
                    $out[] = $lval % $rval;
                    break;

                case '/':
                    if ($rval == 0) {
                        $this->throwError("color: Can't divide
by zero");
                        break 2;
                    }

                    $out[] = (int) ($lval / $rval);
                    break;

                case '==':
                    return $this->opEq($left, $right);

                case '!=':
                    return $this->opNeq($left, $right);

                default:
                    $this->throwError("color: unknown op
$op");
                    break 2;
            }
        }

        if (isset($left[4])) {
            $out[4] = $left[4];
        } elseif (isset($right[4])) {
            $out[4] = $right[4];
        }

        return $this->fixColor($out);
    }

    /**
     * Compare color and number
     *
     * @param string $op
     * @param array  $left
     * @param array  $right
     *
     * @return array
     */
    protected function opColorNumber($op, $left, $right)
    {
        $value = $right[1];

        return $this->opColorColor(
            $op,
            $left,
            [Type::T_COLOR, $value, $value, $value]
        );
    }

    /**
     * Compare number and color
     *
     * @param string $op
     * @param array  $left
     * @param array  $right
     *
     * @return array
     */
    protected function opNumberColor($op, $left, $right)
    {
        $value = $left[1];

        return $this->opColorColor(
            $op,
            [Type::T_COLOR, $value, $value, $value],
            $right
        );
    }

    /**
     * Compare number1 == number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opEq($left, $right)
    {
        if (($lStr = $this->coerceString($left)) && ($rStr =
$this->coerceString($right))) {
            $lStr[1] = '';
            $rStr[1] = '';

            $left = $this->compileValue($lStr);
            $right = $this->compileValue($rStr);
        }

        return $this->toBool($left === $right);
    }

    /**
     * Compare number1 != number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opNeq($left, $right)
    {
        if (($lStr = $this->coerceString($left)) && ($rStr =
$this->coerceString($right))) {
            $lStr[1] = '';
            $rStr[1] = '';

            $left = $this->compileValue($lStr);
            $right = $this->compileValue($rStr);
        }

        return $this->toBool($left !== $right);
    }

    /**
     * Compare number1 >= number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opGteNumberNumber($left, $right)
    {
        return $this->toBool($left[1] >= $right[1]);
    }

    /**
     * Compare number1 > number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opGtNumberNumber($left, $right)
    {
        return $this->toBool($left[1] > $right[1]);
    }

    /**
     * Compare number1 <= number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opLteNumberNumber($left, $right)
    {
        return $this->toBool($left[1] <= $right[1]);
    }

    /**
     * Compare number1 < number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opLtNumberNumber($left, $right)
    {
        return $this->toBool($left[1] < $right[1]);
    }

    /**
     * Three-way comparison, aka spaceship operator
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opCmpNumberNumber($left, $right)
    {
        $n = $left[1] - $right[1];

        return new Node\Number($n ? $n / abs($n) : 0, '');
    }

    /**
     * Cast to boolean
     *
     * @api
     *
     * @param mixed $thing
     *
     * @return array
     */
    public function toBool($thing)
    {
        return $thing ? static::$true : static::$false;
    }

    /**
     * Compiles a primitive value into a CSS property value.
     *
     * Values in scssphp are typed by being wrapped in arrays, their format
is
     * typically:
     *
     *     array(type, contents [, additional_contents]*)
     *
     * The input is expected to be reduced. This function will not work on
     * things like expressions and variables.
     *
     * @api
     *
     * @param array $value
     *
     * @return string
     */
    public function compileValue($value)
    {
        $value = $this->reduce($value);

        switch ($value[0]) {
            case Type::T_KEYWORD:
                return $value[1];

            case Type::T_COLOR:
                // [1] - red component (either number for a %)
                // [2] - green component
                // [3] - blue component
                // [4] - optional alpha component
                list(, $r, $g, $b) = $value;

                $r = round($r);
                $g = round($g);
                $b = round($b);

                if (count($value) === 5 && $value[4] !== 1) { //
rgba
                    $a = new Node\Number($value[4], '');

                    return 'rgba(' . $r . ', ' . $g .
', ' . $b . ', ' . $a . ')';
                }

                $h = sprintf('#%02x%02x%02x', $r, $g, $b);

                // Converting hex color to short notation (e.g. #003399 to
#039)
                if ($h[1] === $h[2] && $h[3] === $h[4] &&
$h[5] === $h[6]) {
                    $h = '#' . $h[1] . $h[3] . $h[5];
                }

                return $h;

            case Type::T_NUMBER:
                return $value->output($this);

            case Type::T_STRING:
                return $value[1] . $this->compileStringContent($value) .
$value[1];

            case Type::T_FUNCTION:
                $args = ! empty($value[2]) ?
$this->compileValue($value[2]) : '';

                return "$value[1]($args)";

            case Type::T_LIST:
                $value = $this->extractInterpolation($value);

                if ($value[0] !== Type::T_LIST) {
                    return $this->compileValue($value);
                }

                list(, $delim, $items) = $value;

                if ($delim !== ' ') {
                    $delim .= ' ';
                }

                $filtered = [];

                foreach ($items as $item) {
                    if ($item[0] === Type::T_NULL) {
                        continue;
                    }

                    $filtered[] = $this->compileValue($item);
                }

                return implode("$delim", $filtered);

            case Type::T_MAP:
                $keys = $value[1];
                $values = $value[2];
                $filtered = [];

                for ($i = 0, $s = count($keys); $i < $s; $i++) {
                    $filtered[$this->compileValue($keys[$i])] =
$this->compileValue($values[$i]);
                }

                array_walk($filtered, function (&$value, $key) {
                    $value = $key . ': ' . $value;
                });

                return '(' . implode(', ', $filtered) .
')';

            case Type::T_INTERPOLATED:
                // node created by extractInterpolation
                list(, $interpolate, $left, $right) = $value;
                list(,, $whiteLeft, $whiteRight) = $interpolate;

                $left = count($left[2]) > 0 ?
                    $this->compileValue($left) . $whiteLeft :
'';

                $right = count($right[2]) > 0 ?
                    $whiteRight . $this->compileValue($right) :
'';

                return $left . $this->compileValue($interpolate) .
$right;

            case Type::T_INTERPOLATE:
                // strip quotes if it's a string
                $reduced = $this->reduce($value[1]);

                switch ($reduced[0]) {
                    case Type::T_LIST:
                        $reduced =
$this->extractInterpolation($reduced);

                        if ($reduced[0] !== Type::T_LIST) {
                            break;
                        }

                        list(, $delim, $items) = $reduced;

                        if ($delim !== ' ') {
                            $delim .= ' ';
                        }

                        $filtered = [];

                        foreach ($items as $item) {
                            if ($item[0] === Type::T_NULL) {
                                continue;
                            }

                            $temp =
$this->compileValue([Type::T_KEYWORD, $item]);
                            if ($temp[0] === Type::T_STRING) {
                                $filtered[] =
$this->compileStringContent($temp);
                            } elseif ($temp[0] === Type::T_KEYWORD) {
                                $filtered[] = $temp[1];
                            } else {
                                $filtered[] =
$this->compileValue($temp);
                            }
                        }

                        $reduced = [Type::T_KEYWORD,
implode("$delim", $filtered)];
                        break;

                    case Type::T_STRING:
                        $reduced = [Type::T_KEYWORD,
$this->compileStringContent($reduced)];
                        break;

                    case Type::T_NULL:
                        $reduced = [Type::T_KEYWORD, ''];
                }

                return $this->compileValue($reduced);

            case Type::T_NULL:
                return 'null';

            default:
                $this->throwError("unknown value type:
$value[0]");
        }
    }

    /**
     * Flatten list
     *
     * @param array $list
     *
     * @return string
     */
    protected function flattenList($list)
    {
        return $this->compileValue($list);
    }

    /**
     * Compile string content
     *
     * @param array $string
     *
     * @return string
     */
    protected function compileStringContent($string)
    {
        $parts = [];

        foreach ($string[2] as $part) {
            if (is_array($part) || $part instanceof \ArrayAccess) {
                $parts[] = $this->compileValue($part);
            } else {
                $parts[] = $part;
            }
        }

        return implode($parts);
    }

    /**
     * Extract interpolation; it doesn't need to be recursive,
compileValue will handle that
     *
     * @param array $list
     *
     * @return array
     */
    protected function extractInterpolation($list)
    {
        $items = $list[2];

        foreach ($items as $i => $item) {
            if ($item[0] === Type::T_INTERPOLATE) {
                $before = [Type::T_LIST, $list[1], array_slice($items, 0,
$i)];
                $after  = [Type::T_LIST, $list[1], array_slice($items, $i +
1)];

                return [Type::T_INTERPOLATED, $item, $before, $after];
            }
        }

        return $list;
    }

    /**
     * Find the final set of selectors
     *
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param \Leafo\ScssPhp\Block                $selfParent
     *
     * @return array
     */
    protected function multiplySelectors(Environment $env, $selfParent =
null)
    {
        $envs            = $this->compactEnv($env);
        $selectors       = [];
        $parentSelectors = [[]];

        $selfParentSelectors = null;

        if (! is_null($selfParent) && $selfParent->selectors) {
            $selfParentSelectors =
$this->evalSelectors($selfParent->selectors);
        }

        while ($env = array_pop($envs)) {
            if (empty($env->selectors)) {
                continue;
            }

            $selectors = $env->selectors;

            do {
                $stillHasSelf = false;
                $prevSelectors = $selectors;
                $selectors = [];

                foreach ($prevSelectors as $selector) {
                    foreach ($parentSelectors as $parent) {
                        if ($selfParentSelectors) {
                            foreach ($selfParentSelectors as $selfParent) {
                                // if no '&' in the selector,
each call will give same result, only add once
                                $s = $this->joinSelectors($parent,
$selector, $stillHasSelf, $selfParent);
                                $selectors[serialize($s)] = $s;
                            }
                        } else {
                            $s = $this->joinSelectors($parent,
$selector, $stillHasSelf);
                            $selectors[serialize($s)] = $s;
                        }
                    }
                }
            } while ($stillHasSelf);

            $parentSelectors = $selectors;
        }

        $selectors = array_values($selectors);

        return $selectors;
    }

    /**
     * Join selectors; looks for & to replace, or append parent before
child
     *
     * @param array   $parent
     * @param array   $child
     * @param boolean &$stillHasSelf
     * @param array   $selfParentSelectors

     * @return array
     */
    protected function joinSelectors($parent, $child, &$stillHasSelf,
$selfParentSelectors = null)
    {
        $setSelf = false;
        $out = [];

        foreach ($child as $part) {
            $newPart = [];

            foreach ($part as $p) {
                // only replace & once and should be recalled to be
able to make combinations
                if ($p === static::$selfSelector && $setSelf) {
                    $stillHasSelf = true;
                }

                if ($p === static::$selfSelector && ! $setSelf) {
                    $setSelf = true;

                    if (is_null($selfParentSelectors)) {
                        $selfParentSelectors = $parent;
                    }

                    foreach ($selfParentSelectors as $i => $parentPart)
{
                        if ($i > 0) {
                            $out[] = $newPart;
                            $newPart = [];
                        }

                        foreach ($parentPart as $pp) {
                            if (is_array($pp)) {
                                $flatten = [];
                                array_walk_recursive($pp, function ($a) use
(&$flatten) {
                                    $flatten[] = $a;
                                });
                                $pp = implode($flatten);
                            }

                            $newPart[] = $pp;
                        }
                    }
                } else {
                    $newPart[] = $p;
                }
            }

            $out[] = $newPart;
        }

        return $setSelf ? $out : array_merge($parent, $child);
    }

    /**
     * Multiply media
     *
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param array                               $childQueries
     *
     * @return array
     */
    protected function multiplyMedia(Environment $env = null, $childQueries
= null)
    {
        if (! isset($env) ||
            ! empty($env->block->type) &&
$env->block->type !== Type::T_MEDIA
        ) {
            return $childQueries;
        }

        // plain old block, skip
        if (empty($env->block->type)) {
            return $this->multiplyMedia($env->parent, $childQueries);
        }

        $parentQueries = isset($env->block->queryList)
            ? $env->block->queryList
            : [[[Type::T_MEDIA_VALUE, $env->block->value]]];

        $store = [$this->env, $this->storeEnv];
        $this->env = $env;
        $this->storeEnv = null;
        $parentQueries = $this->evaluateMediaQuery($parentQueries);
        list($this->env, $this->storeEnv) = $store;

        if ($childQueries === null) {
            $childQueries = $parentQueries;
        } else {
            $originalQueries = $childQueries;
            $childQueries = [];

            foreach ($parentQueries as $parentQuery) {
                foreach ($originalQueries as $childQuery) {
                    $childQueries[] = array_merge(
                        $parentQuery,
                        [[Type::T_MEDIA_TYPE, [Type::T_KEYWORD,
'all']]],
                        $childQuery
                    );
                }
            }
        }

        return $this->multiplyMedia($env->parent, $childQueries);
    }

    /**
     * Convert env linked list to stack
     *
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     *
     * @return array
     */
    protected function compactEnv(Environment $env)
    {
        for ($envs = []; $env; $env = $env->parent) {
            $envs[] = $env;
        }

        return $envs;
    }

    /**
     * Convert env stack to singly linked list
     *
     * @param array $envs
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function extractEnv($envs)
    {
        for ($env = null; $e = array_pop($envs);) {
            $e->parent = $env;
            $env = $e;
        }

        return $env;
    }

    /**
     * Push environment
     *
     * @param \Leafo\ScssPhp\Block $block
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function pushEnv(Block $block = null)
    {
        $env = new Environment;
        $env->parent = $this->env;
        $env->store  = [];
        $env->block  = $block;
        $env->depth  = isset($this->env->depth) ?
$this->env->depth + 1 : 0;

        $this->env = $env;

        return $env;
    }

    /**
     * Pop environment
     */
    protected function popEnv()
    {
        $this->env = $this->env->parent;
    }

    /**
     * Get store environment
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function getStoreEnv()
    {
        return isset($this->storeEnv) ? $this->storeEnv :
$this->env;
    }

    /**
     * Set variable
     *
     * @param string                              $name
     * @param mixed                               $value
     * @param boolean                             $shadow
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param mixed                               $valueUnreduced
     */
    protected function set($name, $value, $shadow = false, Environment $env
= null, $valueUnreduced = null)
    {
        $name = $this->normalizeName($name);

        if (! isset($env)) {
            $env = $this->getStoreEnv();
        }

        if ($shadow) {
            $this->setRaw($name, $value, $env, $valueUnreduced);
        } else {
            $this->setExisting($name, $value, $env, $valueUnreduced);
        }
    }

    /**
     * Set existing variable
     *
     * @param string                              $name
     * @param mixed                               $value
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param mixed                               $valueUnreduced
     */
    protected function setExisting($name, $value, Environment $env,
$valueUnreduced = null)
    {
        $storeEnv = $env;

        $hasNamespace = $name[0] === '^' || $name[0] ===
'@' || $name[0] === '%';

        for (;;) {
            if (array_key_exists($name, $env->store)) {
                break;
            }

            if (! $hasNamespace && isset($env->marker)) {
                $env = $storeEnv;
                break;
            }

            if (! isset($env->parent)) {
                $env = $storeEnv;
                break;
            }

            $env = $env->parent;
        }

        $env->store[$name] = $value;

        if ($valueUnreduced) {
            $env->storeUnreduced[$name] = $valueUnreduced;
        }
    }

    /**
     * Set raw variable
     *
     * @param string                              $name
     * @param mixed                               $value
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param mixed                               $valueUnreduced
     */
    protected function setRaw($name, $value, Environment $env,
$valueUnreduced = null)
    {
        $env->store[$name] = $value;

        if ($valueUnreduced) {
            $env->storeUnreduced[$name] = $valueUnreduced;
        }
    }

    /**
     * Get variable
     *
     * @api
     *
     * @param string                              $name
     * @param boolean                             $shouldThrow
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param boolean                             $unreduced
     *
     * @return mixed|null
     */
    public function get($name, $shouldThrow = true, Environment $env =
null, $unreduced = false)
    {
        $normalizedName = $this->normalizeName($name);
        $specialContentKey = static::$namespaces['special'] .
'content';

        if (! isset($env)) {
            $env = $this->getStoreEnv();
        }

        $nextIsRoot = false;
        $hasNamespace = $normalizedName[0] === '^' ||
$normalizedName[0] === '@' || $normalizedName[0] ===
'%';

        $maxDepth = 10000;

        for (;;) {
            if ($maxDepth-- <= 0) {
                break;
            }

            if (array_key_exists($normalizedName, $env->store)) {
                if ($unreduced &&
isset($env->storeUnreduced[$normalizedName])) {
                    return $env->storeUnreduced[$normalizedName];
                }

                return $env->store[$normalizedName];
            }

            if (! $hasNamespace && isset($env->marker)) {
                if (! $nextIsRoot && !
empty($env->store[$specialContentKey])) {
                    $env = $env->store[$specialContentKey]->scope;
                    continue;
                }

                $env = $this->rootEnv;
                continue;
            }

            if (! isset($env->parent)) {
                break;
            }

            $env = $env->parent;
        }

        if ($shouldThrow) {
            $this->throwError("Undefined variable \$$name" .
($maxDepth<=0 ? " (infinite recursion)" : ""));
        }

        // found nothing
        return null;
    }

    /**
     * Has variable?
     *
     * @param string                              $name
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     *
     * @return boolean
     */
    protected function has($name, Environment $env = null)
    {
        return $this->get($name, false, $env) !== null;
    }

    /**
     * Inject variables
     *
     * @param array $args
     */
    protected function injectVariables(array $args)
    {
        if (empty($args)) {
            return;
        }

        $parser = $this->parserFactory(__METHOD__);

        foreach ($args as $name => $strValue) {
            if ($name[0] === '$') {
                $name = substr($name, 1);
            }

            if (! $parser->parseValue($strValue, $value)) {
                $value = $this->coerceValue($strValue);
            }

            $this->set($name, $value);
        }
    }

    /**
     * Set variables
     *
     * @api
     *
     * @param array $variables
     */
    public function setVariables(array $variables)
    {
        $this->registeredVars = array_merge($this->registeredVars,
$variables);
    }

    /**
     * Unset variable
     *
     * @api
     *
     * @param string $name
     */
    public function unsetVariable($name)
    {
        unset($this->registeredVars[$name]);
    }

    /**
     * Returns list of variables
     *
     * @api
     *
     * @return array
     */
    public function getVariables()
    {
        return $this->registeredVars;
    }

    /**
     * Adds to list of parsed files
     *
     * @api
     *
     * @param string $path
     */
    public function addParsedFile($path)
    {
        if (isset($path) && file_exists($path)) {
            $this->parsedFiles[realpath($path)] = filemtime($path);
        }
    }

    /**
     * Returns list of parsed files
     *
     * @api
     *
     * @return array
     */
    public function getParsedFiles()
    {
        return $this->parsedFiles;
    }

    /**
     * Add import path
     *
     * @api
     *
     * @param string|callable $path
     */
    public function addImportPath($path)
    {
        if (! in_array($path, $this->importPaths)) {
            $this->importPaths[] = $path;
        }
    }

    /**
     * Set import paths
     *
     * @api
     *
     * @param string|array $path
     */
    public function setImportPaths($path)
    {
        $this->importPaths = (array) $path;
    }

    /**
     * Set number precision
     *
     * @api
     *
     * @param integer $numberPrecision
     */
    public function setNumberPrecision($numberPrecision)
    {
        Node\Number::$precision = $numberPrecision;
    }

    /**
     * Set formatter
     *
     * @api
     *
     * @param string $formatterName
     */
    public function setFormatter($formatterName)
    {
        $this->formatter = $formatterName;
    }

    /**
     * Set line number style
     *
     * @api
     *
     * @param string $lineNumberStyle
     */
    public function setLineNumberStyle($lineNumberStyle)
    {
        $this->lineNumberStyle = $lineNumberStyle;
    }

    /**
     * Enable/disable source maps
     *
     * @api
     *
     * @param integer $sourceMap
     */
    public function setSourceMap($sourceMap)
    {
        $this->sourceMap = $sourceMap;
    }

    /**
     * Set source map options
     *
     * @api
     *
     * @param array $sourceMapOptions
     */
    public function setSourceMapOptions($sourceMapOptions)
    {
        $this->sourceMapOptions = $sourceMapOptions;
    }

    /**
     * Register function
     *
     * @api
     *
     * @param string   $name
     * @param callable $func
     * @param array    $prototype
     */
    public function registerFunction($name, $func, $prototype = null)
    {
        $this->userFunctions[$this->normalizeName($name)] = [$func,
$prototype];
    }

    /**
     * Unregister function
     *
     * @api
     *
     * @param string $name
     */
    public function unregisterFunction($name)
    {
        unset($this->userFunctions[$this->normalizeName($name)]);
    }

    /**
     * Add feature
     *
     * @api
     *
     * @param string $name
     */
    public function addFeature($name)
    {
        $this->registeredFeatures[$name] = true;
    }

    /**
     * Import file
     *
     * @param string                               $path
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     */
    protected function importFile($path, OutputBlock $out)
    {
        // see if tree is cached
        $realPath = realpath($path);

        if (isset($this->importCache[$realPath])) {
            $this->handleImportLoop($realPath);

            $tree = $this->importCache[$realPath];
        } else {
            $code   = file_get_contents($path);
            $parser = $this->parserFactory($path);
            $tree   = $parser->parse($code);

            $this->importCache[$realPath] = $tree;
        }

        $pi = pathinfo($path);
        array_unshift($this->importPaths, $pi['dirname']);
        $this->compileChildrenNoReturn($tree->children, $out);
        array_shift($this->importPaths);
    }

    /**
     * Return the file path for an import url if it exists
     *
     * @api
     *
     * @param string $url
     *
     * @return string|null
     */
    public function findImport($url)
    {
        $urls = [];

        // for "normal" scss imports (ignore vanilla css and
external requests)
        if (! preg_match('/\.css$|^https?:\/\//', $url)) {
            // try both normal and the _partial filename
            $urls = [$url, preg_replace('/[^\/]+$/',
'_\0', $url)];
        }

        $hasExtension = preg_match('/[.]s?css$/', $url);

        foreach ($this->importPaths as $dir) {
            if (is_string($dir)) {
                // check urls for normal import paths
                foreach ($urls as $full) {
                    $separator = (
                        ! empty($dir) &&
                        substr($dir, -1) !== '/' &&
                        substr($full, 0, 1) !== '/'
                    ) ? '/' : '';
                    $full = $dir . $separator . $full;

                    if ($this->fileExists($file = $full .
'.scss') ||
                        ($hasExtension &&
$this->fileExists($file = $full))
                    ) {
                        return $file;
                    }
                }
            } elseif (is_callable($dir)) {
                // check custom callback for import path
                $file = call_user_func($dir, $url);

                if ($file !== null) {
                    return $file;
                }
            }
        }

        return null;
    }

    /**
     * Set encoding
     *
     * @api
     *
     * @param string $encoding
     */
    public function setEncoding($encoding)
    {
        $this->encoding = $encoding;
    }

    /**
     * Ignore errors?
     *
     * @api
     *
     * @param boolean $ignoreErrors
     *
     * @return \Leafo\ScssPhp\Compiler
     */
    public function setIgnoreErrors($ignoreErrors)
    {
        $this->ignoreErrors = $ignoreErrors;

        return $this;
    }

    /**
     * Throw error (exception)
     *
     * @api
     *
     * @param string $msg Message with optional sprintf()-style vararg
parameters
     *
     * @throws \Leafo\ScssPhp\Exception\CompilerException
     */
    public function throwError($msg)
    {
        if ($this->ignoreErrors) {
            return;
        }

        $line   = $this->sourceLine;
        $column = $this->sourceColumn;

        $loc = isset($this->sourceNames[$this->sourceIndex])
             ? $this->sourceNames[$this->sourceIndex] . " on
line $line, at column $column"
             : "line: $line, column: $column";

        if (func_num_args() > 1) {
            $msg = call_user_func_array('sprintf',
func_get_args());
        }

        $msg = "$msg: $loc";

        $callStackMsg = $this->callStackMessage();

        if ($callStackMsg) {
            $msg .= "\nCall Stack:\n" . $callStackMsg;
        }

        throw new CompilerException($msg);
    }

    /**
     * Beautify call stack for output
     *
     * @param boolean $all
     * @param null    $limit
     *
     * @return string
     */
    protected function callStackMessage($all = false, $limit = null)
    {
        $callStackMsg = [];
        $ncall = 0;

        if ($this->callStack) {
            foreach (array_reverse($this->callStack) as $call) {
                if ($all || (isset($call['n']) &&
$call['n'])) {
                    $msg = "#" . $ncall++ . " " .
$call['n'] . " ";
                    $msg .=
(isset($this->sourceNames[$call[Parser::SOURCE_INDEX]])
                          ?
$this->sourceNames[$call[Parser::SOURCE_INDEX]]
                          : '(unknown file)');
                    $msg .= " on line " .
$call[Parser::SOURCE_LINE];
                    $callStackMsg[] = $msg;

                    if (! is_null($limit) && $ncall>$limit) {
                        break;
                    }
                }
            }
        }

        return implode("\n", $callStackMsg);
    }

    /**
     * Handle import loop
     *
     * @param string $name
     *
     * @throws \Exception
     */
    protected function handleImportLoop($name)
    {
        for ($env = $this->env; $env; $env = $env->parent) {
            $file = $this->sourceNames[$env->block->sourceIndex];

            if (realpath($file) === $name) {
                $this->throwError('An @import loop has been found:
%s imports %s', $file, basename($file));
                break;
            }
        }
    }

    /**
     * Does file exist?
     *
     * @param string $name
     *
     * @return boolean
     */
    protected function fileExists($name)
    {
        return file_exists($name) && is_file($name);
    }

    /**
     * Call SCSS @function
     *
     * @param string $name
     * @param array  $argValues
     * @param array  $returnValue
     *
     * @return boolean Returns true if returnValue is set; otherwise, false
     */
    protected function callScssFunction($name, $argValues,
&$returnValue)
    {
        $func = $this->get(static::$namespaces['function'] .
$name, false);

        if (! $func) {
            return false;
        }

        $this->pushEnv();

        $storeEnv = $this->storeEnv;
        $this->storeEnv = $this->env;

        // set the args
        if (isset($func->args)) {
            $this->applyArguments($func->args, $argValues);
        }

        // throw away lines and children
        $tmp = new OutputBlock;
        $tmp->lines    = [];
        $tmp->children = [];

        $this->env->marker = 'function';

        $ret = $this->compileChildren($func->children, $tmp,
$this->env->marker . " " . $name);

        $this->storeEnv = $storeEnv;

        $this->popEnv();

        $returnValue = ! isset($ret) ? static::$defaultValue : $ret;

        return true;
    }

    /**
     * Call built-in and registered (PHP) functions
     *
     * @param string $name
     * @param array  $args
     * @param array  $returnValue
     *
     * @return boolean Returns true if returnValue is set; otherwise, false
     */
    protected function callNativeFunction($name, $args, &$returnValue)
    {
        // try a lib function
        $name = $this->normalizeName($name);

        if (isset($this->userFunctions[$name])) {
            // see if we can find a user function
            list($f, $prototype) = $this->userFunctions[$name];
        } elseif (($f = $this->getBuiltinFunction($name)) &&
is_callable($f)) {
            $libName   = $f[1];
            $prototype = isset(static::$$libName) ? static::$$libName :
null;
        } else {
            return false;
        }

        @list($sorted, $kwargs) = $this->sortArgs($prototype, $args);

        if ($name !== 'if' && $name !== 'call')
{
            foreach ($sorted as &$val) {
                // @todo fix root cause for this php 7.4 hack
                if ($val === null)  continue;

                $val = $this->reduce($val, true);
            }
        }

        $returnValue = call_user_func($f, $sorted, $kwargs);

        if (! isset($returnValue)) {
            return false;
        }

        $returnValue = $this->coerceValue($returnValue);

        return true;
    }

    /**
     * Get built-in function
     *
     * @param string $name Normalized name
     *
     * @return array
     */
    protected function getBuiltinFunction($name)
    {
        $libName = 'lib' . preg_replace_callback(
            '/_(.)/',
            function ($m) {
                return ucfirst($m[1]);
            },
            ucfirst($name)
        );

        return [$this, $libName];
    }

    /**
     * Sorts keyword arguments
     *
     * @param array $prototype
     * @param array $args
     *
     * @return array
     */
    protected function sortArgs($prototype, $args)
    {
        $keyArgs = [];
        $posArgs = [];

        // separate positional and keyword arguments
        foreach ($args as $arg) {
            list($key, $value) = $arg;

            $key = $key[1];

            if (empty($key)) {
                $posArgs[] = empty($arg[2]) ? $value : $arg;
            } else {
                $keyArgs[$key] = $value;
            }
        }

        if (! isset($prototype)) {
            return [$posArgs, $keyArgs];
        }

        // copy positional args
        $finalArgs = array_pad($posArgs, count($prototype), null);

        // overwrite positional args with keyword args
        foreach ($prototype as $i => $names) {
            foreach ((array) $names as $name) {
                if (isset($keyArgs[$name])) {
                    $finalArgs[$i] = $keyArgs[$name];
                }
            }
        }

        return [$finalArgs, $keyArgs];
    }

    /**
     * Apply argument values per definition
     *
     * @param array $argDef
     * @param array $argValues
     *
     * @throws \Exception
     */
    protected function applyArguments($argDef, $argValues)
    {
        $storeEnv = $this->getStoreEnv();

        $env = new Environment;
        $env->store = $storeEnv->store;

        $hasVariable = false;
        $args = [];

        foreach ($argDef as $i => $arg) {
            list($name, $default, $isVariable) = $argDef[$i];

            $args[$name] = [$i, $name, $default, $isVariable];
            $hasVariable |= $isVariable;
        }

        $keywordArgs = [];
        $deferredKeywordArgs = [];
        $remaining = [];

        // assign the keyword args
        foreach ((array) $argValues as $arg) {
            if (! empty($arg[0])) {
                if (! isset($args[$arg[0][1]])) {
                    if ($hasVariable) {
                        $deferredKeywordArgs[$arg[0][1]] = $arg[1];
                    } else {
                        $this->throwError("Mixin or function
doesn't have an argument named $%s.", $arg[0][1]);
                        break;
                    }
                } elseif ($args[$arg[0][1]][0] < count($remaining)) {
                    $this->throwError("The argument $%s was passed
both by position and by name.", $arg[0][1]);
                    break;
                } else {
                    $keywordArgs[$arg[0][1]] = $arg[1];
                }
            } elseif (count($keywordArgs)) {
                $this->throwError('Positional arguments must come
before keyword arguments.');
                break;
            } elseif ($arg[2] === true) {
                $val = $this->reduce($arg[1], true);

                if ($val[0] === Type::T_LIST) {
                    foreach ($val[2] as $name => $item) {
                        if (! is_numeric($name)) {
                            $keywordArgs[$name] = $item;
                        } else {
                            $remaining[] = $item;
                        }
                    }
                } elseif ($val[0] === Type::T_MAP) {
                    foreach ($val[1] as $i => $name) {
                        $name =
$this->compileStringContent($this->coerceString($name));
                        $item = $val[2][$i];

                        if (! is_numeric($name)) {
                            $keywordArgs[$name] = $item;
                        } else {
                            $remaining[] = $item;
                        }
                    }
                } else {
                    $remaining[] = $val;
                }
            } else {
                $remaining[] = $arg[1];
            }
        }

        foreach ($args as $arg) {
            list($i, $name, $default, $isVariable) = $arg;

            if ($isVariable) {
                $val = [Type::T_LIST, ',', [], $isVariable];

                for ($count = count($remaining); $i < $count; $i++) {
                    $val[2][] = $remaining[$i];
                }

                foreach ($deferredKeywordArgs as $itemName => $item) {
                    $val[2][$itemName] = $item;
                }
            } elseif (isset($remaining[$i])) {
                $val = $remaining[$i];
            } elseif (isset($keywordArgs[$name])) {
                $val = $keywordArgs[$name];
            } elseif (! empty($default)) {
                continue;
            } else {
                $this->throwError("Missing argument $name");
                break;
            }

            $this->set($name, $this->reduce($val, true), true, $env);
        }

        $storeEnv->store = $env->store;

        foreach ($args as $arg) {
            list($i, $name, $default, $isVariable) = $arg;

            if ($isVariable || isset($remaining[$i]) ||
isset($keywordArgs[$name]) || empty($default)) {
                continue;
            }

            $this->set($name, $this->reduce($default, true), true);
        }
    }

    /**
     * Coerce a php value into a scss one
     *
     * @param mixed $value
     *
     * @return array|\Leafo\ScssPhp\Node\Number
     */
    protected function coerceValue($value)
    {
        if (is_array($value) || $value instanceof \ArrayAccess) {
            return $value;
        }

        if (is_bool($value)) {
            return $this->toBool($value);
        }

        if ($value === null) {
            return static::$null;
        }

        if (is_numeric($value)) {
            return new Node\Number($value, '');
        }

        if ($value === '') {
            return static::$emptyString;
        }

        if (preg_match('/^(#([0-9a-f]{6})|#([0-9a-f]{3}))$/i',
$value, $m)) {
            $color = [Type::T_COLOR];

            if (isset($m[3])) {
                $num = hexdec($m[3]);

                foreach ([3, 2, 1] as $i) {
                    $t = $num & 0xf;
                    $color[$i] = $t << 4 | $t;
                    $num >>= 4;
                }
            } else {
                $num = hexdec($m[2]);

                foreach ([3, 2, 1] as $i) {
                    $color[$i] = $num & 0xff;
                    $num >>= 8;
                }
            }

            return $color;
        }

        return [Type::T_KEYWORD, $value];
    }

    /**
     * Coerce something to map
     *
     * @param array $item
     *
     * @return array
     */
    protected function coerceMap($item)
    {
        if ($item[0] === Type::T_MAP) {
            return $item;
        }

        if ($item === static::$emptyList) {
            return static::$emptyMap;
        }

        return [Type::T_MAP, [$item], [static::$null]];
    }

    /**
     * Coerce something to list
     *
     * @param array  $item
     * @param string $delim
     *
     * @return array
     */
    protected function coerceList($item, $delim = ',')
    {
        if (isset($item) && $item[0] === Type::T_LIST) {
            return $item;
        }

        if (isset($item) && $item[0] === Type::T_MAP) {
            $keys = $item[1];
            $values = $item[2];
            $list = [];

            for ($i = 0, $s = count($keys); $i < $s; $i++) {
                $key = $keys[$i];
                $value = $values[$i];

                $list[] = [
                    Type::T_LIST,
                    '',
                    [[Type::T_KEYWORD,
$this->compileStringContent($this->coerceString($key))], $value]
                ];
            }

            return [Type::T_LIST, ',', $list];
        }

        return [Type::T_LIST, $delim, ! isset($item) ? []: [$item]];
    }

    /**
     * Coerce color for expression
     *
     * @param array $value
     *
     * @return array|null
     */
    protected function coerceForExpression($value)
    {
        if ($color = $this->coerceColor($value)) {
            return $color;
        }

        return $value;
    }

    /**
     * Coerce value to color
     *
     * @param array $value
     *
     * @return array|null
     */
    protected function coerceColor($value)
    {
        switch ($value[0]) {
            case Type::T_COLOR:
                return $value;

            case Type::T_KEYWORD:
                $name = strtolower($value[1]);

                if (isset(Colors::$cssColors[$name])) {
                    $rgba = explode(',',
Colors::$cssColors[$name]);

                    return isset($rgba[3])
                        ? [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1],
(int) $rgba[2], (int) $rgba[3]]
                        : [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1],
(int) $rgba[2]];
                }

                return null;
        }

        return null;
    }

    /**
     * Coerce value to string
     *
     * @param array $value
     *
     * @return array|null
     */
    protected function coerceString($value)
    {
        if ($value[0] === Type::T_STRING) {
            return $value;
        }

        return [Type::T_STRING, '',
[$this->compileValue($value)]];
    }

    /**
     * Coerce value to a percentage
     *
     * @param array $value
     *
     * @return integer|float
     */
    protected function coercePercent($value)
    {
        if ($value[0] === Type::T_NUMBER) {
            if (! empty($value[2]['%'])) {
                return $value[1] / 100;
            }

            return $value[1];
        }

        return 0;
    }

    /**
     * Assert value is a map
     *
     * @api
     *
     * @param array $value
     *
     * @return array
     *
     * @throws \Exception
     */
    public function assertMap($value)
    {
        $value = $this->coerceMap($value);

        if ($value[0] !== Type::T_MAP) {
            $this->throwError('expecting map, %s received',
$value[0]);
        }

        return $value;
    }

    /**
     * Assert value is a list
     *
     * @api
     *
     * @param array $value
     *
     * @return array
     *
     * @throws \Exception
     */
    public function assertList($value)
    {
        if ($value[0] !== Type::T_LIST) {
            $this->throwError('expecting list, %s received',
$value[0]);
        }

        return $value;
    }

    /**
     * Assert value is a color
     *
     * @api
     *
     * @param array $value
     *
     * @return array
     *
     * @throws \Exception
     */
    public function assertColor($value)
    {
        if ($color = $this->coerceColor($value)) {
            return $color;
        }

        $this->throwError('expecting color, %s received',
$value[0]);
    }

    /**
     * Assert value is a number
     *
     * @api
     *
     * @param array $value
     *
     * @return integer|float
     *
     * @throws \Exception
     */
    public function assertNumber($value)
    {
        if ($value[0] !== Type::T_NUMBER) {
            $this->throwError('expecting number, %s received',
$value[0]);
        }

        return $value[1];
    }

    /**
     * Make sure a color's components don't go out of bounds
     *
     * @param array $c
     *
     * @return array
     */
    protected function fixColor($c)
    {
        foreach ([1, 2, 3] as $i) {
            if ($c[$i] < 0) {
                $c[$i] = 0;
            }

            if ($c[$i] > 255) {
                $c[$i] = 255;
            }
        }

        return $c;
    }

    /**
     * Convert RGB to HSL
     *
     * @api
     *
     * @param integer $red
     * @param integer $green
     * @param integer $blue
     *
     * @return array
     */
    public function toHSL($red, $green, $blue)
    {
        $min = min($red, $green, $blue);
        $max = max($red, $green, $blue);

        $l = $min + $max;
        $d = $max - $min;

        if ((int) $d === 0) {
            $h = $s = 0;
        } else {
            if ($l < 255) {
                $s = $d / $l;
            } else {
                $s = $d / (510 - $l);
            }

            if ($red == $max) {
                $h = 60 * ($green - $blue) / $d;
            } elseif ($green == $max) {
                $h = 60 * ($blue - $red) / $d + 120;
            } elseif ($blue == $max) {
                $h = 60 * ($red - $green) / $d + 240;
            }
        }

        return [Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1];
    }

    /**
     * Hue to RGB helper
     *
     * @param float $m1
     * @param float $m2
     * @param float $h
     *
     * @return float
     */
    protected function hueToRGB($m1, $m2, $h)
    {
        if ($h < 0) {
            $h += 1;
        } elseif ($h > 1) {
            $h -= 1;
        }

        if ($h * 6 < 1) {
            return $m1 + ($m2 - $m1) * $h * 6;
        }

        if ($h * 2 < 1) {
            return $m2;
        }

        if ($h * 3 < 2) {
            return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
        }

        return $m1;
    }

    /**
     * Convert HSL to RGB
     *
     * @api
     *
     * @param integer $hue        H from 0 to 360
     * @param integer $saturation S from 0 to 100
     * @param integer $lightness  L from 0 to 100
     *
     * @return array
     */
    public function toRGB($hue, $saturation, $lightness)
    {
        if ($hue < 0) {
            $hue += 360;
        }

        $h = $hue / 360;
        $s = min(100, max(0, $saturation)) / 100;
        $l = min(100, max(0, $lightness)) / 100;

        $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
        $m1 = $l * 2 - $m2;

        $r = $this->hueToRGB($m1, $m2, $h + 1/3) * 255;
        $g = $this->hueToRGB($m1, $m2, $h) * 255;
        $b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255;

        $out = [Type::T_COLOR, $r, $g, $b];

        return $out;
    }

    // Built in functions

    //protected static $libCall = ['name', 'args...'];
    protected function libCall($args, $kwargs)
    {
        $name =
$this->compileStringContent($this->coerceString($this->reduce(array_shift($args),
true)));

        $posArgs = [];

        foreach ($args as $arg) {
            if (empty($arg[0])) {
                if ($arg[2] === true) {
                    $tmp = $this->reduce($arg[1]);

                    if ($tmp[0] === Type::T_LIST) {
                        foreach ($tmp[2] as $item) {
                            $posArgs[] = [null, $item, false];
                        }
                    } else {
                        $posArgs[] = [null, $tmp, true];
                    }

                    continue;
                }

                $posArgs[] = [null, $this->reduce($arg), false];
                continue;
            }

            $posArgs[] = [null, $arg, false];
        }

        if (count($kwargs)) {
            foreach ($kwargs as $key => $value) {
                $posArgs[] = [[Type::T_VARIABLE, $key], $value, false];
            }
        }

        return $this->reduce([Type::T_FUNCTION_CALL, $name, $posArgs]);
    }

    protected static $libIf = ['condition', 'if-true',
'if-false'];
    protected function libIf($args)
    {
        list($cond, $t, $f) = $args;

        if (! $this->isTruthy($this->reduce($cond, true))) {
            return $this->reduce($f, true);
        }

        return $this->reduce($t, true);
    }

    protected static $libIndex = ['list', 'value'];
    protected function libIndex($args)
    {
        list($list, $value) = $args;

        if ($value[0] === Type::T_MAP) {
            return static::$null;
        }

        if ($list[0] === Type::T_MAP ||
            $list[0] === Type::T_STRING ||
            $list[0] === Type::T_KEYWORD ||
            $list[0] === Type::T_INTERPOLATE
        ) {
            $list = $this->coerceList($list, ' ');
        }

        if ($list[0] !== Type::T_LIST) {
            return static::$null;
        }

        $values = [];

        foreach ($list[2] as $item) {
            $values[] = $this->normalizeValue($item);
        }

        $key = array_search($this->normalizeValue($value), $values);

        return false === $key ? static::$null : $key + 1;
    }

    protected static $libRgb = ['red', 'green',
'blue'];
    protected function libRgb($args)
    {
        list($r, $g, $b) = $args;

        return [Type::T_COLOR, $r[1], $g[1], $b[1]];
    }

    protected static $libRgba = [
        ['red', 'color'],
        'green', 'blue', 'alpha'];
    protected function libRgba($args)
    {
        if ($color = $this->coerceColor($args[0])) {
            $num = isset($args[3]) ? $args[3] : $args[1];
            $alpha = $this->assertNumber($num);
            $color[4] = $alpha;

            return $color;
        }

        list($r, $g, $b, $a) = $args;

        return [Type::T_COLOR, $r[1], $g[1], $b[1], $a[1]];
    }

    // helper function for adjust_color, change_color, and scale_color
    protected function alterColor($args, $fn)
    {
        $color = $this->assertColor($args[0]);

        foreach ([1, 2, 3, 7] as $i) {
            if (isset($args[$i])) {
                $val = $this->assertNumber($args[$i]);
                $ii = $i === 7 ? 4 : $i; // alpha
                $color[$ii] = call_user_func($fn, isset($color[$ii]) ?
$color[$ii] : 0, $val, $i);
            }
        }

        if (isset($args[4]) || isset($args[5]) || isset($args[6])) {
            $hsl = $this->toHSL($color[1], $color[2], $color[3]);

            foreach ([4, 5, 6] as $i) {
                if (isset($args[$i])) {
                    $val = $this->assertNumber($args[$i]);
                    $hsl[$i - 3] = call_user_func($fn, $hsl[$i - 3], $val,
$i);
                }
            }

            $rgb = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);

            if (isset($color[4])) {
                $rgb[4] = $color[4];
            }

            $color = $rgb;
        }

        return $color;
    }

    protected static $libAdjustColor = [
        'color', 'red', 'green',
'blue',
        'hue', 'saturation', 'lightness',
'alpha'
    ];
    protected function libAdjustColor($args)
    {
        return $this->alterColor($args, function ($base, $alter, $i) {
            return $base + $alter;
        });
    }

    protected static $libChangeColor = [
        'color', 'red', 'green',
'blue',
        'hue', 'saturation', 'lightness',
'alpha'
    ];
    protected function libChangeColor($args)
    {
        return $this->alterColor($args, function ($base, $alter, $i) {
            return $alter;
        });
    }

    protected static $libScaleColor = [
        'color', 'red', 'green',
'blue',
        'hue', 'saturation', 'lightness',
'alpha'
    ];
    protected function libScaleColor($args)
    {
        return $this->alterColor($args, function ($base, $scale, $i) {
            // 1, 2, 3 - rgb
            // 4, 5, 6 - hsl
            // 7 - a
            switch ($i) {
                case 1:
                case 2:
                case 3:
                    $max = 255;
                    break;

                case 4:
                    $max = 360;
                    break;

                case 7:
                    $max = 1;
                    break;

                default:
                    $max = 100;
            }

            $scale = $scale / 100;

            if ($scale < 0) {
                return $base * $scale + $base;
            }

            return ($max - $base) * $scale + $base;
        });
    }

    protected static $libIeHexStr = ['color'];
    protected function libIeHexStr($args)
    {
        $color = $this->coerceColor($args[0]);
        $color[4] = isset($color[4]) ? round(255 * $color[4]) : 255;

        return sprintf('#%02X%02X%02X%02X', $color[4], $color[1],
$color[2], $color[3]);
    }

    protected static $libRed = ['color'];
    protected function libRed($args)
    {
        $color = $this->coerceColor($args[0]);

        return $color[1];
    }

    protected static $libGreen = ['color'];
    protected function libGreen($args)
    {
        $color = $this->coerceColor($args[0]);

        return $color[2];
    }

    protected static $libBlue = ['color'];
    protected function libBlue($args)
    {
        $color = $this->coerceColor($args[0]);

        return $color[3];
    }

    protected static $libAlpha = ['color'];
    protected function libAlpha($args)
    {
        if ($color = $this->coerceColor($args[0])) {
            return isset($color[4]) ? $color[4] : 1;
        }

        // this might be the IE function, so return value unchanged
        return null;
    }

    protected static $libOpacity = ['color'];
    protected function libOpacity($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

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

    // mix two colors
    protected static $libMix = ['color-1', 'color-2',
'weight'];
    protected function libMix($args)
    {
        list($first, $second, $weight) = $args;

        $first = $this->assertColor($first);
        $second = $this->assertColor($second);

        if (! isset($weight)) {
            $weight = 0.5;
        } else {
            $weight = $this->coercePercent($weight);
        }

        $firstAlpha = isset($first[4]) ? $first[4] : 1;
        $secondAlpha = isset($second[4]) ? $second[4] : 1;

        $w = $weight * 2 - 1;
        $a = $firstAlpha - $secondAlpha;

        $w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) /
2.0;
        $w2 = 1.0 - $w1;

        $new = [Type::T_COLOR,
            $w1 * $first[1] + $w2 * $second[1],
            $w1 * $first[2] + $w2 * $second[2],
            $w1 * $first[3] + $w2 * $second[3],
        ];

        if ($firstAlpha != 1.0 || $secondAlpha != 1.0) {
            $new[] = $firstAlpha * $weight + $secondAlpha * (1 - $weight);
        }

        return $this->fixColor($new);
    }

    protected static $libHsl = ['hue', 'saturation',
'lightness'];
    protected function libHsl($args)
    {
        list($h, $s, $l) = $args;

        return $this->toRGB($h[1], $s[1], $l[1]);
    }

    protected static $libHsla = ['hue', 'saturation',
'lightness', 'alpha'];
    protected function libHsla($args)
    {
        list($h, $s, $l, $a) = $args;

        $color = $this->toRGB($h[1], $s[1], $l[1]);
        $color[4] = $a[1];

        return $color;
    }

    protected static $libHue = ['color'];
    protected function libHue($args)
    {
        $color = $this->assertColor($args[0]);
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);

        return new Node\Number($hsl[1], 'deg');
    }

    protected static $libSaturation = ['color'];
    protected function libSaturation($args)
    {
        $color = $this->assertColor($args[0]);
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);

        return new Node\Number($hsl[2], '%');
    }

    protected static $libLightness = ['color'];
    protected function libLightness($args)
    {
        $color = $this->assertColor($args[0]);
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);

        return new Node\Number($hsl[3], '%');
    }

    protected function adjustHsl($color, $idx, $amount)
    {
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);
        $hsl[$idx] += $amount;
        $out = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);

        if (isset($color[4])) {
            $out[4] = $color[4];
        }

        return $out;
    }

    protected static $libAdjustHue = ['color',
'degrees'];
    protected function libAdjustHue($args)
    {
        $color = $this->assertColor($args[0]);
        $degrees = $this->assertNumber($args[1]);

        return $this->adjustHsl($color, 1, $degrees);
    }

    protected static $libLighten = ['color', 'amount'];
    protected function libLighten($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = Util::checkRange('amount', new Range(0, 100),
$args[1], '%');

        return $this->adjustHsl($color, 3, $amount);
    }

    protected static $libDarken = ['color', 'amount'];
    protected function libDarken($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = Util::checkRange('amount', new Range(0, 100),
$args[1], '%');

        return $this->adjustHsl($color, 3, -$amount);
    }

    protected static $libSaturate = ['color',
'amount'];
    protected function libSaturate($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

        $color = $this->assertColor($value);
        $amount = 100 * $this->coercePercent($args[1]);

        return $this->adjustHsl($color, 2, $amount);
    }

    protected static $libDesaturate = ['color',
'amount'];
    protected function libDesaturate($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = 100 * $this->coercePercent($args[1]);

        return $this->adjustHsl($color, 2, -$amount);
    }

    protected static $libGrayscale = ['color'];
    protected function libGrayscale($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

        return $this->adjustHsl($this->assertColor($value), 2, -100);
    }

    protected static $libComplement = ['color'];
    protected function libComplement($args)
    {
        return $this->adjustHsl($this->assertColor($args[0]), 1,
180);
    }

    protected static $libInvert = ['color'];
    protected function libInvert($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

        $color = $this->assertColor($value);
        $color[1] = 255 - $color[1];
        $color[2] = 255 - $color[2];
        $color[3] = 255 - $color[3];

        return $color;
    }

    // increases opacity by amount
    protected static $libOpacify = ['color', 'amount'];
    protected function libOpacify($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = $this->coercePercent($args[1]);

        $color[4] = (isset($color[4]) ? $color[4] : 1) + $amount;
        $color[4] = min(1, max(0, $color[4]));

        return $color;
    }

    protected static $libFadeIn = ['color', 'amount'];
    protected function libFadeIn($args)
    {
        return $this->libOpacify($args);
    }

    // decreases opacity by amount
    protected static $libTransparentize = ['color',
'amount'];
    protected function libTransparentize($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = $this->coercePercent($args[1]);

        $color[4] = (isset($color[4]) ? $color[4] : 1) - $amount;
        $color[4] = min(1, max(0, $color[4]));

        return $color;
    }

    protected static $libFadeOut = ['color', 'amount'];
    protected function libFadeOut($args)
    {
        return $this->libTransparentize($args);
    }

    protected static $libUnquote = ['string'];
    protected function libUnquote($args)
    {
        $str = $args[0];

        if ($str[0] === Type::T_STRING) {
            $str[1] = '';
        }

        return $str;
    }

    protected static $libQuote = ['string'];
    protected function libQuote($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_STRING && ! empty($value[1])) {
            return $value;
        }

        return [Type::T_STRING, '"', [$value]];
    }

    protected static $libPercentage = ['value'];
    protected function libPercentage($args)
    {
        return new Node\Number($this->coercePercent($args[0]) * 100,
'%');
    }

    protected static $libRound = ['value'];
    protected function libRound($args)
    {
        $num = $args[0];

        return new Node\Number(round($num[1]), $num[2]);
    }

    protected static $libFloor = ['value'];
    protected function libFloor($args)
    {
        $num = $args[0];

        return new Node\Number(floor($num[1]), $num[2]);
    }

    protected static $libCeil = ['value'];
    protected function libCeil($args)
    {
        $num = $args[0];

        return new Node\Number(ceil($num[1]), $num[2]);
    }

    protected static $libAbs = ['value'];
    protected function libAbs($args)
    {
        $num = $args[0];

        return new Node\Number(abs($num[1]), $num[2]);
    }

    protected function libMin($args)
    {
        $numbers = $this->getNormalizedNumbers($args);
        $min = null;

        foreach ($numbers as $key => $number) {
            if (null === $min || $number[1] <= $min[1]) {
                $min = [$key, $number[1]];
            }
        }

        return $args[$min[0]];
    }

    protected function libMax($args)
    {
        $numbers = $this->getNormalizedNumbers($args);
        $max = null;

        foreach ($numbers as $key => $number) {
            if (null === $max || $number[1] >= $max[1]) {
                $max = [$key, $number[1]];
            }
        }

        return $args[$max[0]];
    }

    /**
     * Helper to normalize args containing numbers
     *
     * @param array $args
     *
     * @return array
     */
    protected function getNormalizedNumbers($args)
    {
        $unit = null;
        $originalUnit = null;
        $numbers = [];

        foreach ($args as $key => $item) {
            if ($item[0] !== Type::T_NUMBER) {
                $this->throwError('%s is not a number',
$item[0]);
                break;
            }

            $number = $item->normalize();

            if (null === $unit) {
                $unit = $number[2];
                $originalUnit = $item->unitStr();
            } elseif ($number[1] && $unit !== $number[2]) {
                $this->throwError('Incompatible units:
"%s" and "%s".', $originalUnit,
$item->unitStr());
                break;
            }

            $numbers[$key] = $number;
        }

        return $numbers;
    }

    protected static $libLength = ['list'];
    protected function libLength($args)
    {
        $list = $this->coerceList($args[0]);

        return count($list[2]);
    }

    //protected static $libListSeparator = ['list...'];
    protected function libListSeparator($args)
    {
        if (count($args) > 1) {
            return 'comma';
        }

        $list = $this->coerceList($args[0]);

        if (count($list[2]) <= 1) {
            return 'space';
        }

        if ($list[1] === ',') {
            return 'comma';
        }

        return 'space';
    }

    protected static $libNth = ['list', 'n'];
    protected function libNth($args)
    {
        $list = $this->coerceList($args[0]);
        $n = $this->assertNumber($args[1]);

        if ($n > 0) {
            $n--;
        } elseif ($n < 0) {
            $n += count($list[2]);
        }

        return isset($list[2][$n]) ? $list[2][$n] : static::$defaultValue;
    }

    protected static $libSetNth = ['list', 'n',
'value'];
    protected function libSetNth($args)
    {
        $list = $this->coerceList($args[0]);
        $n = $this->assertNumber($args[1]);

        if ($n > 0) {
            $n--;
        } elseif ($n < 0) {
            $n += count($list[2]);
        }

        if (! isset($list[2][$n])) {
            $this->throwError('Invalid argument for
"n"');

            return null;
        }

        $list[2][$n] = $args[2];

        return $list;
    }

    protected static $libMapGet = ['map', 'key'];
    protected function libMapGet($args)
    {
        $map = $this->assertMap($args[0]);
        $key =
$this->compileStringContent($this->coerceString($args[1]));

        for ($i = count($map[1]) - 1; $i >= 0; $i--) {
            if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
                return $map[2][$i];
            }
        }

        return static::$null;
    }

    protected static $libMapKeys = ['map'];
    protected function libMapKeys($args)
    {
        $map = $this->assertMap($args[0]);
        $keys = $map[1];

        return [Type::T_LIST, ',', $keys];
    }

    protected static $libMapValues = ['map'];
    protected function libMapValues($args)
    {
        $map = $this->assertMap($args[0]);
        $values = $map[2];

        return [Type::T_LIST, ',', $values];
    }

    protected static $libMapRemove = ['map', 'key'];
    protected function libMapRemove($args)
    {
        $map = $this->assertMap($args[0]);
        $key =
$this->compileStringContent($this->coerceString($args[1]));

        for ($i = count($map[1]) - 1; $i >= 0; $i--) {
            if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
                array_splice($map[1], $i, 1);
                array_splice($map[2], $i, 1);
            }
        }

        return $map;
    }

    protected static $libMapHasKey = ['map', 'key'];
    protected function libMapHasKey($args)
    {
        $map = $this->assertMap($args[0]);
        $key =
$this->compileStringContent($this->coerceString($args[1]));

        for ($i = count($map[1]) - 1; $i >= 0; $i--) {
            if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
                return true;
            }
        }

        return false;
    }

    protected static $libMapMerge = ['map-1', 'map-2'];
    protected function libMapMerge($args)
    {
        $map1 = $this->assertMap($args[0]);
        $map2 = $this->assertMap($args[1]);

        foreach ($map2[1] as $i2 => $key2) {
            $key =
$this->compileStringContent($this->coerceString($key2));

            foreach ($map1[1] as $i1 => $key1) {
                if ($key ===
$this->compileStringContent($this->coerceString($key1))) {
                    $map1[2][$i1] = $map2[2][$i2];
                    continue 2;
                }
            }

            $map1[1][] = $map2[1][$i2];
            $map1[2][] = $map2[2][$i2];
        }

        return $map1;
    }

    protected static $libKeywords = ['args'];
    protected function libKeywords($args)
    {
        $this->assertList($args[0]);

        $keys = [];
        $values = [];

        foreach ($args[0][2] as $name => $arg) {
            $keys[] = [Type::T_KEYWORD, $name];
            $values[] = $arg;
        }

        return [Type::T_MAP, $keys, $values];
    }

    protected function listSeparatorForJoin($list1, $sep)
    {
        if (! isset($sep)) {
            return $list1[1];
        }

        switch ($this->compileValue($sep)) {
            case 'comma':
                return ',';

            case 'space':
                return '';

            default:
                return $list1[1];
        }
    }

    protected static $libJoin = ['list1', 'list2',
'separator'];
    protected function libJoin($args)
    {
        list($list1, $list2, $sep) = $args;

        $list1 = $this->coerceList($list1, ' ');
        $list2 = $this->coerceList($list2, ' ');
        $sep = $this->listSeparatorForJoin($list1, $sep);

        return [Type::T_LIST, $sep, array_merge($list1[2], $list2[2])];
    }

    protected static $libAppend = ['list', 'val',
'separator'];
    protected function libAppend($args)
    {
        list($list1, $value, $sep) = $args;

        $list1 = $this->coerceList($list1, ' ');
        $sep = $this->listSeparatorForJoin($list1, $sep);

        return [Type::T_LIST, $sep, array_merge($list1[2], [$value])];
    }

    protected function libZip($args)
    {
        foreach ($args as $arg) {
            $this->assertList($arg);
        }

        $lists = [];
        $firstList = array_shift($args);

        foreach ($firstList[2] as $key => $item) {
            $list = [Type::T_LIST, '', [$item]];

            foreach ($args as $arg) {
                if (isset($arg[2][$key])) {
                    $list[2][] = $arg[2][$key];
                } else {
                    break 2;
                }
            }

            $lists[] = $list;
        }

        return [Type::T_LIST, ',', $lists];
    }

    protected static $libTypeOf = ['value'];
    protected function libTypeOf($args)
    {
        $value = $args[0];

        switch ($value[0]) {
            case Type::T_KEYWORD:
                if ($value === static::$true || $value === static::$false)
{
                    return 'bool';
                }

                if ($this->coerceColor($value)) {
                    return 'color';
                }

                // fall-thru
            case Type::T_FUNCTION:
                return 'string';

            case Type::T_LIST:
                if (isset($value[3]) && $value[3]) {
                    return 'arglist';
                }

                // fall-thru
            default:
                return $value[0];
        }
    }

    protected static $libUnit = ['number'];
    protected function libUnit($args)
    {
        $num = $args[0];

        if ($num[0] === Type::T_NUMBER) {
            return [Type::T_STRING, '"',
[$num->unitStr()]];
        }

        return '';
    }

    protected static $libUnitless = ['number'];
    protected function libUnitless($args)
    {
        $value = $args[0];

        return $value[0] === Type::T_NUMBER &&
$value->unitless();
    }

    protected static $libComparable = ['number-1',
'number-2'];
    protected function libComparable($args)
    {
        list($number1, $number2) = $args;

        if (! isset($number1[0]) || $number1[0] !== Type::T_NUMBER ||
            ! isset($number2[0]) || $number2[0] !== Type::T_NUMBER
        ) {
            $this->throwError('Invalid argument(s) for
"comparable"');

            return null;
        }

        $number1 = $number1->normalize();
        $number2 = $number2->normalize();

        return $number1[2] === $number2[2] || $number1->unitless() ||
$number2->unitless();
    }

    protected static $libStrIndex = ['string',
'substring'];
    protected function libStrIndex($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $substring = $this->coerceString($args[1]);
        $substringContent = $this->compileStringContent($substring);

        $result = strpos($stringContent, $substringContent);

        return $result === false ? static::$null : new Node\Number($result
+ 1, '');
    }

    protected static $libStrInsert = ['string',
'insert', 'index'];
    protected function libStrInsert($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $insert = $this->coerceString($args[1]);
        $insertContent = $this->compileStringContent($insert);

        list(, $index) = $args[2];

        $string[2] = [substr_replace($stringContent, $insertContent, $index
- 1, 0)];

        return $string;
    }

    protected static $libStrLength = ['string'];
    protected function libStrLength($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        return new Node\Number(strlen($stringContent), '');
    }

    protected static $libStrSlice = ['string',
'start-at', 'end-at:-1'];
    protected function libStrSlice($args)
    {
        if (isset($args[2]) && ! $args[2][1]) {
            return static::$nullString;
        }

        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $start = (int) $args[1][1];

        if ($start > 0) {
            $start--;
        }

        $end    = isset($args[2]) ? (int) $args[2][1] : -1;
        $length = $end < 0 ? $end + 1 : ($end > 0 ? $end - $start :
$end);

        $string[2] = $length
            ? [substr($stringContent, $start, $length)]
            : [substr($stringContent, $start)];

        return $string;
    }

    protected static $libToLowerCase = ['string'];
    protected function libToLowerCase($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $string[2] = [function_exists('mb_strtolower') ?
mb_strtolower($stringContent) : strtolower($stringContent)];

        return $string;
    }

    protected static $libToUpperCase = ['string'];
    protected function libToUpperCase($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $string[2] = [function_exists('mb_strtoupper') ?
mb_strtoupper($stringContent) : strtoupper($stringContent)];

        return $string;
    }

    protected static $libFeatureExists = ['feature'];
    protected function libFeatureExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->toBool(
            array_key_exists($name, $this->registeredFeatures) ?
$this->registeredFeatures[$name] : false
        );
    }

    protected static $libFunctionExists = ['name'];
    protected function libFunctionExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        // user defined functions
        if ($this->has(static::$namespaces['function'] .
$name)) {
            return true;
        }

        $name = $this->normalizeName($name);

        if (isset($this->userFunctions[$name])) {
            return true;
        }

        // built-in functions
        $f = $this->getBuiltinFunction($name);

        return $this->toBool(is_callable($f));
    }

    protected static $libGlobalVariableExists = ['name'];
    protected function libGlobalVariableExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->has($name, $this->rootEnv);
    }

    protected static $libMixinExists = ['name'];
    protected function libMixinExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->has(static::$namespaces['mixin'] .
$name);
    }

    protected static $libVariableExists = ['name'];
    protected function libVariableExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->has($name);
    }

    /**
     * Workaround IE7's content counter bug.
     *
     * @param array $args
     *
     * @return array
     */
    protected function libCounter($args)
    {
        $list = array_map([$this, 'compileValue'], $args);

        return [Type::T_STRING, '', ['counter(' .
implode(',', $list) . ')']];
    }

    protected static $libRandom = ['limit'];
    protected function libRandom($args)
    {
        if (isset($args[0])) {
            $n = $this->assertNumber($args[0]);

            if ($n < 1) {
                $this->throwError("limit must be greater than or
equal to 1");

                return null;
            }

            return new Node\Number(mt_rand(1, $n), '');
        }

        return new Node\Number(mt_rand(1, mt_getrandmax()), '');
    }

    protected function libUniqueId()
    {
        static $id;

        if (! isset($id)) {
            $id = mt_rand(0, pow(36, 8));
        }

        $id += mt_rand(0, 10) + 1;

        return [Type::T_STRING, '', ['u' .
str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)]];
    }

    protected static $libInspect = ['value'];
    protected function libInspect($args)
    {
        if ($args[0] === static::$null) {
            return [Type::T_KEYWORD, 'null'];
        }

        return $args[0];
    }

    /**
     * Preprocess selector args
     *
     * @param array $arg
     *
     * @return array|boolean
     */
    protected function getSelectorArg($arg)
    {
        static $parser = null;

        if (is_null($parser)) {
            $parser = $this->parserFactory(__METHOD__);
        }

        $arg = $this->libUnquote([$arg]);
        $arg = $this->compileValue($arg);

        $parsedSelector = [];

        if ($parser->parseSelector($arg, $parsedSelector)) {
            $selector = $this->evalSelectors($parsedSelector);
            $gluedSelector = $this->glueFunctionSelectors($selector);

            return $gluedSelector;
        }

        return false;
    }

    /**
     * Postprocess selector to output in right format
     *
     * @param array $selectors
     *
     * @return string
     */
    protected function formatOutputSelector($selectors)
    {
        $selectors = $this->collapseSelectors($selectors, true);

        return $selectors;
    }

    protected static $libIsSuperselector = ['super',
'sub'];
    protected function libIsSuperselector($args)
    {
        list($super, $sub) = $args;

        $super = $this->getSelectorArg($super);
        $sub = $this->getSelectorArg($sub);

        return $this->isSuperSelector($super, $sub);
    }

    /**
     * Test a $super selector again $sub
     *
     * @param array $super
     * @param array $sub
     *
     * @return boolean
     */
    protected function isSuperSelector($super, $sub)
    {
        // one and only one selector for each arg
        if (! $super || count($super) !== 1) {
            $this->throwError("Invalid super selector for
isSuperSelector()");
        }

        if (! $sub || count($sub) !== 1) {
            $this->throwError("Invalid sub selector for
isSuperSelector()");
        }

        $super = reset($super);
        $sub = reset($sub);

        $i = 0;
        $nextMustMatch = false;

        foreach ($super as $node) {
            $compound = '';

            array_walk_recursive(
                $node,
                function ($value, $key) use (&$compound) {
                    $compound .= $value;
                }
            );

            if ($this->isImmediateRelationshipCombinator($compound)) {
                if ($node !== $sub[$i]) {
                    return false;
                }

                $nextMustMatch = true;
                $i++;
            } else {
                while ($i < count($sub) && !
$this->isSuperPart($node, $sub[$i])) {
                    if ($nextMustMatch) {
                        return false;
                    }

                    $i++;
                }

                if ($i >= count($sub)) {
                    return false;
                }

                $nextMustMatch = false;
                $i++;
            }
        }

        return true;
    }

    /**
     * Test a part of super selector again a part of sub selector
     *
     * @param array $superParts
     * @param array $subParts
     *
     * @return boolean
     */
    protected function isSuperPart($superParts, $subParts)
    {
        $i = 0;

        foreach ($superParts as $superPart) {
            while ($i < count($subParts) && $subParts[$i] !==
$superPart) {
                $i++;
            }

            if ($i >= count($subParts)) {
                return false;
            }

            $i++;
        }

        return true;
    }

    //protected static $libSelectorAppend = ['selector...'];
    protected function libSelectorAppend($args)
    {
        if (count($args) < 1) {
            $this->throwError("selector-append() needs at least 1
argument");
        }

        $selectors = array_map([$this, 'getSelectorArg'], $args);

        return
$this->formatOutputSelector($this->selectorAppend($selectors));
    }

    /**
     * Append parts of the last selector in the list to the previous,
recursively
     *
     * @param array $selectors
     *
     * @return array
     *
     * @throws \Leafo\ScssPhp\Exception\CompilerException
     */
    protected function selectorAppend($selectors)
    {
        $lastSelectors = array_pop($selectors);

        if (! $lastSelectors) {
            $this->throwError("Invalid selector list in
selector-append()");
        }

        while (count($selectors)) {
            $previousSelectors = array_pop($selectors);

            if (! $previousSelectors) {
                $this->throwError("Invalid selector list in
selector-append()");
            }

            // do the trick, happening $lastSelector to $previousSelector
            $appended = [];

            foreach ($lastSelectors as $lastSelector) {
                $previous = $previousSelectors;

                foreach ($lastSelector as $lastSelectorParts) {
                    foreach ($lastSelectorParts as $lastSelectorPart) {
                        foreach ($previous as $i => $previousSelector) {
                            foreach ($previousSelector as $j =>
$previousSelectorParts) {
                                $previous[$i][$j][] = $lastSelectorPart;
                            }
                        }
                    }
                }

                foreach ($previous as $ps) {
                    $appended[] = $ps;
                }
            }

            $lastSelectors = $appended;
        }

        return $lastSelectors;
    }

    protected static $libSelectorExtend = ['selectors',
'extendee', 'extender'];
    protected function libSelectorExtend($args)
    {
        list($selectors, $extendee, $extender) = $args;

        $selectors = $this->getSelectorArg($selectors);
        $extendee = $this->getSelectorArg($extendee);
        $extender = $this->getSelectorArg($extender);

        if (! $selectors || ! $extendee || ! $extender) {
            $this->throwError("selector-extend() invalid
arguments");
        }

        $extended = $this->extendOrReplaceSelectors($selectors,
$extendee, $extender);

        return $this->formatOutputSelector($extended);
    }

    protected static $libSelectorReplace = ['selectors',
'original', 'replacement'];
    protected function libSelectorReplace($args)
    {
        list($selectors, $original, $replacement) = $args;

        $selectors = $this->getSelectorArg($selectors);
        $original = $this->getSelectorArg($original);
        $replacement = $this->getSelectorArg($replacement);

        if (! $selectors || ! $original || ! $replacement) {
            $this->throwError("selector-replace() invalid
arguments");
        }

        $replaced = $this->extendOrReplaceSelectors($selectors,
$original, $replacement, true);

        return $this->formatOutputSelector($replaced);
    }

    /**
     * Extend/replace in selectors
     * used by selector-extend and selector-replace that use the same logic
     *
     * @param array   $selectors
     * @param array   $extendee
     * @param array   $extender
     * @param boolean $replace
     *
     * @return array
     */
    protected function extendOrReplaceSelectors($selectors, $extendee,
$extender, $replace = false)
    {
        $saveExtends = $this->extends;
        $saveExtendsMap = $this->extendsMap;

        $this->extends = [];
        $this->extendsMap = [];

        foreach ($extendee as $es) {
            // only use the first one
            $this->pushExtends(reset($es), $extender, null);
        }

        $extended = [];

        foreach ($selectors as $selector) {
            if (! $replace) {
                $extended[] = $selector;
            }

            $n = count($extended);

            $this->matchExtends($selector, $extended);

            // if didnt match, keep the original selector if we are in a
replace operation
            if ($replace and count($extended) === $n) {
                $extended[] = $selector;
            }
        }

        $this->extends = $saveExtends;
        $this->extendsMap = $saveExtendsMap;

        return $extended;
    }

    //protected static $libSelectorNest = ['selector...'];
    protected function libSelectorNest($args)
    {
        if (count($args) < 1) {
            $this->throwError("selector-nest() needs at least 1
argument");
        }

        $selectorsMap = array_map([$this, 'getSelectorArg'],
$args);

        $envs = [];
        foreach ($selectorsMap as $selectors) {
            $env = new Environment();
            $env->selectors = $selectors;

            $envs[] = $env;
        }

        $envs = array_reverse($envs);
        $env = $this->extractEnv($envs);
        $outputSelectors = $this->multiplySelectors($env);

        return $this->formatOutputSelector($outputSelectors);
    }

    protected static $libSelectorParse = ['selectors'];
    protected function libSelectorParse($args)
    {
        $selectors = reset($args);
        $selectors = $this->getSelectorArg($selectors);

        return $this->formatOutputSelector($selectors);
    }

    protected static $libSelectorUnify = ['selectors1',
'selectors2'];
    protected function libSelectorUnify($args)
    {
        list($selectors1, $selectors2) = $args;

        $selectors1 = $this->getSelectorArg($selectors1);
        $selectors2 = $this->getSelectorArg($selectors2);

        if (! $selectors1 || ! $selectors2) {
            $this->throwError("selector-unify() invalid
arguments");
        }

        // only consider the first compound of each
        $compound1 = reset($selectors1);
        $compound2 = reset($selectors2);

        // unify them and that's it
        $unified = $this->unifyCompoundSelectors($compound1,
$compound2);

        return $this->formatOutputSelector($unified);
    }

    /**
     * The selector-unify magic as its best
     * (at least works as expected on test cases)
     *
     * @param array $compound1
     * @param array $compound2
     * @return array|mixed
     */
    protected function unifyCompoundSelectors($compound1, $compound2)
    {
        if (! count($compound1)) {
            return $compound2;
        }

        if (! count($compound2)) {
            return $compound1;
        }

        // check that last part are compatible
        $lastPart1 = array_pop($compound1);
        $lastPart2 = array_pop($compound2);
        $last = $this->mergeParts($lastPart1, $lastPart2);

        if (! $last) {
            return [[]];
        }

        $unifiedCompound = [$last];
        $unifiedSelectors = [$unifiedCompound];

        // do the rest
        while (count($compound1) || count($compound2)) {
            $part1 = end($compound1);
            $part2 = end($compound2);

            if ($part1 && ($match2 =
$this->matchPartInCompound($part1, $compound2))) {
                list($compound2, $part2, $after2) = $match2;

                if ($after2) {
                    $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, $after2);
                }

                $c = $this->mergeParts($part1, $part2);
                $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, [$c]);
                $part1 = $part2 = null;

                array_pop($compound1);
            }

            if ($part2 && ($match1 =
$this->matchPartInCompound($part2, $compound1))) {
                list($compound1, $part1, $after1) = $match1;

                if ($after1) {
                    $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, $after1);
                }

                $c = $this->mergeParts($part2, $part1);
                $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, [$c]);
                $part1 = $part2 = null;

                array_pop($compound2);
            }

            $new = [];

            if ($part1 && $part2) {
                array_pop($compound1);
                array_pop($compound2);

                $s = $this->prependSelectors($unifiedSelectors,
[$part2]);
                $new = array_merge($new, $this->prependSelectors($s,
[$part1]));
                $s = $this->prependSelectors($unifiedSelectors,
[$part1]);
                $new = array_merge($new, $this->prependSelectors($s,
[$part2]));
            } elseif ($part1) {
                array_pop($compound1);

                $new = array_merge($new,
$this->prependSelectors($unifiedSelectors, [$part1]));
            } elseif ($part2) {
                array_pop($compound2);

                $new = array_merge($new,
$this->prependSelectors($unifiedSelectors, [$part2]));
            }

            if ($new) {
                $unifiedSelectors = $new;
            }
        }

        return $unifiedSelectors;
    }

    /**
     * Prepend each selector from $selectors with $parts
     *
     * @param array $selectors
     * @param array $parts
     *
     * @return array
     */
    protected function prependSelectors($selectors, $parts)
    {
        $new = [];

        foreach ($selectors as $compoundSelector) {
            array_unshift($compoundSelector, $parts);

            $new[] = $compoundSelector;
        }

        return $new;
    }

    /**
     * Try to find a matching part in a compound:
     * - with same html tag name
     * - with some class or id or something in common
     *
     * @param array $part
     * @param array $compound
     *
     * @return array|boolean
     */
    protected function matchPartInCompound($part, $compound)
    {
        $partTag = $this->findTagName($part);
        $before = $compound;
        $after = [];

        // try to find a match by tag name first
        while (count($before)) {
            $p = array_pop($before);

            if ($partTag && $partTag !== '*' &&
$partTag == $this->findTagName($p)) {
                return [$before, $p, $after];
            }

            $after[] = $p;
        }

        // try again matching a non empty intersection and a compatible
tagname
        $before = $compound;
        $after = [];

        while (count($before)) {
            $p = array_pop($before);

            if ($this->checkCompatibleTags($partTag,
$this->findTagName($p))) {
                if (count(array_intersect($part, $p))) {
                    return [$before, $p, $after];
                }
            }

            $after[] = $p;
        }

        return false;
    }

    /**
     * Merge two part list taking care that
     * - the html tag is coming first - if any
     * - the :something are coming last
     *
     * @param array $parts1
     * @param array $parts2
     *
     * @return array
     */
    protected function mergeParts($parts1, $parts2)
    {
        $tag1 = $this->findTagName($parts1);
        $tag2 = $this->findTagName($parts2);
        $tag = $this->checkCompatibleTags($tag1, $tag2);

        // not compatible tags
        if ($tag === false) {
            return [];
        }

        if ($tag) {
            if ($tag1) {
                $parts1 = array_diff($parts1, [$tag1]);
            }

            if ($tag2) {
                $parts2 = array_diff($parts2, [$tag2]);
            }
        }

        $mergedParts = array_merge($parts1, $parts2);
        $mergedOrderedParts = [];

        foreach ($mergedParts as $part) {
            if (strpos($part, ':') === 0) {
                $mergedOrderedParts[] = $part;
            }
        }

        $mergedParts = array_diff($mergedParts, $mergedOrderedParts);
        $mergedParts = array_merge($mergedParts, $mergedOrderedParts);

        if ($tag) {
            array_unshift($mergedParts, $tag);
        }

        return $mergedParts;
    }

    /**
     * Check the compatibility between two tag names:
     * if both are defined they should be identical or one has to be
'*'
     *
     * @param string $tag1
     * @param string $tag2
     *
     * @return array|boolean
     */
    protected function checkCompatibleTags($tag1, $tag2)
    {
        $tags = [$tag1, $tag2];
        $tags = array_unique($tags);
        $tags = array_filter($tags);

        if (count($tags)>1) {
            $tags = array_diff($tags, ['*']);
        }

        // not compatible nodes
        if (count($tags)>1) {
            return false;
        }

        return $tags;
    }

    /**
     * Find the html tag name in a selector parts list
     *
     * @param array $parts
     *
     * @return mixed|string
     */
    protected function findTagName($parts)
    {
        foreach ($parts as $part) {
            if (! preg_match('/^[\[.:#%_-]/', $part)) {
                return $part;
            }
        }

        return '';
    }

    protected static $libSimpleSelectors = ['selector'];
    protected function libSimpleSelectors($args)
    {
        $selector = reset($args);
        $selector = $this->getSelectorArg($selector);

        // remove selectors list layer, keeping the first one
        $selector = reset($selector);

        // remove parts list layer, keeping the first part
        $part = reset($selector);

        $listParts = [];

        foreach ($part as $p) {
            $listParts[] = [Type::T_STRING, '', [$p]];
        }

        return [Type::T_LIST, ',', $listParts];
    }
}
composer.json000064400000001342151166614520007275 0ustar00{
    "name": "gantry/joomla",
    "description": "Gantry Framework Library",
    "license": "GPLv2",

    "require": {
        "php": ">=5.5.9",
        "symfony/event-dispatcher": "~2.8",
        "symfony/yaml": "~2.8",
        "twig/twig": "~1.41",
        "pimple/pimple": "~3.0",
        "filp/whoops": "~2.5.0",
        "rockettheme/toolbox": "~1.3",
        "leafo/scssphp": "~0.8",
        "erusev/parsedown-extra": "~0.7.0"
    },
    "require-dev": {
        "phpunit/phpunit": "3.7.*"
    },
    "autoload": {
        "psr-4": {
            "Gantry\\": "classes/Gantry",
            "Leafo\\ScssPhp\\": "classes/Leafo/ScssPhp"
        }
    },
    "config": {
        "platform": {
            "php": "5.5.9"
        }
    }
}
composer.lock000064400000116261151166614520007263 0ustar00{
    "_readme": [
        "This file locks the dependencies of your project to a known
state",
        "Read more about it at
https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
        "This file is @generated automatically"
    ],
    "content-hash": "f03f176a8d30aa23c9c08489789a8c68",
    "packages": [
        {
            "name": "erusev/parsedown",
            "version": "1.7.4",
            "source": {
                "type": "git",
                "url":
"https://github.com/erusev/parsedown.git",
                "reference":
"cb17b6477dfff935958ba01325f2e8a2bfa6dab3"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
                "reference":
"cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
                "shasum": ""
            },
            "require": {
                "ext-mbstring": "*",
                "php": ">=5.3.0"
            },
            "require-dev": {
                "phpunit/phpunit": "^4.8.35"
            },
            "type": "library",
            "autoload": {
                "psr-0": {
                    "Parsedown": ""
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Emanuil Rusev",
                    "email": "hello@erusev.com",
                    "homepage": "http://erusev.com"
                }
            ],
            "description": "Parser for Markdown.",
            "homepage": "http://parsedown.org",
            "keywords": [
                "markdown",
                "parser"
            ],
            "support": {
                "issues":
"https://github.com/erusev/parsedown/issues",
                "source":
"https://github.com/erusev/parsedown/tree/1.7.x"
            },
            "time": "2019-12-30T22:54:17+00:00"
        },
        {
            "name": "erusev/parsedown-extra",
            "version": "0.7.1",
            "source": {
                "type": "git",
                "url":
"https://github.com/erusev/parsedown-extra.git",
                "reference":
"0db5cce7354e4b76f155d092ab5eb3981c21258c"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/erusev/parsedown-extra/zipball/0db5cce7354e4b76f155d092ab5eb3981c21258c",
                "reference":
"0db5cce7354e4b76f155d092ab5eb3981c21258c",
                "shasum": ""
            },
            "require": {
                "erusev/parsedown": "~1.4"
            },
            "type": "library",
            "autoload": {
                "psr-0": {
                    "ParsedownExtra": ""
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Emanuil Rusev",
                    "email": "hello@erusev.com",
                    "homepage": "http://erusev.com"
                }
            ],
            "description": "An extension of Parsedown that
adds support for Markdown Extra.",
            "homepage":
"https://github.com/erusev/parsedown-extra",
            "keywords": [
                "markdown",
                "markdown extra",
                "parsedown",
                "parser"
            ],
            "support": {
                "issues":
"https://github.com/erusev/parsedown-extra/issues",
                "source":
"https://github.com/erusev/parsedown-extra/tree/master"
            },
            "time": "2015-11-01T10:19:22+00:00"
        },
        {
            "name": "filp/whoops",
            "version": "2.5.1",
            "source": {
                "type": "git",
                "url":
"https://github.com/filp/whoops.git",
                "reference":
"ee9699e79d8fcdd15c107e035d7b965e4fa854ac"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/filp/whoops/zipball/ee9699e79d8fcdd15c107e035d7b965e4fa854ac",
                "reference":
"ee9699e79d8fcdd15c107e035d7b965e4fa854ac",
                "shasum": ""
            },
            "require": {
                "php": "^5.5.9 || ^7.0",
                "psr/log": "^1.0.1"
            },
            "require-dev": {
                "mockery/mockery": "^0.9 || ^1.0",
                "phpunit/phpunit": "^4.8.35 || ^5.7",
                "symfony/var-dumper": "^2.6 || ^3.0 ||
^4.0"
            },
            "suggest": {
                "symfony/var-dumper": "Pretty print complex
values better with var-dumper available",
                "whoops/soap": "Formats errors as SOAP
responses"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "2.2-dev"
                }
            },
            "autoload": {
                "psr-4": {
                    "Whoops\\": "src/Whoops/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Filipe Dobreira",
                    "homepage":
"https://github.com/filp",
                    "role": "Developer"
                }
            ],
            "description": "php error handling for cool
kids",
            "homepage":
"https://filp.github.io/whoops/",
            "keywords": [
                "error",
                "exception",
                "handling",
                "library",
                "throwable",
                "whoops"
            ],
            "support": {
                "issues":
"https://github.com/filp/whoops/issues",
                "source":
"https://github.com/filp/whoops/tree/2.5.1"
            },
            "time": "2019-12-21T10:00:00+00:00"
        },
        {
            "name": "leafo/scssphp",
            "version": "v0.8.4",
            "source": {
                "type": "git",
                "url":
"https://github.com/leafo/scssphp.git",
                "reference":
"b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/leafo/scssphp/zipball/b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9",
                "reference":
"b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9",
                "shasum": ""
            },
            "require": {
                "php": "^5.4.0 || ^7"
            },
            "require-dev": {
                "phpunit/phpunit": "~4.6",
                "squizlabs/php_codesniffer": "~2.5",
                "twbs/bootstrap": "~4.3",
                "zurb/foundation": "~6.5"
            },
            "bin": [
                "bin/pscss"
            ],
            "type": "library",
            "autoload": {
                "psr-4": {
                    "Leafo\\ScssPhp\\": "src/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Leaf Corcoran",
                    "email": "leafot@gmail.com",
                    "homepage": "http://leafo.net"
                }
            ],
            "description": "scssphp is a compiler for SCSS
written in PHP.",
            "homepage":
"http://leafo.github.io/scssphp/",
            "keywords": [
                "css",
                "less",
                "sass",
                "scss",
                "stylesheet"
            ],
            "support": {
                "issues":
"https://github.com/leafo/scssphp/issues",
                "source":
"https://github.com/leafo/scssphp/tree/v0.8.4"
            },
            "abandoned": "scssphp/scssphp",
            "time": "2019-06-18T21:15:44+00:00"
        },
        {
            "name": "pimple/pimple",
            "version": "v3.2.3",
            "source": {
                "type": "git",
                "url":
"https://github.com/silexphp/Pimple.git",
                "reference":
"9e403941ef9d65d20cba7d54e29fe906db42cf32"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
                "reference":
"9e403941ef9d65d20cba7d54e29fe906db42cf32",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.0",
                "psr/container": "^1.0"
            },
            "require-dev": {
                "symfony/phpunit-bridge": "^3.2"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "3.2.x-dev"
                }
            },
            "autoload": {
                "psr-0": {
                    "Pimple": "src/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Fabien Potencier",
                    "email": "fabien@symfony.com"
                }
            ],
            "description": "Pimple, a simple Dependency
Injection Container",
            "homepage": "http://pimple.sensiolabs.org",
            "keywords": [
                "container",
                "dependency injection"
            ],
            "support": {
                "issues":
"https://github.com/silexphp/Pimple/issues",
                "source":
"https://github.com/silexphp/Pimple/tree/master"
            },
            "time": "2018-01-21T07:42:36+00:00"
        },
        {
            "name": "psr/container",
            "version": "1.0.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/php-fig/container.git",
                "reference":
"b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
                "reference":
"b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.0"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.0.x-dev"
                }
            },
            "autoload": {
                "psr-4": {
                    "Psr\\Container\\": "src/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage":
"http://www.php-fig.org/"
                }
            ],
            "description": "Common Container Interface (PHP
FIG PSR-11)",
            "homepage":
"https://github.com/php-fig/container",
            "keywords": [
                "PSR-11",
                "container",
                "container-interface",
                "container-interop",
                "psr"
            ],
            "support": {
                "issues":
"https://github.com/php-fig/container/issues",
                "source":
"https://github.com/php-fig/container/tree/master"
            },
            "time": "2017-02-14T16:28:37+00:00"
        },
        {
            "name": "psr/log",
            "version": "1.1.3",
            "source": {
                "type": "git",
                "url":
"https://github.com/php-fig/log.git",
                "reference":
"0f73288fd15629204f9d42b7055f72dacbe811fc"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
                "reference":
"0f73288fd15629204f9d42b7055f72dacbe811fc",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.0"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.1.x-dev"
                }
            },
            "autoload": {
                "psr-4": {
                    "Psr\\Log\\": "Psr/Log/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage":
"http://www.php-fig.org/"
                }
            ],
            "description": "Common interface for logging
libraries",
            "homepage":
"https://github.com/php-fig/log",
            "keywords": [
                "log",
                "psr",
                "psr-3"
            ],
            "support": {
                "source":
"https://github.com/php-fig/log/tree/1.1.3"
            },
            "time": "2020-03-23T09:12:05+00:00"
        },
        {
            "name": "rockettheme/toolbox",
            "version": "1.4.7",
            "source": {
                "type": "git",
                "url":
"https://github.com/rockettheme/toolbox.git",
                "reference":
"6a86bc0607884d2194260b6b72d67333e0141585"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/rockettheme/toolbox/zipball/6a86bc0607884d2194260b6b72d67333e0141585",
                "reference":
"6a86bc0607884d2194260b6b72d67333e0141585",
                "shasum": ""
            },
            "require": {
                "ext-json": "*",
                "php": ">=5.4.0",
                "pimple/pimple": "~3.0",
                "symfony/event-dispatcher": ">2.5",
                "symfony/yaml": ">2.5"
            },
            "require-dev": {
                "phpunit/phpunit": "~6"
            },
            "type": "library",
            "autoload": {
                "psr-4": {
                    "RocketTheme\\Toolbox\\ArrayTraits\\":
"ArrayTraits/src",
                    "RocketTheme\\Toolbox\\Blueprints\\":
"Blueprints/src",
                    "RocketTheme\\Toolbox\\Compat\\":
"Compat/src",
                    "RocketTheme\\Toolbox\\DI\\":
"DI/src",
                    "RocketTheme\\Toolbox\\Event\\":
"Event/src",
                    "RocketTheme\\Toolbox\\File\\":
"File/src",
                    "RocketTheme\\Toolbox\\ResourceLocator\\":
"ResourceLocator/src",
                    "RocketTheme\\Toolbox\\Session\\":
"Session/src",
                    "RocketTheme\\Toolbox\\StreamWrapper\\":
"StreamWrapper/src"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "description": "RocketTheme Toolbox
Library",
            "homepage": "http://www.rockettheme.com",
            "keywords": [
                "php",
                "rockettheme"
            ],
            "support": {
                "issues":
"https://github.com/rockettheme/toolbox/issues",
                "source":
"https://github.com/rockettheme/toolbox/tree/1.4.7"
            },
            "time": "2020-03-19T18:24:40+00:00"
        },
        {
            "name": "symfony/event-dispatcher",
            "version": "v2.8.52",
            "source": {
                "type": "git",
                "url":
"https://github.com/symfony/event-dispatcher.git",
                "reference":
"a77e974a5fecb4398833b0709210e3d5e334ffb0"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0",
                "reference":
"a77e974a5fecb4398833b0709210e3d5e334ffb0",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.9"
            },
            "require-dev": {
                "psr/log": "~1.0",
                "symfony/config": "^2.0.5|~3.0.0",
                "symfony/dependency-injection":
"~2.6|~3.0.0",
                "symfony/expression-language":
"~2.6|~3.0.0",
                "symfony/stopwatch": "~2.3|~3.0.0"
            },
            "suggest": {
                "symfony/dependency-injection": "",
                "symfony/http-kernel": ""
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "2.8-dev"
                }
            },
            "autoload": {
                "psr-4": {
                    "Symfony\\Component\\EventDispatcher\\":
""
                },
                "exclude-from-classmap": [
                    "/Tests/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Fabien Potencier",
                    "email": "fabien@symfony.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage":
"https://symfony.com/contributors"
                }
            ],
            "description": "Symfony EventDispatcher
Component",
            "homepage": "https://symfony.com",
            "support": {
                "source":
"https://github.com/symfony/event-dispatcher/tree/v2.8.50"
            },
            "time": "2018-11-21T14:20:20+00:00"
        },
        {
            "name": "symfony/polyfill-ctype",
            "version": "v1.19.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/symfony/polyfill-ctype.git",
                "reference":
"aed596913b70fae57be53d86faa2e9ef85a2297b"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b",
                "reference":
"aed596913b70fae57be53d86faa2e9ef85a2297b",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.3"
            },
            "suggest": {
                "ext-ctype": "For best performance"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-main": "1.19-dev"
                },
                "thanks": {
                    "name": "symfony/polyfill",
                    "url":
"https://github.com/symfony/polyfill"
                }
            },
            "autoload": {
                "psr-4": {
                    "Symfony\\Polyfill\\Ctype\\": ""
                },
                "files": [
                    "bootstrap.php"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Gert de Pagter",
                    "email": "BackEndTea@gmail.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage":
"https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill for ctype
functions",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "ctype",
                "polyfill",
                "portable"
            ],
            "support": {
                "source":
"https://github.com/symfony/polyfill-ctype/tree/v1.19.0"
            },
            "funding": [
                {
                    "url":
"https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url":
"https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "time": "2020-10-23T09:01:57+00:00"
        },
        {
            "name": "symfony/yaml",
            "version": "v2.8.52",
            "source": {
                "type": "git",
                "url":
"https://github.com/symfony/yaml.git",
                "reference":
"02c1859112aa779d9ab394ae4f3381911d84052b"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b",
                "reference":
"02c1859112aa779d9ab394ae4f3381911d84052b",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.9",
                "symfony/polyfill-ctype": "~1.8"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "2.8-dev"
                }
            },
            "autoload": {
                "psr-4": {
                    "Symfony\\Component\\Yaml\\": ""
                },
                "exclude-from-classmap": [
                    "/Tests/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Fabien Potencier",
                    "email": "fabien@symfony.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage":
"https://symfony.com/contributors"
                }
            ],
            "description": "Symfony Yaml Component",
            "homepage": "https://symfony.com",
            "support": {
                "source":
"https://github.com/symfony/yaml/tree/v2.8.52"
            },
            "time": "2018-11-11T11:18:13+00:00"
        },
        {
            "name": "twig/twig",
            "version": "v1.42.5",
            "source": {
                "type": "git",
                "url":
"https://github.com/twigphp/Twig.git",
                "reference":
"87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/twigphp/Twig/zipball/87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e",
                "reference":
"87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e",
                "shasum": ""
            },
            "require": {
                "php": ">=5.5.0",
                "symfony/polyfill-ctype": "^1.8"
            },
            "require-dev": {
                "psr/container": "^1.0",
                "symfony/phpunit-bridge": "^4.4|^5.0"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.42-dev"
                }
            },
            "autoload": {
                "psr-0": {
                    "Twig_": "lib/"
                },
                "psr-4": {
                    "Twig\\": "src/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Fabien Potencier",
                    "email": "fabien@symfony.com",
                    "homepage":
"http://fabien.potencier.org",
                    "role": "Lead Developer"
                },
                {
                    "name": "Twig Team",
                    "role": "Contributors"
                },
                {
                    "name": "Armin Ronacher",
                    "email":
"armin.ronacher@active-4.com",
                    "role": "Project Founder"
                }
            ],
            "description": "Twig, the flexible, fast, and
secure template language for PHP",
            "homepage": "https://twig.symfony.com",
            "keywords": [
                "templating"
            ],
            "support": {
                "issues":
"https://github.com/twigphp/Twig/issues",
                "source":
"https://github.com/twigphp/Twig/tree/1.x"
            },
            "time": "2020-02-11T05:59:23+00:00"
        }
    ],
    "packages-dev": [
        {
            "name": "phpunit/php-code-coverage",
            "version": "1.2.18",
            "source": {
                "type": "git",
                "url":
"https://github.com/sebastianbergmann/php-code-coverage.git",
                "reference":
"fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b",
                "reference":
"fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.3",
                "phpunit/php-file-iterator":
">=1.3.0@stable",
                "phpunit/php-text-template":
">=1.2.0@stable",
                "phpunit/php-token-stream":
">=1.1.3,<1.3.0"
            },
            "require-dev": {
                "phpunit/phpunit": "3.7.*@dev"
            },
            "suggest": {
                "ext-dom": "*",
                "ext-xdebug": ">=2.0.5"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.2.x-dev"
                }
            },
            "autoload": {
                "classmap": [
                    "PHP/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "include-path": [
                ""
            ],
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Sebastian Bergmann",
                    "email":
"sb@sebastian-bergmann.de",
                    "role": "lead"
                }
            ],
            "description": "Library that provides
collection, processing, and rendering functionality for PHP code coverage
information.",
            "homepage":
"https://github.com/sebastianbergmann/php-code-coverage",
            "keywords": [
                "coverage",
                "testing",
                "xunit"
            ],
            "support": {
                "irc":
"irc://irc.freenode.net/phpunit",
                "issues":
"https://github.com/sebastianbergmann/php-code-coverage/issues",
                "source":
"https://github.com/sebastianbergmann/php-code-coverage/tree/1.2.18"
            },
            "time": "2014-09-02T10:13:14+00:00"
        },
        {
            "name": "phpunit/php-file-iterator",
            "version": "1.4.5",
            "source": {
                "type": "git",
                "url":
"https://github.com/sebastianbergmann/php-file-iterator.git",
                "reference":
"730b01bc3e867237eaac355e06a36b85dd93a8b4"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
                "reference":
"730b01bc3e867237eaac355e06a36b85dd93a8b4",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.3"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.4.x-dev"
                }
            },
            "autoload": {
                "classmap": [
                    "src/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Sebastian Bergmann",
                    "email":
"sb@sebastian-bergmann.de",
                    "role": "lead"
                }
            ],
            "description": "FilterIterator implementation
that filters files based on a list of suffixes.",
            "homepage":
"https://github.com/sebastianbergmann/php-file-iterator/",
            "keywords": [
                "filesystem",
                "iterator"
            ],
            "support": {
                "irc":
"irc://irc.freenode.net/phpunit",
                "issues":
"https://github.com/sebastianbergmann/php-file-iterator/issues",
                "source":
"https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5"
            },
            "time": "2017-11-27T13:52:08+00:00"
        },
        {
            "name": "phpunit/php-text-template",
            "version": "1.2.1",
            "source": {
                "type": "git",
                "url":
"https://github.com/sebastianbergmann/php-text-template.git",
                "reference":
"31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
                "reference":
"31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.3"
            },
            "type": "library",
            "autoload": {
                "classmap": [
                    "src/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Sebastian Bergmann",
                    "email": "sebastian@phpunit.de",
                    "role": "lead"
                }
            ],
            "description": "Simple template engine.",
            "homepage":
"https://github.com/sebastianbergmann/php-text-template/",
            "keywords": [
                "template"
            ],
            "support": {
                "issues":
"https://github.com/sebastianbergmann/php-text-template/issues",
                "source":
"https://github.com/sebastianbergmann/php-text-template/tree/1.2.1"
            },
            "time": "2015-06-21T13:50:34+00:00"
        },
        {
            "name": "phpunit/php-timer",
            "version": "1.0.9",
            "source": {
                "type": "git",
                "url":
"https://github.com/sebastianbergmann/php-timer.git",
                "reference":
"3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
                "reference":
"3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
                "shasum": ""
            },
            "require": {
                "php": "^5.3.3 || ^7.0"
            },
            "require-dev": {
                "phpunit/phpunit": "^4.8.35 || ^5.7 ||
^6.0"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.0-dev"
                }
            },
            "autoload": {
                "classmap": [
                    "src/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Sebastian Bergmann",
                    "email":
"sb@sebastian-bergmann.de",
                    "role": "lead"
                }
            ],
            "description": "Utility class for timing",
            "homepage":
"https://github.com/sebastianbergmann/php-timer/",
            "keywords": [
                "timer"
            ],
            "support": {
                "issues":
"https://github.com/sebastianbergmann/php-timer/issues",
                "source":
"https://github.com/sebastianbergmann/php-timer/tree/master"
            },
            "time": "2017-02-26T11:10:40+00:00"
        },
        {
            "name": "phpunit/php-token-stream",
            "version": "1.2.2",
            "source": {
                "type": "git",
                "url":
"https://github.com/sebastianbergmann/php-token-stream.git",
                "reference":
"ad4e1e23ae01b483c16f600ff1bebec184588e32"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32",
                "reference":
"ad4e1e23ae01b483c16f600ff1bebec184588e32",
                "shasum": ""
            },
            "require": {
                "ext-tokenizer": "*",
                "php": ">=5.3.3"
            },
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.2-dev"
                }
            },
            "autoload": {
                "classmap": [
                    "PHP/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "include-path": [
                ""
            ],
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Sebastian Bergmann",
                    "email":
"sb@sebastian-bergmann.de",
                    "role": "lead"
                }
            ],
            "description": "Wrapper around PHP's
tokenizer extension.",
            "homepage":
"https://github.com/sebastianbergmann/php-token-stream/",
            "keywords": [
                "tokenizer"
            ],
            "support": {
                "irc":
"irc://irc.freenode.net/phpunit",
                "issues":
"https://github.com/sebastianbergmann/php-token-stream/issues",
                "source":
"https://github.com/sebastianbergmann/php-token-stream/tree/1.2.2"
            },
            "abandoned": true,
            "time": "2014-03-03T05:10:30+00:00"
        },
        {
            "name": "phpunit/phpunit",
            "version": "3.7.38",
            "source": {
                "type": "git",
                "url":
"https://github.com/sebastianbergmann/phpunit.git",
                "reference":
"38709dc22d519a3d1be46849868aa2ddf822bcf6"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38709dc22d519a3d1be46849868aa2ddf822bcf6",
                "reference":
"38709dc22d519a3d1be46849868aa2ddf822bcf6",
                "shasum": ""
            },
            "require": {
                "ext-ctype": "*",
                "ext-dom": "*",
                "ext-json": "*",
                "ext-pcre": "*",
                "ext-reflection": "*",
                "ext-spl": "*",
                "php": ">=5.3.3",
                "phpunit/php-code-coverage": "~1.2",
                "phpunit/php-file-iterator": "~1.3",
                "phpunit/php-text-template": "~1.1",
                "phpunit/php-timer": "~1.0",
                "phpunit/phpunit-mock-objects": "~1.2",
                "symfony/yaml": "~2.0"
            },
            "require-dev": {
                "pear-pear.php.net/pear": "1.9.4"
            },
            "suggest": {
                "phpunit/php-invoker": "~1.1"
            },
            "bin": [
                "composer/bin/phpunit"
            ],
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "3.7.x-dev"
                }
            },
            "autoload": {
                "classmap": [
                    "PHPUnit/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "include-path": [
                "",
                "../../symfony/yaml/"
            ],
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Sebastian Bergmann",
                    "email": "sebastian@phpunit.de",
                    "role": "lead"
                }
            ],
            "description": "The PHP Unit Testing
framework.",
            "homepage": "http://www.phpunit.de/",
            "keywords": [
                "phpunit",
                "testing",
                "xunit"
            ],
            "support": {
                "irc":
"irc://irc.freenode.net/phpunit",
                "issues":
"https://github.com/sebastianbergmann/phpunit/issues",
                "source":
"https://github.com/sebastianbergmann/phpunit/tree/3.7"
            },
            "time": "2014-10-17T09:04:17+00:00"
        },
        {
            "name": "phpunit/phpunit-mock-objects",
            "version": "1.2.3",
            "source": {
                "type": "git",
                "url":
"https://github.com/sebastianbergmann/phpunit-mock-objects.git",
                "reference":
"5794e3c5c5ba0fb037b11d8151add2a07fa82875"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875",
                "reference":
"5794e3c5c5ba0fb037b11d8151add2a07fa82875",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.3",
                "phpunit/php-text-template":
">=1.1.1@stable"
            },
            "suggest": {
                "ext-soap": "*"
            },
            "type": "library",
            "autoload": {
                "classmap": [
                    "PHPUnit/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "include-path": [
                ""
            ],
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Sebastian Bergmann",
                    "email":
"sb@sebastian-bergmann.de",
                    "role": "lead"
                }
            ],
            "description": "Mock Object library for
PHPUnit",
            "homepage":
"https://github.com/sebastianbergmann/phpunit-mock-objects/",
            "keywords": [
                "mock",
                "xunit"
            ],
            "support": {
                "irc":
"irc://irc.freenode.net/phpunit",
                "issues":
"https://github.com/sebastianbergmann/phpunit-mock-objects/issues",
                "source":
"https://github.com/sebastianbergmann/phpunit-mock-objects/tree/1.2.3"
            },
            "abandoned": true,
            "time": "2013-01-13T10:24:48+00:00"
        }
    ],
    "aliases": [],
    "minimum-stability": "stable",
    "stability-flags": [],
    "prefer-stable": false,
    "prefer-lowest": false,
    "platform": {
        "php": ">=5.5.9"
    },
    "platform-dev": [],
    "platform-overrides": {
        "php": "5.5.9"
    },
    "plugin-api-version": "2.0.0"
}
Loader.php000064400000001367151166614520006501 0ustar00<?php

/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry5;

abstract class Loader
{
    public static function setup()
    {
        self::get();
    }

    /**
     * @return mixed
     */
    public static function get()
    {
        static $loader;

        if (!$loader) {
            require_once __DIR__ . '/RealLoader.php';
            $loader = RealLoader::getClassLoader();
        }

        return $loader;
    }
}
MD5SUMS000064400000165776151166614520005621
0ustar00vendor/rockettheme/toolbox/ResourceLocator/src/UniformResourceIterator.php	8425750b905dcd7e08da95f1d7901a90
vendor/rockettheme/toolbox/ResourceLocator/src/UniformResourceLocator.php	17d5021c8dd8905a63dcdeeedd307d3a
vendor/rockettheme/toolbox/ResourceLocator/src/RecursiveUniformResourceIterator.php	267434b9de11ac00f5796e6d65055a81
vendor/rockettheme/toolbox/ResourceLocator/src/ResourceLocatorInterface.php	88c18b6ba9bbb10e14af755fa164da21
vendor/rockettheme/toolbox/composer.json	f89585e67ab10392f78cb43fdcb558b3
vendor/rockettheme/toolbox/File/src/File.php	cd9ec903953af5e03ab6296d81047247
vendor/rockettheme/toolbox/File/src/MoFile.php	fca92b606792b6d9829bc288f2d2e969
vendor/rockettheme/toolbox/File/src/FileInterface.php	388204852baee6fb82f3d79cfa6f6078
vendor/rockettheme/toolbox/File/src/IniFile.php	0db28506d60bb93d2da2954e7165cb26
vendor/rockettheme/toolbox/File/src/PhpFile.php	71da92c6d301692320824582d7b77236
vendor/rockettheme/toolbox/File/src/JsonFile.php	7f24d036362a0db48c3dc6ecdad5c04e
vendor/rockettheme/toolbox/File/src/MarkdownFile.php	b568d2f7f7259ecab44a12bdb240323d
vendor/rockettheme/toolbox/File/src/LogFile.php	b232545d74c89ddc4ead96ac4602172b
vendor/rockettheme/toolbox/File/src/YamlFile.php	4b9da488a8e175c8182cff4be0d32127
vendor/rockettheme/toolbox/Session/src/Session.php	51837671eab82fbba1e7790b7ad52dc1
vendor/rockettheme/toolbox/Session/src/Message.php	318e6c4e8453ba2929d2bc76bf02196e
vendor/rockettheme/toolbox/Blueprints/src/BlueprintSchema.php	54be55c0beec7d92fb9e28cb541407a4
vendor/rockettheme/toolbox/Blueprints/src/Blueprints.php	0dcc792b213241c38ca8af45b3ca0c38
vendor/rockettheme/toolbox/Blueprints/src/BlueprintForm.php	b2b397a773f4cb29d0d5c2329ccc23c1
vendor/rockettheme/toolbox/DI/src/Container.php	cac7a4bf0f6c0e393fbd777d845681d9
vendor/rockettheme/toolbox/DI/src/ServiceProviderInterface.php	95b4daf6165a071147f5a621c61c53f3
vendor/rockettheme/toolbox/ArrayTraits/src/Iterator.php	dfa2dee2acdc645c70bd1ad2f0769e2a
vendor/rockettheme/toolbox/ArrayTraits/src/Constructor.php	6a975da179d99e17cc7e49b00102a718
vendor/rockettheme/toolbox/ArrayTraits/src/ArrayAccessWithGetters.php	32b274f266b1c98f221e13a42b111f5d
vendor/rockettheme/toolbox/ArrayTraits/src/NestedArrayAccess.php	c8cb0a80e969fe64319101649a95bed6
vendor/rockettheme/toolbox/ArrayTraits/src/NestedArrayAccessWithGetters.php	f3044ab6b551163458d04b5349f554bc
vendor/rockettheme/toolbox/ArrayTraits/src/Countable.php	a1002148c4cc06f3b6f4bcc807b5bf8b
vendor/rockettheme/toolbox/ArrayTraits/src/Serializable.php	108604ea685eda1cc7339fffdb506e4c
vendor/rockettheme/toolbox/ArrayTraits/src/ExportInterface.php	85a427fb236c257856cc290f3db51c0f
vendor/rockettheme/toolbox/ArrayTraits/src/Export.php	ab0ea60169527f36056c4f5ffffa76f8
vendor/rockettheme/toolbox/ArrayTraits/src/ArrayAccess.php	70046c4821eac9dde65dc31fdf997492
vendor/rockettheme/toolbox/StreamWrapper/src/ReadOnlyStream.php	be543485cce9d434e4d2036e0c39031a
vendor/rockettheme/toolbox/StreamWrapper/src/Stream.php	def0c957518fe03329a10ac7909a8ffa
vendor/rockettheme/toolbox/StreamWrapper/src/StreamBuilder.php	c4f109a9ded5165126c2f8ba7ea2d90b
vendor/rockettheme/toolbox/StreamWrapper/src/StreamInterface.php	104a1418675c761c7039f4ed54c11c63
vendor/rockettheme/toolbox/Compat/src/Yaml/Parser.php	0b9b4ab5393b0abcac1a6cf91c8c8d33
vendor/rockettheme/toolbox/Compat/src/Yaml/Exception/ParseException.php	2794bd6de9a601e7515200cea7786707
vendor/rockettheme/toolbox/Compat/src/Yaml/Exception/ExceptionInterface.php	0c51a689d978fbe1c2913939752821ba
vendor/rockettheme/toolbox/Compat/src/Yaml/Exception/RuntimeException.php	8b35bef00c5d97daeb0574058b42a27d
vendor/rockettheme/toolbox/Compat/src/Yaml/Inline.php	5cfd9bf3fdf66b4d01db6e3ca5f9d995
vendor/rockettheme/toolbox/Compat/src/Yaml/Unescaper.php	07b339b2698360defdb6ba28efcea679
vendor/rockettheme/toolbox/Compat/src/Yaml/Yaml.php	de7a8a3f84d2cc2395ca215f777b6e8a
vendor/rockettheme/toolbox/Event/src/EventSubscriberInterface.php	deee6fce3aae142d1e4129f4573e310a
vendor/rockettheme/toolbox/Event/src/Event.php	a28e4af27f18a1e906aaa70c2aadf8a8
vendor/rockettheme/toolbox/Event/src/EventDispatcher.php	d8f71272daf15a98dec45d859193e045
vendor/psr/container/composer.json	c2397ca596d172818c606be79a5e3532
vendor/psr/container/src/ContainerExceptionInterface.php	d7a4fda88b943f658e13f7f5aa3e85f1
vendor/psr/container/src/ContainerInterface.php	d459395043420a1cfde26c94500f5b66
vendor/psr/container/src/NotFoundExceptionInterface.php	8fc6d3d2099bf1fd8d03a3273157c8d3
vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php	a653a140fb81bf4c37da14a60c2ad1d7
vendor/psr/log/Psr/Log/Test/DummyTest.php	52cb71ea2645f556b4e1e519d1c78a76
vendor/psr/log/Psr/Log/Test/TestLogger.php	885f63b13ecb1ab70b3da51573770ef4
vendor/psr/log/Psr/Log/AbstractLogger.php	26eb607e5318188016615326bd89a9be
vendor/psr/log/Psr/Log/LoggerInterface.php	460689f292a11ebce586ef066313dd5d
vendor/psr/log/Psr/Log/NullLogger.php	0cfdfa8d81e5b22c24c96ae7014213c0
vendor/psr/log/Psr/Log/LoggerAwareTrait.php	221f47ac7d3da4800d2c0e26cdfb351b
vendor/psr/log/Psr/Log/LoggerTrait.php	80438cede9b432c45cd59838fc3080af
vendor/psr/log/Psr/Log/LoggerAwareInterface.php	cfac6d4dc3ebf2c7f0e49f74d1bcd44a
vendor/psr/log/Psr/Log/LogLevel.php	cc226142fd5d390d030b39c61cf97843
vendor/psr/log/Psr/Log/InvalidArgumentException.php	7d2f0bd1583524d739fff12f0507de65
vendor/psr/log/composer.json	770ed2c20c6b6665a84464a5249bd949
vendor/symfony/polyfill-ctype/composer.json	f29439f5875e3dec0fe0c315697c9d4f
vendor/symfony/polyfill-ctype/Ctype.php	72cb4d141eac1392c3de359dba4d44e0
vendor/symfony/polyfill-ctype/bootstrap.php	1db5658a127d172b16c69d52ccb1e2c3
vendor/symfony/event-dispatcher/composer.json	d7270613fa19777931d0d2eddb562c89
vendor/symfony/event-dispatcher/EventSubscriberInterface.php	d4c1feefee64b854cc9a0c01e39bfa64
vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php	eb265310d6d9cf71d940cfe2aca08463
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php	aa09cc5a555cfad4239045906e2850b0
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php	fad536cee771596d633e94dc75043b72
vendor/symfony/event-dispatcher/Debug/WrappedListener.php	9ed9324bcbecd82f07950ccbc7cc87d9
vendor/symfony/event-dispatcher/GenericEvent.php	2f62d765d811eda158ea011125abf774
vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php	df71d3c551163a03d68a33eb9f72f483
vendor/symfony/event-dispatcher/Event.php	824a898479b083a50450a1b5e6b80b4a
vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php	df72f94abae6b2dbc6b75ce3e24c3568
vendor/symfony/event-dispatcher/EventDispatcherInterface.php	86e0183d0e63915029c8878ed747e4f1
vendor/symfony/event-dispatcher/EventDispatcher.php	4a8774ed7dbccaf556fd17eb66915c71
vendor/symfony/yaml/composer.json	d000bae2c9b7d463324910aed788996b
vendor/symfony/yaml/Parser.php	c9d6556834f295d8371277848ffe56e2
vendor/symfony/yaml/Escaper.php	21458379ea80c0095597a7b14777cb73
vendor/symfony/yaml/Exception/ParseException.php	ba83e463c16a0e1668cf0e805e725d15
vendor/symfony/yaml/Exception/ExceptionInterface.php	55d4a87d030efc9426685250882fdf25
vendor/symfony/yaml/Exception/RuntimeException.php	14010c4f6f5c12606dd5e00ef5855fdc
vendor/symfony/yaml/Exception/DumpException.php	dc67a59a9101373a0a0aab4d1a37406b
vendor/symfony/yaml/Inline.php	38c3843dbe14eff3b8a98405bf65e0a8
vendor/symfony/yaml/Unescaper.php	40396d53f0098edcde3f9af23027517d
vendor/symfony/yaml/Yaml.php	78a6da237baf9ac7c901bf2f6fac63f9
vendor/symfony/yaml/Dumper.php	04604771d1e476d186195c147801f305
vendor/leafo/scssphp/composer.json	417f56149ef8d4862a8c8c0a45ddf60f
vendor/leafo/scssphp/src/Node/Number.php	195ce552a436bdb291352ef3e4cd4a78
vendor/leafo/scssphp/src/Type.php	abfbe75324d42bc7b130a4ec431a624f
vendor/leafo/scssphp/src/Block.php	4c43bcd075e2ffcacb5c491c37c27046
vendor/leafo/scssphp/src/Compiler.php	019b48d70e2fa55e02e5b8d06ee0e349
vendor/leafo/scssphp/src/Version.php	2ac2d30bbd8e245ee593afb6467f09e4
vendor/leafo/scssphp/src/Parser.php	b12cd546d81b152a727a7922645fdcf7
vendor/leafo/scssphp/src/Colors.php	295c55df99b2191b81c6da7448e9af52
vendor/leafo/scssphp/src/Base/Range.php	2634b9665da681fb1e50420ea9702c3c
vendor/leafo/scssphp/src/Util.php	5710340220e864fb853dfd473f9df9f4
vendor/leafo/scssphp/src/Formatter.php	0f1cd07cf367132dd13a859142022505
vendor/leafo/scssphp/src/Exception/ServerException.php	4e00d8cfb7e3a6dbf05dffaa867e1fe6
vendor/leafo/scssphp/src/Exception/ParserException.php	cc518ee76b91ce09ab392a11f714294c
vendor/leafo/scssphp/src/Exception/CompilerException.php	17fd8bfd2a370441aa4c2e425f59c07b
vendor/leafo/scssphp/src/Exception/RangeException.php	9ca291b8c5a0caedbc86e23063953924
vendor/leafo/scssphp/src/Compiler/Environment.php	b9c4b402b28c13b43ca44c2670cf71ab
vendor/leafo/scssphp/src/Formatter/Expanded.php	d09155914135a92c2cfbf1388224f01d
vendor/leafo/scssphp/src/Formatter/Compact.php	e24f5d6a2bbadcc54335050dffce25d7
vendor/leafo/scssphp/src/Formatter/Crunched.php	afb9516f4492d1a9e0defd939f002549
vendor/leafo/scssphp/src/Formatter/OutputBlock.php	994794d71e821cdf744ecaa4f28ad283
vendor/leafo/scssphp/src/Formatter/Nested.php	2e98c4fe5275478f6d7aef36debe0740
vendor/leafo/scssphp/src/Formatter/Debug.php	fbc41ade7b0d5430b56318ee8a131cff
vendor/leafo/scssphp/src/Formatter/Compressed.php	c47dc6a42b36a305fa83b6d4097beb28
vendor/leafo/scssphp/src/SourceMap/Base64.php	40ab6332ab6b0d77fc15319986201421
vendor/leafo/scssphp/src/SourceMap/Base64VLQEncoder.php	0298a9e3eba41a1db4308bb6d5ac9c24
vendor/leafo/scssphp/src/SourceMap/Base64VLQ.php	484ef560dbbd472d8239cec0c0bdd559
vendor/leafo/scssphp/src/SourceMap/SourceMapGenerator.php	ee95e4d200a3ed65366455b12f872353
vendor/leafo/scssphp/src/Node.php	f1b586d8f17cd18ba23038802b25eae3
vendor/leafo/scssphp/src/Cache.php	e21c46a866443e04b3144d6e11f4cff9
vendor/leafo/scssphp/example/Server.php	dedf297185fc87468ebc8a2656947907
vendor/leafo/scssphp/scss.inc.php	813af81fe77b469f37fe77eeaa0d3d35
vendor/composer/installed.php	c922f63b23a44d981f2c5a28bde36ba6
vendor/composer/autoload_classmap.php	19b09a79c57450ba3e5261ad53f4402d
vendor/composer/InstalledVersions.php	69a48729834fad5e8b274b651f9f1c44
vendor/composer/autoload_static.php	88942d3b3262a256dbe15c6f63212bec
vendor/composer/ClassLoader.php	ad1f263da6c61ea82c172393b477eae7
vendor/composer/platform_check.php	c47686309128dfb31536974619fcc396
vendor/composer/autoload_psr4.php	dff0396c1110fe68072fbcd926593407
vendor/composer/installed.json	5287d6aad045a3c5f7cec3486d3e4698
vendor/composer/autoload_files.php	6c4d89647de36d487eb4df5a15811ca2
vendor/composer/autoload_real.php	7e674eeba79b8bc47b38cfb57232be48
vendor/composer/autoload_namespaces.php	79e8fc8b53221d087196058611b50106
vendor/autoload.php	dc3cdcd10448cf48c73dc86777d842e9
vendor/twig/twig/composer.json	e2e110c800452730273ac1364050cc70
vendor/twig/twig/lib/Twig/Test/Method.php	2d036635d5b69759deb499e65cd9cb53
vendor/twig/twig/lib/Twig/Test/Function.php	70a2e2f9d4a0ab7e081d0168534374f7
vendor/twig/twig/lib/Twig/Test/NodeTestCase.php	49a86ec5804b1efc15b36ae6921406f3
vendor/twig/twig/lib/Twig/Test/Node.php	73396c65865edd730253eb6628371caf
vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php	253cb1313bdcbc5679a3a7fc412fdfea
vendor/twig/twig/lib/Twig/Node/Expression.php	02ec7a8e9711e15867449575ede9f62c
vendor/twig/twig/lib/Twig/Node/AutoEscape.php	567440282e90ad48390bbc38760e73e4
vendor/twig/twig/lib/Twig/Node/Set.php	ee6843b1c44064f861b52bc2c8a4e9ea
vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php	0841b13bf17f7b63e8bb3035ab853d49
vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php	38fc779e9c5980ba7f94a2849dd5324f
vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php	7be826a6a6075075f25093008418431e
vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php	ddf5b605c4e2a73aeb733db9a94b2ec8
vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php	cd17f8493b63f5b89dfe547596ce71f4
vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php	a9150247da6362e0a59803119ef8a957
vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php	f7ba0d4e9fea1f9f0901d57a35adf22a
vendor/twig/twig/lib/Twig/Node/Expression/Array.php	acca240be16008563b01161a6a922592
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php	e288666b50d25c9ca715e28e7efe2246
vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php	a42e3600f6ee4b9015146f146407de1a
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php	0098bfbf0fcfcb1dcf7c377e106a3535
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php	cb87841e24a810cf136c6b406a115974
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php	49bb447d9e19c4ffe3febf89bd425896
vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php	8026dc07f391b027960a24f0028bf4e0
vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php	c96dc2e1b4a97f56d40c7017242dd3a8
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php	2ab4af71cc11116201a8c2f85a109f5b
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php	297075c07489ad267ac9e036252de819
vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php	ae3ba349dcf1cb2f7a4b5b206703b480
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php	631726763384ac53411e7a8ab0379be6
vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php	e4bd5c0f836e5287ec2dff90d0134c97
vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php	dee985d04916ec39044cc3208cc3ff27
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php	d916b824e012ed7d252a5492d9fea98c
vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php	f61f1ed73ffa40b38f9cca63a20cb587
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php	3911fba4609bf376ed8cf8ab7cbb783e
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php	08054e7515875543c026e70a260503d2
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php	6ab46e39a69bf3dfd478f682b8e72119
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php	740b30dafbbd84abb3c4bf84237bbff1
vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php	aad02a9dcb4a9b961a14cfd9a395840c
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php	bf16aa555475333ead541693f891f31f
vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php	02f14987dc37f3695153f6b6d05280b8
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php	37a6c0d27da41f67c71d37c9522fc3ab
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php	5fe25024048cf2f6f40ef1201c482033
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php	b058fc597486fcc70186a058cbe8008c
vendor/twig/twig/lib/Twig/Node/Expression/Binary.php	c56d590a0b11cb204c27965802f542a3
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php	d1e1bd8bb4e790fd89d212607c6214f0
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php	82b2f2f30567e3189d54c03c9b4f1ca4
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php	29417f636626e6afe122482bd58f77c6
vendor/twig/twig/lib/Twig/Node/Expression/TempName.php	46961e0750fca62408ed5a3c37d98d87
vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php	8610ae5fcc509a77c0d8a1f34076863b
vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php	8bc235ac05cb6555da8b1578cfce1202
vendor/twig/twig/lib/Twig/Node/Expression/Constant.php	1799976844e1d589e8b42144040015fa
vendor/twig/twig/lib/Twig/Node/Expression/Filter.php	c6895989d8ebaa867d86738f02cac94c
vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php	8b14d518a26c60fd5b63cebfdc72dcb0
vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php	d0c94f835d7aa723b332cde44dd4c911
vendor/twig/twig/lib/Twig/Node/Expression/Unary.php	42f8324645ca23fcffab442ef2205c4e
vendor/twig/twig/lib/Twig/Node/Expression/Function.php	40e182f31e72a824417ec353538b0f5b
vendor/twig/twig/lib/Twig/Node/Expression/Test.php	2f251e2ec130563384764b4728f924c1
vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php	1c85c9566478e9454b836df8afdb49ca
vendor/twig/twig/lib/Twig/Node/Expression/Parent.php	32794ff97e17f73dc017f16994e70b8d
vendor/twig/twig/lib/Twig/Node/Expression/Call.php	2afc9b891fea3ecef2133b1184a7e060
vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php	ac7f8286538e20cefafc9eeb376c653f
vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php	b23bacc7daa066580bafbb184ed3c30f
vendor/twig/twig/lib/Twig/Node/Expression/Name.php	14fef2e2519c6465afbe540252fd983c
vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php	54c0d3c943d3e6f224234669bbaa076d
vendor/twig/twig/lib/Twig/Node/Text.php	2ceac17b4de1dd2f2a4c528550fb9b83
vendor/twig/twig/lib/Twig/Node/Module.php	3a4cea9d80361e47c146b3956b713e06
vendor/twig/twig/lib/Twig/Node/Block.php	f8ba844823cd01212ff4c2eeb4bd5173
vendor/twig/twig/lib/Twig/Node/Include.php	88d8a4b5ba6e88c695f38c95addca309
vendor/twig/twig/lib/Twig/Node/Macro.php	3c43a99961404047611ec1bfbdb2bc20
vendor/twig/twig/lib/Twig/Node/With.php	16b1d072dc99c58079cf148f66391546
vendor/twig/twig/lib/Twig/Node/Deprecated.php	1781466f91c8ff87c35027deaf4067c8
vendor/twig/twig/lib/Twig/Node/Do.php	871cddc4995f464d1595902ebe13a09b
vendor/twig/twig/lib/Twig/Node/Print.php	72cda7dba065a6b014ad6ef7dc81c404
vendor/twig/twig/lib/Twig/Node/Body.php	f0f8df41745713e81dfaa75cc9265a3a
vendor/twig/twig/lib/Twig/Node/Import.php	7b73350a4337407e4d6483ffcaab6f72
vendor/twig/twig/lib/Twig/Node/If.php	f877f342873179aba2901b88367e9d63
vendor/twig/twig/lib/Twig/Node/SetTemp.php	0a4391961609db681888f3f62c8cb9e8
vendor/twig/twig/lib/Twig/Node/Embed.php	72cab50fd3878b3384249f1bf950b3de
vendor/twig/twig/lib/Twig/Node/Flush.php	ca578f22204072bb6784755eda23058c
vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php	f9c9f7b960f2ff2025785f1ac3e26d6b
vendor/twig/twig/lib/Twig/Node/For.php	d8f7034152c131f5343f7df3e7c90b02
vendor/twig/twig/lib/Twig/Node/Spaceless.php	c951b7a8810c22d79630239725087bba
vendor/twig/twig/lib/Twig/Node/BlockReference.php	22705486f37e7e25ed2a0fcd9dd76205
vendor/twig/twig/lib/Twig/Node/ForLoop.php	e3a85fbc1a53256d72e73a47cc8cc285
vendor/twig/twig/lib/Twig/Node/Sandbox.php	314467c75e7bb20a8c03b0afb311e21e
vendor/twig/twig/lib/Twig/Node/CheckSecurity.php	e58ad9e711d95c58724e3f460f2ec785
vendor/twig/twig/lib/Twig/Util/TemplateDirIterator.php	77b59f8d0098cde86db5c2b5fdf6634e
vendor/twig/twig/lib/Twig/Util/DeprecationCollector.php	841670da4d34353fde12664debad9aa9
vendor/twig/twig/lib/Twig/FunctionInterface.php	3da376a63add0412aacb80e4287369fc
vendor/twig/twig/lib/Twig/Lexer.php	bdbb2b34e421a070b404ed446dae5da4
vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php	e1e629fbb3094426dd875e1c58baf659
vendor/twig/twig/lib/Twig/Loader/Array.php	d74371345bd83298f7a3bda6a33d12fc
vendor/twig/twig/lib/Twig/Loader/Filesystem.php	c1d01790d034d65d53df3095be33a3d3
vendor/twig/twig/lib/Twig/Loader/Chain.php	f054bbbb376b6222645132a4104bde38
vendor/twig/twig/lib/Twig/Loader/String.php	13f5ccb47bba6bf7c9d68b3ae390cc73
vendor/twig/twig/lib/Twig/LexerInterface.php	64bfc41b1fe5c31654465feda90cc872
vendor/twig/twig/lib/Twig/CacheInterface.php	b7b72185b7e1d7725a04f59c7dd7aedc
vendor/twig/twig/lib/Twig/FilterInterface.php	4d1ee1bc4f07665ad94fd7fca9082352
vendor/twig/twig/lib/Twig/RuntimeLoaderInterface.php	9fc74752e3ccc9765210d1fba2ddc0d9
vendor/twig/twig/lib/Twig/Compiler.php	85db6f2fc0eac55f14a1e4c8fa9a241e
vendor/twig/twig/lib/Twig/Environment.php	f641a8e9b4eb0405ce2d416759bb6c6a
vendor/twig/twig/lib/Twig/Filter.php	02f93c5a401fcdfe12aadbfb705405cf
vendor/twig/twig/lib/Twig/SimpleFunction.php	65a3dd4008d980d70b15107352687a58
vendor/twig/twig/lib/Twig/ExtensionInterface.php	993c47854290a867168ee45e090ea539
vendor/twig/twig/lib/Twig/TokenParserInterface.php	acd4e709ef52b069abb88593d163e65f
vendor/twig/twig/lib/Twig/FactoryRuntimeLoader.php	965a0baf7212fef6ea2e800237c4cf13
vendor/twig/twig/lib/Twig/Markup.php	34d62ee5481c94ec179f183c7e33164e
vendor/twig/twig/lib/Twig/Parser.php	2280a4f28af7447163155cbb59547c01
vendor/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php	7897962ea6cece13730b91dc26c5d8ed
vendor/twig/twig/lib/Twig/Error/Syntax.php	caf5f775a5d61062126041b079c747ab
vendor/twig/twig/lib/Twig/Error/Loader.php	eb99e644d8d69985551f57392d40de2c
vendor/twig/twig/lib/Twig/Error/Runtime.php	0c5e927235bf5d8a0f208f4de1c9c38a
vendor/twig/twig/lib/Twig/TokenParser/Extends.php	d8cf415bbca37de06b67154c5430cea8
vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php	5bc7d481acf4dd38985c162ab4cae027
vendor/twig/twig/lib/Twig/TokenParser/Set.php	be1b06306361964abb2660eccf3f3156
vendor/twig/twig/lib/Twig/TokenParser/Block.php	2cc395683206cb660f3db54d6b87fecb
vendor/twig/twig/lib/Twig/TokenParser/Include.php	cf774d3b6c9eadd7711fa2df720f7a34
vendor/twig/twig/lib/Twig/TokenParser/Macro.php	16ab3b0ea57429059dcc3cc76d28edff
vendor/twig/twig/lib/Twig/TokenParser/With.php	28a7098fc100768007970b2041682280
vendor/twig/twig/lib/Twig/TokenParser/Deprecated.php	6f9be7f865b86fd55a14b188d2b61f5d
vendor/twig/twig/lib/Twig/TokenParser/Filter.php	68da51661238d0198f0ce9977898e4d6
vendor/twig/twig/lib/Twig/TokenParser/Do.php	a024ef320e3269eadfdd1e2b5dc05fa4
vendor/twig/twig/lib/Twig/TokenParser/Import.php	f2ccc63d6071dd3731f846fc0b654bcb
vendor/twig/twig/lib/Twig/TokenParser/Use.php	fd71c20b76449c6efa228a4a67b44290
vendor/twig/twig/lib/Twig/TokenParser/If.php	0270a02fbe4ea7e03b89f58974012d15
vendor/twig/twig/lib/Twig/TokenParser/Embed.php	1c313a3c27b20a3630982a84c5b19988
vendor/twig/twig/lib/Twig/TokenParser/Flush.php	a239d8a52f738bfc5c18e3011cb7b2fc
vendor/twig/twig/lib/Twig/TokenParser/From.php	7004cf5adb5746b3d47fe329fb679e61
vendor/twig/twig/lib/Twig/TokenParser/For.php	f663d48c14cc064537cc1f4ea4cba112
vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php	b773efd8e7494d6ff2b138c7d8ac472a
vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php	34f1d8640aa365cc0896449d04ab2ca8
vendor/twig/twig/lib/Twig/CompilerInterface.php	f879825a8eb81494fe560951b7315b1d
vendor/twig/twig/lib/Twig/TemplateWrapper.php	6c53c78587248aed3b245aee02686259
vendor/twig/twig/lib/Twig/NodeInterface.php	0eee13147783b69c7abd5b2c1958eac5
vendor/twig/twig/lib/Twig/ContainerRuntimeLoader.php	95d433f3c7de31318ff0e1fd6585c1f0
vendor/twig/twig/lib/Twig/Filter/Method.php	1b9bbcdccd7b605ea1b99a64fc941366
vendor/twig/twig/lib/Twig/Filter/Function.php	c1d586918f181931620ae18a9aca6405
vendor/twig/twig/lib/Twig/Filter/Node.php	e6e9fe4cb145b15c0111ec7700814bc3
vendor/twig/twig/lib/Twig/Autoloader.php	eb9478b25ed1364b5d95d27a0ad45481
vendor/twig/twig/lib/Twig/TestInterface.php	f9dd5060017c46c5a87995d740ef2c27
vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php	c718685eebc9e55023f7efefac0658ab
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php	774c190dd87be83c9ebac19544fa3c38
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedTagError.php	8361f5eeaee1f42ab335dacf0999ead2
vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php	473ddcf4c14aa80c8fbc36842a265bc3
vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php	f01595f594858048d26829875b74a514
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php	4247d0dc4002dc3285d5d7a7831b567e
vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php	8f6906681f3dad4af51b3820fbf8edc6
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php	4230f8c32c9a1d9fc9741523e95d55f4
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php	e5164378ab0e6f7e06aac81ef51ab6c2
vendor/twig/twig/lib/Twig/Function.php	e7f1f51d835a26fdab7536254a2cdab5
vendor/twig/twig/lib/Twig/FunctionCallableInterface.php	3e69f85ce1f6333e1eabaccc20d32faf
vendor/twig/twig/lib/Twig/Extension.php	56158c52565b15eaab10dc772e4e2dbd
vendor/twig/twig/lib/Twig/LoaderInterface.php	17fab939689d57c0359a9fc910eb9f15
vendor/twig/twig/lib/Twig/NodeVisitorInterface.php	55123f74d0421add26460fdb57cb89fe
vendor/twig/twig/lib/Twig/Source.php	b0c94a75545adf1564c6aec5724c8a25
vendor/twig/twig/lib/Twig/BaseNodeVisitor.php	1ed9b177b55a0d479cbe34b63dea580b
vendor/twig/twig/lib/Twig/Test.php	0ecd2cd7e2d45a4e9a98c4e02c098de9
vendor/twig/twig/lib/Twig/Profiler/Node/EnterProfile.php	885e5d61024db413d0e3f2f12db6ba73
vendor/twig/twig/lib/Twig/Profiler/Node/LeaveProfile.php	680a4932ed3f03b6e1de4b036bc63d12
vendor/twig/twig/lib/Twig/Profiler/Dumper/Text.php	d9b5f30f385d94b04e3084322d308765
vendor/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php	de55c6d77ab92900639af3db0720e735
vendor/twig/twig/lib/Twig/Profiler/Dumper/Html.php	0b965d47fd5d38bc78d8d2543c9a1d6e
vendor/twig/twig/lib/Twig/Profiler/Dumper/Base.php	25dbf92c3b4a724b4baa515ff35cffdf
vendor/twig/twig/lib/Twig/Profiler/Profile.php	138ad25c5ab391aab4b316096cd57deb
vendor/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php	1ce8071a411924db93550334b8e1d085
vendor/twig/twig/lib/Twig/ParserInterface.php	4af85028cb2fa68a645350153f9b34ef
vendor/twig/twig/lib/Twig/Template.php	43e5bba4755d56197abd26c6e937417b
vendor/twig/twig/lib/Twig/TokenParserBroker.php	dfe0fb95128967ba0d2822e742310a99
vendor/twig/twig/lib/Twig/TokenStream.php	afb3054b7bfe34c261a20df93807f8c3
vendor/twig/twig/lib/Twig/TestCallableInterface.php	fc239b277f8b479666009cfecf82a42b
vendor/twig/twig/lib/Twig/NodeOutputInterface.php	e89f45756e52660402470855d4288e55
vendor/twig/twig/lib/Twig/SimpleFilter.php	7448c500ff00e371ee4bb980d2536030
vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php	25cb96407499dd198e2a29890823a4ff
vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php	18395988a9e87b16ba82d4a371085067
vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php	044d68e2b7f74a08fba1b5440d7c7fa9
vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php	14d6553aaf462a57822a6cfd945bea52
vendor/twig/twig/lib/Twig/Token.php	c33584305776aa03342690aa0381aa6f
vendor/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php	ba7437ba97d953923ef1e185d643f136
vendor/twig/twig/lib/Twig/Extension/StringLoader.php	d7ad54d0cdd88eed621935468748f76a
vendor/twig/twig/lib/Twig/Extension/Core.php	688edb5b040795ffc1c649949b9020e4
vendor/twig/twig/lib/Twig/Extension/Escaper.php	f5bda38342085f42bf0c409a480a4916
vendor/twig/twig/lib/Twig/Extension/Staging.php	35df3645402559babb0101c51cbc1977
vendor/twig/twig/lib/Twig/Extension/Debug.php	20a45cf8947048e4df447cac84670ad7
vendor/twig/twig/lib/Twig/Extension/Profiler.php	f1fc3dd099f3f9abda52047475a17e06
vendor/twig/twig/lib/Twig/Extension/Optimizer.php	8be7d45230ef827595644df29173f352
vendor/twig/twig/lib/Twig/Extension/GlobalsInterface.php	88cc9e31a42c9b0483a98557e6acee94
vendor/twig/twig/lib/Twig/Extension/Sandbox.php	555caff70bee91ccdbf25284843bb50f
vendor/twig/twig/lib/Twig/FilterCallableInterface.php	111ca315f23d3ae1355c155d1392bcaa
vendor/twig/twig/lib/Twig/SourceContextLoaderInterface.php	f13f494d889b1d42b6aa3360f4cee0f2
vendor/twig/twig/lib/Twig/Error.php	e041f9feb249c738f0fe66d8bddb829d
vendor/twig/twig/lib/Twig/SimpleTest.php	140c0ae0d35491457f251fab039ed014
vendor/twig/twig/lib/Twig/ExpressionParser.php	596a3e70da0d05a703269b1038491a8e
vendor/twig/twig/lib/Twig/Cache/Filesystem.php	27728fef0414b2c8ce6edba11274ad81
vendor/twig/twig/lib/Twig/Cache/Null.php	adf465a2c9d22aa4718b879fd0c0f9fe
vendor/twig/twig/lib/Twig/Node.php	0cd57326fc3e51779ffae9bc156792c0
vendor/twig/twig/lib/Twig/Function/Method.php	73e2b6c6f1ee78bbdcd33f8bf91c64a8
vendor/twig/twig/lib/Twig/Function/Function.php	592b4962a8cb86a39ddfe863b9b0aee3
vendor/twig/twig/lib/Twig/Function/Node.php	cd46095566edba7cc788d1dab5cd4be7
vendor/twig/twig/lib/Twig/NodeCaptureInterface.php	134adf58815fb44f921645e13318c9c6
vendor/twig/twig/lib/Twig/TokenParser.php	c9302daee438f554ad386d08b70d03a9
vendor/twig/twig/lib/Twig/TemplateInterface.php	511694c1bc7af224c35c0433293aec3b
vendor/twig/twig/lib/Twig/NodeTraverser.php	def08b94d6df738637708a70c5602e84
vendor/twig/twig/src/Test/NodeTestCase.php	125f5fd18c36eaa693b752a4d7476daf
vendor/twig/twig/src/Test/IntegrationTestCase.php	e5c7b569e8cc8eadf160d3fa57ba3aa6
vendor/twig/twig/src/Node/BlockNode.php	97ea179a615d5d2c5ff6b90b459377e3
vendor/twig/twig/src/Node/SpacelessNode.php	2d3c8755c73bed018a3ce411a595b1c9
vendor/twig/twig/src/Node/TextNode.php	b73cfa8d24ef5545d6e6c1eeab83913f
vendor/twig/twig/src/Node/Expression/InlinePrint.php	8033af7464e2cb9ae87d6ee343abed2a
vendor/twig/twig/src/Node/Expression/Test/SameasTest.php	351447f267ce109847737db1c7cf79f2
vendor/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php	18f18498f678e2086ef18a155faca801
vendor/twig/twig/src/Node/Expression/Test/ConstantTest.php	fee0676b41473cbc1ac31a8a635af236
vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php	218894e6a1b1e013874782c6325d23bf
vendor/twig/twig/src/Node/Expression/Test/EvenTest.php	b636929a69395d39ba5b4f522abb3bb9
vendor/twig/twig/src/Node/Expression/Test/NullTest.php	942b74b8233afa6e6bb4c4eea456c4ef
vendor/twig/twig/src/Node/Expression/Test/OddTest.php	d29bc1a216c4cc2f003b817e9775bf2e
vendor/twig/twig/src/Node/Expression/GetAttrExpression.php	d1cdd426b423b0d08570573e4880c938
vendor/twig/twig/src/Node/Expression/Binary/InBinary.php	bcda121bdc19e23408b93449c23b807a
vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php	1168e30d8ec8572eac8c5986e4896406
vendor/twig/twig/src/Node/Expression/Binary/GreaterBinary.php	ca99250bde07c3ed8b263a64d3b7f5c6
vendor/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php	ff86e9a1e5a7e327e6f63217036fcf39
vendor/twig/twig/src/Node/Expression/Binary/ModBinary.php	dfe1f12b2a2293b70f71989904a97a34
vendor/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php	3281cc8e2362eea41e92d472ddd56444
vendor/twig/twig/src/Node/Expression/Binary/OrBinary.php	837e3f0772594775c8a141bd7b9a6363
vendor/twig/twig/src/Node/Expression/Binary/BitwiseAndBinary.php	201272eafbdb16269449ce5f20d766a8
vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php	a4c7fff788b02718101f6d0da4aef648
vendor/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php	9350f1cb951cdd2ecde8a3030cfa1d04
vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php	45ff83d84433dc9e2fdfd7683723ed94
vendor/twig/twig/src/Node/Expression/Binary/NotInBinary.php	fa9f33a3009118c94c907d4dbff507e8
vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php	471adb6892da3d726d4e86e2452e241a
vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php	cc2fb53232128df6bf1bee24a40d0722
vendor/twig/twig/src/Node/Expression/Binary/MulBinary.php	699818dae2a4000ad41e6baf7200ca21
vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php	3fed54bc7ff2d7230a9591948c1d0d5a
vendor/twig/twig/src/Node/Expression/Binary/LessBinary.php	eb498d0aba006adae71a654219697f65
vendor/twig/twig/src/Node/Expression/Binary/RangeBinary.php	43cf2ff8a101776586aa901b6c82ec3b
vendor/twig/twig/src/Node/Expression/Binary/SubBinary.php	3300438124f9bd44b7a5d335f5fc8cd1
vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php	6be789a33f824dbb29787784f35df04f
vendor/twig/twig/src/Node/Expression/Binary/MatchesBinary.php	835b52f3e10c37e90fb736c9758c5992
vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php	84a2b4c4d6c13a0176bdaf00e5c372bf
vendor/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php	e39894ac7340226215da856ac0261f8d
vendor/twig/twig/src/Node/Expression/Binary/PowerBinary.php	8f5daf8a347d13de49e2b090a990a791
vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php	30c1793c5131ecdbefff42891eb3dda2
vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php	5b39ad25381fdb440cba49aafc5eb0b9
vendor/twig/twig/src/Node/Expression/ParentExpression.php	5e8eef1a3dd0d9a9bca06152828edf4a
vendor/twig/twig/src/Node/Expression/Unary/AbstractUnary.php	f3ed92173c568dd729c41594187d5343
vendor/twig/twig/src/Node/Expression/Unary/NegUnary.php	8f2084984e11516f80a87cb55ef0f1b7
vendor/twig/twig/src/Node/Expression/Unary/PosUnary.php	f2a2bc5865635ad93866a8b331bce43c
vendor/twig/twig/src/Node/Expression/Unary/NotUnary.php	391b6f417e545857e3c0c7ea9d0c892b
vendor/twig/twig/src/Node/Expression/ConstantExpression.php	e1d60af11430d1fe99d71060ff419290
vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php	f2217e1fa522a3e39edaa47770319cf0
vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php	c55ff9544a43eb9e633c7551cc433fc7
vendor/twig/twig/src/Node/Expression/FunctionExpression.php	9c8f4ffdf1e1c2c568a699189faaccd3
vendor/twig/twig/src/Node/Expression/TempNameExpression.php	0c5f00038e513b69ea88e9af59813392
vendor/twig/twig/src/Node/Expression/ArrayExpression.php	ce49f5f454bb932cf981e8e50b585ad6
vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php	ff8f80d3a8f2a277d3f2f2ac0347d580
vendor/twig/twig/src/Node/Expression/AbstractExpression.php	d5ca7d7caa49f283cd01cdab662c39a3
vendor/twig/twig/src/Node/Expression/TestExpression.php	6be361e1070bc64276802f80fd6542da
vendor/twig/twig/src/Node/Expression/NameExpression.php	f12992526fa684784300d8ceacbda3bb
vendor/twig/twig/src/Node/Expression/CallExpression.php	ca58040390a601d2130b00db5a0b2a99
vendor/twig/twig/src/Node/Expression/AssignNameExpression.php	cc5bfb4184c9baef68b97b9293d34d99
vendor/twig/twig/src/Node/Expression/ConditionalExpression.php	2f47ccb7652d5ea7885c38d1f924df76
vendor/twig/twig/src/Node/Expression/FilterExpression.php	17f312cb547832eb531a02df8bbf2361
vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php	fad5b659f2fff3636ca1c7ef1a9d84ff
vendor/twig/twig/src/Node/Expression/MethodCallExpression.php	c4ea21df31e0b128d0ff4c0678307f40
vendor/twig/twig/src/Node/ImportNode.php	b9b814fdb9af405da68db48540ee48ca
vendor/twig/twig/src/Node/SetTempNode.php	885249b2273600311a37e0ccd447bb42
vendor/twig/twig/src/Node/SandboxNode.php	bbdad2a3286f74417db855fae7ad0b5c
vendor/twig/twig/src/Node/IncludeNode.php	f44350904ee58443be53485d6198ff85
vendor/twig/twig/src/Node/AutoEscapeNode.php	2c55c1ff619165ae24547b44c1194445
vendor/twig/twig/src/Node/BlockReferenceNode.php	92a7112d4f25298fbec8961437cdf4b3
vendor/twig/twig/src/Node/DoNode.php	2eda4c45ba8b695fbdf8126ed4a2be08
vendor/twig/twig/src/Node/SandboxedPrintNode.php	363fce5057b33e6a8761f6475947e8b9
vendor/twig/twig/src/Node/PrintNode.php	1a9d90a02a9395c7e1a6b28087bc4be3
vendor/twig/twig/src/Node/FlushNode.php	57b3532a899ef0e1982f2ef6cae12661
vendor/twig/twig/src/Node/CheckSecurityNode.php	ed66056587fa42e49f7c4c186185cf45
vendor/twig/twig/src/Node/DeprecatedNode.php	9b204a25e34b0821ffe5de21a36433cd
vendor/twig/twig/src/Node/EmbedNode.php	29ef69b498d3c6e2adbf48defcfdbbb5
vendor/twig/twig/src/Node/MacroNode.php	8e1087156b9bbf9186693023f5cdb339
vendor/twig/twig/src/Node/CheckToStringNode.php	aed732468557aa7452e811c9f4714225
vendor/twig/twig/src/Node/ForLoopNode.php	ae58208e3094d0bbb8c6b331a6415325
vendor/twig/twig/src/Node/NodeOutputInterface.php	dc306c16a9fec4e8131a4759d5fd64f4
vendor/twig/twig/src/Node/BodyNode.php	986ca7ce4713c6d7edfb46e7ec1f5e3c
vendor/twig/twig/src/Node/SetNode.php	046328f3002248db09e4d2a89f14d103
vendor/twig/twig/src/Node/ModuleNode.php	2c7560df59a620d24403c1d2d1960d0d
vendor/twig/twig/src/Node/ForNode.php	af91e29f641bcca7f345647eabb05786
vendor/twig/twig/src/Node/Node.php	b0680196ba89ca47e10103b3b531c14c
vendor/twig/twig/src/Node/IfNode.php	a572c70775120ad6ece493223bd70025
vendor/twig/twig/src/Node/NodeCaptureInterface.php	c431522464753dc23e9e06651dcca6b7
vendor/twig/twig/src/Node/WithNode.php	d58187c0da435b05dbbbf09f2284a58b
vendor/twig/twig/src/Util/TemplateDirIterator.php	95e8577b871e6576d437d31832f09155
vendor/twig/twig/src/Util/DeprecationCollector.php	942cb3a3f6794ca867a3e6b2c2de9ba1
vendor/twig/twig/src/TwigTest.php	f204c330acbda7993a2dc262c61b7627
vendor/twig/twig/src/TwigFilter.php	6997d5d7cfc92d3a10e525f68ba05277
vendor/twig/twig/src/Lexer.php	d6f61cd1fd85d698688feacb9e173cff
vendor/twig/twig/src/Loader/ArrayLoader.php	9019742f167205606a9c6e3bb85e3513
vendor/twig/twig/src/Loader/FilesystemLoader.php	a290d218d1731e8ac8d6ec531259f87e
vendor/twig/twig/src/Loader/ExistsLoaderInterface.php	345293588ecb537893db1514d6cb54ac
vendor/twig/twig/src/Loader/ChainLoader.php	964557a5539e078c6051614d04edb11e
vendor/twig/twig/src/Loader/LoaderInterface.php	d525c4c99898b8a5e7a15939bde66438
vendor/twig/twig/src/Loader/SourceContextLoaderInterface.php	b19786586c3608754da4ab4c63a59052
vendor/twig/twig/src/Compiler.php	47e391f784152c9fd3bb838e18a99145
vendor/twig/twig/src/Environment.php	4de50829a6ea6a4ccf38532446f6d737
vendor/twig/twig/src/Markup.php	04a575426a4c4a911e48d09c0e37fbe1
vendor/twig/twig/src/Parser.php	acaa0615bcfa9f5a60b9f3337a8de605
vendor/twig/twig/src/FileExtensionEscapingStrategy.php	012bf2f6bb81f53138e3457116eac0c6
vendor/twig/twig/src/Error/LoaderError.php	88ecd8f4caaea426867302cc79e5d664
vendor/twig/twig/src/Error/SyntaxError.php	7e3cab0242418ec374266731c66200bd
vendor/twig/twig/src/Error/RuntimeError.php	d4ea3fc112e7ac1f0271c140b0414c9a
vendor/twig/twig/src/Error/Error.php	9c0cb07c081e8d406fc4498b6c74c042
vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php	f71fc041fd314dbbf3b241f220974213
vendor/twig/twig/src/TokenParser/AbstractTokenParser.php	414442707567ab1608c3c9d161518fb0
vendor/twig/twig/src/TokenParser/EmbedTokenParser.php	697fc69a0a4eb5d06be1a3675d76632f
vendor/twig/twig/src/TokenParser/IfTokenParser.php	c5c6c61c73f7ca660d425e2e7a8a6e5b
vendor/twig/twig/src/TokenParser/UseTokenParser.php	a7a3c1765db0005a73dac9d98600a57c
vendor/twig/twig/src/TokenParser/IncludeTokenParser.php	8442f2750f7f59942924ab91baad9836
vendor/twig/twig/src/TokenParser/DoTokenParser.php	9735a11437b31bacd805b6484fe97f62
vendor/twig/twig/src/TokenParser/WithTokenParser.php	76f39d9c2f2976765afb16f025b6712f
vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php	a2e806185585f54c123c74313d1039e1
vendor/twig/twig/src/TokenParser/SandboxTokenParser.php	b4e82d6e3edbf7425288ba1625afb4c5
vendor/twig/twig/src/TokenParser/SetTokenParser.php	7cf31479a55d08d3159bf38671d8d60f
vendor/twig/twig/src/TokenParser/BlockTokenParser.php	e0c6fd95e6126d471a93c9d2be39f17c
vendor/twig/twig/src/TokenParser/ImportTokenParser.php	62928b38ade9553b3ce69bae576c8d78
vendor/twig/twig/src/TokenParser/TokenParserInterface.php	5bdbcae78c9d4c19c3399e1927daeecb
vendor/twig/twig/src/TokenParser/MacroTokenParser.php	69cbb64bd6796067cab0a39c0a379aac
vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php	df73a30dd1a1222b0fc2ff37f4d7b6ac
vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php	c518eb0e45b10bda2c562a885cd62885
vendor/twig/twig/src/TokenParser/ForTokenParser.php	c901ddef0fbb4e9cddc15cf1782fbd85
vendor/twig/twig/src/TokenParser/FilterTokenParser.php	4ae926ceddfbcce96afd676b0773185d
vendor/twig/twig/src/TokenParser/FromTokenParser.php	4fcc064fd242a4d0342c05407bdf6b03
vendor/twig/twig/src/TokenParser/FlushTokenParser.php	e17300c14c09668b656791dc892bc592
vendor/twig/twig/src/TokenParser/ApplyTokenParser.php	4a6c468b2a13cc6483dc69fe7d1429da
vendor/twig/twig/src/TemplateWrapper.php	2e4be076b56a6b01ae8259e6f3f2af07
vendor/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php	85f35aebed11d999cc49d7e33c939e69
vendor/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php	5168f15aa362004eb85ad5343ed9b5f8
vendor/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php	fd1ea8a2d1362d9d08bc286f69a946b6
vendor/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php	ab487ec3041846a0172ff59e388db827
vendor/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php	9759fb37627bfa4ad59e83eeebbd3859
vendor/twig/twig/src/Sandbox/SecurityError.php	15844b0e64f84351308fbb19ab527b7e
vendor/twig/twig/src/Sandbox/SecurityPolicyInterface.php	d52874981b38a6c5a0ef57029ae7fbec
vendor/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php	cd7077cf541307775e25d34b3860e1d9
vendor/twig/twig/src/Sandbox/SecurityPolicy.php	90476341be144439cb05639fa001fb40
vendor/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php	e55b4abc2a4ad5a836d2b0cfcec0e56d
vendor/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php	126b8f204ffc4e34db381592cba2d03a
vendor/twig/twig/src/TwigFunction.php	29d66df1a8526c1190f4434f314a259d
vendor/twig/twig/src/Source.php	7c28d26af6d13610023779507d9f6d5a
vendor/twig/twig/src/Profiler/Node/LeaveProfileNode.php	d20e5cf6e162cf8020432893f4c829e8
vendor/twig/twig/src/Profiler/Node/EnterProfileNode.php	24b7fd54ba4b1254c6d19ee5f34746e8
vendor/twig/twig/src/Profiler/Dumper/HtmlDumper.php	177c1f2c16b7d1d5ac0c53d44c7dd585
vendor/twig/twig/src/Profiler/Dumper/TextDumper.php	ef6338c77dc17ffb369daa4969134f07
vendor/twig/twig/src/Profiler/Dumper/BlackfireDumper.php	c6c8b27f9d27d140cf4f4816706c1feb
vendor/twig/twig/src/Profiler/Dumper/BaseDumper.php	d18c8fd29ce71af7649896d1cea671d2
vendor/twig/twig/src/Profiler/Profile.php	7dfdd7aab606108526740ed2410cb7d3
vendor/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php	acc3e10830d101f5849d6ee26f2ee4a1
vendor/twig/twig/src/Template.php	161f70e5a9d3e49134b1d823f121edf3
vendor/twig/twig/src/TokenStream.php	1848541512aedbc4fcefb7b84c4cd3b3
vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php	38bbc40b3e159c6184ee927b8d97c095
vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php	2ccbe2293d291022063a686f8c114c4e
vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php	0067b09ef187578b7ff95b9b7c9ac4c9
vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php	bebc4917738e86fd34a61f44b7be9046
vendor/twig/twig/src/NodeVisitor/NodeVisitorInterface.php	8dc65f4298409383e4242ac7fc513170
vendor/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php	f91c18954f4d77e8fba611ddb7de3916
vendor/twig/twig/src/Token.php	e5728a1e9926ae153bdcfb95a0712c97
vendor/twig/twig/src/Extension/CoreExtension.php	f07fd6837a1d2ef76d565b1b5c7b7617
vendor/twig/twig/src/Extension/InitRuntimeInterface.php	a1554d9ac096cd64648e2c66ebd8fdb6
vendor/twig/twig/src/Extension/EscaperExtension.php	b840d14ab6afb9c191b9369b5e8d9980
vendor/twig/twig/src/Extension/SandboxExtension.php	8203e3ef64a61d9c9e52a961c0c417b6
vendor/twig/twig/src/Extension/ExtensionInterface.php	8f82cf100a66579fc3b488a975b6812b
vendor/twig/twig/src/Extension/OptimizerExtension.php	f821f3d4e2916c16233a63110ed184fe
vendor/twig/twig/src/Extension/ProfilerExtension.php	ea7d8f41bc59894bfec77fe998910dec
vendor/twig/twig/src/Extension/StringLoaderExtension.php	f5199b0b4e8e73d1760c8e228c72fb6b
vendor/twig/twig/src/Extension/RuntimeExtensionInterface.php	d8c82a29001d5e0577086c048f382b57
vendor/twig/twig/src/Extension/DebugExtension.php	0d3f0dea4f9f1f39923d739b050ec96b
vendor/twig/twig/src/Extension/StagingExtension.php	75df53505422debc4765a060db6cec20
vendor/twig/twig/src/Extension/AbstractExtension.php	e65a39567db9f0f0fe57ad6c14382907
vendor/twig/twig/src/Extension/GlobalsInterface.php	0f0ff64e333784e74159f7482c0ac060
vendor/twig/twig/src/ExpressionParser.php	91f9bd5cb7e804efd796d3cfa075c831
vendor/twig/twig/src/Cache/NullCache.php	fa98d9d6ed44f56b84a228b2dada9e8f
vendor/twig/twig/src/Cache/CacheInterface.php	13f38b201b8bfe6b57d969ce8fbacf1a
vendor/twig/twig/src/Cache/FilesystemCache.php	0ed58d3c9f70b82e95fc35acc1fa8a52
vendor/twig/twig/src/NodeTraverser.php	730486d6ae853d34a512ed12c93fb8c2
vendor/twig/twig/.php_cs.dist	b7ec4235c9dd739061a490d1c8c88cd2
vendor/erusev/parsedown/composer.json	c1adae9f98f6984940f68d6c90a06c2d
vendor/erusev/parsedown/Parsedown.php	acc9a4fb3e5b35ab01fe94f6004752ff
vendor/erusev/parsedown-extra/composer.json	7f98abb73c7ee10118c71c2700c22832
vendor/erusev/parsedown-extra/ParsedownExtra.php	deb72b834488c808b98f0684d8b2e3f2
vendor/pimple/pimple/composer.json	80e243b39ca21cef8e361b3a1aaf079f
vendor/pimple/pimple/src/Pimple/Psr11/Container.php	4411acca76925cc4e94bc470d161e189
vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php	6a33f32873d78b1808bd43be9a4392c7
vendor/pimple/pimple/src/Pimple/Container.php	3a390cddce4453064ad9b4a38f326363
vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php	7555b2e4ae4c277e56c08da33cf40443
vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php	2dc56ac7feb866f7361fdfbaf2cafd57
vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php	55390d6d7b50e403d05b6b1a7c144bf9
vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php	065c95be5677c6a2f843285963b8ee9d
vendor/pimple/pimple/src/Pimple/ServiceIterator.php	28b5483a73f2e07ed391a419843e43d9
vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php	18486f1b6cde60410f53c9be072360db
vendor/filp/whoops/composer.json	11e35e4138afa3c1464a72d8550dd449
vendor/filp/whoops/src/Whoops/Util/Misc.php	02005cbd83a2a9aa0cff8962ad8b418f
vendor/filp/whoops/src/Whoops/Util/TemplateHelper.php	909e673af75a337c547707b64032a54f
vendor/filp/whoops/src/Whoops/Util/HtmlDumperOutput.php	52204c168b5e20951ced4a3787c0a720
vendor/filp/whoops/src/Whoops/Util/SystemFacade.php	4f17170ef2000e28703c07c03573ca8f
vendor/filp/whoops/src/Whoops/Handler/PrettyPageHandler.php	f3b3c501d1337733dc14e98a607e84a0
vendor/filp/whoops/src/Whoops/Handler/HandlerInterface.php	f05d795d4eaaa4e618769f10edd793aa
vendor/filp/whoops/src/Whoops/Handler/JsonResponseHandler.php	2251524c9e7163970a2e80c0f12f4f3f
vendor/filp/whoops/src/Whoops/Handler/CallbackHandler.php	a30545cc40cbbf0e61cfff91a1658824
vendor/filp/whoops/src/Whoops/Handler/PlainTextHandler.php	d6de7467d515b3b20ca7da725a4eee33
vendor/filp/whoops/src/Whoops/Handler/XmlResponseHandler.php	bfe495bda523a9d70ff5f195c998f76a
vendor/filp/whoops/src/Whoops/Handler/Handler.php	b9806fcac27d461ccfb63e99fc90dce9
vendor/filp/whoops/src/Whoops/RunInterface.php	e830baf53ea90ffe1e4703bdc0db3483
vendor/filp/whoops/src/Whoops/Exception/Frame.php	18dc466dd7794afb0f2a77f58847a00f
vendor/filp/whoops/src/Whoops/Exception/ErrorException.php	b238ac07f53f6cbf478b5a06e0a8852f
vendor/filp/whoops/src/Whoops/Exception/Formatter.php	843bb343dc362bb9ca32be11ea3adc5e
vendor/filp/whoops/src/Whoops/Exception/Inspector.php	c05a12c7691bd0856564d7a219f5c193
vendor/filp/whoops/src/Whoops/Exception/FrameCollection.php	596724142877b27c3b03876cacc1b3f0
vendor/filp/whoops/src/Whoops/Run.php	1df95977de4d3ea5e23ed2a3cf231768
vendor/filp/whoops/src/Whoops/Resources/views/frames_description.html.php	ea4b6615cf9221f9d27d19a4c6d6a611
vendor/filp/whoops/src/Whoops/Resources/views/panel_left.html.php	3df8a4cc2822ee0130da3ce32306e6df
vendor/filp/whoops/src/Whoops/Resources/views/env_details.html.php	4637004c195dde704c2eefef2d93a2ab
vendor/filp/whoops/src/Whoops/Resources/views/header.html.php	dd74ac168d521c247c6857cf514244b8
vendor/filp/whoops/src/Whoops/Resources/views/frames_container.html.php	80efd4952ddb4c3f144b32d1e84b4bc4
vendor/filp/whoops/src/Whoops/Resources/views/frame_list.html.php	c91fd78b96ac0df36d9d347817f567b9
vendor/filp/whoops/src/Whoops/Resources/views/header_outer.html.php	1a5b525dbe7a85ea4290b01d8f10a23d
vendor/filp/whoops/src/Whoops/Resources/views/panel_details.html.php	b8f33759ab3e0937d4c59cea7fe52cac
vendor/filp/whoops/src/Whoops/Resources/views/panel_left_outer.html.php	9daf3c9e801802e8323dba01684fef11
vendor/filp/whoops/src/Whoops/Resources/views/panel_details_outer.html.php	e51888ceb95613f987d3c9d4f78a4975
vendor/filp/whoops/src/Whoops/Resources/views/layout.html.php	9adfa105c02f53eb083040b3d4f53918
vendor/filp/whoops/src/Whoops/Resources/views/frame_code.html.php	9a42801b7e8a522c207addf91a2d4027
vendor/filp/whoops/src/Whoops/Resources/css/whoops.base.css	639d8e21a9c8ceb9b37b2acf27c3918b
vendor/filp/whoops/src/Whoops/Resources/js/clipboard.min.js	e830f929b40edf1808f3cd9b43acabc4
vendor/filp/whoops/src/Whoops/Resources/js/zepto.min.js	54c9c5d40126e729d3eb1db81420c3d2
vendor/filp/whoops/src/Whoops/Resources/js/whoops.base.js	79416cd9db5f46db89e3e55ef7498ad2
vendor/filp/whoops/src/Whoops/Resources/js/prettify.min.js	75d1fbe9771e432b36fa387bc561973f
composer.json	536ae3adc38f1b520a3da619d7d96471
Loader.php	ca2419dbbc6aa32be63ffab39f0e3cb2
classes/Gantry/Component/Remote/Response.php	cee89b2ee3ab1dd440148b340fa82e60
classes/Gantry/Component/Collection/CollectionInterface.php	10477d9403def2c80de2aa6a0eb50d02
classes/Gantry/Component/Collection/Collection.php	ac5ba1eeeee4ed4983ee235ce5f41b6b
classes/Gantry/Component/Stylesheet/Scss/Compiler.php	5fb1e07730c880b8b0f9cfdcbf28835e
classes/Gantry/Component/Stylesheet/ScssCompiler.php	571b076d18711f5afed6427d070b95b6
classes/Gantry/Component/Stylesheet/CssCompilerInterface.php	0ece7e83830c0ddf5195b2ed4c1cd9fe
classes/Gantry/Component/Stylesheet/CssCompiler.php	eb7168cac29e442b2651dd2ed300bc50
classes/Gantry/Component/System/Messages.php	759949ab372d95dd1a65936b9b9e30f9
classes/Gantry/Component/Outline/OutlineCollection.php	6fd3e07674846d29ebab926ff0c58094
classes/Gantry/Component/Assignments/AbstractAssignments.php	ed796fdb68d168b5d94e411432363ffb
classes/Gantry/Component/Assignments/AssignmentFilter.php	55b289ee2f107e86a6bbec522a8fb4b8
classes/Gantry/Component/Assignments/AssignmentsInterface.php	ab9ef29f534f2e8829d4056412d3c5d2
classes/Gantry/Component/Gettext/Gettext.php	72a70e3f7f68d661c2e9198ecb355cd6
classes/Gantry/Component/Url/Url.php	33d556f3d0269cdd8c43ead43410056a
classes/Gantry/Component/Translator/TranslatorInterface.php	574e98e9c253675feb00b79358579a82
classes/Gantry/Component/Translator/Translator.php	c9d055139a13c814a1591768f800ff47
classes/Gantry/Component/Position/Module.php	d30fadeb50ab0cb713286d565f71be9e
classes/Gantry/Component/Position/Position.php	ff21fe0ae1174afa76e899573b19f046
classes/Gantry/Component/Position/Positions.php	0ac36254c10c00555e4b9be6f97b4464
classes/Gantry/Component/Theme/ThemeTrait.php	b0eb6edbc89c065f072277f1999af71a
classes/Gantry/Component/Theme/AbstractTheme.php	ebd0a95329a1ac94bc37b57f219c81d2
classes/Gantry/Component/Theme/ThemeDetails.php	dde95cdb608caf8ca0945ff80bf2958e
classes/Gantry/Component/Theme/ThemeInterface.php	c367c7edb46ac65ede26aac9d18dbad8
classes/Gantry/Component/Theme/ThemeInstaller.php	39d251ecde79665d4967ebca4db6943e
classes/Gantry/Component/Controller/RestfulControllerInterface.php	e960666e531ea781cb2292f1339da613
classes/Gantry/Component/Controller/BaseController.php	6603e73ed91aa8e667879774e411d8c3
classes/Gantry/Component/Controller/HtmlController.php	7564c530b17f8a128ae3b577e772f374
classes/Gantry/Component/Controller/JsonController.php	4a3f7cf43cff4310f8a49a0f87d736d9
classes/Gantry/Component/Router/RouterInterface.php	0bd894af8fe20e6a847b390c889a1598
classes/Gantry/Component/Router/Router.php	e62bcafacdb85a5fc761b1de3378af7f
classes/Gantry/Component/Admin/HtmlController.php	166c8dc1ca290fb9e24c8ca39754eb98
classes/Gantry/Component/Admin/JsonController.php	92dd0e04d65e3d8a19128a646fefc0ad
classes/Gantry/Component/File/CompiledFile.php	0daef493572f71a456a3e194278599f6
classes/Gantry/Component/File/CompiledYamlFile.php	bf781791af4c327e47e97699ea8e3f2e
classes/Gantry/Component/Twig/Node/TwigNodeThrow.php	ba11d620645a85bb63cae89118274d00
classes/Gantry/Component/Twig/Node/TwigNodeStyles.php	9c9f7cd832ca19e710c0e202c7ce94e2
classes/Gantry/Component/Twig/Node/TwigNodePageblock.php	e252a28cd5d811cef8f612350d3b7e34
classes/Gantry/Component/Twig/Node/TwigNodeAssets.php	31e8ee776cc571237ea0ee88a3927860
classes/Gantry/Component/Twig/Node/TwigNodeSwitch.php	25617ee15dc8c4b8df8fd3b5bdcaf0aa
classes/Gantry/Component/Twig/Node/TwigNodeTryCatch.php	d728ccbb0eac3e3fa2d7bc31d7a9c632
classes/Gantry/Component/Twig/Node/TwigNodeMarkdown.php	cefb4fc13c31b1cdd90d4cf41e55169c
classes/Gantry/Component/Twig/Node/TwigNodeScripts.php	57f08371cd71c54ef5edef0a6b3b50ad
classes/Gantry/Component/Twig/TwigCacheFilesystem.php	c56017572b2c88b8f0916de7b8806f8b
classes/Gantry/Component/Twig/TokenParser/TokenParserStyles.php	0a99bf956e0c73105478768dfddff836
classes/Gantry/Component/Twig/TokenParser/TokenParserAssets.php	6270d8b8021761f7b5367a4c68ce9813
classes/Gantry/Component/Twig/TokenParser/TokenParserThrow.php	a55c48e15d609a8c5bcacaafa94a3ff6
classes/Gantry/Component/Twig/TokenParser/TokenParserTryCatch.php	53cd9eead0686e3b4fd636e9b2274a34
classes/Gantry/Component/Twig/TokenParser/TokenParserMarkdown.php	e03150fa61318acf2faadca7c4811e64
classes/Gantry/Component/Twig/TokenParser/TokenParserPageblock.php	854d7e41a3a73090999ed906514439f1
classes/Gantry/Component/Twig/TokenParser/TokenParserSwitch.php	b58b2acf5c336daa0c56a43fa39f5328
classes/Gantry/Component/Twig/TokenParser/TokenParserScripts.php	0e7741005f3b3d6b711a0eb58603e05a
classes/Gantry/Component/Twig/TwigExtension.php	cae4d18b76ce1c8aa44fb3a49b7c02ba
classes/Gantry/Component/Menu/AbstractMenu.php	80fa1a2f0671f887a5c8471add9b78bf
classes/Gantry/Component/Menu/Item.php	a79b661122213a2a7eb8887ea9e511d6
classes/Gantry/Component/Response/Response.php	3dd4778327626b7408cb5f0aac171d1a
classes/Gantry/Component/Response/HtmlResponse.php	f5a1f2511538630bcf58586d26044c13
classes/Gantry/Component/Response/RedirectResponse.php	02e7fd408e68f34e0ea814515083751e
classes/Gantry/Component/Response/JsonResponse.php	5dc68287fe040538a8ffee60e2f04249
classes/Gantry/Component/Config/CompiledBlueprints.php	621abc031548172b47050f6532cbc89e
classes/Gantry/Component/Config/Config.php	1f681f504f332337f59e35f8eae3f060
classes/Gantry/Component/Config/Validation.php	0d4bee661f0bf30b48f1a3d4d01110cc
classes/Gantry/Component/Config/ConfigFileFinder.php	5033be2d2eb098402a68f15be4bc7c78
classes/Gantry/Component/Config/BlueprintSchema.php	6e5bfc355d33fe624cdd4c80763ec341
classes/Gantry/Component/Config/CompiledBase.php	c72c992e1b0b281171b59eda78247512
classes/Gantry/Component/Config/CompiledConfig.php	88bb7959070b0d72c80181846a4e5e96
classes/Gantry/Component/Config/ValidationException.php	935aeb2dd563c1b7adc8a5fefeb224f4
classes/Gantry/Component/Config/BlueprintForm.php	4b2abf4298db1d02e136f5ae7bd18ada
classes/Gantry/Component/Gantry/GantryTrait.php	295f32670fb38ee81546baccd62967f8
classes/Gantry/Component/Filesystem/Folder.php	650e9d70396e7664fd7eda3f16aa811c
classes/Gantry/Component/Filesystem/Streams.php	857b6358e651c25848e91f7f387389ec
classes/Gantry/Component/Request/Input.php	404f7d2856ae936668d2aec0350fa3ff
classes/Gantry/Component/Request/Request.php	09a3b2ede054c16539ee4a2517fbe334
classes/Gantry/Component/Layout/Version/Format0.php	1e3b1c1a2a7b134b557141ee0b36fdd7
classes/Gantry/Component/Layout/Version/Format2.php	c91ff7dd610996d8f1f663a8c9bcbe67
classes/Gantry/Component/Layout/Version/Format1.php	e68be941bd60a11e91b89b8841f0af49
classes/Gantry/Component/Layout/Layout.php	f01a0d56a17cf1fe25d00c856cc195c3
classes/Gantry/Component/Layout/LayoutReader.php	11352868575de32abbf7d1c891b08a0d
classes/Gantry/Component/Whoops/SystemFacade.php	cbea454e3da9668078b653245ccdca07
classes/Gantry/Component/Content/Document/HtmlDocument.php	fa9088b77b5cad295b9a93a640cc6317
classes/Gantry/Component/Content/Block/HtmlBlock.php	75e06c78599c95fbf3a3fc92a06185ad
classes/Gantry/Component/Content/Block/ContentBlockInterface.php	77eb2eb1912c772e1df08b0b85b6f4c6
classes/Gantry/Component/Content/Block/ContentBlock.php	cdae900ee4f4061e404c707da3226278
classes/Gantry/Component/Content/Block/HtmlBlockInterface.php	d07876e7fd8ed7d7627bd1299d7886a5
classes/Gantry/Admin/Controller/Html/Menu.php	1125808254c4973a154aca468e10c499
classes/Gantry/Admin/Controller/Html/Configurations.php	ebee59573efc2f01c5e188892131d08f
classes/Gantry/Admin/Controller/Html/About.php	391d7a58d99b95c5626948641d148ea9
classes/Gantry/Admin/Controller/Html/Positions.php	30a73b1e16088fc6a01d004d7d99b438
classes/Gantry/Admin/Controller/Html/Themes.php	616f09e0eaa75a6f23c1dcca459cb375
classes/Gantry/Admin/Controller/Html/Import.php	d1340d47120647bbbd4050d7d029b9c7
classes/Gantry/Admin/Controller/Html/Install.php	5ad5c435956004b9cb23bfb6c54ce291
classes/Gantry/Admin/Controller/Html/Configurations/Layout.php	105841d708c07ae973d57fe6cce7b854
classes/Gantry/Admin/Controller/Html/Configurations/Settings.php	7c9cfe0fd7c590f97f9e182d20be9b67
classes/Gantry/Admin/Controller/Html/Configurations/Page.php	ccd591d8c6ff0c2b7dd15b04e5ea4478
classes/Gantry/Admin/Controller/Html/Configurations/Styles.php	963f8fb111ea75a6591f96cd10320ebf
classes/Gantry/Admin/Controller/Html/Configurations/Assignments.php	f067a9c6afc531534c8242dc3a56e15b
classes/Gantry/Admin/Controller/Html/Export.php	7cf16b77d3ca85ed117eb5d30833c090
classes/Gantry/Admin/Controller/Html/Cache.php	0de32f0eb00c809f8e44f5ba0bc2a309
classes/Gantry/Admin/Controller/Json/Layouts.php	9607440399c4b83ccc19521dff7d9c7c
classes/Gantry/Admin/Controller/Json/Particle.php	0b7f8ef000e540a8c643f25bfe1968a5
classes/Gantry/Admin/Controller/Json/Filepicker.php	b2f7471dee04e4b2ad754228500aa76e
classes/Gantry/Admin/Controller/Json/Icons.php	95b1133b998cc6dcd7bc13e81a127c3c
classes/Gantry/Admin/Controller/Json/Unsaved.php	62217c2f2679593fffc8da0fedb85121
classes/Gantry/Admin/Controller/Json/Devprod.php	15a2db08c7be605a4a31b73db3e5d86a
classes/Gantry/Admin/Controller/Json/Fontpicker.php	a6825b8dd8c535ea41e49035015f979b
classes/Gantry/Admin/Controller/Json/Atoms.php	660ff61d1c48fd0cb239d1dfabe70dac
classes/Gantry/Admin/Controller/Json/Changelog.php	c33f075180e9e3d10d44993ad6d97a8d
classes/Gantry/Admin/Controller/Json/Confirmdeletion.php	1984644991df87ea68a6eb3a53610799
classes/Gantry/Admin/EventListener.php	60932cd47c3a33bee8f9063723bb7413
classes/Gantry/Admin/Theme.php	23a7a26fe9d701408d5875f4a8cd35e6
classes/Gantry/Admin/Router.php	553eacb6119e39f1fa90b991f0a29f94
classes/Gantry/Admin/ThemeList.php	b02080569b6a4d07033453a529089164
classes/Gantry/Admin/Particles.php	64b7efcd53ac7868019f8bd647b9f37d
classes/Gantry/Admin/Page.php	db9422e99a986fc07a9818c86084420b
classes/Gantry/Admin/Styles.php	4c9eb72286fff333adaf574e237c8fa6
classes/Gantry/Joomla/Assignments/AssignmentsStyle.php	19feedfe5c26d71127d52ed98c15da17
classes/Gantry/Joomla/Assignments/AssignmentsMenu.php	c8b6a6413152c00a40bad0a2bc1c91fe
classes/Gantry/Joomla/Module/ModuleCollection.php	f0a236cc93108ef9c38830e29d05866a
classes/Gantry/Joomla/Module/Module.php	2a86553caf9bd623eb750f0dbb869a38
classes/Gantry/Joomla/Module/ModuleFinder.php	db2696a4dd65c4d898e2e8174b68a8f1
classes/Gantry/Joomla/TemplateInstaller.php	4fdf13e5f9ae538c6dce08225d5ebd35
classes/Gantry/Joomla/Manifest.php	9ed02c686f5b25d8ff8b23489d85ccc3
classes/Gantry/Joomla/CacheHelper.php	b4a21debd1511a21d7de69dae796f7ad
classes/Gantry/Joomla/StyleHelper.php	64b1903fe8009a6e7bbe92809606a1b2
classes/Gantry/Joomla/Content/Content.php	586c252630f20aa49b01fa1b42eb336c
classes/Gantry/Joomla/Content/ContentFinder.php	262abd669d66d11e6e6f3e628d3496c5
classes/Gantry/Joomla/Category/Category.php	2c6c6269e8442f1224ad42419947e04c
classes/Gantry/Joomla/Category/CategoryFinder.php	4e702a56617ef5a2ba071650b898bbfa
classes/Gantry/Joomla/Object/Finder.php	3f53910031f6eaa213bd54259bcfdb12
classes/Gantry/Joomla/Object/Collection.php	a5a2e2fb3261e6fd09e41ac7dfc41f0d
classes/Gantry/Joomla/Object/AbstractObject.php	f9b5559abd646d9ef92f3e879a072ae0
classes/Gantry/Framework/Menu.php	26d32b9845a59baba8d9dd4bff2bbdfd
classes/Gantry/Framework/Configurations.php	bc5a609a891824495b78ed93ea3d30b9
classes/Gantry/Framework/Outlines.php	ab3d762b8f1de0aedd27df00f3f9d0bf
classes/Gantry/Framework/Request.php	58555c06be380d52a22c04a652620989
classes/Gantry/Framework/Services/ErrorServiceProvider.php	ad9dd475588a8d32822570c1f682041e
classes/Gantry/Framework/Services/StreamsServiceProvider.php	a0013c46b7c760d1a53fcde9d9e41eb2
classes/Gantry/Framework/Services/ConfigServiceProvider.php	ced0eafc2dc229ab9078adb63865687b
classes/Gantry/Framework/Theme.php	074a997f91d0a41342c766dfbe54023d
classes/Gantry/Framework/Positions.php	6bd2bc88fdd51522a5f48b9d06304dce
classes/Gantry/Framework/Document.php	77ca5040d4fdd556ef17525c642bab5e
classes/Gantry/Framework/Base/Theme.php	681c315a8a1dec1c90da4c18d9632614
classes/Gantry/Framework/Base/Platform.php	d3f67e87fad9cc407d4b49297b58bd44
classes/Gantry/Framework/Base/Site.php	bade0b530f22572eb4c0953a4f3031ef
classes/Gantry/Framework/Base/Page.php	20ea33956562c2f3929739d27e5aeabc
classes/Gantry/Framework/Base/Gantry.php	1bfb22e53b538f8ca6189086ee576e90
classes/Gantry/Framework/Platform.php	f5ae836b203ad2babac883949e93d1e7
classes/Gantry/Framework/Exporter.php	8d8da2f85f8fe62d8254fa1a6a516948
classes/Gantry/Framework/Site.php	1d999ad888699917f6c773ebe0c06c0e
classes/Gantry/Framework/Translator.php	a9bb62ca922c00c4d637a5e1fd95d71f
classes/Gantry/Framework/Atoms.php	57e2135cfd13a26334af5abb393670f9
classes/Gantry/Framework/ThemeInstaller.php	938f85addf555eb074c2cfcc2774c5c1
classes/Gantry/Framework/Markdown/Parsedown.php	c195585f11377e5921a4237eb9bd0721
classes/Gantry/Framework/Markdown/ParsedownTrait.php	7c85834c410aad3cba1e1ddfe2f26add
classes/Gantry/Framework/Markdown/ParsedownExtra.php	d006c235d6331bda4fd1845d79931ba3
classes/Gantry/Framework/Page.php	99fc2ef5d72ddec60e1524440fcd9863
classes/Gantry/Framework/Gantry.php	e5c5e28f1f487d1088d6a76a804beb15
classes/Gantry/Framework/Exception.php	c2b27188880895d6042fa484b4cd0648
classes/Gantry/Framework/Assignments.php	5c4719bb7762d882b057da8ec988a2ca
classes/Leafo/ScssPhp/Compiler.php	5c9a8af28b29fb611844db990f9a898d
MD5SUMS	d41d8cd98f00b204e9800998ecf8427e
bootstrap.php	c0596c070aa6a3c63292a82f6a2feb32
composer.lock	29d0e791a303437d335b61abc8b7330f
RealLoader.php	a32341d18c4fff022fcd14e9d3b42be4
RealLoader.php000064400000007256151166614520007310 0ustar00<?php

/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry5;

/**
 * Use \Gantry5\Loader::setup() or \Gantry5\Loader::get() instead.
 *
 * This class separates Loader logic from the \Gantry5\Loader class. By
adding this extra class we are able to upgrade
 * Gantry5 and initializing the new version during a single request -- as
long as Gantry5 has not been initialized.
 *
 * @internal
 */
abstract class RealLoader
{
    protected static $errorMessagePhpMin = 'You are running PHP %s,
but Gantry 5 Framework needs at least PHP %s to run.';
    protected static $errorMessageGantryLoaded = 'Attempting to load
Gantry 5 Framework multiple times.';

    /**
     * Initializes Gantry5 and returns Composer ClassLoader.
     *
     * @return \Composer\Autoload\ClassLoader
     * @throws \RuntimeException
     * @throws \LogicException
     */
    public static function getClassLoader()
    {
        // Fail safe version check for PHP <5.5.9.
        if (version_compare($phpVersion = PHP_VERSION, '5.5.9',
'<')) {
            throw new \RuntimeException(sprintf(self::$errorMessagePhpMin,
$phpVersion, '5.5.9'));
        }

        if (defined('GANTRY5_VERSION')) {
            throw new \LogicException(self::$errorMessageGantryLoaded);
        }

        define('GANTRY5_VERSION', '5.4.37');
        define('GANTRY5_VERSION_DATE', 'January 25,
2021');

        if (!defined('DS')) {
            define('DS', DIRECTORY_SEPARATOR);
        }

        define('GANTRY_DEBUGGER',
class_exists('Gantry\\Debugger'));

        return self::autoload();
    }

    /**
     * @return \Composer\Autoload\ClassLoader
     * @throws \LogicException
     * @internal
     */
    protected static function autoload()
    {
        // Register platform specific overrides.
        if (defined('JVERSION') &&
defined('JPATH_ROOT')) {
            define('GANTRY5_PLATFORM', 'joomla');
            define('GANTRY5_ROOT', JPATH_ROOT);
        } elseif (defined('WP_DEBUG') &&
defined('ABSPATH')) {
            define('GANTRY5_PLATFORM', 'wordpress');
            if (class_exists('Env') &&
defined('CONTENT_DIR')) {
                // Bedrock support.
                define('GANTRY5_ROOT', preg_replace('|'
. preg_quote(CONTENT_DIR). '$|', '', WP_CONTENT_DIR));
            } else {
                // Plain WP support.
                define('GANTRY5_ROOT', dirname(WP_CONTENT_DIR));
            }
        } elseif (defined('GRAV_VERSION') &&
defined('ROOT_DIR')) {
            define('GANTRY5_PLATFORM', 'grav');
            define('GANTRY5_ROOT', rtrim(ROOT_DIR,
'/'));
        } elseif (defined('PRIME_ROOT')) {
            define('GANTRY5_PLATFORM', 'prime');
            define('GANTRY5_ROOT', PRIME_ROOT);
        } else {
            throw new \RuntimeException('Gantry: CMS not
detected!');
        }

        $base = __DIR__;
        $vendor = "{$base}/platforms/" . GANTRY5_PLATFORM;
        $dev = is_dir($vendor);
        if (!$dev) {
            $vendor = $base;
        }
        $autoload = "{$vendor}/vendor/autoload.php";

        // Initialize auto-loading.
        if (!file_exists($autoload)) {
            throw new \LogicException('Please run composer in Gantry 5
Library!');
        }

        /** @var \Composer\Autoload\ClassLoader $loader */
        $loader = require_once $autoload;

        if ($dev) {
            $loader->addPsr4('Gantry\\',
"{$base}/classes/Gantry");
        }

        return $loader;
    }
}
vendor/autoload.php000064400000000262151166614520010371 0ustar00<?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInit7bfda60b2ff69dc96fe4a1f15c2e6d42::getLoader();
vendor/composer/autoload_classmap.php000064400000000350151166614520014101
0ustar00<?php

// autoload_classmap.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Composer\\InstalledVersions' => $vendorDir .
'/composer/InstalledVersions.php',
);
vendor/composer/autoload_files.php000064400000000360151166614520013401
0ustar00<?php

// autoload_files.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir .
'/symfony/polyfill-ctype/bootstrap.php',
);
vendor/composer/autoload_namespaces.php000064400000000607151166614520014422
0ustar00<?php

// autoload_namespaces.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Twig_' => array($vendorDir . '/twig/twig/lib'),
    'Pimple' => array($vendorDir .
'/pimple/pimple/src'),
    'ParsedownExtra' => array($vendorDir .
'/erusev/parsedown-extra'),
    'Parsedown' => array($vendorDir .
'/erusev/parsedown'),
);
vendor/composer/autoload_psr4.php000064400000003237151166614520013175
0ustar00<?php

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Whoops\\' => array($vendorDir .
'/filp/whoops/src/Whoops'),
    'Twig\\' => array($vendorDir .
'/twig/twig/src'),
    'Symfony\\Polyfill\\Ctype\\' => array($vendorDir .
'/symfony/polyfill-ctype'),
    'Symfony\\Component\\Yaml\\' => array($vendorDir .
'/symfony/yaml'),
    'Symfony\\Component\\EventDispatcher\\' =>
array($vendorDir . '/symfony/event-dispatcher'),
    'RocketTheme\\Toolbox\\StreamWrapper\\' =>
array($vendorDir . '/rockettheme/toolbox/StreamWrapper/src'),
    'RocketTheme\\Toolbox\\Session\\' => array($vendorDir .
'/rockettheme/toolbox/Session/src'),
    'RocketTheme\\Toolbox\\ResourceLocator\\' =>
array($vendorDir . '/rockettheme/toolbox/ResourceLocator/src'),
    'RocketTheme\\Toolbox\\File\\' => array($vendorDir .
'/rockettheme/toolbox/File/src'),
    'RocketTheme\\Toolbox\\Event\\' => array($vendorDir .
'/rockettheme/toolbox/Event/src'),
    'RocketTheme\\Toolbox\\DI\\' => array($vendorDir .
'/rockettheme/toolbox/DI/src'),
    'RocketTheme\\Toolbox\\Compat\\' => array($vendorDir .
'/rockettheme/toolbox/Compat/src'),
    'RocketTheme\\Toolbox\\Blueprints\\' => array($vendorDir .
'/rockettheme/toolbox/Blueprints/src'),
    'RocketTheme\\Toolbox\\ArrayTraits\\' => array($vendorDir
. '/rockettheme/toolbox/ArrayTraits/src'),
    'Psr\\Log\\' => array($vendorDir .
'/psr/log/Psr/Log'),
    'Psr\\Container\\' => array($vendorDir .
'/psr/container/src'),
    'Leafo\\ScssPhp\\' => array($baseDir .
'/classes/Leafo/ScssPhp', $vendorDir .
'/leafo/scssphp/src'),
    'Gantry\\' => array($baseDir .
'/classes/Gantry'),
);
vendor/composer/autoload_real.php000064400000004731151166614520013230
0ustar00<?php

// autoload_real.php @generated by Composer

class ComposerAutoloaderInit7bfda60b2ff69dc96fe4a1f15c2e6d42
{
    private static $loader;

    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    /**
     * @return \Composer\Autoload\ClassLoader
     */
    public static function getLoader()
    {
        if (null !== self::$loader) {
            return self::$loader;
        }

        require __DIR__ . '/platform_check.php';

       
spl_autoload_register(array('ComposerAutoloaderInit7bfda60b2ff69dc96fe4a1f15c2e6d42',
'loadClassLoader'), true, true);
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
       
spl_autoload_unregister(array('ComposerAutoloaderInit7bfda60b2ff69dc96fe4a1f15c2e6d42',
'loadClassLoader'));

        $useStaticLoader = PHP_VERSION_ID >= 50600 &&
!defined('HHVM_VERSION') &&
(!function_exists('zend_loader_file_encoded') ||
!zend_loader_file_encoded());
        if ($useStaticLoader) {
            require __DIR__ . '/autoload_static.php';

           
call_user_func(\Composer\Autoload\ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::getInitializer($loader));
        } else {
            $map = require __DIR__ . '/autoload_namespaces.php';
            foreach ($map as $namespace => $path) {
                $loader->set($namespace, $path);
            }

            $map = require __DIR__ . '/autoload_psr4.php';
            foreach ($map as $namespace => $path) {
                $loader->setPsr4($namespace, $path);
            }

            $classMap = require __DIR__ .
'/autoload_classmap.php';
            if ($classMap) {
                $loader->addClassMap($classMap);
            }
        }

        $loader->register(true);

        if ($useStaticLoader) {
            $includeFiles =
Composer\Autoload\ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::$files;
        } else {
            $includeFiles = require __DIR__ .
'/autoload_files.php';
        }
        foreach ($includeFiles as $fileIdentifier => $file) {
           
composerRequire7bfda60b2ff69dc96fe4a1f15c2e6d42($fileIdentifier, $file);
        }

        return $loader;
    }
}

function composerRequire7bfda60b2ff69dc96fe4a1f15c2e6d42($fileIdentifier,
$file)
{
    if
(empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
        require $file;

        $GLOBALS['__composer_autoload_files'][$fileIdentifier] =
true;
    }
}
vendor/composer/autoload_static.php000064400000012315151166614520013571
0ustar00<?php

// autoload_static.php @generated by Composer

namespace Composer\Autoload;

class ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42
{
    public static $files = array (
        '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ .
'/..' . '/symfony/polyfill-ctype/bootstrap.php',
    );

    public static $prefixLengthsPsr4 = array (
        'W' => 
        array (
            'Whoops\\' => 7,
        ),
        'T' => 
        array (
            'Twig\\' => 5,
        ),
        'S' => 
        array (
            'Symfony\\Polyfill\\Ctype\\' => 23,
            'Symfony\\Component\\Yaml\\' => 23,
            'Symfony\\Component\\EventDispatcher\\' => 34,
        ),
        'R' => 
        array (
            'RocketTheme\\Toolbox\\StreamWrapper\\' => 34,
            'RocketTheme\\Toolbox\\Session\\' => 28,
            'RocketTheme\\Toolbox\\ResourceLocator\\' => 36,
            'RocketTheme\\Toolbox\\File\\' => 25,
            'RocketTheme\\Toolbox\\Event\\' => 26,
            'RocketTheme\\Toolbox\\DI\\' => 23,
            'RocketTheme\\Toolbox\\Compat\\' => 27,
            'RocketTheme\\Toolbox\\Blueprints\\' => 31,
            'RocketTheme\\Toolbox\\ArrayTraits\\' => 32,
        ),
        'P' => 
        array (
            'Psr\\Log\\' => 8,
            'Psr\\Container\\' => 14,
        ),
        'L' => 
        array (
            'Leafo\\ScssPhp\\' => 14,
        ),
        'G' => 
        array (
            'Gantry\\' => 7,
        ),
    );

    public static $prefixDirsPsr4 = array (
        'Whoops\\' => 
        array (
            0 => __DIR__ . '/..' .
'/filp/whoops/src/Whoops',
        ),
        'Twig\\' => 
        array (
            0 => __DIR__ . '/..' . '/twig/twig/src',
        ),
        'Symfony\\Polyfill\\Ctype\\' => 
        array (
            0 => __DIR__ . '/..' .
'/symfony/polyfill-ctype',
        ),
        'Symfony\\Component\\Yaml\\' => 
        array (
            0 => __DIR__ . '/..' . '/symfony/yaml',
        ),
        'Symfony\\Component\\EventDispatcher\\' => 
        array (
            0 => __DIR__ . '/..' .
'/symfony/event-dispatcher',
        ),
        'RocketTheme\\Toolbox\\StreamWrapper\\' => 
        array (
            0 => __DIR__ . '/..' .
'/rockettheme/toolbox/StreamWrapper/src',
        ),
        'RocketTheme\\Toolbox\\Session\\' => 
        array (
            0 => __DIR__ . '/..' .
'/rockettheme/toolbox/Session/src',
        ),
        'RocketTheme\\Toolbox\\ResourceLocator\\' => 
        array (
            0 => __DIR__ . '/..' .
'/rockettheme/toolbox/ResourceLocator/src',
        ),
        'RocketTheme\\Toolbox\\File\\' => 
        array (
            0 => __DIR__ . '/..' .
'/rockettheme/toolbox/File/src',
        ),
        'RocketTheme\\Toolbox\\Event\\' => 
        array (
            0 => __DIR__ . '/..' .
'/rockettheme/toolbox/Event/src',
        ),
        'RocketTheme\\Toolbox\\DI\\' => 
        array (
            0 => __DIR__ . '/..' .
'/rockettheme/toolbox/DI/src',
        ),
        'RocketTheme\\Toolbox\\Compat\\' => 
        array (
            0 => __DIR__ . '/..' .
'/rockettheme/toolbox/Compat/src',
        ),
        'RocketTheme\\Toolbox\\Blueprints\\' => 
        array (
            0 => __DIR__ . '/..' .
'/rockettheme/toolbox/Blueprints/src',
        ),
        'RocketTheme\\Toolbox\\ArrayTraits\\' => 
        array (
            0 => __DIR__ . '/..' .
'/rockettheme/toolbox/ArrayTraits/src',
        ),
        'Psr\\Log\\' => 
        array (
            0 => __DIR__ . '/..' .
'/psr/log/Psr/Log',
        ),
        'Psr\\Container\\' => 
        array (
            0 => __DIR__ . '/..' .
'/psr/container/src',
        ),
        'Leafo\\ScssPhp\\' => 
        array (
            0 => __DIR__ . '/../..' .
'/classes/Leafo/ScssPhp',
            1 => __DIR__ . '/..' .
'/leafo/scssphp/src',
        ),
        'Gantry\\' => 
        array (
            0 => __DIR__ . '/../..' .
'/classes/Gantry',
        ),
    );

    public static $prefixesPsr0 = array (
        'T' => 
        array (
            'Twig_' => 
            array (
                0 => __DIR__ . '/..' .
'/twig/twig/lib',
            ),
        ),
        'P' => 
        array (
            'Pimple' => 
            array (
                0 => __DIR__ . '/..' .
'/pimple/pimple/src',
            ),
            'ParsedownExtra' => 
            array (
                0 => __DIR__ . '/..' .
'/erusev/parsedown-extra',
            ),
            'Parsedown' => 
            array (
                0 => __DIR__ . '/..' .
'/erusev/parsedown',
            ),
        ),
    );

    public static $classMap = array (
        'Composer\\InstalledVersions' => __DIR__ .
'/..' . '/composer/InstalledVersions.php',
    );

    public static function getInitializer(ClassLoader $loader)
    {
        return \Closure::bind(function () use ($loader) {
            $loader->prefixLengthsPsr4 =
ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::$prefixLengthsPsr4;
            $loader->prefixDirsPsr4 =
ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::$prefixDirsPsr4;
            $loader->prefixesPsr0 =
ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::$prefixesPsr0;
            $loader->classMap =
ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::$classMap;

        }, null, ClassLoader::class);
    }
}
vendor/composer/ClassLoader.php000064400000032243151166614520012610
0ustar00<?php

/*
 * This file is part of Composer.
 *
 * (c) Nils Adermann <naderman@naderman.de>
 *     Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Composer\Autoload;

/**
 * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
 *
 *     $loader = new \Composer\Autoload\ClassLoader();
 *
 *     // register classes with namespaces
 *     $loader->add('Symfony\Component',
__DIR__.'/component');
 *     $loader->add('Symfony',          
__DIR__.'/framework');
 *
 *     // activate the autoloader
 *     $loader->register();
 *
 *     // to enable searching the include path (eg. for PEAR packages)
 *     $loader->setUseIncludePath(true);
 *
 * In this example, if you try to use a class in the Symfony\Component
 * namespace or one of its children (Symfony\Component\Console for
instance),
 * the autoloader will first look for the class under the component/
 * directory, and it will then fallback to the framework/ directory if not
 * found before giving up.
 *
 * This class is loosely based on the Symfony UniversalClassLoader.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @see    https://www.php-fig.org/psr/psr-0/
 * @see    https://www.php-fig.org/psr/psr-4/
 */
class ClassLoader
{
    // PSR-4
    private $prefixLengthsPsr4 = array();
    private $prefixDirsPsr4 = array();
    private $fallbackDirsPsr4 = array();

    // PSR-0
    private $prefixesPsr0 = array();
    private $fallbackDirsPsr0 = array();

    private $useIncludePath = false;
    private $classMap = array();
    private $classMapAuthoritative = false;
    private $missingClasses = array();
    private $apcuPrefix;

    public function getPrefixes()
    {
        if (!empty($this->prefixesPsr0)) {
            return call_user_func_array('array_merge',
array_values($this->prefixesPsr0));
        }

        return array();
    }

    public function getPrefixesPsr4()
    {
        return $this->prefixDirsPsr4;
    }

    public function getFallbackDirs()
    {
        return $this->fallbackDirsPsr0;
    }

    public function getFallbackDirsPsr4()
    {
        return $this->fallbackDirsPsr4;
    }

    public function getClassMap()
    {
        return $this->classMap;
    }

    /**
     * @param array $classMap Class to filename map
     */
    public function addClassMap(array $classMap)
    {
        if ($this->classMap) {
            $this->classMap = array_merge($this->classMap,
$classMap);
        } else {
            $this->classMap = $classMap;
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix, either
     * appending or prepending to the ones previously set for this prefix.
     *
     * @param string       $prefix  The prefix
     * @param array|string $paths   The PSR-0 root directories
     * @param bool         $prepend Whether to prepend the directories
     */
    public function add($prefix, $paths, $prepend = false)
    {
        if (!$prefix) {
            if ($prepend) {
                $this->fallbackDirsPsr0 = array_merge(
                    (array) $paths,
                    $this->fallbackDirsPsr0
                );
            } else {
                $this->fallbackDirsPsr0 = array_merge(
                    $this->fallbackDirsPsr0,
                    (array) $paths
                );
            }

            return;
        }

        $first = $prefix[0];
        if (!isset($this->prefixesPsr0[$first][$prefix])) {
            $this->prefixesPsr0[$first][$prefix] = (array) $paths;

            return;
        }
        if ($prepend) {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                (array) $paths,
                $this->prefixesPsr0[$first][$prefix]
            );
        } else {
            $this->prefixesPsr0[$first][$prefix] = array_merge(
                $this->prefixesPsr0[$first][$prefix],
                (array) $paths
            );
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace, either
     * appending or prepending to the ones previously set for this
namespace.
     *
     * @param string       $prefix  The prefix/namespace, with trailing
'\\'
     * @param array|string $paths   The PSR-4 base directories
     * @param bool         $prepend Whether to prepend the directories
     *
     * @throws \InvalidArgumentException
     */
    public function addPsr4($prefix, $paths, $prepend = false)
    {
        if (!$prefix) {
            // Register directories for the root namespace.
            if ($prepend) {
                $this->fallbackDirsPsr4 = array_merge(
                    (array) $paths,
                    $this->fallbackDirsPsr4
                );
            } else {
                $this->fallbackDirsPsr4 = array_merge(
                    $this->fallbackDirsPsr4,
                    (array) $paths
                );
            }
        } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
            // Register directories for a new namespace.
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4
prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
        } elseif ($prepend) {
            // Prepend directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                (array) $paths,
                $this->prefixDirsPsr4[$prefix]
            );
        } else {
            // Append directories for an already registered namespace.
            $this->prefixDirsPsr4[$prefix] = array_merge(
                $this->prefixDirsPsr4[$prefix],
                (array) $paths
            );
        }
    }

    /**
     * Registers a set of PSR-0 directories for a given prefix,
     * replacing any others previously set for this prefix.
     *
     * @param string       $prefix The prefix
     * @param array|string $paths  The PSR-0 base directories
     */
    public function set($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr0 = (array) $paths;
        } else {
            $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
        }
    }

    /**
     * Registers a set of PSR-4 directories for a given namespace,
     * replacing any others previously set for this namespace.
     *
     * @param string       $prefix The prefix/namespace, with trailing
'\\'
     * @param array|string $paths  The PSR-4 base directories
     *
     * @throws \InvalidArgumentException
     */
    public function setPsr4($prefix, $paths)
    {
        if (!$prefix) {
            $this->fallbackDirsPsr4 = (array) $paths;
        } else {
            $length = strlen($prefix);
            if ('\\' !== $prefix[$length - 1]) {
                throw new \InvalidArgumentException("A non-empty PSR-4
prefix must end with a namespace separator.");
            }
            $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
            $this->prefixDirsPsr4[$prefix] = (array) $paths;
        }
    }

    /**
     * Turns on searching the include path for class files.
     *
     * @param bool $useIncludePath
     */
    public function setUseIncludePath($useIncludePath)
    {
        $this->useIncludePath = $useIncludePath;
    }

    /**
     * Can be used to check if the autoloader uses the include path to
check
     * for classes.
     *
     * @return bool
     */
    public function getUseIncludePath()
    {
        return $this->useIncludePath;
    }

    /**
     * Turns off searching the prefix and fallback directories for classes
     * that have not been registered with the class map.
     *
     * @param bool $classMapAuthoritative
     */
    public function setClassMapAuthoritative($classMapAuthoritative)
    {
        $this->classMapAuthoritative = $classMapAuthoritative;
    }

    /**
     * Should class lookup fail if not found in the current class map?
     *
     * @return bool
     */
    public function isClassMapAuthoritative()
    {
        return $this->classMapAuthoritative;
    }

    /**
     * APCu prefix to use to cache found/not-found classes, if the
extension is enabled.
     *
     * @param string|null $apcuPrefix
     */
    public function setApcuPrefix($apcuPrefix)
    {
        $this->apcuPrefix = function_exists('apcu_fetch')
&& filter_var(ini_get('apc.enabled'),
FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
    }

    /**
     * The APCu prefix in use, or null if APCu caching is not enabled.
     *
     * @return string|null
     */
    public function getApcuPrefix()
    {
        return $this->apcuPrefix;
    }

    /**
     * Registers this instance as an autoloader.
     *
     * @param bool $prepend Whether to prepend the autoloader or not
     */
    public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true,
$prepend);
    }

    /**
     * Unregisters this instance as an autoloader.
     */
    public function unregister()
    {
        spl_autoload_unregister(array($this, 'loadClass'));
    }

    /**
     * Loads the given class or interface.
     *
     * @param  string    $class The name of the class
     * @return bool|null True if loaded, null otherwise
     */
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            includeFile($file);

            return true;
        }
    }

    /**
     * Finds the path to the file where the class is defined.
     *
     * @param string $class The name of the class
     *
     * @return string|false The path if found, false otherwise
     */
    public function findFile($class)
    {
        // class map lookup
        if (isset($this->classMap[$class])) {
            return $this->classMap[$class];
        }
        if ($this->classMapAuthoritative ||
isset($this->missingClasses[$class])) {
            return false;
        }
        if (null !== $this->apcuPrefix) {
            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
            if ($hit) {
                return $file;
            }
        }

        $file = $this->findFileWithExtension($class, '.php');

        // Search for Hack files if we are running on HHVM
        if (false === $file && defined('HHVM_VERSION')) {
            $file = $this->findFileWithExtension($class,
'.hh');
        }

        if (null !== $this->apcuPrefix) {
            apcu_add($this->apcuPrefix.$class, $file);
        }

        if (false === $file) {
            // Remember that this class does not exist.
            $this->missingClasses[$class] = true;
        }

        return $file;
    }

    private function findFileWithExtension($class, $ext)
    {
        // PSR-4 lookup
        $logicalPathPsr4 = strtr($class, '\\',
DIRECTORY_SEPARATOR) . $ext;

        $first = $class[0];
        if (isset($this->prefixLengthsPsr4[$first])) {
            $subPath = $class;
            while (false !== $lastPos = strrpos($subPath, '\\'))
{
                $subPath = substr($subPath, 0, $lastPos);
                $search = $subPath . '\\';
                if (isset($this->prefixDirsPsr4[$search])) {
                    $pathEnd = DIRECTORY_SEPARATOR .
substr($logicalPathPsr4, $lastPos + 1);
                    foreach ($this->prefixDirsPsr4[$search] as $dir) {
                        if (file_exists($file = $dir . $pathEnd)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-4 fallback dirs
        foreach ($this->fallbackDirsPsr4 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR .
$logicalPathPsr4)) {
                return $file;
            }
        }

        // PSR-0 lookup
        if (false !== $pos = strrpos($class, '\\')) {
            // namespaced class name
            $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
                . strtr(substr($logicalPathPsr4, $pos + 1), '_',
DIRECTORY_SEPARATOR);
        } else {
            // PEAR-like class name
            $logicalPathPsr0 = strtr($class, '_',
DIRECTORY_SEPARATOR) . $ext;
        }

        if (isset($this->prefixesPsr0[$first])) {
            foreach ($this->prefixesPsr0[$first] as $prefix => $dirs)
{
                if (0 === strpos($class, $prefix)) {
                    foreach ($dirs as $dir) {
                        if (file_exists($file = $dir . DIRECTORY_SEPARATOR
. $logicalPathPsr0)) {
                            return $file;
                        }
                    }
                }
            }
        }

        // PSR-0 fallback dirs
        foreach ($this->fallbackDirsPsr0 as $dir) {
            if (file_exists($file = $dir . DIRECTORY_SEPARATOR .
$logicalPathPsr0)) {
                return $file;
            }
        }

        // PSR-0 include paths.
        if ($this->useIncludePath && $file =
stream_resolve_include_path($logicalPathPsr0)) {
            return $file;
        }

        return false;
    }
}

/**
 * Scope isolated include.
 *
 * Prevents access to $this/self from included files.
 */
function includeFile($file)
{
    include $file;
}
vendor/composer/installed.json000064400000063373151166614520012565
0ustar00{
    "packages": [
        {
            "name": "erusev/parsedown",
            "version": "1.7.4",
            "version_normalized": "1.7.4.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/erusev/parsedown.git",
                "reference":
"cb17b6477dfff935958ba01325f2e8a2bfa6dab3"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
                "reference":
"cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
                "shasum": ""
            },
            "require": {
                "ext-mbstring": "*",
                "php": ">=5.3.0"
            },
            "require-dev": {
                "phpunit/phpunit": "^4.8.35"
            },
            "time": "2019-12-30T22:54:17+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-0": {
                    "Parsedown": ""
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Emanuil Rusev",
                    "email": "hello@erusev.com",
                    "homepage": "http://erusev.com"
                }
            ],
            "description": "Parser for Markdown.",
            "homepage": "http://parsedown.org",
            "keywords": [
                "markdown",
                "parser"
            ],
            "support": {
                "issues":
"https://github.com/erusev/parsedown/issues",
                "source":
"https://github.com/erusev/parsedown/tree/1.7.x"
            },
            "install-path": "../erusev/parsedown"
        },
        {
            "name": "erusev/parsedown-extra",
            "version": "0.7.1",
            "version_normalized": "0.7.1.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/erusev/parsedown-extra.git",
                "reference":
"0db5cce7354e4b76f155d092ab5eb3981c21258c"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/erusev/parsedown-extra/zipball/0db5cce7354e4b76f155d092ab5eb3981c21258c",
                "reference":
"0db5cce7354e4b76f155d092ab5eb3981c21258c",
                "shasum": ""
            },
            "require": {
                "erusev/parsedown": "~1.4"
            },
            "time": "2015-11-01T10:19:22+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-0": {
                    "ParsedownExtra": ""
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Emanuil Rusev",
                    "email": "hello@erusev.com",
                    "homepage": "http://erusev.com"
                }
            ],
            "description": "An extension of Parsedown that
adds support for Markdown Extra.",
            "homepage":
"https://github.com/erusev/parsedown-extra",
            "keywords": [
                "markdown",
                "markdown extra",
                "parsedown",
                "parser"
            ],
            "support": {
                "issues":
"https://github.com/erusev/parsedown-extra/issues",
                "source":
"https://github.com/erusev/parsedown-extra/tree/master"
            },
            "install-path": "../erusev/parsedown-extra"
        },
        {
            "name": "filp/whoops",
            "version": "2.5.1",
            "version_normalized": "2.5.1.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/filp/whoops.git",
                "reference":
"ee9699e79d8fcdd15c107e035d7b965e4fa854ac"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/filp/whoops/zipball/ee9699e79d8fcdd15c107e035d7b965e4fa854ac",
                "reference":
"ee9699e79d8fcdd15c107e035d7b965e4fa854ac",
                "shasum": ""
            },
            "require": {
                "php": "^5.5.9 || ^7.0",
                "psr/log": "^1.0.1"
            },
            "require-dev": {
                "mockery/mockery": "^0.9 || ^1.0",
                "phpunit/phpunit": "^4.8.35 || ^5.7",
                "symfony/var-dumper": "^2.6 || ^3.0 ||
^4.0"
            },
            "suggest": {
                "symfony/var-dumper": "Pretty print complex
values better with var-dumper available",
                "whoops/soap": "Formats errors as SOAP
responses"
            },
            "time": "2019-12-21T10:00:00+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "2.2-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Whoops\\": "src/Whoops/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Filipe Dobreira",
                    "homepage":
"https://github.com/filp",
                    "role": "Developer"
                }
            ],
            "description": "php error handling for cool
kids",
            "homepage":
"https://filp.github.io/whoops/",
            "keywords": [
                "error",
                "exception",
                "handling",
                "library",
                "throwable",
                "whoops"
            ],
            "support": {
                "issues":
"https://github.com/filp/whoops/issues",
                "source":
"https://github.com/filp/whoops/tree/2.5.1"
            },
            "install-path": "../filp/whoops"
        },
        {
            "name": "leafo/scssphp",
            "version": "v0.8.4",
            "version_normalized": "0.8.4.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/leafo/scssphp.git",
                "reference":
"b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/leafo/scssphp/zipball/b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9",
                "reference":
"b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9",
                "shasum": ""
            },
            "require": {
                "php": "^5.4.0 || ^7"
            },
            "require-dev": {
                "phpunit/phpunit": "~4.6",
                "squizlabs/php_codesniffer": "~2.5",
                "twbs/bootstrap": "~4.3",
                "zurb/foundation": "~6.5"
            },
            "time": "2019-06-18T21:15:44+00:00",
            "bin": [
                "bin/pscss"
            ],
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Leafo\\ScssPhp\\": "src/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Leaf Corcoran",
                    "email": "leafot@gmail.com",
                    "homepage": "http://leafo.net"
                }
            ],
            "description": "scssphp is a compiler for SCSS
written in PHP.",
            "homepage":
"http://leafo.github.io/scssphp/",
            "keywords": [
                "css",
                "less",
                "sass",
                "scss",
                "stylesheet"
            ],
            "support": {
                "issues":
"https://github.com/leafo/scssphp/issues",
                "source":
"https://github.com/leafo/scssphp/tree/v0.8.4"
            },
            "abandoned": "scssphp/scssphp",
            "install-path": "../leafo/scssphp"
        },
        {
            "name": "pimple/pimple",
            "version": "v3.2.3",
            "version_normalized": "3.2.3.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/silexphp/Pimple.git",
                "reference":
"9e403941ef9d65d20cba7d54e29fe906db42cf32"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
                "reference":
"9e403941ef9d65d20cba7d54e29fe906db42cf32",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.0",
                "psr/container": "^1.0"
            },
            "require-dev": {
                "symfony/phpunit-bridge": "^3.2"
            },
            "time": "2018-01-21T07:42:36+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "3.2.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-0": {
                    "Pimple": "src/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Fabien Potencier",
                    "email": "fabien@symfony.com"
                }
            ],
            "description": "Pimple, a simple Dependency
Injection Container",
            "homepage": "http://pimple.sensiolabs.org",
            "keywords": [
                "container",
                "dependency injection"
            ],
            "support": {
                "issues":
"https://github.com/silexphp/Pimple/issues",
                "source":
"https://github.com/silexphp/Pimple/tree/master"
            },
            "install-path": "../pimple/pimple"
        },
        {
            "name": "psr/container",
            "version": "1.0.0",
            "version_normalized": "1.0.0.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/php-fig/container.git",
                "reference":
"b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
                "reference":
"b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.0"
            },
            "time": "2017-02-14T16:28:37+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.0.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Psr\\Container\\": "src/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage":
"http://www.php-fig.org/"
                }
            ],
            "description": "Common Container Interface (PHP
FIG PSR-11)",
            "homepage":
"https://github.com/php-fig/container",
            "keywords": [
                "PSR-11",
                "container",
                "container-interface",
                "container-interop",
                "psr"
            ],
            "support": {
                "issues":
"https://github.com/php-fig/container/issues",
                "source":
"https://github.com/php-fig/container/tree/master"
            },
            "install-path": "../psr/container"
        },
        {
            "name": "psr/log",
            "version": "1.1.3",
            "version_normalized": "1.1.3.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/php-fig/log.git",
                "reference":
"0f73288fd15629204f9d42b7055f72dacbe811fc"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
                "reference":
"0f73288fd15629204f9d42b7055f72dacbe811fc",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.0"
            },
            "time": "2020-03-23T09:12:05+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.1.x-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Psr\\Log\\": "Psr/Log/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "PHP-FIG",
                    "homepage":
"http://www.php-fig.org/"
                }
            ],
            "description": "Common interface for logging
libraries",
            "homepage":
"https://github.com/php-fig/log",
            "keywords": [
                "log",
                "psr",
                "psr-3"
            ],
            "support": {
                "source":
"https://github.com/php-fig/log/tree/1.1.3"
            },
            "install-path": "../psr/log"
        },
        {
            "name": "rockettheme/toolbox",
            "version": "1.4.7",
            "version_normalized": "1.4.7.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/rockettheme/toolbox.git",
                "reference":
"6a86bc0607884d2194260b6b72d67333e0141585"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/rockettheme/toolbox/zipball/6a86bc0607884d2194260b6b72d67333e0141585",
                "reference":
"6a86bc0607884d2194260b6b72d67333e0141585",
                "shasum": ""
            },
            "require": {
                "ext-json": "*",
                "php": ">=5.4.0",
                "pimple/pimple": "~3.0",
                "symfony/event-dispatcher": ">2.5",
                "symfony/yaml": ">2.5"
            },
            "require-dev": {
                "phpunit/phpunit": "~6"
            },
            "time": "2020-03-19T18:24:40+00:00",
            "type": "library",
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "RocketTheme\\Toolbox\\ArrayTraits\\":
"ArrayTraits/src",
                    "RocketTheme\\Toolbox\\Blueprints\\":
"Blueprints/src",
                    "RocketTheme\\Toolbox\\Compat\\":
"Compat/src",
                    "RocketTheme\\Toolbox\\DI\\":
"DI/src",
                    "RocketTheme\\Toolbox\\Event\\":
"Event/src",
                    "RocketTheme\\Toolbox\\File\\":
"File/src",
                    "RocketTheme\\Toolbox\\ResourceLocator\\":
"ResourceLocator/src",
                    "RocketTheme\\Toolbox\\Session\\":
"Session/src",
                    "RocketTheme\\Toolbox\\StreamWrapper\\":
"StreamWrapper/src"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "description": "RocketTheme Toolbox
Library",
            "homepage": "http://www.rockettheme.com",
            "keywords": [
                "php",
                "rockettheme"
            ],
            "support": {
                "issues":
"https://github.com/rockettheme/toolbox/issues",
                "source":
"https://github.com/rockettheme/toolbox/tree/1.4.7"
            },
            "install-path": "../rockettheme/toolbox"
        },
        {
            "name": "symfony/event-dispatcher",
            "version": "v2.8.52",
            "version_normalized": "2.8.52.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/symfony/event-dispatcher.git",
                "reference":
"a77e974a5fecb4398833b0709210e3d5e334ffb0"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0",
                "reference":
"a77e974a5fecb4398833b0709210e3d5e334ffb0",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.9"
            },
            "require-dev": {
                "psr/log": "~1.0",
                "symfony/config": "^2.0.5|~3.0.0",
                "symfony/dependency-injection":
"~2.6|~3.0.0",
                "symfony/expression-language":
"~2.6|~3.0.0",
                "symfony/stopwatch": "~2.3|~3.0.0"
            },
            "suggest": {
                "symfony/dependency-injection": "",
                "symfony/http-kernel": ""
            },
            "time": "2018-11-21T14:20:20+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "2.8-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Symfony\\Component\\EventDispatcher\\":
""
                },
                "exclude-from-classmap": [
                    "/Tests/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Fabien Potencier",
                    "email": "fabien@symfony.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage":
"https://symfony.com/contributors"
                }
            ],
            "description": "Symfony EventDispatcher
Component",
            "homepage": "https://symfony.com",
            "support": {
                "source":
"https://github.com/symfony/event-dispatcher/tree/v2.8.50"
            },
            "install-path":
"../symfony/event-dispatcher"
        },
        {
            "name": "symfony/polyfill-ctype",
            "version": "v1.19.0",
            "version_normalized": "1.19.0.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/symfony/polyfill-ctype.git",
                "reference":
"aed596913b70fae57be53d86faa2e9ef85a2297b"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b",
                "reference":
"aed596913b70fae57be53d86faa2e9ef85a2297b",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.3"
            },
            "suggest": {
                "ext-ctype": "For best performance"
            },
            "time": "2020-10-23T09:01:57+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-main": "1.19-dev"
                },
                "thanks": {
                    "name": "symfony/polyfill",
                    "url":
"https://github.com/symfony/polyfill"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Symfony\\Polyfill\\Ctype\\": ""
                },
                "files": [
                    "bootstrap.php"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Gert de Pagter",
                    "email": "BackEndTea@gmail.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage":
"https://symfony.com/contributors"
                }
            ],
            "description": "Symfony polyfill for ctype
functions",
            "homepage": "https://symfony.com",
            "keywords": [
                "compatibility",
                "ctype",
                "polyfill",
                "portable"
            ],
            "support": {
                "source":
"https://github.com/symfony/polyfill-ctype/tree/v1.19.0"
            },
            "funding": [
                {
                    "url":
"https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url":
"https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "install-path": "../symfony/polyfill-ctype"
        },
        {
            "name": "symfony/yaml",
            "version": "v2.8.52",
            "version_normalized": "2.8.52.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/symfony/yaml.git",
                "reference":
"02c1859112aa779d9ab394ae4f3381911d84052b"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b",
                "reference":
"02c1859112aa779d9ab394ae4f3381911d84052b",
                "shasum": ""
            },
            "require": {
                "php": ">=5.3.9",
                "symfony/polyfill-ctype": "~1.8"
            },
            "time": "2018-11-11T11:18:13+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "2.8-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-4": {
                    "Symfony\\Component\\Yaml\\": ""
                },
                "exclude-from-classmap": [
                    "/Tests/"
                ]
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "MIT"
            ],
            "authors": [
                {
                    "name": "Fabien Potencier",
                    "email": "fabien@symfony.com"
                },
                {
                    "name": "Symfony Community",
                    "homepage":
"https://symfony.com/contributors"
                }
            ],
            "description": "Symfony Yaml Component",
            "homepage": "https://symfony.com",
            "support": {
                "source":
"https://github.com/symfony/yaml/tree/v2.8.52"
            },
            "install-path": "../symfony/yaml"
        },
        {
            "name": "twig/twig",
            "version": "v1.42.5",
            "version_normalized": "1.42.5.0",
            "source": {
                "type": "git",
                "url":
"https://github.com/twigphp/Twig.git",
                "reference":
"87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e"
            },
            "dist": {
                "type": "zip",
                "url":
"https://api.github.com/repos/twigphp/Twig/zipball/87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e",
                "reference":
"87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e",
                "shasum": ""
            },
            "require": {
                "php": ">=5.5.0",
                "symfony/polyfill-ctype": "^1.8"
            },
            "require-dev": {
                "psr/container": "^1.0",
                "symfony/phpunit-bridge": "^4.4|^5.0"
            },
            "time": "2020-02-11T05:59:23+00:00",
            "type": "library",
            "extra": {
                "branch-alias": {
                    "dev-master": "1.42-dev"
                }
            },
            "installation-source": "dist",
            "autoload": {
                "psr-0": {
                    "Twig_": "lib/"
                },
                "psr-4": {
                    "Twig\\": "src/"
                }
            },
            "notification-url":
"https://packagist.org/downloads/",
            "license": [
                "BSD-3-Clause"
            ],
            "authors": [
                {
                    "name": "Fabien Potencier",
                    "email": "fabien@symfony.com",
                    "homepage":
"http://fabien.potencier.org",
                    "role": "Lead Developer"
                },
                {
                    "name": "Twig Team",
                    "role": "Contributors"
                },
                {
                    "name": "Armin Ronacher",
                    "email":
"armin.ronacher@active-4.com",
                    "role": "Project Founder"
                }
            ],
            "description": "Twig, the flexible, fast, and
secure template language for PHP",
            "homepage": "https://twig.symfony.com",
            "keywords": [
                "templating"
            ],
            "support": {
                "issues":
"https://github.com/twigphp/Twig/issues",
                "source":
"https://github.com/twigphp/Twig/tree/1.x"
            },
            "install-path": "../twig/twig"
        }
    ],
    "dev": false,
    "dev-package-names": []
}
vendor/composer/installed.php000064400000006067151166614520012400
0ustar00<?php return array (
  'root' => 
  array (
    'pretty_version' => '5.4.37',
    'version' => '5.4.37.0',
    'aliases' => 
    array (
    ),
    'reference' =>
'a701c1159e7a000861a1b2094ed392f752343e72',
    'name' => 'gantry/joomla',
  ),
  'versions' => 
  array (
    'erusev/parsedown' => 
    array (
      'pretty_version' => '1.7.4',
      'version' => '1.7.4.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'cb17b6477dfff935958ba01325f2e8a2bfa6dab3',
    ),
    'erusev/parsedown-extra' => 
    array (
      'pretty_version' => '0.7.1',
      'version' => '0.7.1.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'0db5cce7354e4b76f155d092ab5eb3981c21258c',
    ),
    'filp/whoops' => 
    array (
      'pretty_version' => '2.5.1',
      'version' => '2.5.1.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'ee9699e79d8fcdd15c107e035d7b965e4fa854ac',
    ),
    'gantry/joomla' => 
    array (
      'pretty_version' => '5.4.37',
      'version' => '5.4.37.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'a701c1159e7a000861a1b2094ed392f752343e72',
    ),
    'leafo/scssphp' => 
    array (
      'pretty_version' => 'v0.8.4',
      'version' => '0.8.4.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9',
    ),
    'pimple/pimple' => 
    array (
      'pretty_version' => 'v3.2.3',
      'version' => '3.2.3.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'9e403941ef9d65d20cba7d54e29fe906db42cf32',
    ),
    'psr/container' => 
    array (
      'pretty_version' => '1.0.0',
      'version' => '1.0.0.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'b7ce3b176482dbbc1245ebf52b181af44c2cf55f',
    ),
    'psr/log' => 
    array (
      'pretty_version' => '1.1.3',
      'version' => '1.1.3.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'0f73288fd15629204f9d42b7055f72dacbe811fc',
    ),
    'rockettheme/toolbox' => 
    array (
      'pretty_version' => '1.4.7',
      'version' => '1.4.7.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'6a86bc0607884d2194260b6b72d67333e0141585',
    ),
    'symfony/event-dispatcher' => 
    array (
      'pretty_version' => 'v2.8.52',
      'version' => '2.8.52.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'a77e974a5fecb4398833b0709210e3d5e334ffb0',
    ),
    'symfony/polyfill-ctype' => 
    array (
      'pretty_version' => 'v1.19.0',
      'version' => '1.19.0.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'aed596913b70fae57be53d86faa2e9ef85a2297b',
    ),
    'symfony/yaml' => 
    array (
      'pretty_version' => 'v2.8.52',
      'version' => '2.8.52.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'02c1859112aa779d9ab394ae4f3381911d84052b',
    ),
    'twig/twig' => 
    array (
      'pretty_version' => 'v1.42.5',
      'version' => '1.42.5.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e',
    ),
  ),
);
vendor/composer/InstalledVersions.php000064400000013542151166614520014065
0ustar00<?php











namespace Composer;

use Composer\Semver\VersionParser;






class InstalledVersions
{
private static $installed = array (
  'root' => 
  array (
    'pretty_version' => '5.4.37',
    'version' => '5.4.37.0',
    'aliases' => 
    array (
    ),
    'reference' =>
'a701c1159e7a000861a1b2094ed392f752343e72',
    'name' => 'gantry/joomla',
  ),
  'versions' => 
  array (
    'erusev/parsedown' => 
    array (
      'pretty_version' => '1.7.4',
      'version' => '1.7.4.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'cb17b6477dfff935958ba01325f2e8a2bfa6dab3',
    ),
    'erusev/parsedown-extra' => 
    array (
      'pretty_version' => '0.7.1',
      'version' => '0.7.1.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'0db5cce7354e4b76f155d092ab5eb3981c21258c',
    ),
    'filp/whoops' => 
    array (
      'pretty_version' => '2.5.1',
      'version' => '2.5.1.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'ee9699e79d8fcdd15c107e035d7b965e4fa854ac',
    ),
    'gantry/joomla' => 
    array (
      'pretty_version' => '5.4.37',
      'version' => '5.4.37.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'a701c1159e7a000861a1b2094ed392f752343e72',
    ),
    'leafo/scssphp' => 
    array (
      'pretty_version' => 'v0.8.4',
      'version' => '0.8.4.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9',
    ),
    'pimple/pimple' => 
    array (
      'pretty_version' => 'v3.2.3',
      'version' => '3.2.3.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'9e403941ef9d65d20cba7d54e29fe906db42cf32',
    ),
    'psr/container' => 
    array (
      'pretty_version' => '1.0.0',
      'version' => '1.0.0.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'b7ce3b176482dbbc1245ebf52b181af44c2cf55f',
    ),
    'psr/log' => 
    array (
      'pretty_version' => '1.1.3',
      'version' => '1.1.3.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'0f73288fd15629204f9d42b7055f72dacbe811fc',
    ),
    'rockettheme/toolbox' => 
    array (
      'pretty_version' => '1.4.7',
      'version' => '1.4.7.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'6a86bc0607884d2194260b6b72d67333e0141585',
    ),
    'symfony/event-dispatcher' => 
    array (
      'pretty_version' => 'v2.8.52',
      'version' => '2.8.52.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'a77e974a5fecb4398833b0709210e3d5e334ffb0',
    ),
    'symfony/polyfill-ctype' => 
    array (
      'pretty_version' => 'v1.19.0',
      'version' => '1.19.0.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'aed596913b70fae57be53d86faa2e9ef85a2297b',
    ),
    'symfony/yaml' => 
    array (
      'pretty_version' => 'v2.8.52',
      'version' => '2.8.52.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'02c1859112aa779d9ab394ae4f3381911d84052b',
    ),
    'twig/twig' => 
    array (
      'pretty_version' => 'v1.42.5',
      'version' => '1.42.5.0',
      'aliases' => 
      array (
      ),
      'reference' =>
'87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e',
    ),
  ),
);







public static function getInstalledPackages()
{
return array_keys(self::$installed['versions']);
}









public static function isInstalled($packageName)
{
return isset(self::$installed['versions'][$packageName]);
}














public static function satisfies(VersionParser $parser, $packageName,
$constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided =
$parser->parseConstraints(self::getVersionRanges($packageName));

return $provided->matches($constraint);
}










public static function getVersionRanges($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName .
'" is not installed');
}

$ranges = array();
if
(isset(self::$installed['versions'][$packageName]['pretty_version']))
{
$ranges[] =
self::$installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases',
self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges,
self::$installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced',
self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges,
self::$installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided',
self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges,
self::$installed['versions'][$packageName]['provided']);
}

return implode(' || ', $ranges);
}





public static function getVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName .
'" is not installed');
}

if
(!isset(self::$installed['versions'][$packageName]['version']))
{
return null;
}

return
self::$installed['versions'][$packageName]['version'];
}





public static function getPrettyVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName .
'" is not installed');
}

if
(!isset(self::$installed['versions'][$packageName]['pretty_version']))
{
return null;
}

return
self::$installed['versions'][$packageName]['pretty_version'];
}





public static function getReference($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName .
'" is not installed');
}

if
(!isset(self::$installed['versions'][$packageName]['reference']))
{
return null;
}

return
self::$installed['versions'][$packageName]['reference'];
}





public static function getRootPackage()
{
return self::$installed['root'];
}







public static function getRawData()
{
return self::$installed;
}



















public static function reload($data)
{
self::$installed = $data;
}
}
vendor/composer/platform_check.php000064400000001635151166614520013376
0ustar00<?php

// platform_check.php @generated by Composer

$issues = array();

if (!(PHP_VERSION_ID >= 50509)) {
    $issues[] = 'Your Composer dependencies require a PHP version
">= 5.5.9". You are running ' . PHP_VERSION .
'.';
}

if ($issues) {
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }
    if (!ini_get('display_errors')) {
        if (PHP_SAPI === 'cli' || PHP_SAPI ===
'phpdbg') {
            fwrite(STDERR, 'Composer detected issues in your
platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) .
PHP_EOL.PHP_EOL);
        } elseif (!headers_sent()) {
            echo 'Composer detected issues in your platform:' .
PHP_EOL.PHP_EOL . str_replace('You are running
'.PHP_VERSION.'.', '', implode(PHP_EOL, $issues))
. PHP_EOL.PHP_EOL;
        }
    }
    trigger_error(
        'Composer detected issues in your platform: ' .
implode(' ', $issues),
        E_USER_ERROR
    );
}
vendor/erusev/parsedown/composer.json000064400000001423151166614520014105
0ustar00{
    "name": "erusev/parsedown",
    "description": "Parser for Markdown.",
    "keywords": ["markdown", "parser"],
    "homepage": "http://parsedown.org",
    "type": "library",
    "license": "MIT",
    "authors": [
        {
            "name": "Emanuil Rusev",
            "email": "hello@erusev.com",
            "homepage": "http://erusev.com"
        }
    ],
    "require": {
        "php": ">=5.3.0",
        "ext-mbstring": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "^4.8.35"
    },
    "autoload": {
        "psr-0": {"Parsedown": ""}
    },
    "autoload-dev": {
        "psr-0": {
            "TestParsedown": "test/",
            "ParsedownTest": "test/",
            "CommonMarkTest": "test/",
            "CommonMarkTestWeak": "test/"
        }
    }
}
vendor/erusev/parsedown/Parsedown.php000064400000123225151166614520014043
0ustar00<?php

#
#
# Parsedown
# http://parsedown.org
#
# (c) Emanuil Rusev
# http://erusev.com
#
# For the full license information, view the LICENSE file that was
distributed
# with this source code.
#
#

class Parsedown
{
    # ~

    const version = '1.7.4';

    # ~

    function text($text)
    {
        # make sure no definitions are set
        $this->DefinitionData = array();

        # standardize line breaks
        $text = str_replace(array("\r\n", "\r"),
"\n", $text);

        # remove surrounding line breaks
        $text = trim($text, "\n");

        # split text into lines
        $lines = explode("\n", $text);

        # iterate through lines to identify blocks
        $markup = $this->lines($lines);

        # trim line breaks
        $markup = trim($markup, "\n");

        return $markup;
    }

    #
    # Setters
    #

    function setBreaksEnabled($breaksEnabled)
    {
        $this->breaksEnabled = $breaksEnabled;

        return $this;
    }

    protected $breaksEnabled;

    function setMarkupEscaped($markupEscaped)
    {
        $this->markupEscaped = $markupEscaped;

        return $this;
    }

    protected $markupEscaped;

    function setUrlsLinked($urlsLinked)
    {
        $this->urlsLinked = $urlsLinked;

        return $this;
    }

    protected $urlsLinked = true;

    function setSafeMode($safeMode)
    {
        $this->safeMode = (bool) $safeMode;

        return $this;
    }

    protected $safeMode;

    protected $safeLinksWhitelist = array(
        'http://',
        'https://',
        'ftp://',
        'ftps://',
        'mailto:',
        'data:image/png;base64,',
        'data:image/gif;base64,',
        'data:image/jpeg;base64,',
        'irc:',
        'ircs:',
        'git:',
        'ssh:',
        'news:',
        'steam:',
    );

    #
    # Lines
    #

    protected $BlockTypes = array(
        '#' => array('Header'),
        '*' => array('Rule', 'List'),
        '+' => array('List'),
        '-' => array('SetextHeader',
'Table', 'Rule', 'List'),
        '0' => array('List'),
        '1' => array('List'),
        '2' => array('List'),
        '3' => array('List'),
        '4' => array('List'),
        '5' => array('List'),
        '6' => array('List'),
        '7' => array('List'),
        '8' => array('List'),
        '9' => array('List'),
        ':' => array('Table'),
        '<' => array('Comment',
'Markup'),
        '=' => array('SetextHeader'),
        '>' => array('Quote'),
        '[' => array('Reference'),
        '_' => array('Rule'),
        '`' => array('FencedCode'),
        '|' => array('Table'),
        '~' => array('FencedCode'),
    );

    # ~

    protected $unmarkedBlockTypes = array(
        'Code',
    );

    #
    # Blocks
    #

    protected function lines(array $lines)
    {
        $CurrentBlock = null;

        foreach ($lines as $line)
        {
            if (chop($line) === '')
            {
                if (isset($CurrentBlock))
                {
                    $CurrentBlock['interrupted'] = true;
                }

                continue;
            }

            if (strpos($line, "\t") !== false)
            {
                $parts = explode("\t", $line);

                $line = $parts[0];

                unset($parts[0]);

                foreach ($parts as $part)
                {
                    $shortage = 4 - mb_strlen($line, 'utf-8') %
4;

                    $line .= str_repeat(' ', $shortage);
                    $line .= $part;
                }
            }

            $indent = 0;

            while (isset($line[$indent]) and $line[$indent] === '
')
            {
                $indent ++;
            }

            $text = $indent > 0 ? substr($line, $indent) : $line;

            # ~

            $Line = array('body' => $line, 'indent'
=> $indent, 'text' => $text);

            # ~

            if (isset($CurrentBlock['continuable']))
            {
                $Block =
$this->{'block'.$CurrentBlock['type'].'Continue'}($Line,
$CurrentBlock);

                if (isset($Block))
                {
                    $CurrentBlock = $Block;

                    continue;
                }
                else
                {
                    if
($this->isBlockCompletable($CurrentBlock['type']))
                    {
                        $CurrentBlock =
$this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
                    }
                }
            }

            # ~

            $marker = $text[0];

            # ~

            $blockTypes = $this->unmarkedBlockTypes;

            if (isset($this->BlockTypes[$marker]))
            {
                foreach ($this->BlockTypes[$marker] as $blockType)
                {
                    $blockTypes []= $blockType;
                }
            }

            #
            # ~

            foreach ($blockTypes as $blockType)
            {
                $Block = $this->{'block'.$blockType}($Line,
$CurrentBlock);

                if (isset($Block))
                {
                    $Block['type'] = $blockType;

                    if ( ! isset($Block['identified']))
                    {
                        $Blocks []= $CurrentBlock;

                        $Block['identified'] = true;
                    }

                    if ($this->isBlockContinuable($blockType))
                    {
                        $Block['continuable'] = true;
                    }

                    $CurrentBlock = $Block;

                    continue 2;
                }
            }

            # ~

            if (isset($CurrentBlock) and !
isset($CurrentBlock['type']) and !
isset($CurrentBlock['interrupted']))
            {
                $CurrentBlock['element']['text'] .=
"\n".$text;
            }
            else
            {
                $Blocks []= $CurrentBlock;

                $CurrentBlock = $this->paragraph($Line);

                $CurrentBlock['identified'] = true;
            }
        }

        # ~

        if (isset($CurrentBlock['continuable']) and
$this->isBlockCompletable($CurrentBlock['type']))
        {
            $CurrentBlock =
$this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
        }

        # ~

        $Blocks []= $CurrentBlock;

        unset($Blocks[0]);

        # ~

        $markup = '';

        foreach ($Blocks as $Block)
        {
            if (isset($Block['hidden']))
            {
                continue;
            }

            $markup .= "\n";
            $markup .= isset($Block['markup']) ?
$Block['markup'] :
$this->element($Block['element']);
        }

        $markup .= "\n";

        # ~

        return $markup;
    }

    protected function isBlockContinuable($Type)
    {
        return method_exists($this,
'block'.$Type.'Continue');
    }

    protected function isBlockCompletable($Type)
    {
        return method_exists($this,
'block'.$Type.'Complete');
    }

    #
    # Code

    protected function blockCode($Line, $Block = null)
    {
        if (isset($Block) and ! isset($Block['type']) and !
isset($Block['interrupted']))
        {
            return;
        }

        if ($Line['indent'] >= 4)
        {
            $text = substr($Line['body'], 4);

            $Block = array(
                'element' => array(
                    'name' => 'pre',
                    'handler' => 'element',
                    'text' => array(
                        'name' => 'code',
                        'text' => $text,
                    ),
                ),
            );

            return $Block;
        }
    }

    protected function blockCodeContinue($Line, $Block)
    {
        if ($Line['indent'] >= 4)
        {
            if (isset($Block['interrupted']))
            {
               
$Block['element']['text']['text'] .=
"\n";

                unset($Block['interrupted']);
            }

            $Block['element']['text']['text']
.= "\n";

            $text = substr($Line['body'], 4);

            $Block['element']['text']['text']
.= $text;

            return $Block;
        }
    }

    protected function blockCodeComplete($Block)
    {
        $text =
$Block['element']['text']['text'];

        $Block['element']['text']['text'] =
$text;

        return $Block;
    }

    #
    # Comment

    protected function blockComment($Line)
    {
        if ($this->markupEscaped or $this->safeMode)
        {
            return;
        }

        if (isset($Line['text'][3]) and
$Line['text'][3] === '-' and $Line['text'][2]
=== '-' and $Line['text'][1] === '!')
        {
            $Block = array(
                'markup' => $Line['body'],
            );

            if (preg_match('/-->$/', $Line['text']))
            {
                $Block['closed'] = true;
            }

            return $Block;
        }
    }

    protected function blockCommentContinue($Line, array $Block)
    {
        if (isset($Block['closed']))
        {
            return;
        }

        $Block['markup'] .= "\n" .
$Line['body'];

        if (preg_match('/-->$/', $Line['text']))
        {
            $Block['closed'] = true;
        }

        return $Block;
    }

    #
    # Fenced Code

    protected function blockFencedCode($Line)
    {
        if
(preg_match('/^['.$Line['text'][0].']{3,}[
]*([^`]+)?[ ]*$/', $Line['text'], $matches))
        {
            $Element = array(
                'name' => 'code',
                'text' => '',
            );

            if (isset($matches[1]))
            {
                /**
                 *
https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
                 * Every HTML element may have a class attribute specified.
                 * The attribute, if specified, must have a value that is a
set
                 * of space-separated tokens representing the various
classes
                 * that the element belongs to.
                 * [...]
                 * The space characters, for the purposes of this
specification,
                 * are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab),
                 * U+000A LINE FEED (LF), U+000C FORM FEED (FF), and
                 * U+000D CARRIAGE RETURN (CR).
                 */
                $language = substr($matches[1], 0, strcspn($matches[1],
" \t\n\f\r"));

                $class = 'language-'.$language;

                $Element['attributes'] = array(
                    'class' => $class,
                );
            }

            $Block = array(
                'char' => $Line['text'][0],
                'element' => array(
                    'name' => 'pre',
                    'handler' => 'element',
                    'text' => $Element,
                ),
            );

            return $Block;
        }
    }

    protected function blockFencedCodeContinue($Line, $Block)
    {
        if (isset($Block['complete']))
        {
            return;
        }

        if (isset($Block['interrupted']))
        {
            $Block['element']['text']['text']
.= "\n";

            unset($Block['interrupted']);
        }

        if (preg_match('/^'.$Block['char'].'{3,}[
]*$/', $Line['text']))
        {
            $Block['element']['text']['text']
= substr($Block['element']['text']['text'],
1);

            $Block['complete'] = true;

            return $Block;
        }

        $Block['element']['text']['text'] .=
"\n".$Line['body'];

        return $Block;
    }

    protected function blockFencedCodeComplete($Block)
    {
        $text =
$Block['element']['text']['text'];

        $Block['element']['text']['text'] =
$text;

        return $Block;
    }

    #
    # Header

    protected function blockHeader($Line)
    {
        if (isset($Line['text'][1]))
        {
            $level = 1;

            while (isset($Line['text'][$level]) and
$Line['text'][$level] === '#')
            {
                $level ++;
            }

            if ($level > 6)
            {
                return;
            }

            $text = trim($Line['text'], '# ');

            $Block = array(
                'element' => array(
                    'name' => 'h' . min(6, $level),
                    'text' => $text,
                    'handler' => 'line',
                ),
            );

            return $Block;
        }
    }

    #
    # List

    protected function blockList($Line)
    {
        list($name, $pattern) = $Line['text'][0] <=
'-' ? array('ul', '[*+-]') :
array('ol', '[0-9]+[.]');

        if (preg_match('/^('.$pattern.'[ ]+)(.*)/',
$Line['text'], $matches))
        {
            $Block = array(
                'indent' => $Line['indent'],
                'pattern' => $pattern,
                'element' => array(
                    'name' => $name,
                    'handler' => 'elements',
                ),
            );

            if($name === 'ol')
            {
                $listStart = stristr($matches[0], '.', true);

                if($listStart !== '1')
                {
                    $Block['element']['attributes'] =
array('start' => $listStart);
                }
            }

            $Block['li'] = array(
                'name' => 'li',
                'handler' => 'li',
                'text' => array(
                    $matches[2],
                ),
            );

            $Block['element']['text'] []= &
$Block['li'];

            return $Block;
        }
    }

    protected function blockListContinue($Line, array $Block)
    {
        if ($Block['indent'] === $Line['indent'] and
preg_match('/^'.$Block['pattern'].'(?:[
]+(.*)|$)/', $Line['text'], $matches))
        {
            if (isset($Block['interrupted']))
            {
                $Block['li']['text'] []= '';

                $Block['loose'] = true;

                unset($Block['interrupted']);
            }

            unset($Block['li']);

            $text = isset($matches[1]) ? $matches[1] : '';

            $Block['li'] = array(
                'name' => 'li',
                'handler' => 'li',
                'text' => array(
                    $text,
                ),
            );

            $Block['element']['text'] []= &
$Block['li'];

            return $Block;
        }

        if ($Line['text'][0] === '[' and
$this->blockReference($Line))
        {
            return $Block;
        }

        if ( ! isset($Block['interrupted']))
        {
            $text = preg_replace('/^[ ]{0,4}/', '',
$Line['body']);

            $Block['li']['text'] []= $text;

            return $Block;
        }

        if ($Line['indent'] > 0)
        {
            $Block['li']['text'] []= '';

            $text = preg_replace('/^[ ]{0,4}/', '',
$Line['body']);

            $Block['li']['text'] []= $text;

            unset($Block['interrupted']);

            return $Block;
        }
    }

    protected function blockListComplete(array $Block)
    {
        if (isset($Block['loose']))
        {
            foreach ($Block['element']['text'] as
&$li)
            {
                if (end($li['text']) !== '')
                {
                    $li['text'] []= '';
                }
            }
        }

        return $Block;
    }

    #
    # Quote

    protected function blockQuote($Line)
    {
        if (preg_match('/^>[ ]?(.*)/',
$Line['text'], $matches))
        {
            $Block = array(
                'element' => array(
                    'name' => 'blockquote',
                    'handler' => 'lines',
                    'text' => (array) $matches[1],
                ),
            );

            return $Block;
        }
    }

    protected function blockQuoteContinue($Line, array $Block)
    {
        if ($Line['text'][0] === '>' and
preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
        {
            if (isset($Block['interrupted']))
            {
                $Block['element']['text'] []=
'';

                unset($Block['interrupted']);
            }

            $Block['element']['text'] []= $matches[1];

            return $Block;
        }

        if ( ! isset($Block['interrupted']))
        {
            $Block['element']['text'] []=
$Line['text'];

            return $Block;
        }
    }

    #
    # Rule

    protected function blockRule($Line)
    {
        if
(preg_match('/^(['.$Line['text'][0].'])([
]*\1){2,}[ ]*$/', $Line['text']))
        {
            $Block = array(
                'element' => array(
                    'name' => 'hr'
                ),
            );

            return $Block;
        }
    }

    #
    # Setext

    protected function blockSetextHeader($Line, array $Block = null)
    {
        if ( ! isset($Block) or isset($Block['type']) or
isset($Block['interrupted']))
        {
            return;
        }

        if (chop($Line['text'], $Line['text'][0]) ===
'')
        {
            $Block['element']['name'] =
$Line['text'][0] === '=' ? 'h1' :
'h2';

            return $Block;
        }
    }

    #
    # Markup

    protected function blockMarkup($Line)
    {
        if ($this->markupEscaped or $this->safeMode)
        {
            return;
        }

        if (preg_match('/^<(\w[\w-]*)(?:[
]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/',
$Line['text'], $matches))
        {
            $element = strtolower($matches[1]);

            if (in_array($element, $this->textLevelElements))
            {
                return;
            }

            $Block = array(
                'name' => $matches[1],
                'depth' => 0,
                'markup' => $Line['text'],
            );

            $length = strlen($matches[0]);

            $remainder = substr($Line['text'], $length);

            if (trim($remainder) === '')
            {
                if (isset($matches[2]) or in_array($matches[1],
$this->voidElements))
                {
                    $Block['closed'] = true;

                    $Block['void'] = true;
                }
            }
            else
            {
                if (isset($matches[2]) or in_array($matches[1],
$this->voidElements))
                {
                    return;
                }

                if (preg_match('/<\/'.$matches[1].'>[
]*$/i', $remainder))
                {
                    $Block['closed'] = true;
                }
            }

            return $Block;
        }
    }

    protected function blockMarkupContinue($Line, array $Block)
    {
        if (isset($Block['closed']))
        {
            return;
        }

        if
(preg_match('/^<'.$Block['name'].'(?:[
]*'.$this->regexHtmlAttribute.')*[ ]*>/i',
$Line['text'])) # open
        {
            $Block['depth'] ++;
        }

        if
(preg_match('/(.*?)<\/'.$Block['name'].'>[
]*$/i', $Line['text'], $matches)) # close
        {
            if ($Block['depth'] > 0)
            {
                $Block['depth'] --;
            }
            else
            {
                $Block['closed'] = true;
            }
        }

        if (isset($Block['interrupted']))
        {
            $Block['markup'] .= "\n";

            unset($Block['interrupted']);
        }

        $Block['markup'] .=
"\n".$Line['body'];

        return $Block;
    }

    #
    # Reference

    protected function blockReference($Line)
    {
        if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[
]+["\'(](.+)["\')])?[ ]*$/',
$Line['text'], $matches))
        {
            $id = strtolower($matches[1]);

            $Data = array(
                'url' => $matches[2],
                'title' => null,
            );

            if (isset($matches[3]))
            {
                $Data['title'] = $matches[3];
            }

            $this->DefinitionData['Reference'][$id] = $Data;

            $Block = array(
                'hidden' => true,
            );

            return $Block;
        }
    }

    #
    # Table

    protected function blockTable($Line, array $Block = null)
    {
        if ( ! isset($Block) or isset($Block['type']) or
isset($Block['interrupted']))
        {
            return;
        }

        if (strpos($Block['element']['text'],
'|') !== false and chop($Line['text'], '
-:|') === '')
        {
            $alignments = array();

            $divider = $Line['text'];

            $divider = trim($divider);
            $divider = trim($divider, '|');

            $dividerCells = explode('|', $divider);

            foreach ($dividerCells as $dividerCell)
            {
                $dividerCell = trim($dividerCell);

                if ($dividerCell === '')
                {
                    continue;
                }

                $alignment = null;

                if ($dividerCell[0] === ':')
                {
                    $alignment = 'left';
                }

                if (substr($dividerCell, - 1) === ':')
                {
                    $alignment = $alignment === 'left' ?
'center' : 'right';
                }

                $alignments []= $alignment;
            }

            # ~

            $HeaderElements = array();

            $header = $Block['element']['text'];

            $header = trim($header);
            $header = trim($header, '|');

            $headerCells = explode('|', $header);

            foreach ($headerCells as $index => $headerCell)
            {
                $headerCell = trim($headerCell);

                $HeaderElement = array(
                    'name' => 'th',
                    'text' => $headerCell,
                    'handler' => 'line',
                );

                if (isset($alignments[$index]))
                {
                    $alignment = $alignments[$index];

                    $HeaderElement['attributes'] = array(
                        'style' => 'text-align:
'.$alignment.';',
                    );
                }

                $HeaderElements []= $HeaderElement;
            }

            # ~

            $Block = array(
                'alignments' => $alignments,
                'identified' => true,
                'element' => array(
                    'name' => 'table',
                    'handler' => 'elements',
                ),
            );

            $Block['element']['text'] []= array(
                'name' => 'thead',
                'handler' => 'elements',
            );

            $Block['element']['text'] []= array(
                'name' => 'tbody',
                'handler' => 'elements',
                'text' => array(),
            );

           
$Block['element']['text'][0]['text'] []=
array(
                'name' => 'tr',
                'handler' => 'elements',
                'text' => $HeaderElements,
            );

            return $Block;
        }
    }

    protected function blockTableContinue($Line, array $Block)
    {
        if (isset($Block['interrupted']))
        {
            return;
        }

        if ($Line['text'][0] === '|' or
strpos($Line['text'], '|'))
        {
            $Elements = array();

            $row = $Line['text'];

            $row = trim($row);
            $row = trim($row, '|');

            preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/',
$row, $matches);

            foreach ($matches[0] as $index => $cell)
            {
                $cell = trim($cell);

                $Element = array(
                    'name' => 'td',
                    'handler' => 'line',
                    'text' => $cell,
                );

                if (isset($Block['alignments'][$index]))
                {
                    $Element['attributes'] = array(
                        'style' => 'text-align:
'.$Block['alignments'][$index].';',
                    );
                }

                $Elements []= $Element;
            }

            $Element = array(
                'name' => 'tr',
                'handler' => 'elements',
                'text' => $Elements,
            );

           
$Block['element']['text'][1]['text'] []=
$Element;

            return $Block;
        }
    }

    #
    # ~
    #

    protected function paragraph($Line)
    {
        $Block = array(
            'element' => array(
                'name' => 'p',
                'text' => $Line['text'],
                'handler' => 'line',
            ),
        );

        return $Block;
    }

    #
    # Inline Elements
    #

    protected $InlineTypes = array(
        '"' => array('SpecialCharacter'),
        '!' => array('Image'),
        '&' => array('SpecialCharacter'),
        '*' => array('Emphasis'),
        ':' => array('Url'),
        '<' => array('UrlTag',
'EmailTag', 'Markup', 'SpecialCharacter'),
        '>' => array('SpecialCharacter'),
        '[' => array('Link'),
        '_' => array('Emphasis'),
        '`' => array('Code'),
        '~' => array('Strikethrough'),
        '\\' => array('EscapeSequence'),
    );

    # ~

    protected $inlineMarkerList = '!"*_&[:<>`~\\';

    #
    # ~
    #

    public function line($text, $nonNestables=array())
    {
        $markup = '';

        # $excerpt is based on the first occurrence of a marker

        while ($excerpt = strpbrk($text, $this->inlineMarkerList))
        {
            $marker = $excerpt[0];

            $markerPosition = strpos($text, $marker);

            $Excerpt = array('text' => $excerpt,
'context' => $text);

            foreach ($this->InlineTypes[$marker] as $inlineType)
            {
                # check to see if the current inline type is nestable in
the current context

                if ( ! empty($nonNestables) and in_array($inlineType,
$nonNestables))
                {
                    continue;
                }

                $Inline =
$this->{'inline'.$inlineType}($Excerpt);

                if ( ! isset($Inline))
                {
                    continue;
                }

                # makes sure that the inline belongs to "our"
marker

                if (isset($Inline['position']) and
$Inline['position'] > $markerPosition)
                {
                    continue;
                }

                # sets a default inline position

                if ( ! isset($Inline['position']))
                {
                    $Inline['position'] = $markerPosition;
                }

                # cause the new element to 'inherit' our non
nestables

                foreach ($nonNestables as $non_nestable)
                {
                   
$Inline['element']['nonNestables'][] = $non_nestable;
                }

                # the text that comes before the inline
                $unmarkedText = substr($text, 0,
$Inline['position']);

                # compile the unmarked text
                $markup .= $this->unmarkedText($unmarkedText);

                # compile the inline
                $markup .= isset($Inline['markup']) ?
$Inline['markup'] :
$this->element($Inline['element']);

                # remove the examined text
                $text = substr($text, $Inline['position'] +
$Inline['extent']);

                continue 2;
            }

            # the marker does not belong to an inline

            $unmarkedText = substr($text, 0, $markerPosition + 1);

            $markup .= $this->unmarkedText($unmarkedText);

            $text = substr($text, $markerPosition + 1);
        }

        $markup .= $this->unmarkedText($text);

        return $markup;
    }

    #
    # ~
    #

    protected function inlineCode($Excerpt)
    {
        $marker = $Excerpt['text'][0];

        if (preg_match('/^('.$marker.'+)[ ]*(.+?)[
]*(?<!'.$marker.')\1(?!'.$marker.')/s',
$Excerpt['text'], $matches))
        {
            $text = $matches[2];
            $text = preg_replace("/[ ]*\n/", ' ',
$text);

            return array(
                'extent' => strlen($matches[0]),
                'element' => array(
                    'name' => 'code',
                    'text' => $text,
                ),
            );
        }
    }

    protected function inlineEmailTag($Excerpt)
    {
        if (strpos($Excerpt['text'], '>') !== false
and preg_match('/^<((mailto:)?\S+?@\S+?)>/i',
$Excerpt['text'], $matches))
        {
            $url = $matches[1];

            if ( ! isset($matches[2]))
            {
                $url = 'mailto:' . $url;
            }

            return array(
                'extent' => strlen($matches[0]),
                'element' => array(
                    'name' => 'a',
                    'text' => $matches[1],
                    'attributes' => array(
                        'href' => $url,
                    ),
                ),
            );
        }
    }

    protected function inlineEmphasis($Excerpt)
    {
        if ( ! isset($Excerpt['text'][1]))
        {
            return;
        }

        $marker = $Excerpt['text'][0];

        if ($Excerpt['text'][1] === $marker and
preg_match($this->StrongRegex[$marker], $Excerpt['text'],
$matches))
        {
            $emphasis = 'strong';
        }
        elseif (preg_match($this->EmRegex[$marker],
$Excerpt['text'], $matches))
        {
            $emphasis = 'em';
        }
        else
        {
            return;
        }

        return array(
            'extent' => strlen($matches[0]),
            'element' => array(
                'name' => $emphasis,
                'handler' => 'line',
                'text' => $matches[1],
            ),
        );
    }

    protected function inlineEscapeSequence($Excerpt)
    {
        if (isset($Excerpt['text'][1]) and
in_array($Excerpt['text'][1], $this->specialCharacters))
        {
            return array(
                'markup' => $Excerpt['text'][1],
                'extent' => 2,
            );
        }
    }

    protected function inlineImage($Excerpt)
    {
        if ( ! isset($Excerpt['text'][1]) or
$Excerpt['text'][1] !== '[')
        {
            return;
        }

        $Excerpt['text']= substr($Excerpt['text'], 1);

        $Link = $this->inlineLink($Excerpt);

        if ($Link === null)
        {
            return;
        }

        $Inline = array(
            'extent' => $Link['extent'] + 1,
            'element' => array(
                'name' => 'img',
                'attributes' => array(
                    'src' =>
$Link['element']['attributes']['href'],
                    'alt' =>
$Link['element']['text'],
                ),
            ),
        );

        $Inline['element']['attributes'] +=
$Link['element']['attributes'];

       
unset($Inline['element']['attributes']['href']);

        return $Inline;
    }

    protected function inlineLink($Excerpt)
    {
        $Element = array(
            'name' => 'a',
            'handler' => 'line',
            'nonNestables' => array('Url',
'Link'),
            'text' => null,
            'attributes' => array(
                'href' => null,
                'title' => null,
            ),
        );

        $extent = 0;

        $remainder = $Excerpt['text'];

        if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder,
$matches))
        {
            $Element['text'] = $matches[1];

            $extent += strlen($matches[0]);

            $remainder = substr($remainder, $extent);
        }
        else
        {
            return;
        }

        if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[
]+("[^"]*"|\'[^\']*\'))?\s*[)]/',
$remainder, $matches))
        {
            $Element['attributes']['href'] =
$matches[1];

            if (isset($matches[2]))
            {
                $Element['attributes']['title'] =
substr($matches[2], 1, - 1);
            }

            $extent += strlen($matches[0]);
        }
        else
        {
            if (preg_match('/^\s*\[(.*?)\]/', $remainder,
$matches))
            {
                $definition = strlen($matches[1]) ? $matches[1] :
$Element['text'];
                $definition = strtolower($definition);

                $extent += strlen($matches[0]);
            }
            else
            {
                $definition = strtolower($Element['text']);
            }

            if ( !
isset($this->DefinitionData['Reference'][$definition]))
            {
                return;
            }

            $Definition =
$this->DefinitionData['Reference'][$definition];

            $Element['attributes']['href'] =
$Definition['url'];
            $Element['attributes']['title'] =
$Definition['title'];
        }

        return array(
            'extent' => $extent,
            'element' => $Element,
        );
    }

    protected function inlineMarkup($Excerpt)
    {
        if ($this->markupEscaped or $this->safeMode or
strpos($Excerpt['text'], '>') === false)
        {
            return;
        }

        if ($Excerpt['text'][1] === '/' and
preg_match('/^<\/\w[\w-]*[ ]*>/s',
$Excerpt['text'], $matches))
        {
            return array(
                'markup' => $matches[0],
                'extent' => strlen($matches[0]),
            );
        }

        if ($Excerpt['text'][1] === '!' and
preg_match('/^<!---?[^>-](?:-?[^-])*-->/s',
$Excerpt['text'], $matches))
        {
            return array(
                'markup' => $matches[0],
                'extent' => strlen($matches[0]),
            );
        }

        if ($Excerpt['text'][1] !== ' ' and
preg_match('/^<\w[\w-]*(?:[
]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s',
$Excerpt['text'], $matches))
        {
            return array(
                'markup' => $matches[0],
                'extent' => strlen($matches[0]),
            );
        }
    }

    protected function inlineSpecialCharacter($Excerpt)
    {
        if ($Excerpt['text'][0] === '&' and !
preg_match('/^&#?\w+;/', $Excerpt['text']))
        {
            return array(
                'markup' => '&amp;',
                'extent' => 1,
            );
        }

        $SpecialCharacter = array('>' => 'gt',
'<' => 'lt', '"' =>
'quot');

        if (isset($SpecialCharacter[$Excerpt['text'][0]]))
        {
            return array(
                'markup' =>
'&'.$SpecialCharacter[$Excerpt['text'][0]].';',
                'extent' => 1,
            );
        }
    }

    protected function inlineStrikethrough($Excerpt)
    {
        if ( ! isset($Excerpt['text'][1]))
        {
            return;
        }

        if ($Excerpt['text'][1] === '~' and
preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/',
$Excerpt['text'], $matches))
        {
            return array(
                'extent' => strlen($matches[0]),
                'element' => array(
                    'name' => 'del',
                    'text' => $matches[1],
                    'handler' => 'line',
                ),
            );
        }
    }

    protected function inlineUrl($Excerpt)
    {
        if ($this->urlsLinked !== true or !
isset($Excerpt['text'][2]) or $Excerpt['text'][2] !==
'/')
        {
            return;
        }

        if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui',
$Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
        {
            $url = $matches[0][0];

            $Inline = array(
                'extent' => strlen($matches[0][0]),
                'position' => $matches[0][1],
                'element' => array(
                    'name' => 'a',
                    'text' => $url,
                    'attributes' => array(
                        'href' => $url,
                    ),
                ),
            );

            return $Inline;
        }
    }

    protected function inlineUrlTag($Excerpt)
    {
        if (strpos($Excerpt['text'], '>') !== false
and preg_match('/^<(\w+:\/{2}[^ >]+)>/i',
$Excerpt['text'], $matches))
        {
            $url = $matches[1];

            return array(
                'extent' => strlen($matches[0]),
                'element' => array(
                    'name' => 'a',
                    'text' => $url,
                    'attributes' => array(
                        'href' => $url,
                    ),
                ),
            );
        }
    }

    # ~

    protected function unmarkedText($text)
    {
        if ($this->breaksEnabled)
        {
            $text = preg_replace('/[ ]*\n/', "<br
/>\n", $text);
        }
        else
        {
            $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/',
"<br />\n", $text);
            $text = str_replace(" \n", "\n", $text);
        }

        return $text;
    }

    #
    # Handlers
    #

    protected function element(array $Element)
    {
        if ($this->safeMode)
        {
            $Element = $this->sanitiseElement($Element);
        }

        $markup = '<'.$Element['name'];

        if (isset($Element['attributes']))
        {
            foreach ($Element['attributes'] as $name =>
$value)
            {
                if ($value === null)
                {
                    continue;
                }

                $markup .= '
'.$name.'="'.self::escape($value).'"';
            }
        }

        $permitRawHtml = false;

        if (isset($Element['text']))
        {
            $text = $Element['text'];
        }
        // very strongly consider an alternative if you're writing an
        // extension
        elseif (isset($Element['rawHtml']))
        {
            $text = $Element['rawHtml'];
            $allowRawHtmlInSafeMode =
isset($Element['allowRawHtmlInSafeMode']) &&
$Element['allowRawHtmlInSafeMode'];
            $permitRawHtml = !$this->safeMode ||
$allowRawHtmlInSafeMode;
        }

        if (isset($text))
        {
            $markup .= '>';

            if (!isset($Element['nonNestables']))
            {
                $Element['nonNestables'] = array();
            }

            if (isset($Element['handler']))
            {
                $markup .= $this->{$Element['handler']}($text,
$Element['nonNestables']);
            }
            elseif (!$permitRawHtml)
            {
                $markup .= self::escape($text, true);
            }
            else
            {
                $markup .= $text;
            }

            $markup .=
'</'.$Element['name'].'>';
        }
        else
        {
            $markup .= ' />';
        }

        return $markup;
    }

    protected function elements(array $Elements)
    {
        $markup = '';

        foreach ($Elements as $Element)
        {
            $markup .= "\n" . $this->element($Element);
        }

        $markup .= "\n";

        return $markup;
    }

    # ~

    protected function li($lines)
    {
        $markup = $this->lines($lines);

        $trimmedMarkup = trim($markup);

        if ( ! in_array('', $lines) and substr($trimmedMarkup, 0,
3) === '<p>')
        {
            $markup = $trimmedMarkup;
            $markup = substr($markup, 3);

            $position = strpos($markup, "</p>");

            $markup = substr_replace($markup, '', $position, 4);
        }

        return $markup;
    }

    #
    # Deprecated Methods
    #

    function parse($text)
    {
        $markup = $this->text($text);

        return $markup;
    }

    protected function sanitiseElement(array $Element)
    {
        static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/';
        static $safeUrlNameToAtt  = array(
            'a'   => 'href',
            'img' => 'src',
        );

        if (isset($safeUrlNameToAtt[$Element['name']]))
        {
            $Element = $this->filterUnsafeUrlInAttribute($Element,
$safeUrlNameToAtt[$Element['name']]);
        }

        if ( ! empty($Element['attributes']))
        {
            foreach ($Element['attributes'] as $att => $val)
            {
                # filter out badly parsed attribute
                if ( ! preg_match($goodAttribute, $att))
                {
                    unset($Element['attributes'][$att]);
                }
                # dump onevent attribute
                elseif (self::striAtStart($att, 'on'))
                {
                    unset($Element['attributes'][$att]);
                }
            }
        }

        return $Element;
    }

    protected function filterUnsafeUrlInAttribute(array $Element,
$attribute)
    {
        foreach ($this->safeLinksWhitelist as $scheme)
        {
            if
(self::striAtStart($Element['attributes'][$attribute], $scheme))
            {
                return $Element;
            }
        }

        $Element['attributes'][$attribute] =
str_replace(':', '%3A',
$Element['attributes'][$attribute]);

        return $Element;
    }

    #
    # Static Methods
    #

    protected static function escape($text, $allowQuotes = false)
    {
        return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES :
ENT_QUOTES, 'UTF-8');
    }

    protected static function striAtStart($string, $needle)
    {
        $len = strlen($needle);

        if ($len > strlen($string))
        {
            return false;
        }
        else
        {
            return strtolower(substr($string, 0, $len)) ===
strtolower($needle);
        }
    }

    static function instance($name = 'default')
    {
        if (isset(self::$instances[$name]))
        {
            return self::$instances[$name];
        }

        $instance = new static();

        self::$instances[$name] = $instance;

        return $instance;
    }

    private static $instances = array();

    #
    # Fields
    #

    protected $DefinitionData;

    #
    # Read-Only

    protected $specialCharacters = array(
        '\\', '`', '*', '_',
'{', '}', '[', ']', '(',
')', '>', '#', '+',
'-', '.', '!', '|',
    );

    protected $StrongRegex = array(
        '*' =>
'/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
        '_' =>
'/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
    );

    protected $EmRegex = array(
        '*' =>
'/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
        '_' =>
'/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
    );

    protected $regexHtmlAttribute =
'[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';

    protected $voidElements = array(
        'area', 'base', 'br',
'col', 'command', 'embed', 'hr',
'img', 'input', 'link', 'meta',
'param', 'source',
    );

    protected $textLevelElements = array(
        'a', 'br', 'bdo', 'abbr',
'blink', 'nextid', 'acronym',
'basefont',
        'b', 'em', 'big', 'cite',
'small', 'spacer', 'listing',
        'i', 'rp', 'del', 'code',  
       'strike', 'marquee',
        'q', 'rt', 'ins', 'font',  
       'strong',
        's', 'tt', 'kbd', 'mark',
        'u', 'xm', 'sub', 'nobr',
                   'sup', 'ruby',
                   'var', 'span',
                   'wbr', 'time',
    );
}
vendor/erusev/parsedown-extra/composer.json000064400000001121151166614520015221
0ustar00{
    "name": "erusev/parsedown-extra",
    "description": "An extension of Parsedown that adds
support for Markdown Extra.",
    "keywords": ["markdown", "markdown
extra", "parser", "parsedown"],
    "homepage":
"https://github.com/erusev/parsedown-extra",
    "type": "library",
    "license": "MIT",
    "authors": [
        {
            "name": "Emanuil Rusev",
            "email": "hello@erusev.com",
            "homepage": "http://erusev.com"
        }
    ],
    "require": {
        "erusev/parsedown": "~1.4"
    },
    "autoload": {
        "psr-0": {"ParsedownExtra": ""}
    }
}vendor/erusev/parsedown-extra/ParsedownExtra.php000064400000031323151166614520016165
0ustar00<?php

#
#
# Parsedown Extra
# https://github.com/erusev/parsedown-extra
#
# (c) Emanuil Rusev
# http://erusev.com
#
# For the full license information, view the LICENSE file that was
distributed
# with this source code.
#
#

class ParsedownExtra extends Parsedown
{
    # ~

    const version = '0.7.0';

    # ~

    function __construct()
    {
        if (parent::version < '1.5.0')
        {
            throw new Exception('ParsedownExtra requires a later
version of Parsedown');
        }

        $this->BlockTypes[':'] []= 'DefinitionList';
        $this->BlockTypes['*'] []= 'Abbreviation';

        # identify footnote definitions before reference definitions
        array_unshift($this->BlockTypes['['],
'Footnote');

        # identify footnote markers before before links
        array_unshift($this->InlineTypes['['],
'FootnoteMarker');
    }

    #
    # ~

    function text($text)
    {
        $markup = parent::text($text);

        # merge consecutive dl elements

        $markup = preg_replace('/<\/dl>\s+<dl>\s+/',
'', $markup);

        # add footnotes

        if (isset($this->DefinitionData['Footnote']))
        {
            $Element = $this->buildFootnoteElement();

            $markup .= "\n" . $this->element($Element);
        }

        return $markup;
    }

    #
    # Blocks
    #

    #
    # Abbreviation

    protected function blockAbbreviation($Line)
    {
        if (preg_match('/^\*\[(.+?)\]:[ ]*(.+?)[ ]*$/',
$Line['text'], $matches))
        {
            $this->DefinitionData['Abbreviation'][$matches[1]]
= $matches[2];

            $Block = array(
                'hidden' => true,
            );

            return $Block;
        }
    }

    #
    # Footnote

    protected function blockFootnote($Line)
    {
        if (preg_match('/^\[\^(.+?)\]:[ ]?(.*)$/',
$Line['text'], $matches))
        {
            $Block = array(
                'label' => $matches[1],
                'text' => $matches[2],
                'hidden' => true,
            );

            return $Block;
        }
    }

    protected function blockFootnoteContinue($Line, $Block)
    {
        if ($Line['text'][0] === '[' and
preg_match('/^\[\^(.+?)\]:/', $Line['text']))
        {
            return;
        }

        if (isset($Block['interrupted']))
        {
            if ($Line['indent'] >= 4)
            {
                $Block['text'] .= "\n\n" .
$Line['text'];

                return $Block;
            }
        }
        else
        {
            $Block['text'] .= "\n" .
$Line['text'];

            return $Block;
        }
    }

    protected function blockFootnoteComplete($Block)
    {
       
$this->DefinitionData['Footnote'][$Block['label']] =
array(
            'text' => $Block['text'],
            'count' => null,
            'number' => null,
        );

        return $Block;
    }

    #
    # Definition List

    protected function blockDefinitionList($Line, $Block)
    {
        if ( ! isset($Block) or isset($Block['type']))
        {
            return;
        }

        $Element = array(
            'name' => 'dl',
            'handler' => 'elements',
            'text' => array(),
        );

        $terms = explode("\n",
$Block['element']['text']);

        foreach ($terms as $term)
        {
            $Element['text'] []= array(
                'name' => 'dt',
                'handler' => 'line',
                'text' => $term,
            );
        }

        $Block['element'] = $Element;

        $Block = $this->addDdElement($Line, $Block);

        return $Block;
    }

    protected function blockDefinitionListContinue($Line, array $Block)
    {
        if ($Line['text'][0] === ':')
        {
            $Block = $this->addDdElement($Line, $Block);

            return $Block;
        }
        else
        {
            if (isset($Block['interrupted']) and
$Line['indent'] === 0)
            {
                return;
            }

            if (isset($Block['interrupted']))
            {
                $Block['dd']['handler'] =
'text';
                $Block['dd']['text'] .=
"\n\n";

                unset($Block['interrupted']);
            }

            $text = substr($Line['body'],
min($Line['indent'], 4));

            $Block['dd']['text'] .= "\n" .
$text;

            return $Block;
        }
    }

    #
    # Header

    protected function blockHeader($Line)
    {
        $Block = parent::blockHeader($Line);

        if (preg_match('/[
#]*{('.$this->regexAttribute.'+)}[ ]*$/',
$Block['element']['text'], $matches,
PREG_OFFSET_CAPTURE))
        {
            $attributeString = $matches[1][0];

            $Block['element']['attributes'] =
$this->parseAttributeData($attributeString);

            $Block['element']['text'] =
substr($Block['element']['text'], 0, $matches[0][1]);
        }

        return $Block;
    }

    #
    # Markup

    protected function blockMarkupComplete($Block)
    {
        if ( ! isset($Block['void']))
        {
            $Block['markup'] =
$this->processTag($Block['markup']);
        }

        return $Block;
    }

    #
    # Setext

    protected function blockSetextHeader($Line, array $Block = null)
    {
        $Block = parent::blockSetextHeader($Line, $Block);

        if (preg_match('/[
]*{('.$this->regexAttribute.'+)}[ ]*$/',
$Block['element']['text'], $matches,
PREG_OFFSET_CAPTURE))
        {
            $attributeString = $matches[1][0];

            $Block['element']['attributes'] =
$this->parseAttributeData($attributeString);

            $Block['element']['text'] =
substr($Block['element']['text'], 0, $matches[0][1]);
        }

        return $Block;
    }

    #
    # Inline Elements
    #

    #
    # Footnote Marker

    protected function inlineFootnoteMarker($Excerpt)
    {
        if (preg_match('/^\[\^(.+?)\]/',
$Excerpt['text'], $matches))
        {
            $name = $matches[1];

            if ( !
isset($this->DefinitionData['Footnote'][$name]))
            {
                return;
            }

           
$this->DefinitionData['Footnote'][$name]['count']
++;

            if ( !
isset($this->DefinitionData['Footnote'][$name]['number']))
            {
               
$this->DefinitionData['Footnote'][$name]['number'] =
++ $this->footnoteCount; # » &
            }

            $Element = array(
                'name' => 'sup',
                'attributes' => array('id' =>
'fnref'.$this->DefinitionData['Footnote'][$name]['count'].':'.$name),
                'handler' => 'element',
                'text' => array(
                    'name' => 'a',
                    'attributes' => array('href'
=> '#fn:'.$name, 'class' =>
'footnote-ref'),
                    'text' =>
$this->DefinitionData['Footnote'][$name]['number'],
                ),
            );

            return array(
                'extent' => strlen($matches[0]),
                'element' => $Element,
            );
        }
    }

    private $footnoteCount = 0;

    #
    # Link

    protected function inlineLink($Excerpt)
    {
        $Link = parent::inlineLink($Excerpt);

        $remainder = substr($Excerpt['text'],
$Link['extent']);

        if (preg_match('/^[
]*{('.$this->regexAttribute.'+)}/', $remainder,
$matches))
        {
            $Link['element']['attributes'] +=
$this->parseAttributeData($matches[1]);

            $Link['extent'] += strlen($matches[0]);
        }

        return $Link;
    }

    #
    # ~
    #

    protected function unmarkedText($text)
    {
        $text = parent::unmarkedText($text);

        if (isset($this->DefinitionData['Abbreviation']))
        {
            foreach ($this->DefinitionData['Abbreviation'] as
$abbreviation => $meaning)
            {
                $pattern = '/\b'.preg_quote($abbreviation,
'/').'\b/';

                $text = preg_replace($pattern, '<abbr
title="'.$meaning.'">'.$abbreviation.'</abbr>',
$text);
            }
        }

        return $text;
    }

    #
    # Util Methods
    #

    protected function addDdElement(array $Line, array $Block)
    {
        $text = substr($Line['text'], 1);
        $text = trim($text);

        unset($Block['dd']);

        $Block['dd'] = array(
            'name' => 'dd',
            'handler' => 'line',
            'text' => $text,
        );

        if (isset($Block['interrupted']))
        {
            $Block['dd']['handler'] = 'text';

            unset($Block['interrupted']);
        }

        $Block['element']['text'] []= &
$Block['dd'];

        return $Block;
    }

    protected function buildFootnoteElement()
    {
        $Element = array(
            'name' => 'div',
            'attributes' => array('class' =>
'footnotes'),
            'handler' => 'elements',
            'text' => array(
                array(
                    'name' => 'hr',
                ),
                array(
                    'name' => 'ol',
                    'handler' => 'elements',
                    'text' => array(),
                ),
            ),
        );

        uasort($this->DefinitionData['Footnote'],
'self::sortFootnotes');

        foreach ($this->DefinitionData['Footnote'] as
$definitionId => $DefinitionData)
        {
            if ( ! isset($DefinitionData['number']))
            {
                continue;
            }

            $text = $DefinitionData['text'];

            $text = parent::text($text);

            $numbers = range(1, $DefinitionData['count']);

            $backLinksMarkup = '';

            foreach ($numbers as $number)
            {
                $backLinksMarkup .= ' <a
href="#fnref'.$number.':'.$definitionId.'"
rev="footnote"
class="footnote-backref">&#8617;</a>';
            }

            $backLinksMarkup = substr($backLinksMarkup, 1);

            if (substr($text, - 4) === '</p>')
            {
                $backLinksMarkup = '&#160;'.$backLinksMarkup;

                $text = substr_replace($text,
$backLinksMarkup.'</p>', - 4);
            }
            else
            {
                $text .=
"\n".'<p>'.$backLinksMarkup.'</p>';
            }

            $Element['text'][1]['text'] []= array(
                'name' => 'li',
                'attributes' => array('id' =>
'fn:'.$definitionId),
                'text' => "\n".$text."\n",
            );
        }

        return $Element;
    }

    # ~

    protected function parseAttributeData($attributeString)
    {
        $Data = array();

        $attributes = preg_split('/[ ]+/', $attributeString, - 1,
PREG_SPLIT_NO_EMPTY);

        foreach ($attributes as $attribute)
        {
            if ($attribute[0] === '#')
            {
                $Data['id'] = substr($attribute, 1);
            }
            else # "."
            {
                $classes []= substr($attribute, 1);
            }
        }

        if (isset($classes))
        {
            $Data['class'] = implode(' ', $classes);
        }

        return $Data;
    }

    # ~

    protected function processTag($elementMarkup) # recursive
    {
        # http://stackoverflow.com/q/1148928/200145
        libxml_use_internal_errors(true);

        $DOMDocument = new DOMDocument;

        # http://stackoverflow.com/q/11309194/200145
        $elementMarkup = mb_convert_encoding($elementMarkup,
'HTML-ENTITIES', 'UTF-8');

        # http://stackoverflow.com/q/4879946/200145
        $DOMDocument->loadHTML($elementMarkup);
        $DOMDocument->removeChild($DOMDocument->doctype);
       
$DOMDocument->replaceChild($DOMDocument->firstChild->firstChild->firstChild,
$DOMDocument->firstChild);

        $elementText = '';

        if
($DOMDocument->documentElement->getAttribute('markdown')
=== '1')
        {
            foreach ($DOMDocument->documentElement->childNodes as
$Node)
            {
                $elementText .= $DOMDocument->saveHTML($Node);
            }

           
$DOMDocument->documentElement->removeAttribute('markdown');

            $elementText =
"\n".$this->text($elementText)."\n";
        }
        else
        {
            foreach ($DOMDocument->documentElement->childNodes as
$Node)
            {
                $nodeMarkup = $DOMDocument->saveHTML($Node);

                if ($Node instanceof DOMElement and !
in_array($Node->nodeName, $this->textLevelElements))
                {
                    $elementText .= $this->processTag($nodeMarkup);
                }
                else
                {
                    $elementText .= $nodeMarkup;
                }
            }
        }

        # because we don't want for markup to get encoded
        $DOMDocument->documentElement->nodeValue =
'placeholder\x1A';

        $markup =
$DOMDocument->saveHTML($DOMDocument->documentElement);
        $markup = str_replace('placeholder\x1A', $elementText,
$markup);

        return $markup;
    }

    # ~

    protected function sortFootnotes($A, $B) # callback
    {
        return $A['number'] - $B['number'];
    }

    #
    # Fields
    #

    protected $regexAttribute = '(?:[#.][-\w]+[ ]*)';
}
vendor/filp/whoops/composer.json000064400000002154151166614520013045
0ustar00{
    "name": "filp/whoops",
    "license": "MIT",
    "description": "php error handling for cool kids",
    "keywords": ["library", "error",
"handling", "exception", "whoops",
"throwable"],
    "homepage": "https://filp.github.io/whoops/",
    "authors": [
        {
            "name": "Filipe Dobreira",
            "homepage": "https://github.com/filp",
            "role": "Developer"
        }
    ],
    "require": {
        "php": "^5.5.9 || ^7.0",
        "psr/log": "^1.0.1"        
    },
    "require-dev": {
        "phpunit/phpunit": "^4.8.35 || ^5.7",
        "mockery/mockery": "^0.9 || ^1.0",
        "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0"
    },
    "suggest": {
        "symfony/var-dumper": "Pretty print complex values
better with var-dumper available",
        "whoops/soap": "Formats errors as SOAP
responses"
    },
    "autoload": {
        "psr-4": {
            "Whoops\\": "src/Whoops/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Whoops\\": "tests/Whoops/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "2.2-dev"
        }
    }
}
vendor/filp/whoops/src/Whoops/Exception/ErrorException.php000064400000000543151166614520020010
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Exception;

use ErrorException as BaseErrorException;

/**
 * Wraps ErrorException; mostly used for typing (at least now)
 * to easily cleanup the stack trace of redundant info.
 */
class ErrorException extends BaseErrorException
{
}
vendor/filp/whoops/src/Whoops/Exception/Formatter.php000064400000004305151166614520017003
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Exception;

class Formatter
{
    /**
     * Returns all basic information about the exception in a simple array
     * for further convertion to other languages
     * @param  Inspector $inspector
     * @param  bool      $shouldAddTrace
     * @return array
     */
    public static function formatExceptionAsDataArray(Inspector $inspector,
$shouldAddTrace)
    {
        $exception = $inspector->getException();
        $response = [
            'type'    => get_class($exception),
            'message' => $exception->getMessage(),
            'file'    => $exception->getFile(),
            'line'    => $exception->getLine(),
        ];

        if ($shouldAddTrace) {
            $frames    = $inspector->getFrames();
            $frameData = [];

            foreach ($frames as $frame) {
                /** @var Frame $frame */
                $frameData[] = [
                    'file'     => $frame->getFile(),
                    'line'     => $frame->getLine(),
                    'function' => $frame->getFunction(),
                    'class'    => $frame->getClass(),
                    'args'     => $frame->getArgs(),
                ];
            }

            $response['trace'] = $frameData;
        }

        return $response;
    }

    public static function formatExceptionPlain(Inspector $inspector)
    {
        $message = $inspector->getException()->getMessage();
        $frames = $inspector->getFrames();

        $plain = $inspector->getExceptionName();
        $plain .= ' thrown with message "';
        $plain .= $message;
        $plain .= '"'."\n\n";

        $plain .= "Stacktrace:\n";
        foreach ($frames as $i => $frame) {
            $plain .= "#". (count($frames) - $i - 1). "
";
            $plain .= $frame->getClass() ?: '';
            $plain .= $frame->getClass() &&
$frame->getFunction() ? ":" : "";
            $plain .= $frame->getFunction() ?: '';
            $plain .= ' in ';
            $plain .= ($frame->getFile() ?:
'<#unknown>');
            $plain .= ':';
            $plain .= (int) $frame->getLine(). "\n";
        }

        return $plain;
    }
}
vendor/filp/whoops/src/Whoops/Exception/Frame.php000064400000017164151166614520016101
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Exception;

use InvalidArgumentException;
use Serializable;

class Frame implements Serializable
{
    /**
     * @var array
     */
    protected $frame;

    /**
     * @var string
     */
    protected $fileContentsCache;

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

    /**
     * @var bool
     */
    protected $application;

    /**
     * @param array[]
     */
    public function __construct(array $frame)
    {
        $this->frame = $frame;
    }

    /**
     * @param  bool        $shortened
     * @return string|null
     */
    public function getFile($shortened = false)
    {
        if (empty($this->frame['file'])) {
            return null;
        }

        $file = $this->frame['file'];

        // Check if this frame occurred within an eval().
        // @todo: This can be made more reliable by checking if we've
entered
        // eval() in a previous trace, but will need some more work on the
upper
        // trace collector(s).
        if (preg_match('/^(.*)\((\d+)\) : (?:eval\(\)\'d|assert)
code$/', $file, $matches)) {
            $file = $this->frame['file'] = $matches[1];
            $this->frame['line'] = (int) $matches[2];
        }

        if ($shortened && is_string($file)) {
            // Replace the part of the path that all frames have in common,
and add 'soft hyphens' for smoother line-breaks.
            $dirname =
dirname(dirname(dirname(dirname(dirname(dirname(__DIR__))))));
            if ($dirname !== '/') {
                $file = str_replace($dirname, "&hellip;",
$file);
            }
            $file = str_replace("/", "/&shy;",
$file);
        }

        return $file;
    }

    /**
     * @return int|null
     */
    public function getLine()
    {
        return isset($this->frame['line']) ?
$this->frame['line'] : null;
    }

    /**
     * @return string|null
     */
    public function getClass()
    {
        return isset($this->frame['class']) ?
$this->frame['class'] : null;
    }

    /**
     * @return string|null
     */
    public function getFunction()
    {
        return isset($this->frame['function']) ?
$this->frame['function'] : null;
    }

    /**
     * @return array
     */
    public function getArgs()
    {
        return isset($this->frame['args']) ? (array)
$this->frame['args'] : [];
    }

    /**
     * Returns the full contents of the file for this frame,
     * if it's known.
     * @return string|null
     */
    public function getFileContents()
    {
        if ($this->fileContentsCache === null && $filePath =
$this->getFile()) {
            // Leave the stage early when 'Unknown' or
'[internal]' is passed
            // this would otherwise raise an exception when
            // open_basedir is enabled.
            if ($filePath === "Unknown" || $filePath ===
'[internal]') {
                return null;
            }

            try {
                $this->fileContentsCache = file_get_contents($filePath);
            } catch (ErrorException $exception) {
                // Internal file paths of PHP extensions cannot be opened
            }
        }

        return $this->fileContentsCache;
    }

    /**
     * Adds a comment to this frame, that can be received and
     * used by other handlers. For example, the PrettyPage handler
     * can attach these comments under the code for each frame.
     *
     * An interesting use for this would be, for example, code analysis
     * & annotations.
     *
     * @param string $comment
     * @param string $context Optional string identifying the origin of the
comment
     */
    public function addComment($comment, $context = 'global')
    {
        $this->comments[] = [
            'comment' => $comment,
            'context' => $context,
        ];
    }

    /**
     * Returns all comments for this frame. Optionally allows
     * a filter to only retrieve comments from a specific
     * context.
     *
     * @param  string  $filter
     * @return array[]
     */
    public function getComments($filter = null)
    {
        $comments = $this->comments;

        if ($filter !== null) {
            $comments = array_filter($comments, function ($c) use ($filter)
{
                return $c['context'] == $filter;
            });
        }

        return $comments;
    }

    /**
     * Returns the array containing the raw frame data from which
     * this Frame object was built
     *
     * @return array
     */
    public function getRawFrame()
    {
        return $this->frame;
    }

    /**
     * Returns the contents of the file for this frame as an
     * array of lines, and optionally as a clamped range of lines.
     *
     * NOTE: lines are 0-indexed
     *
     * @example
     *     Get all lines for this file
     *     $frame->getFileLines(); // => array( 0 =>
'<?php', 1 => '...', ...)
     * @example
     *     Get one line for this file, starting at line 10 (zero-indexed,
remember!)
     *     $frame->getFileLines(9, 1); // array( 10 =>
'...', 11 => '...')
     *
     * @throws InvalidArgumentException if $length is less than or equal to
0
     * @param  int                      $start
     * @param  int                      $length
     * @return string[]|null
     */
    public function getFileLines($start = 0, $length = null)
    {
        if (null !== ($contents = $this->getFileContents())) {
            $lines = explode("\n", $contents);

            // Get a subset of lines from $start to $end
            if ($length !== null) {
                $start  = (int) $start;
                $length = (int) $length;
                if ($start < 0) {
                    $start = 0;
                }

                if ($length <= 0) {
                    throw new InvalidArgumentException(
                        "\$length($length) cannot be lower or equal to
0"
                    );
                }

                $lines = array_slice($lines, $start, $length, true);
            }

            return $lines;
        }
    }

    /**
     * Implements the Serializable interface, with special
     * steps to also save the existing comments.
     *
     * @see Serializable::serialize
     * @return string
     */
    public function serialize()
    {
        $frame = $this->frame;
        if (!empty($this->comments)) {
            $frame['_comments'] = $this->comments;
        }

        return serialize($frame);
    }

    /**
     * Unserializes the frame data, while also preserving
     * any existing comment data.
     *
     * @see Serializable::unserialize
     * @param string $serializedFrame
     */
    public function unserialize($serializedFrame)
    {
        $frame = unserialize($serializedFrame);

        if (!empty($frame['_comments'])) {
            $this->comments = $frame['_comments'];
            unset($frame['_comments']);
        }

        $this->frame = $frame;
    }

    /**
     * Compares Frame against one another
     * @param  Frame $frame
     * @return bool
     */
    public function equals(Frame $frame)
    {
        if (!$this->getFile() || $this->getFile() ===
'Unknown' || !$this->getLine()) {
            return false;
        }
        return $frame->getFile() === $this->getFile() &&
$frame->getLine() === $this->getLine();
    }

    /**
     * Returns whether this frame belongs to the application or not.
     *
     * @return boolean
     */
    public function isApplication()
    {
        return $this->application;
    }

    /**
     * Mark as an frame belonging to the application.
     *
     * @param boolean $application
     */
    public function setApplication($application)
    {
        $this->application = $application;
    }
}
vendor/filp/whoops/src/Whoops/Exception/FrameCollection.php000064400000011233151166614520020104
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Exception;

use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use Serializable;
use UnexpectedValueException;

/**
 * Exposes a fluent interface for dealing with an ordered list
 * of stack-trace frames.
 */
class FrameCollection implements ArrayAccess, IteratorAggregate,
Serializable, Countable
{
    /**
     * @var array[]
     */
    private $frames;

    /**
     * @param array $frames
     */
    public function __construct(array $frames)
    {
        $this->frames = array_map(function ($frame) {
            return new Frame($frame);
        }, $frames);
    }

    /**
     * Filters frames using a callable, returns the same FrameCollection
     *
     * @param  callable        $callable
     * @return FrameCollection
     */
    public function filter($callable)
    {
        $this->frames = array_values(array_filter($this->frames,
$callable));
        return $this;
    }

    /**
     * Map the collection of frames
     *
     * @param  callable        $callable
     * @return FrameCollection
     */
    public function map($callable)
    {
        // Contain the map within a higher-order callable
        // that enforces type-correctness for the $callable
        $this->frames = array_map(function ($frame) use ($callable) {
            $frame = call_user_func($callable, $frame);

            if (!$frame instanceof Frame) {
                throw new UnexpectedValueException(
                    "Callable to " . __CLASS__ . "::map must
return a Frame object"
                );
            }

            return $frame;
        }, $this->frames);

        return $this;
    }

    /**
     * Returns an array with all frames, does not affect
     * the internal array.
     *
     * @todo   If this gets any more complex than this,
     *         have getIterator use this method.
     * @see    FrameCollection::getIterator
     * @return array
     */
    public function getArray()
    {
        return $this->frames;
    }

    /**
     * @see IteratorAggregate::getIterator
     * @return ArrayIterator
     */
    public function getIterator()
    {
        return new ArrayIterator($this->frames);
    }

    /**
     * @see ArrayAccess::offsetExists
     * @param int $offset
     */
    public function offsetExists($offset)
    {
        return isset($this->frames[$offset]);
    }

    /**
     * @see ArrayAccess::offsetGet
     * @param int $offset
     */
    public function offsetGet($offset)
    {
        return $this->frames[$offset];
    }

    /**
     * @see ArrayAccess::offsetSet
     * @param int $offset
     */
    public function offsetSet($offset, $value)
    {
        throw new \Exception(__CLASS__ . ' is read only');
    }

    /**
     * @see ArrayAccess::offsetUnset
     * @param int $offset
     */
    public function offsetUnset($offset)
    {
        throw new \Exception(__CLASS__ . ' is read only');
    }

    /**
     * @see Countable::count
     * @return int
     */
    public function count()
    {
        return count($this->frames);
    }

    /**
     * Count the frames that belongs to the application.
     *
     * @return int
     */
    public function countIsApplication()
    {
        return count(array_filter($this->frames, function (Frame $f) {
            return $f->isApplication();
        }));
    }

    /**
     * @see Serializable::serialize
     * @return string
     */
    public function serialize()
    {
        return serialize($this->frames);
    }

    /**
     * @see Serializable::unserialize
     * @param string $serializedFrames
     */
    public function unserialize($serializedFrames)
    {
        $this->frames = unserialize($serializedFrames);
    }

    /**
     * @param Frame[] $frames Array of Frame instances, usually from
$e->getPrevious()
     */
    public function prependFrames(array $frames)
    {
        $this->frames = array_merge($frames, $this->frames);
    }

    /**
     * Gets the innermost part of stack trace that is not the same as that
of outer exception
     *
     * @param  FrameCollection $parentFrames Outer exception frames to
compare tail against
     * @return Frame[]
     */
    public function topDiff(FrameCollection $parentFrames)
    {
        $diff = $this->frames;

        $parentFrames = $parentFrames->getArray();
        $p = count($parentFrames)-1;

        for ($i = count($diff)-1; $i >= 0 && $p >= 0; $i--) {
            /** @var Frame $tailFrame */
            $tailFrame = $diff[$i];
            if ($tailFrame->equals($parentFrames[$p])) {
                unset($diff[$i]);
            }
            $p--;
        }
        return $diff;
    }
}
vendor/filp/whoops/src/Whoops/Exception/Inspector.php000064400000021356151166614520017013
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Exception;

use Whoops\Util\Misc;

class Inspector
{
    /**
     * @var \Throwable
     */
    private $exception;

    /**
     * @var \Whoops\Exception\FrameCollection
     */
    private $frames;

    /**
     * @var \Whoops\Exception\Inspector
     */
    private $previousExceptionInspector;

    /**
     * @var \Throwable[]
     */
    private $previousExceptions;

    /**
     * @param \Throwable $exception The exception to inspect
     */
    public function __construct($exception)
    {
        $this->exception = $exception;
    }

    /**
     * @return \Throwable
     */
    public function getException()
    {
        return $this->exception;
    }

    /**
     * @return string
     */
    public function getExceptionName()
    {
        return get_class($this->exception);
    }

    /**
     * @return string
     */
    public function getExceptionMessage()
    {
        return
$this->extractDocrefUrl($this->exception->getMessage())['message'];
    }

    /**
     * @return string[]
     */
    public function getPreviousExceptionMessages()
    {
        return array_map(function ($prev) {
            /** @var \Throwable $prev */
            return
$this->extractDocrefUrl($prev->getMessage())['message'];
        }, $this->getPreviousExceptions());
    }

    /**
     * @return int[]
     */
    public function getPreviousExceptionCodes()
    {
        return array_map(function ($prev) {
            /** @var \Throwable $prev */
            return $prev->getCode();
        }, $this->getPreviousExceptions());
    }

    /**
     * Returns a url to the php-manual related to the underlying error -
when available.
     *
     * @return string|null
     */
    public function getExceptionDocrefUrl()
    {
        return
$this->extractDocrefUrl($this->exception->getMessage())['url'];
    }

    private function extractDocrefUrl($message)
    {
        $docref = [
            'message' => $message,
            'url' => null,
        ];

        // php embbeds urls to the manual into the Exception message with
the following ini-settings defined
        //
http://php.net/manual/en/errorfunc.configuration.php#ini.docref-root
        if (!ini_get('html_errors') ||
!ini_get('docref_root')) {
            return $docref;
        }

        $pattern = "/\[<a
href='([^']+)'>(?:[^<]+)<\/a>\]/";
        if (preg_match($pattern, $message, $matches)) {
            // -> strip those automatically generated links from the
exception message
            $docref['message'] = preg_replace($pattern,
'', $message, 1);
            $docref['url'] = $matches[1];
        }

        return $docref;
    }

    /**
     * Does the wrapped Exception has a previous Exception?
     * @return bool
     */
    public function hasPreviousException()
    {
        return $this->previousExceptionInspector ||
$this->exception->getPrevious();
    }

    /**
     * Returns an Inspector for a previous Exception, if any.
     * @todo   Clean this up a bit, cache stuff a bit better.
     * @return Inspector
     */
    public function getPreviousExceptionInspector()
    {
        if ($this->previousExceptionInspector === null) {
            $previousException = $this->exception->getPrevious();

            if ($previousException) {
                $this->previousExceptionInspector = new
Inspector($previousException);
            }
        }

        return $this->previousExceptionInspector;
    }


    /**
     * Returns an array of all previous exceptions for this
inspector's exception
     * @return \Throwable[]
     */
    public function getPreviousExceptions()
    {
        if ($this->previousExceptions === null) {
            $this->previousExceptions = [];

            $prev = $this->exception->getPrevious();
            while ($prev !== null) {
                $this->previousExceptions[] = $prev;
                $prev = $prev->getPrevious();
            }
        }

        return $this->previousExceptions;
    }

    /**
     * Returns an iterator for the inspected exception's
     * frames.
     * @return \Whoops\Exception\FrameCollection
     */
    public function getFrames()
    {
        if ($this->frames === null) {
            $frames = $this->getTrace($this->exception);

            // Fill empty line/file info for call_user_func_array usages
(PHP Bug #44428)
            foreach ($frames as $k => $frame) {
                if (empty($frame['file'])) {
                    // Default values when file and line are missing
                    $file = '[internal]';
                    $line = 0;

                    $next_frame = !empty($frames[$k + 1]) ? $frames[$k + 1]
: [];

                    if ($this->isValidNextFrame($next_frame)) {
                        $file = $next_frame['file'];
                        $line = $next_frame['line'];
                    }

                    $frames[$k]['file'] = $file;
                    $frames[$k]['line'] = $line;
                }
            }

            // Find latest non-error handling frame index ($i) used to
remove error handling frames
            $i = 0;
            foreach ($frames as $k => $frame) {
                if ($frame['file'] ==
$this->exception->getFile() && $frame['line'] ==
$this->exception->getLine()) {
                    $i = $k;
                }
            }

            // Remove error handling frames
            if ($i > 0) {
                array_splice($frames, 0, $i);
            }

            $firstFrame =
$this->getFrameFromException($this->exception);
            array_unshift($frames, $firstFrame);

            $this->frames = new FrameCollection($frames);

            if ($previousInspector =
$this->getPreviousExceptionInspector()) {
                // Keep outer frame on top of the inner one
                $outerFrames = $this->frames;
                $newFrames = clone $previousInspector->getFrames();
                // I assume it will always be set, but let's be safe
                if (isset($newFrames[0])) {
                    $newFrames[0]->addComment(
                        $previousInspector->getExceptionMessage(),
                        'Exception message:'
                    );
                }
               
$newFrames->prependFrames($outerFrames->topDiff($newFrames));
                $this->frames = $newFrames;
            }
        }

        return $this->frames;
    }

    /**
     * Gets the backtrace from an exception.
     *
     * If xdebug is installed
     *
     * @param \Throwable $e
     * @return array
     */
    protected function getTrace($e)
    {
        $traces = $e->getTrace();

        // Get trace from xdebug if enabled, failure exceptions only trace
to the shutdown handler by default
        if (!$e instanceof \ErrorException) {
            return $traces;
        }

        if (!Misc::isLevelFatal($e->getSeverity())) {
            return $traces;
        }

        if (!extension_loaded('xdebug') || !xdebug_is_enabled())
{
            return $traces;
        }

        // Use xdebug to get the full stack trace and remove the shutdown
handler stack trace
        $stack = array_reverse(xdebug_get_function_stack());
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
        $traces = array_diff_key($stack, $trace);

        return $traces;
    }

    /**
     * Given an exception, generates an array in the format
     * generated by Exception::getTrace()
     * @param  \Throwable $exception
     * @return array
     */
    protected function getFrameFromException($exception)
    {
        return [
            'file'  => $exception->getFile(),
            'line'  => $exception->getLine(),
            'class' => get_class($exception),
            'args'  => [
                $exception->getMessage(),
            ],
        ];
    }

    /**
     * Given an error, generates an array in the format
     * generated by ErrorException
     * @param  ErrorException $exception
     * @return array
     */
    protected function getFrameFromError(ErrorException $exception)
    {
        return [
            'file'  => $exception->getFile(),
            'line'  => $exception->getLine(),
            'class' => null,
            'args'  => [],
        ];
    }

    /**
     * Determine if the frame can be used to fill in previous frame's
missing info
     * happens for call_user_func and call_user_func_array usages (PHP Bug
#44428)
     *
     * @param array $frame
     * @return bool
     */
    protected function isValidNextFrame(array $frame)
    {
        if (empty($frame['file'])) {
            return false;
        }

        if (empty($frame['line'])) {
            return false;
        }

        if (empty($frame['function']) ||
!stristr($frame['function'], 'call_user_func')) {
            return false;
        }

        return true;
    }
}
vendor/filp/whoops/src/Whoops/Handler/CallbackHandler.php000064400000002510151166614520017445
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Handler;

use InvalidArgumentException;

/**
 * Wrapper for Closures passed as handlers. Can be used
 * directly, or will be instantiated automagically by Whoops\Run
 * if passed to Run::pushHandler
 */
class CallbackHandler extends Handler
{
    /**
     * @var callable
     */
    protected $callable;

    /**
     * @throws InvalidArgumentException If argument is not callable
     * @param  callable                 $callable
     */
    public function __construct($callable)
    {
        if (!is_callable($callable)) {
            throw new InvalidArgumentException(
                'Argument to ' . __METHOD__ . ' must be
valid callable'
            );
        }

        $this->callable = $callable;
    }

    /**
     * @return int|null
     */
    public function handle()
    {
        $exception = $this->getException();
        $inspector = $this->getInspector();
        $run       = $this->getRun();
        $callable  = $this->callable;

        // invoke the callable directly, to get simpler stacktraces (in
comparison to call_user_func).
        // this assumes that $callable is a properly typed php-callable,
which we check in __construct().
        return $callable($exception, $inspector, $run);
    }
}
vendor/filp/whoops/src/Whoops/Handler/Handler.php000064400000003515151166614520016036
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Handler;

use Whoops\Exception\Inspector;
use Whoops\RunInterface;

/**
 * Abstract implementation of a Handler.
 */
abstract class Handler implements HandlerInterface
{
    /*
     Return constants that can be returned from Handler::handle
     to message the handler walker.
     */
    const DONE         = 0x10; // returning this is optional, only exists
for
                               // semantic purposes
    /**
     * The Handler has handled the Throwable in some way, and wishes to
skip any other Handler.
     * Execution will continue.
     */
    const LAST_HANDLER = 0x20;
    /**
     * The Handler has handled the Throwable in some way, and wishes to
quit/stop execution
     */
    const QUIT         = 0x30;

    /**
     * @var RunInterface
     */
    private $run;

    /**
     * @var Inspector $inspector
     */
    private $inspector;

    /**
     * @var \Throwable $exception
     */
    private $exception;

    /**
     * @param RunInterface $run
     */
    public function setRun(RunInterface $run)
    {
        $this->run = $run;
    }

    /**
     * @return RunInterface
     */
    protected function getRun()
    {
        return $this->run;
    }

    /**
     * @param Inspector $inspector
     */
    public function setInspector(Inspector $inspector)
    {
        $this->inspector = $inspector;
    }

    /**
     * @return Inspector
     */
    protected function getInspector()
    {
        return $this->inspector;
    }

    /**
     * @param \Throwable $exception
     */
    public function setException($exception)
    {
        $this->exception = $exception;
    }

    /**
     * @return \Throwable
     */
    protected function getException()
    {
        return $this->exception;
    }
}
vendor/filp/whoops/src/Whoops/Handler/HandlerInterface.php000064400000001317151166614520017655
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Handler;

use Whoops\Exception\Inspector;
use Whoops\RunInterface;

interface HandlerInterface
{
    /**
     * @return int|null A handler may return nothing, or a
Handler::HANDLE_* constant
     */
    public function handle();

    /**
     * @param  RunInterface  $run
     * @return void
     */
    public function setRun(RunInterface $run);

    /**
     * @param  \Throwable $exception
     * @return void
     */
    public function setException($exception);

    /**
     * @param  Inspector $inspector
     * @return void
     */
    public function setInspector(Inspector $inspector);
}
vendor/filp/whoops/src/Whoops/Handler/JsonResponseHandler.php000064400000003707151166614520020412
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Handler;

use Whoops\Exception\Formatter;

/**
 * Catches an exception and converts it to a JSON
 * response. Additionally can also return exception
 * frames for consumption by an API.
 */
class JsonResponseHandler extends Handler
{
    /**
     * @var bool
     */
    private $returnFrames = false;

    /**
     * @var bool
     */
    private $jsonApi = false;

    /**
     * Returns errors[[]] instead of error[] to be in compliance with the
json:api spec
     * @param bool $jsonApi Default is false
     * @return $this
     */
    public function setJsonApi($jsonApi = false)
    {
        $this->jsonApi = (bool) $jsonApi;
        return $this;
    }

    /**
     * @param  bool|null  $returnFrames
     * @return bool|$this
     */
    public function addTraceToOutput($returnFrames = null)
    {
        if (func_num_args() == 0) {
            return $this->returnFrames;
        }

        $this->returnFrames = (bool) $returnFrames;
        return $this;
    }

    /**
     * @return int
     */
    public function handle()
    {
        if ($this->jsonApi === true) {
            $response = [
                'errors' => [
                    Formatter::formatExceptionAsDataArray(
                        $this->getInspector(),
                        $this->addTraceToOutput()
                    ),
                ]
            ];
        } else {
            $response = [
                'error' =>
Formatter::formatExceptionAsDataArray(
                    $this->getInspector(),
                    $this->addTraceToOutput()
                ),
            ];
        }

        echo json_encode($response,
defined('JSON_PARTIAL_OUTPUT_ON_ERROR') ?
JSON_PARTIAL_OUTPUT_ON_ERROR : 0);

        return Handler::QUIT;
    }

    /**
     * @return string
     */
    public function contentType()
    {
        return 'application/json';
    }
}
vendor/filp/whoops/src/Whoops/Handler/PlainTextHandler.php000064400000021163151166614520017666
0ustar00<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
* Plaintext handler for command line and logs.
* @author Pierre-Yves Landuré <https://howto.biapy.com/>
*/

namespace Whoops\Handler;

use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Whoops\Exception\Frame;

/**
* Handler outputing plaintext error messages. Can be used
* directly, or will be instantiated automagically by Whoops\Run
* if passed to Run::pushHandler
*/
class PlainTextHandler extends Handler
{
    const VAR_DUMP_PREFIX = '   | ';

    /**
     * @var \Psr\Log\LoggerInterface
     */
    protected $logger;

    /**
     * @var callable
     */
    protected $dumper;

    /**
     * @var bool
     */
    private $addTraceToOutput = true;

    /**
     * @var bool|integer
     */
    private $addTraceFunctionArgsToOutput = false;

    /**
     * @var integer
     */
    private $traceFunctionArgsOutputLimit = 1024;

    /**
     * @var bool
     */
    private $addPreviousToOutput = true;

    /**
     * @var bool
     */
    private $loggerOnly = false;

    /**
     * Constructor.
     * @throws InvalidArgumentException     If argument is not null or a
LoggerInterface
     * @param  \Psr\Log\LoggerInterface|null $logger
     */
    public function __construct($logger = null)
    {
        $this->setLogger($logger);
    }

    /**
     * Set the output logger interface.
     * @throws InvalidArgumentException     If argument is not null or a
LoggerInterface
     * @param  \Psr\Log\LoggerInterface|null $logger
     */
    public function setLogger($logger = null)
    {
        if (! (is_null($logger)
            || $logger instanceof LoggerInterface)) {
            throw new InvalidArgumentException(
                'Argument to ' . __METHOD__ .
                " must be a valid Logger Interface (aka. Monolog),
" .
                get_class($logger) . ' given.'
            );
        }

        $this->logger = $logger;
    }

    /**
     * @return \Psr\Log\LoggerInterface|null
     */
    public function getLogger()
    {
        return $this->logger;
    }

    /**
     * Set var dumper callback function.
     *
     * @param  callable $dumper
     * @return void
     */
    public function setDumper(callable $dumper)
    {
        $this->dumper = $dumper;
    }

    /**
     * Add error trace to output.
     * @param  bool|null  $addTraceToOutput
     * @return bool|$this
     */
    public function addTraceToOutput($addTraceToOutput = null)
    {
        if (func_num_args() == 0) {
            return $this->addTraceToOutput;
        }

        $this->addTraceToOutput = (bool) $addTraceToOutput;
        return $this;
    }

    /**
     * Add previous exceptions to output.
     * @param  bool|null $addPreviousToOutput
     * @return bool|$this
     */
    public function addPreviousToOutput($addPreviousToOutput = null)
    {
        if (func_num_args() == 0) {
            return $this->addPreviousToOutput;
        }

        $this->addPreviousToOutput = (bool) $addPreviousToOutput;
        return $this;
    }

    /**
     * Add error trace function arguments to output.
     * Set to True for all frame args, or integer for the n first frame
args.
     * @param  bool|integer|null $addTraceFunctionArgsToOutput
     * @return null|bool|integer
     */
    public function
addTraceFunctionArgsToOutput($addTraceFunctionArgsToOutput = null)
    {
        if (func_num_args() == 0) {
            return $this->addTraceFunctionArgsToOutput;
        }

        if (! is_integer($addTraceFunctionArgsToOutput)) {
            $this->addTraceFunctionArgsToOutput = (bool)
$addTraceFunctionArgsToOutput;
        } else {
            $this->addTraceFunctionArgsToOutput =
$addTraceFunctionArgsToOutput;
        }
    }

    /**
     * Set the size limit in bytes of frame arguments var_dump output.
     * If the limit is reached, the var_dump output is discarded.
     * Prevent memory limit errors.
     * @var integer
     */
    public function
setTraceFunctionArgsOutputLimit($traceFunctionArgsOutputLimit)
    {
        $this->traceFunctionArgsOutputLimit = (integer)
$traceFunctionArgsOutputLimit;
    }

    /**
     * Create plain text response and return it as a string
     * @return string
     */
    public function generateResponse()
    {
        $exception = $this->getException();
        $message = $this->getExceptionOutput($exception);

        if ($this->addPreviousToOutput) {
            $previous = $exception->getPrevious();
            while ($previous) {
                $message .= "\n\nCaused by\n" .
$this->getExceptionOutput($previous);
                $previous = $previous->getPrevious();
            }
        }


        return $message . $this->getTraceOutput() . "\n";
    }

    /**
     * Get the size limit in bytes of frame arguments var_dump output.
     * If the limit is reached, the var_dump output is discarded.
     * Prevent memory limit errors.
     * @return integer
     */
    public function getTraceFunctionArgsOutputLimit()
    {
        return $this->traceFunctionArgsOutputLimit;
    }

    /**
     * Only output to logger.
     * @param  bool|null $loggerOnly
     * @return null|bool
     */
    public function loggerOnly($loggerOnly = null)
    {
        if (func_num_args() == 0) {
            return $this->loggerOnly;
        }

        $this->loggerOnly = (bool) $loggerOnly;
    }

    /**
     * Test if handler can output to stdout.
     * @return bool
     */
    private function canOutput()
    {
        return !$this->loggerOnly();
    }

    /**
     * Get the frame args var_dump.
     * @param  \Whoops\Exception\Frame $frame [description]
     * @param  integer                 $line  [description]
     * @return string
     */
    private function getFrameArgsOutput(Frame $frame, $line)
    {
        if ($this->addTraceFunctionArgsToOutput() === false
            || $this->addTraceFunctionArgsToOutput() < $line) {
            return '';
        }

        // Dump the arguments:
        ob_start();
        $this->dump($frame->getArgs());
        if (ob_get_length() >
$this->getTraceFunctionArgsOutputLimit()) {
            // The argument var_dump is to big.
            // Discarded to limit memory usage.
            ob_clean();
            return sprintf(
                "\n%sArguments dump length greater than %d Bytes.
Discarded.",
                self::VAR_DUMP_PREFIX,
                $this->getTraceFunctionArgsOutputLimit()
            );
        }

        return sprintf(
            "\n%s",
            preg_replace('/^/m', self::VAR_DUMP_PREFIX,
ob_get_clean())
        );
    }

    /**
     * Dump variable.
     *
     * @param mixed $var
     * @return void
     */
    protected function dump($var)
    {
        if ($this->dumper) {
            call_user_func($this->dumper, $var);
        } else {
            var_dump($var);
        }
    }

    /**
     * Get the exception trace as plain text.
     * @return string
     */
    private function getTraceOutput()
    {
        if (! $this->addTraceToOutput()) {
            return '';
        }
        $inspector = $this->getInspector();
        $frames = $inspector->getFrames();

        $response = "\nStack trace:";

        $line = 1;
        foreach ($frames as $frame) {
            /** @var Frame $frame */
            $class = $frame->getClass();

            $template = "\n%3d. %s->%s() %s:%d%s";
            if (! $class) {
                // Remove method arrow (->) from output.
                $template = "\n%3d. %s%s() %s:%d%s";
            }

            $response .= sprintf(
                $template,
                $line,
                $class,
                $frame->getFunction(),
                $frame->getFile(),
                $frame->getLine(),
                $this->getFrameArgsOutput($frame, $line)
            );

            $line++;
        }

        return $response;
    }

    /**
     * Get the exception as plain text.
     * @param \Throwable $exception
     * @return string
     */
    private function getExceptionOutput($exception)
    {
        return sprintf(
            "%s: %s in file %s on line %d",
            get_class($exception),
            $exception->getMessage(),
            $exception->getFile(),
            $exception->getLine()
        );
    }

    /**
     * @return int
     */
    public function handle()
    {
        $response = $this->generateResponse();

        if ($this->getLogger()) {
            $this->getLogger()->error($response);
        }

        if (! $this->canOutput()) {
            return Handler::DONE;
        }

        echo $response;

        return Handler::QUIT;
    }

    /**
     * @return string
     */
    public function contentType()
    {
        return 'text/plain';
    }
}
vendor/filp/whoops/src/Whoops/Handler/PrettyPageHandler.php000064400000056537151166614520020057
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Handler;

use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use UnexpectedValueException;
use Whoops\Exception\Formatter;
use Whoops\Util\Misc;
use Whoops\Util\TemplateHelper;

class PrettyPageHandler extends Handler
{
    const EDITOR_SUBLIME = "sublime";
    const EDITOR_TEXTMATE = "textmate";
    const EDITOR_EMACS = "emacs";
    const EDITOR_MACVIM = "macvim";
    const EDITOR_PHPSTORM = "phpstorm";
    const EDITOR_IDEA = "idea";
    const EDITOR_VSCODE = "vscode";
    const EDITOR_ATOM = "atom";
    const EDITOR_ESPRESSO = "espresso";
    const EDITOR_XDEBUG = "xdebug";

    /**
     * Search paths to be scanned for resources, in the reverse
     * order they're declared.
     *
     * @var array
     */
    private $searchPaths = [];

    /**
     * Fast lookup cache for known resource locations.
     *
     * @var array
     */
    private $resourceCache = [];

    /**
     * The name of the custom css file.
     *
     * @var string
     */
    private $customCss = null;

    /**
     * @var array[]
     */
    private $extraTables = [];

    /**
     * @var bool
     */
    private $handleUnconditionally = false;

    /**
     * @var string
     */
    private $pageTitle = "Whoops! There was an error.";

    /**
     * @var array[]
     */
    private $applicationPaths;

    /**
     * @var array[]
     */
    private $blacklist = [
        '_GET' => [],
        '_POST' => [],
        '_FILES' => [],
        '_COOKIE' => [],
        '_SESSION' => [],
        '_SERVER' => [],
        '_ENV' => [],
    ];

    /**
     * A string identifier for a known IDE/text editor, or a closure
     * that resolves a string that can be used to open a given file
     * in an editor. If the string contains the special substrings
     * %file or %line, they will be replaced with the correct data.
     *
     * @example
     *  "txmt://open?url=%file&line=%line"
     * @var mixed $editor
     */
    protected $editor;

    /**
     * A list of known editor strings
     * @var array
     */
    protected $editors = [
        "sublime"  =>
"subl://open?url=file://%file&line=%line",
        "textmate" =>
"txmt://open?url=file://%file&line=%line",
        "emacs"    =>
"emacs://open?url=file://%file&line=%line",
        "macvim"   =>
"mvim://open/?url=file://%file&line=%line",
        "phpstorm" =>
"phpstorm://open?file=%file&line=%line",
        "idea"     =>
"idea://open?file=%file&line=%line",
        "vscode"   => "vscode://file/%file:%line",
        "atom"     =>
"atom://core/open/file?filename=%file&line=%line",
        "espresso" =>
"x-espresso://open?filepath=%file&lines=%line",
    ];

    /**
     * @var TemplateHelper
     */
    private $templateHelper;

    /**
     * Constructor.
     */
    public function __construct()
    {
        if (ini_get('xdebug.file_link_format') ||
extension_loaded('xdebug')) {
            // Register editor using xdebug's file_link_format option.
            $this->editors['xdebug'] = function ($file, $line)
{
                return str_replace(['%f', '%l'],
[$file, $line], ini_get('xdebug.file_link_format'));
            };

            // If xdebug is available, use it as default editor.
            $this->setEditor('xdebug');
        }

        // Add the default, local resource search path:
        $this->searchPaths[] = __DIR__ . "/../Resources";

        // blacklist php provided auth based values
        $this->blacklist('_SERVER', 'PHP_AUTH_PW');

        $this->templateHelper = new TemplateHelper();

        if
(class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
            $cloner = new VarCloner();
            // Only dump object internals if a custom caster exists for
performance reasons
            // https://github.com/filp/whoops/pull/404
            $cloner->addCasters(['*' => function ($obj, $a,
$stub, $isNested, $filter = 0) {
                $class = $stub->class;
                $classes = [$class => $class] + class_parents($class) +
class_implements($class);

                foreach ($classes as $class) {
                    if (isset(AbstractCloner::$defaultCasters[$class])) {
                        return $a;
                    }
                }

                // Remove all internals
                return [];
            }]);
            $this->templateHelper->setCloner($cloner);
        }
    }

    /**
     * @return int|null
     */
    public function handle()
    {
        if (!$this->handleUnconditionally()) {
            // Check conditions for outputting HTML:
            // @todo: Make this more robust
            if (PHP_SAPI === 'cli') {
                // Help users who have been relying on an internal test
value
                // fix their code to the proper method
                if (isset($_ENV['whoops-test'])) {
                    throw new \Exception(
                        'Use handleUnconditionally instead of
whoops-test'
                        .' environment variable'
                    );
                }

                return Handler::DONE;
            }
        }

        $templateFile =
$this->getResource("views/layout.html.php");
        $cssFile      =
$this->getResource("css/whoops.base.css");
        $zeptoFile    = $this->getResource("js/zepto.min.js");
        $prettifyFile =
$this->getResource("js/prettify.min.js");
        $clipboard    =
$this->getResource("js/clipboard.min.js");
        $jsFile       =
$this->getResource("js/whoops.base.js");

        if ($this->customCss) {
            $customCssFile = $this->getResource($this->customCss);
        }

        $inspector = $this->getInspector();
        $frames = $this->getExceptionFrames();
        $code = $this->getExceptionCode();

        // List of variables that will be passed to the layout template.
        $vars = [
            "page_title" => $this->getPageTitle(),

            // @todo: Asset compiler
            "stylesheet" => file_get_contents($cssFile),
            "zepto"      => file_get_contents($zeptoFile),
            "prettify"   => file_get_contents($prettifyFile),
            "clipboard"  => file_get_contents($clipboard),
            "javascript" => file_get_contents($jsFile),

            // Template paths:
            "header"                     =>
$this->getResource("views/header.html.php"),
            "header_outer"               =>
$this->getResource("views/header_outer.html.php"),
            "frame_list"                 =>
$this->getResource("views/frame_list.html.php"),
            "frames_description"         =>
$this->getResource("views/frames_description.html.php"),
            "frames_container"           =>
$this->getResource("views/frames_container.html.php"),
            "panel_details"              =>
$this->getResource("views/panel_details.html.php"),
            "panel_details_outer"        =>
$this->getResource("views/panel_details_outer.html.php"),
            "panel_left"                 =>
$this->getResource("views/panel_left.html.php"),
            "panel_left_outer"           =>
$this->getResource("views/panel_left_outer.html.php"),
            "frame_code"                 =>
$this->getResource("views/frame_code.html.php"),
            "env_details"                =>
$this->getResource("views/env_details.html.php"),

            "title"            => $this->getPageTitle(),
            "name"             => explode("\\",
$inspector->getExceptionName()),
            "message"          =>
$inspector->getExceptionMessage(),
            "previousMessages" =>
$inspector->getPreviousExceptionMessages(),
            "docref_url"       =>
$inspector->getExceptionDocrefUrl(),
            "code"             => $code,
            "previousCodes"    =>
$inspector->getPreviousExceptionCodes(),
            "plain_exception"  =>
Formatter::formatExceptionPlain($inspector),
            "frames"           => $frames,
            "has_frames"       => !!count($frames),
            "handler"          => $this,
            "handlers"         =>
$this->getRun()->getHandlers(),

            "active_frames_tab" => count($frames) &&
$frames->offsetGet(0)->isApplication() ?  'application' :
'all',
            "has_frames_tabs"   =>
$this->getApplicationPaths(),

            "tables"      => [
                "GET Data"              =>
$this->masked($_GET, '_GET'),
                "POST Data"             =>
$this->masked($_POST, '_POST'),
                "Files"                 => isset($_FILES) ?
$this->masked($_FILES, '_FILES') : [],
                "Cookies"               =>
$this->masked($_COOKIE, '_COOKIE'),
                "Session"               => isset($_SESSION) ?
$this->masked($_SESSION, '_SESSION') :  [],
                "Server/Request Data"   =>
$this->masked($_SERVER, '_SERVER'),
                "Environment Variables" =>
$this->masked($_ENV, '_ENV'),
            ],
        ];

        if (isset($customCssFile)) {
            $vars["stylesheet"] .=
file_get_contents($customCssFile);
        }

        // Add extra entries list of data tables:
        // @todo: Consolidate addDataTable and addDataTableCallback
        $extraTables = array_map(function ($table) use ($inspector) {
            return $table instanceof \Closure ? $table($inspector) :
$table;
        }, $this->getDataTables());
        $vars["tables"] = array_merge($extraTables,
$vars["tables"]);

        $plainTextHandler = new PlainTextHandler();
        $plainTextHandler->setException($this->getException());
        $plainTextHandler->setInspector($this->getInspector());
        $vars["preface"] = "<!--\n\n\n" . 
$this->templateHelper->escape($plainTextHandler->generateResponse())
. "\n\n\n\n\n\n\n\n\n\n\n-->";

        $this->templateHelper->setVariables($vars);
        $this->templateHelper->render($templateFile);

        return Handler::QUIT;
    }

    /**
     * Get the stack trace frames of the exception that is currently being
handled.
     *
     * @return \Whoops\Exception\FrameCollection;
     */
    protected function getExceptionFrames()
    {
        $frames = $this->getInspector()->getFrames();

        if ($this->getApplicationPaths()) {
            foreach ($frames as $frame) {
                foreach ($this->getApplicationPaths() as $path) {
                    if (strpos($frame->getFile(), $path) === 0) {
                        $frame->setApplication(true);
                        break;
                    }
                }
            }
        }

        return $frames;
    }

    /**
     * Get the code of the exception that is currently being handled.
     *
     * @return string
     */
    protected function getExceptionCode()
    {
        $exception = $this->getException();

        $code = $exception->getCode();
        if ($exception instanceof \ErrorException) {
            // ErrorExceptions wrap the php-error types within the
'severity' property
            $code = Misc::translateErrorCode($exception->getSeverity());
        }

        return (string) $code;
    }

    /**
     * @return string
     */
    public function contentType()
    {
        return 'text/html';
    }

    /**
     * Adds an entry to the list of tables displayed in the template.
     * The expected data is a simple associative array. Any nested arrays
     * will be flattened with print_r
     * @param string $label
     * @param array  $data
     */
    public function addDataTable($label, array $data)
    {
        $this->extraTables[$label] = $data;
    }

    /**
     * Lazily adds an entry to the list of tables displayed in the table.
     * The supplied callback argument will be called when the error is
rendered,
     * it should produce a simple associative array. Any nested arrays will
     * be flattened with print_r.
     *
     * @throws InvalidArgumentException If $callback is not callable
     * @param  string                   $label
     * @param  callable                 $callback Callable returning an
associative array
     */
    public function addDataTableCallback($label, /* callable */ $callback)
    {
        if (!is_callable($callback)) {
            throw new InvalidArgumentException('Expecting callback
argument to be callable');
        }

        $this->extraTables[$label] = function
(\Whoops\Exception\Inspector $inspector = null) use ($callback) {
            try {
                $result = call_user_func($callback, $inspector);

                // Only return the result if it can be iterated over by
foreach().
                return is_array($result) || $result instanceof \Traversable
? $result : [];
            } catch (\Exception $e) {
                // Don't allow failure to break the rendering of the
original exception.
                return [];
            }
        };
    }

    /**
     * Returns all the extra data tables registered with this handler.
     * Optionally accepts a 'label' parameter, to only return the
data
     * table under that label.
     * @param  string|null      $label
     * @return array[]|callable
     */
    public function getDataTables($label = null)
    {
        if ($label !== null) {
            return isset($this->extraTables[$label]) ?
                   $this->extraTables[$label] : [];
        }

        return $this->extraTables;
    }

    /**
     * Allows to disable all attempts to dynamically decide whether to
     * handle or return prematurely.
     * Set this to ensure that the handler will perform no matter what.
     * @param  bool|null $value
     * @return bool|null
     */
    public function handleUnconditionally($value = null)
    {
        if (func_num_args() == 0) {
            return $this->handleUnconditionally;
        }

        $this->handleUnconditionally = (bool) $value;
    }

    /**
     * Adds an editor resolver, identified by a string
     * name, and that may be a string path, or a callable
     * resolver. If the callable returns a string, it will
     * be set as the file reference's href attribute.
     *
     * @example
     *  $run->addEditor('macvim',
"mvim://open?url=file://%file&line=%line")
     * @example
     *   $run->addEditor('remove-it', function($file, $line) {
     *       unlink($file);
     *       return "http://stackoverflow.com";
     *   });
     * @param string $identifier
     * @param string|callable $resolver
     */
    public function addEditor($identifier, $resolver)
    {
        $this->editors[$identifier] = $resolver;
    }

    /**
     * Set the editor to use to open referenced files, by a string
     * identifier, or a callable that will be executed for every
     * file reference, with a $file and $line argument, and should
     * return a string.
     *
     * @example
     *   $run->setEditor(function($file, $line) { return
"file:///{$file}"; });
     * @example
     *   $run->setEditor('sublime');
     *
     * @throws InvalidArgumentException If invalid argument identifier
provided
     * @param  string|callable          $editor
     */
    public function setEditor($editor)
    {
        if (!is_callable($editor) &&
!isset($this->editors[$editor])) {
            throw new InvalidArgumentException(
                "Unknown editor identifier: $editor. Known
editors:" .
                implode(",", array_keys($this->editors))
            );
        }

        $this->editor = $editor;
    }

    /**
     * Given a string file path, and an integer file line,
     * executes the editor resolver and returns, if available,
     * a string that may be used as the href property for that
     * file reference.
     *
     * @throws InvalidArgumentException If editor resolver does not return
a string
     * @param  string                   $filePath
     * @param  int                      $line
     * @return string|bool
     */
    public function getEditorHref($filePath, $line)
    {
        $editor = $this->getEditor($filePath, $line);

        if (empty($editor)) {
            return false;
        }

        // Check that the editor is a string, and replace the
        // %line and %file placeholders:
        if (!isset($editor['url']) ||
!is_string($editor['url'])) {
            throw new UnexpectedValueException(
                __METHOD__ . " should always resolve to a string or a
valid editor array; got something else instead."
            );
        }

        $editor['url'] = str_replace("%line",
rawurlencode($line), $editor['url']);
        $editor['url'] = str_replace("%file",
rawurlencode($filePath), $editor['url']);

        return $editor['url'];
    }

    /**
     * Given a boolean if the editor link should
     * act as an Ajax request. The editor must be a
     * valid callable function/closure
     *
     * @throws UnexpectedValueException  If editor resolver does not return
a boolean
     * @param  string                   $filePath
     * @param  int                      $line
     * @return bool
     */
    public function getEditorAjax($filePath, $line)
    {
        $editor = $this->getEditor($filePath, $line);

        // Check that the ajax is a bool
        if (!isset($editor['ajax']) ||
!is_bool($editor['ajax'])) {
            throw new UnexpectedValueException(
                __METHOD__ . " should always resolve to a bool; got
something else instead."
            );
        }
        return $editor['ajax'];
    }

    /**
     * Given a boolean if the editor link should
     * act as an Ajax request. The editor must be a
     * valid callable function/closure
     *
     * @param  string $filePath
     * @param  int    $line
     * @return array
     */
    protected function getEditor($filePath, $line)
    {
        if (!$this->editor || (!is_string($this->editor) &&
!is_callable($this->editor))) {
            return [];
        }

        if (is_string($this->editor) &&
isset($this->editors[$this->editor]) &&
!is_callable($this->editors[$this->editor])) {
            return [
                'ajax' => false,
                'url' => $this->editors[$this->editor],
            ];
        }

        if (is_callable($this->editor) ||
(isset($this->editors[$this->editor]) &&
is_callable($this->editors[$this->editor]))) {
            if (is_callable($this->editor)) {
                $callback = call_user_func($this->editor, $filePath,
$line);
            } else {
                $callback =
call_user_func($this->editors[$this->editor], $filePath, $line);
            }

            if (empty($callback)) {
                return [];
            }

            if (is_string($callback)) {
                return [
                    'ajax' => false,
                    'url' => $callback,
                ];
            }

            return [
                'ajax' => isset($callback['ajax']) ?
$callback['ajax'] : false,
                'url' => isset($callback['url']) ?
$callback['url'] : $callback,
            ];
        }

        return [];
    }

    /**
     * @param  string $title
     * @return void
     */
    public function setPageTitle($title)
    {
        $this->pageTitle = (string) $title;
    }

    /**
     * @return string
     */
    public function getPageTitle()
    {
        return $this->pageTitle;
    }

    /**
     * Adds a path to the list of paths to be searched for
     * resources.
     *
     * @throws InvalidArgumentException If $path is not a valid directory
     *
     * @param  string $path
     * @return void
     */
    public function addResourcePath($path)
    {
        if (!is_dir($path)) {
            throw new InvalidArgumentException(
                "'$path' is not a valid directory"
            );
        }

        array_unshift($this->searchPaths, $path);
    }

    /**
     * Adds a custom css file to be loaded.
     *
     * @param  string $name
     * @return void
     */
    public function addCustomCss($name)
    {
        $this->customCss = $name;
    }

    /**
     * @return array
     */
    public function getResourcePaths()
    {
        return $this->searchPaths;
    }

    /**
     * Finds a resource, by its relative path, in all available search
paths.
     * The search is performed starting at the last search path, and all
the
     * way back to the first, enabling a cascading-type system of overrides
     * for all resources.
     *
     * @throws RuntimeException If resource cannot be found in any of the
available paths
     *
     * @param  string $resource
     * @return string
     */
    protected function getResource($resource)
    {
        // If the resource was found before, we can speed things up
        // by caching its absolute, resolved path:
        if (isset($this->resourceCache[$resource])) {
            return $this->resourceCache[$resource];
        }

        // Search through available search paths, until we find the
        // resource we're after:
        foreach ($this->searchPaths as $path) {
            $fullPath = $path . "/$resource";

            if (is_file($fullPath)) {
                // Cache the result:
                $this->resourceCache[$resource] = $fullPath;
                return $fullPath;
            }
        }

        // If we got this far, nothing was found.
        throw new RuntimeException(
            "Could not find resource '$resource' in any
resource paths."
            . "(searched: " . join(", ",
$this->searchPaths). ")"
        );
    }

    /**
     * @deprecated
     *
     * @return string
     */
    public function getResourcesPath()
    {
        $allPaths = $this->getResourcePaths();

        // Compat: return only the first path added
        return end($allPaths) ?: null;
    }

    /**
     * @deprecated
     *
     * @param  string $resourcesPath
     * @return void
     */
    public function setResourcesPath($resourcesPath)
    {
        $this->addResourcePath($resourcesPath);
    }

    /**
     * Return the application paths.
     *
     * @return array
     */
    public function getApplicationPaths()
    {
        return $this->applicationPaths;
    }

    /**
     * Set the application paths.
     *
     * @param array $applicationPaths
     */
    public function setApplicationPaths($applicationPaths)
    {
        $this->applicationPaths = $applicationPaths;
    }

    /**
     * Set the application root path.
     *
     * @param string $applicationRootPath
     */
    public function setApplicationRootPath($applicationRootPath)
    {
       
$this->templateHelper->setApplicationRootPath($applicationRootPath);
    }

    /**
     * blacklist a sensitive value within one of the superglobal arrays.
     *
     * @param $superGlobalName string the name of the superglobal array,
e.g. '_GET'
     * @param $key string the key within the superglobal
     */
    public function blacklist($superGlobalName, $key)
    {
        $this->blacklist[$superGlobalName][] = $key;
    }

    /**
     * Checks all values within the given superGlobal array.
     * Blacklisted values will be replaced by a equal length string
cointaining only '*' characters.
     *
     * We intentionally dont rely on $GLOBALS as it depends on
'auto_globals_jit' php.ini setting.
     *
     * @param $superGlobal array One of the superglobal arrays
     * @param $superGlobalName string the name of the superglobal array,
e.g. '_GET'
     * @return array $values without sensitive data
     */
    private function masked(array $superGlobal, $superGlobalName)
    {
        $blacklisted = $this->blacklist[$superGlobalName];

        $values = $superGlobal;
        foreach ($blacklisted as $key) {
            if (isset($superGlobal[$key]) &&
is_string($superGlobal[$key])) {
                $values[$key] = str_repeat('*',
strlen($superGlobal[$key]));
            }
        }
        return $values;
    }
}
vendor/filp/whoops/src/Whoops/Handler/XmlResponseHandler.php000064400000005156151166614520020241
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Handler;

use SimpleXMLElement;
use Whoops\Exception\Formatter;

/**
 * Catches an exception and converts it to an XML
 * response. Additionally can also return exception
 * frames for consumption by an API.
 */
class XmlResponseHandler extends Handler
{
    /**
     * @var bool
     */
    private $returnFrames = false;

    /**
     * @param  bool|null  $returnFrames
     * @return bool|$this
     */
    public function addTraceToOutput($returnFrames = null)
    {
        if (func_num_args() == 0) {
            return $this->returnFrames;
        }

        $this->returnFrames = (bool) $returnFrames;
        return $this;
    }

    /**
     * @return int
     */
    public function handle()
    {
        $response = [
            'error' => Formatter::formatExceptionAsDataArray(
                $this->getInspector(),
                $this->addTraceToOutput()
            ),
        ];

        echo self::toXml($response);

        return Handler::QUIT;
    }

    /**
     * @return string
     */
    public function contentType()
    {
        return 'application/xml';
    }

    /**
     * @param  SimpleXMLElement  $node Node to append data to, will be
modified in place
     * @param  array|\Traversable $data
     * @return SimpleXMLElement  The modified node, for chaining
     */
    private static function addDataToNode(\SimpleXMLElement $node, $data)
    {
        assert(is_array($data) || $data instanceof Traversable);

        foreach ($data as $key => $value) {
            if (is_numeric($key)) {
                // Convert the key to a valid string
                $key = "unknownNode_". (string) $key;
            }

            // Delete any char not allowed in XML element names
            $key = preg_replace('/[^a-z0-9\-\_\.\:]/i',
'', $key);

            if (is_array($value)) {
                $child = $node->addChild($key);
                self::addDataToNode($child, $value);
            } else {
                $value = str_replace('&',
'&amp;', print_r($value, true));
                $node->addChild($key, $value);
            }
        }

        return $node;
    }

    /**
     * The main function for converting to an XML document.
     *
     * @param  array|\Traversable $data
     * @return string            XML
     */
    private static function toXml($data)
    {
        assert(is_array($data) || $data instanceof Traversable);

        $node = simplexml_load_string("<?xml
version='1.0' encoding='utf-8'?><root
/>");

        return self::addDataToNode($node, $data)->asXML();
    }
}
vendor/filp/whoops/src/Whoops/Resources/css/whoops.base.css000064400000027011151166614520020074
0ustar00body {
  font: 12px "Helvetica Neue", helvetica, arial, sans-serif;
  color: #131313;
  background: #eeeeee;
  padding:0;
  margin: 0;
  max-height: 100%;

  text-rendering: optimizeLegibility;
}
  a {
    text-decoration: none;
  }

.Whoops.container {
    position: relative;
    z-index: 9999999999;
}

.panel {
    overflow-y: scroll;
    height: 100%;
    position: fixed;
    margin: 0;
    left: 0;
    top: 0;
}

.branding {
  position: absolute;
  top: 10px;
  right: 20px;
  color: #777777;
  font-size: 10px;
    z-index: 100;
}
  .branding a {
    color: #e95353;
  }

header {
  color: white;
  box-sizing: border-box;
  background-color: #2a2a2a;
  padding: 35px 40px;
  max-height: 180px;
  overflow: hidden;
  transition: 0.5s;
}

  header.header-expand {
    max-height: 1000px;
  }

  .exc-title {
    margin: 0;
    color: #bebebe;
    font-size: 14px;
  }
    .exc-title-primary, .exc-title-secondary {
      color: #e95353;
    }

    .exc-message {
      font-size: 20px;
      word-wrap: break-word;
      margin: 4px 0 0 0;
      color: white;
    }
      .exc-message span {
        display: block;
      }
      .exc-message-empty-notice {
        color: #a29d9d;
        font-weight: 300;
      }

.prev-exc-title {
  margin: 10px 0;
}

.prev-exc-title + ul {
  margin: 0;
  padding: 0 0 0 20px;
  line-height: 12px;
}

.prev-exc-title + ul li {
  font: 12px "Helvetica Neue", helvetica, arial, sans-serif;
}

.prev-exc-title + ul li .prev-exc-code {
  display: inline-block;
  color: #bebebe;
}

.details-container {
  left: 30%;
  width: 70%;
  background: #fafafa;
}
  .details {
    padding: 5px;
  }

    .details-heading {
      color: #4288CE;
      font-weight: 300;
      padding-bottom: 10px;
      margin-bottom: 10px;
      border-bottom: 1px solid rgba(0, 0, 0, .1);
    }

    .details pre.sf-dump {
      white-space: pre;
      word-wrap: inherit;
    }

    .details pre.sf-dump,
    .details pre.sf-dump .sf-dump-num,
    .details pre.sf-dump .sf-dump-const,
    .details pre.sf-dump .sf-dump-str,
    .details pre.sf-dump .sf-dump-note,
    .details pre.sf-dump .sf-dump-ref,
    .details pre.sf-dump .sf-dump-public,
    .details pre.sf-dump .sf-dump-protected,
    .details pre.sf-dump .sf-dump-private,
    .details pre.sf-dump .sf-dump-meta,
    .details pre.sf-dump .sf-dump-key,
    .details pre.sf-dump .sf-dump-index {
      color: #463C54;
    }

.left-panel {
  width: 30%;
  background: #ded8d8;
}

  .frames-description {
    background: rgba(0, 0, 0, .05);
    padding: 8px 15px;
    color: #a29d9d;
    font-size: 11px;
  }

  .frames-description.frames-description-application {
    text-align: center;
    font-size: 12px;
  }
  .frames-container.frames-container-application
.frame:not(.frame-application) {
    display: none;
  }

  .frames-tab {
    color: #a29d9d;
    display: inline-block;
    padding: 4px 8px;
    margin: 0 2px;
    border-radius: 3px;
  }

  .frames-tab.frames-tab-active {
    background-color: #2a2a2a;
    color: #bebebe;
  }

  .frame {
    padding: 14px;
    cursor: pointer;
    transition: all 0.1s ease;
    background: #eeeeee;
  }
    .frame:not(:last-child) {
      border-bottom: 1px solid rgba(0, 0, 0, .05);
    }

    .frame.active {
      box-shadow: inset -5px 0 0 0 #4288CE;
      color: #4288CE;
    }

    .frame:not(.active):hover {
      background: #BEE9EA;
    }

    .frame-method-info {
      margin-bottom: 10px;
    }

    .frame-class, .frame-function, .frame-index {
      font-size: 14px;
    }

    .frame-index {
      float: left;
    }

    .frame-method-info {
      margin-left: 24px;
    }

    .frame-index {
      font-size: 11px;
      color: #a29d9d;
      background-color: rgba(0, 0, 0, .05);
      height: 18px;
      width: 18px;
      line-height: 18px;
      border-radius: 5px;
      padding: 0 1px 0 1px;
      text-align: center;
      display: inline-block;
    }

    .frame-application .frame-index {
      background-color: #2a2a2a;
      color: #bebebe;
    }

    .frame-file {
      font-family: "Inconsolata", "Fira Mono",
"Source Code Pro", Monaco, Consolas, "Lucida Console",
monospace;
      color: #a29d9d;
    }

      .frame-file .editor-link {
        color: #a29d9d;
      }

    .frame-line {
      font-weight: bold;
    }

    .frame-line:before {
      content: ":";
    }

    .frame-code {
      padding: 5px;
      background: #303030;
      display: none;
    }

    .frame-code.active {
      display: block;
    }

    .frame-code .frame-file {
      color: #a29d9d;
      padding: 12px 6px;

      border-bottom: none;
    }

    .code-block {
      padding: 10px;
      margin: 0;
      border-radius: 6px;
      box-shadow: 0 3px 0 rgba(0, 0, 0, .05),
                  0 10px 30px rgba(0, 0, 0, .05),
                  inset 0 0 1px 0 rgba(255, 255, 255, .07);
      -moz-tab-size: 4;
      -o-tab-size: 4;
      tab-size: 4;
    }

    .linenums {
      margin: 0;
      margin-left: 10px;
    }

    .frame-comments {
      border-top: none;
      margin-top: 15px;

      font-size: 12px;
    }

    .frame-comments.empty {
    }

    .frame-comments.empty:before {
      content: "No comments for this stack frame.";
      font-weight: 300;
      color: #a29d9d;
    }

    .frame-comment {
      padding: 10px;
      color: #e3e3e3;
      border-radius: 6px;
      background-color: rgba(255, 255, 255, .05);
    }
      .frame-comment a {
        font-weight: bold;
        text-decoration: none;
      }
        .frame-comment a:hover {
          color: #4bb1b1;
        }

    .frame-comment:not(:last-child) {
      border-bottom: 1px dotted rgba(0, 0, 0, .3);
    }

    .frame-comment-context {
      font-size: 10px;
      color: white;
    }

.delimiter {
  display: inline-block;
}

.data-table-container label {
  font-size: 16px;
  color: #303030;
  font-weight: bold;
  margin: 10px 0;

  display: block;

  margin-bottom: 5px;
  padding-bottom: 5px;
}
  .data-table {
    width: 100%;
    margin-bottom: 10px;
  }

  .data-table tbody {
    font: 13px "Inconsolata", "Fira Mono", "Source
Code Pro", Monaco, Consolas, "Lucida Console", monospace;
  }

  .data-table thead {
    display: none;
  }

  .data-table tr {
    padding: 5px 0;
  }

  .data-table td:first-child {
    width: 20%;
    min-width: 130px;
    overflow: hidden;
    font-weight: bold;
    color: #463C54;
    padding-right: 5px;

  }

  .data-table td:last-child {
    width: 80%;
    -ms-word-break: break-all;
    word-break: break-all;
    word-break: break-word;
    -webkit-hyphens: auto;
    -moz-hyphens: auto;
    hyphens: auto;
  }

  .data-table span.empty {
    color: rgba(0, 0, 0, .3);
    font-weight: 300;
  }
  .data-table label.empty {
    display: inline;
  }

.handler {
  padding: 4px 0;
  font: 14px "Inconsolata", "Fira Mono", "Source
Code Pro", Monaco, Consolas, "Lucida Console", monospace;
}

/* prettify code style
Uses the Doxy theme as a base */
pre .str, code .str { color: #BCD42A; }  /* string  */
pre .kwd, code .kwd { color: #4bb1b1;  font-weight: bold; }  /* keyword*/
pre .com, code .com { color: #888; font-weight: bold; } /* comment */
pre .typ, code .typ { color: #ef7c61; }  /* type  */
pre .lit, code .lit { color: #BCD42A; }  /* literal */
pre .pun, code .pun { color: #fff; font-weight: bold;  } /* punctuation  */
pre .pln, code .pln { color: #e9e4e5; }  /* plaintext  */
pre .tag, code .tag { color: #4bb1b1; }  /* html/xml tag  */
pre .htm, code .htm { color: #dda0dd; }  /* html tag */
pre .xsl, code .xsl { color: #d0a0d0; }  /* xslt tag */
pre .atn, code .atn { color: #ef7c61; font-weight: normal;} /* html/xml
attribute name */
pre .atv, code .atv { color: #bcd42a; }  /* html/xml attribute value  */
pre .dec, code .dec { color: #606; }  /* decimal  */
pre.code-block, code.code-block, .frame-args.code-block,
.frame-args.code-block samp {
  font-family: "Inconsolata", "Fira Mono", "Source
Code Pro", Monaco, Consolas, "Lucida Console", monospace;
  background: #333;
  color: #e9e4e5;
}
  pre.code-block {
    white-space: pre-wrap;
  }

  pre.code-block a, code.code-block a {
    text-decoration:none;
  }

  .linenums li {
    color: #A5A5A5;
  }

  .linenums li.current{
    background: rgba(255, 100, 100, .07);
  }
    .linenums li.current.active {
      background: rgba(255, 100, 100, .17);
    }

pre:not(.prettyprinted) {
  padding-left: 60px;
}

#plain-exception {
  display: none;
}

.rightButton {
  cursor: pointer;
  border: 0;
  opacity: .8;
  background: none;

  color: rgba(255, 255, 255, 0.1);
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.1);

  border-radius: 3px;

  outline: none !important;
}

  .rightButton:hover {
    box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.3);
    color: rgba(255, 255, 255, 0.3);
  }

/* inspired by githubs kbd styles */
kbd {
  -moz-border-bottom-colors: none;
  -moz-border-left-colors: none;
  -moz-border-right-colors: none;
  -moz-border-top-colors: none;
  background-color: #fcfcfc;
  border-color: #ccc #ccc #bbb;
  border-image: none;
  border-style: solid;
  border-width: 1px;
  color: #555;
  display: inline-block;
  font-size: 11px;
  line-height: 10px;
  padding: 3px 5px;
  vertical-align: middle;
}


/* == Media queries */

/* Expand the spacing in the details section */
@media (min-width: 1000px) {
  .details, .frame-code {
    padding: 20px 40px;
  }

  .details-container {
    left: 32%;
    width: 68%;
  }

  .frames-container {
    margin: 5px;
  }

  .left-panel {
    width: 32%;
  }
}

/* Stack panels */
@media (max-width: 600px) {
  .panel {
    position: static;
    width: 100%;
  }
}

/* Stack details tables */
@media (max-width: 400px) {
  .data-table,
  .data-table tbody,
  .data-table tbody tr,
  .data-table tbody td {
    display: block;
    width: 100%;
  }

    .data-table tbody tr:first-child {
      padding-top: 0;
    }

      .data-table tbody td:first-child,
      .data-table tbody td:last-child {
        padding-left: 0;
        padding-right: 0;
      }

      .data-table tbody td:last-child {
        padding-top: 3px;
      }
}

.tooltipped {
  position: relative
}
.tooltipped:after {
  position: absolute;
  z-index: 1000000;
  display: none;
  padding: 5px 8px;
  color: #fff;
  text-align: center;
  text-decoration: none;
  text-shadow: none;
  text-transform: none;
  letter-spacing: normal;
  word-wrap: break-word;
  white-space: pre;
  pointer-events: none;
  content: attr(aria-label);
  background: rgba(0, 0, 0, 0.8);
  border-radius: 3px;
  -webkit-font-smoothing: subpixel-antialiased
}
.tooltipped:before {
  position: absolute;
  z-index: 1000001;
  display: none;
  width: 0;
  height: 0;
  color: rgba(0, 0, 0, 0.8);
  pointer-events: none;
  content: "";
  border: 5px solid transparent
}
.tooltipped:hover:before,
.tooltipped:hover:after,
.tooltipped:active:before,
.tooltipped:active:after,
.tooltipped:focus:before,
.tooltipped:focus:after {
  display: inline-block;
  text-decoration: none
}
.tooltipped-s:after {
  top: 100%;
  right: 50%;
  margin-top: 5px
}
.tooltipped-s:before {
  top: auto;
  right: 50%;
  bottom: -5px;
  margin-right: -5px;
  border-bottom-color: rgba(0, 0, 0, 0.8)
}

pre.sf-dump {
  padding: 0px !important;
  margin: 0px !important;
}

.search-for-help {
  width: 85%;
  padding: 0;
  margin: 10px 0;
  list-style-type: none;
  display: inline-block;
}
  .search-for-help li {
    display: inline-block;
    margin-right: 5px;
  }
  .search-for-help li:last-child {
    margin-right: 0;
  }
    .search-for-help li a {

    }
      .search-for-help li a i {
        width: 16px;
        height: 16px;
        overflow: hidden;
        display: block;
      }
      .search-for-help li a svg {
        fill: #fff;
      }
      .search-for-help li a svg path {
        background-size: contain;
      }
vendor/filp/whoops/src/Whoops/Resources/js/clipboard.min.js000064400000021114151166614520020033
0ustar00/*!
 * clipboard.js v1.5.3
 * https://zenorocha.github.io/clipboard.js
 *
 * Licensed MIT © Zeno Rocha
 */
!function(t){if("object"==typeof
exports&&"undefined"!=typeof
module)module.exports=t();else if("function"==typeof
define&&define.amd)define([],t);else{var
e;e="undefined"!=typeof
window?window:"undefined"!=typeof
global?global:"undefined"!=typeof
self?self:this,e.Clipboard=t()}}(function(){var t,e,n;return function
t(e,n,r){function o(a,c){if(!n[a]){if(!e[a]){var
s="function"==typeof
require&&require;if(!c&&s)return s(a,!0);if(i)return
i(a,!0);var u=new Error("Cannot find module
'"+a+"'");throw
u.code="MODULE_NOT_FOUND",u}var
l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var
n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return
n[a].exports}for(var i="function"==typeof
require&&require,a=0;a<r.length;a++)o(r[a]);return
o}({1:[function(t,e,n){var
r=t("matches-selector");e.exports=function(t,e,n){for(var
o=n?t:t.parentNode;o&&o!==document;){if(r(o,e))return
o;o=o.parentNode}}},{"matches-selector":2}],2:[function(t,e,n){function
r(t,e){if(i)return i.call(t,e);for(var
n=t.parentNode.querySelectorAll(e),r=0;r<n.length;++r)if(n[r]==t)return!0;return!1}var
o=Element.prototype,i=o.matchesSelector||o.webkitMatchesSelector||o.mozMatchesSelector||o.msMatchesSelector||o.oMatchesSelector;e.exports=r},{}],3:[function(t,e,n){function
r(t,e,n,r){var i=o.apply(this,arguments);return
t.addEventListener(n,i),{destroy:function(){t.removeEventListener(n,i)}}}function
o(t,e,n,r){return function(n){var
o=i(n.target,e,!0);o&&(Object.defineProperty(n,"target",{value:o}),r.call(t,n))}}var
i=t("closest");e.exports=r},{closest:1}],4:[function(t,e,n){n.node=function(t){return
void 0!==t&&t instanceof
HTMLElement&&1===t.nodeType},n.nodeList=function(t){var
e=Object.prototype.toString.call(t);return void
0!==t&&("[object NodeList]"===e||"[object
HTMLCollection]"===e)&&"length"in
t&&(0===t.length||n.node(t[0]))},n.string=function(t){return"string"==typeof
t||t instanceof String},n.function=function(t){var
e=Object.prototype.toString.call(t);return"[object
Function]"===e}},{}],5:[function(t,e,n){function
r(t,e,n){if(!t&&!e&&!n)throw new Error("Missing
required arguments");if(!c.string(e))throw new TypeError("Second
argument must be a String");if(!c.function(n))throw new
TypeError("Third argument must be a
Function");if(c.node(t))return o(t,e,n);if(c.nodeList(t))return
i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First
argument must be a String, HTMLElement, HTMLCollection, or
NodeList")}function o(t,e,n){return
t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function
i(t,e,n){return
Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function
a(t,e,n){return s(document.body,t,e,n)}var
c=t("./is"),s=t("delegate");e.exports=r},{"./is":4,delegate:3}],6:[function(t,e,n){function
r(t){var
e;if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName)t.select(),e=t.value;else{var
n=window.getSelection(),r=document.createRange();r.selectNodeContents(t),n.removeAllRanges(),n.addRange(r),e=n.toString()}return
e}e.exports=r},{}],7:[function(t,e,n){function
r(){}r.prototype={on:function(t,e,n){var
r=this.e||(this.e={});return(r[t]||(r[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function
r(){o.off(t,r),e.apply(n,arguments)}var o=this;return
r._=e,this.on(t,r,n)},emit:function(t){var
e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),r=0,o=n.length;for(r;o>r;r++)n[r].fn.apply(n[r].ctx,e);return
this},off:function(t,e){var
n=this.e||(this.e={}),r=n[t],o=[];if(r&&e)for(var
i=0,a=r.length;a>i;i++)r[i].fn!==e&&r[i].fn._!==e&&o.push(r[i]);return
o.length?n[t]=o:delete
n[t],this}},e.exports=r},{}],8:[function(t,e,n){"use
strict";function r(t){return
t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t
instanceof e))throw new TypeError("Cannot call a class as a
function")}n.__esModule=!0;var i=function(){function t(t,e){for(var
n=0;n<e.length;n++){var
r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in
r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}return
function(e,n,r){return
n&&t(e.prototype,n),r&&t(e,r),e}}(),a=t("select"),c=r(a),s=function(){function
t(e){o(this,t),this.resolveOptions(e),this.initSelection()}return
t.prototype.resolveOptions=function t(){var e=arguments.length<=0||void
0===arguments[0]?{}:arguments[0];this.action=e.action,this.emitter=e.emitter,this.target=e.target,this.text=e.text,this.trigger=e.trigger,this.selectedText=""},t.prototype.initSelection=function
t(){if(this.text&&this.target)throw new Error('Multiple
attributes declared, use either "target" or
"text"');if(this.text)this.selectFake();else{if(!this.target)throw
new Error('Missing required attributes, use either "target"
or
"text"');this.selectTarget()}},t.prototype.selectFake=function
t(){var
e=this;this.removeFake(),this.fakeHandler=document.body.addEventListener("click",function(){return
e.removeFake()}),this.fakeElem=document.createElement("textarea"),this.fakeElem.style.position="absolute",this.fakeElem.style.left="-9999px",this.fakeElem.style.top=(window.pageYOffset||document.documentElement.scrollTop)+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,document.body.appendChild(this.fakeElem),this.selectedText=c.default(this.fakeElem),this.copyText()},t.prototype.removeFake=function
t(){this.fakeHandler&&(document.body.removeEventListener("click"),this.fakeHandler=null),this.fakeElem&&(document.body.removeChild(this.fakeElem),this.fakeElem=null)},t.prototype.selectTarget=function
t(){this.selectedText=c.default(this.target),this.copyText()},t.prototype.copyText=function
t(){var e=void
0;try{e=document.execCommand(this.action)}catch(n){e=!1}this.handleResult(e)},t.prototype.handleResult=function
t(e){e?this.emitter.emit("success",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)}):this.emitter.emit("error",{action:this.action,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})},t.prototype.clearSelection=function
t(){this.target&&this.target.blur(),window.getSelection().removeAllRanges()},t.prototype.destroy=function
t(){this.removeFake()},i(t,[{key:"action",set:function t(){var
e=arguments.length<=0||void
0===arguments[0]?"copy":arguments[0];if(this._action=e,"copy"!==this._action&&"cut"!==this._action)throw
new Error('Invalid "action" value, use either
"copy" or "cut"')},get:function t(){return
this._action}},{key:"target",set:function t(e){if(void
0!==e){if(!e||"object"!=typeof e||1!==e.nodeType)throw new
Error('Invalid "target" value, use a valid
Element');this._target=e}},get:function t(){return
this._target}}]),t}();n.default=s,e.exports=n.default},{select:6}],9:[function(t,e,n){"use
strict";function r(t){return
t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t
instanceof e))throw new TypeError("Cannot call a class as a
function")}function i(t,e){if("function"!=typeof
e&&null!==e)throw new TypeError("Super expression must either
be null or a function, not "+typeof
e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function
a(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return
e.getAttribute(n)}n.__esModule=!0;var
c=t("./clipboard-action"),s=r(c),u=t("tiny-emitter"),l=r(u),f=t("good-listener"),d=r(f),h=function(t){function
e(n,r){o(this,e),t.call(this),this.resolveOptions(r),this.listenClick(n)}return
i(e,t),e.prototype.resolveOptions=function t(){var
e=arguments.length<=0||void
0===arguments[0]?{}:arguments[0];this.action="function"==typeof
e.action?e.action:this.defaultAction,this.target="function"==typeof
e.target?e.target:this.defaultTarget,this.text="function"==typeof
e.text?e.text:this.defaultText},e.prototype.listenClick=function t(e){var
n=this;this.listener=d.default(e,"click",function(t){return
n.onClick(t)})},e.prototype.onClick=function
t(e){this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new
s.default({action:this.action(e.target),target:this.target(e.target),text:this.text(e.target),trigger:e.target,emitter:this})},e.prototype.defaultAction=function
t(e){return a("action",e)},e.prototype.defaultTarget=function
t(e){var n=a("target",e);return n?document.querySelector(n):void
0},e.prototype.defaultText=function t(e){return
a("text",e)},e.prototype.destroy=function
t(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)},e}(l.default);n.default=h,e.exports=n.default},{"./clipboard-action":8,"good-listener":5,"tiny-emitter":7}]},{},[9])(9)});vendor/filp/whoops/src/Whoops/Resources/js/prettify.min.js000064400000032703151166614520017750
0ustar00var r=null;window.PR_SHOULD_USE_CONTINUATION=!0;
(function(){function O(a){function i(d){var
a=d.charCodeAt(0);if(a!==92)return a;var
f=d.charAt(1);return(a=s[f])?a:"0"<=f&&f<="7"?parseInt(d.substring(1),8):f==="u"||f==="x"?parseInt(d.substring(2),16):d.charCodeAt(1)}function
g(d){if(d<32)return(d<16?"\\x0":"\\x")+d.toString(16);d=String.fromCharCode(d);return
d==="\\"||d==="-"||d==="]"||d==="^"?"\\"+d:d}function
j(d){var
a=d.substring(1,d.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),d=[],f=
a[0]==="^",b=["["];f&&b.push("^");for(var
f=f?1:0,c=a.length;f<c;++f){var
h=a[f];if(/\\[bdsw]/i.test(h))b.push(h);else{var
h=i(h),e;f+2<c&&"-"===a[f+1]?(e=i(a[f+2]),f+=2):e=h;d.push([h,e]);e<65||h>122||(e<65||h>90||d.push([Math.max(65,h)|32,Math.min(e,90)|32]),e<97||h>122||d.push([Math.max(97,h)&-33,Math.min(e,122)&-33]))}}d.sort(function(d,a){return
d[0]-a[0]||a[1]-d[1]});a=[];c=[];for(f=0;f<d.length;++f)h=d[f],h[0]<=c[1]+1?c[1]=Math.max(c[1],h[1]):a.push(c=h);for(f=0;f<a.length;++f)h=a[f],b.push(g(h[0])),
h[1]>h[0]&&(h[1]+1>h[0]&&b.push("-"),b.push(g(h[1])));b.push("]");return
b.join("")}function t(d){for(var
a=d.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=a.length,i=[],c=0,h=0;c<b;++c){var
e=a[c];e==="("?++h:"\\"===e.charAt(0)&&(e=+e.substring(1))&&(e<=h?i[e]=-1:a[c]=g(e))}for(c=1;c<i.length;++c)-1===i[c]&&(i[c]=++z);for(h=c=0;c<b;++c)e=a[c],e==="("?(++h,i[h]||(a[c]="(?:")):"\\"===e.charAt(0)&&(e=+e.substring(1))&&e<=h&&
(a[c]="\\"+i[e]);for(c=0;c<b;++c)"^"===a[c]&&"^"!==a[c+1]&&(a[c]="");if(d.ignoreCase&&w)for(c=0;c<b;++c)e=a[c],d=e.charAt(0),e.length>=2&&d==="["?a[c]=j(e):d!=="\\"&&(a[c]=e.replace(/[A-Za-z]/g,function(d){d=d.charCodeAt(0);return"["+String.fromCharCode(d&-33,d|32)+"]"}));return
a.join("")}for(var z=0,w=!1,k=!1,m=0,b=a.length;m<b;++m){var
o=a[m];if(o.ignoreCase)k=!0;else
if(/[a-z]/i.test(o.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){w=!0;k=!1;break}}for(var
s={b:8,t:9,n:10,v:11,
f:12,r:13},q=[],m=0,b=a.length;m<b;++m){o=a[m];if(o.global||o.multiline)throw
Error(""+o);q.push("(?:"+t(o)+")")}return
RegExp(q.join("|"),k?"gi":"g")}function
P(a,i){function g(a){switch(a.nodeType){case
1:if(j.test(a.className))break;for(var
b=a.firstChild;b;b=b.nextSibling)g(b);b=a.nodeName.toLowerCase();if("br"===b||"li"===b)t[k]="\n",w[k<<1]=z++,w[k++<<1|1]=a;break;case
3:case
4:b=a.nodeValue,b.length&&(b=i?b.replace(/\r\n?/g,"\n"):b.replace(/[\t\n\r
]+/g," "),t[k]=b,w[k<<1]=z,z+=b.length,w[k++<<
1|1]=a)}}var
j=/(?:^|\s)nocode(?:\s|$)/,t=[],z=0,w=[],k=0;g(a);return{a:t.join("").replace(/\n$/,""),d:w}}function
E(a,i,g,j){i&&(a={a:i,e:a},g(a),j.push.apply(j,a.g))}function
x(a,i){function g(a){for(var
k=a.e,m=[k,"pln"],b=0,o=a.a.match(t)||[],s={},q=0,d=o.length;q<d;++q){var
v=o[q],f=s[v],u=void 0,c;if(typeof f==="string")c=!1;else{var
h=j[v.charAt(0)];if(h)u=v.match(h[1]),f=h[0];else{for(c=0;c<z;++c)if(h=i[c],u=v.match(h[1])){f=h[0];break}u||(f="pln")}if((c=f.length>=5&&"lang-"===f.substring(0,
5))&&!(u&&typeof
u[1]==="string"))c=!1,f="src";c||(s[v]=f)}h=b;b+=v.length;if(c){c=u[1];var
e=v.indexOf(c),p=e+c.length;u[2]&&(p=v.length-u[2].length,e=p-c.length);f=f.substring(5);E(k+h,v.substring(0,e),g,m);E(k+h+e,c,F(f,c),m);E(k+h+p,v.substring(p),g,m)}else
m.push(k+h,f)}a.g=m}var j={},t;(function(){for(var
g=a.concat(i),k=[],m={},b=0,o=g.length;b<o;++b){var
s=g[b],q=s[3];if(q)for(var
d=q.length;--d>=0;)j[q.charAt(d)]=s;s=s[1];q=""+s;m.hasOwnProperty(q)||(k.push(s),m[q]=r)}k.push(/[\S\s]/);t=
O(k)})();var z=i.length;return g}function l(a){var
i=[],g=[];a.tripleQuotedStrings?i.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,r,"'\""]):a.multiLineStrings?i.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,r,"'\"`"]):i.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,r,"\"'"]);a.verbatimStrings&&
g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,r]);var
j=a.hashComments;j&&(a.cStyleComments?(j>1?i.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,r,"#"]):i.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,r,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,r])):i.push(["com",/^#[^\n\r]*/,r,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,r]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,
r]));a.regexLiterals&&g.push(["lang-regex",/^(?:^^\.?|[+-]|[!=]={0,2}|#|%=?|&&?=?|\(|\*=?|[+-]=|->|\/=?|::?|<<?=?|>{1,3}=?|[,;?@[{~]|\^\^?=?|\|\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(j=a.types)&&g.push(["typ",j]);a=(""+a.keywords).replace(/^
|
$/g,"");a.length&&g.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),r]);i.push(["pln",/^\s+/,r,"
\r\n\t\u00a0"]);g.push(["lit",
/^@[$_a-z][\w$@]*/i,r],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,r],["pln",/^[$_a-z][\w$@]*/i,r],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,r,"0123456789"],["pln",/^\\[\S\s]?/,r],["pun",/^.[^\s\w"$'./@\\`]*/,r]);return
x(i,g)}function G(a,i,g){function j(a){switch(a.nodeType){case
1:if(z.test(a.className))break;if("br"===a.nodeName)t(a),a.parentNode&&a.parentNode.removeChild(a);else
for(a=a.firstChild;a;a=a.nextSibling)j(a);break;case 3:case 4:if(g){var b=
a.nodeValue,f=b.match(n);if(f){var
i=b.substring(0,f.index);a.nodeValue=i;(b=b.substring(f.index+f[0].length))&&a.parentNode.insertBefore(k.createTextNode(b),a.nextSibling);t(a);i||a.parentNode.removeChild(a)}}}}function
t(a){function i(a,b){var d=b?a.cloneNode(!1):a,e=a.parentNode;if(e){var
e=i(e,1),f=a.nextSibling;e.appendChild(d);for(var
g=f;g;g=f)f=g.nextSibling,e.appendChild(g)}return
d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var
a=i(a.nextSibling,0),f;(f=a.parentNode)&&f.nodeType===
1;)a=f;b.push(a)}for(var
z=/(?:^|\s)nocode(?:\s|$)/,n=/\r\n?|\n/,k=a.ownerDocument,m=k.createElement("li");a.firstChild;)m.appendChild(a.firstChild);for(var
b=[m],o=0;o<b.length;++o)j(b[o]);i===(i|0)&&b[0].setAttribute("value",i);var
s=k.createElement("ol");s.className="linenums";for(var
i=Math.max(0,i-1|0)||0,o=0,q=b.length;o<q;++o)m=b[o],m.className="L"+(o+i)%10,m.firstChild||m.appendChild(k.createTextNode("\u00a0")),s.appendChild(m);a.appendChild(s)}function
n(a,i){for(var g=i.length;--g>=0;){var j=
i[g];A.hasOwnProperty(j)?C.console&&console.warn("cannot
override language handler %s",j):A[j]=a}}function
F(a,i){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(i)?"default-markup":"default-code";return
A[a]}function H(a){var i=a.h;try{var
g=P(a.c,a.i),j=g.a;a.a=j;a.d=g.d;a.e=0;F(i,j)(a);var
t=/\bMSIE\s(\d+)/.exec(navigator.userAgent),t=t&&+t[1]<=8,i=/\n/g,n=a.a,w=n.length,g=0,k=a.d,m=k.length,j=0,b=a.g,o=b.length,s=0;b[o]=w;var
q,d;for(d=q=0;d<o;)b[d]!==b[d+2]?(b[q++]=b[d++],b[q++]=b[d++]):d+=2;o=q;
for(d=q=0;d<o;){for(var
v=b[d],f=b[d+1],u=d+2;u+2<=o&&b[u+1]===f;)u+=2;b[q++]=v;b[q++]=f;d=u}b.length=q;var
c=a.c,h;if(c)h=c.style.display,c.style.display="none";try{for(;j<m;){var
e=k[j+2]||w,p=b[s+2]||w,u=Math.min(e,p),l=k[j+1],D;if(l.nodeType!==1&&(D=n.substring(g,u))){t&&(D=D.replace(i,"\r"));l.nodeValue=D;var
y=l.ownerDocument,x=y.createElement("span");x.className=b[s+1];var
B=l.parentNode;B.replaceChild(x,l);x.appendChild(l);g<e&&(k[j+1]=l=y.createTextNode(n.substring(u,e)),B.insertBefore(l,
x.nextSibling))}g=u;g>=e&&(j+=2);g>=p&&(s+=2)}}finally{if(c)c.style.display=h}}catch(A){C.console&&console.log(A&&A.stack?A.stack:A)}}var
C=window,y=["break,continue,do,else,for,if,return,while"],B=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],I=[B,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],
J=[B,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],K=[J,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],B=[B,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],
L=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],M=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],N=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
Q=/\S/,R=l({keywords:[I,K,B,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+L,M,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};n(R,["default-code"]);n(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);n(x([["pln",/^\s+/,r,"
\t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,r,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],
["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);n(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);n(l({keywords:I,hashComments:!0,cStyleComments:!0,types:N}),["c","cc","cpp","cxx","cyc","m"]);n(l({keywords:"null,true,false"}),["json"]);n(l({keywords:K,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:N}),
["cs"]);n(l({keywords:J,cStyleComments:!0}),["java"]);n(l({keywords:y,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);n(l({keywords:L,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py"]);n(l({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);n(l({keywords:M,hashComments:!0,
multiLineStrings:!0,regexLiterals:!0}),["rb"]);n(l({keywords:B,cStyleComments:!0,regexLiterals:!0}),["js"]);n(l({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);n(x([],[["str",/^[\S\s]+/]]),["regex"]);var
S=C.PR={createSimpleLexer:x,registerLangHandler:n,sourceDecorator:l,
PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:C.prettyPrintOne=function(a,i,g){var
j=document.createElement("pre");j.innerHTML=a;g&&G(j,g,!0);H({h:i,j:g,c:j,i:1});return
j.innerHTML},prettyPrint:C.prettyPrint=function(a){function i(){var
u;for(var
g=C.PR_SHOULD_USE_CONTINUATION?k.now()+250:Infinity;m<j.length&&
k.now()<g;m++){var
c=j[m],h=c.className;if(s.test(h)&&!q.test(h)){for(var
e=!1,p=c.parentNode;p;p=p.parentNode)if(f.test(p.tagName)&&p.className&&s.test(p.className)){e=!0;break}if(!e){c.className+="
prettyprinted";var h=h.match(o),n;if(e=!h){for(var e=c,p=void
0,l=e.firstChild;l;l=l.nextSibling)var
t=l.nodeType,p=t===1?p?e:l:t===3?Q.test(l.nodeValue)?e:p:p;e=(n=p===e?void
0:p)&&v.test(n.tagName)}e&&(h=n.className.match(o));h&&(h=h[1]);u=d.test(c.tagName)?1:(e=(e=c.currentStyle)?e.whiteSpace:document.defaultView&&
document.defaultView.getComputedStyle?document.defaultView.getComputedStyle(c,r).getPropertyValue("white-space"):0)&&"pre"===e.substring(0,3),e=u;(p=(p=c.className.match(/\blinenums\b(?::(\d+))?/))?p[1]&&p[1].length?+p[1]:!0:!1)&&G(c,p,e);b={h:h,c:c,j:p,i:e};H(b)}}}m<j.length?setTimeout(i,250):a&&a()}for(var
g=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],j=[],n=0;n<g.length;++n)for(var
l=0,w=g[n].length;l<w;++l)j.push(g[n][l]);var g=
r,k=Date;k.now||(k={now:function(){return+new Date}});var
m=0,b,o=/\blang(?:uage)?-([\w.]+)(?!\S)/,s=/\bprettyprint\b/,q=/\bprettyprinted\b/,d=/pre|xmp/i,v=/^code$/i,f=/^(?:pre|code|xmp)$/i;i()}};typeof
define==="function"&&define.amd&&define("google-code-prettify",[],function(){return
S})})();
vendor/filp/whoops/src/Whoops/Resources/js/whoops.base.js000064400000014620151166614540017550
0ustar00Zepto(function($) {
  var $leftPanel      = $('.left-panel');
  var $frameContainer = $('.frames-container');
  var $appFramesTab   = $('#application-frames-tab');
  var $allFramesTab   = $('#all-frames-tab');
  var $container      = $('.details-container');
  var $activeLine     = $frameContainer.find('.frame.active');
  var $activeFrame    = $container.find('.frame-code.active');
  var $ajaxEditors    = $('.editor-link[data-ajax]');
  var $header         = $('header');

  $header.on('mouseenter', function () {
    if ($header.find('.exception').height() >= 145) {
      $header.addClass('header-expand');
    }
  });
  $header.on('mouseleave', function () {
    $header.removeClass('header-expand');
  });

  /*
   * add prettyprint classes to our current active codeblock
   * run prettyPrint() to highlight the active code
   * scroll to the line when prettyprint is done
   * highlight the current line
   */
  var renderCurrentCodeblock = function(id) {

    // remove previous codeblocks so we only render the active one
    $('.code-block').removeClass('prettyprint');

    // pass the id in when we can for speed
    if (typeof(id) === 'undefined' || typeof(id) ===
'object') {
      var id =
/frame\-line\-([\d]*)/.exec($activeLine.attr('id'))[1];
    }

    $('#frame-code-linenums-' +
id).addClass('prettyprint');
    $('#frame-code-args-' +
id).addClass('prettyprint');

    prettyPrint(highlightCurrentLine);

  }

  /*
   * Highlight the active and neighboring lines for the current frame
   * Adjust the offset to make sure that line is veritcally centered
   */

  var highlightCurrentLine = function() {
    var activeLineNumber =
+($activeLine.find('.frame-line').text());
    var $lines           = $activeFrame.find('.linenums li');
    var firstLine        = +($lines.first().val());

    // We show more code than needed, purely for proper syntax highlighting
    // Let’s hide a big chunk of that code and then scroll the remaining
block
    $activeFrame.find('.code-block').first().css({
      maxHeight: 345,
      overflow: 'hidden',
    });

    var $offset = $($lines[activeLineNumber - firstLine - 10]);
    if ($offset.length > 0) {
      $offset[0].scrollIntoView();
    }

    $($lines[activeLineNumber - firstLine -
1]).addClass('current');
    $($lines[activeLineNumber - firstLine]).addClass('current
active');
    $($lines[activeLineNumber - firstLine +
1]).addClass('current');

    $container.scrollTop(0);

  }

  /*
   * click handler for loading codeblocks
   */

  $frameContainer.on('click', '.frame', function() {

    var $this  = $(this);
    var id     =
/frame\-line\-([\d]*)/.exec($this.attr('id'))[1];
    var $codeFrame = $('#frame-code-' + id);

    if ($codeFrame) {

      $activeLine.removeClass('active');
      $activeFrame.removeClass('active');

      $this.addClass('active');
      $codeFrame.addClass('active');

      $activeLine  = $this;
      $activeFrame = $codeFrame;

      renderCurrentCodeblock(id);

    }

  });

  var clipboard = new Clipboard('.clipboard');
  var showTooltip = function(elem, msg) {
    elem.setAttribute('class', 'clipboard tooltipped
tooltipped-s');
    elem.setAttribute('aria-label', msg);
  };

  clipboard.on('success', function(e) {
      e.clearSelection();

      showTooltip(e.trigger, 'Copied!');
  });

  clipboard.on('error', function(e) {
      showTooltip(e.trigger, fallbackMessage(e.action));
  });

  var btn = document.querySelector('.clipboard');

  btn.addEventListener('mouseleave', function(e) {
    e.currentTarget.setAttribute('class', 'clipboard');
    e.currentTarget.removeAttribute('aria-label');
  });

  function fallbackMessage(action) {
    var actionMsg = '';
    var actionKey = (action === 'cut' ? 'X' :
'C');

    if (/Mac/i.test(navigator.userAgent)) {
        actionMsg = 'Press ⌘-' + actionKey + ' to ' +
action;
    } else {
        actionMsg = 'Press Ctrl-' + actionKey + ' to '
+ action;
    }

    return actionMsg;
  }

  function scrollIntoView($node, $parent) {
    var nodeOffset = $node.offset();
    var nodeTop = nodeOffset.top;
    var nodeBottom = nodeTop + nodeOffset.height;
    var parentScrollTop = $parent.scrollTop();
    var parentHeight = $parent.height();

    if (nodeTop < 0) {
      $parent.scrollTop(parentScrollTop + nodeTop);
    } else if (nodeBottom > parentHeight) {
      $parent.scrollTop(parentScrollTop + nodeBottom - parentHeight);
    }
  }

  $(document).on('keydown', function(e) {
    var applicationFrames =
$frameContainer.hasClass('frames-container-application'),
        frameClass = applicationFrames ?
'.frame.frame-application' : '.frame';

	  if(e.ctrlKey || e.which === 74  || e.which === 75) {
		  // CTRL+Arrow-UP/k and Arrow-Down/j support:
		  // 1) select the next/prev element
		  // 2) make sure the newly selected element is within the view-scope
		  // 3) focus the (right) container, so arrow-up/down (without ctrl)
scroll the details
		  if (e.which === 38 /* arrow up */ || e.which === 75 /* k */) {
			  $activeLine.prev(frameClass).click();
			  scrollIntoView($activeLine, $leftPanel);
			  $container.focus();
			  e.preventDefault();
		  } else if (e.which === 40 /* arrow down */ || e.which === 74 /* j */) {
			  $activeLine.next(frameClass).click();
			  scrollIntoView($activeLine, $leftPanel);
			  $container.focus();
			  e.preventDefault();
		  }
	  } else if (e.which == 78 /* n */) {
      if ($appFramesTab.length) {
       
setActiveFramesTab($('.frames-tab:not(.frames-tab-active)'));
      }
    }
  });

  // Render late enough for highlightCurrentLine to be ready
  renderCurrentCodeblock();

  // Avoid to quit the page with some protocol (e.g. IntelliJ Platform REST
API)
  $ajaxEditors.on('click', function(e){
    e.preventDefault();
    $.get(this.href);
  });

  // Symfony VarDumper: Close the by default expanded objects
  $('.sf-dump-expanded')
    .removeClass('sf-dump-expanded')
    .addClass('sf-dump-compact');
  $('.sf-dump-toggle span').html('&#9654;');

  // Make the given frames-tab active
  function setActiveFramesTab($tab) {
    $tab.addClass('frames-tab-active');

    if ($tab.attr('id') == 'application-frames-tab') {
      $frameContainer.addClass('frames-container-application');
      $allFramesTab.removeClass('frames-tab-active');
    } else {
     
$frameContainer.removeClass('frames-container-application');
      $appFramesTab.removeClass('frames-tab-active');
    }
  }

  $('a.frames-tab').on('click', function(e) {
    e.preventDefault();
    setActiveFramesTab($(this));
  });
});
vendor/filp/whoops/src/Whoops/Resources/js/zepto.min.js000064400000060074151166614540017247
0ustar00/* Zepto v1.1.3 - zepto event ajax form ie - zeptojs.com/license */
var Zepto=function(){function L(t){return
null==t?String(t):j[T.call(t)]||"object"}function
Z(t){return"function"==L(t)}function $(t){return
null!=t&&t==t.window}function _(t){return
null!=t&&t.nodeType==t.DOCUMENT_NODE}function
D(t){return"object"==L(t)}function R(t){return
D(t)&&!$(t)&&Object.getPrototypeOf(t)==Object.prototype}function
M(t){return"number"==typeof t.length}function k(t){return
s.call(t,function(t){return null!=t})}function z(t){return
t.length>0?n.fn.concat.apply([],t):t}function F(t){return
t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function
q(t){return t in f?f[t]:f[t]=new
RegExp("(^|\\s)"+t+"(\\s|$)")}function
H(t,e){return"number"!=typeof
e||c[F(t)]?e:e+"px"}function I(t){var e,n;return
u[t]||(e=a.createElement(t),a.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),u[t]=n),u[t]}function
V(t){return"children"in
t?o.call(t.children):n.map(t.childNodes,function(t){return
1==t.nodeType?t:void 0})}function U(n,i,r){for(e in
i)r&&(R(i[e])||A(i[e]))?(R(i[e])&&!R(n[e])&&(n[e]={}),A(i[e])&&!A(n[e])&&(n[e]=[]),U(n[e],i[e],r)):i[e]!==t&&(n[e]=i[e])}function
B(t,e){return null==e?n(t):n(t).filter(e)}function J(t,e,n,i){return
Z(e)?e.call(t,n,i):e}function
X(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function
W(e,n){var i=e.className,r=i&&i.baseVal!==t;return
n===t?r?i.baseVal:i:void(r?i.baseVal=n:e.className=n)}function Y(t){var
e;try{return
t?"true"==t||("false"==t?!1:"null"==t?null:/^0/.test(t)||isNaN(e=Number(t))?/^[\[\{]/.test(t)?n.parseJSON(t):t:e):t}catch(i){return
t}}function G(t,e){e(t);for(var n in t.childNodes)G(t.childNodes[n],e)}var
t,e,n,i,C,N,r=[],o=r.slice,s=r.filter,a=window.document,u={},f={},c={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},l=/^\s*<(\w+|!)[^>]*>/,h=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,p=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,d=/^(?:body|html)$/i,m=/([A-Z])/g,g=["val","css","html","text","data","width","height","offset"],v=["after","prepend","before","append"],y=a.createElement("table"),x=a.createElement("tr"),b={tr:a.createElement("tbody"),tbody:y,thead:y,tfoot:y,td:x,th:x,"*":a.createElement("div")},w=/complete|loaded|interactive/,E=/^[\w-]*$/,j={},T=j.toString,S={},O=a.createElement("div"),P={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},A=Array.isArray||function(t){return
t instanceof Array};return
S.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var
n=t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return
n.call(t,e);var i,r=t.parentNode,o=!r;return
o&&(r=O).appendChild(t),i=~S.qsa(r,e).indexOf(t),o&&O.removeChild(t),i},C=function(t){return
t.replace(/-+(.)?/g,function(t,e){return
e?e.toUpperCase():""})},N=function(t){return
s.call(t,function(e,n){return
t.indexOf(e)==n})},S.fragment=function(e,i,r){var s,u,f;return
h.test(e)&&(s=n(a.createElement(RegExp.$1))),s||(e.replace&&(e=e.replace(p,"<$1></$2>")),i===t&&(i=l.test(e)&&RegExp.$1),i
in
b||(i="*"),f=b[i],f.innerHTML=""+e,s=n.each(o.call(f.childNodes),function(){f.removeChild(this)})),R(r)&&(u=n(s),n.each(r,function(t,e){g.indexOf(t)>-1?u[t](e):u.attr(t,e)})),s},S.Z=function(t,e){return
t=t||[],t.__proto__=n.fn,t.selector=e||"",t},S.isZ=function(t){return
t instanceof S.Z},S.init=function(e,i){var r;if(!e)return
S.Z();if("string"==typeof
e)if(e=e.trim(),"<"==e[0]&&l.test(e))r=S.fragment(e,RegExp.$1,i),e=null;else{if(i!==t)return
n(i).find(e);r=S.qsa(a,e)}else{if(Z(e))return
n(a).ready(e);if(S.isZ(e))return e;if(A(e))r=k(e);else
if(D(e))r=[e],e=null;else
if(l.test(e))r=S.fragment(e.trim(),RegExp.$1,i),e=null;else{if(i!==t)return
n(i).find(e);r=S.qsa(a,e)}}return S.Z(r,e)},n=function(t,e){return
S.init(t,e)},n.extend=function(t){var
e,n=o.call(arguments,1);return"boolean"==typeof
t&&(e=t,t=n.shift()),n.forEach(function(n){U(t,n,e)}),t},S.qsa=function(t,e){var
n,i="#"==e[0],r=!i&&"."==e[0],s=i||r?e.slice(1):e,a=E.test(s);return
_(t)&&a&&i?(n=t.getElementById(s))?[n]:[]:1!==t.nodeType&&9!==t.nodeType?[]:o.call(a&&!i?r?t.getElementsByClassName(s):t.getElementsByTagName(e):t.querySelectorAll(e))},n.contains=function(t,e){return
t!==e&&t.contains(e)},n.type=L,n.isFunction=Z,n.isWindow=$,n.isArray=A,n.isPlainObject=R,n.isEmptyObject=function(t){var
e;for(e in t)return!1;return!0},n.inArray=function(t,e,n){return
r.indexOf.call(e,t,n)},n.camelCase=C,n.trim=function(t){return
null==t?"":String.prototype.trim.call(t)},n.uuid=0,n.support={},n.expr={},n.map=function(t,e){var
n,r,o,i=[];if(M(t))for(r=0;r<t.length;r++)n=e(t[r],r),null!=n&&i.push(n);else
for(o in t)n=e(t[o],o),null!=n&&i.push(n);return
z(i)},n.each=function(t,e){var
n,i;if(M(t)){for(n=0;n<t.length;n++)if(e.call(t[n],n,t[n])===!1)return
t}else for(i in t)if(e.call(t[i],i,t[i])===!1)return t;return
t},n.grep=function(t,e){return
s.call(t,e)},window.JSON&&(n.parseJSON=JSON.parse),n.each("Boolean
Number String Function Array Date RegExp Object Error".split("
"),function(t,e){j["[object
"+e+"]"]=e.toLowerCase()}),n.fn={forEach:r.forEach,reduce:r.reduce,push:r.push,sort:r.sort,indexOf:r.indexOf,concat:r.concat,map:function(t){return
n(n.map(this,function(e,n){return t.call(e,n,e)}))},slice:function(){return
n(o.apply(this,arguments))},ready:function(t){return
w.test(a.readyState)&&a.body?t(n):a.addEventListener("DOMContentLoaded",function(){t(n)},!1),this},get:function(e){return
e===t?o.call(this):this[e>=0?e:e+this.length]},toArray:function(){return
this.get()},size:function(){return this.length},remove:function(){return
this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return
r.every.call(this,function(e,n){return
t.call(e,n,e)!==!1}),this},filter:function(t){return
Z(t)?this.not(this.not(t)):n(s.call(this,function(e){return
S.matches(e,t)}))},add:function(t,e){return
n(N(this.concat(n(t,e))))},is:function(t){return
this.length>0&&S.matches(this[0],t)},not:function(e){var
i=[];if(Z(e)&&e.call!==t)this.each(function(t){e.call(this,t)||i.push(this)});else{var
r="string"==typeof
e?this.filter(e):M(e)&&Z(e.item)?o.call(e):n(e);this.forEach(function(t){r.indexOf(t)<0&&i.push(t)})}return
n(i)},has:function(t){return this.filter(function(){return
D(t)?n.contains(this,t):n(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var
t=this[0];return t&&!D(t)?t:n(t)},last:function(){var
t=this[this.length-1];return t&&!D(t)?t:n(t)},find:function(t){var
e,i=this;return e="object"==typeof t?n(t).filter(function(){var
t=this;return r.some.call(i,function(e){return
n.contains(e,t)})}):1==this.length?n(S.qsa(this[0],t)):this.map(function(){return
S.qsa(this,t)})},closest:function(t,e){var
i=this[0],r=!1;for("object"==typeof
t&&(r=n(t));i&&!(r?r.indexOf(i)>=0:S.matches(i,t));)i=i!==e&&!_(i)&&i.parentNode;return
n(i)},parents:function(t){for(var
e=[],i=this;i.length>0;)i=n.map(i,function(t){return(t=t.parentNode)&&!_(t)&&e.indexOf(t)<0?(e.push(t),t):void
0});return B(e,t)},parent:function(t){return
B(N(this.pluck("parentNode")),t)},children:function(t){return
B(this.map(function(){return V(this)}),t)},contents:function(){return
this.map(function(){return
o.call(this.childNodes)})},siblings:function(t){return
B(this.map(function(t,e){return s.call(V(e.parentNode),function(t){return
t!==e})}),t)},empty:function(){return
this.each(function(){this.innerHTML=""})},pluck:function(t){return
n.map(this,function(e){return e[t]})},show:function(){return
this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=I(this.nodeName))})},replaceWith:function(t){return
this.before(t).remove()},wrap:function(t){var
e=Z(t);if(this[0]&&!e)var
i=n(t).get(0),r=i.parentNode||this.length>1;return
this.each(function(o){n(this).wrapAll(e?t.call(this,o):r?i.cloneNode(!0):i)})},wrapAll:function(t){if(this[0]){n(this[0]).before(t=n(t));for(var
e;(e=t.children()).length;)t=e.first();n(t).append(this)}return
this},wrapInner:function(t){var e=Z(t);return this.each(function(i){var
r=n(this),o=r.contents(),s=e?t.call(this,i):t;o.length?o.wrapAll(s):r.append(s)})},unwrap:function(){return
this.parent().each(function(){n(this).replaceWith(n(this).children())}),this},clone:function(){return
this.map(function(){return this.cloneNode(!0)})},hide:function(){return
this.css("display","none")},toggle:function(e){return
this.each(function(){var
i=n(this);(e===t?"none"==i.css("display"):e)?i.show():i.hide()})},prev:function(t){return
n(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return
n(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return
0===arguments.length?this.length>0?this[0].innerHTML:null:this.each(function(e){var
i=this.innerHTML;n(this).empty().append(J(this,t,e,i))})},text:function(e){return
0===arguments.length?this.length>0?this[0].textContent:null:this.each(function(){this.textContent=e===t?"":""+e})},attr:function(n,i){var
r;return"string"==typeof
n&&i===t?0==this.length||1!==this[0].nodeType?t:"value"==n&&"INPUT"==this[0].nodeName?this.val():!(r=this[0].getAttribute(n))&&n
in
this[0]?this[0][n]:r:this.each(function(t){if(1===this.nodeType)if(D(n))for(e
in n)X(this,e,n[e]);else
X(this,n,J(this,i,t,this.getAttribute(n)))})},removeAttr:function(t){return
this.each(function(){1===this.nodeType&&X(this,t)})},prop:function(e,n){return
e=P[e]||e,n===t?this[0]&&this[0][e]:this.each(function(t){this[e]=J(this,n,t,this[e])})},data:function(e,n){var
i=this.attr("data-"+e.replace(m,"-$1").toLowerCase(),n);return
null!==i?Y(i):t},val:function(t){return
0===arguments.length?this[0]&&(this[0].multiple?n(this[0]).find("option").filter(function(){return
this.selected}).pluck("value"):this[0].value):this.each(function(e){this.value=J(this,t,e,this.value)})},offset:function(t){if(t)return
this.each(function(e){var
i=n(this),r=J(this,t,e,i.offset()),o=i.offsetParent().offset(),s={top:r.top-o.top,left:r.left-o.left};"static"==i.css("position")&&(s.position="relative"),i.css(s)});if(0==this.length)return
null;var
e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(t,i){if(arguments.length<2){var
r=this[0],o=getComputedStyle(r,"");if(!r)return;if("string"==typeof
t)return r.style[C(t)]||o.getPropertyValue(t);if(A(t)){var s={};return
n.each(A(t)?t:[t],function(t,e){s[e]=r.style[C(e)]||o.getPropertyValue(e)}),s}}var
a="";if("string"==L(t))i||0===i?a=F(t)+":"+H(t,i):this.each(function(){this.style.removeProperty(F(t))});else
for(e in
t)t[e]||0===t[e]?a+=F(e)+":"+H(e,t[e])+";":this.each(function(){this.style.removeProperty(F(e))});return
this.each(function(){this.style.cssText+=";"+a})},index:function(t){return
t?this.indexOf(n(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return
t?r.some.call(this,function(t){return
this.test(W(t))},q(t)):!1},addClass:function(t){return
t?this.each(function(e){i=[];var
r=W(this),o=J(this,t,e,r);o.split(/\s+/g).forEach(function(t){n(this).hasClass(t)||i.push(t)},this),i.length&&W(this,r+(r?"
":"")+i.join("
"))}):this},removeClass:function(e){return
this.each(function(n){return
e===t?W(this,""):(i=W(this),J(this,e,n,i).split(/\s+/g).forEach(function(t){i=i.replace(q(t),"
")}),void W(this,i.trim()))})},toggleClass:function(e,i){return
e?this.each(function(r){var
o=n(this),s=J(this,e,r,W(this));s.split(/\s+/g).forEach(function(e){(i===t?!o.hasClass(e):i)?o.addClass(e):o.removeClass(e)})}):this},scrollTop:function(e){if(this.length){var
n="scrollTop"in this[0];return
e===t?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=e}:function(){this.scrollTo(this.scrollX,e)})}},scrollLeft:function(e){if(this.length){var
n="scrollLeft"in this[0];return
e===t?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=e}:function(){this.scrollTo(e,this.scrollY)})}},position:function(){if(this.length){var
t=this[0],e=this.offsetParent(),i=this.offset(),r=d.test(e[0].nodeName)?{top:0,left:0}:e.offset();return
i.top-=parseFloat(n(t).css("margin-top"))||0,i.left-=parseFloat(n(t).css("margin-left"))||0,r.top+=parseFloat(n(e[0]).css("border-top-width"))||0,r.left+=parseFloat(n(e[0]).css("border-left-width"))||0,{top:i.top-r.top,left:i.left-r.left}}},offsetParent:function(){return
this.map(function(){for(var
t=this.offsetParent||a.body;t&&!d.test(t.nodeName)&&"static"==n(t).css("position");)t=t.offsetParent;return
t})}},n.fn.detach=n.fn.remove,["width","height"].forEach(function(e){var
i=e.replace(/./,function(t){return
t[0].toUpperCase()});n.fn[e]=function(r){var o,s=this[0];return
r===t?$(s)?s["inner"+i]:_(s)?s.documentElement["scroll"+i]:(o=this.offset())&&o[e]:this.each(function(t){s=n(this),s.css(e,J(this,r,t,s[e]()))})}}),v.forEach(function(t,e){var
i=e%2;n.fn[t]=function(){var t,o,r=n.map(arguments,function(e){return
t=L(e),"object"==t||"array"==t||null==e?e:S.fragment(e)}),s=this.length>1;return
r.length<1?this:this.each(function(t,a){o=i?a:a.parentNode,a=0==e?a.nextSibling:1==e?a.firstChild:2==e?a:null,r.forEach(function(t){if(s)t=t.cloneNode(!0);else
if(!o)return
n(t).remove();G(o.insertBefore(t,a),function(t){null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src||window.eval.call(window,t.innerHTML)})})})},n.fn[i?t+"To":"insert"+(e?"Before":"After")]=function(e){return
n(e)[t](this),this}}),S.Z.prototype=n.fn,S.uniq=N,S.deserializeValue=Y,n.zepto=S,n}();window.Zepto=Zepto,void
0===window.$&&(window.$=Zepto),function(t){function l(t){return
t._zid||(t._zid=e++)}function h(t,e,n,i){if(e=p(e),e.ns)var
r=d(e.ns);return(s[l(t)]||[]).filter(function(t){return!(!t||e.e&&t.e!=e.e||e.ns&&!r.test(t.ns)||n&&l(t.fn)!==l(n)||i&&t.sel!=i)})}function
p(t){var
e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join("
")}}function d(t){return new RegExp("(?:^|
)"+t.replace(" "," .* ?")+"(?:
|$)")}function m(t,e){return t.del&&!u&&t.e in
f||!!e}function g(t){return c[t]||u&&f[t]||t}function
v(e,i,r,o,a,u,f){var
h=l(e),d=s[h]||(s[h]=[]);i.split(/\s/).forEach(function(i){if("ready"==i)return
t(document).ready(r);var s=p(i);s.fn=r,s.sel=a,s.e in
c&&(r=function(e){var
n=e.relatedTarget;return!n||n!==this&&!t.contains(this,n)?s.fn.apply(this,arguments):void
0}),s.del=u;var
l=u||r;s.proxy=function(t){if(t=j(t),!t.isImmediatePropagationStopped()){t.data=o;var
i=l.apply(e,t._args==n?[t]:[t].concat(t._args));return
i===!1&&(t.preventDefault(),t.stopPropagation()),i}},s.i=d.length,d.push(s),"addEventListener"in
e&&e.addEventListener(g(s.e),s.proxy,m(s,f))})}function
y(t,e,n,i,r){var
o=l(t);(e||"").split(/\s/).forEach(function(e){h(t,e,n,i).forEach(function(e){delete
s[o][e.i],"removeEventListener"in
t&&t.removeEventListener(g(e.e),e.proxy,m(e,r))})})}function
j(e,i){return(i||!e.isDefaultPrevented)&&(i||(i=e),t.each(E,function(t,n){var
r=i[t];e[t]=function(){return
this[n]=x,r&&r.apply(i,arguments)},e[n]=b}),(i.defaultPrevented!==n?i.defaultPrevented:"returnValue"in
i?i.returnValue===!1:i.getPreventDefault&&i.getPreventDefault())&&(e.isDefaultPrevented=x)),e}function
T(t){var e,i={originalEvent:t};for(e in
t)w.test(e)||t[e]===n||(i[e]=t[e]);return j(i,t)}var
n,e=1,i=Array.prototype.slice,r=t.isFunction,o=function(t){return"string"==typeof
t},s={},a={},u="onfocusin"in
window,f={focus:"focusin",blur:"focusout"},c={mouseenter:"mouseover",mouseleave:"mouseout"};a.click=a.mousedown=a.mouseup=a.mousemove="MouseEvents",t.event={add:v,remove:y},t.proxy=function(e,n){if(r(e)){var
i=function(){return e.apply(n,arguments)};return
i._zid=l(e),i}if(o(n))return t.proxy(e[n],e);throw new
TypeError("expected function")},t.fn.bind=function(t,e,n){return
this.on(t,e,n)},t.fn.unbind=function(t,e){return
this.off(t,e)},t.fn.one=function(t,e,n,i){return this.on(t,e,n,i,1)};var
x=function(){return!0},b=function(){return!1},w=/^([A-Z]|returnValue$|layer[XY]$)/,E={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return
this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return
this.off(e,t,n)},t.fn.live=function(e,n){return
t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return
t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,s,a,u,f){var
c,l,h=this;return
e&&!o(e)?(t.each(e,function(t,e){h.on(t,s,a,e,f)}),h):(o(s)||r(u)||u===!1||(u=a,a=s,s=n),(r(a)||a===!1)&&(u=a,a=n),u===!1&&(u=b),h.each(function(n,r){f&&(c=function(t){return
y(r,t.type,u),u.apply(this,arguments)}),s&&(l=function(e){var
n,o=t(e.target).closest(s,r).get(0);return
o&&o!==r?(n=t.extend(T(e),{currentTarget:o,liveFired:r}),(c||u).apply(o,[n].concat(i.call(arguments,1)))):void
0}),v(r,e,u,a,s,l||c)}))},t.fn.off=function(e,i,s){var a=this;return
e&&!o(e)?(t.each(e,function(t,e){a.off(t,i,e)}),a):(o(i)||r(s)||s===!1||(s=i,i=n),s===!1&&(s=b),a.each(function(){y(this,e,s,i)}))},t.fn.trigger=function(e,n){return
e=o(e)||t.isPlainObject(e)?t.Event(e):j(e),e._args=n,this.each(function(){"dispatchEvent"in
this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,n){var
i,r;return
this.each(function(s,a){i=T(o(e)?t.Event(e):e),i._args=n,i.target=a,t.each(h(a,e.type||e),function(t,e){return
r=e.proxy(i),i.isImmediatePropagationStopped()?!1:void
0})}),r},"focusin focusout load resize scroll unload click dblclick
mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change
select keydown keypress keyup error".split("
").forEach(function(e){t.fn[e]=function(t){return
t?this.bind(e,t):this.trigger(e)}}),["focus","blur"].forEach(function(e){t.fn[e]=function(t){return
t?this.bind(e,t):this.each(function(){try{this[e]()}catch(t){}}),this}}),t.Event=function(t,e){o(t)||(e=t,t=e.type);var
n=document.createEvent(a[t]||"Events"),i=!0;if(e)for(var r in
e)"bubbles"==r?i=!!e[r]:n[r]=e[r];return
n.initEvent(t,i,!0),j(n)}}(Zepto),function(t){function l(e,n,i){var
r=t.Event(n);return t(e).trigger(r,i),!r.isDefaultPrevented()}function
h(t,e,i,r){return t.global?l(e||n,i,r):void 0}function
p(e){e.global&&0===t.active++&&h(e,null,"ajaxStart")}function
d(e){e.global&&!--t.active&&h(e,null,"ajaxStop")}function
m(t,e){var n=e.context;return
e.beforeSend.call(n,t,e)===!1||h(e,n,"ajaxBeforeSend",[t,e])===!1?!1:void
h(e,n,"ajaxSend",[t,e])}function g(t,e,n,i){var
r=n.context,o="success";n.success.call(r,t,o,e),i&&i.resolveWith(r,[t,o,e]),h(n,r,"ajaxSuccess",[e,n,t]),y(o,e,n)}function
v(t,e,n,i,r){var
o=i.context;i.error.call(o,n,e,t),r&&r.rejectWith(o,[n,e,t]),h(i,o,"ajaxError",[n,i,t||e]),y(e,n,i)}function
y(t,e,n){var
i=n.context;n.complete.call(i,e,t),h(n,i,"ajaxComplete",[e,n]),d(n)}function
x(){}function b(t){return
t&&(t=t.split(";",2)[0]),t&&(t==f?"html":t==u?"json":s.test(t)?"script":a.test(t)&&"xml")||"text"}function
w(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function
E(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()||(e.url=w(e.url,e.data),e.data=void
0)}function j(e,n,i,r){return t.isFunction(n)&&(r=i,i=n,n=void
0),t.isFunction(i)||(r=i,i=void
0),{url:e,data:n,success:i,dataType:r}}function S(e,n,i,r){var
o,s=t.isArray(n),a=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),r&&(n=i?r:r+"["+(a||"object"==o||"array"==o?n:"")+"]"),!r&&s?e.add(u.name,u.value):"array"==o||!i&&"object"==o?S(e,u,i,n):e.add(n,u)})}var
i,r,e=0,n=window.document,o=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,s=/^(?:text|application)\/javascript/i,a=/^(?:text|application)\/xml/i,u="application/json",f="text/html",c=/^\s*$/;t.active=0,t.ajaxJSONP=function(i,r){if(!("type"in
i))return t.ajax(i);var
f,h,o=i.jsonpCallback,s=(t.isFunction(o)?o():o)||"jsonp"+
++e,a=n.createElement("script"),u=window[s],c=function(e){t(a).triggerHandler("error",e||"abort")},l={abort:c};return
r&&r.promise(l),t(a).on("load
error",function(e,n){clearTimeout(h),t(a).off().remove(),"error"!=e.type&&f?g(f[0],l,i,r):v(null,n||"error",l,i,r),window[s]=u,f&&t.isFunction(u)&&u(f[0]),u=f=void
0}),m(l,i)===!1?(c("abort"),l):(window[s]=function(){f=arguments},a.src=i.url.replace(/\?(.+)=\?/,"?$1="+s),n.head.appendChild(a),i.timeout>0&&(h=setTimeout(function(){c("timeout")},i.timeout)),l)},t.ajaxSettings={type:"GET",beforeSend:x,success:x,error:x,complete:x,context:null,global:!0,xhr:function(){return
new window.XMLHttpRequest},accepts:{script:"text/javascript,
application/javascript,
application/x-javascript",json:u,xml:"application/xml,
text/xml",html:f,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0},t.ajax=function(e){var
n=t.extend({},e||{}),o=t.Deferred&&t.Deferred();for(i in
t.ajaxSettings)void
0===n[i]&&(n[i]=t.ajaxSettings[i]);p(n),n.crossDomain||(n.crossDomain=/^([\w-]+:)?\/\/([^\/]+)/.test(n.url)&&RegExp.$2!=window.location.host),n.url||(n.url=window.location.toString()),E(n),n.cache===!1&&(n.url=w(n.url,"_="+Date.now()));var
s=n.dataType,a=/\?.+=\?/.test(n.url);if("jsonp"==s||a)return
a||(n.url=w(n.url,n.jsonp?n.jsonp+"=?":n.jsonp===!1?"":"callback=?")),t.ajaxJSONP(n,o);var
j,u=n.accepts[s],f={},l=function(t,e){f[t.toLowerCase()]=[t,e]},h=/^([\w-]+:)\/\//.test(n.url)?RegExp.$1:window.location.protocol,d=n.xhr(),y=d.setRequestHeader;if(o&&o.promise(d),n.crossDomain||l("X-Requested-With","XMLHttpRequest"),l("Accept",u||"*/*"),(u=n.mimeType||u)&&(u.indexOf(",")>-1&&(u=u.split(",",2)[0]),d.overrideMimeType&&d.overrideMimeType(u)),(n.contentType||n.contentType!==!1&&n.data&&"GET"!=n.type.toUpperCase())&&l("Content-Type",n.contentType||"application/x-www-form-urlencoded"),n.headers)for(r
in
n.headers)l(r,n.headers[r]);if(d.setRequestHeader=l,d.onreadystatechange=function(){if(4==d.readyState){d.onreadystatechange=x,clearTimeout(j);var
e,i=!1;if(d.status>=200&&d.status<300||304==d.status||0==d.status&&"file:"==h){s=s||b(n.mimeType||d.getResponseHeader("content-type")),e=d.responseText;try{"script"==s?(1,eval)(e):"xml"==s?e=d.responseXML:"json"==s&&(e=c.test(e)?null:t.parseJSON(e))}catch(r){i=r}i?v(i,"parsererror",d,n,o):g(e,d,n,o)}else
v(d.statusText||null,d.status?"error":"abort",d,n,o)}},m(d,n)===!1)return
d.abort(),v(null,"abort",d,n,o),d;if(n.xhrFields)for(r in
n.xhrFields)d[r]=n.xhrFields[r];var T="async"in
n?n.async:!0;d.open(n.type,n.url,T,n.username,n.password);for(r in
f)y.apply(d,f[r]);return
n.timeout>0&&(j=setTimeout(function(){d.onreadystatechange=x,d.abort(),v(null,"timeout",d,n,o)},n.timeout)),d.send(n.data?n.data:null),d},t.get=function(){return
t.ajax(j.apply(null,arguments))},t.post=function(){var
e=j.apply(null,arguments);return
e.type="POST",t.ajax(e)},t.getJSON=function(){var
e=j.apply(null,arguments);return
e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,i){if(!this.length)return
this;var a,r=this,s=e.split(/\s/),u=j(e,n,i),f=u.success;return
s.length>1&&(u.url=s[0],a=s[1]),u.success=function(e){r.html(a?t("<div>").html(e.replace(o,"")).find(a):e),f&&f.apply(r,arguments)},t.ajax(u),this};var
T=encodeURIComponent;t.param=function(t,e){var n=[];return
n.add=function(t,e){this.push(T(t)+"="+T(e))},S(n,t,e),n.join("&").replace(/%20/g,"+")}}(Zepto),function(t){t.fn.serializeArray=function(){var
n,e=[];return
t([].slice.call(this.get(0).elements)).each(function(){n=t(this);var
i=n.attr("type");"fieldset"!=this.nodeName.toLowerCase()&&!this.disabled&&"submit"!=i&&"reset"!=i&&"button"!=i&&("radio"!=i&&"checkbox"!=i||this.checked)&&e.push({name:n.attr("name"),value:n.val()})}),e},t.fn.serialize=function(){var
t=[];return
this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(e)this.bind("submit",e);else
if(this.length){var
n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return
this}}(Zepto),function(t){"__proto__"in{}||t.extend(t.zepto,{Z:function(e,n){return
e=e||[],t.extend(e,t.fn),e.selector=n||"",e.__Z=!0,e},isZ:function(e){return"array"===t.type(e)&&"__Z"in
e}});try{getComputedStyle(void 0)}catch(e){var
n=getComputedStyle;window.getComputedStyle=function(t){try{return
n(t)}catch(e){return null}}}}(Zepto);
vendor/filp/whoops/src/Whoops/Resources/views/env_details.html.php000064400000003031151166614540021446
0ustar00<?php /* List data-table values, i.e: $_SERVER, $_GET, .... */
?>
<div class="details">
  <h2 class="details-heading">Environment &amp;
details:</h2>

  <div class="data-table-container"
id="data-tables">
    <?php foreach ($tables as $label => $data): ?>
      <div class="data-table" id="sg-<?php echo
$tpl->escape($tpl->slug($label)) ?>">
        <?php if (!empty($data)): ?>
            <label><?php echo $tpl->escape($label)
?></label>
            <table class="data-table">
              <thead>
                <tr>
                  <td class="data-table-k">Key</td>
                  <td class="data-table-v">Value</td>
                </tr>
              </thead>
            <?php foreach ($data as $k => $value): ?>
              <tr>
                <td><?php echo $tpl->escape($k)
?></td>
                <td><?php echo $tpl->dump($value)
?></td>
              </tr>
            <?php endforeach ?>
            </table>
        <?php else: ?>
            <label class="empty"><?php echo
$tpl->escape($label) ?></label>
            <span class="empty">empty</span>
        <?php endif ?>
      </div>
    <?php endforeach ?>
  </div>

  <?php /* List registered handlers, in order of first to last
registered */ ?>
  <div class="data-table-container"
id="handlers">
    <label>Registered Handlers</label>
    <?php foreach ($handlers as $i => $h): ?>
      <div class="handler <?php echo ($h === $handler) ?
'active' : ''?>">
        <?php echo $i ?>. <?php echo
$tpl->escape(get_class($h)) ?>
      </div>
    <?php endforeach ?>
  </div>

</div>
vendor/filp/whoops/src/Whoops/Resources/views/frames_container.html.php000064400000000242151166614540022471
0ustar00<div class="frames-container <?php echo
$active_frames_tab == 'application' ?
'frames-container-application' : '' ?>">
  <?php $tpl->render($frame_list) ?>
</div>vendor/filp/whoops/src/Whoops/Resources/views/frames_description.html.php000064400000001224151166614540023033
0ustar00<div class="frames-description <?php echo
$has_frames_tabs ? 'frames-description-application' :
'' ?>">
  <?php if ($has_frames_tabs): ?>
    <a href="#" id="application-frames-tab"
class="frames-tab <?php echo $active_frames_tab ==
'application' ? 'frames-tab-active' : ''
?>">
        Application frames (<?php echo $frames->countIsApplication()
?>)
    </a>
    <a href="#" id="all-frames-tab"
class="frames-tab <?php echo $active_frames_tab == 'all'
? 'frames-tab-active' : '' ?>">
      All frames (<?php echo count($frames) ?>)
    </a>
  <?php else: ?>
    <span>
        Stack frames (<?php echo count($frames) ?>)
    </span>
  <?php endif; ?>
</div>
vendor/filp/whoops/src/Whoops/Resources/views/frame_code.html.php000064400000005336151166614540021247
0ustar00<?php /* Display a code block for all frames in the stack.
       * @todo: This should PROBABLY be done on-demand, lest
       * we get 200 frames to process. */ ?>
<div class="frame-code-container <?php echo (!$has_frames ?
'empty' : '') ?>">
  <?php foreach ($frames as $i => $frame): ?>
    <?php $line = $frame->getLine(); ?>
      <div class="frame-code <?php echo ($i == 0 ) ?
'active' : '' ?>" id="frame-code-<?php
echo $i ?>">
        <div class="frame-file">
          <?php $filePath = $frame->getFile(); ?>
          <?php if ($filePath && $editorHref =
$handler->getEditorHref($filePath, (int) $line)): ?>
            <a href="<?php echo $editorHref ?>"
class="editor-link"<?php echo
($handler->getEditorAjax($filePath, (int) $line) ? '
data-ajax' : '') ?>>
              Open:
              <strong><?php echo
$tpl->breakOnDelimiter('/', $tpl->escape($filePath ?:
'<#unknown>')) ?></strong>
            </a>
          <?php else: ?>
            <strong><?php echo
$tpl->breakOnDelimiter('/', $tpl->escape($filePath ?:
'<#unknown>')) ?></strong>
          <?php endif ?>
        </div>
        <?php
          // Do nothing if there's no line to work off
          if ($line !== null):

          // the $line is 1-indexed, we nab -1 where needed to account for
this
          $range = $frame->getFileLines($line - 20, 40);

          // getFileLines can return null if there is no source code
          if ($range):
            $range = array_map(function ($line) { return empty($line) ?
' ' : $line;}, $range);
            $start = key($range) + 1;
            $code  = join("\n", $range);
        ?>
            <pre id="frame-code-linenums-<?=$i?>"
class="code-block linenums:<?php echo $start
?>"><?php echo $tpl->escape($code) ?></pre>

          <?php endif ?>
        <?php endif ?>

        <?php $frameArgs = $tpl->dumpArgs($frame); ?>
        <?php if ($frameArgs): ?>
          <div class="frame-file">
              Arguments
          </div>
          <div id="frame-code-args-<?=$i?>"
class="code-block frame-args">
              <?php echo $frameArgs; ?>
          </div>
        <?php endif ?>

        <?php
          // Append comments for this frame
          $comments = $frame->getComments();
        ?>
        <div class="frame-comments <?php echo empty($comments) ?
'empty' : '' ?>">
          <?php foreach ($comments as $commentNo => $comment): ?>
            <?php extract($comment) ?>
            <div class="frame-comment"
id="comment-<?php echo $i . '-' . $commentNo
?>">
              <span class="frame-comment-context"><?php
echo $tpl->escape($context) ?></span>
              <?php echo $tpl->escapeButPreserveUris($comment) ?>
            </div>
          <?php endforeach ?>
        </div>

      </div>
  <?php endforeach ?>
</div>
vendor/filp/whoops/src/Whoops/Resources/views/frame_list.html.php000064400000002017151166614540021301
0ustar00<?php /* List file names & line numbers for all stack
frames;
         clicking these links/buttons will display the code view
         for that particular frame */ ?>
<?php foreach ($frames as $i => $frame): ?>
  <div class="frame <?php echo ($i == 0 ? 'active' :
'') ?> <?php echo ($frame->isApplication() ?
'frame-application' : '') ?>"
id="frame-line-<?php echo $i ?>">
      <span class="frame-index"><?php echo
(count($frames) - $i - 1) ?></span>
      <div class="frame-method-info">
        <span class="frame-class"><?php echo
$tpl->breakOnDelimiter('\\',
$tpl->escape($frame->getClass() ?: '')) ?></span>
        <span class="frame-function"><?php echo
$tpl->breakOnDelimiter('\\',
$tpl->escape($frame->getFunction() ?: ''))
?></span>
      </div>

    <div class="frame-file">
        <?php echo $frame->getFile() ?
$tpl->breakOnDelimiter('/',
$tpl->shorten($tpl->escape($frame->getFile()))) :
'<#unknown>' ?><!--
   --><span class="frame-line"><?php echo (int)
$frame->getLine() ?></span>
    </div>
  </div>
<?php endforeach;
vendor/filp/whoops/src/Whoops/Resources/views/header.html.php000064400000022311151166614540020403
0ustar00<div class="exception">
  <div class="exc-title">
    <?php foreach ($name as $i => $nameSection): ?>
      <?php if ($i == count($name) - 1): ?>
        <span class="exc-title-primary"><?php echo
$tpl->escape($nameSection) ?></span>
      <?php else: ?>
        <?php echo $tpl->escape($nameSection) . ' \\' ?>
      <?php endif ?>
    <?php endforeach ?>
    <?php if ($code): ?>
      <span title="Exception Code">(<?php echo
$tpl->escape($code) ?>)</span>
    <?php endif ?>
  </div>

  <div class="exc-message">
    <?php if (!empty($message)): ?>
      <span><?php echo $tpl->escape($message)
?></span>


      <?php if (count($previousMessages)): ?>
        <div class="exc-title prev-exc-title">
          <span class="exc-title-secondary">Previous
exceptions</span>
        </div>

        <ul>
          <?php foreach ($previousMessages as $i =>
$previousMessage): ?>
            <li>
              <?php echo $tpl->escape($previousMessage) ?>
              <span class="prev-exc-code">(<?php echo
$previousCodes[$i] ?>)</span>
            </li>
          <?php endforeach; ?>
        </ul>
      <?php endif ?>



    <?php else: ?>
      <span class="exc-message-empty-notice">No
message</span>
    <?php endif ?>

    <ul class="search-for-help">
      <?php if (!empty($docref_url)): ?>
      <li>
        <a rel="noopener noreferrer" target="_blank"
href="<?php echo $docref_url; ?>" title="Search for
help in the PHP manual.">
          <!-- PHP icon by Icons Solid -->
          <!-- https://www.iconfinder.com/icons/322421/book_icon -->
          <!-- Free for commercial use -->
          <svg height="16px" id="Layer_1"
style="enable-background:new 0 0 32 32;" version="1.1"
viewBox="0 0 32 32" width="16px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"><g
transform="translate(240 0)"><path
d="M-211,4v26h-24c-1.104,0-2-0.895-2-2s0.896-2,2-2h22V0h-22c-2.209,0-4,1.791-4,4v24c0,2.209,1.791,4,4,4h26V4H-211z
   M-235,8V2h20v22h-20V8z M-219,6h-12V4h12V6z M-223,10h-8V8h8V10z
M-227,14h-4v-2h4V14z"/></g></svg>
        </a>
      </li>
      <?php endif ?>
      <li>
        <a rel="noopener noreferrer" target="_blank"
href="https://google.com/search?q=<?php echo
urlencode(implode('\\', $name).' '.$message)
?>" title="Search for help on Google.">
          <!-- Google icon by Alfredo H, from
https://www.iconfinder.com/alfredoh -->
          <!-- Creative Commons (Attribution 3.0 Unported) -->
          <!-- http://creativecommons.org/licenses/by/3.0/ -->
          <svg class="google" height="16"
viewBox="0 0 512 512" width="16"
xmlns="http://www.w3.org/2000/svg">
            <path d="M457.732 216.625c2.628 14.04 4.063 28.743
4.063 44.098C461.795 380.688 381.48 466 260.205 466c-116.024
0-210-93.977-210-210s93.976-210 210-210c56.703 0 104.076 20.867 140.44
54.73l-59.205 59.197v-.135c-22.046-21.002-50-31.762-81.236-31.762-69.297
0-125.604 58.537-125.604 127.84 0 69.29 56.306 127.97 125.604 127.97 62.87
0 105.653-35.966 114.46-85.313h-114.46v-81.902h197.528z"/>
          </svg>
        </a>
      </li>
      <li>
        <a rel="noopener noreferrer" target="_blank"
href="https://duckduckgo.com/?q=<?php echo
urlencode(implode('\\', $name).' '.$message)
?>" title="Search for help on DuckDuckGo.">
          <!-- DuckDuckGo icon by IconBaandar Team, from
https://www.iconfinder.com/iconbaandar -->
          <!-- Creative Commons (Attribution 3.0 Unported) -->
          <!-- http://creativecommons.org/licenses/by/3.0/ -->
          <svg class="duckduckgo" height="16"
viewBox="150 150 1675 1675" width="16"
xmlns="http://www.w3.org/2000/svg">
            <path d="M1792 1024c0 204.364-80.472 398.56-224.955
543.04-144.483 144.48-338.68 224.95-543.044 224.95-204.36
0-398.56-80.47-543.04-224.95-144.48-144.482-224.95-338.676-224.95-543.04
0-204.365 80.47-398.562 224.96-543.045C625.44 336.47 819.64 256 1024
256c204.367 0 398.565 80.47 543.05 224.954C1711.532 625.437 1792 819.634
1792 1024zm-270.206 497.787C1654.256 1389.327 1728 1211.36 1728
1024c0-187.363-73.74-365.332-206.203-497.796C1389.332 393.74 1211.363 320
1024 320s-365.33 73.742-497.795 206.205C393.742 658.67 320 836.637 320
1024c0 187.36 73.744 365.326 206.206 497.787C658.67 1654.25 836.638 1727.99
1024 1727.99c187.362 0 365.33-73.74 497.794-206.203z"/>
            <path d="M1438.64
1177.41c0-.03-.005-.017-.01.004l.01-.004z"/>
            <path d="M1499.8
976.878c.03-.156-.024-.048-.11.107l.11-.107z"/>
            <path d="M1105.19
991.642zm-68.013-376.128c-8.087-10.14-18.028-19.965-29.89-29.408-13.29-10.582-29-20.76-47.223-30.443-35.07-18.624-74.482-31.61-115.265-38.046-39.78-6.28-80.84-6.256-120.39.917l1.37
31.562c1.8.164 7.7 3.9 14.36 8.32-20.68 5.94-39.77 14.447-39.48 39.683l.2
17.48 17.3-1.73c29.38-2.95 60.17-2.06 90.32 2.61 9.21 1.42 18.36 3.2 27.38
5.32l-4.33 1.15c-20.45 5.58-38.93 12.52-54.25 20.61-46.28 24.32-75.51
60.85-90.14 108.37-14.14 45.95-14.27 101.81-2.72 166.51l.06.06c15.14 84.57
64.16 316.39 104.11 505.39 19.78 93.59 37.38 176.83 47.14 224.4 3.26 15.84
5.03 31.02 5.52 45.52.3 9.08.09 17.96-.58 26.62-.45 5.8-1.11 11.51-1.96
17.112l31.62 4.75c.71-4.705 1.3-9.494 1.76-14.373 48.964 10.517 99.78 16.05
151.88 16.05 60.68 0 119.61-7.505 175.91-21.64 3.04 6.08 6.08 12.19 9.11
18.32l28.62-14.128c-2.11-4.27-4.235-8.55-6.37-12.84-23.005-46.124-47.498-93.01-68.67-133.534-15.39-29.466-29.01-55.53-39.046-75.58-26.826-53.618-53.637-119.47-68.28-182.368-8.78-37.705-13.128-74.098-10.308-105.627-15.31-6.28-26.69-11.8-31.968-15.59l-.01.015c-14.22-10.2-31.11-28.12-41.82-49.717-8.618-17.376-13.4-37.246-10.147-57.84
3.17-19.84 27.334-46.714 57.843-67.46v-.063c26.554-18.05 58.75-32.506
86.32-34.31 7.835-.51 16.31-1.008 23.99-1.45 33.45-1.95 50.243-2.93
84.475-11.42 10.88-2.697 26.19-6.56 43.53-11.09
2.364-40.7-5.947-87.596-21.04-133.234-22.004-66.53-58.68-131.25-97.627-170.21-12.543-12.55-28.17-22.79-45.9-30.933-16.88-7.753-35.64-13.615-55.436-17.782zm-10.658
178.553s6.77-42.485 58.39-33.977c27.96 4.654 37.89 29.833 37.89
29.833s-25.31-14.46-44.95-14.198c-40.33.53-51.35 18.342-51.35
18.342zm-240.45-18.802c48.49-19.853 72.11 11.298 72.11
11.298s-35.21-15.928-69.46 5.59c-34.19 21.477-32.92 43.452-32.92
43.452s-18.17-40.5 30.26-60.34zm296.5 95.4c0-6.677 2.68-12.694 7.01-17.02
4.37-4.37 10.42-7.074 17.1-7.074 6.73 0 12.79 2.7 17.15 7.05 4.33 4.33 7.01
10.36 7.01 17.05 0 6.74-2.7 12.81-7.07 17.18-4.33 4.33-10.37 7.01-17.1
7.01-6.68 0-12.72-2.69-17.05-7.03-4.36-4.37-7.07-10.43-7.07-17.16zm-268.42
51.27c0-8.535 3.41-16.22 8.93-21.738 5.55-5.55 13.25-8.982 21.81-8.982 8.51
0 16.18 3.415 21.7 8.934 5.55 5.55 8.98 13.25 8.98 21.78 0 8.53-3.44
16.23-8.98 21.79-5.52 5.52-13.19 8.93-21.71 8.93-8.55
0-16.26-3.43-21.82-8.99-5.52-5.52-8.93-13.2-8.93-21.74z"/>
            <path d="M1102.48
986.34zm390.074-64.347c-28.917-11.34-74.89-12.68-93.32-3.778-11.5
5.567-35.743 13.483-63.565 21.707-25.75 7.606-53.9 15.296-78.15
21.702-17.69 4.67-33.3 8.66-44.4 11.435-34.92 8.76-52.05 9.77-86.17
11.78-7.84.46-16.48.97-24.48 1.5-28.12 1.86-60.97 16.77-88.05
35.4v.06c-31.12 21.4-55.77 49.12-59.01 69.59-3.32 21.24 1.56 41.74 10.35
59.67 10.92 22.28 28.15 40.77 42.66 51.29l.01-.02c5.38 3.9 16.98 9.6 32.6
16.08 26.03 10.79 63.2 23.76 101.25 34.23 43.6 11.99 89.11 21.05 121.69
20.41 34.26-.69 77.73-10.52 114.54-24.67 22.15-8.52 42.21-18.71 56.88-29.58
17.85-13.22 28.7-28.42
28.4-44.74-.07-3.89-.72-7.63-1.97-11.21l-.02.01c-11.6-33.06-50.37-23.59-105.53-10.12-46.86
11.445-107.94 26.365-169.01
20.434-32.56-3.167-54.45-10.61-67.88-20.133-5.96-4.224-9.93-8.67-12.18-13.11-1.96-3.865-2.68-7.84-2.33-11.714.39-4.42
2.17-9.048 5.1-13.57l-.05-.03c7.86-12.118 23.082-9.72 43.93-6.43 25.91 4.08
58.2 9.172 99.013-3.61 39.63-12.378 87.76-29.9 131.184-47.39 42.405-17.08
80.08-34.078 100.74-46.18 25.46-14.87 37.57-29.428 40.59-42.866
2.725-12.152-.89-22.48-8.903-31.07-5.87-6.29-14.254-11.31-23.956-15.115z"/>
          </svg>
        </a>
      </li>
      <li>
        <a rel="noopener noreferrer" target="_blank"
href="https://stackoverflow.com/search?q=<?php echo
urlencode(implode('\\', $name).' '.$message)
?>" title="Search for help on Stack Overflow.">
          <!-- Stack Overflow icon by Picons.me, from
https://www.iconfinder.com/Picons -->
          <!-- Free for commercial use -->
          <svg class="stackoverflow" height="16"
viewBox="-1163 1657.697 56.693 56.693" width="16"
xmlns="http://www.w3.org/2000/svg">
            <path d="M-1126.04 1689.533l-16.577-9.778 2.088-3.54
16.578 9.778zM-1127.386 1694.635l-18.586-4.996 1.068-3.97 18.586
4.995zM-1127.824 1700.137l-19.165-1.767.378-4.093 19.165 1.767zM-1147.263
1701.293h19.247v4.11h-19.247z"/>
            <path d="M-1121.458 1710.947s0
.96-.032.96v.016h-30.796s-.96
0-.96-.016h-.032v-20.03h3.288v16.805h25.244v-16.804h3.288v19.07zM-1130.667
1667.04l10.844 15.903-3.396 2.316-10.843-15.903zM-1118.313 1663.044l3.29
18.963-4.05.703-3.29-18.963z"/>
          </svg>
        </a>
      </li>
    </ul>

    <span id="plain-exception"><?php echo
$tpl->escape($plain_exception) ?></span>
    <button id="copy-button" class="rightButton
clipboard" data-clipboard-text="<?php echo
$tpl->escape($plain_exception) ?>" title="Copy exception
details to clipboard">
      COPY
    </button>
    <button id="hide-error" class="rightButton"
title="Hide error message"
onclick="document.getElementsByClassName('Whoops')[0].style.display
= 'none';">
      HIDE
    </button>
  </div>
</div>
vendor/filp/whoops/src/Whoops/Resources/views/header_outer.html.php000064400000000064151166614540021622
0ustar00<header>
  <?php $tpl->render($header) ?>
</header>
vendor/filp/whoops/src/Whoops/Resources/views/layout.html.php000064400000001467151166614540020501
0ustar00<?php
/**
* Layout template file for Whoops's pretty error output.
*/
?>
<!DOCTYPE html><?php echo $preface; ?>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="robots"
content="noindex,nofollow"/>
    <meta name="viewport" content="width=device-width,
initial-scale=1, shrink-to-fit=no"/>
    <title><?php echo $tpl->escape($page_title)
?></title>

    <style><?php echo $stylesheet ?></style>
  </head>
  <body>

    <div class="Whoops container">
      <div class="stack-container">

        <?php $tpl->render($panel_left_outer) ?>

        <?php $tpl->render($panel_details_outer) ?>

      </div>
    </div>

    <script><?php echo $prettify ?></script>
    <script><?php echo $zepto ?></script>
    <script><?php echo $clipboard ?></script>
    <script><?php echo $javascript ?></script>
  </body>
</html>
vendor/filp/whoops/src/Whoops/Resources/views/panel_details.html.php000064400000000106151166614540021755
0ustar00<?php $tpl->render($frame_code) ?>
<?php $tpl->render($env_details)
?>vendor/filp/whoops/src/Whoops/Resources/views/panel_details_outer.html.php000064400000000127151166614540023176
0ustar00<div class="panel details-container cf">
  <?php $tpl->render($panel_details) ?>
</div>vendor/filp/whoops/src/Whoops/Resources/views/panel_left.html.php000064400000000150151166614540021261
0ustar00<?php 
$tpl->render($header_outer);
$tpl->render($frames_description);
$tpl->render($frames_container);
vendor/filp/whoops/src/Whoops/Resources/views/panel_left_outer.html.php000064400000000171151166614540022502
0ustar00<div class="panel left-panel cf <?php echo (!$has_frames
? 'empty' : '') ?>">
  <?php $tpl->render($panel_left) ?>
</div>vendor/filp/whoops/src/Whoops/Run.php000064400000033000151166614540013642
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops;

use InvalidArgumentException;
use Whoops\Exception\ErrorException;
use Whoops\Exception\Inspector;
use Whoops\Handler\CallbackHandler;
use Whoops\Handler\Handler;
use Whoops\Handler\HandlerInterface;
use Whoops\Util\Misc;
use Whoops\Util\SystemFacade;

final class Run implements RunInterface
{
    private $isRegistered;
    private $allowQuit       = true;
    private $sendOutput      = true;

    /**
     * @var integer|false
     */
    private $sendHttpCode    = 500;

    /**
     * @var HandlerInterface[]
     */
    private $handlerQueue = [];

    private $silencedPatterns = [];

    private $system;

    public function __construct(SystemFacade $system = null)
    {
        $this->system = $system ?: new SystemFacade;
    }

    /**
     * Prepends a handler to the start of the queue
     *
     * @throws InvalidArgumentException  If argument is not callable or
instance of HandlerInterface
     * @param  Callable|HandlerInterface $handler
     * @return Run
     * @deprecated use appendHandler and prependHandler instead
     */
    public function pushHandler($handler)
    {
        return $this->prependHandler($handler);
    }

    /**
     * Appends a handler to the end of the queue
     *
     * @throws InvalidArgumentException  If argument is not callable or
instance of HandlerInterface
     * @param  Callable|HandlerInterface $handler
     * @return Run
     */
    public function appendHandler($handler)
    {
        array_push($this->handlerQueue,
$this->resolveHandler($handler));
        return $this;
    }

    /**
     * Prepends a handler to the start of the queue
     *
     * @throws InvalidArgumentException  If argument is not callable or
instance of HandlerInterface
     * @param  Callable|HandlerInterface $handler
     * @return Run
     */
    public function prependHandler($handler)
    {
        array_unshift($this->handlerQueue,
$this->resolveHandler($handler));
        return $this;
    }

    /**
     * Create a CallbackHandler from callable and throw if handler is
invalid
     *
     * @throws InvalidArgumentException  If argument is not callable or
instance of HandlerInterface
     * @param Callable|HandlerInterface $handler
     * @return HandlerInterface
     */
    private function resolveHandler($handler)
    {
        if (is_callable($handler)) {
            $handler = new CallbackHandler($handler);
        }

        if (!$handler instanceof HandlerInterface) {
            throw new InvalidArgumentException(
                "Argument to " . __METHOD__ . " must be a
callable, or instance of "
                . "Whoops\\Handler\\HandlerInterface"
            );
        }

        return $handler;
    }

    /**
     * Removes the last handler in the queue and returns it.
     * Returns null if there"s nothing else to pop.
     * @return null|HandlerInterface
     */
    public function popHandler()
    {
        return array_pop($this->handlerQueue);
    }

    /**
     * Removes the first handler in the queue and returns it.
     * Returns null if there"s nothing else to shift.
     * @return null|HandlerInterface
     */
    public function shiftHandler()
    {
        return array_shift($this->handlerQueue);
    }

    /**
     * Returns an array with all handlers, in the
     * order they were added to the queue.
     * @return array
     */
    public function getHandlers()
    {
        return $this->handlerQueue;
    }

    /**
     * Clears all handlers in the handlerQueue, including
     * the default PrettyPage handler.
     * @return Run
     */
    public function clearHandlers()
    {
        $this->handlerQueue = [];
        return $this;
    }

    /**
     * @param  \Throwable $exception
     * @return Inspector
     */
    private function getInspector($exception)
    {
        return new Inspector($exception);
    }

    /**
     * Registers this instance as an error handler.
     * @return Run
     */
    public function register()
    {
        if (!$this->isRegistered) {
            // Workaround PHP bug 42098
            // https://bugs.php.net/bug.php?id=42098
            class_exists("\\Whoops\\Exception\\ErrorException");
            class_exists("\\Whoops\\Exception\\FrameCollection");
            class_exists("\\Whoops\\Exception\\Frame");
            class_exists("\\Whoops\\Exception\\Inspector");

            $this->system->setErrorHandler([$this,
self::ERROR_HANDLER]);
            $this->system->setExceptionHandler([$this,
self::EXCEPTION_HANDLER]);
            $this->system->registerShutdownFunction([$this,
self::SHUTDOWN_HANDLER]);

            $this->isRegistered = true;
        }

        return $this;
    }

    /**
     * Unregisters all handlers registered by this Whoops\Run instance
     * @return Run
     */
    public function unregister()
    {
        if ($this->isRegistered) {
            $this->system->restoreExceptionHandler();
            $this->system->restoreErrorHandler();

            $this->isRegistered = false;
        }

        return $this;
    }

    /**
     * Should Whoops allow Handlers to force the script to quit?
     * @param  bool|int $exit
     * @return bool
     */
    public function allowQuit($exit = null)
    {
        if (func_num_args() == 0) {
            return $this->allowQuit;
        }

        return $this->allowQuit = (bool) $exit;
    }

    /**
     * Silence particular errors in particular files
     * @param  array|string $patterns List or a single regex pattern to
match
     * @param  int          $levels   Defaults to E_STRICT | E_DEPRECATED
     * @return \Whoops\Run
     */
    public function silenceErrorsInPaths($patterns, $levels = 10240)
    {
        $this->silencedPatterns = array_merge(
            $this->silencedPatterns,
            array_map(
                function ($pattern) use ($levels) {
                    return [
                        "pattern" => $pattern,
                        "levels" => $levels,
                    ];
                },
                (array) $patterns
            )
        );
        return $this;
    }


    /**
     * Returns an array with silent errors in path configuration
     *
     * @return array
     */
    public function getSilenceErrorsInPaths()
    {
        return $this->silencedPatterns;
    }

    /*
     * Should Whoops send HTTP error code to the browser if possible?
     * Whoops will by default send HTTP code 500, but you may wish to
     * use 502, 503, or another 5xx family code.
     *
     * @param bool|int $code
     * @return int|false
     */
    public function sendHttpCode($code = null)
    {
        if (func_num_args() == 0) {
            return $this->sendHttpCode;
        }

        if (!$code) {
            return $this->sendHttpCode = false;
        }

        if ($code === true) {
            $code = 500;
        }

        if ($code < 400 || 600 <= $code) {
            throw new InvalidArgumentException(
                 "Invalid status code '$code', must be 4xx
or 5xx"
            );
        }

        return $this->sendHttpCode = $code;
    }

    /**
     * Should Whoops push output directly to the client?
     * If this is false, output will be returned by handleException
     * @param  bool|int $send
     * @return bool
     */
    public function writeToOutput($send = null)
    {
        if (func_num_args() == 0) {
            return $this->sendOutput;
        }

        return $this->sendOutput = (bool) $send;
    }

    /**
     * Handles an exception, ultimately generating a Whoops error
     * page.
     *
     * @param  \Throwable $exception
     * @return string     Output generated by handlers
     */
    public function handleException($exception)
    {
        // Walk the registered handlers in the reverse order
        // they were registered, and pass off the exception
        $inspector = $this->getInspector($exception);

        // Capture output produced while handling the exception,
        // we might want to send it straight away to the client,
        // or return it silently.
        $this->system->startOutputBuffering();
        
        // Just in case there are no handlers:
        $handlerResponse = null;
        $handlerContentType = null;

        try {
            foreach ($this->handlerQueue as $handler) {
                $handler->setRun($this);
                $handler->setInspector($inspector);
                $handler->setException($exception);

                // The HandlerInterface does not require an Exception
passed to handle()
                // and neither of our bundled handlers use it.
                // However, 3rd party handlers may have already relied on
this parameter,
                // and removing it would be possibly breaking for users.
                $handlerResponse = $handler->handle($exception);

                // Collect the content type for possible sending in the
headers.
                $handlerContentType = method_exists($handler,
'contentType') ? $handler->contentType() : null;

                if (in_array($handlerResponse, [Handler::LAST_HANDLER,
Handler::QUIT])) {
                    // The Handler has handled the exception in some way,
and
                    // wishes to quit execution (Handler::QUIT), or skip
any
                    // other handlers (Handler::LAST_HANDLER). If
$this->allowQuit
                    // is false, Handler::QUIT behaves like
Handler::LAST_HANDLER
                    break;
                }
            }

            $willQuit = $handlerResponse == Handler::QUIT &&
$this->allowQuit();
        } finally {
            $output = $this->system->cleanOutputBuffer();
        }

        // If we're allowed to, send output generated by handlers
directly
        // to the output, otherwise, and if the script doesn't quit,
return
        // it so that it may be used by the caller
        if ($this->writeToOutput()) {
            // @todo Might be able to clean this up a bit better
            if ($willQuit) {
                // Cleanup all other output buffers before sending our
output:
                while ($this->system->getOutputBufferLevel() > 0)
{
                    $this->system->endOutputBuffering();
                }

                // Send any headers if needed:
                if (Misc::canSendHeaders() && $handlerContentType)
{
                    header("Content-Type:
{$handlerContentType}");
                }
            }

            $this->writeToOutputNow($output);
        }

        if ($willQuit) {
            // HHVM fix for https://github.com/facebook/hhvm/issues/4055
            $this->system->flushOutputBuffer();

            $this->system->stopExecution(1);
        }

        return $output;
    }

    /**
     * Converts generic PHP errors to \ErrorException
     * instances, before passing them off to be handled.
     *
     * This method MUST be compatible with set_error_handler.
     *
     * @param int    $level
     * @param string $message
     * @param string $file
     * @param int    $line
     *
     * @return bool
     * @throws ErrorException
     */
    public function handleError($level, $message, $file = null, $line =
null)
    {
        if ($level & $this->system->getErrorReportingLevel()) {
            foreach ($this->silencedPatterns as $entry) {
                $pathMatches = (bool)
preg_match($entry["pattern"], $file);
                $levelMatches = $level & $entry["levels"];
                if ($pathMatches && $levelMatches) {
                    // Ignore the error, abort handling
                    // See https://github.com/filp/whoops/issues/418
                    return true;
                }
            }

            // XXX we pass $level for the "code" param only for
BC reasons.
            // see https://github.com/filp/whoops/issues/267
            $exception = new ErrorException($message, /*code*/ $level,
/*severity*/ $level, $file, $line);
            if ($this->canThrowExceptions) {
                throw $exception;
            } else {
                $this->handleException($exception);
            }
            // Do not propagate errors which were already handled by
Whoops.
            return true;
        }

        // Propagate error to the next handler, allows error_get_last() to
        // work on silenced errors.
        return false;
    }

    /**
     * Special case to deal with Fatal errors and the like.
     */
    public function handleShutdown()
    {
        // If we reached this step, we are in shutdown handler.
        // An exception thrown in a shutdown handler will not be propagated
        // to the exception handler. Pass that information along.
        $this->canThrowExceptions = false;

        $error = $this->system->getLastError();
        if ($error && Misc::isLevelFatal($error['type']))
{
            // If there was a fatal error,
            // it was not handled in handleError yet.
            $this->allowQuit = false;
            $this->handleError(
                $error['type'],
                $error['message'],
                $error['file'],
                $error['line']
            );
        }
    }

    /**
     * In certain scenarios, like in shutdown handler, we can not throw
exceptions
     * @var bool
     */
    private $canThrowExceptions = true;

    /**
     * Echo something to the browser
     * @param  string $output
     * @return $this
     */
    private function writeToOutputNow($output)
    {
        if ($this->sendHttpCode() &&
\Whoops\Util\Misc::canSendHeaders()) {
            $this->system->setHttpResponseCode(
                $this->sendHttpCode()
            );
        }

        echo $output;

        return $this;
    }
}
vendor/filp/whoops/src/Whoops/RunInterface.php000064400000006507151166614540015477
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops;

use InvalidArgumentException;
use Whoops\Exception\ErrorException;
use Whoops\Handler\HandlerInterface;

interface RunInterface
{
    const EXCEPTION_HANDLER = "handleException";
    const ERROR_HANDLER     = "handleError";
    const SHUTDOWN_HANDLER  = "handleShutdown";

    /**
     * Pushes a handler to the end of the stack
     *
     * @throws InvalidArgumentException  If argument is not callable or
instance of HandlerInterface
     * @param  Callable|HandlerInterface $handler
     * @return Run
     */
    public function pushHandler($handler);

    /**
     * Removes the last handler in the stack and returns it.
     * Returns null if there"s nothing else to pop.
     *
     * @return null|HandlerInterface
     */
    public function popHandler();

    /**
     * Returns an array with all handlers, in the
     * order they were added to the stack.
     *
     * @return array
     */
    public function getHandlers();

    /**
     * Clears all handlers in the handlerStack, including
     * the default PrettyPage handler.
     *
     * @return Run
     */
    public function clearHandlers();

    /**
     * Registers this instance as an error handler.
     *
     * @return Run
     */
    public function register();

    /**
     * Unregisters all handlers registered by this Whoops\Run instance
     *
     * @return Run
     */
    public function unregister();

    /**
     * Should Whoops allow Handlers to force the script to quit?
     *
     * @param  bool|int $exit
     * @return bool
     */
    public function allowQuit($exit = null);

    /**
     * Silence particular errors in particular files
     *
     * @param  array|string $patterns List or a single regex pattern to
match
     * @param  int          $levels   Defaults to E_STRICT | E_DEPRECATED
     * @return \Whoops\Run
     */
    public function silenceErrorsInPaths($patterns, $levels = 10240);

    /**
     * Should Whoops send HTTP error code to the browser if possible?
     * Whoops will by default send HTTP code 500, but you may wish to
     * use 502, 503, or another 5xx family code.
     *
     * @param bool|int $code
     * @return int|false
     */
    public function sendHttpCode($code = null);

    /**
     * Should Whoops push output directly to the client?
     * If this is false, output will be returned by handleException
     *
     * @param  bool|int $send
     * @return bool
     */
    public function writeToOutput($send = null);

    /**
     * Handles an exception, ultimately generating a Whoops error
     * page.
     *
     * @param  \Throwable $exception
     * @return string     Output generated by handlers
     */
    public function handleException($exception);

    /**
     * Converts generic PHP errors to \ErrorException
     * instances, before passing them off to be handled.
     *
     * This method MUST be compatible with set_error_handler.
     *
     * @param int    $level
     * @param string $message
     * @param string $file
     * @param int    $line
     *
     * @return bool
     * @throws ErrorException
     */
    public function handleError($level, $message, $file = null, $line =
null);

    /**
     * Special case to deal with Fatal errors and the like.
     */
    public function handleShutdown();
}
vendor/filp/whoops/src/Whoops/Util/HtmlDumperOutput.php000064400000001316151166614540017322
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Util;

/**
 * Used as output callable for
Symfony\Component\VarDumper\Dumper\HtmlDumper::dump()
 *
 * @see TemplateHelper::dump()
 */
class HtmlDumperOutput
{
    private $output;

    public function __invoke($line, $depth)
    {
        // A negative depth means "end of dump"
        if ($depth >= 0) {
            // Adds a two spaces indentation to the line
            $this->output .= str_repeat('  ', $depth) . $line
. "\n";
        }
    }

    public function getOutput()
    {
        return $this->output;
    }

    public function clear()
    {
        $this->output = null;
    }
}
vendor/filp/whoops/src/Whoops/Util/Misc.php000064400000003703151166614540014715
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Util;

class Misc
{
    /**
     * Can we at this point in time send HTTP headers?
     *
     * Currently this checks if we are even serving an HTTP request,
     * as opposed to running from a command line.
     *
     * If we are serving an HTTP request, we check if it's not too
late.
     *
     * @return bool
     */
    public static function canSendHeaders()
    {
        return isset($_SERVER["REQUEST_URI"]) &&
!headers_sent();
    }

    public static function isAjaxRequest()
    {
        return (
            !empty($_SERVER['HTTP_X_REQUESTED_WITH'])
            &&
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) ==
'xmlhttprequest');
    }

    /**
     * Check, if possible, that this execution was triggered by a command
line.
     * @return bool
     */
    public static function isCommandLine()
    {
        return PHP_SAPI == 'cli';
    }

    /**
     * Translate ErrorException code into the represented constant.
     *
     * @param int $error_code
     * @return string
     */
    public static function translateErrorCode($error_code)
    {
        $constants = get_defined_constants(true);
        if (array_key_exists('Core', $constants)) {
            foreach ($constants['Core'] as $constant =>
$value) {
                if (substr($constant, 0, 2) == 'E_' &&
$value == $error_code) {
                    return $constant;
                }
            }
        }
        return "E_UNKNOWN";
    }
    
    /**
     * Determine if an error level is fatal (halts execution)
     *
     * @param int $level
     * @return bool
     */
    public static function isLevelFatal($level)
    {
        $errors = E_ERROR;
        $errors |= E_PARSE;
        $errors |= E_CORE_ERROR;
        $errors |= E_CORE_WARNING;
        $errors |= E_COMPILE_ERROR;
        $errors |= E_COMPILE_WARNING;
        return ($level & $errors) > 0;
    }
}
vendor/filp/whoops/src/Whoops/Util/SystemFacade.php000064400000004605151166614540016374
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Util;

class SystemFacade
{
    /**
     * Turns on output buffering.
     *
     * @return bool
     */
    public function startOutputBuffering()
    {
        return ob_start();
    }

    /**
     * @param callable $handler
     * @param int      $types
     *
     * @return callable|null
     */
    public function setErrorHandler(callable $handler, $types =
'use-php-defaults')
    {
        // Since PHP 5.4 the constant E_ALL contains all errors (even
E_STRICT)
        if ($types === 'use-php-defaults') {
            $types = E_ALL;
        }
        return set_error_handler($handler, $types);
    }

    /**
     * @param callable $handler
     *
     * @return callable|null
     */
    public function setExceptionHandler(callable $handler)
    {
        return set_exception_handler($handler);
    }

    /**
     * @return void
     */
    public function restoreExceptionHandler()
    {
        restore_exception_handler();
    }

    /**
     * @return void
     */
    public function restoreErrorHandler()
    {
        restore_error_handler();
    }

    /**
     * @param callable $function
     *
     * @return void
     */
    public function registerShutdownFunction(callable $function)
    {
        register_shutdown_function($function);
    }

    /**
     * @return string|false
     */
    public function cleanOutputBuffer()
    {
        return ob_get_clean();
    }

    /**
     * @return int
     */
    public function getOutputBufferLevel()
    {
        return ob_get_level();
    }

    /**
     * @return bool
     */
    public function endOutputBuffering()
    {
        return ob_end_clean();
    }

    /**
     * @return void
     */
    public function flushOutputBuffer()
    {
        flush();
    }

    /**
     * @return int
     */
    public function getErrorReportingLevel()
    {
        return error_reporting();
    }

    /**
     * @return array|null
     */
    public function getLastError()
    {
        return error_get_last();
    }

    /**
     * @param int $httpCode
     *
     * @return int
     */
    public function setHttpResponseCode($httpCode)
    {
        return http_response_code($httpCode);
    }

    /**
     * @param int $exitStatus
     */
    public function stopExecution($exitStatus)
    {
        exit($exitStatus);
    }
}
vendor/filp/whoops/src/Whoops/Util/TemplateHelper.php000064400000022523151166614540016736
0ustar00<?php
/**
 * Whoops - php errors for cool kids
 * @author Filipe Dobreira <http://github.com/filp>
 */

namespace Whoops\Util;

use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Whoops\Exception\Frame;

/**
 * Exposes useful tools for working with/in templates
 */
class TemplateHelper
{
    /**
     * An array of variables to be passed to all templates
     * @var array
     */
    private $variables = [];

    /**
     * @var HtmlDumper
     */
    private $htmlDumper;

    /**
     * @var HtmlDumperOutput
     */
    private $htmlDumperOutput;

    /**
     * @var AbstractCloner
     */
    private $cloner;

    /**
     * @var string
     */
    private $applicationRootPath;

    public function __construct()
    {
        // root path for ordinary composer projects
        $this->applicationRootPath =
dirname(dirname(dirname(dirname(dirname(dirname(__DIR__))))));
    }

    /**
     * Escapes a string for output in an HTML document
     *
     * @param  string $raw
     * @return string
     */
    public function escape($raw)
    {
        $flags = ENT_QUOTES;

        // HHVM has all constants defined, but only ENT_IGNORE
        // works at the moment
        if (defined("ENT_SUBSTITUTE") &&
!defined("HHVM_VERSION")) {
            $flags |= ENT_SUBSTITUTE;
        } else {
            // This is for 5.3.
            // The documentation warns of a potential security issue,
            // but it seems it does not apply in our case, because
            // we do not blacklist anything anywhere.
            $flags |= ENT_IGNORE;
        }

        $raw = str_replace(chr(9), '    ', $raw);

        return htmlspecialchars($raw, $flags, "UTF-8");
    }

    /**
     * Escapes a string for output in an HTML document, but preserves
     * URIs within it, and converts them to clickable anchor elements.
     *
     * @param  string $raw
     * @return string
     */
    public function escapeButPreserveUris($raw)
    {
        $escaped = $this->escape($raw);
        return preg_replace(
           
"@([A-z]+?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@",
            "<a href=\"$1\" target=\"_blank\"
rel=\"noreferrer noopener\">$1</a>",
            $escaped
        );
    }

    /**
     * Makes sure that the given string breaks on the delimiter.
     *
     * @param  string $delimiter
     * @param  string $s
     * @return string
     */
    public function breakOnDelimiter($delimiter, $s)
    {
        $parts = explode($delimiter, $s);
        foreach ($parts as &$part) {
            $part = '<span class="delimiter">' .
$part . '</span>';
        }

        return implode($delimiter, $parts);
    }

    /**
     * Replace the part of the path that all files have in common.
     *
     * @param  string $path
     * @return string
     */
    public function shorten($path)
    {
        if ($this->applicationRootPath != "/") {
            $path = str_replace($this->applicationRootPath,
'&hellip;', $path);
        }

        return $path;
    }

    private function getDumper()
    {
        if (!$this->htmlDumper &&
class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
            $this->htmlDumperOutput = new HtmlDumperOutput();
            // re-use the same var-dumper instance, so it won't
re-render the global styles/scripts on each dump.
            $this->htmlDumper = new
HtmlDumper($this->htmlDumperOutput);

            $styles = [
                'default' => 'color:#FFFFFF;
line-height:normal; font:12px "Inconsolata", "Fira
Mono", "Source Code Pro", Monaco, Consolas, "Lucida
Console", monospace !important; word-wrap: break-word; white-space:
pre-wrap; position:relative; z-index:99999; word-break: normal',
                'num' => 'color:#BCD42A',
                'const' => 'color: #4bb1b1;',
                'str' => 'color:#BCD42A',
                'note' => 'color:#ef7c61',
                'ref' => 'color:#A0A0A0',
                'public' => 'color:#FFFFFF',
                'protected' => 'color:#FFFFFF',
                'private' => 'color:#FFFFFF',
                'meta' => 'color:#FFFFFF',
                'key' => 'color:#BCD42A',
                'index' => 'color:#ef7c61',
            ];
            $this->htmlDumper->setStyles($styles);
        }

        return $this->htmlDumper;
    }

    /**
     * Format the given value into a human readable string.
     *
     * @param  mixed $value
     * @return string
     */
    public function dump($value)
    {
        $dumper = $this->getDumper();

        if ($dumper) {
            // re-use the same DumpOutput instance, so it won't
re-render the global styles/scripts on each dump.
            // exclude verbose information (e.g. exception stack traces)
            if
(class_exists('Symfony\Component\VarDumper\Caster\Caster')) {
                $cloneVar = $this->getCloner()->cloneVar($value,
Caster::EXCLUDE_VERBOSE);
                // Symfony VarDumper 2.6 Caster class dont exist.
            } else {
                $cloneVar = $this->getCloner()->cloneVar($value);
            }

            $dumper->dump(
                $cloneVar,
                $this->htmlDumperOutput
            );

            $output = $this->htmlDumperOutput->getOutput();
            $this->htmlDumperOutput->clear();

            return $output;
        }

        return htmlspecialchars(print_r($value, true));
    }

    /**
     * Format the args of the given Frame as a human readable html string
     *
     * @param  Frame $frame
     * @return string the rendered html
     */
    public function dumpArgs(Frame $frame)
    {
        // we support frame args only when the optional dumper is available
        if (!$this->getDumper()) {
            return '';
        }

        $html = '';
        $numFrames = count($frame->getArgs());

        if ($numFrames > 0) {
            $html = '<ol class="linenums">';
            foreach ($frame->getArgs() as $j => $frameArg) {
                $html .= '<li>'. $this->dump($frameArg)
.'</li>';
            }
            $html .= '</ol>';
        }

        return $html;
    }

    /**
     * Convert a string to a slug version of itself
     *
     * @param  string $original
     * @return string
     */
    public function slug($original)
    {
        $slug = str_replace(" ", "-", $original);
        $slug = preg_replace('/[^\w\d\-\_]/i', '',
$slug);
        return strtolower($slug);
    }

    /**
     * Given a template path, render it within its own scope. This
     * method also accepts an array of additional variables to be
     * passed to the template.
     *
     * @param string $template
     * @param array  $additionalVariables
     */
    public function render($template, array $additionalVariables = null)
    {
        $variables = $this->getVariables();

        // Pass the helper to the template:
        $variables["tpl"] = $this;

        if ($additionalVariables !== null) {
            $variables = array_replace($variables, $additionalVariables);
        }

        call_user_func(function () {
            extract(func_get_arg(1));
            require func_get_arg(0);
        }, $template, $variables);
    }

    /**
     * Sets the variables to be passed to all templates rendered
     * by this template helper.
     *
     * @param array $variables
     */
    public function setVariables(array $variables)
    {
        $this->variables = $variables;
    }

    /**
     * Sets a single template variable, by its name:
     *
     * @param string $variableName
     * @param mixed  $variableValue
     */
    public function setVariable($variableName, $variableValue)
    {
        $this->variables[$variableName] = $variableValue;
    }

    /**
     * Gets a single template variable, by its name, or
     * $defaultValue if the variable does not exist
     *
     * @param  string $variableName
     * @param  mixed  $defaultValue
     * @return mixed
     */
    public function getVariable($variableName, $defaultValue = null)
    {
        return isset($this->variables[$variableName]) ?
            $this->variables[$variableName] : $defaultValue;
    }

    /**
     * Unsets a single template variable, by its name
     *
     * @param string $variableName
     */
    public function delVariable($variableName)
    {
        unset($this->variables[$variableName]);
    }

    /**
     * Returns all variables for this helper
     *
     * @return array
     */
    public function getVariables()
    {
        return $this->variables;
    }

    /**
     * Set the cloner used for dumping variables.
     *
     * @param AbstractCloner $cloner
     */
    public function setCloner($cloner)
    {
        $this->cloner = $cloner;
    }

    /**
     * Get the cloner used for dumping variables.
     *
     * @return AbstractCloner
     */
    public function getCloner()
    {
        if (!$this->cloner) {
            $this->cloner = new VarCloner();
        }
        return $this->cloner;
    }

    /**
     * Set the application root path.
     *
     * @param string $applicationRootPath
     */
    public function setApplicationRootPath($applicationRootPath)
    {
        $this->applicationRootPath = $applicationRootPath;
    }

    /**
     * Return the application root path.
     *
     * @return string
     */
    public function getApplicationRootPath()
    {
        return $this->applicationRootPath;
    }
}
vendor/leafo/scssphp/composer.json000064400000002127151166614540013347
0ustar00{
    "name": "leafo/scssphp",
    "type": "library",
    "description": "scssphp is a compiler for SCSS written
in PHP.",
    "keywords": ["css", "stylesheet",
"scss", "sass", "less"],
    "homepage": "http://leafo.github.io/scssphp/",
    "license": [
        "MIT"
    ],
    "authors": [
        {
            "name": "Leaf Corcoran",
            "email": "leafot@gmail.com",
            "homepage": "http://leafo.net"
        }
    ],
    "autoload": {
        "psr-4": { "Leafo\\ScssPhp\\": "src/"
}
    },
    "autoload-dev": {
        "psr-4": { "Leafo\\ScssPhp\\Test\\":
"tests/" }
    },
    "require": {
        "php": "^5.4.0 || ^7"
    },
    "require-dev": {
        "squizlabs/php_codesniffer": "~2.5",
        "phpunit/phpunit": "~4.6",
        "twbs/bootstrap": "~4.3",
        "zurb/foundation": "~6.5"
    },
    "bin": ["bin/pscss"],
    "archive": {
        "exclude": [
            "/Makefile",
            "/.gitattributes",
            "/.gitignore",
            "/.travis.yml",
            "/phpunit.xml.dist",
            "/tests"
        ]
    },
    "abandoned": "scssphp/scssphp"
}
vendor/leafo/scssphp/example/Server.php000064400000033223151166614540014240
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2017 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

use Leafo\ScssPhp\Compiler;
use Leafo\ScssPhp\Exception\ServerException;
use Leafo\ScssPhp\Version;

/**
 * Server
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Server
{
    /**
     * @var boolean
     */
    private $showErrorsAsCSS;

    /**
     * @var string
     */
    private $dir;

    /**
     * @var string
     */
    private $cacheDir;

    /**
     * @var \Leafo\ScssPhp\Compiler
     */
    private $scss;

    /**
     * Join path components
     *
     * @param string $left  Path component, left of the directory separator
     * @param string $right Path component, right of the directory
separator
     *
     * @return string
     */
    protected function join($left, $right)
    {
        return rtrim($left, '/\\') . DIRECTORY_SEPARATOR .
ltrim($right, '/\\');
    }

    /**
     * Get name of requested .scss file
     *
     * @return string|null
     */
    protected function inputName()
    {
        switch (true) {
            case isset($_GET['p']):
                return $_GET['p'];
            case isset($_SERVER['PATH_INFO']):
                return $_SERVER['PATH_INFO'];
            case isset($_SERVER['DOCUMENT_URI']):
                return substr($_SERVER['DOCUMENT_URI'],
strlen($_SERVER['SCRIPT_NAME']));
        }
    }

    /**
     * Get path to requested .scss file
     *
     * @return string
     */
    protected function findInput()
    {
        if (($input = $this->inputName())
            && strpos($input, '..') === false
            && substr($input, -5) === '.scss'
        ) {
            $name = $this->join($this->dir, $input);

            if (is_file($name) && is_readable($name)) {
                return $name;
            }
        }

        return false;
    }

    /**
     * Get path to cached .css file
     *
     * @return string
     */
    protected function cacheName($fname)
    {
        return $this->join($this->cacheDir, md5($fname) .
'.css');
    }

    /**
     * Get path to meta data
     *
     * @return string
     */
    protected function metadataName($out)
    {
        return $out . '.meta';
    }

    /**
     * Determine whether .scss file needs to be re-compiled.
     *
     * @param string $out  Output path
     * @param string $etag ETag
     *
     * @return boolean True if compile required.
     */
    protected function needsCompile($out, &$etag)
    {
        if (! is_file($out)) {
            return true;
        }

        $mtime = filemtime($out);

        $metadataName = $this->metadataName($out);

        if (is_readable($metadataName)) {
            $metadata = unserialize(file_get_contents($metadataName));

            foreach ($metadata['imports'] as $import =>
$originalMtime) {
                $currentMtime = filemtime($import);

                if ($currentMtime !== $originalMtime || $currentMtime >
$mtime) {
                    return true;
                }
            }

            $metaVars =
crc32(serialize($this->scss->getVariables()));

            if ($metaVars !== $metadata['vars']) {
                return true;
            }

            $etag = $metadata['etag'];

            return false;
        }

        return true;
    }

    /**
     * Get If-Modified-Since header from client request
     *
     * @return string|null
     */
    protected function getIfModifiedSinceHeader()
    {
        $modifiedSince = null;

        if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
            $modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];

            if (false !== ($semicolonPos = strpos($modifiedSince,
';'))) {
                $modifiedSince = substr($modifiedSince, 0, $semicolonPos);
            }
        }

        return $modifiedSince;
    }

    /**
     * Get If-None-Match header from client request
     *
     * @return string|null
     */
    protected function getIfNoneMatchHeader()
    {
        $noneMatch = null;

        if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
            $noneMatch = $_SERVER['HTTP_IF_NONE_MATCH'];
        }

        return $noneMatch;
    }

    /**
     * Compile .scss file
     *
     * @param string $in  Input path (.scss)
     * @param string $out Output path (.css)
     *
     * @return array
     */
    protected function compile($in, $out)
    {
        $start   = microtime(true);
        $css     = $this->scss->compile(file_get_contents($in), $in);
        $elapsed = round((microtime(true) - $start), 4);

        $v    = Version::VERSION;
        $t    = gmdate('r');
        $css  = "/* compiled by scssphp $v on $t (${elapsed}s)
*/\n\n" . $css;
        $etag = md5($css);

        file_put_contents($out, $css);
        file_put_contents(
            $this->metadataName($out),
            serialize([
                'etag'    => $etag,
                'imports' =>
$this->scss->getParsedFiles(),
                'vars'    =>
crc32(serialize($this->scss->getVariables())),
            ])
        );

        return [$css, $etag];
    }

    /**
     * Format error as a pseudo-element in CSS
     *
     * @param \Exception $error
     *
     * @return string
     */
    protected function createErrorCSS(\Exception $error)
    {
        $message = str_replace(
            ["'", "\n"],
            ["\\'", "\\A"],
            $error->getfile() . ":\n\n" .
$error->getMessage()
        );

        return "body { display: none !important; }
                html:after {
                    background: white;
                    color: black;
                    content: '$message';
                    display: block !important;
                    font-family: mono;
                    padding: 1em;
                    white-space: pre;
                }";
    }

    /**
     * Render errors as a pseudo-element within valid CSS, displaying the
errors on any
     * page that includes this CSS.
     *
     * @param boolean $show
     */
    public function showErrorsAsCSS($show = true)
    {
        $this->showErrorsAsCSS = $show;
    }

    /**
     * Compile .scss file
     *
     * @param string $in  Input file (.scss)
     * @param string $out Output file (.css) optional
     *
     * @return string|bool
     *
     * @throws \Leafo\ScssPhp\Exception\ServerException
     */
    public function compileFile($in, $out = null)
    {
        if (! is_readable($in)) {
            throw new ServerException('load error: failed to find
' . $in);
        }

        $pi = pathinfo($in);

        $this->scss->addImportPath($pi['dirname'] .
'/');

        $compiled = $this->scss->compile(file_get_contents($in),
$in);

        if ($out !== null) {
            return file_put_contents($out, $compiled);
        }

        return $compiled;
    }

    /**
     * Check if file need compiling
     *
     * @param string $in  Input file (.scss)
     * @param string $out Output file (.css)
     *
     * @return bool
     */
    public function checkedCompile($in, $out)
    {
        if (! is_file($out) || filemtime($in) > filemtime($out)) {
            $this->compileFile($in, $out);

            return true;
        }

        return false;
    }

    /**
     * Compile requested scss and serve css.  Outputs HTTP response.
     *
     * @param string $salt Prefix a string to the filename for creating the
cache name hash
     */
    public function serve($salt = '')
    {
        $protocol = isset($_SERVER['SERVER_PROTOCOL'])
            ? $_SERVER['SERVER_PROTOCOL']
            : 'HTTP/1.0';

        if ($input = $this->findInput()) {
            $output = $this->cacheName($salt . $input);
            $etag = $noneMatch = trim($this->getIfNoneMatchHeader(),
'"');

            if ($this->needsCompile($output, $etag)) {
                try {
                    list($css, $etag) = $this->compile($input, $output);

                    $lastModified = gmdate('r',
filemtime($output));

                    header('Last-Modified: ' . $lastModified);
                    header('Content-type: text/css');
                    header('ETag: "' . $etag .
'"');

                    echo $css;
                } catch (\Exception $e) {
                    if ($this->showErrorsAsCSS) {
                        header('Content-type: text/css');

                        echo $this->createErrorCSS($e);
                    } else {
                        header($protocol . ' 500 Internal Server
Error');
                        header('Content-type: text/plain');

                        echo 'Parse error: ' .
$e->getMessage() . "\n";
                    }
                }

                return;
            }

            header('X-SCSS-Cache: true');
            header('Content-type: text/css');
            header('ETag: "' . $etag . '"');

            if ($etag === $noneMatch) {
                header($protocol . ' 304 Not Modified');

                return;
            }

            $modifiedSince = $this->getIfModifiedSinceHeader();
            $mtime = filemtime($output);

            if (strtotime($modifiedSince) === $mtime) {
                header($protocol . ' 304 Not Modified');

                return;
            }

            $lastModified  = gmdate('r', $mtime);
            header('Last-Modified: ' . $lastModified);

            echo file_get_contents($output);

            return;
        }

        header($protocol . ' 404 Not Found');
        header('Content-type: text/plain');

        $v = Version::VERSION;
        echo "/* INPUT NOT FOUND scss $v */\n";
    }

    /**
     * Based on explicit input/output files does a full change check on
cache before compiling.
     *
     * @param string  $in
     * @param string  $out
     * @param boolean $force
     *
     * @return string Compiled CSS results
     *
     * @throws \Leafo\ScssPhp\Exception\ServerException
     */
    public function checkedCachedCompile($in, $out, $force = false)
    {
        if (! is_file($in) || ! is_readable($in)) {
            throw new ServerException('Invalid or unreadable input
file specified.');
        }

        if (is_dir($out) || ! is_writable(file_exists($out) ? $out :
dirname($out))) {
            throw new ServerException('Invalid or unwritable output
file specified.');
        }

        if ($force || $this->needsCompile($out, $etag)) {
            list($css, $etag) = $this->compile($in, $out);
        } else {
            $css = file_get_contents($out);
        }

        return $css;
    }

    /**
     * Execute scssphp on a .scss file or a scssphp cache structure
     *
     * The scssphp cache structure contains information about a specific
     * scss file having been parsed. It can be used as a hint for future
     * calls to determine whether or not a rebuild is required.
     *
     * The cache structure contains two important keys that may be used
     * externally:
     *
     * compiled: The final compiled CSS
     * updated: The time (in seconds) the CSS was last compiled
     *
     * The cache structure is a plain-ol' PHP associative array and
can
     * be serialized and unserialized without a hitch.
     *
     * @param mixed   $in    Input
     * @param boolean $force Force rebuild?
     *
     * @return array scssphp cache structure
     */
    public function cachedCompile($in, $force = false)
    {
        // assume no root
        $root = null;

        if (is_string($in)) {
            $root = $in;
        } elseif (is_array($in) and isset($in['root'])) {
            if ($force or ! isset($in['files'])) {
                // If we are forcing a recompile or if for some reason the
                // structure does not contain any file information we
should
                // specify the root to trigger a rebuild.
                $root = $in['root'];
            } elseif (isset($in['files']) and
is_array($in['files'])) {
                foreach ($in['files'] as $fname => $ftime) {
                    if (! file_exists($fname) or filemtime($fname) >
$ftime) {
                        // One of the files we knew about previously has
changed
                        // so we should look at our incoming root again.
                        $root = $in['root'];
                        break;
                    }
                }
            }
        } else {
            // TODO: Throw an exception? We got neither a string nor
something
            // that looks like a compatible lessphp cache structure.
            return null;
        }

        if ($root !== null) {
            // If we have a root value which means we should rebuild.
            $out = [];
            $out['root'] = $root;
            $out['compiled'] = $this->compileFile($root);
            $out['files'] = $this->scss->getParsedFiles();
            $out['updated'] = time();
            return $out;
        } else {
            // No changes, pass back the structure
            // we were given initially.
            return $in;
        }
    }

    /**
     * Constructor
     *
     * @param string                       $dir      Root directory to
.scss files
     * @param string                       $cacheDir Cache directory
     * @param \Leafo\ScssPhp\Compiler|null $scss     SCSS compiler instance
     */
    public function __construct($dir, $cacheDir = null, $scss = null)
    {
        $this->dir = $dir;

        if (! isset($cacheDir)) {
            $cacheDir = $this->join($dir, 'scss_cache');
        }

        $this->cacheDir = $cacheDir;

        if (! is_dir($this->cacheDir)) {
            throw new ServerException('Cache directory doesn\'t
exist: ' . $cacheDir);
        }

        if (! isset($scss)) {
            $scss = new Compiler();
            $scss->setImportPaths($this->dir);
        }

        $this->scss = $scss;
        $this->showErrorsAsCSS = false;

        date_default_timezone_set('UTC');
    }
}
vendor/leafo/scssphp/scss.inc.php000064400000003153151166614540013061
0ustar00<?php
if (version_compare(PHP_VERSION, '5.4') < 0) {
    throw new \Exception('scssphp requires PHP 5.4 or above');
}

if (! class_exists('Leafo\ScssPhp\Version', false)) {
    include_once __DIR__ . '/src/Base/Range.php';
    include_once __DIR__ . '/src/Block.php';
    include_once __DIR__ . '/src/Cache.php';
    include_once __DIR__ . '/src/Colors.php';
    include_once __DIR__ . '/src/Compiler.php';
    include_once __DIR__ . '/src/Compiler/Environment.php';
    include_once __DIR__ .
'/src/Exception/CompilerException.php';
    include_once __DIR__ . '/src/Exception/ParserException.php';
    include_once __DIR__ . '/src/Exception/RangeException.php';
    include_once __DIR__ . '/src/Exception/ServerException.php';
    include_once __DIR__ . '/src/Formatter.php';
    include_once __DIR__ . '/src/Formatter/Compact.php';
    include_once __DIR__ . '/src/Formatter/Compressed.php';
    include_once __DIR__ . '/src/Formatter/Crunched.php';
    include_once __DIR__ . '/src/Formatter/Debug.php';
    include_once __DIR__ . '/src/Formatter/Expanded.php';
    include_once __DIR__ . '/src/Formatter/Nested.php';
    include_once __DIR__ . '/src/Formatter/OutputBlock.php';
    include_once __DIR__ . '/src/Node.php';
    include_once __DIR__ . '/src/Node/Number.php';
    include_once __DIR__ . '/src/Parser.php';
    include_once __DIR__ . '/src/SourceMap/Base64.php';
    include_once __DIR__ . '/src/SourceMap/Base64VLQ.php';
    include_once __DIR__ .
'/src/SourceMap/SourceMapGenerator.php';
    include_once __DIR__ . '/src/Type.php';
    include_once __DIR__ . '/src/Util.php';
    include_once __DIR__ . '/src/Version.php';
}
vendor/leafo/scssphp/src/Base/Range.php000064400000001424151166614540014032
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2015-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Base;

/**
 * Range
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class Range
{
    public $first;
    public $last;

    /**
     * Initialize range
     *
     * @param integer|float $first
     * @param integer|float $last
     */
    public function __construct($first, $last)
    {
        $this->first = $first;
        $this->last = $last;
    }

    /**
     * Test for inclusion in range
     *
     * @param integer|float $value
     *
     * @return boolean
     */
    public function includes($value)
    {
        return $value >= $this->first && $value <=
$this->last;
    }
}
vendor/leafo/scssphp/src/Block.php000064400000001570151166614540013160
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

/**
 * Block
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class Block
{
    /**
     * @var string
     */
    public $type;

    /**
     * @var \Leafo\ScssPhp\Block
     */
    public $parent;

    /**
     * @var string
     */
    public $sourceName;

    /**
     * @var integer
     */
    public $sourceIndex;

    /**
     * @var integer
     */
    public $sourceLine;

    /**
     * @var integer
     */
    public $sourceColumn;

    /**
     * @var array
     */
    public $selectors;

    /**
     * @var array
     */
    public $comments;

    /**
     * @var array
     */
    public $children;

    /**
     * @var \Leafo\ScssPhp\Block
     */
    public $selfParent;
}
vendor/leafo/scssphp/src/Cache.php000064400000014507151166614540013135
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

use Exception;

/**
 * The scss cache manager.
 *
 * In short:
 *
 * allow to put in cache/get from cache a generic result from a known
operation on a generic dataset,
 * taking in account options that affects the result
 *
 * The cache manager is agnostic about data format and only the operation
is expected to be described by string
 *
 */

/**
 * SCSS cache
 *
 * @author Cedric Morin
 */
class Cache
{
    const CACHE_VERSION = 0;

    // directory used for storing data
    public static $cacheDir = false;

    // prefix for the storing data
    public static $prefix = 'scssphp_';

    // force a refresh : 'once' for refreshing the first hit on a
cache only, true to never use the cache in this hit
    public static $forceFefresh = false;

    // specifies the number of seconds after which data cached will be seen
as 'garbage' and potentially cleaned up
    public static $gcLifetime = 604800;

    // array of already refreshed cache if $forceFefresh==='once'
    protected static $refreshed = [];

    /**
     * Constructor
     *
     * @param array $options
     */
    public function __construct($options)
    {
        // check $cacheDir
        if (isset($options['cache_dir'])) {
            self::$cacheDir = $options['cache_dir'];
        }

        if (empty(self::$cacheDir)) {
            throw new Exception('cache_dir not set');
        }

        if (isset($options['prefix'])) {
            self::$prefix = $options['prefix'];
        }

        if (empty(self::$prefix)) {
            throw new Exception('prefix not set');
        }

        if (isset($options['forceRefresh'])) {
            self::$forceFefresh = $options['force_refresh'];
        }

        self::checkCacheDir();
    }

    /**
     * Get the cached result of $operation on $what,
     * which is known as dependant from the content of $options
     *
     * @param string  $operation    parse, compile...
     * @param mixed   $what         content key (e.g., filename to be
treated)
     * @param array   $options      any option that affect the operation
result on the content
     * @param integer $lastModified last modified timestamp
     *
     * @return mixed
     *
     * @throws \Exception
     */
    public function getCache($operation, $what, $options = [],
$lastModified = null)
    {
        $fileCache = self::$cacheDir . self::cacheName($operation, $what,
$options);

        if ((! self::$forceRefresh || (self::$forceRefresh ===
'once' && isset(self::$refreshed[$fileCache])))
            && file_exists($fileCache)
        ) {
            $cacheTime = filemtime($fileCache);

            if ((is_null($lastModified) || $cacheTime > $lastModified)
                && $cacheTime + self::$gcLifetime > time()
            ) {
                $c = file_get_contents($fileCache);
                $c = unserialize($c);

                if (is_array($c) && isset($c['value'])) {
                    return $c['value'];
                }
            }
        }

        return null;
    }

    /**
     * Put in cache the result of $operation on $what,
     * which is known as dependant from the content of $options
     *
     * @param string $operation
     * @param mixed  $what
     * @param mixed  $value
     * @param array  $options
     */
    public function setCache($operation, $what, $value, $options = [])
    {
        $fileCache = self::$cacheDir . self::cacheName($operation, $what,
$options);

        $c = ['value' => $value];
        $c = serialize($c);
        file_put_contents($fileCache, $c);

        if (self::$forceRefresh === 'once') {
            self::$refreshed[$fileCache] = true;
        }
    }

    /**
     * Get the cache name for the caching of $operation on $what,
     * which is known as dependant from the content of $options
     *
     * @param string $operation
     * @param mixed  $what
     * @param array  $options
     *
     * @return string
     */
    private static function cacheName($operation, $what, $options = [])
    {
        $t = [
          'version' => self::CACHE_VERSION,
          'operation' => $operation,
          'what' => $what,
          'options' => $options
        ];

        $t = self::$prefix
          . sha1(json_encode($t))
          . ".$operation"
          . ".scsscache";

        return $t;
    }

    /**
     * Check that the cache dir exists and is writeable
     *
     * @throws \Exception
     */
    public static function checkCacheDir()
    {
        self::$cacheDir = str_replace('\\', '/',
self::$cacheDir);
        self::$cacheDir = rtrim(self::$cacheDir, '/') .
'/';

        if (! file_exists(self::$cacheDir)) {
            if (! mkdir(self::$cacheDir)) {
                throw new Exception('Cache directory couldn\'t be
created: ' . self::$cacheDir);
            }
        } elseif (! is_dir(self::$cacheDir)) {
            throw new Exception('Cache directory doesn\'t exist:
' . self::$cacheDir);
        } elseif (! is_writable(self::$cacheDir)) {
            throw new Exception('Cache directory isn\'t writable:
' . self::$cacheDir);
        }
    }

    /**
     * Delete unused cached files
     */
    public static function cleanCache()
    {
        static $clean = false;

        if ($clean || empty(self::$cacheDir)) {
            return;
        }

        $clean = true;

        // only remove files with extensions created by SCSSPHP Cache
        // css files removed based on the list files
        $removeTypes = ['scsscache' => 1];

        $files = scandir(self::$cacheDir);

        if (! $files) {
            return;
        }

        $checkTime = time() - self::$gcLifetime;

        foreach ($files as $file) {
            // don't delete if the file wasn't created with
SCSSPHP Cache
            if (strpos($file, self::$prefix) !== 0) {
                continue;
            }

            $parts = explode('.', $file);
            $type = array_pop($parts);

            if (! isset($removeTypes[$type])) {
                continue;
            }

            $fullPath = self::$cacheDir . $file;
            $mtime = filemtime($fullPath);

            // don't delete if it's a relatively new file
            if ($mtime > $checkTime) {
                continue;
            }

            unlink($fullPath);
        }
    }
}
vendor/leafo/scssphp/src/Colors.php000064400000013450151166614540013367
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

/**
 * CSS Colors
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Colors
{
    /**
     * CSS Colors
     *
     * @see http://www.w3.org/TR/css3-color
     *
     * @var array
     */
    public static $cssColors = [
        'aliceblue' => '240,248,255',
        'antiquewhite' => '250,235,215',
        'aqua' => '0,255,255',
        'aquamarine' => '127,255,212',
        'azure' => '240,255,255',
        'beige' => '245,245,220',
        'bisque' => '255,228,196',
        'black' => '0,0,0',
        'blanchedalmond' => '255,235,205',
        'blue' => '0,0,255',
        'blueviolet' => '138,43,226',
        'brown' => '165,42,42',
        'burlywood' => '222,184,135',
        'cadetblue' => '95,158,160',
        'chartreuse' => '127,255,0',
        'chocolate' => '210,105,30',
        'coral' => '255,127,80',
        'cornflowerblue' => '100,149,237',
        'cornsilk' => '255,248,220',
        'crimson' => '220,20,60',
        'cyan' => '0,255,255',
        'darkblue' => '0,0,139',
        'darkcyan' => '0,139,139',
        'darkgoldenrod' => '184,134,11',
        'darkgray' => '169,169,169',
        'darkgreen' => '0,100,0',
        'darkgrey' => '169,169,169',
        'darkkhaki' => '189,183,107',
        'darkmagenta' => '139,0,139',
        'darkolivegreen' => '85,107,47',
        'darkorange' => '255,140,0',
        'darkorchid' => '153,50,204',
        'darkred' => '139,0,0',
        'darksalmon' => '233,150,122',
        'darkseagreen' => '143,188,143',
        'darkslateblue' => '72,61,139',
        'darkslategray' => '47,79,79',
        'darkslategrey' => '47,79,79',
        'darkturquoise' => '0,206,209',
        'darkviolet' => '148,0,211',
        'deeppink' => '255,20,147',
        'deepskyblue' => '0,191,255',
        'dimgray' => '105,105,105',
        'dimgrey' => '105,105,105',
        'dodgerblue' => '30,144,255',
        'firebrick' => '178,34,34',
        'floralwhite' => '255,250,240',
        'forestgreen' => '34,139,34',
        'fuchsia' => '255,0,255',
        'gainsboro' => '220,220,220',
        'ghostwhite' => '248,248,255',
        'gold' => '255,215,0',
        'goldenrod' => '218,165,32',
        'gray' => '128,128,128',
        'green' => '0,128,0',
        'greenyellow' => '173,255,47',
        'grey' => '128,128,128',
        'honeydew' => '240,255,240',
        'hotpink' => '255,105,180',
        'indianred' => '205,92,92',
        'indigo' => '75,0,130',
        'ivory' => '255,255,240',
        'khaki' => '240,230,140',
        'lavender' => '230,230,250',
        'lavenderblush' => '255,240,245',
        'lawngreen' => '124,252,0',
        'lemonchiffon' => '255,250,205',
        'lightblue' => '173,216,230',
        'lightcoral' => '240,128,128',
        'lightcyan' => '224,255,255',
        'lightgoldenrodyellow' => '250,250,210',
        'lightgray' => '211,211,211',
        'lightgreen' => '144,238,144',
        'lightgrey' => '211,211,211',
        'lightpink' => '255,182,193',
        'lightsalmon' => '255,160,122',
        'lightseagreen' => '32,178,170',
        'lightskyblue' => '135,206,250',
        'lightslategray' => '119,136,153',
        'lightslategrey' => '119,136,153',
        'lightsteelblue' => '176,196,222',
        'lightyellow' => '255,255,224',
        'lime' => '0,255,0',
        'limegreen' => '50,205,50',
        'linen' => '250,240,230',
        'magenta' => '255,0,255',
        'maroon' => '128,0,0',
        'mediumaquamarine' => '102,205,170',
        'mediumblue' => '0,0,205',
        'mediumorchid' => '186,85,211',
        'mediumpurple' => '147,112,219',
        'mediumseagreen' => '60,179,113',
        'mediumslateblue' => '123,104,238',
        'mediumspringgreen' => '0,250,154',
        'mediumturquoise' => '72,209,204',
        'mediumvioletred' => '199,21,133',
        'midnightblue' => '25,25,112',
        'mintcream' => '245,255,250',
        'mistyrose' => '255,228,225',
        'moccasin' => '255,228,181',
        'navajowhite' => '255,222,173',
        'navy' => '0,0,128',
        'oldlace' => '253,245,230',
        'olive' => '128,128,0',
        'olivedrab' => '107,142,35',
        'orange' => '255,165,0',
        'orangered' => '255,69,0',
        'orchid' => '218,112,214',
        'palegoldenrod' => '238,232,170',
        'palegreen' => '152,251,152',
        'paleturquoise' => '175,238,238',
        'palevioletred' => '219,112,147',
        'papayawhip' => '255,239,213',
        'peachpuff' => '255,218,185',
        'peru' => '205,133,63',
        'pink' => '255,192,203',
        'plum' => '221,160,221',
        'powderblue' => '176,224,230',
        'purple' => '128,0,128',
        'rebeccapurple' => '102,51,153',
        'red' => '255,0,0',
        'rosybrown' => '188,143,143',
        'royalblue' => '65,105,225',
        'saddlebrown' => '139,69,19',
        'salmon' => '250,128,114',
        'sandybrown' => '244,164,96',
        'seagreen' => '46,139,87',
        'seashell' => '255,245,238',
        'sienna' => '160,82,45',
        'silver' => '192,192,192',
        'skyblue' => '135,206,235',
        'slateblue' => '106,90,205',
        'slategray' => '112,128,144',
        'slategrey' => '112,128,144',
        'snow' => '255,250,250',
        'springgreen' => '0,255,127',
        'steelblue' => '70,130,180',
        'tan' => '210,180,140',
        'teal' => '0,128,128',
        'thistle' => '216,191,216',
        'tomato' => '255,99,71',
        'transparent' => '0,0,0,0',
        'turquoise' => '64,224,208',
        'violet' => '238,130,238',
        'wheat' => '245,222,179',
        'white' => '255,255,255',
        'whitesmoke' => '245,245,245',
        'yellow' => '255,255,0',
        'yellowgreen' => '154,205,50',
    ];
}
vendor/leafo/scssphp/src/Compiler/Environment.php000064400000001165151166614540016204
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Compiler;

/**
 * Compiler environment
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class Environment
{
    /**
     * @var \Leafo\ScssPhp\Block
     */
    public $block;

    /**
     * @var \Leafo\ScssPhp\Compiler\Environment
     */
    public $parent;

    /**
     * @var array
     */
    public $store;

    /**
     * @var array
     */
    public $storeUnreduced;

    /**
     * @var integer
     */
    public $depth;
}
vendor/leafo/scssphp/src/Compiler.php000064400000554220151166614540013705
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

use Leafo\ScssPhp\Base\Range;
use Leafo\ScssPhp\Block;
use Leafo\ScssPhp\Cache;
use Leafo\ScssPhp\Colors;
use Leafo\ScssPhp\Compiler\Environment;
use Leafo\ScssPhp\Exception\CompilerException;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\Node;
use Leafo\ScssPhp\SourceMap\SourceMapGenerator;
use Leafo\ScssPhp\Type;
use Leafo\ScssPhp\Parser;
use Leafo\ScssPhp\Util;

/**
 * The scss compiler and parser.
 *
 * Converting SCSS to CSS is a three stage process. The incoming file is
parsed
 * by `Parser` into a syntax tree, then it is compiled into another tree
 * representing the CSS structure by `Compiler`. The CSS tree is fed into a
 * formatter, like `Formatter` which then outputs CSS as a string.
 *
 * During the first compile, all values are *reduced*, which means that
their
 * types are brought to the lowest form before being dump as strings. This
 * handles math equations, variable dereferences, and the like.
 *
 * The `compile` function of `Compiler` is the entry point.
 *
 * In summary:
 *
 * The `Compiler` class creates an instance of the parser, feeds it SCSS
code,
 * then transforms the resulting tree to a CSS tree. This class also holds
the
 * evaluation context, such as all available mixins and variables at any
given
 * time.
 *
 * The `Parser` class is only concerned with parsing its input.
 *
 * The `Formatter` takes a CSS tree, and dumps it to a formatted string,
 * handling things like indentation.
 */

/**
 * SCSS compiler
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Compiler
{
    const LINE_COMMENTS = 1;
    const DEBUG_INFO    = 2;

    const WITH_RULE     = 1;
    const WITH_MEDIA    = 2;
    const WITH_SUPPORTS = 4;
    const WITH_ALL      = 7;

    const SOURCE_MAP_NONE   = 0;
    const SOURCE_MAP_INLINE = 1;
    const SOURCE_MAP_FILE   = 2;

    /**
     * @var array
     */
    static protected $operatorNames = [
        '+'   => 'add',
        '-'   => 'sub',
        '*'   => 'mul',
        '/'   => 'div',
        '%'   => 'mod',

        '=='  => 'eq',
        '!='  => 'neq',
        '<'   => 'lt',
        '>'   => 'gt',

        '<='  => 'lte',
        '>='  => 'gte',
        '<=>' => 'cmp',
    ];

    /**
     * @var array
     */
    static protected $namespaces = [
        'special'  => '%',
        'mixin'    => '@',
        'function' => '^',
    ];

    static public $true         = [Type::T_KEYWORD, 'true'];
    static public $false        = [Type::T_KEYWORD, 'false'];
    static public $null         = [Type::T_NULL];
    static public $nullString   = [Type::T_STRING, '', []];
    static public $defaultValue = [Type::T_KEYWORD, ''];
    static public $selfSelector = [Type::T_SELF];
    static public $emptyList    = [Type::T_LIST, '', []];
    static public $emptyMap     = [Type::T_MAP, [], []];
    static public $emptyString  = [Type::T_STRING, '"', []];
    static public $with         = [Type::T_KEYWORD, 'with'];
    static public $without      = [Type::T_KEYWORD, 'without'];

    protected $importPaths = [''];
    protected $importCache = [];
    protected $importedFiles = [];
    protected $userFunctions = [];
    protected $registeredVars = [];
    protected $registeredFeatures = [
        'extend-selector-pseudoclass' => false,
        'at-error'                    => true,
        'units-level-3'               => false,
        'global-variable-shadowing'   => false,
    ];

    protected $encoding = null;
    protected $lineNumberStyle = null;

    protected $sourceMap = self::SOURCE_MAP_NONE;
    protected $sourceMapOptions = [];

    /**
     * @var string|\Leafo\ScssPhp\Formatter
     */
    protected $formatter = 'Leafo\ScssPhp\Formatter\Nested';

    protected $rootEnv;
    protected $rootBlock;

    /**
     * @var \Leafo\ScssPhp\Compiler\Environment
     */
    protected $env;
    protected $scope;
    protected $storeEnv;
    protected $charsetSeen;
    protected $sourceNames;

    protected $cache;

    protected $indentLevel;
    protected $extends;
    protected $extendsMap;
    protected $parsedFiles;
    protected $parser;
    protected $sourceIndex;
    protected $sourceLine;
    protected $sourceColumn;
    protected $stderr;
    protected $shouldEvaluate;
    protected $ignoreErrors;

    protected $callStack = [];

    /**
     * Constructor
     */
    public function __construct($cacheOptions = null)
    {
        $this->parsedFiles = [];
        $this->sourceNames = [];

        if ($cacheOptions) {
            $this->cache = new Cache($cacheOptions);
        }
    }

    public function getCompileOptions()
    {
        $options = [
            'importPaths'        => $this->importPaths,
            'registeredVars'     => $this->registeredVars,
            'registeredFeatures' =>
$this->registeredFeatures,
            'encoding'           => $this->encoding,
            'sourceMap'          =>
serialize($this->sourceMap),
            'sourceMapOptions'   =>
$this->sourceMapOptions,
            'formatter'          => $this->formatter,
        ];

        return $options;
    }

    /**
     * Compile scss
     *
     * @api
     *
     * @param string $code
     * @param string $path
     *
     * @return string
     */
    public function compile($code, $path = null)
    {
        if ($this->cache) {
            $cacheKey = ($path ? $path : "(stdin)") .
":" . md5($code);
            $compileOptions = $this->getCompileOptions();
            $cache = $this->cache->getCache("compile",
$cacheKey, $compileOptions);

            if (is_array($cache)
                && isset($cache['dependencies'])
                && isset($cache['out'])
            ) {
                // check if any dependency file changed before accepting
the cache
                foreach ($cache['dependencies'] as $file =>
$mtime) {
                    if (! file_exists($file)
                        || filemtime($file) !== $mtime
                    ) {
                        unset($cache);
                        break;
                    }
                }

                if (isset($cache)) {
                    return $cache['out'];
                }
            }
        }


        $this->indentLevel    = -1;
        $this->extends        = [];
        $this->extendsMap     = [];
        $this->sourceIndex    = null;
        $this->sourceLine     = null;
        $this->sourceColumn   = null;
        $this->env            = null;
        $this->scope          = null;
        $this->storeEnv       = null;
        $this->charsetSeen    = null;
        $this->shouldEvaluate = null;
        $this->stderr         = fopen('php://stderr',
'w');

        $this->parser = $this->parserFactory($path);
        $tree = $this->parser->parse($code);
        $this->parser = null;

        $this->formatter = new $this->formatter();
        $this->rootBlock = null;
        $this->rootEnv   = $this->pushEnv($tree);

        $this->injectVariables($this->registeredVars);
        $this->compileRoot($tree);
        $this->popEnv();

        $sourceMapGenerator = null;

        if ($this->sourceMap) {
            if (is_object($this->sourceMap) &&
$this->sourceMap instanceof SourceMapGenerator) {
                $sourceMapGenerator = $this->sourceMap;
                $this->sourceMap = self::SOURCE_MAP_FILE;
            } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) {
                $sourceMapGenerator = new
SourceMapGenerator($this->sourceMapOptions);
            }
        }

        $out = $this->formatter->format($this->scope,
$sourceMapGenerator);

        if (! empty($out) && $this->sourceMap &&
$this->sourceMap !== self::SOURCE_MAP_NONE) {
            $sourceMap    = $sourceMapGenerator->generateJson();
            $sourceMapUrl = null;

            switch ($this->sourceMap) {
                case self::SOURCE_MAP_INLINE:
                    $sourceMapUrl =
sprintf('data:application/json,%s',
Util::encodeURIComponent($sourceMap));
                    break;

                case self::SOURCE_MAP_FILE:
                    $sourceMapUrl =
$sourceMapGenerator->saveMap($sourceMap);
                    break;
            }

            $out .= sprintf('/*# sourceMappingURL=%s */',
$sourceMapUrl);
        }

        if ($this->cache && isset($cacheKey) &&
isset($compileOptions)) {
            $v = [
                'dependencies' => $this->getParsedFiles(),
                'out' => &$out,
            ];

            $this->cache->setCache("compile", $cacheKey,
$v, $compileOptions);
        }

        return $out;
    }

    /**
     * Instantiate parser
     *
     * @param string $path
     *
     * @return \Leafo\ScssPhp\Parser
     */
    protected function parserFactory($path)
    {
        $parser = new Parser($path, count($this->sourceNames),
$this->encoding, $this->cache);

        $this->sourceNames[] = $path;
        $this->addParsedFile($path);

        return $parser;
    }

    /**
     * Is self extend?
     *
     * @param array $target
     * @param array $origin
     *
     * @return boolean
     */
    protected function isSelfExtend($target, $origin)
    {
        foreach ($origin as $sel) {
            if (in_array($target, $sel)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Push extends
     *
     * @param array     $target
     * @param array     $origin
     * @param \stdClass $block
     */
    protected function pushExtends($target, $origin, $block)
    {
        if ($this->isSelfExtend($target, $origin)) {
            return;
        }

        $i = count($this->extends);
        $this->extends[] = [$target, $origin, $block];

        foreach ($target as $part) {
            if (isset($this->extendsMap[$part])) {
                $this->extendsMap[$part][] = $i;
            } else {
                $this->extendsMap[$part] = [$i];
            }
        }
    }

    /**
     * Make output block
     *
     * @param string $type
     * @param array  $selectors
     *
     * @return \Leafo\ScssPhp\Formatter\OutputBlock
     */
    protected function makeOutputBlock($type, $selectors = null)
    {
        $out = new OutputBlock;
        $out->type         = $type;
        $out->lines        = [];
        $out->children     = [];
        $out->parent       = $this->scope;
        $out->selectors    = $selectors;
        $out->depth        = $this->env->depth;

        if ($this->env->block instanceof Block) {
            $out->sourceName   = $this->env->block->sourceName;
            $out->sourceLine   = $this->env->block->sourceLine;
            $out->sourceColumn =
$this->env->block->sourceColumn;
        } else {
            $out->sourceName   = null;
            $out->sourceLine   = null;
            $out->sourceColumn = null;
        }

        return $out;
    }

    /**
     * Compile root
     *
     * @param \Leafo\ScssPhp\Block $rootBlock
     */
    protected function compileRoot(Block $rootBlock)
    {
        $this->rootBlock = $this->scope =
$this->makeOutputBlock(Type::T_ROOT);

        $this->compileChildrenNoReturn($rootBlock->children,
$this->scope);
        $this->flattenSelectors($this->scope);
        $this->missingSelectors();
    }

    /**
     * Report missing selectors
     */
    protected function missingSelectors()
    {
        foreach ($this->extends as $extend) {
            if (isset($extend[3])) {
                continue;
            }

            list($target, $origin, $block) = $extend;

            // ignore if !optional
            if ($block[2]) {
                continue;
            }

            $target = implode(' ', $target);
            $origin = $this->collapseSelectors($origin);

            $this->sourceLine = $block[Parser::SOURCE_LINE];
            $this->throwError("\"$origin\" failed to
@extend \"$target\". The selector \"$target\" was not
found.");
        }
    }

    /**
     * Flatten selectors
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
     * @param string                               $parentKey
     */
    protected function flattenSelectors(OutputBlock $block, $parentKey =
null)
    {
        if ($block->selectors) {
            $selectors = [];

            foreach ($block->selectors as $s) {
                $selectors[] = $s;

                if (! is_array($s)) {
                    continue;
                }

                // check extends
                if (! empty($this->extendsMap)) {
                    $this->matchExtends($s, $selectors);

                    // remove duplicates
                    array_walk($selectors, function (&$value) {
                        $value = serialize($value);
                    });

                    $selectors = array_unique($selectors);

                    array_walk($selectors, function (&$value) {
                        $value = unserialize($value);
                    });
                }
            }

            $block->selectors = [];
            $placeholderSelector = false;

            foreach ($selectors as $selector) {
                if ($this->hasSelectorPlaceholder($selector)) {
                    $placeholderSelector = true;
                    continue;
                }

                $block->selectors[] =
$this->compileSelector($selector);
            }

            if ($placeholderSelector && 0 ===
count($block->selectors) && null !== $parentKey) {
                unset($block->parent->children[$parentKey]);

                return;
            }
        }

        foreach ($block->children as $key => $child) {
            $this->flattenSelectors($child, $key);
        }
    }

    /**
     * Glue parts of :not( or :nth-child( ... that are in general splitted
in selectors parts
     *
     * @param array $parts
     *
     * @return array
     */
    protected function glueFunctionSelectors($parts)
    {
        $new = [];

        foreach ($parts as $part) {
            if (is_array($part)) {
                $part = $this->glueFunctionSelectors($part);
                $new[] = $part;
            } else {
                // a selector part finishing with a ) is the last part of a
:not( or :nth-child(
                // and need to be joined to this
                if (count($new) && is_string($new[count($new) - 1])
                    && strlen($part) && substr($part, -1)
=== ')' && strpos($part, '(') === false
                ) {
                    $new[count($new) - 1] .= $part;
                } else {
                    $new[] = $part;
                }
            }
        }

        return $new;
    }

    /**
     * Match extends
     *
     * @param array   $selector
     * @param array   $out
     * @param integer $from
     * @param boolean $initial
     */
    protected function matchExtends($selector, &$out, $from = 0,
$initial = true)
    {
        static $partsPile = [];

        $selector = $this->glueFunctionSelectors($selector);

        foreach ($selector as $i => $part) {
            if ($i < $from) {
                continue;
            }

            // check that we are not building an infinite loop of
extensions
            // if the new part is just including a previous part don't
try to extend anymore
            if (count($part) > 1) {
                foreach ($partsPile as $previousPart) {
                    if (! count(array_diff($previousPart, $part))) {
                        continue 2;
                    }
                }
            }

            if ($this->matchExtendsSingle($part, $origin)) {
                $partsPile[] = $part;
                $after       = array_slice($selector, $i + 1);
                $before      = array_slice($selector, 0, $i);

                list($before, $nonBreakableBefore) =
$this->extractRelationshipFromFragment($before);

                foreach ($origin as $new) {
                    $k = 0;

                    // remove shared parts
                    if (count($new) > 1) {
                        while ($k < $i && isset($new[$k])
&& $selector[$k] === $new[$k]) {
                            $k++;
                        }
                    }

                    $replacement = [];
                    $tempReplacement = $k > 0 ? array_slice($new, $k) :
$new;

                    for ($l = count($tempReplacement) - 1; $l >= 0;
$l--) {
                        $slice = [];

                        foreach ($tempReplacement[$l] as $chunk) {
                            if (! in_array($chunk, $slice)) {
                                $slice[] = $chunk;
                            }
                        }

                        array_unshift($replacement, $slice);

                        if (!
$this->isImmediateRelationshipCombinator(end($slice))) {
                            break;
                        }
                    }

                    $afterBefore = $l != 0 ? array_slice($tempReplacement,
0, $l) : [];

                    // Merge shared direct relationships.
                    $mergedBefore =
$this->mergeDirectRelationships($afterBefore, $nonBreakableBefore);

                    $result = array_merge(
                        $before,
                        $mergedBefore,
                        $replacement,
                        $after
                    );

                    if ($result === $selector) {
                        continue;
                    }

                    $out[] = $result;

                    // recursively check for more matches
                    $startRecurseFrom = count($before) +
min(count($nonBreakableBefore), count($mergedBefore));
                    $this->matchExtends($result, $out,
$startRecurseFrom, false);

                    // selector sequence merging
                    if (! empty($before) && count($new) > 1) {
                        $preSharedParts = $k > 0 ? array_slice($before,
0, $k) : [];
                        $postSharedParts = $k > 0 ? array_slice($before,
$k) : $before;

                        list($betweenSharedParts, $nonBreakable2) =
$this->extractRelationshipFromFragment($afterBefore);

                        $result2 = array_merge(
                            $preSharedParts,
                            $betweenSharedParts,
                            $postSharedParts,
                            $nonBreakable2,
                            $nonBreakableBefore,
                            $replacement,
                            $after
                        );

                        $out[] = $result2;
                    }
                }

                array_pop($partsPile);
            }
        }
    }

    /**
     * Match extends single
     *
     * @param array $rawSingle
     * @param array $outOrigin
     *
     * @return boolean
     */
    protected function matchExtendsSingle($rawSingle, &$outOrigin)
    {
        $counts = [];
        $single = [];

        // simple usual cases, no need to do the whole trick
        if (in_array($rawSingle,
[['>'],['+'],['~']])) {
            return false;
        }

        foreach ($rawSingle as $part) {
            // matches Number
            if (! is_string($part)) {
                return false;
            }

            if (! preg_match('/^[\[.:#%]/', $part) &&
count($single)) {
                $single[count($single) - 1] .= $part;
            } else {
                $single[] = $part;
            }
        }

        $extendingDecoratedTag = false;

        if (count($single) > 1) {
            $matches = null;
            $extendingDecoratedTag = preg_match('/^[a-z0-9]+$/i',
$single[0], $matches) ? $matches[0] : false;
        }

        foreach ($single as $part) {
            if (isset($this->extendsMap[$part])) {
                foreach ($this->extendsMap[$part] as $idx) {
                    $counts[$idx] = isset($counts[$idx]) ? $counts[$idx] +
1 : 1;
                }
            }
        }

        $outOrigin = [];
        $found = false;

        foreach ($counts as $idx => $count) {
            list($target, $origin, /* $block */) = $this->extends[$idx];

            $origin = $this->glueFunctionSelectors($origin);

            // check count
            if ($count !== count($target)) {
                continue;
            }

            $this->extends[$idx][3] = true;

            $rem = array_diff($single, $target);

            foreach ($origin as $j => $new) {
                // prevent infinite loop when target extends itself
                if ($this->isSelfExtend($single, $origin)) {
                    return false;
                }

                $replacement = end($new);

                // Extending a decorated tag with another tag is not
possible.
                if ($extendingDecoratedTag && $replacement[0] !=
$extendingDecoratedTag &&
                    preg_match('/^[a-z0-9]+$/i', $replacement[0])
                ) {
                    unset($origin[$j]);
                    continue;
                }

                $combined = $this->combineSelectorSingle($replacement,
$rem);

                if (count(array_diff($combined,
$origin[$j][count($origin[$j]) - 1]))) {
                    $origin[$j][count($origin[$j]) - 1] = $combined;
                }
            }

            $outOrigin = array_merge($outOrigin, $origin);

            $found = true;
        }

        return $found;
    }

    /**
     * Extract a relationship from the fragment.
     *
     * When extracting the last portion of a selector we will be left with
a
     * fragment which may end with a direction relationship combinator.
This
     * method will extract the relationship fragment and return it along
side
     * the rest.
     *
     * @param array $fragment The selector fragment maybe ending with a
direction relationship combinator.
     *
     * @return array The selector without the relationship fragment if any,
the relationship fragment.
     */
    protected function extractRelationshipFromFragment(array $fragment)
    {
        $parents = [];
        $children = [];
        $j = $i = count($fragment);

        for (;;) {
            $children = $j != $i ? array_slice($fragment, $j, $i - $j) :
[];
            $parents = array_slice($fragment, 0, $j);
            $slice = end($parents);

            if (empty($slice) || !
$this->isImmediateRelationshipCombinator($slice[0])) {
                break;
            }

            $j -= 2;
        }

        return [$parents, $children];
    }

    /**
     * Combine selector single
     *
     * @param array $base
     * @param array $other
     *
     * @return array
     */
    protected function combineSelectorSingle($base, $other)
    {
        $tag = [];
        $out = [];
        $wasTag = true;

        foreach ([$base, $other] as $single) {
            foreach ($single as $part) {
                if (preg_match('/^[\[.:#]/', $part)) {
                    $out[] = $part;
                    $wasTag = false;
                } elseif (preg_match('/^[^_-]/', $part)) {
                    $tag[] = $part;
                    $wasTag = true;
                } elseif ($wasTag) {
                    $tag[count($tag) - 1] .= $part;
                } else {
                    $out[count($out) - 1] .= $part;
                }
            }
        }

        if (count($tag)) {
            array_unshift($out, $tag[0]);
        }

        return $out;
    }

    /**
     * Compile media
     *
     * @param \Leafo\ScssPhp\Block $media
     */
    protected function compileMedia(Block $media)
    {
        $this->pushEnv($media);

        $mediaQueries =
$this->compileMediaQuery($this->multiplyMedia($this->env));

        if (! empty($mediaQueries) && $mediaQueries) {
            $previousScope = $this->scope;
            $parentScope = $this->mediaParent($this->scope);

            foreach ($mediaQueries as $mediaQuery) {
                $this->scope = $this->makeOutputBlock(Type::T_MEDIA,
[$mediaQuery]);

                $parentScope->children[] = $this->scope;
                $parentScope = $this->scope;
            }

            // top level properties in a media cause it to be wrapped
            $needsWrap = false;

            foreach ($media->children as $child) {
                $type = $child[0];

                if ($type !== Type::T_BLOCK &&
                    $type !== Type::T_MEDIA &&
                    $type !== Type::T_DIRECTIVE &&
                    $type !== Type::T_IMPORT
                ) {
                    $needsWrap = true;
                    break;
                }
            }

            if ($needsWrap) {
                $wrapped = new Block;
                $wrapped->sourceName = $media->sourceName;
                $wrapped->sourceIndex = $media->sourceIndex;
                $wrapped->sourceLine = $media->sourceLine;
                $wrapped->sourceColumn = $media->sourceColumn;
                $wrapped->selectors = [];
                $wrapped->comments = [];
                $wrapped->parent = $media;
                $wrapped->children = $media->children;

                $media->children = [[Type::T_BLOCK, $wrapped]];
                if (isset($this->lineNumberStyle)) {
                    $annotation =
$this->makeOutputBlock(Type::T_COMMENT);
                    $annotation->depth = 0;

                    $file = $this->sourceNames[$media->sourceIndex];
                    $line = $media->sourceLine;

                    switch ($this->lineNumberStyle) {
                        case static::LINE_COMMENTS:
                            $annotation->lines[] = '/* line '
. $line
                                                 . ($file ? ', '
. $file : '')
                                                 . ' */';
                            break;

                        case static::DEBUG_INFO:
                            $annotation->lines[] = '@media
-sass-debug-info{'
                                                 . ($file ?
'filename{font-family:"' . $file . '"}' :
'')
                                                 .
'line{font-family:' . $line . '}}';
                            break;
                    }

                    $this->scope->children[] = $annotation;
                }
            }

            $this->compileChildrenNoReturn($media->children,
$this->scope);

            $this->scope = $previousScope;
        }

        $this->popEnv();
    }

    /**
     * Media parent
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     *
     * @return \Leafo\ScssPhp\Formatter\OutputBlock
     */
    protected function mediaParent(OutputBlock $scope)
    {
        while (! empty($scope->parent)) {
            if (! empty($scope->type) && $scope->type !==
Type::T_MEDIA) {
                break;
            }

            $scope = $scope->parent;
        }

        return $scope;
    }

    /**
     * Compile directive
     *
     * @param \Leafo\ScssPhp\Block $block
     */
    protected function compileDirective(Block $block)
    {
        $s = '@' . $block->name;

        if (! empty($block->value)) {
            $s .= ' ' . $this->compileValue($block->value);
        }

        if ($block->name === 'keyframes' ||
substr($block->name, -10) === '-keyframes') {
            $this->compileKeyframeBlock($block, [$s]);
        } else {
            $this->compileNestedBlock($block, [$s]);
        }
    }

    /**
     * Compile at-root
     *
     * @param \Leafo\ScssPhp\Block $block
     */
    protected function compileAtRoot(Block $block)
    {
        $env     = $this->pushEnv($block);
        $envs    = $this->compactEnv($env);
        $without = isset($block->with) ?
$this->compileWith($block->with) : static::WITH_RULE;

        // wrap inline selector
        if ($block->selector) {
            $wrapped = new Block;
            $wrapped->sourceName   = $block->sourceName;
            $wrapped->sourceIndex  = $block->sourceIndex;
            $wrapped->sourceLine   = $block->sourceLine;
            $wrapped->sourceColumn = $block->sourceColumn;
            $wrapped->selectors    = $block->selector;
            $wrapped->comments     = [];
            $wrapped->parent       = $block;
            $wrapped->children     = $block->children;
            $wrapped->selfParent   = $block->selfParent;

            $block->children = [[Type::T_BLOCK, $wrapped]];
            $block->selector = null;
        }

        $selfParent = $block->selfParent;

        if (! $block->selfParent->selectors &&
isset($block->parent) && $block->parent &&
            isset($block->parent->selectors) &&
$block->parent->selectors
        ) {
            $selfParent = $block->parent;
        }

        $this->env = $this->filterWithout($envs, $without);

        $saveScope   = $this->scope;
        $this->scope = $this->filterScopeWithout($saveScope,
$without);

        // propagate selfParent to the children where they still can be
useful
        $this->compileChildrenNoReturn($block->children,
$this->scope, $selfParent);

        $this->scope = $this->completeScope($this->scope,
$saveScope);
        $this->scope = $saveScope;
        $this->env   = $this->extractEnv($envs);

        $this->popEnv();
    }

    /**
     * Filter at-root scope depending of with/without option
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     * @param mixed                                $without
     *
     * @return mixed
     */
    protected function filterScopeWithout($scope, $without)
    {
        $filteredScopes = [];

        if ($scope->type === TYPE::T_ROOT) {
            return $scope;
        }

        // start from the root
        while ($scope->parent && $scope->parent->type !==
TYPE::T_ROOT) {
            $scope = $scope->parent;
        }

        for (;;) {
            if (! $scope) {
                break;
            }

            if (! $this->isWithout($without, $scope)) {
                $s = clone $scope;
                $s->children = [];
                $s->lines = [];
                $s->parent = null;

                if ($s->type !== Type::T_MEDIA && $s->type
!== Type::T_DIRECTIVE) {
                    $s->selectors = [];
                }

                $filteredScopes[] = $s;
            }

            if ($scope->children) {
                $scope = end($scope->children);
            } else {
                $scope = null;
            }
        }

        if (! count($filteredScopes)) {
            return $this->rootBlock;
        }

        $newScope = array_shift($filteredScopes);
        $newScope->parent = $this->rootBlock;

        $this->rootBlock->children[] = $newScope;

        $p = &$newScope;

        while (count($filteredScopes)) {
            $s = array_shift($filteredScopes);
            $s->parent = $p;
            $p->children[] = &$s;
            $p = $s;
        }

        return $newScope;
    }

    /**
     * found missing selector from a at-root compilation in the previous
scope
     * (if at-root is just enclosing a property, the selector is in the
parent tree)
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $previousScope
     *
     * @return mixed
     */
    protected function completeScope($scope, $previousScope)
    {
        if (! $scope->type && (! $scope->selectors || !
count($scope->selectors)) && count($scope->lines)) {
            $scope->selectors =
$this->findScopeSelectors($previousScope, $scope->depth);
        }

        if ($scope->children) {
            foreach ($scope->children as $k => $c) {
                $scope->children[$k] = $this->completeScope($c,
$previousScope);
            }
        }

        return $scope;
    }

    /**
     * Find a selector by the depth node in the scope
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     * @param integer                              $depth
     *
     * @return array
     */
    protected function findScopeSelectors($scope, $depth)
    {
        if ($scope->depth === $depth && $scope->selectors) {
            return $scope->selectors;
        }

        if ($scope->children) {
            foreach (array_reverse($scope->children) as $c) {
                if ($s = $this->findScopeSelectors($c, $depth)) {
                    return $s;
                }
            }
        }

        return [];
    }

    /**
     * Compile @at-root's with: inclusion / without: exclusion into
filter flags
     *
     * @param array $with
     *
     * @return integer
     */
    protected function compileWith($with)
    {
        static $mapping = [
            'rule'     => self::WITH_RULE,
            'media'    => self::WITH_MEDIA,
            'supports' => self::WITH_SUPPORTS,
            'all'      => self::WITH_ALL,
        ];

        // exclude selectors by default
        $without = static::WITH_RULE;

        if ($this->libMapHasKey([$with, static::$with])) {
            $without = static::WITH_ALL;

            $list = $this->coerceList($this->libMapGet([$with,
static::$with]));

            foreach ($list[2] as $item) {
                $keyword =
$this->compileStringContent($this->coerceString($item));

                if (array_key_exists($keyword, $mapping)) {
                    $without &= ~($mapping[$keyword]);
                }
            }
        }

        if ($this->libMapHasKey([$with, static::$without])) {
            $without = 0;

            $list = $this->coerceList($this->libMapGet([$with,
static::$without]));

            foreach ($list[2] as $item) {
                $keyword =
$this->compileStringContent($this->coerceString($item));

                if (array_key_exists($keyword, $mapping)) {
                    $without |= $mapping[$keyword];
                }
            }
        }

        return $without;
    }

    /**
     * Filter env stack
     *
     * @param array   $envs
     * @param integer $without
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function filterWithout($envs, $without)
    {
        $filtered = [];

        foreach ($envs as $e) {
            if ($e->block && $this->isWithout($without,
$e->block)) {
                $ec = clone $e;
                $ec->block = null;
                $ec->selectors = [];
                $filtered[] = $ec;
            } else {
                $filtered[] = $e;
            }
        }

        return $this->extractEnv($filtered);
    }

    /**
     * Filter WITH rules
     *
     * @param integer                                                  
$without
     * @param \Leafo\ScssPhp\Block|\Leafo\ScssPhp\Formatter\OutputBlock
$block
     *
     * @return boolean
     */
    protected function isWithout($without, $block)
    {
        if (isset($block->type)) {
            if ($block->type === Type::T_MEDIA) {
                return ($without & static::WITH_MEDIA) ? true : false;
            }

            if ($block->type === Type::T_DIRECTIVE) {
                if (isset($block->name) && $block->name ===
'supports') {
                    return ($without & static::WITH_SUPPORTS) ? true :
false;
                }

                if (isset($block->selectors) &&
strpos(serialize($block->selectors), '@supports') !== false) {
                    return ($without & static::WITH_SUPPORTS) ? true :
false;
                }
            }
        }

        if ((($without & static::WITH_RULE) &&
isset($block->selectors))) {
            return true;
        }

        return false;
    }

    /**
     * Compile keyframe block
     *
     * @param \Leafo\ScssPhp\Block $block
     * @param array                $selectors
     */
    protected function compileKeyframeBlock(Block $block, $selectors)
    {
        $env = $this->pushEnv($block);

        $envs = $this->compactEnv($env);

        $this->env = $this->extractEnv(array_filter($envs, function
(Environment $e) {
            return ! isset($e->block->selectors);
        }));

        $this->scope = $this->makeOutputBlock($block->type,
$selectors);
        $this->scope->depth = 1;
        $this->scope->parent->children[] = $this->scope;

        $this->compileChildrenNoReturn($block->children,
$this->scope);

        $this->scope = $this->scope->parent;
        $this->env   = $this->extractEnv($envs);

        $this->popEnv();
    }

    /**
     * Compile nested block
     *
     * @param \Leafo\ScssPhp\Block $block
     * @param array                $selectors
     */
    protected function compileNestedBlock(Block $block, $selectors)
    {
        $this->pushEnv($block);

        $this->scope = $this->makeOutputBlock($block->type,
$selectors);
        $this->scope->parent->children[] = $this->scope;

        // wrap assign children in a block
        // except for @font-face
        if ($block->type !== Type::T_DIRECTIVE || $block->name !==
"font-face") {
            // need wrapping?
            $needWrapping = false;

            foreach ($block->children as $child) {
                if ($child[0] === Type::T_ASSIGN) {
                    $needWrapping = true;
                    break;
                }
            }

            if ($needWrapping) {
                $wrapped = new Block;
                $wrapped->sourceName = $block->sourceName;
                $wrapped->sourceIndex = $block->sourceIndex;
                $wrapped->sourceLine = $block->sourceLine;
                $wrapped->sourceColumn = $block->sourceColumn;
                $wrapped->selectors = [];
                $wrapped->comments = [];
                $wrapped->parent = $block;
                $wrapped->children = $block->children;
                $wrapped->selfParent = $block->selfParent;

                $block->children = [[Type::T_BLOCK, $wrapped]];
            }
        }

        $this->compileChildrenNoReturn($block->children,
$this->scope);

        $this->scope = $this->scope->parent;

        $this->popEnv();
    }

    /**
     * Recursively compiles a block.
     *
     * A block is analogous to a CSS block in most cases. A single SCSS
document
     * is encapsulated in a block when parsed, but it does not have parent
tags
     * so all of its children appear on the root level when compiled.
     *
     * Blocks are made up of selectors and children.
     *
     * The children of a block are just all the blocks that are defined
within.
     *
     * Compiling the block involves pushing a fresh environment on the
stack,
     * and iterating through the props, compiling each one.
     *
     * @see Compiler::compileChild()
     *
     * @param \Leafo\ScssPhp\Block $block
     */
    protected function compileBlock(Block $block)
    {
        $env = $this->pushEnv($block);
        $env->selectors = $this->evalSelectors($block->selectors);

        $out = $this->makeOutputBlock(null);

        if (isset($this->lineNumberStyle) &&
count($env->selectors) && count($block->children)) {
            $annotation = $this->makeOutputBlock(Type::T_COMMENT);
            $annotation->depth = 0;

            $file = $this->sourceNames[$block->sourceIndex];
            $line = $block->sourceLine;

            switch ($this->lineNumberStyle) {
                case static::LINE_COMMENTS:
                    $annotation->lines[] = '/* line ' . $line
                                         . ($file ? ', ' . $file
: '')
                                         . ' */';
                    break;

                case static::DEBUG_INFO:
                    $annotation->lines[] = '@media
-sass-debug-info{'
                                         . ($file ?
'filename{font-family:"' . $file . '"}' :
'')
                                         . 'line{font-family:' .
$line . '}}';
                    break;
            }

            $this->scope->children[] = $annotation;
        }

        $this->scope->children[] = $out;

        if (count($block->children)) {
            $out->selectors = $this->multiplySelectors($env,
$block->selfParent);

            // propagate selfParent to the children where they still can be
useful
            $selfParentSelectors = null;

            if (isset($block->selfParent->selectors)) {
                $selfParentSelectors = $block->selfParent->selectors;
                $block->selfParent->selectors = $out->selectors;
            }

            $this->compileChildrenNoReturn($block->children, $out,
$block->selfParent);

            // and revert for the following childs of the same block
            if ($selfParentSelectors) {
                $block->selfParent->selectors = $selfParentSelectors;
            }
        }

        $this->formatter->stripSemicolon($out->lines);

        $this->popEnv();
    }

    /**
     * Compile root level comment
     *
     * @param array $block
     */
    protected function compileComment($block)
    {
        $out = $this->makeOutputBlock(Type::T_COMMENT);
        $out->lines[] = is_string($block[1]) ? $block[1] :
$this->compileValue($block[1]);

        $this->scope->children[] = $out;
    }

    /**
     * Evaluate selectors
     *
     * @param array $selectors
     *
     * @return array
     */
    protected function evalSelectors($selectors)
    {
        $this->shouldEvaluate = false;

        $selectors = array_map([$this, 'evalSelector'],
$selectors);

        // after evaluating interpolates, we might need a second pass
        if ($this->shouldEvaluate) {
            $selectors = $this->revertSelfSelector($selectors);
            $buffer = $this->collapseSelectors($selectors);
            $parser = $this->parserFactory(__METHOD__);

            if ($parser->parseSelector($buffer, $newSelectors)) {
                $selectors = array_map([$this, 'evalSelector'],
$newSelectors);
            }
        }

        return $selectors;
    }

    /**
     * Evaluate selector
     *
     * @param array $selector
     *
     * @return array
     */
    protected function evalSelector($selector)
    {
        return array_map([$this, 'evalSelectorPart'], $selector);
    }

    /**
     * Evaluate selector part; replaces all the interpolates, stripping
quotes
     *
     * @param array $part
     *
     * @return array
     */
    protected function evalSelectorPart($part)
    {
        foreach ($part as &$p) {
            if (is_array($p) && ($p[0] === Type::T_INTERPOLATE ||
$p[0] === Type::T_STRING)) {
                $p = $this->compileValue($p);

                // force re-evaluation
                if (strpos($p, '&') !== false || strpos($p,
',') !== false) {
                    $this->shouldEvaluate = true;
                }
            } elseif (is_string($p) && strlen($p) >= 2
&&
                ($first = $p[0]) && ($first === '"'
|| $first === "'") &&
                substr($p, -1) === $first
            ) {
                $p = substr($p, 1, -1);
            }
        }

        return $this->flattenSelectorSingle($part);
    }

    /**
     * Collapse selectors
     *
     * @param array   $selectors
     * @param boolean $selectorFormat
     *   if false return a collapsed string
     *   if true return an array description of a structured selector
     *
     * @return string
     */
    protected function collapseSelectors($selectors, $selectorFormat =
false)
    {
        $parts = [];

        foreach ($selectors as $selector) {
            $output = [];
            $glueNext = false;

            foreach ($selector as $node) {
                $compound = '';

                array_walk_recursive(
                    $node,
                    function ($value, $key) use (&$compound) {
                        $compound .= $value;
                    }
                );

                if ($selectorFormat &&
$this->isImmediateRelationshipCombinator($compound)) {
                    if (count($output)) {
                        $output[count($output) - 1] .= ' ' .
$compound;
                    } else {
                        $output[] = $compound;
                    }
                    $glueNext = true;
                } elseif ($glueNext) {
                    $output[count($output) - 1] .= ' ' .
$compound;
                    $glueNext = false;
                } else {
                    $output[] = $compound;
                }
            }

            if ($selectorFormat) {
                foreach ($output as &$o) {
                    $o = [Type::T_STRING, '', [$o]];
                }
                $output = [Type::T_LIST, ' ', $output];
            } else {
                $output = implode(' ', $output);
            }

            $parts[] = $output;
        }

        if ($selectorFormat) {
            $parts = [Type::T_LIST, ',', $parts];
        } else {
            $parts = implode(', ', $parts);
        }

        return $parts;
    }

    /**
     * Parse down the selector and revert [self] to "&"
before a reparsing
     *
     * @param array $selectors
     *
     * @return array
     */
    protected function revertSelfSelector($selectors)
    {
        foreach ($selectors as &$part) {
            if (is_array($part)) {
                if ($part === [Type::T_SELF]) {
                    $part = '&';
                } else {
                    $part = $this->revertSelfSelector($part);
                }
            }
        }

        return $selectors;
    }

    /**
     * Flatten selector single; joins together .classes and #ids
     *
     * @param array $single
     *
     * @return array
     */
    protected function flattenSelectorSingle($single)
    {
        $joined = [];

        foreach ($single as $part) {
            if (empty($joined) ||
                ! is_string($part) ||
                preg_match('/[\[.:#%]/', $part)
            ) {
                $joined[] = $part;
                continue;
            }

            if (is_array(end($joined))) {
                $joined[] = $part;
            } else {
                $joined[count($joined) - 1] .= $part;
            }
        }

        return $joined;
    }

    /**
     * Compile selector to string; self(&) should have been replaced by
now
     *
     * @param string|array $selector
     *
     * @return string
     */
    protected function compileSelector($selector)
    {
        if (! is_array($selector)) {
            return $selector; // media and the like
        }

        return implode(
            ' ',
            array_map(
                [$this, 'compileSelectorPart'],
                $selector
            )
        );
    }

    /**
     * Compile selector part
     *
     * @param array $piece
     *
     * @return string
     */
    protected function compileSelectorPart($piece)
    {
        foreach ($piece as &$p) {
            if (! is_array($p)) {
                continue;
            }

            switch ($p[0]) {
                case Type::T_SELF:
                    $p = '&';
                    break;

                default:
                    $p = $this->compileValue($p);
                    break;
            }
        }

        return implode($piece);
    }

    /**
     * Has selector placeholder?
     *
     * @param array $selector
     *
     * @return boolean
     */
    protected function hasSelectorPlaceholder($selector)
    {
        if (! is_array($selector)) {
            return false;
        }

        foreach ($selector as $parts) {
            foreach ($parts as $part) {
                if (strlen($part) && '%' === $part[0]) {
                    return true;
                }
            }
        }

        return false;
    }

    protected function pushCallStack($name = '')
    {
        $this->callStack[] = [
          'n' => $name,
          Parser::SOURCE_INDEX => $this->sourceIndex,
          Parser::SOURCE_LINE => $this->sourceLine,
          Parser::SOURCE_COLUMN => $this->sourceColumn
        ];

        // infinite calling loop
        if (count($this->callStack) > 25000) {
            // not displayed but you can var_dump it to deep debug
            $msg = $this->callStackMessage(true, 100);
            $msg = "Infinite calling loop";
            $this->throwError($msg);
        }
    }

    protected function popCallStack()
    {
        array_pop($this->callStack);
    }

    /**
     * Compile children and return result
     *
     * @param array                                $stms
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     * @param string                               $traceName
     *
     * @return array|null
     */
    protected function compileChildren($stms, OutputBlock $out, $traceName
= '')
    {
        $this->pushCallStack($traceName);

        foreach ($stms as $stm) {
            $ret = $this->compileChild($stm, $out);

            if (isset($ret)) {
                return $ret;
            }
        }

        $this->popCallStack();

        return null;
    }

    /**
     * Compile children and throw exception if unexpected @return
     *
     * @param array                                $stms
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     * @param \Leafo\ScssPhp\Block                 $selfParent
     * @param string                               $traceName
     *
     * @throws \Exception
     */
    protected function compileChildrenNoReturn($stms, OutputBlock $out,
$selfParent = null, $traceName = '')
    {
        $this->pushCallStack($traceName);

        foreach ($stms as $stm) {
            if ($selfParent && isset($stm[1]) &&
is_object($stm[1]) && $stm[1] instanceof Block) {
                $stm[1]->selfParent = $selfParent;
                $ret = $this->compileChild($stm, $out);
                $stm[1]->selfParent = null;
            } elseif ($selfParent && $stm[0] === TYPE::T_INCLUDE) {
                $stm['selfParent'] = $selfParent;
                $ret = $this->compileChild($stm, $out);
                unset($stm['selfParent']);
            } else {
                $ret = $this->compileChild($stm, $out);
            }

            if (isset($ret)) {
                $this->throwError('@return may only be used within
a function');

                return;
            }
        }

        $this->popCallStack();
    }


    /**
     * evaluate media query : compile internal value keeping the structure
inchanged
     *
     * @param array $queryList
     *
     * @return array
     */
    protected function evaluateMediaQuery($queryList)
    {
        foreach ($queryList as $kql => $query) {
            foreach ($query as $kq => $q) {
                for ($i = 1; $i < count($q); $i++) {
                    $value = $this->compileValue($q[$i]);

                    // the parser had no mean to know if media type or
expression if it was an interpolation
                    if ($q[0] == Type::T_MEDIA_TYPE &&
                        (strpos($value, '(') !== false ||
                        strpos($value, ')') !== false ||
                        strpos($value, ':') !== false)
                    ) {
                        $queryList[$kql][$kq][0] =
Type::T_MEDIA_EXPRESSION;

                        if (strpos($value, 'and') !== false) {
                            $values = explode('and', $value);
                            $value = trim(array_pop($values));

                            while ($v = trim(array_pop($values))) {
                                $type = Type::T_MEDIA_EXPRESSION;

                                if (strpos($v, '(') === false
&&
                                    strpos($v, ')') === false
&&
                                    strpos($v, ':') === false
                                ) {
                                    $type = Type::T_MEDIA_TYPE;
                                }

                                if (substr($v, 0, 1) === '('
&& substr($v, -1) === ')') {
                                    $v = substr($v, 1, -1);
                                }

                                $queryList[$kql][] =
[$type,[Type::T_KEYWORD, $v]];
                            }
                        }

                        if (substr($value, 0, 1) === '('
&& substr($value, -1) === ')') {
                            $value = substr($value, 1, -1);
                        }
                    }

                    $queryList[$kql][$kq][$i] = [Type::T_KEYWORD, $value];
                }
            }
        }

        return $queryList;
    }

    /**
     * Compile media query
     *
     * @param array $queryList
     *
     * @return array
     */
    protected function compileMediaQuery($queryList)
    {
        $start = '@media ';
        $default = trim($start);
        $out = [];
        $current = "";

        foreach ($queryList as $query) {
            $type = null;
            $parts = [];

            $mediaTypeOnly = true;

            foreach ($query as $q) {
                if ($q[0] !== Type::T_MEDIA_TYPE) {
                    $mediaTypeOnly = false;
                    break;
                }
            }

            foreach ($query as $q) {
                switch ($q[0]) {
                    case Type::T_MEDIA_TYPE:
                        $newType = array_map([$this,
'compileValue'], array_slice($q, 1));
                        // combining not and anything else than media type
is too risky and should be avoided
                        if (! $mediaTypeOnly) {
                            if (in_array(Type::T_NOT, $newType) || ($type
&& in_array(Type::T_NOT, $type) )) {
                                if ($type) {
                                    array_unshift($parts, implode('
', array_filter($type)));
                                }

                                if (! empty($parts)) {
                                    if (strlen($current)) {
                                        $current .=
$this->formatter->tagSeparator;
                                    }

                                    $current .= implode(' and ',
$parts);
                                }

                                if ($current) {
                                    $out[] = $start . $current;
                                }

                                $current = "";
                                $type = null;
                                $parts = [];
                            }
                        }

                        if ($newType === ['all'] &&
$default) {
                            $default = $start . 'all';
                        }

                        // all can be safely ignored and mixed with
whatever else
                        if ($newType !== ['all']) {
                            if ($type) {
                                $type = $this->mergeMediaTypes($type,
$newType);

                                if (empty($type)) {
                                    // merge failed : ignore this query
that is not valid, skip to the next one
                                    $parts = [];
                                    $default = ''; // if
everything fail, no @media at all
                                    continue 3;
                                }
                            } else {
                                $type = $newType;
                            }
                        }
                        break;

                    case Type::T_MEDIA_EXPRESSION:
                        if (isset($q[2])) {
                            $parts[] = '('
                                . $this->compileValue($q[1])
                                . $this->formatter->assignSeparator
                                . $this->compileValue($q[2])
                                . ')';
                        } else {
                            $parts[] = '('
                                . $this->compileValue($q[1])
                                . ')';
                        }
                        break;

                    case Type::T_MEDIA_VALUE:
                        $parts[] = $this->compileValue($q[1]);
                        break;
                }
            }

            if ($type) {
                array_unshift($parts, implode(' ',
array_filter($type)));
            }

            if (! empty($parts)) {
                if (strlen($current)) {
                    $current .= $this->formatter->tagSeparator;
                }

                $current .= implode(' and ', $parts);
            }
        }

        if ($current) {
            $out[] = $start . $current;
        }

        // no @media type except all, and no conflict?
        if (! $out && $default) {
            $out[] = $default;
        }

        return $out;
    }

    /**
     * Merge direct relationships between selectors
     *
     * @param array $selectors1
     * @param array $selectors2
     *
     * @return array
     */
    protected function mergeDirectRelationships($selectors1, $selectors2)
    {
        if (empty($selectors1) || empty($selectors2)) {
            return array_merge($selectors1, $selectors2);
        }

        $part1 = end($selectors1);
        $part2 = end($selectors2);

        if (! $this->isImmediateRelationshipCombinator($part1[0])
&& $part1 !== $part2) {
            return array_merge($selectors1, $selectors2);
        }

        $merged = [];

        do {
            $part1 = array_pop($selectors1);
            $part2 = array_pop($selectors2);

            if (! $this->isImmediateRelationshipCombinator($part1[0])
&& $part1 !== $part2) {
                if
($this->isImmediateRelationshipCombinator(reset($merged)[0])) {
                    array_unshift($merged, [$part1[0] . $part2[0]]);
                    $merged = array_merge($selectors1, $selectors2,
$merged);
                } else {
                    $merged = array_merge($selectors1, [$part1],
$selectors2, [$part2], $merged);
                }

                break;
            }

            array_unshift($merged, $part1);
        } while (! empty($selectors1) && ! empty($selectors2));

        return $merged;
    }

    /**
     * Merge media types
     *
     * @param array $type1
     * @param array $type2
     *
     * @return array|null
     */
    protected function mergeMediaTypes($type1, $type2)
    {
        if (empty($type1)) {
            return $type2;
        }

        if (empty($type2)) {
            return $type1;
        }

        $m1 = '';
        $t1 = '';

        if (count($type1) > 1) {
            $m1= strtolower($type1[0]);
            $t1= strtolower($type1[1]);
        } else {
            $t1 = strtolower($type1[0]);
        }

        $m2 = '';
        $t2 = '';

        if (count($type2) > 1) {
            $m2 = strtolower($type2[0]);
            $t2 = strtolower($type2[1]);
        } else {
            $t2 = strtolower($type2[0]);
        }

        if (($m1 === Type::T_NOT) ^ ($m2 === Type::T_NOT)) {
            if ($t1 === $t2) {
                return null;
            }

            return [
                $m1 === Type::T_NOT ? $m2 : $m1,
                $m1 === Type::T_NOT ? $t2 : $t1,
            ];
        }

        if ($m1 === Type::T_NOT && $m2 === Type::T_NOT) {
            // CSS has no way of representing "neither screen nor
print"
            if ($t1 !== $t2) {
                return null;
            }

            return [Type::T_NOT, $t1];
        }

        if ($t1 !== $t2) {
            return null;
        }

        // t1 == t2, neither m1 nor m2 are "not"
        return [empty($m1)? $m2 : $m1, $t1];
    }

    /**
     * Compile import; returns true if the value was something that could
be imported
     *
     * @param array                                $rawPath
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     * @param boolean                              $once
     *
     * @return boolean
     */
    protected function compileImport($rawPath, OutputBlock $out, $once =
false)
    {
        if ($rawPath[0] === Type::T_STRING) {
            $path = $this->compileStringContent($rawPath);

            if ($path = $this->findImport($path)) {
                if (! $once || ! in_array($path, $this->importedFiles))
{
                    $this->importFile($path, $out);
                    $this->importedFiles[] = $path;
                }

                return true;
            }

            return false;
        }

        if ($rawPath[0] === Type::T_LIST) {
            // handle a list of strings
            if (count($rawPath[2]) === 0) {
                return false;
            }

            foreach ($rawPath[2] as $path) {
                if ($path[0] !== Type::T_STRING) {
                    return false;
                }
            }

            foreach ($rawPath[2] as $path) {
                $this->compileImport($path, $out);
            }

            return true;
        }

        return false;
    }

    /**
     * Compile child; returns a value to halt execution
     *
     * @param array                                $child
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     *
     * @return array
     */
    protected function compileChild($child, OutputBlock $out)
    {
        if (isset($child[Parser::SOURCE_LINE])) {
            $this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ?
$child[Parser::SOURCE_INDEX] : null;
            $this->sourceLine = isset($child[Parser::SOURCE_LINE]) ?
$child[Parser::SOURCE_LINE] : -1;
            $this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ?
$child[Parser::SOURCE_COLUMN] : -1;
        } elseif (is_array($child) &&
isset($child[1]->sourceLine)) {
            $this->sourceIndex = $child[1]->sourceIndex;
            $this->sourceLine = $child[1]->sourceLine;
            $this->sourceColumn = $child[1]->sourceColumn;
        } elseif (! empty($out->sourceLine) && !
empty($out->sourceName)) {
            $this->sourceLine = $out->sourceLine;
            $this->sourceIndex = array_search($out->sourceName,
$this->sourceNames);

            if ($this->sourceIndex === false) {
                $this->sourceIndex = null;
            }
        }

        switch ($child[0]) {
            case Type::T_SCSSPHP_IMPORT_ONCE:
                $rawPath = $this->reduce($child[1]);

                if (! $this->compileImport($rawPath, $out, true)) {
                    $out->lines[] = '@import ' .
$this->compileValue($rawPath) . ';';
                }
                break;

            case Type::T_IMPORT:
                $rawPath = $this->reduce($child[1]);

                if (! $this->compileImport($rawPath, $out)) {
                    $out->lines[] = '@import ' .
$this->compileValue($rawPath) . ';';
                }
                break;

            case Type::T_DIRECTIVE:
                $this->compileDirective($child[1]);
                break;

            case Type::T_AT_ROOT:
                $this->compileAtRoot($child[1]);
                break;

            case Type::T_MEDIA:
                $this->compileMedia($child[1]);
                break;

            case Type::T_BLOCK:
                $this->compileBlock($child[1]);
                break;

            case Type::T_CHARSET:
                if (! $this->charsetSeen) {
                    $this->charsetSeen = true;

                    $out->lines[] = '@charset ' .
$this->compileValue($child[1]) . ';';
                }
                break;

            case Type::T_ASSIGN:
                list(, $name, $value) = $child;

                if ($name[0] === Type::T_VARIABLE) {
                    $flags = isset($child[3]) ? $child[3] : [];
                    $isDefault = in_array('!default', $flags);
                    $isGlobal = in_array('!global', $flags);

                    if ($isGlobal) {
                        $this->set($name[1], $this->reduce($value),
false, $this->rootEnv, $value);
                        break;
                    }

                    $shouldSet = $isDefault &&
                        (($result = $this->get($name[1], false)) ===
null
                        || $result === static::$null);

                    if (! $isDefault || $shouldSet) {
                        $this->set($name[1], $this->reduce($value),
true, null, $value);
                    }
                    break;
                }

                $compiledName = $this->compileValue($name);

                // handle shorthand syntax: size / line-height
                if ($compiledName === 'font' || $compiledName ===
'grid-row' || $compiledName === 'grid-column') {
                    if ($value[0] === Type::T_VARIABLE) {
                        // if the font value comes from variable, the
content is already reduced
                        // (i.e., formulas were already calculated), so we
need the original unreduced value
                        $value = $this->get($value[1], true, null,
true);
                    }

                    $fontValue=&$value;

                    if ($value[0] === Type::T_LIST &&
$value[1]==',') {
                        // this is the case if more than one font is given:
example: "font: 400 1em/1.3 arial,helvetica"
                        // we need to handle the first list element
                        $fontValue=&$value[2][0];
                    }

                    if ($fontValue[0] === Type::T_EXPRESSION &&
$fontValue[1] === '/') {
                        $fontValue = $this->expToString($fontValue);
                    } elseif ($fontValue[0] === Type::T_LIST) {
                        foreach ($fontValue[2] as &$item) {
                            if ($item[0] === Type::T_EXPRESSION &&
$item[1] === '/') {
                                $item = $this->expToString($item);
                            }
                        }
                    }
                }

                // if the value reduces to null from something else then
                // the property should be discarded
                if ($value[0] !== Type::T_NULL) {
                    $value = $this->reduce($value);

                    if ($value[0] === Type::T_NULL || $value ===
static::$nullString) {
                        break;
                    }
                }

                $compiledValue = $this->compileValue($value);

                $out->lines[] = $this->formatter->property(
                    $compiledName,
                    $compiledValue
                );
                break;

            case Type::T_COMMENT:
                if ($out->type === Type::T_ROOT) {
                    $this->compileComment($child);
                    break;
                }

                $out->lines[] = $child[1];
                break;

            case Type::T_MIXIN:
            case Type::T_FUNCTION:
                list(, $block) = $child;

                $this->set(static::$namespaces[$block->type] .
$block->name, $block);
                break;

            case Type::T_EXTEND:
                foreach ($child[1] as $sel) {
                    $results = $this->evalSelectors([$sel]);

                    foreach ($results as $result) {
                        // only use the first one
                        $result = current($result);

                        $this->pushExtends($result, $out->selectors,
$child);
                    }
                }
                break;

            case Type::T_IF:
                list(, $if) = $child;

                if ($this->isTruthy($this->reduce($if->cond,
true))) {
                    return $this->compileChildren($if->children,
$out);
                }

                foreach ($if->cases as $case) {
                    if ($case->type === Type::T_ELSE ||
                        $case->type === Type::T_ELSEIF &&
$this->isTruthy($this->reduce($case->cond))
                    ) {
                        return
$this->compileChildren($case->children, $out);
                    }
                }
                break;

            case Type::T_EACH:
                list(, $each) = $child;

                $list =
$this->coerceList($this->reduce($each->list));

                $this->pushEnv();

                foreach ($list[2] as $item) {
                    if (count($each->vars) === 1) {
                        $this->set($each->vars[0], $item, true);
                    } else {
                        list(,, $values) = $this->coerceList($item);

                        foreach ($each->vars as $i => $var) {
                            $this->set($var, isset($values[$i]) ?
$values[$i] : static::$null, true);
                        }
                    }

                    $ret = $this->compileChildren($each->children,
$out);

                    if ($ret) {
                        if ($ret[0] !== Type::T_CONTROL) {
                            $this->popEnv();

                            return $ret;
                        }

                        if ($ret[1]) {
                            break;
                        }
                    }
                }

                $this->popEnv();
                break;

            case Type::T_WHILE:
                list(, $while) = $child;

                while ($this->isTruthy($this->reduce($while->cond,
true))) {
                    $ret = $this->compileChildren($while->children,
$out);

                    if ($ret) {
                        if ($ret[0] !== Type::T_CONTROL) {
                            return $ret;
                        }

                        if ($ret[1]) {
                            break;
                        }
                    }
                }
                break;

            case Type::T_FOR:
                list(, $for) = $child;

                $start = $this->reduce($for->start, true);
                $end   = $this->reduce($for->end, true);

                if (! ($start[2] == $end[2] || $end->unitless())) {
                    $this->throwError('Incompatible units:
"%s" and "%s".', $start->unitStr(),
$end->unitStr());

                    break;
                }

                $unit  = $start[2];
                $start = $start[1];
                $end   = $end[1];

                $d = $start < $end ? 1 : -1;

                for (;;) {
                    if ((! $for->until && $start - $d == $end)
||
                        ($for->until && $start == $end)
                    ) {
                        break;
                    }

                    $this->set($for->var, new Node\Number($start,
$unit));
                    $start += $d;

                    $ret = $this->compileChildren($for->children,
$out);

                    if ($ret) {
                        if ($ret[0] !== Type::T_CONTROL) {
                            return $ret;
                        }

                        if ($ret[1]) {
                            break;
                        }
                    }
                }
                break;

            case Type::T_BREAK:
                return [Type::T_CONTROL, true];

            case Type::T_CONTINUE:
                return [Type::T_CONTROL, false];

            case Type::T_RETURN:
                return $this->reduce($child[1], true);

            case Type::T_NESTED_PROPERTY:
                list(, $prop) = $child;

                $prefixed = [];
                $prefix = $this->compileValue($prop->prefix) .
'-';

                foreach ($prop->children as $child) {
                    switch ($child[0]) {
                        case Type::T_ASSIGN:
                            array_unshift($child[1][2], $prefix);
                            break;

                        case Type::T_NESTED_PROPERTY:
                            array_unshift($child[1]->prefix[2],
$prefix);
                            break;
                    }

                    $prefixed[] = $child;
                }

                $this->compileChildrenNoReturn($prefixed, $out);
                break;

            case Type::T_INCLUDE:
                // including a mixin
                list(, $name, $argValues, $content) = $child;

                $mixin =
$this->get(static::$namespaces['mixin'] . $name, false);

                if (! $mixin) {
                    $this->throwError("Undefined mixin
$name");
                    break;
                }

                $callingScope = $this->getStoreEnv();

                // push scope, apply args
                $this->pushEnv();
                $this->env->depth--;

                $storeEnv = $this->storeEnv;
                $this->storeEnv = $this->env;

                // Find the parent selectors in the env to be able to know
what '&' refers to in the mixin
                // and assign this fake parent to childs
                $selfParent = null;

                if (isset($child['selfParent']) &&
isset($child['selfParent']->selectors)) {
                    $selfParent = $child['selfParent'];
                } else {
                    $parentSelectors =
$this->multiplySelectors($this->env);

                    if ($parentSelectors) {
                        $parent = new Block();
                        $parent->selectors = $parentSelectors;

                        foreach ($mixin->children as $k => $child) {
                            if (isset($child[1]) &&
is_object($child[1]) && $child[1] instanceof Block) {
                                $mixin->children[$k][1]->parent =
$parent;
                            }
                        }
                    }
                }

                // clone the stored content to not have its scope spoiled
by a further call to the same mixin
                // i.e., recursive @include of the same mixin
                if (isset($content)) {
                    $copyContent = clone $content;
                    $copyContent->scope = $callingScope;

                   
$this->setRaw(static::$namespaces['special'] .
'content', $copyContent, $this->env);
                }

                if (isset($mixin->args)) {
                    $this->applyArguments($mixin->args, $argValues);
                }

                $this->env->marker = 'mixin';

                $this->compileChildrenNoReturn($mixin->children,
$out, $selfParent, $this->env->marker . " " . $name);

                $this->storeEnv = $storeEnv;

                $this->popEnv();
                break;

            case Type::T_MIXIN_CONTENT:
                $env = isset($this->storeEnv) ? $this->storeEnv :
$this->env;
                $content =
$this->get(static::$namespaces['special'] .
'content', false, $env);

                if (! $content) {
                    $content = new \stdClass();
                    $content->scope = new \stdClass();
                    $content->children =
$this->storeEnv->parent->block->children;
                    break;
                }

                $storeEnv = $this->storeEnv;
                $this->storeEnv = $content->scope;
                $this->compileChildrenNoReturn($content->children,
$out);

                $this->storeEnv = $storeEnv;
                break;

            case Type::T_DEBUG:
                list(, $value) = $child;

                $fname = $this->sourceNames[$this->sourceIndex];
                $line = $this->sourceLine;
                $value = $this->compileValue($this->reduce($value,
true));
                fwrite($this->stderr, "File $fname on line $line
DEBUG: $value\n");
                break;

            case Type::T_WARN:
                list(, $value) = $child;

                $fname = $this->sourceNames[$this->sourceIndex];
                $line = $this->sourceLine;
                $value = $this->compileValue($this->reduce($value,
true));
                fwrite($this->stderr, "File $fname on line $line
WARN: $value\n");
                break;

            case Type::T_ERROR:
                list(, $value) = $child;

                $fname = $this->sourceNames[$this->sourceIndex];
                $line = $this->sourceLine;
                $value = $this->compileValue($this->reduce($value,
true));
                $this->throwError("File $fname on line $line ERROR:
$value\n");
                break;

            case Type::T_CONTROL:
                $this->throwError('@break/@continue not permitted
in this scope');
                break;

            default:
                $this->throwError("unknown child type:
$child[0]");
        }
    }

    /**
     * Reduce expression to string
     *
     * @param array $exp
     *
     * @return array
     */
    protected function expToString($exp)
    {
        list(, $op, $left, $right, /* $inParens */, $whiteLeft,
$whiteRight) = $exp;

        $content = [$this->reduce($left)];

        if ($whiteLeft) {
            $content[] = ' ';
        }

        $content[] = $op;

        if ($whiteRight) {
            $content[] = ' ';
        }

        $content[] = $this->reduce($right);

        return [Type::T_STRING, '', $content];
    }

    /**
     * Is truthy?
     *
     * @param array $value
     *
     * @return boolean
     */
    protected function isTruthy($value)
    {
        return $value !== static::$false && $value !==
static::$null;
    }

    /**
     * Is the value a direct relationship combinator?
     *
     * @param string $value
     *
     * @return boolean
     */
    protected function isImmediateRelationshipCombinator($value)
    {
        return $value === '>' || $value === '+' ||
$value === '~';
    }

    /**
     * Should $value cause its operand to eval
     *
     * @param array $value
     *
     * @return boolean
     */
    protected function shouldEval($value)
    {
        switch ($value[0]) {
            case Type::T_EXPRESSION:
                if ($value[1] === '/') {
                    return $this->shouldEval($value[2]) ||
$this->shouldEval($value[3]);
                }

                // fall-thru
            case Type::T_VARIABLE:
            case Type::T_FUNCTION_CALL:
                return true;
        }

        return false;
    }

    /**
     * Reduce value
     *
     * @param array   $value
     * @param boolean $inExp
     *
     * @return array|\Leafo\ScssPhp\Node\Number
     */
    protected function reduce($value, $inExp = false)
    {

        switch ($value[0]) {
            case Type::T_EXPRESSION:
                list(, $op, $left, $right, $inParens) = $value;

                $opName = isset(static::$operatorNames[$op]) ?
static::$operatorNames[$op] : $op;
                $inExp = $inExp || $this->shouldEval($left) ||
$this->shouldEval($right);

                $left = $this->reduce($left, true);

                if ($op !== 'and' && $op !==
'or') {
                    $right = $this->reduce($right, true);
                }

                // special case: looks like css shorthand
                if ($opName == 'div' && ! $inParens
&& ! $inExp && isset($right[2])
                    && (($right[0] !== Type::T_NUMBER &&
$right[2] != '')
                    || ($right[0] === Type::T_NUMBER && !
$right->unitless()))
                ) {
                    return $this->expToString($value);
                }

                $left = $this->coerceForExpression($left);
                $right = $this->coerceForExpression($right);

                $ltype = $left[0];
                $rtype = $right[0];

                $ucOpName = ucfirst($opName);
                $ucLType  = ucfirst($ltype);
                $ucRType  = ucfirst($rtype);

                // this tries:
                // 1. op[op name][left type][right type]
                // 2. op[left type][right type] (passing the op as first
arg
                // 3. op[op name]
                $fn = "op${ucOpName}${ucLType}${ucRType}";

                if (is_callable([$this, $fn]) ||
                    (($fn = "op${ucLType}${ucRType}") &&
                        is_callable([$this, $fn]) &&
                        $passOp = true) ||
                    (($fn = "op${ucOpName}") &&
                        is_callable([$this, $fn]) &&
                        $genOp = true)
                ) {
                    $coerceUnit = false;

                    if (! isset($genOp) &&
                        $left[0] === Type::T_NUMBER && $right[0]
=== Type::T_NUMBER
                    ) {
                        $coerceUnit = true;

                        switch ($opName) {
                            case 'mul':
                                $targetUnit = $left[2];

                                foreach ($right[2] as $unit => $exp) {
                                    $targetUnit[$unit] =
(isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) + $exp;
                                }
                                break;

                            case 'div':
                                $targetUnit = $left[2];

                                foreach ($right[2] as $unit => $exp) {
                                    $targetUnit[$unit] =
(isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) - $exp;
                                }
                                break;

                            case 'mod':
                                $targetUnit = $left[2];
                                break;

                            default:
                                $targetUnit = $left->unitless() ?
$right[2] : $left[2];
                        }

                        if (! $left->unitless() && !
$right->unitless()) {
                            $left = $left->normalize();
                            $right = $right->normalize();
                        }
                    }

                    $shouldEval = $inParens || $inExp;

                    if (isset($passOp)) {
                        $out = $this->$fn($op, $left, $right,
$shouldEval);
                    } else {
                        $out = $this->$fn($left, $right, $shouldEval);
                    }

                    if (isset($out)) {
                        if ($coerceUnit && $out[0] ===
Type::T_NUMBER) {
                            $out = $out->coerce($targetUnit);
                        }

                        return $out;
                    }
                }

                return $this->expToString($value);

            case Type::T_UNARY:
                list(, $op, $exp, $inParens) = $value;

                $inExp = $inExp || $this->shouldEval($exp);
                $exp = $this->reduce($exp);

                if ($exp[0] === Type::T_NUMBER) {
                    switch ($op) {
                        case '+':
                            return new Node\Number($exp[1], $exp[2]);

                        case '-':
                            return new Node\Number(-$exp[1], $exp[2]);
                    }
                }

                if ($op === 'not') {
                    if ($inExp || $inParens) {
                        if ($exp === static::$false || $exp ===
static::$null) {
                            return static::$true;
                        }

                        return static::$false;
                    }

                    $op = $op . ' ';
                }

                return [Type::T_STRING, '', [$op, $exp]];

            case Type::T_VARIABLE:
                return $this->reduce($this->get($value[1]));

            case Type::T_LIST:
                foreach ($value[2] as &$item) {
                    $item = $this->reduce($item);
                }

                return $value;

            case Type::T_MAP:
                foreach ($value[1] as &$item) {
                    $item = $this->reduce($item);
                }

                foreach ($value[2] as &$item) {
                    $item = $this->reduce($item);
                }

                return $value;

            case Type::T_STRING:
                foreach ($value[2] as &$item) {
                    if (is_array($item) || $item instanceof \ArrayAccess) {
                        $item = $this->reduce($item);
                    }
                }

                return $value;

            case Type::T_INTERPOLATE:
                $value[1] = $this->reduce($value[1]);
                if ($inExp) {
                    return $value[1];
                }

                return $value;

            case Type::T_FUNCTION_CALL:
                return $this->fncall($value[1], $value[2]);

            case Type::T_SELF:
                $selfSelector = $this->multiplySelectors($this->env);
                $selfSelector = $this->collapseSelectors($selfSelector,
true);
                return $selfSelector;

            default:
                return $value;
        }
    }

    /**
     * Function caller
     *
     * @param string $name
     * @param array  $argValues
     *
     * @return array|null
     */
    protected function fncall($name, $argValues)
    {
        // SCSS @function
        if ($this->callScssFunction($name, $argValues, $returnValue)) {
            return $returnValue;
        }

        // native PHP functions
        if ($this->callNativeFunction($name, $argValues, $returnValue))
{
            return $returnValue;
        }

        // for CSS functions, simply flatten the arguments into a list
        $listArgs = [];

        foreach ((array) $argValues as $arg) {
            if (empty($arg[0])) {
                $listArgs[] = $this->reduce($arg[1]);
            }
        }

        return [Type::T_FUNCTION, $name, [Type::T_LIST, ',',
$listArgs]];
    }

    /**
     * Normalize name
     *
     * @param string $name
     *
     * @return string
     */
    protected function normalizeName($name)
    {
        return str_replace('-', '_', $name);
    }

    /**
     * Normalize value
     *
     * @param array $value
     *
     * @return array
     */
    public function normalizeValue($value)
    {
        $value = $this->coerceForExpression($this->reduce($value));

        switch ($value[0]) {
            case Type::T_LIST:
                $value = $this->extractInterpolation($value);

                if ($value[0] !== Type::T_LIST) {
                    return [Type::T_KEYWORD,
$this->compileValue($value)];
                }

                foreach ($value[2] as $key => $item) {
                    $value[2][$key] = $this->normalizeValue($item);
                }

                return $value;

            case Type::T_STRING:
                return [$value[0], '"',
[$this->compileStringContent($value)]];

            case Type::T_NUMBER:
                return $value->normalize();

            case Type::T_INTERPOLATE:
                return [Type::T_KEYWORD, $this->compileValue($value)];

            default:
                return $value;
        }
    }

    /**
     * Add numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opAddNumberNumber($left, $right)
    {
        return new Node\Number($left[1] + $right[1], $left[2]);
    }

    /**
     * Multiply numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opMulNumberNumber($left, $right)
    {
        return new Node\Number($left[1] * $right[1], $left[2]);
    }

    /**
     * Subtract numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opSubNumberNumber($left, $right)
    {
        return new Node\Number($left[1] - $right[1], $left[2]);
    }

    /**
     * Divide numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return array|\Leafo\ScssPhp\Node\Number
     */
    protected function opDivNumberNumber($left, $right)
    {
        if ($right[1] == 0) {
            return [Type::T_STRING, '', [$left[1] . $left[2] .
'/' . $right[1] . $right[2]]];
        }

        return new Node\Number($left[1] / $right[1], $left[2]);
    }

    /**
     * Mod numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opModNumberNumber($left, $right)
    {
        return new Node\Number($left[1] % $right[1], $left[2]);
    }

    /**
     * Add strings
     *
     * @param array $left
     * @param array $right
     *
     * @return array|null
     */
    protected function opAdd($left, $right)
    {
        if ($strLeft = $this->coerceString($left)) {
            if ($right[0] === Type::T_STRING) {
                $right[1] = '';
            }

            $strLeft[2][] = $right;

            return $strLeft;
        }

        if ($strRight = $this->coerceString($right)) {
            if ($left[0] === Type::T_STRING) {
                $left[1] = '';
            }

            array_unshift($strRight[2], $left);

            return $strRight;
        }

        return null;
    }

    /**
     * Boolean and
     *
     * @param array   $left
     * @param array   $right
     * @param boolean $shouldEval
     *
     * @return array|null
     */
    protected function opAnd($left, $right, $shouldEval)
    {
        $truthy = ($left === static::$null || $right === static::$null) ||
                  ($left === static::$false || $left === static::$true)
&&
                  ($right === static::$false || $right === static::$true);

        if (! $shouldEval) {
            if (! $truthy) {
                return null;
            }
        }

        if ($left !== static::$false && $left !== static::$null) {
            return $this->reduce($right, true);
        }

        return $left;
    }

    /**
     * Boolean or
     *
     * @param array   $left
     * @param array   $right
     * @param boolean $shouldEval
     *
     * @return array|null
     */
    protected function opOr($left, $right, $shouldEval)
    {
        $truthy = ($left === static::$null || $right === static::$null) ||
                  ($left === static::$false || $left === static::$true)
&&
                  ($right === static::$false || $right === static::$true);

        if (! $shouldEval) {
            if (! $truthy) {
                return null;
            }
        }

        if ($left !== static::$false && $left !== static::$null) {
            return $left;
        }

        return $this->reduce($right, true);
    }

    /**
     * Compare colors
     *
     * @param string $op
     * @param array  $left
     * @param array  $right
     *
     * @return array
     */
    protected function opColorColor($op, $left, $right)
    {
        $out = [Type::T_COLOR];

        foreach ([1, 2, 3] as $i) {
            $lval = isset($left[$i]) ? $left[$i] : 0;
            $rval = isset($right[$i]) ? $right[$i] : 0;

            switch ($op) {
                case '+':
                    $out[] = $lval + $rval;
                    break;

                case '-':
                    $out[] = $lval - $rval;
                    break;

                case '*':
                    $out[] = $lval * $rval;
                    break;

                case '%':
                    $out[] = $lval % $rval;
                    break;

                case '/':
                    if ($rval == 0) {
                        $this->throwError("color: Can't divide
by zero");
                        break 2;
                    }

                    $out[] = (int) ($lval / $rval);
                    break;

                case '==':
                    return $this->opEq($left, $right);

                case '!=':
                    return $this->opNeq($left, $right);

                default:
                    $this->throwError("color: unknown op
$op");
                    break 2;
            }
        }

        if (isset($left[4])) {
            $out[4] = $left[4];
        } elseif (isset($right[4])) {
            $out[4] = $right[4];
        }

        return $this->fixColor($out);
    }

    /**
     * Compare color and number
     *
     * @param string $op
     * @param array  $left
     * @param array  $right
     *
     * @return array
     */
    protected function opColorNumber($op, $left, $right)
    {
        $value = $right[1];

        return $this->opColorColor(
            $op,
            $left,
            [Type::T_COLOR, $value, $value, $value]
        );
    }

    /**
     * Compare number and color
     *
     * @param string $op
     * @param array  $left
     * @param array  $right
     *
     * @return array
     */
    protected function opNumberColor($op, $left, $right)
    {
        $value = $left[1];

        return $this->opColorColor(
            $op,
            [Type::T_COLOR, $value, $value, $value],
            $right
        );
    }

    /**
     * Compare number1 == number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opEq($left, $right)
    {
        if (($lStr = $this->coerceString($left)) && ($rStr =
$this->coerceString($right))) {
            $lStr[1] = '';
            $rStr[1] = '';

            $left = $this->compileValue($lStr);
            $right = $this->compileValue($rStr);
        }

        return $this->toBool($left === $right);
    }

    /**
     * Compare number1 != number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opNeq($left, $right)
    {
        if (($lStr = $this->coerceString($left)) && ($rStr =
$this->coerceString($right))) {
            $lStr[1] = '';
            $rStr[1] = '';

            $left = $this->compileValue($lStr);
            $right = $this->compileValue($rStr);
        }

        return $this->toBool($left !== $right);
    }

    /**
     * Compare number1 >= number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opGteNumberNumber($left, $right)
    {
        return $this->toBool($left[1] >= $right[1]);
    }

    /**
     * Compare number1 > number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opGtNumberNumber($left, $right)
    {
        return $this->toBool($left[1] > $right[1]);
    }

    /**
     * Compare number1 <= number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opLteNumberNumber($left, $right)
    {
        return $this->toBool($left[1] <= $right[1]);
    }

    /**
     * Compare number1 < number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opLtNumberNumber($left, $right)
    {
        return $this->toBool($left[1] < $right[1]);
    }

    /**
     * Three-way comparison, aka spaceship operator
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opCmpNumberNumber($left, $right)
    {
        $n = $left[1] - $right[1];

        return new Node\Number($n ? $n / abs($n) : 0, '');
    }

    /**
     * Cast to boolean
     *
     * @api
     *
     * @param mixed $thing
     *
     * @return array
     */
    public function toBool($thing)
    {
        return $thing ? static::$true : static::$false;
    }

    /**
     * Compiles a primitive value into a CSS property value.
     *
     * Values in scssphp are typed by being wrapped in arrays, their format
is
     * typically:
     *
     *     array(type, contents [, additional_contents]*)
     *
     * The input is expected to be reduced. This function will not work on
     * things like expressions and variables.
     *
     * @api
     *
     * @param array $value
     *
     * @return string
     */
    public function compileValue($value)
    {
        $value = $this->reduce($value);

        switch ($value[0]) {
            case Type::T_KEYWORD:
                return $value[1];

            case Type::T_COLOR:
                // [1] - red component (either number for a %)
                // [2] - green component
                // [3] - blue component
                // [4] - optional alpha component
                list(, $r, $g, $b) = $value;

                $r = round($r);
                $g = round($g);
                $b = round($b);

                if (count($value) === 5 && $value[4] !== 1) { //
rgba
                    $a = new Node\Number($value[4], '');

                    return 'rgba(' . $r . ', ' . $g .
', ' . $b . ', ' . $a . ')';
                }

                $h = sprintf('#%02x%02x%02x', $r, $g, $b);

                // Converting hex color to short notation (e.g. #003399 to
#039)
                if ($h[1] === $h[2] && $h[3] === $h[4] &&
$h[5] === $h[6]) {
                    $h = '#' . $h[1] . $h[3] . $h[5];
                }

                return $h;

            case Type::T_NUMBER:
                return $value->output($this);

            case Type::T_STRING:
                return $value[1] . $this->compileStringContent($value) .
$value[1];

            case Type::T_FUNCTION:
                $args = ! empty($value[2]) ?
$this->compileValue($value[2]) : '';

                return "$value[1]($args)";

            case Type::T_LIST:
                $value = $this->extractInterpolation($value);

                if ($value[0] !== Type::T_LIST) {
                    return $this->compileValue($value);
                }

                list(, $delim, $items) = $value;

                if ($delim !== ' ') {
                    $delim .= ' ';
                }

                $filtered = [];

                foreach ($items as $item) {
                    if ($item[0] === Type::T_NULL) {
                        continue;
                    }

                    $filtered[] = $this->compileValue($item);
                }

                return implode("$delim", $filtered);

            case Type::T_MAP:
                $keys = $value[1];
                $values = $value[2];
                $filtered = [];

                for ($i = 0, $s = count($keys); $i < $s; $i++) {
                    $filtered[$this->compileValue($keys[$i])] =
$this->compileValue($values[$i]);
                }

                array_walk($filtered, function (&$value, $key) {
                    $value = $key . ': ' . $value;
                });

                return '(' . implode(', ', $filtered) .
')';

            case Type::T_INTERPOLATED:
                // node created by extractInterpolation
                list(, $interpolate, $left, $right) = $value;
                list(,, $whiteLeft, $whiteRight) = $interpolate;

                $left = count($left[2]) > 0 ?
                    $this->compileValue($left) . $whiteLeft :
'';

                $right = count($right[2]) > 0 ?
                    $whiteRight . $this->compileValue($right) :
'';

                return $left . $this->compileValue($interpolate) .
$right;

            case Type::T_INTERPOLATE:
                // strip quotes if it's a string
                $reduced = $this->reduce($value[1]);

                switch ($reduced[0]) {
                    case Type::T_LIST:
                        $reduced =
$this->extractInterpolation($reduced);

                        if ($reduced[0] !== Type::T_LIST) {
                            break;
                        }

                        list(, $delim, $items) = $reduced;

                        if ($delim !== ' ') {
                            $delim .= ' ';
                        }

                        $filtered = [];

                        foreach ($items as $item) {
                            if ($item[0] === Type::T_NULL) {
                                continue;
                            }

                            $temp =
$this->compileValue([Type::T_KEYWORD, $item]);
                            if ($temp[0] === Type::T_STRING) {
                                $filtered[] =
$this->compileStringContent($temp);
                            } elseif ($temp[0] === Type::T_KEYWORD) {
                                $filtered[] = $temp[1];
                            } else {
                                $filtered[] =
$this->compileValue($temp);
                            }
                        }

                        $reduced = [Type::T_KEYWORD,
implode("$delim", $filtered)];
                        break;

                    case Type::T_STRING:
                        $reduced = [Type::T_KEYWORD,
$this->compileStringContent($reduced)];
                        break;

                    case Type::T_NULL:
                        $reduced = [Type::T_KEYWORD, ''];
                }

                return $this->compileValue($reduced);

            case Type::T_NULL:
                return 'null';

            default:
                $this->throwError("unknown value type:
$value[0]");
        }
    }

    /**
     * Flatten list
     *
     * @param array $list
     *
     * @return string
     */
    protected function flattenList($list)
    {
        return $this->compileValue($list);
    }

    /**
     * Compile string content
     *
     * @param array $string
     *
     * @return string
     */
    protected function compileStringContent($string)
    {
        $parts = [];

        foreach ($string[2] as $part) {
            if (is_array($part) || $part instanceof \ArrayAccess) {
                $parts[] = $this->compileValue($part);
            } else {
                $parts[] = $part;
            }
        }

        return implode($parts);
    }

    /**
     * Extract interpolation; it doesn't need to be recursive,
compileValue will handle that
     *
     * @param array $list
     *
     * @return array
     */
    protected function extractInterpolation($list)
    {
        $items = $list[2];

        foreach ($items as $i => $item) {
            if ($item[0] === Type::T_INTERPOLATE) {
                $before = [Type::T_LIST, $list[1], array_slice($items, 0,
$i)];
                $after  = [Type::T_LIST, $list[1], array_slice($items, $i +
1)];

                return [Type::T_INTERPOLATED, $item, $before, $after];
            }
        }

        return $list;
    }

    /**
     * Find the final set of selectors
     *
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param \Leafo\ScssPhp\Block                $selfParent
     *
     * @return array
     */
    protected function multiplySelectors(Environment $env, $selfParent =
null)
    {
        $envs            = $this->compactEnv($env);
        $selectors       = [];
        $parentSelectors = [[]];

        $selfParentSelectors = null;

        if (! is_null($selfParent) && $selfParent->selectors) {
            $selfParentSelectors =
$this->evalSelectors($selfParent->selectors);
        }

        while ($env = array_pop($envs)) {
            if (empty($env->selectors)) {
                continue;
            }

            $selectors = $env->selectors;

            do {
                $stillHasSelf = false;
                $prevSelectors = $selectors;
                $selectors = [];

                foreach ($prevSelectors as $selector) {
                    foreach ($parentSelectors as $parent) {
                        if ($selfParentSelectors) {
                            foreach ($selfParentSelectors as $selfParent) {
                                // if no '&' in the selector,
each call will give same result, only add once
                                $s = $this->joinSelectors($parent,
$selector, $stillHasSelf, $selfParent);
                                $selectors[serialize($s)] = $s;
                            }
                        } else {
                            $s = $this->joinSelectors($parent,
$selector, $stillHasSelf);
                            $selectors[serialize($s)] = $s;
                        }
                    }
                }
            } while ($stillHasSelf);

            $parentSelectors = $selectors;
        }

        $selectors = array_values($selectors);

        return $selectors;
    }

    /**
     * Join selectors; looks for & to replace, or append parent before
child
     *
     * @param array   $parent
     * @param array   $child
     * @param boolean &$stillHasSelf
     * @param array   $selfParentSelectors

     * @return array
     */
    protected function joinSelectors($parent, $child, &$stillHasSelf,
$selfParentSelectors = null)
    {
        $setSelf = false;
        $out = [];

        foreach ($child as $part) {
            $newPart = [];

            foreach ($part as $p) {
                // only replace & once and should be recalled to be
able to make combinations
                if ($p === static::$selfSelector && $setSelf) {
                    $stillHasSelf = true;
                }

                if ($p === static::$selfSelector && ! $setSelf) {
                    $setSelf = true;

                    if (is_null($selfParentSelectors)) {
                        $selfParentSelectors = $parent;
                    }

                    foreach ($selfParentSelectors as $i => $parentPart)
{
                        if ($i > 0) {
                            $out[] = $newPart;
                            $newPart = [];
                        }

                        foreach ($parentPart as $pp) {
                            if (is_array($pp)) {
                                $flatten = [];
                                array_walk_recursive($pp, function ($a) use
(&$flatten) {
                                    $flatten[] = $a;
                                });
                                $pp = implode($flatten);
                            }

                            $newPart[] = $pp;
                        }
                    }
                } else {
                    $newPart[] = $p;
                }
            }

            $out[] = $newPart;
        }

        return $setSelf ? $out : array_merge($parent, $child);
    }

    /**
     * Multiply media
     *
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param array                               $childQueries
     *
     * @return array
     */
    protected function multiplyMedia(Environment $env = null, $childQueries
= null)
    {
        if (! isset($env) ||
            ! empty($env->block->type) &&
$env->block->type !== Type::T_MEDIA
        ) {
            return $childQueries;
        }

        // plain old block, skip
        if (empty($env->block->type)) {
            return $this->multiplyMedia($env->parent, $childQueries);
        }

        $parentQueries = isset($env->block->queryList)
            ? $env->block->queryList
            : [[[Type::T_MEDIA_VALUE, $env->block->value]]];

        $store = [$this->env, $this->storeEnv];
        $this->env = $env;
        $this->storeEnv = null;
        $parentQueries = $this->evaluateMediaQuery($parentQueries);
        list($this->env, $this->storeEnv) = $store;

        if ($childQueries === null) {
            $childQueries = $parentQueries;
        } else {
            $originalQueries = $childQueries;
            $childQueries = [];

            foreach ($parentQueries as $parentQuery) {
                foreach ($originalQueries as $childQuery) {
                    $childQueries[] = array_merge(
                        $parentQuery,
                        [[Type::T_MEDIA_TYPE, [Type::T_KEYWORD,
'all']]],
                        $childQuery
                    );
                }
            }
        }

        return $this->multiplyMedia($env->parent, $childQueries);
    }

    /**
     * Convert env linked list to stack
     *
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     *
     * @return array
     */
    protected function compactEnv(Environment $env)
    {
        for ($envs = []; $env; $env = $env->parent) {
            $envs[] = $env;
        }

        return $envs;
    }

    /**
     * Convert env stack to singly linked list
     *
     * @param array $envs
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function extractEnv($envs)
    {
        for ($env = null; $e = array_pop($envs);) {
            $e->parent = $env;
            $env = $e;
        }

        return $env;
    }

    /**
     * Push environment
     *
     * @param \Leafo\ScssPhp\Block $block
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function pushEnv(Block $block = null)
    {
        $env = new Environment;
        $env->parent = $this->env;
        $env->store  = [];
        $env->block  = $block;
        $env->depth  = isset($this->env->depth) ?
$this->env->depth + 1 : 0;

        $this->env = $env;

        return $env;
    }

    /**
     * Pop environment
     */
    protected function popEnv()
    {
        $this->env = $this->env->parent;
    }

    /**
     * Get store environment
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function getStoreEnv()
    {
        return isset($this->storeEnv) ? $this->storeEnv :
$this->env;
    }

    /**
     * Set variable
     *
     * @param string                              $name
     * @param mixed                               $value
     * @param boolean                             $shadow
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param mixed                               $valueUnreduced
     */
    protected function set($name, $value, $shadow = false, Environment $env
= null, $valueUnreduced = null)
    {
        $name = $this->normalizeName($name);

        if (! isset($env)) {
            $env = $this->getStoreEnv();
        }

        if ($shadow) {
            $this->setRaw($name, $value, $env, $valueUnreduced);
        } else {
            $this->setExisting($name, $value, $env, $valueUnreduced);
        }
    }

    /**
     * Set existing variable
     *
     * @param string                              $name
     * @param mixed                               $value
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param mixed                               $valueUnreduced
     */
    protected function setExisting($name, $value, Environment $env,
$valueUnreduced = null)
    {
        $storeEnv = $env;

        $hasNamespace = $name[0] === '^' || $name[0] ===
'@' || $name[0] === '%';

        for (;;) {
            if (array_key_exists($name, $env->store)) {
                break;
            }

            if (! $hasNamespace && isset($env->marker)) {
                $env = $storeEnv;
                break;
            }

            if (! isset($env->parent)) {
                $env = $storeEnv;
                break;
            }

            $env = $env->parent;
        }

        $env->store[$name] = $value;

        if ($valueUnreduced) {
            $env->storeUnreduced[$name] = $valueUnreduced;
        }
    }

    /**
     * Set raw variable
     *
     * @param string                              $name
     * @param mixed                               $value
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param mixed                               $valueUnreduced
     */
    protected function setRaw($name, $value, Environment $env,
$valueUnreduced = null)
    {
        $env->store[$name] = $value;

        if ($valueUnreduced) {
            $env->storeUnreduced[$name] = $valueUnreduced;
        }
    }

    /**
     * Get variable
     *
     * @api
     *
     * @param string                              $name
     * @param boolean                             $shouldThrow
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param boolean                             $unreduced
     *
     * @return mixed|null
     */
    public function get($name, $shouldThrow = true, Environment $env =
null, $unreduced = false)
    {
        $normalizedName = $this->normalizeName($name);
        $specialContentKey = static::$namespaces['special'] .
'content';

        if (! isset($env)) {
            $env = $this->getStoreEnv();
        }

        $nextIsRoot = false;
        $hasNamespace = $normalizedName[0] === '^' ||
$normalizedName[0] === '@' || $normalizedName[0] ===
'%';

        $maxDepth = 10000;

        for (;;) {
            if ($maxDepth-- <= 0) {
                break;
            }

            if (array_key_exists($normalizedName, $env->store)) {
                if ($unreduced &&
isset($env->storeUnreduced[$normalizedName])) {
                    return $env->storeUnreduced[$normalizedName];
                }

                return $env->store[$normalizedName];
            }

            if (! $hasNamespace && isset($env->marker)) {
                if (! $nextIsRoot && !
empty($env->store[$specialContentKey])) {
                    $env = $env->store[$specialContentKey]->scope;
                    continue;
                }

                $env = $this->rootEnv;
                continue;
            }

            if (! isset($env->parent)) {
                break;
            }

            $env = $env->parent;
        }

        if ($shouldThrow) {
            $this->throwError("Undefined variable \$$name" .
($maxDepth<=0 ? " (infinite recursion)" : ""));
        }

        // found nothing
        return null;
    }

    /**
     * Has variable?
     *
     * @param string                              $name
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     *
     * @return boolean
     */
    protected function has($name, Environment $env = null)
    {
        return $this->get($name, false, $env) !== null;
    }

    /**
     * Inject variables
     *
     * @param array $args
     */
    protected function injectVariables(array $args)
    {
        if (empty($args)) {
            return;
        }

        $parser = $this->parserFactory(__METHOD__);

        foreach ($args as $name => $strValue) {
            if ($name[0] === '$') {
                $name = substr($name, 1);
            }

            if (! $parser->parseValue($strValue, $value)) {
                $value = $this->coerceValue($strValue);
            }

            $this->set($name, $value);
        }
    }

    /**
     * Set variables
     *
     * @api
     *
     * @param array $variables
     */
    public function setVariables(array $variables)
    {
        $this->registeredVars = array_merge($this->registeredVars,
$variables);
    }

    /**
     * Unset variable
     *
     * @api
     *
     * @param string $name
     */
    public function unsetVariable($name)
    {
        unset($this->registeredVars[$name]);
    }

    /**
     * Returns list of variables
     *
     * @api
     *
     * @return array
     */
    public function getVariables()
    {
        return $this->registeredVars;
    }

    /**
     * Adds to list of parsed files
     *
     * @api
     *
     * @param string $path
     */
    public function addParsedFile($path)
    {
        if (isset($path) && file_exists($path)) {
            $this->parsedFiles[realpath($path)] = filemtime($path);
        }
    }

    /**
     * Returns list of parsed files
     *
     * @api
     *
     * @return array
     */
    public function getParsedFiles()
    {
        return $this->parsedFiles;
    }

    /**
     * Add import path
     *
     * @api
     *
     * @param string|callable $path
     */
    public function addImportPath($path)
    {
        if (! in_array($path, $this->importPaths)) {
            $this->importPaths[] = $path;
        }
    }

    /**
     * Set import paths
     *
     * @api
     *
     * @param string|array $path
     */
    public function setImportPaths($path)
    {
        $this->importPaths = (array) $path;
    }

    /**
     * Set number precision
     *
     * @api
     *
     * @param integer $numberPrecision
     */
    public function setNumberPrecision($numberPrecision)
    {
        Node\Number::$precision = $numberPrecision;
    }

    /**
     * Set formatter
     *
     * @api
     *
     * @param string $formatterName
     */
    public function setFormatter($formatterName)
    {
        $this->formatter = $formatterName;
    }

    /**
     * Set line number style
     *
     * @api
     *
     * @param string $lineNumberStyle
     */
    public function setLineNumberStyle($lineNumberStyle)
    {
        $this->lineNumberStyle = $lineNumberStyle;
    }

    /**
     * Enable/disable source maps
     *
     * @api
     *
     * @param integer $sourceMap
     */
    public function setSourceMap($sourceMap)
    {
        $this->sourceMap = $sourceMap;
    }

    /**
     * Set source map options
     *
     * @api
     *
     * @param array $sourceMapOptions
     */
    public function setSourceMapOptions($sourceMapOptions)
    {
        $this->sourceMapOptions = $sourceMapOptions;
    }

    /**
     * Register function
     *
     * @api
     *
     * @param string   $name
     * @param callable $func
     * @param array    $prototype
     */
    public function registerFunction($name, $func, $prototype = null)
    {
        $this->userFunctions[$this->normalizeName($name)] = [$func,
$prototype];
    }

    /**
     * Unregister function
     *
     * @api
     *
     * @param string $name
     */
    public function unregisterFunction($name)
    {
        unset($this->userFunctions[$this->normalizeName($name)]);
    }

    /**
     * Add feature
     *
     * @api
     *
     * @param string $name
     */
    public function addFeature($name)
    {
        $this->registeredFeatures[$name] = true;
    }

    /**
     * Import file
     *
     * @param string                               $path
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     */
    protected function importFile($path, OutputBlock $out)
    {
        // see if tree is cached
        $realPath = realpath($path);

        if (isset($this->importCache[$realPath])) {
            $this->handleImportLoop($realPath);

            $tree = $this->importCache[$realPath];
        } else {
            $code   = file_get_contents($path);
            $parser = $this->parserFactory($path);
            $tree   = $parser->parse($code);

            $this->importCache[$realPath] = $tree;
        }

        $pi = pathinfo($path);
        array_unshift($this->importPaths, $pi['dirname']);
        $this->compileChildrenNoReturn($tree->children, $out);
        array_shift($this->importPaths);
    }

    /**
     * Return the file path for an import url if it exists
     *
     * @api
     *
     * @param string $url
     *
     * @return string|null
     */
    public function findImport($url)
    {
        $urls = [];

        // for "normal" scss imports (ignore vanilla css and
external requests)
        if (! preg_match('/\.css$|^https?:\/\//', $url)) {
            // try both normal and the _partial filename
            $urls = [$url, preg_replace('/[^\/]+$/',
'_\0', $url)];
        }

        $hasExtension = preg_match('/[.]s?css$/', $url);

        foreach ($this->importPaths as $dir) {
            if (is_string($dir)) {
                // check urls for normal import paths
                foreach ($urls as $full) {
                    $separator = (
                        ! empty($dir) &&
                        substr($dir, -1) !== '/' &&
                        substr($full, 0, 1) !== '/'
                    ) ? '/' : '';
                    $full = $dir . $separator . $full;

                    if ($this->fileExists($file = $full .
'.scss') ||
                        ($hasExtension &&
$this->fileExists($file = $full))
                    ) {
                        return $file;
                    }
                }
            } elseif (is_callable($dir)) {
                // check custom callback for import path
                $file = call_user_func($dir, $url);

                if ($file !== null) {
                    return $file;
                }
            }
        }

        return null;
    }

    /**
     * Set encoding
     *
     * @api
     *
     * @param string $encoding
     */
    public function setEncoding($encoding)
    {
        $this->encoding = $encoding;
    }

    /**
     * Ignore errors?
     *
     * @api
     *
     * @param boolean $ignoreErrors
     *
     * @return \Leafo\ScssPhp\Compiler
     */
    public function setIgnoreErrors($ignoreErrors)
    {
        $this->ignoreErrors = $ignoreErrors;

        return $this;
    }

    /**
     * Throw error (exception)
     *
     * @api
     *
     * @param string $msg Message with optional sprintf()-style vararg
parameters
     *
     * @throws \Leafo\ScssPhp\Exception\CompilerException
     */
    public function throwError($msg)
    {
        if ($this->ignoreErrors) {
            return;
        }

        $line   = $this->sourceLine;
        $column = $this->sourceColumn;

        $loc = isset($this->sourceNames[$this->sourceIndex])
             ? $this->sourceNames[$this->sourceIndex] . " on
line $line, at column $column"
             : "line: $line, column: $column";

        if (func_num_args() > 1) {
            $msg = call_user_func_array('sprintf',
func_get_args());
        }

        $msg = "$msg: $loc";

        $callStackMsg = $this->callStackMessage();

        if ($callStackMsg) {
            $msg .= "\nCall Stack:\n" . $callStackMsg;
        }

        throw new CompilerException($msg);
    }

    /**
     * Beautify call stack for output
     *
     * @param boolean $all
     * @param null    $limit
     *
     * @return string
     */
    protected function callStackMessage($all = false, $limit = null)
    {
        $callStackMsg = [];
        $ncall = 0;

        if ($this->callStack) {
            foreach (array_reverse($this->callStack) as $call) {
                if ($all || (isset($call['n']) &&
$call['n'])) {
                    $msg = "#" . $ncall++ . " " .
$call['n'] . " ";
                    $msg .=
(isset($this->sourceNames[$call[Parser::SOURCE_INDEX]])
                          ?
$this->sourceNames[$call[Parser::SOURCE_INDEX]]
                          : '(unknown file)');
                    $msg .= " on line " .
$call[Parser::SOURCE_LINE];
                    $callStackMsg[] = $msg;

                    if (! is_null($limit) && $ncall>$limit) {
                        break;
                    }
                }
            }
        }

        return implode("\n", $callStackMsg);
    }

    /**
     * Handle import loop
     *
     * @param string $name
     *
     * @throws \Exception
     */
    protected function handleImportLoop($name)
    {
        for ($env = $this->env; $env; $env = $env->parent) {
            $file = $this->sourceNames[$env->block->sourceIndex];

            if (realpath($file) === $name) {
                $this->throwError('An @import loop has been found:
%s imports %s', $file, basename($file));
                break;
            }
        }
    }

    /**
     * Does file exist?
     *
     * @param string $name
     *
     * @return boolean
     */
    protected function fileExists($name)
    {
        return file_exists($name) && is_file($name);
    }

    /**
     * Call SCSS @function
     *
     * @param string $name
     * @param array  $argValues
     * @param array  $returnValue
     *
     * @return boolean Returns true if returnValue is set; otherwise, false
     */
    protected function callScssFunction($name, $argValues,
&$returnValue)
    {
        $func = $this->get(static::$namespaces['function'] .
$name, false);

        if (! $func) {
            return false;
        }

        $this->pushEnv();

        $storeEnv = $this->storeEnv;
        $this->storeEnv = $this->env;

        // set the args
        if (isset($func->args)) {
            $this->applyArguments($func->args, $argValues);
        }

        // throw away lines and children
        $tmp = new OutputBlock;
        $tmp->lines    = [];
        $tmp->children = [];

        $this->env->marker = 'function';

        $ret = $this->compileChildren($func->children, $tmp,
$this->env->marker . " " . $name);

        $this->storeEnv = $storeEnv;

        $this->popEnv();

        $returnValue = ! isset($ret) ? static::$defaultValue : $ret;

        return true;
    }

    /**
     * Call built-in and registered (PHP) functions
     *
     * @param string $name
     * @param array  $args
     * @param array  $returnValue
     *
     * @return boolean Returns true if returnValue is set; otherwise, false
     */
    protected function callNativeFunction($name, $args, &$returnValue)
    {
        // try a lib function
        $name = $this->normalizeName($name);

        if (isset($this->userFunctions[$name])) {
            // see if we can find a user function
            list($f, $prototype) = $this->userFunctions[$name];
        } elseif (($f = $this->getBuiltinFunction($name)) &&
is_callable($f)) {
            $libName   = $f[1];
            $prototype = isset(static::$$libName) ? static::$$libName :
null;
        } else {
            return false;
        }

        @list($sorted, $kwargs) = $this->sortArgs($prototype, $args);

        if ($name !== 'if' && $name !== 'call')
{
            foreach ($sorted as &$val) {
                $val = $this->reduce($val, true);
            }
        }

        $returnValue = call_user_func($f, $sorted, $kwargs);

        if (! isset($returnValue)) {
            return false;
        }

        $returnValue = $this->coerceValue($returnValue);

        return true;
    }

    /**
     * Get built-in function
     *
     * @param string $name Normalized name
     *
     * @return array
     */
    protected function getBuiltinFunction($name)
    {
        $libName = 'lib' . preg_replace_callback(
            '/_(.)/',
            function ($m) {
                return ucfirst($m[1]);
            },
            ucfirst($name)
        );

        return [$this, $libName];
    }

    /**
     * Sorts keyword arguments
     *
     * @param array $prototype
     * @param array $args
     *
     * @return array
     */
    protected function sortArgs($prototype, $args)
    {
        $keyArgs = [];
        $posArgs = [];

        // separate positional and keyword arguments
        foreach ($args as $arg) {
            list($key, $value) = $arg;

            $key = $key[1];

            if (empty($key)) {
                $posArgs[] = empty($arg[2]) ? $value : $arg;
            } else {
                $keyArgs[$key] = $value;
            }
        }

        if (! isset($prototype)) {
            return [$posArgs, $keyArgs];
        }

        // copy positional args
        $finalArgs = array_pad($posArgs, count($prototype), null);

        // overwrite positional args with keyword args
        foreach ($prototype as $i => $names) {
            foreach ((array) $names as $name) {
                if (isset($keyArgs[$name])) {
                    $finalArgs[$i] = $keyArgs[$name];
                }
            }
        }

        return [$finalArgs, $keyArgs];
    }

    /**
     * Apply argument values per definition
     *
     * @param array $argDef
     * @param array $argValues
     *
     * @throws \Exception
     */
    protected function applyArguments($argDef, $argValues)
    {
        $storeEnv = $this->getStoreEnv();

        $env = new Environment;
        $env->store = $storeEnv->store;

        $hasVariable = false;
        $args = [];

        foreach ($argDef as $i => $arg) {
            list($name, $default, $isVariable) = $argDef[$i];

            $args[$name] = [$i, $name, $default, $isVariable];
            $hasVariable |= $isVariable;
        }

        $keywordArgs = [];
        $deferredKeywordArgs = [];
        $remaining = [];

        // assign the keyword args
        foreach ((array) $argValues as $arg) {
            if (! empty($arg[0])) {
                if (! isset($args[$arg[0][1]])) {
                    if ($hasVariable) {
                        $deferredKeywordArgs[$arg[0][1]] = $arg[1];
                    } else {
                        $this->throwError("Mixin or function
doesn't have an argument named $%s.", $arg[0][1]);
                        break;
                    }
                } elseif ($args[$arg[0][1]][0] < count($remaining)) {
                    $this->throwError("The argument $%s was passed
both by position and by name.", $arg[0][1]);
                    break;
                } else {
                    $keywordArgs[$arg[0][1]] = $arg[1];
                }
            } elseif (count($keywordArgs)) {
                $this->throwError('Positional arguments must come
before keyword arguments.');
                break;
            } elseif ($arg[2] === true) {
                $val = $this->reduce($arg[1], true);

                if ($val[0] === Type::T_LIST) {
                    foreach ($val[2] as $name => $item) {
                        if (! is_numeric($name)) {
                            $keywordArgs[$name] = $item;
                        } else {
                            $remaining[] = $item;
                        }
                    }
                } elseif ($val[0] === Type::T_MAP) {
                    foreach ($val[1] as $i => $name) {
                        $name =
$this->compileStringContent($this->coerceString($name));
                        $item = $val[2][$i];

                        if (! is_numeric($name)) {
                            $keywordArgs[$name] = $item;
                        } else {
                            $remaining[] = $item;
                        }
                    }
                } else {
                    $remaining[] = $val;
                }
            } else {
                $remaining[] = $arg[1];
            }
        }

        foreach ($args as $arg) {
            list($i, $name, $default, $isVariable) = $arg;

            if ($isVariable) {
                $val = [Type::T_LIST, ',', [], $isVariable];

                for ($count = count($remaining); $i < $count; $i++) {
                    $val[2][] = $remaining[$i];
                }

                foreach ($deferredKeywordArgs as $itemName => $item) {
                    $val[2][$itemName] = $item;
                }
            } elseif (isset($remaining[$i])) {
                $val = $remaining[$i];
            } elseif (isset($keywordArgs[$name])) {
                $val = $keywordArgs[$name];
            } elseif (! empty($default)) {
                continue;
            } else {
                $this->throwError("Missing argument $name");
                break;
            }

            $this->set($name, $this->reduce($val, true), true, $env);
        }

        $storeEnv->store = $env->store;

        foreach ($args as $arg) {
            list($i, $name, $default, $isVariable) = $arg;

            if ($isVariable || isset($remaining[$i]) ||
isset($keywordArgs[$name]) || empty($default)) {
                continue;
            }

            $this->set($name, $this->reduce($default, true), true);
        }
    }

    /**
     * Coerce a php value into a scss one
     *
     * @param mixed $value
     *
     * @return array|\Leafo\ScssPhp\Node\Number
     */
    protected function coerceValue($value)
    {
        if (is_array($value) || $value instanceof \ArrayAccess) {
            return $value;
        }

        if (is_bool($value)) {
            return $this->toBool($value);
        }

        if ($value === null) {
            return static::$null;
        }

        if (is_numeric($value)) {
            return new Node\Number($value, '');
        }

        if ($value === '') {
            return static::$emptyString;
        }

        if (preg_match('/^(#([0-9a-f]{6})|#([0-9a-f]{3}))$/i',
$value, $m)) {
            $color = [Type::T_COLOR];

            if (isset($m[3])) {
                $num = hexdec($m[3]);

                foreach ([3, 2, 1] as $i) {
                    $t = $num & 0xf;
                    $color[$i] = $t << 4 | $t;
                    $num >>= 4;
                }
            } else {
                $num = hexdec($m[2]);

                foreach ([3, 2, 1] as $i) {
                    $color[$i] = $num & 0xff;
                    $num >>= 8;
                }
            }

            return $color;
        }

        return [Type::T_KEYWORD, $value];
    }

    /**
     * Coerce something to map
     *
     * @param array $item
     *
     * @return array
     */
    protected function coerceMap($item)
    {
        if ($item[0] === Type::T_MAP) {
            return $item;
        }

        if ($item === static::$emptyList) {
            return static::$emptyMap;
        }

        return [Type::T_MAP, [$item], [static::$null]];
    }

    /**
     * Coerce something to list
     *
     * @param array  $item
     * @param string $delim
     *
     * @return array
     */
    protected function coerceList($item, $delim = ',')
    {
        if (isset($item) && $item[0] === Type::T_LIST) {
            return $item;
        }

        if (isset($item) && $item[0] === Type::T_MAP) {
            $keys = $item[1];
            $values = $item[2];
            $list = [];

            for ($i = 0, $s = count($keys); $i < $s; $i++) {
                $key = $keys[$i];
                $value = $values[$i];

                $list[] = [
                    Type::T_LIST,
                    '',
                    [[Type::T_KEYWORD,
$this->compileStringContent($this->coerceString($key))], $value]
                ];
            }

            return [Type::T_LIST, ',', $list];
        }

        return [Type::T_LIST, $delim, ! isset($item) ? []: [$item]];
    }

    /**
     * Coerce color for expression
     *
     * @param array $value
     *
     * @return array|null
     */
    protected function coerceForExpression($value)
    {
        if ($color = $this->coerceColor($value)) {
            return $color;
        }

        return $value;
    }

    /**
     * Coerce value to color
     *
     * @param array $value
     *
     * @return array|null
     */
    protected function coerceColor($value)
    {
        switch ($value[0]) {
            case Type::T_COLOR:
                return $value;

            case Type::T_KEYWORD:
                $name = strtolower($value[1]);

                if (isset(Colors::$cssColors[$name])) {
                    $rgba = explode(',',
Colors::$cssColors[$name]);

                    return isset($rgba[3])
                        ? [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1],
(int) $rgba[2], (int) $rgba[3]]
                        : [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1],
(int) $rgba[2]];
                }

                return null;
        }

        return null;
    }

    /**
     * Coerce value to string
     *
     * @param array $value
     *
     * @return array|null
     */
    protected function coerceString($value)
    {
        if ($value[0] === Type::T_STRING) {
            return $value;
        }

        return [Type::T_STRING, '',
[$this->compileValue($value)]];
    }

    /**
     * Coerce value to a percentage
     *
     * @param array $value
     *
     * @return integer|float
     */
    protected function coercePercent($value)
    {
        if ($value[0] === Type::T_NUMBER) {
            if (! empty($value[2]['%'])) {
                return $value[1] / 100;
            }

            return $value[1];
        }

        return 0;
    }

    /**
     * Assert value is a map
     *
     * @api
     *
     * @param array $value
     *
     * @return array
     *
     * @throws \Exception
     */
    public function assertMap($value)
    {
        $value = $this->coerceMap($value);

        if ($value[0] !== Type::T_MAP) {
            $this->throwError('expecting map, %s received',
$value[0]);
        }

        return $value;
    }

    /**
     * Assert value is a list
     *
     * @api
     *
     * @param array $value
     *
     * @return array
     *
     * @throws \Exception
     */
    public function assertList($value)
    {
        if ($value[0] !== Type::T_LIST) {
            $this->throwError('expecting list, %s received',
$value[0]);
        }

        return $value;
    }

    /**
     * Assert value is a color
     *
     * @api
     *
     * @param array $value
     *
     * @return array
     *
     * @throws \Exception
     */
    public function assertColor($value)
    {
        if ($color = $this->coerceColor($value)) {
            return $color;
        }

        $this->throwError('expecting color, %s received',
$value[0]);
    }

    /**
     * Assert value is a number
     *
     * @api
     *
     * @param array $value
     *
     * @return integer|float
     *
     * @throws \Exception
     */
    public function assertNumber($value)
    {
        if ($value[0] !== Type::T_NUMBER) {
            $this->throwError('expecting number, %s received',
$value[0]);
        }

        return $value[1];
    }

    /**
     * Make sure a color's components don't go out of bounds
     *
     * @param array $c
     *
     * @return array
     */
    protected function fixColor($c)
    {
        foreach ([1, 2, 3] as $i) {
            if ($c[$i] < 0) {
                $c[$i] = 0;
            }

            if ($c[$i] > 255) {
                $c[$i] = 255;
            }
        }

        return $c;
    }

    /**
     * Convert RGB to HSL
     *
     * @api
     *
     * @param integer $red
     * @param integer $green
     * @param integer $blue
     *
     * @return array
     */
    public function toHSL($red, $green, $blue)
    {
        $min = min($red, $green, $blue);
        $max = max($red, $green, $blue);

        $l = $min + $max;
        $d = $max - $min;

        if ((int) $d === 0) {
            $h = $s = 0;
        } else {
            if ($l < 255) {
                $s = $d / $l;
            } else {
                $s = $d / (510 - $l);
            }

            if ($red == $max) {
                $h = 60 * ($green - $blue) / $d;
            } elseif ($green == $max) {
                $h = 60 * ($blue - $red) / $d + 120;
            } elseif ($blue == $max) {
                $h = 60 * ($red - $green) / $d + 240;
            }
        }

        return [Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1];
    }

    /**
     * Hue to RGB helper
     *
     * @param float $m1
     * @param float $m2
     * @param float $h
     *
     * @return float
     */
    protected function hueToRGB($m1, $m2, $h)
    {
        if ($h < 0) {
            $h += 1;
        } elseif ($h > 1) {
            $h -= 1;
        }

        if ($h * 6 < 1) {
            return $m1 + ($m2 - $m1) * $h * 6;
        }

        if ($h * 2 < 1) {
            return $m2;
        }

        if ($h * 3 < 2) {
            return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
        }

        return $m1;
    }

    /**
     * Convert HSL to RGB
     *
     * @api
     *
     * @param integer $hue        H from 0 to 360
     * @param integer $saturation S from 0 to 100
     * @param integer $lightness  L from 0 to 100
     *
     * @return array
     */
    public function toRGB($hue, $saturation, $lightness)
    {
        if ($hue < 0) {
            $hue += 360;
        }

        $h = $hue / 360;
        $s = min(100, max(0, $saturation)) / 100;
        $l = min(100, max(0, $lightness)) / 100;

        $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
        $m1 = $l * 2 - $m2;

        $r = $this->hueToRGB($m1, $m2, $h + 1/3) * 255;
        $g = $this->hueToRGB($m1, $m2, $h) * 255;
        $b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255;

        $out = [Type::T_COLOR, $r, $g, $b];

        return $out;
    }

    // Built in functions

    //protected static $libCall = ['name', 'args...'];
    protected function libCall($args, $kwargs)
    {
        $name =
$this->compileStringContent($this->coerceString($this->reduce(array_shift($args),
true)));

        $posArgs = [];

        foreach ($args as $arg) {
            if (empty($arg[0])) {
                if ($arg[2] === true) {
                    $tmp = $this->reduce($arg[1]);

                    if ($tmp[0] === Type::T_LIST) {
                        foreach ($tmp[2] as $item) {
                            $posArgs[] = [null, $item, false];
                        }
                    } else {
                        $posArgs[] = [null, $tmp, true];
                    }

                    continue;
                }

                $posArgs[] = [null, $this->reduce($arg), false];
                continue;
            }

            $posArgs[] = [null, $arg, false];
        }

        if (count($kwargs)) {
            foreach ($kwargs as $key => $value) {
                $posArgs[] = [[Type::T_VARIABLE, $key], $value, false];
            }
        }

        return $this->reduce([Type::T_FUNCTION_CALL, $name, $posArgs]);
    }

    protected static $libIf = ['condition', 'if-true',
'if-false'];
    protected function libIf($args)
    {
        list($cond, $t, $f) = $args;

        if (! $this->isTruthy($this->reduce($cond, true))) {
            return $this->reduce($f, true);
        }

        return $this->reduce($t, true);
    }

    protected static $libIndex = ['list', 'value'];
    protected function libIndex($args)
    {
        list($list, $value) = $args;

        if ($value[0] === Type::T_MAP) {
            return static::$null;
        }

        if ($list[0] === Type::T_MAP ||
            $list[0] === Type::T_STRING ||
            $list[0] === Type::T_KEYWORD ||
            $list[0] === Type::T_INTERPOLATE
        ) {
            $list = $this->coerceList($list, ' ');
        }

        if ($list[0] !== Type::T_LIST) {
            return static::$null;
        }

        $values = [];

        foreach ($list[2] as $item) {
            $values[] = $this->normalizeValue($item);
        }

        $key = array_search($this->normalizeValue($value), $values);

        return false === $key ? static::$null : $key + 1;
    }

    protected static $libRgb = ['red', 'green',
'blue'];
    protected function libRgb($args)
    {
        list($r, $g, $b) = $args;

        return [Type::T_COLOR, $r[1], $g[1], $b[1]];
    }

    protected static $libRgba = [
        ['red', 'color'],
        'green', 'blue', 'alpha'];
    protected function libRgba($args)
    {
        if ($color = $this->coerceColor($args[0])) {
            $num = isset($args[3]) ? $args[3] : $args[1];
            $alpha = $this->assertNumber($num);
            $color[4] = $alpha;

            return $color;
        }

        list($r, $g, $b, $a) = $args;

        return [Type::T_COLOR, $r[1], $g[1], $b[1], $a[1]];
    }

    // helper function for adjust_color, change_color, and scale_color
    protected function alterColor($args, $fn)
    {
        $color = $this->assertColor($args[0]);

        foreach ([1, 2, 3, 7] as $i) {
            if (isset($args[$i])) {
                $val = $this->assertNumber($args[$i]);
                $ii = $i === 7 ? 4 : $i; // alpha
                $color[$ii] = call_user_func($fn, isset($color[$ii]) ?
$color[$ii] : 0, $val, $i);
            }
        }

        if (isset($args[4]) || isset($args[5]) || isset($args[6])) {
            $hsl = $this->toHSL($color[1], $color[2], $color[3]);

            foreach ([4, 5, 6] as $i) {
                if (isset($args[$i])) {
                    $val = $this->assertNumber($args[$i]);
                    $hsl[$i - 3] = call_user_func($fn, $hsl[$i - 3], $val,
$i);
                }
            }

            $rgb = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);

            if (isset($color[4])) {
                $rgb[4] = $color[4];
            }

            $color = $rgb;
        }

        return $color;
    }

    protected static $libAdjustColor = [
        'color', 'red', 'green',
'blue',
        'hue', 'saturation', 'lightness',
'alpha'
    ];
    protected function libAdjustColor($args)
    {
        return $this->alterColor($args, function ($base, $alter, $i) {
            return $base + $alter;
        });
    }

    protected static $libChangeColor = [
        'color', 'red', 'green',
'blue',
        'hue', 'saturation', 'lightness',
'alpha'
    ];
    protected function libChangeColor($args)
    {
        return $this->alterColor($args, function ($base, $alter, $i) {
            return $alter;
        });
    }

    protected static $libScaleColor = [
        'color', 'red', 'green',
'blue',
        'hue', 'saturation', 'lightness',
'alpha'
    ];
    protected function libScaleColor($args)
    {
        return $this->alterColor($args, function ($base, $scale, $i) {
            // 1, 2, 3 - rgb
            // 4, 5, 6 - hsl
            // 7 - a
            switch ($i) {
                case 1:
                case 2:
                case 3:
                    $max = 255;
                    break;

                case 4:
                    $max = 360;
                    break;

                case 7:
                    $max = 1;
                    break;

                default:
                    $max = 100;
            }

            $scale = $scale / 100;

            if ($scale < 0) {
                return $base * $scale + $base;
            }

            return ($max - $base) * $scale + $base;
        });
    }

    protected static $libIeHexStr = ['color'];
    protected function libIeHexStr($args)
    {
        $color = $this->coerceColor($args[0]);
        $color[4] = isset($color[4]) ? round(255 * $color[4]) : 255;

        return sprintf('#%02X%02X%02X%02X', $color[4], $color[1],
$color[2], $color[3]);
    }

    protected static $libRed = ['color'];
    protected function libRed($args)
    {
        $color = $this->coerceColor($args[0]);

        return $color[1];
    }

    protected static $libGreen = ['color'];
    protected function libGreen($args)
    {
        $color = $this->coerceColor($args[0]);

        return $color[2];
    }

    protected static $libBlue = ['color'];
    protected function libBlue($args)
    {
        $color = $this->coerceColor($args[0]);

        return $color[3];
    }

    protected static $libAlpha = ['color'];
    protected function libAlpha($args)
    {
        if ($color = $this->coerceColor($args[0])) {
            return isset($color[4]) ? $color[4] : 1;
        }

        // this might be the IE function, so return value unchanged
        return null;
    }

    protected static $libOpacity = ['color'];
    protected function libOpacity($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

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

    // mix two colors
    protected static $libMix = ['color-1', 'color-2',
'weight'];
    protected function libMix($args)
    {
        list($first, $second, $weight) = $args;

        $first = $this->assertColor($first);
        $second = $this->assertColor($second);

        if (! isset($weight)) {
            $weight = 0.5;
        } else {
            $weight = $this->coercePercent($weight);
        }

        $firstAlpha = isset($first[4]) ? $first[4] : 1;
        $secondAlpha = isset($second[4]) ? $second[4] : 1;

        $w = $weight * 2 - 1;
        $a = $firstAlpha - $secondAlpha;

        $w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) /
2.0;
        $w2 = 1.0 - $w1;

        $new = [Type::T_COLOR,
            $w1 * $first[1] + $w2 * $second[1],
            $w1 * $first[2] + $w2 * $second[2],
            $w1 * $first[3] + $w2 * $second[3],
        ];

        if ($firstAlpha != 1.0 || $secondAlpha != 1.0) {
            $new[] = $firstAlpha * $weight + $secondAlpha * (1 - $weight);
        }

        return $this->fixColor($new);
    }

    protected static $libHsl = ['hue', 'saturation',
'lightness'];
    protected function libHsl($args)
    {
        list($h, $s, $l) = $args;

        return $this->toRGB($h[1], $s[1], $l[1]);
    }

    protected static $libHsla = ['hue', 'saturation',
'lightness', 'alpha'];
    protected function libHsla($args)
    {
        list($h, $s, $l, $a) = $args;

        $color = $this->toRGB($h[1], $s[1], $l[1]);
        $color[4] = $a[1];

        return $color;
    }

    protected static $libHue = ['color'];
    protected function libHue($args)
    {
        $color = $this->assertColor($args[0]);
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);

        return new Node\Number($hsl[1], 'deg');
    }

    protected static $libSaturation = ['color'];
    protected function libSaturation($args)
    {
        $color = $this->assertColor($args[0]);
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);

        return new Node\Number($hsl[2], '%');
    }

    protected static $libLightness = ['color'];
    protected function libLightness($args)
    {
        $color = $this->assertColor($args[0]);
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);

        return new Node\Number($hsl[3], '%');
    }

    protected function adjustHsl($color, $idx, $amount)
    {
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);
        $hsl[$idx] += $amount;
        $out = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);

        if (isset($color[4])) {
            $out[4] = $color[4];
        }

        return $out;
    }

    protected static $libAdjustHue = ['color',
'degrees'];
    protected function libAdjustHue($args)
    {
        $color = $this->assertColor($args[0]);
        $degrees = $this->assertNumber($args[1]);

        return $this->adjustHsl($color, 1, $degrees);
    }

    protected static $libLighten = ['color', 'amount'];
    protected function libLighten($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = Util::checkRange('amount', new Range(0, 100),
$args[1], '%');

        return $this->adjustHsl($color, 3, $amount);
    }

    protected static $libDarken = ['color', 'amount'];
    protected function libDarken($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = Util::checkRange('amount', new Range(0, 100),
$args[1], '%');

        return $this->adjustHsl($color, 3, -$amount);
    }

    protected static $libSaturate = ['color',
'amount'];
    protected function libSaturate($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

        $color = $this->assertColor($value);
        $amount = 100 * $this->coercePercent($args[1]);

        return $this->adjustHsl($color, 2, $amount);
    }

    protected static $libDesaturate = ['color',
'amount'];
    protected function libDesaturate($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = 100 * $this->coercePercent($args[1]);

        return $this->adjustHsl($color, 2, -$amount);
    }

    protected static $libGrayscale = ['color'];
    protected function libGrayscale($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

        return $this->adjustHsl($this->assertColor($value), 2, -100);
    }

    protected static $libComplement = ['color'];
    protected function libComplement($args)
    {
        return $this->adjustHsl($this->assertColor($args[0]), 1,
180);
    }

    protected static $libInvert = ['color'];
    protected function libInvert($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

        $color = $this->assertColor($value);
        $color[1] = 255 - $color[1];
        $color[2] = 255 - $color[2];
        $color[3] = 255 - $color[3];

        return $color;
    }

    // increases opacity by amount
    protected static $libOpacify = ['color', 'amount'];
    protected function libOpacify($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = $this->coercePercent($args[1]);

        $color[4] = (isset($color[4]) ? $color[4] : 1) + $amount;
        $color[4] = min(1, max(0, $color[4]));

        return $color;
    }

    protected static $libFadeIn = ['color', 'amount'];
    protected function libFadeIn($args)
    {
        return $this->libOpacify($args);
    }

    // decreases opacity by amount
    protected static $libTransparentize = ['color',
'amount'];
    protected function libTransparentize($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = $this->coercePercent($args[1]);

        $color[4] = (isset($color[4]) ? $color[4] : 1) - $amount;
        $color[4] = min(1, max(0, $color[4]));

        return $color;
    }

    protected static $libFadeOut = ['color', 'amount'];
    protected function libFadeOut($args)
    {
        return $this->libTransparentize($args);
    }

    protected static $libUnquote = ['string'];
    protected function libUnquote($args)
    {
        $str = $args[0];

        if ($str[0] === Type::T_STRING) {
            $str[1] = '';
        }

        return $str;
    }

    protected static $libQuote = ['string'];
    protected function libQuote($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_STRING && ! empty($value[1])) {
            return $value;
        }

        return [Type::T_STRING, '"', [$value]];
    }

    protected static $libPercentage = ['value'];
    protected function libPercentage($args)
    {
        return new Node\Number($this->coercePercent($args[0]) * 100,
'%');
    }

    protected static $libRound = ['value'];
    protected function libRound($args)
    {
        $num = $args[0];

        return new Node\Number(round($num[1]), $num[2]);
    }

    protected static $libFloor = ['value'];
    protected function libFloor($args)
    {
        $num = $args[0];

        return new Node\Number(floor($num[1]), $num[2]);
    }

    protected static $libCeil = ['value'];
    protected function libCeil($args)
    {
        $num = $args[0];

        return new Node\Number(ceil($num[1]), $num[2]);
    }

    protected static $libAbs = ['value'];
    protected function libAbs($args)
    {
        $num = $args[0];

        return new Node\Number(abs($num[1]), $num[2]);
    }

    protected function libMin($args)
    {
        $numbers = $this->getNormalizedNumbers($args);
        $min = null;

        foreach ($numbers as $key => $number) {
            if (null === $min || $number[1] <= $min[1]) {
                $min = [$key, $number[1]];
            }
        }

        return $args[$min[0]];
    }

    protected function libMax($args)
    {
        $numbers = $this->getNormalizedNumbers($args);
        $max = null;

        foreach ($numbers as $key => $number) {
            if (null === $max || $number[1] >= $max[1]) {
                $max = [$key, $number[1]];
            }
        }

        return $args[$max[0]];
    }

    /**
     * Helper to normalize args containing numbers
     *
     * @param array $args
     *
     * @return array
     */
    protected function getNormalizedNumbers($args)
    {
        $unit = null;
        $originalUnit = null;
        $numbers = [];

        foreach ($args as $key => $item) {
            if ($item[0] !== Type::T_NUMBER) {
                $this->throwError('%s is not a number',
$item[0]);
                break;
            }

            $number = $item->normalize();

            if (null === $unit) {
                $unit = $number[2];
                $originalUnit = $item->unitStr();
            } elseif ($number[1] && $unit !== $number[2]) {
                $this->throwError('Incompatible units:
"%s" and "%s".', $originalUnit,
$item->unitStr());
                break;
            }

            $numbers[$key] = $number;
        }

        return $numbers;
    }

    protected static $libLength = ['list'];
    protected function libLength($args)
    {
        $list = $this->coerceList($args[0]);

        return count($list[2]);
    }

    //protected static $libListSeparator = ['list...'];
    protected function libListSeparator($args)
    {
        if (count($args) > 1) {
            return 'comma';
        }

        $list = $this->coerceList($args[0]);

        if (count($list[2]) <= 1) {
            return 'space';
        }

        if ($list[1] === ',') {
            return 'comma';
        }

        return 'space';
    }

    protected static $libNth = ['list', 'n'];
    protected function libNth($args)
    {
        $list = $this->coerceList($args[0]);
        $n = $this->assertNumber($args[1]);

        if ($n > 0) {
            $n--;
        } elseif ($n < 0) {
            $n += count($list[2]);
        }

        return isset($list[2][$n]) ? $list[2][$n] : static::$defaultValue;
    }

    protected static $libSetNth = ['list', 'n',
'value'];
    protected function libSetNth($args)
    {
        $list = $this->coerceList($args[0]);
        $n = $this->assertNumber($args[1]);

        if ($n > 0) {
            $n--;
        } elseif ($n < 0) {
            $n += count($list[2]);
        }

        if (! isset($list[2][$n])) {
            $this->throwError('Invalid argument for
"n"');

            return null;
        }

        $list[2][$n] = $args[2];

        return $list;
    }

    protected static $libMapGet = ['map', 'key'];
    protected function libMapGet($args)
    {
        $map = $this->assertMap($args[0]);
        $key =
$this->compileStringContent($this->coerceString($args[1]));

        for ($i = count($map[1]) - 1; $i >= 0; $i--) {
            if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
                return $map[2][$i];
            }
        }

        return static::$null;
    }

    protected static $libMapKeys = ['map'];
    protected function libMapKeys($args)
    {
        $map = $this->assertMap($args[0]);
        $keys = $map[1];

        return [Type::T_LIST, ',', $keys];
    }

    protected static $libMapValues = ['map'];
    protected function libMapValues($args)
    {
        $map = $this->assertMap($args[0]);
        $values = $map[2];

        return [Type::T_LIST, ',', $values];
    }

    protected static $libMapRemove = ['map', 'key'];
    protected function libMapRemove($args)
    {
        $map = $this->assertMap($args[0]);
        $key =
$this->compileStringContent($this->coerceString($args[1]));

        for ($i = count($map[1]) - 1; $i >= 0; $i--) {
            if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
                array_splice($map[1], $i, 1);
                array_splice($map[2], $i, 1);
            }
        }

        return $map;
    }

    protected static $libMapHasKey = ['map', 'key'];
    protected function libMapHasKey($args)
    {
        $map = $this->assertMap($args[0]);
        $key =
$this->compileStringContent($this->coerceString($args[1]));

        for ($i = count($map[1]) - 1; $i >= 0; $i--) {
            if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
                return true;
            }
        }

        return false;
    }

    protected static $libMapMerge = ['map-1', 'map-2'];
    protected function libMapMerge($args)
    {
        $map1 = $this->assertMap($args[0]);
        $map2 = $this->assertMap($args[1]);

        foreach ($map2[1] as $i2 => $key2) {
            $key =
$this->compileStringContent($this->coerceString($key2));

            foreach ($map1[1] as $i1 => $key1) {
                if ($key ===
$this->compileStringContent($this->coerceString($key1))) {
                    $map1[2][$i1] = $map2[2][$i2];
                    continue 2;
                }
            }

            $map1[1][] = $map2[1][$i2];
            $map1[2][] = $map2[2][$i2];
        }

        return $map1;
    }

    protected static $libKeywords = ['args'];
    protected function libKeywords($args)
    {
        $this->assertList($args[0]);

        $keys = [];
        $values = [];

        foreach ($args[0][2] as $name => $arg) {
            $keys[] = [Type::T_KEYWORD, $name];
            $values[] = $arg;
        }

        return [Type::T_MAP, $keys, $values];
    }

    protected function listSeparatorForJoin($list1, $sep)
    {
        if (! isset($sep)) {
            return $list1[1];
        }

        switch ($this->compileValue($sep)) {
            case 'comma':
                return ',';

            case 'space':
                return '';

            default:
                return $list1[1];
        }
    }

    protected static $libJoin = ['list1', 'list2',
'separator'];
    protected function libJoin($args)
    {
        list($list1, $list2, $sep) = $args;

        $list1 = $this->coerceList($list1, ' ');
        $list2 = $this->coerceList($list2, ' ');
        $sep = $this->listSeparatorForJoin($list1, $sep);

        return [Type::T_LIST, $sep, array_merge($list1[2], $list2[2])];
    }

    protected static $libAppend = ['list', 'val',
'separator'];
    protected function libAppend($args)
    {
        list($list1, $value, $sep) = $args;

        $list1 = $this->coerceList($list1, ' ');
        $sep = $this->listSeparatorForJoin($list1, $sep);

        return [Type::T_LIST, $sep, array_merge($list1[2], [$value])];
    }

    protected function libZip($args)
    {
        foreach ($args as $arg) {
            $this->assertList($arg);
        }

        $lists = [];
        $firstList = array_shift($args);

        foreach ($firstList[2] as $key => $item) {
            $list = [Type::T_LIST, '', [$item]];

            foreach ($args as $arg) {
                if (isset($arg[2][$key])) {
                    $list[2][] = $arg[2][$key];
                } else {
                    break 2;
                }
            }

            $lists[] = $list;
        }

        return [Type::T_LIST, ',', $lists];
    }

    protected static $libTypeOf = ['value'];
    protected function libTypeOf($args)
    {
        $value = $args[0];

        switch ($value[0]) {
            case Type::T_KEYWORD:
                if ($value === static::$true || $value === static::$false)
{
                    return 'bool';
                }

                if ($this->coerceColor($value)) {
                    return 'color';
                }

                // fall-thru
            case Type::T_FUNCTION:
                return 'string';

            case Type::T_LIST:
                if (isset($value[3]) && $value[3]) {
                    return 'arglist';
                }

                // fall-thru
            default:
                return $value[0];
        }
    }

    protected static $libUnit = ['number'];
    protected function libUnit($args)
    {
        $num = $args[0];

        if ($num[0] === Type::T_NUMBER) {
            return [Type::T_STRING, '"',
[$num->unitStr()]];
        }

        return '';
    }

    protected static $libUnitless = ['number'];
    protected function libUnitless($args)
    {
        $value = $args[0];

        return $value[0] === Type::T_NUMBER &&
$value->unitless();
    }

    protected static $libComparable = ['number-1',
'number-2'];
    protected function libComparable($args)
    {
        list($number1, $number2) = $args;

        if (! isset($number1[0]) || $number1[0] !== Type::T_NUMBER ||
            ! isset($number2[0]) || $number2[0] !== Type::T_NUMBER
        ) {
            $this->throwError('Invalid argument(s) for
"comparable"');

            return null;
        }

        $number1 = $number1->normalize();
        $number2 = $number2->normalize();

        return $number1[2] === $number2[2] || $number1->unitless() ||
$number2->unitless();
    }

    protected static $libStrIndex = ['string',
'substring'];
    protected function libStrIndex($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $substring = $this->coerceString($args[1]);
        $substringContent = $this->compileStringContent($substring);

        $result = strpos($stringContent, $substringContent);

        return $result === false ? static::$null : new Node\Number($result
+ 1, '');
    }

    protected static $libStrInsert = ['string',
'insert', 'index'];
    protected function libStrInsert($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $insert = $this->coerceString($args[1]);
        $insertContent = $this->compileStringContent($insert);

        list(, $index) = $args[2];

        $string[2] = [substr_replace($stringContent, $insertContent, $index
- 1, 0)];

        return $string;
    }

    protected static $libStrLength = ['string'];
    protected function libStrLength($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        return new Node\Number(strlen($stringContent), '');
    }

    protected static $libStrSlice = ['string',
'start-at', 'end-at'];
    protected function libStrSlice($args)
    {
        if (isset($args[2]) && $args[2][1] == 0) {
            return static::$nullString;
        }

        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $start = (int) $args[1][1];

        if ($start > 0) {
            $start--;
        }

        $end    = (int) $args[2][1];
        $length = $end < 0 ? $end + 1 : ($end > 0 ? $end - $start :
$end);

        $string[2] = $length
            ? [substr($stringContent, $start, $length)]
            : [substr($stringContent, $start)];

        return $string;
    }

    protected static $libToLowerCase = ['string'];
    protected function libToLowerCase($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $string[2] = [function_exists('mb_strtolower') ?
mb_strtolower($stringContent) : strtolower($stringContent)];

        return $string;
    }

    protected static $libToUpperCase = ['string'];
    protected function libToUpperCase($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $string[2] = [function_exists('mb_strtoupper') ?
mb_strtoupper($stringContent) : strtoupper($stringContent)];

        return $string;
    }

    protected static $libFeatureExists = ['feature'];
    protected function libFeatureExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->toBool(
            array_key_exists($name, $this->registeredFeatures) ?
$this->registeredFeatures[$name] : false
        );
    }

    protected static $libFunctionExists = ['name'];
    protected function libFunctionExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        // user defined functions
        if ($this->has(static::$namespaces['function'] .
$name)) {
            return true;
        }

        $name = $this->normalizeName($name);

        if (isset($this->userFunctions[$name])) {
            return true;
        }

        // built-in functions
        $f = $this->getBuiltinFunction($name);

        return $this->toBool(is_callable($f));
    }

    protected static $libGlobalVariableExists = ['name'];
    protected function libGlobalVariableExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->has($name, $this->rootEnv);
    }

    protected static $libMixinExists = ['name'];
    protected function libMixinExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->has(static::$namespaces['mixin'] .
$name);
    }

    protected static $libVariableExists = ['name'];
    protected function libVariableExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->has($name);
    }

    /**
     * Workaround IE7's content counter bug.
     *
     * @param array $args
     *
     * @return array
     */
    protected function libCounter($args)
    {
        $list = array_map([$this, 'compileValue'], $args);

        return [Type::T_STRING, '', ['counter(' .
implode(',', $list) . ')']];
    }

    protected static $libRandom = ['limit'];
    protected function libRandom($args)
    {
        if (isset($args[0])) {
            $n = $this->assertNumber($args[0]);

            if ($n < 1) {
                $this->throwError("limit must be greater than or
equal to 1");

                return null;
            }

            return new Node\Number(mt_rand(1, $n), '');
        }

        return new Node\Number(mt_rand(1, mt_getrandmax()), '');
    }

    protected function libUniqueId()
    {
        static $id;

        if (! isset($id)) {
            $id = mt_rand(0, pow(36, 8));
        }

        $id += mt_rand(0, 10) + 1;

        return [Type::T_STRING, '', ['u' .
str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)]];
    }

    protected static $libInspect = ['value'];
    protected function libInspect($args)
    {
        if ($args[0] === static::$null) {
            return [Type::T_KEYWORD, 'null'];
        }

        return $args[0];
    }

    /**
     * Preprocess selector args
     *
     * @param array $arg
     *
     * @return array|boolean
     */
    protected function getSelectorArg($arg)
    {
        static $parser = null;

        if (is_null($parser)) {
            $parser = $this->parserFactory(__METHOD__);
        }

        $arg = $this->libUnquote([$arg]);
        $arg = $this->compileValue($arg);

        $parsedSelector = [];

        if ($parser->parseSelector($arg, $parsedSelector)) {
            $selector = $this->evalSelectors($parsedSelector);
            $gluedSelector = $this->glueFunctionSelectors($selector);

            return $gluedSelector;
        }

        return false;
    }

    /**
     * Postprocess selector to output in right format
     *
     * @param array $selectors
     *
     * @return string
     */
    protected function formatOutputSelector($selectors)
    {
        $selectors = $this->collapseSelectors($selectors, true);

        return $selectors;
    }

    protected static $libIsSuperselector = ['super',
'sub'];
    protected function libIsSuperselector($args)
    {
        list($super, $sub) = $args;

        $super = $this->getSelectorArg($super);
        $sub = $this->getSelectorArg($sub);

        return $this->isSuperSelector($super, $sub);
    }

    /**
     * Test a $super selector again $sub
     *
     * @param array $super
     * @param array $sub
     *
     * @return boolean
     */
    protected function isSuperSelector($super, $sub)
    {
        // one and only one selector for each arg
        if (! $super || count($super) !== 1) {
            $this->throwError("Invalid super selector for
isSuperSelector()");
        }

        if (! $sub || count($sub) !== 1) {
            $this->throwError("Invalid sub selector for
isSuperSelector()");
        }

        $super = reset($super);
        $sub = reset($sub);

        $i = 0;
        $nextMustMatch = false;

        foreach ($super as $node) {
            $compound = '';

            array_walk_recursive(
                $node,
                function ($value, $key) use (&$compound) {
                    $compound .= $value;
                }
            );

            if ($this->isImmediateRelationshipCombinator($compound)) {
                if ($node !== $sub[$i]) {
                    return false;
                }

                $nextMustMatch = true;
                $i++;
            } else {
                while ($i < count($sub) && !
$this->isSuperPart($node, $sub[$i])) {
                    if ($nextMustMatch) {
                        return false;
                    }

                    $i++;
                }

                if ($i >= count($sub)) {
                    return false;
                }

                $nextMustMatch = false;
                $i++;
            }
        }

        return true;
    }

    /**
     * Test a part of super selector again a part of sub selector
     *
     * @param array $superParts
     * @param array $subParts
     *
     * @return boolean
     */
    protected function isSuperPart($superParts, $subParts)
    {
        $i = 0;

        foreach ($superParts as $superPart) {
            while ($i < count($subParts) && $subParts[$i] !==
$superPart) {
                $i++;
            }

            if ($i >= count($subParts)) {
                return false;
            }

            $i++;
        }

        return true;
    }

    //protected static $libSelectorAppend = ['selector...'];
    protected function libSelectorAppend($args)
    {
        if (count($args) < 1) {
            $this->throwError("selector-append() needs at least 1
argument");
        }

        $selectors = array_map([$this, 'getSelectorArg'], $args);

        return
$this->formatOutputSelector($this->selectorAppend($selectors));
    }

    /**
     * Append parts of the last selector in the list to the previous,
recursively
     *
     * @param array $selectors
     *
     * @return array
     *
     * @throws \Leafo\ScssPhp\Exception\CompilerException
     */
    protected function selectorAppend($selectors)
    {
        $lastSelectors = array_pop($selectors);

        if (! $lastSelectors) {
            $this->throwError("Invalid selector list in
selector-append()");
        }

        while (count($selectors)) {
            $previousSelectors = array_pop($selectors);

            if (! $previousSelectors) {
                $this->throwError("Invalid selector list in
selector-append()");
            }

            // do the trick, happening $lastSelector to $previousSelector
            $appended = [];

            foreach ($lastSelectors as $lastSelector) {
                $previous = $previousSelectors;

                foreach ($lastSelector as $lastSelectorParts) {
                    foreach ($lastSelectorParts as $lastSelectorPart) {
                        foreach ($previous as $i => $previousSelector) {
                            foreach ($previousSelector as $j =>
$previousSelectorParts) {
                                $previous[$i][$j][] = $lastSelectorPart;
                            }
                        }
                    }
                }

                foreach ($previous as $ps) {
                    $appended[] = $ps;
                }
            }

            $lastSelectors = $appended;
        }

        return $lastSelectors;
    }

    protected static $libSelectorExtend = ['selectors',
'extendee', 'extender'];
    protected function libSelectorExtend($args)
    {
        list($selectors, $extendee, $extender) = $args;

        $selectors = $this->getSelectorArg($selectors);
        $extendee = $this->getSelectorArg($extendee);
        $extender = $this->getSelectorArg($extender);

        if (! $selectors || ! $extendee || ! $extender) {
            $this->throwError("selector-extend() invalid
arguments");
        }

        $extended = $this->extendOrReplaceSelectors($selectors,
$extendee, $extender);

        return $this->formatOutputSelector($extended);
    }

    protected static $libSelectorReplace = ['selectors',
'original', 'replacement'];
    protected function libSelectorReplace($args)
    {
        list($selectors, $original, $replacement) = $args;

        $selectors = $this->getSelectorArg($selectors);
        $original = $this->getSelectorArg($original);
        $replacement = $this->getSelectorArg($replacement);

        if (! $selectors || ! $original || ! $replacement) {
            $this->throwError("selector-replace() invalid
arguments");
        }

        $replaced = $this->extendOrReplaceSelectors($selectors,
$original, $replacement, true);

        return $this->formatOutputSelector($replaced);
    }

    /**
     * Extend/replace in selectors
     * used by selector-extend and selector-replace that use the same logic
     *
     * @param array   $selectors
     * @param array   $extendee
     * @param array   $extender
     * @param boolean $replace
     *
     * @return array
     */
    protected function extendOrReplaceSelectors($selectors, $extendee,
$extender, $replace = false)
    {
        $saveExtends = $this->extends;
        $saveExtendsMap = $this->extendsMap;

        $this->extends = [];
        $this->extendsMap = [];

        foreach ($extendee as $es) {
            // only use the first one
            $this->pushExtends(reset($es), $extender, null);
        }

        $extended = [];

        foreach ($selectors as $selector) {
            if (! $replace) {
                $extended[] = $selector;
            }

            $n = count($extended);

            $this->matchExtends($selector, $extended);

            // if didnt match, keep the original selector if we are in a
replace operation
            if ($replace and count($extended) === $n) {
                $extended[] = $selector;
            }
        }

        $this->extends = $saveExtends;
        $this->extendsMap = $saveExtendsMap;

        return $extended;
    }

    //protected static $libSelectorNest = ['selector...'];
    protected function libSelectorNest($args)
    {
        if (count($args) < 1) {
            $this->throwError("selector-nest() needs at least 1
argument");
        }

        $selectorsMap = array_map([$this, 'getSelectorArg'],
$args);

        $envs = [];
        foreach ($selectorsMap as $selectors) {
            $env = new Environment();
            $env->selectors = $selectors;

            $envs[] = $env;
        }

        $envs = array_reverse($envs);
        $env = $this->extractEnv($envs);
        $outputSelectors = $this->multiplySelectors($env);

        return $this->formatOutputSelector($outputSelectors);
    }

    protected static $libSelectorParse = ['selectors'];
    protected function libSelectorParse($args)
    {
        $selectors = reset($args);
        $selectors = $this->getSelectorArg($selectors);

        return $this->formatOutputSelector($selectors);
    }

    protected static $libSelectorUnify = ['selectors1',
'selectors2'];
    protected function libSelectorUnify($args)
    {
        list($selectors1, $selectors2) = $args;

        $selectors1 = $this->getSelectorArg($selectors1);
        $selectors2 = $this->getSelectorArg($selectors2);

        if (! $selectors1 || ! $selectors2) {
            $this->throwError("selector-unify() invalid
arguments");
        }

        // only consider the first compound of each
        $compound1 = reset($selectors1);
        $compound2 = reset($selectors2);

        // unify them and that's it
        $unified = $this->unifyCompoundSelectors($compound1,
$compound2);

        return $this->formatOutputSelector($unified);
    }

    /**
     * The selector-unify magic as its best
     * (at least works as expected on test cases)
     *
     * @param array $compound1
     * @param array $compound2
     * @return array|mixed
     */
    protected function unifyCompoundSelectors($compound1, $compound2)
    {
        if (! count($compound1)) {
            return $compound2;
        }

        if (! count($compound2)) {
            return $compound1;
        }

        // check that last part are compatible
        $lastPart1 = array_pop($compound1);
        $lastPart2 = array_pop($compound2);
        $last = $this->mergeParts($lastPart1, $lastPart2);

        if (! $last) {
            return [[]];
        }

        $unifiedCompound = [$last];
        $unifiedSelectors = [$unifiedCompound];

        // do the rest
        while (count($compound1) || count($compound2)) {
            $part1 = end($compound1);
            $part2 = end($compound2);

            if ($part1 && ($match2 =
$this->matchPartInCompound($part1, $compound2))) {
                list($compound2, $part2, $after2) = $match2;

                if ($after2) {
                    $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, $after2);
                }

                $c = $this->mergeParts($part1, $part2);
                $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, [$c]);
                $part1 = $part2 = null;

                array_pop($compound1);
            }

            if ($part2 && ($match1 =
$this->matchPartInCompound($part2, $compound1))) {
                list($compound1, $part1, $after1) = $match1;

                if ($after1) {
                    $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, $after1);
                }

                $c = $this->mergeParts($part2, $part1);
                $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, [$c]);
                $part1 = $part2 = null;

                array_pop($compound2);
            }

            $new = [];

            if ($part1 && $part2) {
                array_pop($compound1);
                array_pop($compound2);

                $s = $this->prependSelectors($unifiedSelectors,
[$part2]);
                $new = array_merge($new, $this->prependSelectors($s,
[$part1]));
                $s = $this->prependSelectors($unifiedSelectors,
[$part1]);
                $new = array_merge($new, $this->prependSelectors($s,
[$part2]));
            } elseif ($part1) {
                array_pop($compound1);

                $new = array_merge($new,
$this->prependSelectors($unifiedSelectors, [$part1]));
            } elseif ($part2) {
                array_pop($compound2);

                $new = array_merge($new,
$this->prependSelectors($unifiedSelectors, [$part2]));
            }

            if ($new) {
                $unifiedSelectors = $new;
            }
        }

        return $unifiedSelectors;
    }

    /**
     * Prepend each selector from $selectors with $parts
     *
     * @param array $selectors
     * @param array $parts
     *
     * @return array
     */
    protected function prependSelectors($selectors, $parts)
    {
        $new = [];

        foreach ($selectors as $compoundSelector) {
            array_unshift($compoundSelector, $parts);

            $new[] = $compoundSelector;
        }

        return $new;
    }

    /**
     * Try to find a matching part in a compound:
     * - with same html tag name
     * - with some class or id or something in common
     *
     * @param array $part
     * @param array $compound
     *
     * @return array|boolean
     */
    protected function matchPartInCompound($part, $compound)
    {
        $partTag = $this->findTagName($part);
        $before = $compound;
        $after = [];

        // try to find a match by tag name first
        while (count($before)) {
            $p = array_pop($before);

            if ($partTag && $partTag !== '*' &&
$partTag == $this->findTagName($p)) {
                return [$before, $p, $after];
            }

            $after[] = $p;
        }

        // try again matching a non empty intersection and a compatible
tagname
        $before = $compound;
        $after = [];

        while (count($before)) {
            $p = array_pop($before);

            if ($this->checkCompatibleTags($partTag,
$this->findTagName($p))) {
                if (count(array_intersect($part, $p))) {
                    return [$before, $p, $after];
                }
            }

            $after[] = $p;
        }

        return false;
    }

    /**
     * Merge two part list taking care that
     * - the html tag is coming first - if any
     * - the :something are coming last
     *
     * @param array $parts1
     * @param array $parts2
     *
     * @return array
     */
    protected function mergeParts($parts1, $parts2)
    {
        $tag1 = $this->findTagName($parts1);
        $tag2 = $this->findTagName($parts2);
        $tag = $this->checkCompatibleTags($tag1, $tag2);

        // not compatible tags
        if ($tag === false) {
            return [];
        }

        if ($tag) {
            if ($tag1) {
                $parts1 = array_diff($parts1, [$tag1]);
            }

            if ($tag2) {
                $parts2 = array_diff($parts2, [$tag2]);
            }
        }

        $mergedParts = array_merge($parts1, $parts2);
        $mergedOrderedParts = [];

        foreach ($mergedParts as $part) {
            if (strpos($part, ':') === 0) {
                $mergedOrderedParts[] = $part;
            }
        }

        $mergedParts = array_diff($mergedParts, $mergedOrderedParts);
        $mergedParts = array_merge($mergedParts, $mergedOrderedParts);

        if ($tag) {
            array_unshift($mergedParts, $tag);
        }

        return $mergedParts;
    }

    /**
     * Check the compatibility between two tag names:
     * if both are defined they should be identical or one has to be
'*'
     *
     * @param string $tag1
     * @param string $tag2
     *
     * @return array|boolean
     */
    protected function checkCompatibleTags($tag1, $tag2)
    {
        $tags = [$tag1, $tag2];
        $tags = array_unique($tags);
        $tags = array_filter($tags);

        if (count($tags)>1) {
            $tags = array_diff($tags, ['*']);
        }

        // not compatible nodes
        if (count($tags)>1) {
            return false;
        }

        return $tags;
    }

    /**
     * Find the html tag name in a selector parts list
     *
     * @param array $parts
     *
     * @return mixed|string
     */
    protected function findTagName($parts)
    {
        foreach ($parts as $part) {
            if (! preg_match('/^[\[.:#%_-]/', $part)) {
                return $part;
            }
        }

        return '';
    }

    protected static $libSimpleSelectors = ['selector'];
    protected function libSimpleSelectors($args)
    {
        $selector = reset($args);
        $selector = $this->getSelectorArg($selector);

        // remove selectors list layer, keeping the first one
        $selector = reset($selector);

        // remove parts list layer, keeping the first part
        $part = reset($selector);

        $listParts = [];

        foreach ($part as $p) {
            $listParts[] = [Type::T_STRING, '', [$p]];
        }

        return [Type::T_LIST, ',', $listParts];
    }
}
vendor/leafo/scssphp/src/Exception/CompilerException.php000064400000000516151166614540017514
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Exception;

/**
 * Compiler exception
 *
 * @author Oleksandr Savchenko <traveltino@gmail.com>
 */
class CompilerException extends \Exception
{
}
vendor/leafo/scssphp/src/Exception/ParserException.php000064400000000512151166614540017172
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Exception;

/**
 * Parser Exception
 *
 * @author Oleksandr Savchenko <traveltino@gmail.com>
 */
class ParserException extends \Exception
{
}
vendor/leafo/scssphp/src/Exception/RangeException.php000064400000000501151166614550016771
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Exception;

/**
 * Range exception
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class RangeException extends \Exception
{
}
vendor/leafo/scssphp/src/Exception/ServerException.php000064400000000503151166614550017205
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Exception;

/**
 * Server Exception
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class ServerException extends \Exception
{
}
vendor/leafo/scssphp/src/Formatter/Compact.php000064400000001425151166614550015457
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Formatter;

use Leafo\ScssPhp\Formatter;

/**
 * Compact formatter
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Compact extends Formatter
{
    /**
     * {@inheritdoc}
     */
    public function __construct()
    {
        $this->indentLevel = 0;
        $this->indentChar = '';
        $this->break = '';
        $this->open = ' {';
        $this->close = "}\n\n";
        $this->tagSeparator = ',';
        $this->assignSeparator = ':';
        $this->keepSemicolons = true;
    }

    /**
     * {@inheritdoc}
     */
    public function indentStr()
    {
        return ' ';
    }
}
vendor/leafo/scssphp/src/Formatter/Compressed.php000064400000003506151166614550016177
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Formatter;

use Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter\OutputBlock;

/**
 * Compressed formatter
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Compressed extends Formatter
{
    /**
     * {@inheritdoc}
     */
    public function __construct()
    {
        $this->indentLevel = 0;
        $this->indentChar = '  ';
        $this->break = '';
        $this->open = '{';
        $this->close = '}';
        $this->tagSeparator = ',';
        $this->assignSeparator = ':';
        $this->keepSemicolons = false;
    }

    /**
     * {@inheritdoc}
     */
    public function blockLines(OutputBlock $block)
    {
        $inner = $this->indentStr();

        $glue = $this->break . $inner;

        foreach ($block->lines as $index => $line) {
            if (substr($line, 0, 2) === '/*' &&
substr($line, 2, 1) !== '!') {
                unset($block->lines[$index]);
            } elseif (substr($line, 0, 3) === '/*!') {
                $block->lines[$index] = '/*' . substr($line,
3);
            }
        }

        $this->write($inner . implode($glue, $block->lines));

        if (! empty($block->children)) {
            $this->write($this->break);
        }
    }

    /**
     * Output block selectors
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
     */
    protected function blockSelectors(OutputBlock $block)
    {
        $inner = $this->indentStr();

        $this->write(
            $inner
            . implode(
                $this->tagSeparator,
                str_replace([' > ', ' + ', ' ~
'], ['>', '+', '~'],
$block->selectors)
            )
            . $this->open . $this->break
        );
    }
}
vendor/leafo/scssphp/src/Formatter/Crunched.php000064400000003256151166614550015630
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Formatter;

use Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter\OutputBlock;

/**
 * Crunched formatter
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class Crunched extends Formatter
{
    /**
     * {@inheritdoc}
     */
    public function __construct()
    {
        $this->indentLevel = 0;
        $this->indentChar = '  ';
        $this->break = '';
        $this->open = '{';
        $this->close = '}';
        $this->tagSeparator = ',';
        $this->assignSeparator = ':';
        $this->keepSemicolons = false;
    }

    /**
     * {@inheritdoc}
     */
    public function blockLines(OutputBlock $block)
    {
        $inner = $this->indentStr();

        $glue = $this->break . $inner;

        foreach ($block->lines as $index => $line) {
            if (substr($line, 0, 2) === '/*') {
                unset($block->lines[$index]);
            }
        }

        $this->write($inner . implode($glue, $block->lines));

        if (! empty($block->children)) {
            $this->write($this->break);
        }
    }

    /**
     * Output block selectors
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
     */
    protected function blockSelectors(OutputBlock $block)
    {
        $inner = $this->indentStr();

        $this->write(
            $inner
            . implode(
                $this->tagSeparator,
                str_replace([' > ', ' + ', ' ~
'], ['>', '+', '~'],
$block->selectors)
            )
            . $this->open . $this->break
        );
    }
}
vendor/leafo/scssphp/src/Formatter/Debug.php000064400000004760151166614550015124
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Formatter;

use Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter\OutputBlock;

/**
 * Debug formatter
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class Debug extends Formatter
{
    /**
     * {@inheritdoc}
     */
    public function __construct()
    {
        $this->indentLevel = 0;
        $this->indentChar = '';
        $this->break = "\n";
        $this->open = ' {';
        $this->close = ' }';
        $this->tagSeparator = ', ';
        $this->assignSeparator = ': ';
        $this->keepSemicolons = true;
    }

    /**
     * {@inheritdoc}
     */
    protected function indentStr()
    {
        return str_repeat('  ', $this->indentLevel);
    }

    /**
     * {@inheritdoc}
     */
    protected function blockLines(OutputBlock $block)
    {
        $indent = $this->indentStr();

        if (empty($block->lines)) {
            $this->write("{$indent}block->lines: []\n");

            return;
        }

        foreach ($block->lines as $index => $line) {
            $this->write("{$indent}block->lines[{$index}]:
$line\n");
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function blockSelectors(OutputBlock $block)
    {
        $indent = $this->indentStr();

        if (empty($block->selectors)) {
            $this->write("{$indent}block->selectors:
[]\n");

            return;
        }

        foreach ($block->selectors as $index => $selector) {
            $this->write("{$indent}block->selectors[{$index}]:
$selector\n");
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function blockChildren(OutputBlock $block)
    {
        $indent = $this->indentStr();

        if (empty($block->children)) {
            $this->write("{$indent}block->children: []\n");

            return;
        }

        $this->indentLevel++;

        foreach ($block->children as $i => $child) {
            $this->block($child);
        }

        $this->indentLevel--;
    }

    /**
     * {@inheritdoc}
     */
    protected function block(OutputBlock $block)
    {
        $indent = $this->indentStr();

        $this->write("{$indent}block->type:
{$block->type}\n" .
             "{$indent}block->depth: {$block->depth}\n");

        $this->currentBlock = $block;

        $this->blockSelectors($block);
        $this->blockLines($block);
        $this->blockChildren($block);
    }
}
vendor/leafo/scssphp/src/Formatter/Expanded.php000064400000002657151166614550015631
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Formatter;

use Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter\OutputBlock;

/**
 * Expanded formatter
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Expanded extends Formatter
{
    /**
     * {@inheritdoc}
     */
    public function __construct()
    {
        $this->indentLevel = 0;
        $this->indentChar = '  ';
        $this->break = "\n";
        $this->open = ' {';
        $this->close = '}';
        $this->tagSeparator = ', ';
        $this->assignSeparator = ': ';
        $this->keepSemicolons = true;
    }

    /**
     * {@inheritdoc}
     */
    protected function indentStr()
    {
        return str_repeat($this->indentChar, $this->indentLevel);
    }

    /**
     * {@inheritdoc}
     */
    protected function blockLines(OutputBlock $block)
    {
        $inner = $this->indentStr();

        $glue = $this->break . $inner;

        foreach ($block->lines as $index => $line) {
            if (substr($line, 0, 2) === '/*') {
                $block->lines[$index] =
preg_replace('/(\r|\n)+/', $glue, $line);
            }
        }

        $this->write($inner . implode($glue, $block->lines));

        if (empty($block->selectors) || ! empty($block->children)) {
            $this->write($this->break);
        }
    }
}
vendor/leafo/scssphp/src/Formatter/Nested.php000064400000011152151166614550015311
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Formatter;

use Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter\OutputBlock;

/**
 * Nested formatter
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Nested extends Formatter
{
    /**
     * @var integer
     */
    private $depth;

    /**
     * {@inheritdoc}
     */
    public function __construct()
    {
        $this->indentLevel = 0;
        $this->indentChar = '  ';
        $this->break = "\n";
        $this->open = ' {';
        $this->close = ' }';
        $this->tagSeparator = ', ';
        $this->assignSeparator = ': ';
        $this->keepSemicolons = true;
    }

    /**
     * {@inheritdoc}
     */
    protected function indentStr()
    {
        $n = $this->depth - 1;

        return str_repeat($this->indentChar, max($this->indentLevel +
$n, 0));
    }

    /**
     * {@inheritdoc}
     */
    protected function blockLines(OutputBlock $block)
    {
        $inner = $this->indentStr();

        $glue = $this->break . $inner;

        foreach ($block->lines as $index => $line) {
            if (substr($line, 0, 2) === '/*') {
                $block->lines[$index] =
preg_replace('/(\r|\n)+/', $glue, $line);
            }
        }

        $this->write($inner . implode($glue, $block->lines));

        if (! empty($block->children)) {
            $this->write($this->break);
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function blockSelectors(OutputBlock $block)
    {
        $inner = $this->indentStr();

        $this->write($inner
            . implode($this->tagSeparator, $block->selectors)
            . $this->open . $this->break);
    }

    /**
     * {@inheritdoc}
     */
    protected function blockChildren(OutputBlock $block)
    {
        foreach ($block->children as $i => $child) {
            $this->block($child);

            if ($i < count($block->children) - 1) {
                $this->write($this->break);

                if (isset($block->children[$i + 1])) {
                    $next = $block->children[$i + 1];

                    if ($next->depth === max($block->depth, 1)
&& $child->depth >= $next->depth) {
                        $this->write($this->break);
                    }
                }
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function block(OutputBlock $block)
    {
        if ($block->type === 'root') {
            $this->adjustAllChildren($block);
        }

        if (empty($block->lines) && empty($block->children))
{
            return;
        }

        $this->currentBlock = $block;


        $this->depth = $block->depth;

        if (! empty($block->selectors)) {
            $this->blockSelectors($block);

            $this->indentLevel++;
        }

        if (! empty($block->lines)) {
            $this->blockLines($block);
        }

        if (! empty($block->children)) {
            $this->blockChildren($block);
        }

        if (! empty($block->selectors)) {
            $this->indentLevel--;

            $this->write($this->close);
        }

        if ($block->type === 'root') {
            $this->write($this->break);
        }
    }

    /**
     * Adjust the depths of all children, depth first
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
     */
    private function adjustAllChildren(OutputBlock $block)
    {
        // flatten empty nested blocks
        $children = [];

        foreach ($block->children as $i => $child) {
            if (empty($child->lines) &&
empty($child->children)) {
                if (isset($block->children[$i + 1])) {
                    $block->children[$i + 1]->depth =
$child->depth;
                }

                continue;
            }

            $children[] = $child;
        }

        $count = count($children);

        for ($i = 0; $i < $count; $i++) {
            $depth = $children[$i]->depth;
            $j = $i + 1;

            if (isset($children[$j]) && $depth <
$children[$j]->depth) {
                $childDepth = $children[$j]->depth;

                for (; $j < $count; $j++) {
                    if ($depth < $children[$j]->depth &&
$childDepth >= $children[$j]->depth) {
                        $children[$j]->depth = $depth + 1;
                    }
                }
            }
        }

        $block->children = $children;

        // make relative to parent
        foreach ($block->children as $child) {
            $this->adjustAllChildren($child);

            $child->depth = $child->depth - $block->depth;
        }
    }
}
vendor/leafo/scssphp/src/Formatter/OutputBlock.php000064400000001514151166614550016343
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Formatter;

/**
 * Output block
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class OutputBlock
{
    /**
     * @var string
     */
    public $type;

    /**
     * @var integer
     */
    public $depth;

    /**
     * @var array
     */
    public $selectors;

    /**
     * @var array
     */
    public $lines;

    /**
     * @var array
     */
    public $children;

    /**
     * @var \Leafo\ScssPhp\Formatter\OutputBlock
     */
    public $parent;

    /**
     * @var string
     */
    public $sourceName;

    /**
     * @var integer
     */
    public $sourceLine;

    /**
     * @var integer
     */
    public $sourceColumn;
}
vendor/leafo/scssphp/src/Formatter.php000064400000013152151166614550014071
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\SourceMap\SourceMapGenerator;

/**
 * Base formatter
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
abstract class Formatter
{
    /**
     * @var integer
     */
    public $indentLevel;

    /**
     * @var string
     */
    public $indentChar;

    /**
     * @var string
     */
    public $break;

    /**
     * @var string
     */
    public $open;

    /**
     * @var string
     */
    public $close;

    /**
     * @var string
     */
    public $tagSeparator;

    /**
     * @var string
     */
    public $assignSeparator;

    /**
     * @var boolean
     */
    public $keepSemicolons;

    /**
     * @var \Leafo\ScssPhp\Formatter\OutputBlock
     */
    protected $currentBlock;

    /**
     * @var integer
     */
    protected $currentLine;

    /**
     * @var integer
     */
    protected $currentColumn;

    /**
     * @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator
     */
    protected $sourceMapGenerator;

    /**
     * Initialize formatter
     *
     * @api
     */
    abstract public function __construct();

    /**
     * Return indentation (whitespace)
     *
     * @return string
     */
    protected function indentStr()
    {
        return '';
    }

    /**
     * Return property assignment
     *
     * @api
     *
     * @param string $name
     * @param mixed  $value
     *
     * @return string
     */
    public function property($name, $value)
    {
        return rtrim($name) . $this->assignSeparator . $value .
';';
    }

    /**
     * Strip semi-colon appended by property(); it's a separator, not
a terminator
     *
     * @api
     *
     * @param array $lines
     */
    public function stripSemicolon(&$lines)
    {
        if ($this->keepSemicolons) {
            return;
        }

        if (($count = count($lines))
            && substr($lines[$count - 1], -1) === ';'
        ) {
            $lines[$count - 1] = substr($lines[$count - 1], 0, -1);
        }
    }

    /**
     * Output lines inside a block
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
     */
    protected function blockLines(OutputBlock $block)
    {
        $inner = $this->indentStr();

        $glue = $this->break . $inner;

        $this->write($inner . implode($glue, $block->lines));

        if (! empty($block->children)) {
            $this->write($this->break);
        }
    }

    /**
     * Output block selectors
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
     */
    protected function blockSelectors(OutputBlock $block)
    {
        $inner = $this->indentStr();

        $this->write($inner
            . implode($this->tagSeparator, $block->selectors)
            . $this->open . $this->break);
    }

    /**
     * Output block children
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
     */
    protected function blockChildren(OutputBlock $block)
    {
        foreach ($block->children as $child) {
            $this->block($child);
        }
    }

    /**
     * Output non-empty block
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
     */
    protected function block(OutputBlock $block)
    {
        if (empty($block->lines) && empty($block->children))
{
            return;
        }

        $this->currentBlock = $block;

        $pre = $this->indentStr();

        if (! empty($block->selectors)) {
            $this->blockSelectors($block);

            $this->indentLevel++;
        }

        if (! empty($block->lines)) {
            $this->blockLines($block);
        }

        if (! empty($block->children)) {
            $this->blockChildren($block);
        }

        if (! empty($block->selectors)) {
            $this->indentLevel--;

            if (empty($block->children)) {
                $this->write($this->break);
            }

            $this->write($pre . $this->close . $this->break);
        }
    }

    /**
     * Entry point to formatting a block
     *
     * @api
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock             $block      
       An abstract syntax tree
     * @param \Leafo\ScssPhp\SourceMap\SourceMapGenerator|null
$sourceMapGenerator Optional source map generator
     *
     * @return string
     */
    public function format(OutputBlock $block, SourceMapGenerator
$sourceMapGenerator = null)
    {
        $this->sourceMapGenerator = null;

        if ($sourceMapGenerator) {
            $this->currentLine = 1;
            $this->currentColumn = 0;
            $this->sourceMapGenerator = $sourceMapGenerator;
        }

        ob_start();

        $this->block($block);

        $out = ob_get_clean();

        return $out;
    }

    /**
     * @param string $str
     */
    protected function write($str)
    {
        if ($this->sourceMapGenerator) {
            $this->sourceMapGenerator->addMapping(
                $this->currentLine,
                $this->currentColumn,
                $this->currentBlock->sourceLine,
                //columns from parser are off by one
                $this->currentBlock->sourceColumn > 0 ?
$this->currentBlock->sourceColumn - 1 : 0,
                $this->currentBlock->sourceName
            );

            $lines = explode("\n", $str);
            $lineCount = count($lines);
            $this->currentLine += $lineCount-1;

            $lastLine = array_pop($lines);

            $this->currentColumn = ($lineCount === 1 ?
$this->currentColumn : 0) + strlen($lastLine);
        }

        echo $str;
    }
}
vendor/leafo/scssphp/src/Node/Number.php000064400000017144151166614550014250
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\Node;

use Leafo\ScssPhp\Compiler;
use Leafo\ScssPhp\Node;
use Leafo\ScssPhp\Type;

/**
 * Dimension + optional units
 *
 * {@internal
 *     This is a work-in-progress.
 *
 *     The \ArrayAccess interface is temporary until the migration is
complete.
 * }}
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class Number extends Node implements \ArrayAccess
{
    /**
     * @var integer
     */
    static public $precision = 10;

    /**
     * @see http://www.w3.org/TR/2012/WD-css3-values-20120308/
     *
     * @var array
     */
    static protected $unitTable = [
        'in' => [
            'in' => 1,
            'pc' => 6,
            'pt' => 72,
            'px' => 96,
            'cm' => 2.54,
            'mm' => 25.4,
            'q'  => 101.6,
        ],
        'turn' => [
            'deg'  => 360,
            'grad' => 400,
            'rad'  => 6.28318530717958647692528676, // 2 *
M_PI
            'turn' => 1,
        ],
        's' => [
            's'  => 1,
            'ms' => 1000,
        ],
        'Hz' => [
            'Hz'  => 1,
            'kHz' => 0.001,
        ],
        'dpi' => [
            'dpi'  => 1,
            'dpcm' => 2.54,
            'dppx' => 96,
        ],
    ];

    /**
     * @var integer|float
     */
    public $dimension;

    /**
     * @var array
     */
    public $units;

    /**
     * Initialize number
     *
     * @param mixed $dimension
     * @param mixed $initialUnit
     */
    public function __construct($dimension, $initialUnit)
    {
        $this->type      = Type::T_NUMBER;
        $this->dimension = $dimension;
        $this->units     = is_array($initialUnit)
            ? $initialUnit
            : ($initialUnit ? [$initialUnit => 1]
                            : []);
    }

    /**
     * Coerce number to target units
     *
     * @param array $units
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    public function coerce($units)
    {
        if ($this->unitless()) {
            return new Number($this->dimension, $units);
        }

        $dimension = $this->dimension;

        foreach (static::$unitTable['in'] as $unit => $conv) {
            $from       = isset($this->units[$unit]) ?
$this->units[$unit] : 0;
            $to         = isset($units[$unit]) ? $units[$unit] : 0;
            $factor     = pow($conv, $from - $to);
            $dimension /= $factor;
        }

        return new Number($dimension, $units);
    }

    /**
     * Normalize number
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    public function normalize()
    {
        $dimension = $this->dimension;
        $units     = [];

        $this->normalizeUnits($dimension, $units, 'in');

        return new Number($dimension, $units);
    }

    /**
     * {@inheritdoc}
     */
    public function offsetExists($offset)
    {
        if ($offset === -3) {
            return $this->sourceColumn !== null;
        }

        if ($offset === -2) {
            return $this->sourceLine !== null;
        }

        if ($offset === -1
            || $offset === 0
            || $offset === 1
            || $offset === 2
        ) {
            return true;
        }

        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function offsetGet($offset)
    {
        switch ($offset) {
            case -3:
                return $this->sourceColumn;

            case -2:
                return $this->sourceLine;

            case -1:
                return $this->sourceIndex;

            case 0:
                return $this->type;

            case 1:
                return $this->dimension;

            case 2:
                return $this->units;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function offsetSet($offset, $value)
    {
        if ($offset === 1) {
            $this->dimension = $value;
        } elseif ($offset === 2) {
            $this->units = $value;
        } elseif ($offset == -1) {
            $this->sourceIndex = $value;
        } elseif ($offset == -2) {
            $this->sourceLine = $value;
        } elseif ($offset == -3) {
            $this->sourceColumn = $value;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function offsetUnset($offset)
    {
        if ($offset === 1) {
            $this->dimension = null;
        } elseif ($offset === 2) {
            $this->units = null;
        } elseif ($offset === -1) {
            $this->sourceIndex = null;
        } elseif ($offset === -2) {
            $this->sourceLine = null;
        } elseif ($offset === -3) {
            $this->sourceColumn = null;
        }
    }

    /**
     * Returns true if the number is unitless
     *
     * @return boolean
     */
    public function unitless()
    {
        return ! array_sum($this->units);
    }

    /**
     * Returns unit(s) as the product of numerator units divided by the
product of denominator units
     *
     * @return string
     */
    public function unitStr()
    {
        $numerators   = [];
        $denominators = [];

        foreach ($this->units as $unit => $unitSize) {
            if ($unitSize > 0) {
                $numerators = array_pad($numerators, count($numerators) +
$unitSize, $unit);
                continue;
            }

            if ($unitSize < 0) {
                $denominators = array_pad($denominators,
count($denominators) + $unitSize, $unit);
                continue;
            }
        }

        return implode('*', $numerators) . (count($denominators)
? '/' . implode('*', $denominators) : '');
    }

    /**
     * Output number
     *
     * @param \Leafo\ScssPhp\Compiler $compiler
     *
     * @return string
     */
    public function output(Compiler $compiler = null)
    {
        $dimension = round($this->dimension, static::$precision);

        $units = array_filter($this->units, function ($unitSize) {
            return $unitSize;
        });

        if (count($units) > 1 && array_sum($units) === 0) {
            $dimension = $this->dimension;
            $units     = [];

            $this->normalizeUnits($dimension, $units, 'in');

            $dimension = round($dimension, static::$precision);
            $units     = array_filter($units, function ($unitSize) {
                return $unitSize;
            });
        }

        $unitSize = array_sum($units);

        if ($compiler && ($unitSize > 1 || $unitSize < 0 ||
count($units) > 1)) {
            $compiler->throwError((string) $dimension .
$this->unitStr() . " isn't a valid CSS value.");
        }

        reset($units);
        $unit = key($units);
        $dimension = number_format($dimension, static::$precision,
'.', '');

        return (static::$precision ? rtrim(rtrim($dimension,
'0'), '.') : $dimension) . $unit;
    }

    /**
     * {@inheritdoc}
     */
    public function __toString()
    {
        return $this->output();
    }

    /**
     * Normalize units
     *
     * @param integer|float $dimension
     * @param array         $units
     * @param string        $baseUnit
     */
    private function normalizeUnits(&$dimension, &$units, $baseUnit
= 'in')
    {
        $dimension = $this->dimension;
        $units     = [];

        foreach ($this->units as $unit => $exp) {
            if (isset(static::$unitTable[$baseUnit][$unit])) {
                $factor = pow(static::$unitTable[$baseUnit][$unit], $exp);

                $unit = $baseUnit;
                $dimension /= $factor;
            }

            $units[$unit] = $exp + (isset($units[$unit]) ? $units[$unit] :
0);
        }
    }
}
vendor/leafo/scssphp/src/Node.php000064400000001014151166614550013005
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

/**
 * Base node
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
abstract class Node
{
    /**
     * @var string
     */
    public $type;

    /**
     * @var integer
     */
    public $sourceIndex;

    /**
     * @var integer
     */
    public $sourceLine;

    /**
     * @var integer
     */
    public $sourceColumn;
}
vendor/leafo/scssphp/src/Parser.php000064400000212603151166614550013364
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

use Leafo\ScssPhp\Block;
use Leafo\ScssPhp\Cache;
use Leafo\ScssPhp\Compiler;
use Leafo\ScssPhp\Exception\ParserException;
use Leafo\ScssPhp\Node;
use Leafo\ScssPhp\Type;

/**
 * Parser
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Parser
{
    const SOURCE_INDEX  = -1;
    const SOURCE_LINE   = -2;
    const SOURCE_COLUMN = -3;

    /**
     * @var array
     */
    protected static $precedence = [
        '='   => 0,
        'or'  => 1,
        'and' => 2,
        '=='  => 3,
        '!='  => 3,
        '<=>' => 3,
        '<='  => 4,
        '>='  => 4,
        '<'   => 4,
        '>'   => 4,
        '+'   => 5,
        '-'   => 5,
        '*'   => 6,
        '/'   => 6,
        '%'   => 6,
    ];

    protected static $commentPattern;
    protected static $operatorPattern;
    protected static $whitePattern;

    protected $cache;

    private $sourceName;
    private $sourceIndex;
    private $sourcePositions;
    private $charset;
    private $count;
    private $env;
    private $inParens;
    private $eatWhiteDefault;
    private $buffer;
    private $utf8;
    private $encoding;
    private $patternModifiers;
    private $commentsSeen;

    /**
     * Constructor
     *
     * @api
     *
     * @param string               $sourceName
     * @param integer              $sourceIndex
     * @param string               $encoding
     * @param \Leafo\ScssPhp\Cache $cache
     */
    public function __construct($sourceName, $sourceIndex = 0, $encoding =
'utf-8', $cache = null)
    {
        $this->sourceName       = $sourceName ?: '(stdin)';
        $this->sourceIndex      = $sourceIndex;
        $this->charset          = null;
        $this->utf8             = ! $encoding || strtolower($encoding)
=== 'utf-8';
        $this->patternModifiers = $this->utf8 ? 'Aisu' :
'Ais';
        $this->commentsSeen     = [];

        if (empty(static::$operatorPattern)) {
            static::$operatorPattern =
'([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)';

            $commentSingle      = '\/\/';
            $commentMultiLeft   = '\/\*';
            $commentMultiRight  = '\*\/';

            static::$commentPattern = $commentMultiLeft . '.*?' .
$commentMultiRight;
            static::$whitePattern = $this->utf8
                ? '/' . $commentSingle . '[^\n]*\s*|('
. static::$commentPattern . ')\s*|\s+/AisuS'
                : '/' . $commentSingle . '[^\n]*\s*|('
. static::$commentPattern . ')\s*|\s+/AisS';
        }

        if ($cache) {
            $this->cache = $cache;
        }
    }

    /**
     * Get source file name
     *
     * @api
     *
     * @return string
     */
    public function getSourceName()
    {
        return $this->sourceName;
    }

    /**
     * Throw parser error
     *
     * @api
     *
     * @param string $msg
     *
     * @throws \Leafo\ScssPhp\Exception\ParserException
     */
    public function throwParseError($msg = 'parse error')
    {
        list($line, $column) =
$this->getSourcePosition($this->count);

        $loc = empty($this->sourceName)
             ? "line: $line, column: $column"
             : "$this->sourceName on line $line, at column
$column";

        if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
            throw new ParserException("$msg: failed at `$m[1]`
$loc");
        }

        throw new ParserException("$msg: $loc");
    }

    /**
     * Parser buffer
     *
     * @api
     *
     * @param string $buffer
     *
     * @return \Leafo\ScssPhp\Block
     */
    public function parse($buffer)
    {
        if ($this->cache) {
            $cacheKey = $this->sourceName . ":" .
md5($buffer);
            $parseOptions = [
                'charset' => $this->charset,
                'utf8' => $this->utf8,
            ];
            $v = $this->cache->getCache("parse", $cacheKey,
$parseOptions);

            if (! is_null($v)) {
                return $v;
            }
        }

        // strip BOM (byte order marker)
        if (substr($buffer, 0, 3) === "\xef\xbb\xbf") {
            $buffer = substr($buffer, 3);
        }

        $this->buffer          = rtrim($buffer, "\x00..\x1f");
        $this->count           = 0;
        $this->env             = null;
        $this->inParens        = false;
        $this->eatWhiteDefault = true;

        $this->saveEncoding();
        $this->extractLineNumbers($buffer);

        $this->pushBlock(null); // root block
        $this->whitespace();
        $this->pushBlock(null);
        $this->popBlock();

        while ($this->parseChunk()) {
            ;
        }

        if ($this->count !== strlen($this->buffer)) {
            $this->throwParseError();
        }

        if (! empty($this->env->parent)) {
            $this->throwParseError('unclosed block');
        }

        if ($this->charset) {
            array_unshift($this->env->children, $this->charset);
        }

        $this->restoreEncoding();

        if ($this->cache) {
            $this->cache->setCache("parse", $cacheKey,
$this->env, $parseOptions);
        }

        return $this->env;
    }

    /**
     * Parse a value or value list
     *
     * @api
     *
     * @param string $buffer
     * @param string $out
     *
     * @return boolean
     */
    public function parseValue($buffer, &$out)
    {
        $this->count           = 0;
        $this->env             = null;
        $this->inParens        = false;
        $this->eatWhiteDefault = true;
        $this->buffer          = (string) $buffer;

        $this->saveEncoding();

        $list = $this->valueList($out);

        $this->restoreEncoding();

        return $list;
    }

    /**
     * Parse a selector or selector list
     *
     * @api
     *
     * @param string $buffer
     * @param string $out
     *
     * @return boolean
     */
    public function parseSelector($buffer, &$out)
    {
        $this->count           = 0;
        $this->env             = null;
        $this->inParens        = false;
        $this->eatWhiteDefault = true;
        $this->buffer          = (string) $buffer;

        $this->saveEncoding();

        $selector = $this->selectors($out);

        $this->restoreEncoding();

        return $selector;
    }

    /**
     * Parse a single chunk off the head of the buffer and append it to the
     * current parse environment.
     *
     * Returns false when the buffer is empty, or when there is an error.
     *
     * This function is called repeatedly until the entire document is
     * parsed.
     *
     * This parser is most similar to a recursive descent parser. Single
     * functions represent discrete grammatical rules for the language, and
     * they are able to capture the text that represents those rules.
     *
     * Consider the function Compiler::keyword(). (All parse functions are
     * structured the same.)
     *
     * The function takes a single reference argument. When calling the
     * function it will attempt to match a keyword on the head of the
buffer.
     * If it is successful, it will place the keyword in the referenced
     * argument, advance the position in the buffer, and return true. If it
     * fails then it won't advance the buffer and it will return
false.
     *
     * All of these parse functions are powered by Compiler::match(), which
behaves
     * the same way, but takes a literal regular expression. Sometimes it
is
     * more convenient to use match instead of creating a new function.
     *
     * Because of the format of the functions, to parse an entire string of
     * grammatical rules, you can chain them together using &&.
     *
     * But, if some of the rules in the chain succeed before one fails,
then
     * the buffer position will be left at an invalid state. In order to
     * avoid this, Compiler::seek() is used to remember and set buffer
positions.
     *
     * Before parsing a chain, use $s = $this->count to remember the
current
     * position into $s. Then if a chain fails, use $this->seek($s) to
     * go back where we started.
     *
     * @return boolean
     */
    protected function parseChunk()
    {
        $s = $this->count;

        // the directives
        if (isset($this->buffer[$this->count]) &&
$this->buffer[$this->count] === '@') {
            if ($this->literal('@at-root', 8) &&
                ($this->selectors($selector) || true) &&
                ($this->map($with) || true) &&
                $this->matchChar('{')
            ) {
                $atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s);
                $atRoot->selector = $selector;
                $atRoot->with = $with;

                return true;
            }

            $this->seek($s);

            if ($this->literal('@media', 6) &&
$this->mediaQueryList($mediaQueryList) &&
$this->matchChar('{')) {
                $media = $this->pushSpecialBlock(Type::T_MEDIA, $s);
                $media->queryList = $mediaQueryList[2];

                return true;
            }

            $this->seek($s);

            if ($this->literal('@mixin', 6) &&
                $this->keyword($mixinName) &&
                ($this->argumentDef($args) || true) &&
                $this->matchChar('{')
            ) {
                $mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s);
                $mixin->name = $mixinName;
                $mixin->args = $args;

                return true;
            }

            $this->seek($s);

            if ($this->literal('@include', 8) &&
                $this->keyword($mixinName) &&
                ($this->matchChar('(') &&
                    ($this->argValues($argValues) || true) &&
                    $this->matchChar(')') || true) &&
                ($this->end() ||
                    $this->matchChar('{') && $hasBlock
= true)
            ) {
                $child = [Type::T_INCLUDE, $mixinName, isset($argValues) ?
$argValues : null, null];

                if (! empty($hasBlock)) {
                    $include = $this->pushSpecialBlock(Type::T_INCLUDE,
$s);
                    $include->child = $child;
                } else {
                    $this->append($child, $s);
                }

                return true;
            }

            $this->seek($s);

            if ($this->literal('@scssphp-import-once', 20)
&&
                $this->valueList($importPath) &&
                $this->end()
            ) {
                $this->append([Type::T_SCSSPHP_IMPORT_ONCE,
$importPath], $s);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@import', 7) &&
                $this->valueList($importPath) &&
                $this->end()
            ) {
                $this->append([Type::T_IMPORT, $importPath], $s);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@import', 7) &&
                $this->url($importPath) &&
                $this->end()
            ) {
                $this->append([Type::T_IMPORT, $importPath], $s);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@extend', 7) &&
                $this->selectors($selectors) &&
                $this->end()
            ) {
                // check for '!flag'
                $optional = $this->stripOptionalFlag($selectors);
                $this->append([Type::T_EXTEND, $selectors, $optional],
$s);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@function', 9) &&
                $this->keyword($fnName) &&
                $this->argumentDef($args) &&
                $this->matchChar('{')
            ) {
                $func = $this->pushSpecialBlock(Type::T_FUNCTION, $s);
                $func->name = $fnName;
                $func->args = $args;

                return true;
            }

            $this->seek($s);

            if ($this->literal('@break', 6) &&
$this->end()) {
                $this->append([Type::T_BREAK], $s);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@continue', 9) &&
$this->end()) {
                $this->append([Type::T_CONTINUE], $s);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@return', 7) &&
($this->valueList($retVal) || true) && $this->end()) {
                $this->append([Type::T_RETURN, isset($retVal) ? $retVal
: [Type::T_NULL]], $s);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@each', 5) &&
                $this->genericList($varNames, 'variable',
',', false) &&
                $this->literal('in', 2) &&
                $this->valueList($list) &&
                $this->matchChar('{')
            ) {
                $each = $this->pushSpecialBlock(Type::T_EACH, $s);

                foreach ($varNames[2] as $varName) {
                    $each->vars[] = $varName[1];
                }

                $each->list = $list;

                return true;
            }

            $this->seek($s);

            if ($this->literal('@while', 6) &&
                $this->expression($cond) &&
                $this->matchChar('{')
            ) {
                $while = $this->pushSpecialBlock(Type::T_WHILE, $s);
                $while->cond = $cond;

                return true;
            }

            $this->seek($s);

            if ($this->literal('@for', 4) &&
                $this->variable($varName) &&
                $this->literal('from', 4) &&
                $this->expression($start) &&
                ($this->literal('through', 7) ||
                    ($forUntil = true &&
$this->literal('to', 2))) &&
                $this->expression($end) &&
                $this->matchChar('{')
            ) {
                $for = $this->pushSpecialBlock(Type::T_FOR, $s);
                $for->var = $varName[1];
                $for->start = $start;
                $for->end = $end;
                $for->until = isset($forUntil);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@if', 3) &&
$this->valueList($cond) && $this->matchChar('{')) {
                $if = $this->pushSpecialBlock(Type::T_IF, $s);
                $if->cond = $cond;
                $if->cases = [];

                return true;
            }

            $this->seek($s);

            if ($this->literal('@debug', 6) &&
                $this->valueList($value) &&
                $this->end()
            ) {
                $this->append([Type::T_DEBUG, $value], $s);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@warn', 5) &&
                $this->valueList($value) &&
                $this->end()
            ) {
                $this->append([Type::T_WARN, $value], $s);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@error', 6) &&
                $this->valueList($value) &&
                $this->end()
            ) {
                $this->append([Type::T_ERROR, $value], $s);

                return true;
            }

            $this->seek($s);

            if ($this->literal('@content', 8) &&
$this->end()) {
                $this->append([Type::T_MIXIN_CONTENT], $s);

                return true;
            }

            $this->seek($s);

            $last = $this->last();

            if (isset($last) && $last[0] === Type::T_IF) {
                list(, $if) = $last;

                if ($this->literal('@else', 5)) {
                    if ($this->matchChar('{')) {
                        $else = $this->pushSpecialBlock(Type::T_ELSE,
$s);
                    } elseif ($this->literal('if', 2)
&& $this->valueList($cond) &&
$this->matchChar('{')) {
                        $else = $this->pushSpecialBlock(Type::T_ELSEIF,
$s);
                        $else->cond = $cond;
                    }

                    if (isset($else)) {
                        $else->dontAppend = true;
                        $if->cases[] = $else;

                        return true;
                    }
                }

                $this->seek($s);
            }

            // only retain the first @charset directive encountered
            if ($this->literal('@charset', 8) &&
                $this->valueList($charset) &&
                $this->end()
            ) {
                if (! isset($this->charset)) {
                    $statement = [Type::T_CHARSET, $charset];

                    list($line, $column) = $this->getSourcePosition($s);

                    $statement[static::SOURCE_LINE]   = $line;
                    $statement[static::SOURCE_COLUMN] = $column;
                    $statement[static::SOURCE_INDEX]  =
$this->sourceIndex;

                    $this->charset = $statement;
                }

                return true;
            }

            $this->seek($s);

            // doesn't match built in directive, do generic one
            if ($this->matchChar('@', false) &&
                $this->keyword($dirName) &&
                ($this->variable($dirValue) ||
$this->openString('{', $dirValue) || true) &&
                $this->matchChar('{')
            ) {
                if ($dirName === 'media') {
                    $directive = $this->pushSpecialBlock(Type::T_MEDIA,
$s);
                } else {
                    $directive =
$this->pushSpecialBlock(Type::T_DIRECTIVE, $s);
                    $directive->name = $dirName;
                }

                if (isset($dirValue)) {
                    $directive->value = $dirValue;
                }

                return true;
            }

            $this->seek($s);

            return false;
        }

        // property shortcut
        // captures most properties before having to parse a selector
        if ($this->keyword($name, false) &&
            $this->literal(': ', 2) &&
            $this->valueList($value) &&
            $this->end()
        ) {
            $name = [Type::T_STRING, '', [$name]];
            $this->append([Type::T_ASSIGN, $name, $value], $s);

            return true;
        }

        $this->seek($s);

        // variable assigns
        if ($this->variable($name) &&
            $this->matchChar(':') &&
            $this->valueList($value) &&
            $this->end()
        ) {
            // check for '!flag'
            $assignmentFlags = $this->stripAssignmentFlags($value);
            $this->append([Type::T_ASSIGN, $name, $value,
$assignmentFlags], $s);

            return true;
        }

        $this->seek($s);

        // misc
        if ($this->literal('-->', 3)) {
            return true;
        }

        // opening css block
        if ($this->selectors($selectors) &&
$this->matchChar('{', false)) {
            $this->pushBlock($selectors, $s);

            if ($this->eatWhiteDefault) {
                $this->whitespace();
                $this->append(null); // collect comments at the begining
if needed
            }

            return true;
        }

        $this->seek($s);

        // property assign, or nested assign
        if ($this->propertyName($name) &&
$this->matchChar(':')) {
            $foundSomething = false;

            if ($this->valueList($value)) {
                if (empty($this->env->parent)) {
                    $this->throwParseError('expected
"{"');
                }

                $this->append([Type::T_ASSIGN, $name, $value], $s);
                $foundSomething = true;
            }

            if ($this->matchChar('{')) {
                $propBlock =
$this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s);
                $propBlock->prefix = $name;
                $foundSomething = true;
            } elseif ($foundSomething) {
                $foundSomething = $this->end();
            }

            if ($foundSomething) {
                return true;
            }
        }

        $this->seek($s);

        // closing a block
        if ($this->matchChar('}')) {
            $block = $this->popBlock();

            if (isset($block->type) && $block->type ===
Type::T_INCLUDE) {
                $include = $block->child;
                unset($block->child);
                $include[3] = $block;
                $this->append($include, $s);
            } elseif (empty($block->dontAppend)) {
                $type = isset($block->type) ? $block->type :
Type::T_BLOCK;
                $this->append([$type, $block], $s);
            }

            return true;
        }

        // extra stuff
        if ($this->matchChar(';') ||
            $this->literal('<!--', 4)
        ) {
            return true;
        }

        return false;
    }

    /**
     * Push block onto parse tree
     *
     * @param array   $selectors
     * @param integer $pos
     *
     * @return \Leafo\ScssPhp\Block
     */
    protected function pushBlock($selectors, $pos = 0)
    {
        list($line, $column) = $this->getSourcePosition($pos);

        $b = new Block;
        $b->sourceName   = $this->sourceName;
        $b->sourceLine   = $line;
        $b->sourceColumn = $column;
        $b->sourceIndex  = $this->sourceIndex;
        $b->selectors    = $selectors;
        $b->comments     = [];
        $b->parent       = $this->env;

        if (! $this->env) {
            $b->children = [];
        } elseif (empty($this->env->children)) {
            $this->env->children = $this->env->comments;
            $b->children = [];
            $this->env->comments = [];
        } else {
            $b->children = $this->env->comments;
            $this->env->comments = [];
        }

        $this->env = $b;

        return $b;
    }

    /**
     * Push special (named) block onto parse tree
     *
     * @param string  $type
     * @param integer $pos
     *
     * @return \Leafo\ScssPhp\Block
     */
    protected function pushSpecialBlock($type, $pos)
    {
        $block = $this->pushBlock(null, $pos);
        $block->type = $type;

        return $block;
    }

    /**
     * Pop scope and return last block
     *
     * @return \Leafo\ScssPhp\Block
     *
     * @throws \Exception
     */
    protected function popBlock()
    {
        $block = $this->env;

        if (empty($block->parent)) {
            $this->throwParseError('unexpected }');
        }

        if ($block->type == Type::T_AT_ROOT) {
            // keeps the parent in case of self selector &
            $block->selfParent = $block->parent;
        }

        $this->env = $block->parent;

        unset($block->parent);

        $comments = $block->comments;

        if ($comments) {
            $this->env->comments = $comments;
            unset($block->comments);
        }

        return $block;
    }

    /**
     * Peek input stream
     *
     * @param string  $regex
     * @param array   $out
     * @param integer $from
     *
     * @return integer
     */
    protected function peek($regex, &$out, $from = null)
    {
        if (! isset($from)) {
            $from = $this->count;
        }

        $r = '/' . $regex . '/' .
$this->patternModifiers;
        $result = preg_match($r, $this->buffer, $out, null, $from);

        return $result;
    }

    /**
     * Seek to position in input stream (or return current position in
input stream)
     *
     * @param integer $where
     */
    protected function seek($where)
    {
        $this->count = $where;
    }

    /**
     * Match string looking for either ending delim, escape, or string
interpolation
     *
     * {@internal This is a workaround for preg_match's 250K string
match limit. }}
     *
     * @param array  $m     Matches (passed by reference)
     * @param string $delim Delimeter
     *
     * @return boolean True if match; false otherwise
     */
    protected function matchString(&$m, $delim)
    {
        $token = null;

        $end = strlen($this->buffer);

        // look for either ending delim, escape, or string interpolation
        foreach (['#{', '\\', $delim] as $lookahead) {
            $pos = strpos($this->buffer, $lookahead, $this->count);

            if ($pos !== false && $pos < $end) {
                $end = $pos;
                $token = $lookahead;
            }
        }

        if (! isset($token)) {
            return false;
        }

        $match = substr($this->buffer, $this->count, $end -
$this->count);
        $m = [
            $match . $token,
            $match,
            $token
        ];
        $this->count = $end + strlen($token);

        return true;
    }

    /**
     * Try to match something on head of buffer
     *
     * @param string  $regex
     * @param array   $out
     * @param boolean $eatWhitespace
     *
     * @return boolean
     */
    protected function match($regex, &$out, $eatWhitespace = null)
    {
        $r = '/' . $regex . '/' .
$this->patternModifiers;

        if (! preg_match($r, $this->buffer, $out, null,
$this->count)) {
            return false;
        }

        $this->count += strlen($out[0]);

        if (! isset($eatWhitespace)) {
            $eatWhitespace = $this->eatWhiteDefault;
        }

        if ($eatWhitespace) {
            $this->whitespace();
        }

        return true;
    }

    /**
     * Match a single string
     *
     * @param string  $char
     * @param boolean $eatWhitespace
     *
     * @return boolean
     */
    protected function matchChar($char, $eatWhitespace = null)
    {
        if (! isset($this->buffer[$this->count]) ||
$this->buffer[$this->count] !== $char) {
            return false;
        }

        $this->count++;

        if (! isset($eatWhitespace)) {
            $eatWhitespace = $this->eatWhiteDefault;
        }

        if ($eatWhitespace) {
            $this->whitespace();
        }

        return true;
    }

    /**
     * Match literal string
     *
     * @param string  $what
     * @param integer $len
     * @param boolean $eatWhitespace
     *
     * @return boolean
     */
    protected function literal($what, $len, $eatWhitespace = null)
    {
        if (strcasecmp(substr($this->buffer, $this->count, $len),
$what) !== 0) {
            return false;
        }

        $this->count += $len;

        if (! isset($eatWhitespace)) {
            $eatWhitespace = $this->eatWhiteDefault;
        }

        if ($eatWhitespace) {
            $this->whitespace();
        }

        return true;
    }

    /**
     * Match some whitespace
     *
     * @return boolean
     */
    protected function whitespace()
    {
        $gotWhite = false;

        while (preg_match(static::$whitePattern, $this->buffer, $m,
null, $this->count)) {
            if (isset($m[1]) &&
empty($this->commentsSeen[$this->count])) {
                // comment that are kept in the output CSS
                $comment = [];
                $endCommentCount = $this->count + strlen($m[1]);

                // find interpolations in comment
                $p = strpos($this->buffer, '#{',
$this->count);

                while ($p !== false && $p < $endCommentCount) {
                    $c = substr($this->buffer, $this->count, $p -
$this->count);
                    $comment[] = $c;
                    $this->count = $p;
                    $out = null;

                    if ($this->interpolation($out)) {
                        // keep right spaces in the following string part
                        if ($out[3]) {
                            while ($this->buffer[$this->count-1] !==
'}') {
                                $this->count--;
                            }

                            $out[3] = '';
                        }

                        $comment[] = $out;
                    } else {
                        $comment[] = substr($this->buffer,
$this->count, 2);

                        $this->count += 2;
                    }

                    $p = strpos($this->buffer, '#{',
$this->count);
                }

                // remaining part
                $c = substr($this->buffer, $this->count,
$endCommentCount - $this->count);

                if (! $comment) {
                    // single part static comment
                    $this->appendComment([Type::T_COMMENT, $c]);
                } else {
                    $comment[] = $c;
                    $this->appendComment([Type::T_COMMENT,
[Type::T_STRING, '', $comment]]);
                }

                $this->commentsSeen[$this->count] = true;
                $this->count = $endCommentCount;
            } else {
                // comment that are ignored and not kept in the output css
                $this->count += strlen($m[0]);
            }

            $gotWhite = true;
        }

        return $gotWhite;
    }

    /**
     * Append comment to current block
     *
     * @param array $comment
     */
    protected function appendComment($comment)
    {
        if ($comment[0] === Type::T_COMMENT &&
is_string($comment[1])) {
            $comment[1] = substr(preg_replace(['/^\s+/m',
'/^(.)/m'], ['', ' \1'], $comment[1]), 1);
        }

        $this->env->comments[] = $comment;
    }

    /**
     * Append statement to current block
     *
     * @param array   $statement
     * @param integer $pos
     */
    protected function append($statement, $pos = null)
    {
        if (! is_null($statement)) {
            if ($pos !== null) {
                list($line, $column) = $this->getSourcePosition($pos);

                $statement[static::SOURCE_LINE]   = $line;
                $statement[static::SOURCE_COLUMN] = $column;
                $statement[static::SOURCE_INDEX]  = $this->sourceIndex;
            }

            $this->env->children[] = $statement;
        }

        $comments = $this->env->comments;

        if ($comments) {
            $this->env->children =
array_merge($this->env->children, $comments);
            $this->env->comments = [];
        }
    }

    /**
     * Returns last child was appended
     *
     * @return array|null
     */
    protected function last()
    {
        $i = count($this->env->children) - 1;

        if (isset($this->env->children[$i])) {
            return $this->env->children[$i];
        }
    }

    /**
     * Parse media query list
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function mediaQueryList(&$out)
    {
        return $this->genericList($out, 'mediaQuery',
',', false);
    }

    /**
     * Parse media query
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function mediaQuery(&$out)
    {
        $expressions = null;
        $parts = [];

        if (($this->literal('only', 4) && ($only =
true) || $this->literal('not', 3) && ($not = true) ||
true) &&
            $this->mixedKeyword($mediaType)
        ) {
            $prop = [Type::T_MEDIA_TYPE];

            if (isset($only)) {
                $prop[] = [Type::T_KEYWORD, 'only'];
            }

            if (isset($not)) {
                $prop[] = [Type::T_KEYWORD, 'not'];
            }

            $media = [Type::T_LIST, '', []];

            foreach ((array) $mediaType as $type) {
                if (is_array($type)) {
                    $media[2][] = $type;
                } else {
                    $media[2][] = [Type::T_KEYWORD, $type];
                }
            }

            $prop[]  = $media;
            $parts[] = $prop;
        }

        if (empty($parts) || $this->literal('and', 3)) {
            $this->genericList($expressions,
'mediaExpression', 'and', false);

            if (is_array($expressions)) {
                $parts = array_merge($parts, $expressions[2]);
            }
        }

        $out = $parts;

        return true;
    }

    /**
     * Parse media expression
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function mediaExpression(&$out)
    {
        $s = $this->count;
        $value = null;

        if ($this->matchChar('(') &&
            $this->expression($feature) &&
            ($this->matchChar(':') &&
$this->expression($value) || true) &&
            $this->matchChar(')')
        ) {
            $out = [Type::T_MEDIA_EXPRESSION, $feature];

            if ($value) {
                $out[] = $value;
            }

            return true;
        }

        $this->seek($s);

        return false;
    }

    /**
     * Parse argument values
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function argValues(&$out)
    {
        if ($this->genericList($list, 'argValue',
',', false)) {
            $out = $list[2];

            return true;
        }

        return false;
    }

    /**
     * Parse argument value
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function argValue(&$out)
    {
        $s = $this->count;

        $keyword = null;

        if (! $this->variable($keyword) || !
$this->matchChar(':')) {
            $this->seek($s);
            $keyword = null;
        }

        if ($this->genericList($value, 'expression')) {
            $out = [$keyword, $value, false];
            $s = $this->count;

            if ($this->literal('...', 3)) {
                $out[2] = true;
            } else {
                $this->seek($s);
            }

            return true;
        }

        return false;
    }

    /**
     * Parse comma separated value list
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function valueList(&$out)
    {
        return $this->genericList($out, 'spaceList',
',');
    }

    /**
     * Parse space separated value list
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function spaceList(&$out)
    {
        return $this->genericList($out, 'expression');
    }

    /**
     * Parse generic list
     *
     * @param array    $out
     * @param callable $parseItem
     * @param string   $delim
     * @param boolean  $flatten
     *
     * @return boolean
     */
    protected function genericList(&$out, $parseItem, $delim =
'', $flatten = true)
    {
        $s = $this->count;
        $items = [];
        $value = null;

        while ($this->$parseItem($value)) {
            $items[] = $value;

            if ($delim) {
                if (! $this->literal($delim, strlen($delim))) {
                    break;
                }
            }
        }

        if (! $items) {
            $this->seek($s);

            return false;
        }

        if ($flatten && count($items) === 1) {
            $out = $items[0];
        } else {
            $out = [Type::T_LIST, $delim, $items];
        }

        return true;
    }

    /**
     * Parse expression
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function expression(&$out)
    {
        $s = $this->count;

        if ($this->matchChar('(')) {
            if ($this->parenExpression($out, $s, ")")) {
                return true;
            }

            $this->seek($s);
        }

        if ($this->matchChar('[')) {
            if ($this->parenExpression($out, $s, "]",
[Type::T_LIST, Type::T_KEYWORD])) {
                if ($out[0] !== Type::T_LIST && $out[0] !==
Type::T_MAP) {
                    $out = [Type::T_STRING, '', [ '[',
$out, ']' ]];
                }

                return true;
            }

            $this->seek($s);
        }

        if ($this->value($lhs)) {
            $out = $this->expHelper($lhs, 0);

            return true;
        }

        return false;
    }

    /**
     * Parse expression specifically checking for lists in parenthesis or
brackets
     *
     * @param array   $out
     * @param integer $s
     * @param string  $closingParen
     * @param array   $allowedTypes
     *
     * @return boolean
     */
    protected function parenExpression(&$out, $s, $closingParen =
")", $allowedTypes = [Type::T_LIST, Type::T_MAP])
    {
        if ($this->matchChar($closingParen)) {
            $out = [Type::T_LIST, '', []];

            return true;
        }

        if ($this->valueList($out) &&
$this->matchChar($closingParen) && in_array($out[0],
$allowedTypes)) {
            return true;
        }

        $this->seek($s);

        if (in_array(Type::T_MAP, $allowedTypes) &&
$this->map($out)) {
            return true;
        }

        return false;
    }

    /**
     * Parse left-hand side of subexpression
     *
     * @param array   $lhs
     * @param integer $minP
     *
     * @return array
     */
    protected function expHelper($lhs, $minP)
    {
        $operators = static::$operatorPattern;

        $ss = $this->count;
        $whiteBefore = isset($this->buffer[$this->count - 1])
&&
            ctype_space($this->buffer[$this->count - 1]);

        while ($this->match($operators, $m, false) &&
static::$precedence[$m[1]] >= $minP) {
            $whiteAfter = isset($this->buffer[$this->count])
&&
                ctype_space($this->buffer[$this->count]);
            $varAfter = isset($this->buffer[$this->count]) &&
                $this->buffer[$this->count] === '$';

            $this->whitespace();

            $op = $m[1];

            // don't turn negative numbers into expressions
            if ($op === '-' && $whiteBefore && !
$whiteAfter && ! $varAfter) {
                break;
            }

            if (! $this->value($rhs)) {
                break;
            }

            // peek and see if rhs belongs to next operator
            if ($this->peek($operators, $next) &&
static::$precedence[$next[1]] > static::$precedence[$op]) {
                $rhs = $this->expHelper($rhs,
static::$precedence[$next[1]]);
            }

            $lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs,
$this->inParens, $whiteBefore, $whiteAfter];
            $ss = $this->count;
            $whiteBefore = isset($this->buffer[$this->count - 1])
&&
                ctype_space($this->buffer[$this->count - 1]);
        }

        $this->seek($ss);

        return $lhs;
    }

    /**
     * Parse value
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function value(&$out)
    {
        if (! isset($this->buffer[$this->count])) {
            return false;
        }

        $s = $this->count;
        $char = $this->buffer[$this->count];

        if ($this->literal('url(', 4) &&
$this->match('data:([a-z]+)\/([a-z0-9.+-]+);base64,', $m,
false)) {
            $len = strspn(
                $this->buffer,
               
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=',
                $this->count
            );

            $this->count += $len;

            if ($this->matchChar(')')) {
                $content = substr($this->buffer, $s, $this->count -
$s);
                $out = [Type::T_KEYWORD, $content];

                return true;
            }
        }

        $this->seek($s);

        if ($this->literal('url(', 4, false) &&
$this->match('\s*(\/\/\S+)\s*', $m)) {
            $content = 'url(' . $m[1];

            if ($this->matchChar(')')) {
                $content .= ')';
                $out = [Type::T_KEYWORD, $content];

                return true;
            }
        }

        $this->seek($s);

        // not
        if ($char === 'n' &&
$this->literal('not', 3, false)) {
            if ($this->whitespace() && $this->value($inner))
{
                $out = [Type::T_UNARY, 'not', $inner,
$this->inParens];

                return true;
            }

            $this->seek($s);

            if ($this->parenValue($inner)) {
                $out = [Type::T_UNARY, 'not', $inner,
$this->inParens];

                return true;
            }

            $this->seek($s);
        }

        // addition
        if ($char === '+') {
            $this->count++;

            if ($this->value($inner)) {
                $out = [Type::T_UNARY, '+', $inner,
$this->inParens];

                return true;
            }

            $this->count--;

            return false;
        }

        // negation
        if ($char === '-') {
            $this->count++;

            if ($this->variable($inner) || $this->unit($inner) ||
$this->parenValue($inner)) {
                $out = [Type::T_UNARY, '-', $inner,
$this->inParens];

                return true;
            }

            $this->count--;
        }

        // paren
        if ($char === '(' && $this->parenValue($out))
{
            return true;
        }

        if ($char === '#') {
            if ($this->interpolation($out) || $this->color($out)) {
                return true;
            }
        }

        if ($this->matchChar('&', true)) {
            $out = [Type::T_SELF];

            return true;
        }

        if ($char === '$' && $this->variable($out)) {
            return true;
        }

        if ($char === 'p' && $this->progid($out)) {
            return true;
        }

        if (($char === '"' || $char === "'")
&& $this->string($out)) {
            return true;
        }

        if ($this->unit($out)) {
            return true;
        }

        // unicode range with wildcards
        if ($this->literal('U+', 2) &&
$this->match('([0-9A-F]+\?*)(-([0-9A-F]+))?', $m, false)) {
            $out = [Type::T_KEYWORD, 'U+' . $m[0]];
            return true;
        }

        if ($this->keyword($keyword, false)) {
            if ($this->func($keyword, $out)) {
                return true;
            }

            $this->whitespace();

            if ($keyword === 'null') {
                $out = [Type::T_NULL];
            } else {
                $out = [Type::T_KEYWORD, $keyword];
            }

            return true;
        }

        return false;
    }

    /**
     * Parse parenthesized value
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function parenValue(&$out)
    {
        $s = $this->count;

        $inParens = $this->inParens;

        if ($this->matchChar('(')) {
            if ($this->matchChar(')')) {
                $out = [Type::T_LIST, '', []];

                return true;
            }

            $this->inParens = true;

            if ($this->expression($exp) &&
$this->matchChar(')')) {
                $out = $exp;
                $this->inParens = $inParens;

                return true;
            }
        }

        $this->inParens = $inParens;
        $this->seek($s);

        return false;
    }

    /**
     * Parse "progid:"
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function progid(&$out)
    {
        $s = $this->count;

        if ($this->literal('progid:', 7, false) &&
            $this->openString('(', $fn) &&
            $this->matchChar('(')
        ) {
            $this->openString(')', $args, '(');

            if ($this->matchChar(')')) {
                $out = [Type::T_STRING, '', [
                    'progid:', $fn, '(', $args,
')'
                ]];

                return true;
            }
        }

        $this->seek($s);

        return false;
    }

    /**
     * Parse function call
     *
     * @param string $name
     * @param array  $func
     *
     * @return boolean
     */
    protected function func($name, &$func)
    {
        $s = $this->count;

        if ($this->matchChar('(')) {
            if ($name === 'alpha' &&
$this->argumentList($args)) {
                $func = [Type::T_FUNCTION, $name, [Type::T_STRING,
'', $args]];

                return true;
            }

            if ($name !== 'expression' && !
preg_match('/^(-[a-z]+-)?calc$/', $name)) {
                $ss = $this->count;

                if ($this->argValues($args) &&
$this->matchChar(')')) {
                    $func = [Type::T_FUNCTION_CALL, $name, $args];

                    return true;
                }

                $this->seek($ss);
            }

            if (($this->openString(')', $str, '(')
|| true) &&
                $this->matchChar(')')
            ) {
                $args = [];

                if (! empty($str)) {
                    $args[] = [null, [Type::T_STRING, '',
[$str]]];
                }

                $func = [Type::T_FUNCTION_CALL, $name, $args];

                return true;
            }
        }

        $this->seek($s);

        return false;
    }

    /**
     * Parse function call argument list
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function argumentList(&$out)
    {
        $s = $this->count;
        $this->matchChar('(');

        $args = [];

        while ($this->keyword($var)) {
            if ($this->matchChar('=') &&
$this->expression($exp)) {
                $args[] = [Type::T_STRING, '', [$var .
'=']];
                $arg = $exp;
            } else {
                break;
            }

            $args[] = $arg;

            if (! $this->matchChar(',')) {
                break;
            }

            $args[] = [Type::T_STRING, '', [', ']];
        }

        if (! $this->matchChar(')') || ! $args) {
            $this->seek($s);

            return false;
        }

        $out = $args;

        return true;
    }

    /**
     * Parse mixin/function definition  argument list
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function argumentDef(&$out)
    {
        $s = $this->count;
        $this->matchChar('(');

        $args = [];

        while ($this->variable($var)) {
            $arg = [$var[1], null, false];

            $ss = $this->count;

            if ($this->matchChar(':') &&
$this->genericList($defaultVal, 'expression')) {
                $arg[1] = $defaultVal;
            } else {
                $this->seek($ss);
            }

            $ss = $this->count;

            if ($this->literal('...', 3)) {
                $sss = $this->count;

                if (! $this->matchChar(')')) {
                    $this->throwParseError('... has to be after the
final argument');
                }

                $arg[2] = true;
                $this->seek($sss);
            } else {
                $this->seek($ss);
            }

            $args[] = $arg;

            if (! $this->matchChar(',')) {
                break;
            }
        }

        if (! $this->matchChar(')')) {
            $this->seek($s);

            return false;
        }

        $out = $args;

        return true;
    }

    /**
     * Parse map
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function map(&$out)
    {
        $s = $this->count;

        if (! $this->matchChar('(')) {
            return false;
        }

        $keys = [];
        $values = [];

        while ($this->genericList($key, 'expression')
&& $this->matchChar(':') &&
            $this->genericList($value, 'expression')
        ) {
            $keys[] = $key;
            $values[] = $value;

            if (! $this->matchChar(',')) {
                break;
            }
        }

        if (! $keys || ! $this->matchChar(')')) {
            $this->seek($s);

            return false;
        }

        $out = [Type::T_MAP, $keys, $values];

        return true;
    }

    /**
     * Parse color
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function color(&$out)
    {
        $color = [Type::T_COLOR];
        $s     = $this->count;

        if ($this->match('(#([0-9a-f]+))', $m)) {
            $nofValues = strlen($m[2]);
            $hasAlpha  = $nofValues === 4 || $nofValues === 8;
            $channels  = $hasAlpha ? [4, 3, 2, 1] : [3, 2, 1];

            switch ($nofValues) {
                case 3:
                case 4:
                    $num = hexdec($m[2]);

                    foreach ($channels as $i) {
                        $t = $num & 0xf;
                        $color[$i] = $t << 4 | $t;
                        $num >>= 4;
                    }

                    break;

                case 6:
                case 8:
                    $num = hexdec($m[2]);

                    foreach ($channels as $i) {
                        $color[$i] = $num & 0xff;
                        $num >>= 8;
                    }

                    break;

                default:
                    $this->seek($s);

                    return false;
            }

            if ($hasAlpha) {
                if ($color[4] === 255) {
                    $color[4] = 1; // fully opaque
                } else {
                    $color[4] = round($color[4] / 255, 3);
                }
            }

            $out = $color;

            return true;
        }

        return false;
    }

    /**
     * Parse number with unit
     *
     * @param array $unit
     *
     * @return boolean
     */
    protected function unit(&$unit)
    {
        $s = $this->count;

        if ($this->match('([0-9]*(\.)?[0-9]+)([%a-zA-Z]+)?',
$m, false)) {
            if (strlen($this->buffer) === $this->count || !
ctype_digit($this->buffer[$this->count])) {
                $this->whitespace();

                $unit = new Node\Number($m[1], empty($m[3]) ? ''
: $m[3]);

                return true;
            }

            $this->seek($s);
        }

        return false;
    }

    /**
     * Parse string
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function string(&$out)
    {
        $s = $this->count;

        if ($this->matchChar('"', false)) {
            $delim = '"';
        } elseif ($this->matchChar("'", false)) {
            $delim = "'";
        } else {
            return false;
        }

        $content = [];
        $oldWhite = $this->eatWhiteDefault;
        $this->eatWhiteDefault = false;
        $hasInterpolation = false;

        while ($this->matchString($m, $delim)) {
            if ($m[1] !== '') {
                $content[] = $m[1];
            }

            if ($m[2] === '#{') {
                $this->count -= strlen($m[2]);

                if ($this->interpolation($inter, false)) {
                    $content[] = $inter;
                    $hasInterpolation = true;
                } else {
                    $this->count += strlen($m[2]);
                    $content[] = '#{'; // ignore it
                }
            } elseif ($m[2] === '\\') {
                if ($this->matchChar('"', false)) {
                    $content[] = $m[2] . '"';
                } elseif ($this->matchChar("'", false)) {
                    $content[] = $m[2] . "'";
                } elseif ($this->literal("\\", 1, false)) {
                    $content[] = $m[2] . "\\";
                } elseif ($this->literal("\r\n", 2, false)
                  || $this->matchChar("\r", false)
                  || $this->matchChar("\n", false)
                  || $this->matchChar("\f", false)) {
                    // this is a continuation escaping, to be ignored
                } else {
                    $content[] = $m[2];
                }
            } else {
                $this->count -= strlen($delim);
                break; // delim
            }
        }

        $this->eatWhiteDefault = $oldWhite;

        if ($this->literal($delim, strlen($delim))) {
            if ($hasInterpolation) {
                $delim = '"';

                foreach ($content as &$string) {
                    if ($string === "\\\\") {
                        $string = "\\";
                    } elseif ($string === "\\'") {
                        $string = "'";
                    } elseif ($string === '\\"') {
                        $string = '"';
                    }
                }
            }

            $out = [Type::T_STRING, $delim, $content];

            return true;
        }

        $this->seek($s);

        return false;
    }

    /**
     * Parse keyword or interpolation
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function mixedKeyword(&$out)
    {
        $parts = [];

        $oldWhite = $this->eatWhiteDefault;
        $this->eatWhiteDefault = false;

        for (;;) {
            if ($this->keyword($key)) {
                $parts[] = $key;
                continue;
            }

            if ($this->interpolation($inter)) {
                $parts[] = $inter;
                continue;
            }

            break;
        }

        $this->eatWhiteDefault = $oldWhite;

        if (! $parts) {
            return false;
        }

        if ($this->eatWhiteDefault) {
            $this->whitespace();
        }

        $out = $parts;

        return true;
    }

    /**
     * Parse an unbounded string stopped by $end
     *
     * @param string $end
     * @param array  $out
     * @param string $nestingOpen
     *
     * @return boolean
     */
    protected function openString($end, &$out, $nestingOpen = null)
    {
        $oldWhite = $this->eatWhiteDefault;
        $this->eatWhiteDefault = false;

        $patt = '(.*?)([\'"]|#\{|' .
$this->pregQuote($end) . '|' . static::$commentPattern .
')';

        $nestingLevel = 0;

        $content = [];

        while ($this->match($patt, $m, false)) {
            if (isset($m[1]) && $m[1] !== '') {
                $content[] = $m[1];

                if ($nestingOpen) {
                    $nestingLevel += substr_count($m[1], $nestingOpen);
                }
            }

            $tok = $m[2];

            $this->count-= strlen($tok);

            if ($tok === $end && ! $nestingLevel--) {
                break;
            }

            if (($tok === "'" || $tok ===
'"') && $this->string($str)) {
                $content[] = $str;
                continue;
            }

            if ($tok === '#{' &&
$this->interpolation($inter)) {
                $content[] = $inter;
                continue;
            }

            $content[] = $tok;
            $this->count+= strlen($tok);
        }

        $this->eatWhiteDefault = $oldWhite;

        if (! $content) {
            return false;
        }

        // trim the end
        if (is_string(end($content))) {
            $content[count($content) - 1] = rtrim(end($content));
        }

        $out = [Type::T_STRING, '', $content];

        return true;
    }

    /**
     * Parser interpolation
     *
     * @param array   $out
     * @param boolean $lookWhite save information about whitespace before
and after
     *
     * @return boolean
     */
    protected function interpolation(&$out, $lookWhite = true)
    {
        $oldWhite = $this->eatWhiteDefault;
        $this->eatWhiteDefault = true;

        $s = $this->count;

        if ($this->literal('#{', 2) &&
$this->valueList($value) && $this->matchChar('}',
false)) {
            if ($value === [Type::T_SELF]) {
                $out = $value;
            } else {
                if ($lookWhite) {
                    $left = preg_match('/\s/',
$this->buffer[$s - 1]) ? ' ' : '';
                    $right = preg_match('/\s/',
$this->buffer[$this->count]) ? ' ': '';
                } else {
                    $left = $right = false;
                }

                $out = [Type::T_INTERPOLATE, $value, $left, $right];
            }

            $this->eatWhiteDefault = $oldWhite;

            if ($this->eatWhiteDefault) {
                $this->whitespace();
            }

            return true;
        }

        $this->seek($s);

        $this->eatWhiteDefault = $oldWhite;

        return false;
    }

    /**
     * Parse property name (as an array of parts or a string)
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function propertyName(&$out)
    {
        $parts = [];

        $oldWhite = $this->eatWhiteDefault;
        $this->eatWhiteDefault = false;

        for (;;) {
            if ($this->interpolation($inter)) {
                $parts[] = $inter;
                continue;
            }

            if ($this->keyword($text)) {
                $parts[] = $text;
                continue;
            }

            if (! $parts && $this->match('[:.#]', $m,
false)) {
                // css hacks
                $parts[] = $m[0];
                continue;
            }

            break;
        }

        $this->eatWhiteDefault = $oldWhite;

        if (! $parts) {
            return false;
        }

        // match comment hack
        if (preg_match(
            static::$whitePattern,
            $this->buffer,
            $m,
            null,
            $this->count
        )) {
            if (! empty($m[0])) {
                $parts[] = $m[0];
                $this->count += strlen($m[0]);
            }
        }

        $this->whitespace(); // get any extra whitespace

        $out = [Type::T_STRING, '', $parts];

        return true;
    }

    /**
     * Parse comma separated selector list
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function selectors(&$out, $subSelector = false)
    {
        $s = $this->count;
        $selectors = [];

        while ($this->selector($sel, $subSelector)) {
            $selectors[] = $sel;

            if (! $this->matchChar(',', true)) {
                break;
            }

            while ($this->matchChar(',', true)) {
                ; // ignore extra
            }
        }

        if (! $selectors) {
            $this->seek($s);

            return false;
        }

        $out = $selectors;

        return true;
    }

    /**
     * Parse whitespace separated selector list
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function selector(&$out, $subSelector = false)
    {
        $selector = [];

        for (;;) {
            if ($this->match('[>+~]+', $m, true)) {
                $selector[] = [$m[0]];
                continue;
            }

            if ($this->selectorSingle($part, $subSelector)) {
                $selector[] = $part;
                $this->match('\s+', $m);
                continue;
            }

            if ($this->match('\/[^\/]+\/', $m, true)) {
                $selector[] = [$m[0]];
                continue;
            }

            break;
        }

        if (! $selector) {
            return false;
        }

        $out = $selector;

        return true;
    }

    /**
     * Parse the parts that make up a selector
     *
     * {@internal
     *     div[yes=no]#something.hello.world:nth-child(-2n+1)%placeholder
     * }}
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function selectorSingle(&$out, $subSelector = false)
    {
        $oldWhite = $this->eatWhiteDefault;
        $this->eatWhiteDefault = false;

        $parts = [];

        if ($this->matchChar('*', false)) {
            $parts[] = '*';
        }

        for (;;) {
            if (! isset($this->buffer[$this->count])) {
                break;
            }

            $s = $this->count;
            $char = $this->buffer[$this->count];

            // see if we can stop early
            if ($char === '{' || $char === ',' || $char
=== ';' || $char === '}' || $char === '@') {
                break;
            }

            // parsing a sub selector in () stop with the closing )
            if ($subSelector && $char === ')') {
                break;
            }

            //self
            switch ($char) {
                case '&':
                    $parts[] = Compiler::$selfSelector;
                    $this->count++;
                    continue 2;

                case '.':
                    $parts[] = '.';
                    $this->count++;
                    continue 2;

                case '|':
                    $parts[] = '|';
                    $this->count++;
                    continue 2;
            }

            if ($char === '\\' &&
$this->match('\\\\\S', $m)) {
                $parts[] = $m[0];
                continue;
            }

            if ($char === '%') {
                $this->count++;

                if ($this->placeholder($placeholder)) {
                    $parts[] = '%';
                    $parts[] = $placeholder;
                    continue;
                }

                break;
            }

            if ($char === '#') {
                if ($this->interpolation($inter)) {
                    $parts[] = $inter;
                    continue;
                }

                $parts[] = '#';
                $this->count++;
                continue;
            }

            // a pseudo selector
            if ($char === ':') {
                if ($this->buffer[$this->count + 1] ===
':') {
                    $this->count += 2;
                    $part = '::';
                } else {
                    $this->count++;
                    $part = ':';
                }

                if ($this->mixedKeyword($nameParts)) {
                    $parts[] = $part;

                    foreach ($nameParts as $sub) {
                        $parts[] = $sub;
                    }

                    $ss = $this->count;

                    if ($nameParts === ['not'] || $nameParts ===
['is'] ||
                        $nameParts === ['has'] || $nameParts ===
['where']
                    ) {
                        if ($this->matchChar('(') &&
                          ($this->selectors($subs, true) || true)
&&
                          $this->matchChar(')')
                        ) {
                            $parts[] = '(';

                            while ($sub = array_shift($subs)) {
                                while ($ps = array_shift($sub)) {
                                    foreach ($ps as &$p) {
                                        $parts[] = $p;
                                    }
                                    if (count($sub) && reset($sub))
{
                                        $parts[] = ' ';
                                    }
                                }
                                if (count($subs) && reset($subs)) {
                                    $parts[] = ', ';
                                }
                            }

                            $parts[] = ')';
                        } else {
                            $this->seek($ss);
                        }
                    } else {
                        if ($this->matchChar('(') &&
                          ($this->openString(')', $str,
'(') || true) &&
                          $this->matchChar(')')
                        ) {
                            $parts[] = '(';

                            if (! empty($str)) {
                                $parts[] = $str;
                            }

                            $parts[] = ')';
                        } else {
                            $this->seek($ss);
                        }
                    }

                    continue;
                }
            }

            $this->seek($s);

            // attribute selector
            if ($char === '[' &&
                $this->matchChar('[') &&
                ($this->openString(']', $str, '[')
|| true) &&
                $this->matchChar(']')
            ) {
                $parts[] = '[';

                if (! empty($str)) {
                    $parts[] = $str;
                }

                $parts[] = ']';
                continue;
            }

            $this->seek($s);

            // for keyframes
            if ($this->unit($unit)) {
                $parts[] = $unit;
                continue;
            }

            if ($this->keyword($name)) {
                $parts[] = $name;
                continue;
            }

            break;
        }

        $this->eatWhiteDefault = $oldWhite;

        if (! $parts) {
            return false;
        }

        $out = $parts;

        return true;
    }

    /**
     * Parse a variable
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function variable(&$out)
    {
        $s = $this->count;

        if ($this->matchChar('$', false) &&
$this->keyword($name)) {
            $out = [Type::T_VARIABLE, $name];

            return true;
        }

        $this->seek($s);

        return false;
    }

    /**
     * Parse a keyword
     *
     * @param string  $word
     * @param boolean $eatWhitespace
     *
     * @return boolean
     */
    protected function keyword(&$word, $eatWhitespace = null)
    {
        if ($this->match(
            $this->utf8
                ?
'(([\pL\w\x{00A0}-\x{10FFFF}_\-\*!"\']|[\\\\].)([\pL\w\x{00A0}-\x{10FFFF}\-_"\']|[\\\\].)*)'
                :
'(([\w_\-\*!"\']|[\\\\].)([\w\-_"\']|[\\\\].)*)',
            $m,
            $eatWhitespace
        )) {
            $word = $m[1];

            return true;
        }

        return false;
    }

    /**
     * Parse a placeholder
     *
     * @param string $placeholder
     *
     * @return boolean
     */
    protected function placeholder(&$placeholder)
    {
        if ($this->match(
            $this->utf8
                ? '([\pL\w\-_]+)'
                : '([\w\-_]+)',
            $m
        )) {
            $placeholder = $m[1];

            return true;
        }
        if ($this->interpolation($placeholder)) {
            return true;
        }

        return false;
    }

    /**
     * Parse a url
     *
     * @param array $out
     *
     * @return boolean
     */
    protected function url(&$out)
    {
        if
($this->match('(url\(\s*(["\']?)([^)]+)\2\s*\))',
$m)) {
            $out = [Type::T_STRING, '', ['url(' . $m[2]
. $m[3] . $m[2] . ')']];

            return true;
        }

        return false;
    }

    /**
     * Consume an end of statement delimiter
     *
     * @return boolean
     */
    protected function end()
    {
        if ($this->matchChar(';')) {
            return true;
        }

        if ($this->count === strlen($this->buffer) ||
$this->buffer[$this->count] === '}') {
            // if there is end of file or a closing block next then we
don't need a ;
            return true;
        }

        return false;
    }

    /**
     * Strip assignment flag from the list
     *
     * @param array $value
     *
     * @return array
     */
    protected function stripAssignmentFlags(&$value)
    {
        $flags = [];

        for ($token = &$value; $token[0] === Type::T_LIST &&
($s = count($token[2])); $token = &$lastNode) {
            $lastNode = &$token[2][$s - 1];

            while ($lastNode[0] === Type::T_KEYWORD &&
in_array($lastNode[1], ['!default', '!global'])) {
                array_pop($token[2]);

                $node = end($token[2]);

                $token = $this->flattenList($token);

                $flags[] = $lastNode[1];

                $lastNode = $node;
            }
        }

        return $flags;
    }

    /**
     * Strip optional flag from selector list
     *
     * @param array $selectors
     *
     * @return string
     */
    protected function stripOptionalFlag(&$selectors)
    {
        $optional = false;

        $selector = end($selectors);
        $part = end($selector);

        if ($part === ['!optional']) {
            array_pop($selectors[count($selectors) - 1]);

            $optional = true;
        }

        return $optional;
    }

    /**
     * Turn list of length 1 into value type
     *
     * @param array $value
     *
     * @return array
     */
    protected function flattenList($value)
    {
        if ($value[0] === Type::T_LIST && count($value[2]) === 1) {
            return $this->flattenList($value[2][0]);
        }

        return $value;
    }

    /**
     * @deprecated
     *
     * {@internal
     *     advance counter to next occurrence of $what
     *     $until - don't include $what in advance
     *     $allowNewline, if string, will be used as valid char set
     * }}
     */
    protected function to($what, &$out, $until = false, $allowNewline =
false)
    {
        if (is_string($allowNewline)) {
            $validChars = $allowNewline;
        } else {
            $validChars = $allowNewline ? '.' :
"[^\n]";
        }

        if (! $this->match('(' . $validChars . '*?)'
. $this->pregQuote($what), $m, ! $until)) {
            return false;
        }

        if ($until) {
            $this->count -= strlen($what); // give back $what
        }

        $out = $m[1];

        return true;
    }

    /**
     * @deprecated
     */
    protected function show()
    {
        if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
            return $m[1];
        }

        return '';
    }

    /**
     * Quote regular expression
     *
     * @param string $what
     *
     * @return string
     */
    private function pregQuote($what)
    {
        return preg_quote($what, '/');
    }

    /**
     * Extract line numbers from buffer
     *
     * @param string $buffer
     */
    private function extractLineNumbers($buffer)
    {
        $this->sourcePositions = [0 => 0];
        $prev = 0;

        while (($pos = strpos($buffer, "\n", $prev)) !== false) {
            $this->sourcePositions[] = $pos;
            $prev = $pos + 1;
        }

        $this->sourcePositions[] = strlen($buffer);

        if (substr($buffer, -1) !== "\n") {
            $this->sourcePositions[] = strlen($buffer) + 1;
        }
    }

    /**
     * Get source line number and column (given character position in the
buffer)
     *
     * @param integer $pos
     *
     * @return array
     */
    private function getSourcePosition($pos)
    {
        $low = 0;
        $high = count($this->sourcePositions);

        while ($low < $high) {
            $mid = (int) (($high + $low) / 2);

            if ($pos < $this->sourcePositions[$mid]) {
                $high = $mid - 1;
                continue;
            }

            if ($pos >= $this->sourcePositions[$mid + 1]) {
                $low = $mid + 1;
                continue;
            }

            return [$mid + 1, $pos - $this->sourcePositions[$mid]];
        }

        return [$low + 1, $pos - $this->sourcePositions[$low]];
    }

    /**
     * Save internal encoding
     */
    private function saveEncoding()
    {
        if (version_compare(PHP_VERSION, '7.2.0') >= 0) {
            return;
        }

        $iniDirective = 'mbstring' . '.func_overload';
// deprecated in PHP 7.2

        if (ini_get($iniDirective) & 2) {
            $this->encoding = mb_internal_encoding();

            mb_internal_encoding('iso-8859-1');
        }
    }

    /**
     * Restore internal encoding
     */
    private function restoreEncoding()
    {
        if ($this->encoding) {
            mb_internal_encoding($this->encoding);
        }
    }
}
vendor/leafo/scssphp/src/SourceMap/Base64.php000064400000006271151166614550015054
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2015 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\SourceMap;

/**
 * Base 64 Encode/Decode
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class Base64
{
    /**
     * @var array
     */
    private static $encodingMap = [
        0 => 'A',
        1 => 'B',
        2 => 'C',
        3 => 'D',
        4 => 'E',
        5 => 'F',
        6 => 'G',
        7 => 'H',
        8 => 'I',
        9 => 'J',
        10 => 'K',
        11 => 'L',
        12 => 'M',
        13 => 'N',
        14 => 'O',
        15 => 'P',
        16 => 'Q',
        17 => 'R',
        18 => 'S',
        19 => 'T',
        20 => 'U',
        21 => 'V',
        22 => 'W',
        23 => 'X',
        24 => 'Y',
        25 => 'Z',
        26 => 'a',
        27 => 'b',
        28 => 'c',
        29 => 'd',
        30 => 'e',
        31 => 'f',
        32 => 'g',
        33 => 'h',
        34 => 'i',
        35 => 'j',
        36 => 'k',
        37 => 'l',
        38 => 'm',
        39 => 'n',
        40 => 'o',
        41 => 'p',
        42 => 'q',
        43 => 'r',
        44 => 's',
        45 => 't',
        46 => 'u',
        47 => 'v',
        48 => 'w',
        49 => 'x',
        50 => 'y',
        51 => 'z',
        52 => '0',
        53 => '1',
        54 => '2',
        55 => '3',
        56 => '4',
        57 => '5',
        58 => '6',
        59 => '7',
        60 => '8',
        61 => '9',
        62 => '+',
        63 => '/',
    ];

    /**
     * @var array
     */
    private static $decodingMap = [
        'A' => 0,
        'B' => 1,
        'C' => 2,
        'D' => 3,
        'E' => 4,
        'F' => 5,
        'G' => 6,
        'H' => 7,
        'I' => 8,
        'J' => 9,
        'K' => 10,
        'L' => 11,
        'M' => 12,
        'N' => 13,
        'O' => 14,
        'P' => 15,
        'Q' => 16,
        'R' => 17,
        'S' => 18,
        'T' => 19,
        'U' => 20,
        'V' => 21,
        'W' => 22,
        'X' => 23,
        'Y' => 24,
        'Z' => 25,
        'a' => 26,
        'b' => 27,
        'c' => 28,
        'd' => 29,
        'e' => 30,
        'f' => 31,
        'g' => 32,
        'h' => 33,
        'i' => 34,
        'j' => 35,
        'k' => 36,
        'l' => 37,
        'm' => 38,
        'n' => 39,
        'o' => 40,
        'p' => 41,
        'q' => 42,
        'r' => 43,
        's' => 44,
        't' => 45,
        'u' => 46,
        'v' => 47,
        'w' => 48,
        'x' => 49,
        'y' => 50,
        'z' => 51,
        0 => 52,
        1 => 53,
        2 => 54,
        3 => 55,
        4 => 56,
        5 => 57,
        6 => 58,
        7 => 59,
        8 => 60,
        9 => 61,
        '+' => 62,
        '/' => 63,
    ];

    /**
     * Convert to base64
     *
     * @param integer $value
     *
     * @return string
     */
    public static function encode($value)
    {
        return self::$encodingMap[$value];
    }

    /**
     * Convert from base64
     *
     * @param string $value
     *
     * @return integer
     */
    public static function decode($value)
    {
        return self::$decodingMap[$value];
    }
}
vendor/leafo/scssphp/src/SourceMap/Base64VLQ.php000064400000007076151166614550015443
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2015 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\SourceMap;

use Leafo\ScssPhp\SourceMap\Base64;

/**
 * Base 64 VLQ
 *
 * Based on the Base 64 VLQ implementation in Closure Compiler:
 *
https://github.com/google/closure-compiler/blob/master/src/com/google/debugging/sourcemap/Base64VLQ.java
 *
 * Copyright 2011 The Closure Compiler Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the
"License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS"
BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author John Lenz <johnlenz@google.com>
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class Base64VLQ
{
    // A Base64 VLQ digit can represent 5 bits, so it is base-32.
    const VLQ_BASE_SHIFT = 5;

    // A mask of bits for a VLQ digit (11111), 31 decimal.
    const VLQ_BASE_MASK = 31;

    // The continuation bit is the 6th bit.
    const VLQ_CONTINUATION_BIT = 32;

    /**
     * Returns the VLQ encoded value.
     *
     * @param integer $value
     *
     * @return string
     */
    public static function encode($value)
    {
        $encoded = '';
        $vlq = self::toVLQSigned($value);

        do {
            $digit = $vlq & self::VLQ_BASE_MASK;
            $vlq >>= self::VLQ_BASE_SHIFT;

            if ($vlq > 0) {
                $digit |= self::VLQ_CONTINUATION_BIT;
            }

            $encoded .= Base64::encode($digit);
        } while ($vlq > 0);

        return $encoded;
    }

    /**
     * Decodes VLQValue.
     *
     * @param string $str
     * @param integer $index
     *
     * @return integer
     */
    public static function decode($str, &$index)
    {
        $result = 0;
        $shift = 0;

        do {
            $c = $str[$index++];
            $digit = Base64::decode($c);
            $continuation = ($digit & self::VLQ_CONTINUATION_BIT) != 0;
            $digit &= self::VLQ_BASE_MASK;
            $result = $result + ($digit << $shift);
            $shift = $shift + self::VLQ_BASE_SHIFT;
        } while ($continuation);

        return self::fromVLQSigned($result);
    }

    /**
     * Converts from a two-complement value to a value where the sign bit
is
     * is placed in the least significant bit.  For example, as decimals:
     *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
     *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
     *
     * @param integer $value
     *
     * @return integer
     */
    private static function toVLQSigned($value)
    {
        if ($value < 0) {
            return ((-$value) << 1) + 1;
        }

        return ($value << 1) + 0;
    }

    /**
     * Converts to a two-complement value from a value where the sign bit
is
     * is placed in the least significant bit.  For example, as decimals:
     *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
     *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
     *
     * @param integer $value
     *
     * @return integer
     */
    private static function fromVLQSigned($value)
    {
        $negate = ($value & 1) === 1;
        $value = $value >> 1;

        return $negate ? -$value : $value;
    }
}
vendor/leafo/scssphp/src/SourceMap/Base64VLQEncoder.php000064400000014471151166614550016740
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\SourceMap;

/**
 * Base64 VLQ Encoder
 *
 * {@internal Derivative of oyejorge/less.php's
lib/SourceMap/Base64VLQ.php, relicensed with permission. }}
 *
 * @author Josh Schmidt <oyejorge@gmail.com>
 * @author Nicolas FRANÇOIS <nicolas.francois@frog-labs.com>
 */
class Base64VLQEncoder
{
    /**
     * Shift
     *
     * @var integer
     */
    private $shift = 5;

    /**
     * Mask
     *
     * @var integer
     */
    private $mask = 0x1F; // == (1 << shift) == 0b00011111

    /**
     * Continuation bit
     *
     * @var integer
     */
    private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000

    /**
     * Char to integer map
     *
     * @var array
     */
    private $charToIntMap = [
        'A' => 0,  'B' => 1,  'C' =>
2,  'D' => 3,  'E' => 4,  'F' => 5, 
'G' => 6,  'H' => 7,
        'I' => 8,  'J' => 9,  'K' =>
10, 'L' => 11, 'M' => 12, 'N' => 13,
'O' => 14, 'P' => 15,
        'Q' => 16, 'R' => 17, 'S' =>
18, 'T' => 19, 'U' => 20, 'V' => 21,
'W' => 22, 'X' => 23,
        'Y' => 24, 'Z' => 25, 'a' =>
26, 'b' => 27, 'c' => 28, 'd' => 29,
'e' => 30, 'f' => 31,
        'g' => 32, 'h' => 33, 'i' =>
34, 'j' => 35, 'k' => 36, 'l' => 37,
'm' => 38, 'n' => 39,
        'o' => 40, 'p' => 41, 'q' =>
42, 'r' => 43, 's' => 44, 't' => 45,
'u' => 46, 'v' => 47,
        'w' => 48, 'x' => 49, 'y' =>
50, 'z' => 51,   0 => 52,   1 => 53,   2 => 54,   3
=> 55,
          4 => 56,   5 => 57,   6 => 58,   7 => 59,   8 =>
60,   9 => 61, '+' => 62, '/' => 63,
    ];

    /**
     * Integer to char map
     *
     * @var array
     */
    private $intToCharMap = [
         0 => 'A',  1 => 'B',  2 =>
'C',  3 => 'D',  4 => 'E',  5 =>
'F',  6 => 'G',  7 => 'H',
         8 => 'I',  9 => 'J', 10 =>
'K', 11 => 'L', 12 => 'M', 13 =>
'N', 14 => 'O', 15 => 'P',
        16 => 'Q', 17 => 'R', 18 =>
'S', 19 => 'T', 20 => 'U', 21 =>
'V', 22 => 'W', 23 => 'X',
        24 => 'Y', 25 => 'Z', 26 =>
'a', 27 => 'b', 28 => 'c', 29 =>
'd', 30 => 'e', 31 => 'f',
        32 => 'g', 33 => 'h', 34 =>
'i', 35 => 'j', 36 => 'k', 37 =>
'l', 38 => 'm', 39 => 'n',
        40 => 'o', 41 => 'p', 42 =>
'q', 43 => 'r', 44 => 's', 45 =>
't', 46 => 'u', 47 => 'v',
        48 => 'w', 49 => 'x', 50 =>
'y', 51 => 'z', 52 => '0', 53 =>
'1', 54 => '2', 55 => '3',
        56 => '4', 57 => '5', 58 =>
'6', 59 => '7', 60 => '8', 61 =>
'9', 62 => '+', 63 => '/',
    ];

    /**
     * Constructor
     */
    public function __construct()
    {
        // I leave it here for future reference
        // foreach
(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
as $i => $char)
        // {
        //     $this->charToIntMap[$char] = $i;
        //     $this->intToCharMap[$i] = $char;
        // }
    }

    /**
     * Convert from a two-complement value to a value where the sign bit is
     * is placed in the least significant bit. For example, as decimals:
     *   1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
     *   2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
     * We generate the value for 32 bit machines, hence -2147483648 becomes
1, not 4294967297,
     * even on a 64 bit machine.
     *
     * @param string $aValue
     */
    public function toVLQSigned($aValue)
    {
        return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) +
1 : ($aValue << 1) + 0);
    }

    /**
     * Convert to a two-complement value from a value where the sign bit is
     * is placed in the least significant bit. For example, as decimals:
     *   2 (10 binary) becomes 1, 3 (11 binary) becomes -1
     *   4 (100 binary) becomes 2, 5 (101 binary) becomes -2
     * We assume that the value was generated with a 32 bit machine in
mind.
     * Hence
     *   1 becomes -2147483648
     * even on a 64 bit machine.
     *
     * @param integer $aValue
     */
    public function fromVLQSigned($aValue)
    {
        return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1
- 0x7fffffff) : $this->zeroFill($aValue, 1);
    }

    /**
     * Return the base 64 VLQ encoded value.
     *
     * @param string $aValue The value to encode
     *
     * @return string The encoded value
     */
    public function encode($aValue)
    {
        $encoded = '';
        $vlq = $this->toVLQSigned($aValue);

        do {
            $digit = $vlq & $this->mask;
            $vlq = $this->zeroFill($vlq, $this->shift);

            if ($vlq > 0) {
                $digit |= $this->continuationBit;
            }

            $encoded .= $this->base64Encode($digit);
        } while ($vlq > 0);

        return $encoded;
    }

    /**
     * Return the value decoded from base 64 VLQ.
     *
     * @param string $encoded The encoded value to decode
     *
     * @return integer The decoded value
     */
    public function decode($encoded)
    {
        $vlq = 0;
        $i = 0;

        do {
            $digit = $this->base64Decode($encoded[$i]);
            $vlq |= ($digit & $this->mask) << ($i *
$this->shift);
            $i++;
        } while ($digit & $this->continuationBit);

        return $this->fromVLQSigned($vlq);
    }

    /**
     * Right shift with zero fill.
     *
     * @param integer $a number to shift
     * @param integer $b number of bits to shift
     *
     * @return integer
     */
    public function zeroFill($a, $b)
    {
        return ($a >= 0) ? ($a >> $b) : ($a >> $b) &
(PHP_INT_MAX >> ($b - 1));
    }

    /**
     * Encode single 6-bit digit as base64.
     *
     * @param integer $number
     *
     * @return string
     *
     * @throws \Exception If the number is invalid
     */
    public function base64Encode($number)
    {
        if ($number < 0 || $number > 63) {
            throw new \Exception(sprintf('Invalid number
"%s" given. Must be between 0 and 63.', $number));
        }

        return $this->intToCharMap[$number];
    }

    /**
     * Decode single 6-bit digit from base64
     *
     * @param string $char
     *
     * @return integer
     *
     * @throws \Exception If the number is invalid
     */
    public function base64Decode($char)
    {
        if (! array_key_exists($char, $this->charToIntMap)) {
            throw new \Exception(sprintf('Invalid base 64 digit
"%s" given.', $char));
        }

        return $this->charToIntMap[$char];
    }
}
vendor/leafo/scssphp/src/SourceMap/SourceMapGenerator.php000064400000023412151166614550017571
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp\SourceMap;

use Leafo\ScssPhp\Exception\CompilerException;

/**
 * Source Map Generator
 *
 * {@internal Derivative of oyejorge/less.php's
lib/SourceMap/Generator.php, relicensed with permission. }}
 *
 * @author Josh Schmidt <oyejorge@gmail.com>
 * @author Nicolas FRANÇOIS <nicolas.francois@frog-labs.com>
 */
class SourceMapGenerator
{
    /**
     * What version of source map does the generator generate?
     */
    const VERSION = 3;

    /**
     * Array of default options
     *
     * @var array
     */
    protected $defaultOptions = [
        // an optional source root, useful for relocating source files
        // on a server or removing repeated values in the
'sources' entry.
        // This value is prepended to the individual entries in the
'source' field.
        'sourceRoot' => '',

        // an optional name of the generated code that this source map is
associated with.
        'sourceMapFilename' => null,

        // url of the map
        'sourceMapURL' => null,

        // absolute path to a file to write the map to
        'sourceMapWriteTo' => null,

        // output source contents?
        'outputSourceFiles' => false,

        // base path for filename normalization
        'sourceMapRootpath' => '',

        // base path for filename normalization
        'sourceMapBasepath' => ''
    ];

    /**
     * The base64 VLQ encoder
     *
     * @var \Leafo\ScssPhp\SourceMap\Base64VLQ
     */
    protected $encoder;

    /**
     * Array of mappings
     *
     * @var array
     */
    protected $mappings = [];

    /**
     * Array of contents map
     *
     * @var array
     */
    protected $contentsMap = [];

    /**
     * File to content map
     *
     * @var array
     */
    protected $sources = [];
    protected $sourceKeys = [];

    /**
     * @var array
     */
    private $options;

    public function __construct(array $options = [])
    {
        $this->options = array_merge($this->defaultOptions,
$options);
        $this->encoder = new Base64VLQ();
    }

    /**
     * Adds a mapping
     *
     * @param integer $generatedLine   The line number in generated file
     * @param integer $generatedColumn The column number in generated file
     * @param integer $originalLine    The line number in original file
     * @param integer $originalColumn  The column number in original file
     * @param string  $sourceFile      The original source file
     */
    public function addMapping($generatedLine, $generatedColumn,
$originalLine, $originalColumn, $sourceFile)
    {
        $this->mappings[] = [
            'generated_line'   => $generatedLine,
            'generated_column' => $generatedColumn,
            'original_line'    => $originalLine,
            'original_column'  => $originalColumn,
            'source_file'      => $sourceFile
        ];

        $this->sources[$sourceFile] = $sourceFile;
    }

    /**
     * Saves the source map to a file
     *
     * @param string $content The content to write
     *
     * @return string
     *
     * @throws \Leafo\ScssPhp\Exception\CompilerException If the file could
not be saved
     */
    public function saveMap($content)
    {
        $file = $this->options['sourceMapWriteTo'];
        $dir  = dirname($file);

        // directory does not exist
        if (! is_dir($dir)) {
            // FIXME: create the dir automatically?
            throw new CompilerException(
                sprintf('The directory "%s" does not exist.
Cannot save the source map.', $dir)
            );
        }

        // FIXME: proper saving, with dir write check!
        if (file_put_contents($file, $content) === false) {
            throw new CompilerException(sprintf('Cannot save the
source map to "%s"', $file));
        }

        return $this->options['sourceMapURL'];
    }

    /**
     * Generates the JSON source map
     *
     * @return string
     *
     * @see
https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
     */
    public function generateJson()
    {
        $sourceMap = [];
        $mappings  = $this->generateMappings();

        // File version (always the first entry in the object) and must be
a positive integer.
        $sourceMap['version'] = self::VERSION;

        // An optional name of the generated code that this source map is
associated with.
        $file = $this->options['sourceMapFilename'];

        if ($file) {
            $sourceMap['file'] = $file;
        }

        // An optional source root, useful for relocating source files on a
server or removing repeated values in the
        // 'sources' entry. This value is prepended to the
individual entries in the 'source' field.
        $root = $this->options['sourceRoot'];

        if ($root) {
            $sourceMap['sourceRoot'] = $root;
        }

        // A list of original sources used by the 'mappings'
entry.
        $sourceMap['sources'] = [];

        foreach ($this->sources as $sourceUri => $sourceFilename) {
            $sourceMap['sources'][] =
$this->normalizeFilename($sourceFilename);
        }

        // A list of symbol names used by the 'mappings' entry.
        $sourceMap['names'] = [];

        // A string with the encoded mapping data.
        $sourceMap['mappings'] = $mappings;

        if ($this->options['outputSourceFiles']) {
            // An optional list of source content, useful when the
'source' can't be hosted.
            // The contents are listed in the same order as the sources
above.
            // 'null' may be used if some original sources should
be retrieved by name.
            $sourceMap['sourcesContent'] =
$this->getSourcesContent();
        }

        // less.js compat fixes
        if (count($sourceMap['sources']) &&
empty($sourceMap['sourceRoot'])) {
            unset($sourceMap['sourceRoot']);
        }

        return json_encode($sourceMap, JSON_UNESCAPED_SLASHES);
    }

    /**
     * Returns the sources contents
     *
     * @return array|null
     */
    protected function getSourcesContent()
    {
        if (empty($this->sources)) {
            return null;
        }

        $content = [];

        foreach ($this->sources as $sourceFile) {
            $content[] = file_get_contents($sourceFile);
        }

        return $content;
    }

    /**
     * Generates the mappings string
     *
     * @return string
     */
    public function generateMappings()
    {
        if (! count($this->mappings)) {
            return '';
        }

        $this->sourceKeys = array_flip(array_keys($this->sources));

        // group mappings by generated line number.
        $groupedMap = $groupedMapEncoded = [];

        foreach ($this->mappings as $m) {
            $groupedMap[$m['generated_line']][] = $m;
        }

        ksort($groupedMap);
        $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine =
$lastOriginalColumn = 0;

        foreach ($groupedMap as $lineNumber => $lineMap) {
            while (++$lastGeneratedLine < $lineNumber) {
                $groupedMapEncoded[] = ';';
            }

            $lineMapEncoded = [];
            $lastGeneratedColumn = 0;

            foreach ($lineMap as $m) {
                $mapEncoded =
$this->encoder->encode($m['generated_column'] -
$lastGeneratedColumn);
                $lastGeneratedColumn = $m['generated_column'];

                // find the index
                if ($m['source_file']) {
                    $index =
$this->findFileIndex($m['source_file']);

                    if ($index !== false) {
                        $mapEncoded .= $this->encoder->encode($index
- $lastOriginalIndex);
                        $lastOriginalIndex = $index;
                        // lines are stored 0-based in SourceMap spec
version 3
                        $mapEncoded .=
$this->encoder->encode($m['original_line'] - 1 -
$lastOriginalLine);
                        $lastOriginalLine = $m['original_line'] -
1;
                        $mapEncoded .=
$this->encoder->encode($m['original_column'] -
$lastOriginalColumn);
                        $lastOriginalColumn =
$m['original_column'];
                    }
                }

                $lineMapEncoded[] = $mapEncoded;
            }

            $groupedMapEncoded[] = implode(',', $lineMapEncoded)
. ';';
        }

        return rtrim(implode($groupedMapEncoded), ';');
    }

    /**
     * Finds the index for the filename
     *
     * @param string $filename
     *
     * @return integer|false
     */
    protected function findFileIndex($filename)
    {
        return $this->sourceKeys[$filename];
    }

    /**
     * Normalize filename
     *
     * @param string $filename
     *
     * @return string
     */
    protected function normalizeFilename($filename)
    {
        $filename = $this->fixWindowsPath($filename);
        $rootpath = $this->options['sourceMapRootpath'];
        $basePath = $this->options['sourceMapBasepath'];

        // "Trim" the 'sourceMapBasepath' from the
output filename.
        if (strlen($basePath) && strpos($filename, $basePath) ===
0) {
            $filename = substr($filename, strlen($basePath));
        }

        // Remove extra leading path separators.
        if (strpos($filename, '\\') === 0 || strpos($filename,
'/') === 0) {
            $filename = substr($filename, 1);
        }

        return $rootpath . $filename;
    }

    /**
     * Fix windows paths
     *
     * @param string  $path
     * @param boolean $addEndSlash
     *
     * @return string
     */
    public function fixWindowsPath($path, $addEndSlash = false)
    {
        $slash = ($addEndSlash) ? '/' : '';

        if (! empty($path)) {
            $path = str_replace('\\', '/', $path);
            $path = rtrim($path, '/') . $slash;
        }

        return $path;
    }
}
vendor/leafo/scssphp/src/Type.php000064400000003445151166614550013053
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

/**
 * Block/node types
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class Type
{
    const T_ASSIGN = 'assign';
    const T_AT_ROOT = 'at-root';
    const T_BLOCK = 'block';
    const T_BREAK = 'break';
    const T_CHARSET = 'charset';
    const T_COLOR = 'color';
    const T_COMMENT = 'comment';
    const T_CONTINUE = 'continue';
    const T_CONTROL = 'control';
    const T_DEBUG = 'debug';
    const T_DIRECTIVE = 'directive';
    const T_EACH = 'each';
    const T_ELSE = 'else';
    const T_ELSEIF = 'elseif';
    const T_ERROR = 'error';
    const T_EXPRESSION = 'exp';
    const T_EXTEND = 'extend';
    const T_FOR = 'for';
    const T_FUNCTION = 'function';
    const T_FUNCTION_CALL = 'fncall';
    const T_HSL = 'hsl';
    const T_IF = 'if';
    const T_IMPORT = 'import';
    const T_INCLUDE = 'include';
    const T_INTERPOLATE = 'interpolate';
    const T_INTERPOLATED = 'interpolated';
    const T_KEYWORD = 'keyword';
    const T_LIST = 'list';
    const T_MAP = 'map';
    const T_MEDIA = 'media';
    const T_MEDIA_EXPRESSION = 'mediaExp';
    const T_MEDIA_TYPE = 'mediaType';
    const T_MEDIA_VALUE = 'mediaValue';
    const T_MIXIN = 'mixin';
    const T_MIXIN_CONTENT = 'mixin_content';
    const T_NESTED_PROPERTY = 'nestedprop';
    const T_NOT = 'not';
    const T_NULL = 'null';
    const T_NUMBER = 'number';
    const T_RETURN = 'return';
    const T_ROOT = 'root';
    const T_SCSSPHP_IMPORT_ONCE = 'scssphp-import-once';
    const T_SELF = 'self';
    const T_STRING = 'string';
    const T_UNARY = 'unary';
    const T_VARIABLE = 'var';
    const T_WARN = 'warn';
    const T_WHILE = 'while';
}
vendor/leafo/scssphp/src/Util.php000064400000003502151166614550013041
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

use Leafo\ScssPhp\Base\Range;
use Leafo\ScssPhp\Exception\RangeException;

/**
 * Utilty functions
 *
 * @author Anthon Pang <anthon.pang@gmail.com>
 */
class Util
{
    /**
     * Asserts that `value` falls within `range` (inclusive), leaving
     * room for slight floating-point errors.
     *
     * @param string                    $name  The name of the value. Used
in the error message.
     * @param \Leafo\ScssPhp\Base\Range $range Range of values.
     * @param array                     $value The value to check.
     * @param string                    $unit  The unit of the value. Used
in error reporting.
     *
     * @return mixed `value` adjusted to fall within range, if it was
outside by a floating-point margin.
     *
     * @throws \Leafo\ScssPhp\Exception\RangeException
     */
    public static function checkRange($name, Range $range, $value, $unit =
'')
    {
        $val = $value[1];
        $grace = new Range(-0.00001, 0.00001);

        if ($range->includes($val)) {
            return $val;
        }

        if ($grace->includes($val - $range->first)) {
            return $range->first;
        }

        if ($grace->includes($val - $range->last)) {
            return $range->last;
        }

        throw new RangeException("$name {$val} must be between
{$range->first} and {$range->last}$unit");
    }

    /**
     * Encode URI component
     *
     * @param string $string
     *
     * @return string
     */
    public static function encodeURIComponent($string)
    {
        $revert = ['%21' => '!', '%2A'
=> '*', '%27' => "'",
'%28' => '(', '%29' => ')'];

        return strtr(rawurlencode($string), $revert);
    }
}
vendor/leafo/scssphp/src/Version.php000064400000000470151166614550013552
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

/**
 * SCSSPHP version
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Version
{
    const VERSION = 'v0.8.4';
}
vendor/pimple/pimple/composer.json000064400000001256151166614550013355
0ustar00{
    "name": "pimple/pimple",
    "type": "library",
    "description": "Pimple, a simple Dependency Injection
Container",
    "keywords": ["dependency injection",
"container"],
    "homepage": "http://pimple.sensiolabs.org",
    "license": "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        }
    ],
    "require": {
        "php": ">=5.3.0",
        "psr/container": "^1.0"
    },
    "require-dev": {
        "symfony/phpunit-bridge": "^3.2"
    },
    "autoload": {
        "psr-0": { "Pimple": "src/" }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "3.2.x-dev"
        }
    }
}
vendor/pimple/pimple/src/Pimple/Container.php000064400000022177151166614550015310
0ustar00<?php

/*
 * This file is part of Pimple.
 *
 * Copyright (c) 2009 Fabien Potencier
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
copy
 * of this software and associated documentation files (the
"Software"), to deal
 * in the Software without restriction, including without limitation the
rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
 * copies of the Software, and to permit persons to whom the Software is
furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
 * THE SOFTWARE.
 */

namespace Pimple;

use Pimple\Exception\ExpectedInvokableException;
use Pimple\Exception\FrozenServiceException;
use Pimple\Exception\InvalidServiceIdentifierException;
use Pimple\Exception\UnknownIdentifierException;

/**
 * Container main class.
 *
 * @author Fabien Potencier
 */
class Container implements \ArrayAccess
{
    private $values = array();
    private $factories;
    private $protected;
    private $frozen = array();
    private $raw = array();
    private $keys = array();

    /**
     * Instantiates the container.
     *
     * Objects and parameters can be passed as argument to the constructor.
     *
     * @param array $values The parameters or objects
     */
    public function __construct(array $values = array())
    {
        $this->factories = new \SplObjectStorage();
        $this->protected = new \SplObjectStorage();

        foreach ($values as $key => $value) {
            $this->offsetSet($key, $value);
        }
    }

    /**
     * Sets a parameter or an object.
     *
     * Objects must be defined as Closures.
     *
     * Allowing any PHP callable leads to difficult to debug problems
     * as function names (strings) are callable (creating a function with
     * the same name as an existing parameter would break your container).
     *
     * @param string $id    The unique identifier for the parameter or
object
     * @param mixed  $value The value of the parameter or a closure to
define an object
     *
     * @throws FrozenServiceException Prevent override of a frozen service
     */
    public function offsetSet($id, $value)
    {
        if (isset($this->frozen[$id])) {
            throw new FrozenServiceException($id);
        }

        $this->values[$id] = $value;
        $this->keys[$id] = true;
    }

    /**
     * Gets a parameter or an object.
     *
     * @param string $id The unique identifier for the parameter or object
     *
     * @return mixed The value of the parameter or an object
     *
     * @throws UnknownIdentifierException If the identifier is not defined
     */
    public function offsetGet($id)
    {
        if (!isset($this->keys[$id])) {
            throw new UnknownIdentifierException($id);
        }

        if (
            isset($this->raw[$id])
            || !\is_object($this->values[$id])
            || isset($this->protected[$this->values[$id]])
            || !\method_exists($this->values[$id], '__invoke')
        ) {
            return $this->values[$id];
        }

        if (isset($this->factories[$this->values[$id]])) {
            return $this->values[$id]($this);
        }

        $raw = $this->values[$id];
        $val = $this->values[$id] = $raw($this);
        $this->raw[$id] = $raw;

        $this->frozen[$id] = true;

        return $val;
    }

    /**
     * Checks if a parameter or an object is set.
     *
     * @param string $id The unique identifier for the parameter or object
     *
     * @return bool
     */
    public function offsetExists($id)
    {
        return isset($this->keys[$id]);
    }

    /**
     * Unsets a parameter or an object.
     *
     * @param string $id The unique identifier for the parameter or object
     */
    public function offsetUnset($id)
    {
        if (isset($this->keys[$id])) {
            if (\is_object($this->values[$id])) {
                unset($this->factories[$this->values[$id]],
$this->protected[$this->values[$id]]);
            }

            unset($this->values[$id], $this->frozen[$id],
$this->raw[$id], $this->keys[$id]);
        }
    }

    /**
     * Marks a callable as being a factory service.
     *
     * @param callable $callable A service definition to be used as a
factory
     *
     * @return callable The passed callable
     *
     * @throws ExpectedInvokableException Service definition has to be a
closure or an invokable object
     */
    public function factory($callable)
    {
        if (!\method_exists($callable, '__invoke')) {
            throw new ExpectedInvokableException('Service definition
is not a Closure or invokable object.');
        }

        $this->factories->attach($callable);

        return $callable;
    }

    /**
     * Protects a callable from being interpreted as a service.
     *
     * This is useful when you want to store a callable as a parameter.
     *
     * @param callable $callable A callable to protect from being evaluated
     *
     * @return callable The passed callable
     *
     * @throws ExpectedInvokableException Service definition has to be a
closure or an invokable object
     */
    public function protect($callable)
    {
        if (!\method_exists($callable, '__invoke')) {
            throw new ExpectedInvokableException('Callable is not a
Closure or invokable object.');
        }

        $this->protected->attach($callable);

        return $callable;
    }

    /**
     * Gets a parameter or the closure defining an object.
     *
     * @param string $id The unique identifier for the parameter or object
     *
     * @return mixed The value of the parameter or the closure defining an
object
     *
     * @throws UnknownIdentifierException If the identifier is not defined
     */
    public function raw($id)
    {
        if (!isset($this->keys[$id])) {
            throw new UnknownIdentifierException($id);
        }

        if (isset($this->raw[$id])) {
            return $this->raw[$id];
        }

        return $this->values[$id];
    }

    /**
     * Extends an object definition.
     *
     * Useful when you want to extend an existing object definition,
     * without necessarily loading that object.
     *
     * @param string   $id       The unique identifier for the object
     * @param callable $callable A service definition to extend the
original
     *
     * @return callable The wrapped callable
     *
     * @throws UnknownIdentifierException        If the identifier is not
defined
     * @throws FrozenServiceException            If the service is frozen
     * @throws InvalidServiceIdentifierException If the identifier belongs
to a parameter
     * @throws ExpectedInvokableException        If the extension callable
is not a closure or an invokable object
     */
    public function extend($id, $callable)
    {
        if (!isset($this->keys[$id])) {
            throw new UnknownIdentifierException($id);
        }

        if (isset($this->frozen[$id])) {
            throw new FrozenServiceException($id);
        }

        if (!\is_object($this->values[$id]) ||
!\method_exists($this->values[$id], '__invoke')) {
            throw new InvalidServiceIdentifierException($id);
        }

        if (isset($this->protected[$this->values[$id]])) {
            @\trigger_error(\sprintf('How Pimple behaves when
extending protected closures will be fixed in Pimple 4. Are you sure
"%s" should be protected?', $id), \E_USER_DEPRECATED);
        }

        if (!\is_object($callable) || !\method_exists($callable,
'__invoke')) {
            throw new ExpectedInvokableException('Extension service
definition is not a Closure or invokable object.');
        }

        $factory = $this->values[$id];

        $extended = function ($c) use ($callable, $factory) {
            return $callable($factory($c), $c);
        };

        if (isset($this->factories[$factory])) {
            $this->factories->detach($factory);
            $this->factories->attach($extended);
        }

        return $this[$id] = $extended;
    }

    /**
     * Returns all defined value names.
     *
     * @return array An array of value names
     */
    public function keys()
    {
        return \array_keys($this->values);
    }

    /**
     * Registers a service provider.
     *
     * @param ServiceProviderInterface $provider A ServiceProviderInterface
instance
     * @param array                    $values   An array of values that
customizes the provider
     *
     * @return static
     */
    public function register(ServiceProviderInterface $provider, array
$values = array())
    {
        $provider->register($this);

        foreach ($values as $key => $value) {
            $this[$key] = $value;
        }

        return $this;
    }
}
vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php000064400000002662151166614550022574
0ustar00<?php

/*
 * This file is part of Pimple.
 *
 * Copyright (c) 2009 Fabien Potencier
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
copy
 * of this software and associated documentation files (the
"Software"), to deal
 * in the Software without restriction, including without limitation the
rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
 * copies of the Software, and to permit persons to whom the Software is
furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
 * THE SOFTWARE.
 */

namespace Pimple\Exception;

use Psr\Container\ContainerExceptionInterface;

/**
 * A closure or invokable object was expected.
 *
 * @author Pascal Luna <skalpa@zetareticuli.org>
 */
class ExpectedInvokableException extends \InvalidArgumentException
implements ContainerExceptionInterface
{
}
vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php000064400000003171151166614550021760
0ustar00<?php

/*
 * This file is part of Pimple.
 *
 * Copyright (c) 2009 Fabien Potencier
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
copy
 * of this software and associated documentation files (the
"Software"), to deal
 * in the Software without restriction, including without limitation the
rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
 * copies of the Software, and to permit persons to whom the Software is
furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
 * THE SOFTWARE.
 */

namespace Pimple\Exception;

use Psr\Container\ContainerExceptionInterface;

/**
 * An attempt to modify a frozen service was made.
 *
 * @author Pascal Luna <skalpa@zetareticuli.org>
 */
class FrozenServiceException extends \RuntimeException implements
ContainerExceptionInterface
{
    /**
     * @param string $id Identifier of the frozen service
     */
    public function __construct($id)
    {
        parent::__construct(\sprintf('Cannot override frozen service
"%s".', $id));
    }
}
vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php000064400000003262151166614550024107
0ustar00<?php

/*
 * This file is part of Pimple.
 *
 * Copyright (c) 2009 Fabien Potencier
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
copy
 * of this software and associated documentation files (the
"Software"), to deal
 * in the Software without restriction, including without limitation the
rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
 * copies of the Software, and to permit persons to whom the Software is
furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
 * THE SOFTWARE.
 */

namespace Pimple\Exception;

use Psr\Container\NotFoundExceptionInterface;

/**
 * An attempt to perform an operation that requires a service identifier
was made.
 *
 * @author Pascal Luna <skalpa@zetareticuli.org>
 */
class InvalidServiceIdentifierException extends \InvalidArgumentException
implements NotFoundExceptionInterface
{
    /**
     * @param string $id The invalid identifier
     */
    public function __construct($id)
    {
        parent::__construct(\sprintf('Identifier "%s" does
not contain an object definition.', $id));
    }
}
vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php000064400000003201151166614550022630
0ustar00<?php

/*
 * This file is part of Pimple.
 *
 * Copyright (c) 2009 Fabien Potencier
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
copy
 * of this software and associated documentation files (the
"Software"), to deal
 * in the Software without restriction, including without limitation the
rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
 * copies of the Software, and to permit persons to whom the Software is
furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
 * THE SOFTWARE.
 */

namespace Pimple\Exception;

use Psr\Container\NotFoundExceptionInterface;

/**
 * The identifier of a valid service or parameter was expected.
 *
 * @author Pascal Luna <skalpa@zetareticuli.org>
 */
class UnknownIdentifierException extends \InvalidArgumentException
implements NotFoundExceptionInterface
{
    /**
     * @param string $id The unknown identifier
     */
    public function __construct($id)
    {
        parent::__construct(\sprintf('Identifier "%s" is not
defined.', $id));
    }
}
vendor/pimple/pimple/src/Pimple/Psr11/Container.php000064400000003250151166614550016205
0ustar00<?php

/*
 * This file is part of Pimple.
 *
 * Copyright (c) 2009-2017 Fabien Potencier
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
copy
 * of this software and associated documentation files (the
"Software"), to deal
 * in the Software without restriction, including without limitation the
rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
 * copies of the Software, and to permit persons to whom the Software is
furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
 * THE SOFTWARE.
 */

namespace Pimple\Psr11;

use Pimple\Container as PimpleContainer;
use Psr\Container\ContainerInterface;

/**
 * PSR-11 compliant wrapper.
 *
 * @author Pascal Luna <skalpa@zetareticuli.org>
 */
final class Container implements ContainerInterface
{
    private $pimple;

    public function __construct(PimpleContainer $pimple)
    {
        $this->pimple = $pimple;
    }

    public function get($id)
    {
        return $this->pimple[$id];
    }

    public function has($id)
    {
        return isset($this->pimple[$id]);
    }
}
vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php000064400000004541151166614550017213
0ustar00<?php

/*
 * This file is part of Pimple.
 *
 * Copyright (c) 2009 Fabien Potencier
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
copy
 * of this software and associated documentation files (the
"Software"), to deal
 * in the Software without restriction, including without limitation the
rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
 * copies of the Software, and to permit persons to whom the Software is
furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
 * THE SOFTWARE.
 */

namespace Pimple\Psr11;

use Pimple\Container as PimpleContainer;
use Pimple\Exception\UnknownIdentifierException;
use Psr\Container\ContainerInterface;

/**
 * Pimple PSR-11 service locator.
 *
 * @author Pascal Luna <skalpa@zetareticuli.org>
 */
class ServiceLocator implements ContainerInterface
{
    private $container;
    private $aliases = array();

    /**
     * @param PimpleContainer $container The Container instance used to
locate services
     * @param array           $ids       Array of service ids that can be
located. String keys can be used to define aliases
     */
    public function __construct(PimpleContainer $container, array $ids)
    {
        $this->container = $container;

        foreach ($ids as $key => $id) {
            $this->aliases[\is_int($key) ? $id : $key] = $id;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function get($id)
    {
        if (!isset($this->aliases[$id])) {
            throw new UnknownIdentifierException($id);
        }

        return $this->container[$this->aliases[$id]];
    }

    /**
     * {@inheritdoc}
     */
    public function has($id)
    {
        return isset($this->aliases[$id]) &&
isset($this->container[$this->aliases[$id]]);
    }
}
vendor/pimple/pimple/src/Pimple/ServiceIterator.php000064400000003557151166614550016501
0ustar00<?php

/*
 * This file is part of Pimple.
 *
 * Copyright (c) 2009 Fabien Potencier
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
copy
 * of this software and associated documentation files (the
"Software"), to deal
 * in the Software without restriction, including without limitation the
rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
 * copies of the Software, and to permit persons to whom the Software is
furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
 * THE SOFTWARE.
 */

namespace Pimple;

/**
 * Lazy service iterator.
 *
 * @author Pascal Luna <skalpa@zetareticuli.org>
 */
final class ServiceIterator implements \Iterator
{
    private $container;
    private $ids;

    public function __construct(Container $container, array $ids)
    {
        $this->container = $container;
        $this->ids = $ids;
    }

    public function rewind()
    {
        \reset($this->ids);
    }

    public function current()
    {
        return $this->container[\current($this->ids)];
    }

    public function key()
    {
        return \current($this->ids);
    }

    public function next()
    {
        \next($this->ids);
    }

    public function valid()
    {
        return null !== \key($this->ids);
    }
}
vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php000064400000003123151166614550020310
0ustar00<?php

/*
 * This file is part of Pimple.
 *
 * Copyright (c) 2009 Fabien Potencier
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
copy
 * of this software and associated documentation files (the
"Software"), to deal
 * in the Software without restriction, including without limitation the
rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
 * copies of the Software, and to permit persons to whom the Software is
furnished
 * to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
 * THE SOFTWARE.
 */

namespace Pimple;

/**
 * Pimple service provider interface.
 *
 * @author  Fabien Potencier
 * @author  Dominik Zogg
 */
interface ServiceProviderInterface
{
    /**
     * Registers services on the given container.
     *
     * This method should only be used to configure services and
parameters.
     * It should not get services.
     *
     * @param Container $pimple A container instance
     */
    public function register(Container $pimple);
}
vendor/psr/container/composer.json000064400000001216151166614550013363
0ustar00{
    "name": "psr/container",
    "type": "library",
    "description": "Common Container Interface (PHP FIG
PSR-11)",
    "keywords": ["psr", "psr-11",
"container", "container-interop",
"container-interface"],
    "homepage": "https://github.com/php-fig/container",
    "license": "MIT",
    "authors": [
        {
            "name": "PHP-FIG",
            "homepage": "http://www.php-fig.org/"
        }
    ],
    "require": {
        "php": ">=5.3.0"
    },
    "autoload": {
        "psr-4": {
            "Psr\\Container\\": "src/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.0.x-dev"
        }
    }
}
vendor/psr/container/src/ContainerExceptionInterface.php000064400000000370151166614550017563
0ustar00<?php
/**
 * @license http://www.opensource.org/licenses/mit-license.php MIT (see the
LICENSE file)
 */

namespace Psr\Container;

/**
 * Base interface representing a generic exception in a container.
 */
interface ContainerExceptionInterface
{
}
vendor/psr/container/src/ContainerInterface.php000064400000002112151166614550015700
0ustar00<?php
/**
 * @license http://www.opensource.org/licenses/mit-license.php MIT (see the
LICENSE file)
 */

namespace Psr\Container;

/**
 * Describes the interface of a container that exposes methods to read its
entries.
 */
interface ContainerInterface
{
    /**
     * Finds an entry of the container by its identifier and returns it.
     *
     * @param string $id Identifier of the entry to look for.
     *
     * @throws NotFoundExceptionInterface  No entry was found for **this**
identifier.
     * @throws ContainerExceptionInterface Error while retrieving the
entry.
     *
     * @return mixed Entry.
     */
    public function get($id);

    /**
     * Returns true if the container can return an entry for the given
identifier.
     * Returns false otherwise.
     *
     * `has($id)` returning true does not mean that `get($id)` will not
throw an exception.
     * It does however mean that `get($id)` will not throw a
`NotFoundExceptionInterface`.
     *
     * @param string $id Identifier of the entry to look for.
     *
     * @return bool
     */
    public function has($id);
}
vendor/psr/container/src/NotFoundExceptionInterface.php000064400000000400151166614550017367
0ustar00<?php
/**
 * @license http://www.opensource.org/licenses/mit-license.php MIT (see the
LICENSE file)
 */

namespace Psr\Container;

/**
 * No entry was found in the container.
 */
interface NotFoundExceptionInterface extends ContainerExceptionInterface
{
}
vendor/psr/log/composer.json000064400000001061151166614550012160 0ustar00{
    "name": "psr/log",
    "description": "Common interface for logging
libraries",
    "keywords": ["psr", "psr-3",
"log"],
    "homepage": "https://github.com/php-fig/log",
    "license": "MIT",
    "authors": [
        {
            "name": "PHP-FIG",
            "homepage": "http://www.php-fig.org/"
        }
    ],
    "require": {
        "php": ">=5.3.0"
    },
    "autoload": {
        "psr-4": {
            "Psr\\Log\\": "Psr/Log/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.1.x-dev"
        }
    }
}
vendor/psr/log/Psr/Log/AbstractLogger.php000064400000006020151166614550014337
0ustar00<?php

namespace Psr\Log;

/**
 * This is a simple Logger implementation that other Loggers can inherit
from.
 *
 * It simply delegates all log-level-specific methods to the `log` method
to
 * reduce boilerplate code that a simple Logger that does the same thing
with
 * messages regardless of the error level has to implement.
 */
abstract class AbstractLogger implements LoggerInterface
{
    /**
     * System is unusable.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function emergency($message, array $context = array())
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function alert($message, array $context = array())
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function critical($message, array $context = array())
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }

    /**
     * Runtime errors that do not require immediate action but should
typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function error($message, array $context = array())
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable
things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function warning($message, array $context = array())
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function notice($message, array $context = array())
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function info($message, array $context = array())
    {
        $this->log(LogLevel::INFO, $message, $context);
    }

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function debug($message, array $context = array())
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }
}
vendor/psr/log/Psr/Log/InvalidArgumentException.php000064400000000140151166614550016401
0ustar00<?php

namespace Psr\Log;

class InvalidArgumentException extends \InvalidArgumentException
{
}
vendor/psr/log/Psr/Log/LoggerAwareInterface.php000064400000000451151166614550015456
0ustar00<?php

namespace Psr\Log;

/**
 * Describes a logger-aware instance.
 */
interface LoggerAwareInterface
{
    /**
     * Sets a logger instance on the object.
     *
     * @param LoggerInterface $logger
     *
     * @return void
     */
    public function setLogger(LoggerInterface $logger);
}
vendor/psr/log/Psr/Log/LoggerAwareTrait.php000064400000000615151166614550014643
0ustar00<?php

namespace Psr\Log;

/**
 * Basic Implementation of LoggerAwareInterface.
 */
trait LoggerAwareTrait
{
    /**
     * The logger instance.
     *
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * Sets a logger.
     *
     * @param LoggerInterface $logger
     */
    public function setLogger(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
}
vendor/psr/log/Psr/Log/LoggerInterface.php000064400000006052151166614550014501
0ustar00<?php

namespace Psr\Log;

/**
 * Describes a logger instance.
 *
 * The message MUST be a string or object implementing __toString().
 *
 * The message MAY contain placeholders in the form: {foo} where foo
 * will be replaced by the context data in key "foo".
 *
 * The context array can contain arbitrary data. The only assumption that
 * can be made by implementors is that if an Exception instance is given
 * to produce a stack trace, it MUST be in a key named
"exception".
 *
 * See
https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 * for the full interface specification.
 */
interface LoggerInterface
{
    /**
     * System is unusable.
     *
     * @param string  $message
     * @param mixed[] $context
     *
     * @return void
     */
    public function emergency($message, array $context = array());

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string  $message
     * @param mixed[] $context
     *
     * @return void
     */
    public function alert($message, array $context = array());

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string  $message
     * @param mixed[] $context
     *
     * @return void
     */
    public function critical($message, array $context = array());

    /**
     * Runtime errors that do not require immediate action but should
typically
     * be logged and monitored.
     *
     * @param string  $message
     * @param mixed[] $context
     *
     * @return void
     */
    public function error($message, array $context = array());

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable
things
     * that are not necessarily wrong.
     *
     * @param string  $message
     * @param mixed[] $context
     *
     * @return void
     */
    public function warning($message, array $context = array());

    /**
     * Normal but significant events.
     *
     * @param string  $message
     * @param mixed[] $context
     *
     * @return void
     */
    public function notice($message, array $context = array());

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string  $message
     * @param mixed[] $context
     *
     * @return void
     */
    public function info($message, array $context = array());

    /**
     * Detailed debug information.
     *
     * @param string  $message
     * @param mixed[] $context
     *
     * @return void
     */
    public function debug($message, array $context = array());

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed   $level
     * @param string  $message
     * @param mixed[] $context
     *
     * @return void
     *
     * @throws \Psr\Log\InvalidArgumentException
     */
    public function log($level, $message, array $context = array());
}
vendor/psr/log/Psr/Log/LoggerTrait.php000064400000006527151166614550013673
0ustar00<?php

namespace Psr\Log;

/**
 * This is a simple Logger trait that classes unable to extend
AbstractLogger
 * (because they extend another class, etc) can include.
 *
 * It simply delegates all log-level-specific methods to the `log` method
to
 * reduce boilerplate code that a simple Logger that does the same thing
with
 * messages regardless of the error level has to implement.
 */
trait LoggerTrait
{
    /**
     * System is unusable.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function emergency($message, array $context = array())
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function alert($message, array $context = array())
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function critical($message, array $context = array())
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }

    /**
     * Runtime errors that do not require immediate action but should
typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function error($message, array $context = array())
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable
things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function warning($message, array $context = array())
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function notice($message, array $context = array())
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function info($message, array $context = array())
    {
        $this->log(LogLevel::INFO, $message, $context);
    }

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array  $context
     *
     * @return void
     */
    public function debug($message, array $context = array())
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed  $level
     * @param string $message
     * @param array  $context
     *
     * @return void
     *
     * @throws \Psr\Log\InvalidArgumentException
     */
    abstract public function log($level, $message, array $context =
array());
}
vendor/psr/log/Psr/Log/LogLevel.php000064400000000520151166614550013144
0ustar00<?php

namespace Psr\Log;

/**
 * Describes log levels.
 */
class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT     = 'alert';
    const CRITICAL  = 'critical';
    const ERROR     = 'error';
    const WARNING   = 'warning';
    const NOTICE    = 'notice';
    const INFO      = 'info';
    const DEBUG     = 'debug';
}
vendor/psr/log/Psr/Log/NullLogger.php000064400000001303151166614550013505
0ustar00<?php

namespace Psr\Log;

/**
 * This Logger can be used to avoid conditional log calls.
 *
 * Logging should always be optional, and if no logger is provided to your
 * library creating a NullLogger instance to have something to throw logs
at
 * is a good way to avoid littering your code with `if ($this->logger) {
}`
 * blocks.
 */
class NullLogger extends AbstractLogger
{
    /**
     * Logs with an arbitrary level.
     *
     * @param mixed  $level
     * @param string $message
     * @param array  $context
     *
     * @return void
     *
     * @throws \Psr\Log\InvalidArgumentException
     */
    public function log($level, $message, array $context = array())
    {
        // noop
    }
}
vendor/psr/log/Psr/Log/Test/DummyTest.php000064400000000373151166614550014313
0ustar00<?php

namespace Psr\Log\Test;

/**
 * This class is internal and does not follow the BC promise.
 *
 * Do NOT use this class in any way.
 *
 * @internal
 */
class DummyTest
{
    public function __toString()
    {
        return 'DummyTest';
    }
}
vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php000064400000011051151166614550016253
0ustar00<?php

namespace Psr\Log\Test;

use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use PHPUnit\Framework\TestCase;

/**
 * Provides a base test class for ensuring compliance with the
LoggerInterface.
 *
 * Implementors can extend the class and implement abstract methods to run
this
 * as part of their test suite.
 */
abstract class LoggerInterfaceTest extends TestCase
{
    /**
     * @return LoggerInterface
     */
    abstract public function getLogger();

    /**
     * This must return the log messages in order.
     *
     * The simple formatting of the messages is: "<LOG LEVEL>
<MESSAGE>".
     *
     * Example ->error('Foo') would yield "error
Foo".
     *
     * @return string[]
     */
    abstract public function getLogs();

    public function testImplements()
    {
        $this->assertInstanceOf('Psr\Log\LoggerInterface',
$this->getLogger());
    }

    /**
     * @dataProvider provideLevelsAndMessages
     */
    public function testLogsAtAllLevels($level, $message)
    {
        $logger = $this->getLogger();
        $logger->{$level}($message, array('user' =>
'Bob'));
        $logger->log($level, $message, array('user' =>
'Bob'));

        $expected = array(
            $level.' message of level '.$level.' with
context: Bob',
            $level.' message of level '.$level.' with
context: Bob',
        );
        $this->assertEquals($expected, $this->getLogs());
    }

    public function provideLevelsAndMessages()
    {
        return array(
            LogLevel::EMERGENCY => array(LogLevel::EMERGENCY,
'message of level emergency with context: {user}'),
            LogLevel::ALERT => array(LogLevel::ALERT, 'message of
level alert with context: {user}'),
            LogLevel::CRITICAL => array(LogLevel::CRITICAL,
'message of level critical with context: {user}'),
            LogLevel::ERROR => array(LogLevel::ERROR, 'message of
level error with context: {user}'),
            LogLevel::WARNING => array(LogLevel::WARNING, 'message
of level warning with context: {user}'),
            LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of
level notice with context: {user}'),
            LogLevel::INFO => array(LogLevel::INFO, 'message of
level info with context: {user}'),
            LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of
level debug with context: {user}'),
        );
    }

    /**
     * @expectedException \Psr\Log\InvalidArgumentException
     */
    public function testThrowsOnInvalidLevel()
    {
        $logger = $this->getLogger();
        $logger->log('invalid level', 'Foo');
    }

    public function testContextReplacement()
    {
        $logger = $this->getLogger();
        $logger->info('{Message {nothing} {user} {foo.bar}
a}', array('user' => 'Bob', 'foo.bar'
=> 'Bar'));

        $expected = array('info {Message {nothing} Bob Bar a}');
        $this->assertEquals($expected, $this->getLogs());
    }

    public function testObjectCastToString()
    {
        if (method_exists($this, 'createPartialMock')) {
            $dummy =
$this->createPartialMock('Psr\Log\Test\DummyTest',
array('__toString'));
        } else {
            $dummy = $this->getMock('Psr\Log\Test\DummyTest',
array('__toString'));
        }
        $dummy->expects($this->once())
            ->method('__toString')
            ->will($this->returnValue('DUMMY'));

        $this->getLogger()->warning($dummy);

        $expected = array('warning DUMMY');
        $this->assertEquals($expected, $this->getLogs());
    }

    public function testContextCanContainAnything()
    {
        $closed = fopen('php://memory', 'r');
        fclose($closed);

        $context = array(
            'bool' => true,
            'null' => null,
            'string' => 'Foo',
            'int' => 0,
            'float' => 0.5,
            'nested' => array('with object' =>
new DummyTest),
            'object' => new \DateTime,
            'resource' => fopen('php://memory',
'r'),
            'closed' => $closed,
        );

        $this->getLogger()->warning('Crazy context data',
$context);

        $expected = array('warning Crazy context data');
        $this->assertEquals($expected, $this->getLogs());
    }

    public function testContextExceptionKeyCanBeExceptionOrOtherValues()
    {
        $logger = $this->getLogger();
        $logger->warning('Random message',
array('exception' => 'oops'));
        $logger->critical('Uncaught Exception!',
array('exception' => new \LogicException('Fail')));

        $expected = array(
            'warning Random message',
            'critical Uncaught Exception!'
        );
        $this->assertEquals($expected, $this->getLogs());
    }
}
vendor/psr/log/Psr/Log/Test/TestLogger.php000064400000010657151166614550014445
0ustar00<?php

namespace Psr\Log\Test;

use Psr\Log\AbstractLogger;

/**
 * Used for testing purposes.
 *
 * It records all records and gives you access to them for verification.
 *
 * @method bool hasEmergency($record)
 * @method bool hasAlert($record)
 * @method bool hasCritical($record)
 * @method bool hasError($record)
 * @method bool hasWarning($record)
 * @method bool hasNotice($record)
 * @method bool hasInfo($record)
 * @method bool hasDebug($record)
 *
 * @method bool hasEmergencyRecords()
 * @method bool hasAlertRecords()
 * @method bool hasCriticalRecords()
 * @method bool hasErrorRecords()
 * @method bool hasWarningRecords()
 * @method bool hasNoticeRecords()
 * @method bool hasInfoRecords()
 * @method bool hasDebugRecords()
 *
 * @method bool hasEmergencyThatContains($message)
 * @method bool hasAlertThatContains($message)
 * @method bool hasCriticalThatContains($message)
 * @method bool hasErrorThatContains($message)
 * @method bool hasWarningThatContains($message)
 * @method bool hasNoticeThatContains($message)
 * @method bool hasInfoThatContains($message)
 * @method bool hasDebugThatContains($message)
 *
 * @method bool hasEmergencyThatMatches($message)
 * @method bool hasAlertThatMatches($message)
 * @method bool hasCriticalThatMatches($message)
 * @method bool hasErrorThatMatches($message)
 * @method bool hasWarningThatMatches($message)
 * @method bool hasNoticeThatMatches($message)
 * @method bool hasInfoThatMatches($message)
 * @method bool hasDebugThatMatches($message)
 *
 * @method bool hasEmergencyThatPasses($message)
 * @method bool hasAlertThatPasses($message)
 * @method bool hasCriticalThatPasses($message)
 * @method bool hasErrorThatPasses($message)
 * @method bool hasWarningThatPasses($message)
 * @method bool hasNoticeThatPasses($message)
 * @method bool hasInfoThatPasses($message)
 * @method bool hasDebugThatPasses($message)
 */
class TestLogger extends AbstractLogger
{
    /**
     * @var array
     */
    public $records = [];

    public $recordsByLevel = [];

    /**
     * @inheritdoc
     */
    public function log($level, $message, array $context = [])
    {
        $record = [
            'level' => $level,
            'message' => $message,
            'context' => $context,
        ];

        $this->recordsByLevel[$record['level']][] = $record;
        $this->records[] = $record;
    }

    public function hasRecords($level)
    {
        return isset($this->recordsByLevel[$level]);
    }

    public function hasRecord($record, $level)
    {
        if (is_string($record)) {
            $record = ['message' => $record];
        }
        return $this->hasRecordThatPasses(function ($rec) use ($record)
{
            if ($rec['message'] !== $record['message'])
{
                return false;
            }
            if (isset($record['context']) &&
$rec['context'] !== $record['context']) {
                return false;
            }
            return true;
        }, $level);
    }

    public function hasRecordThatContains($message, $level)
    {
        return $this->hasRecordThatPasses(function ($rec) use ($message)
{
            return strpos($rec['message'], $message) !== false;
        }, $level);
    }

    public function hasRecordThatMatches($regex, $level)
    {
        return $this->hasRecordThatPasses(function ($rec) use ($regex) {
            return preg_match($regex, $rec['message']) > 0;
        }, $level);
    }

    public function hasRecordThatPasses(callable $predicate, $level)
    {
        if (!isset($this->recordsByLevel[$level])) {
            return false;
        }
        foreach ($this->recordsByLevel[$level] as $i => $rec) {
            if (call_user_func($predicate, $rec, $i)) {
                return true;
            }
        }
        return false;
    }

    public function __call($method, $args)
    {
        if
(preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/',
$method, $matches) > 0) {
            $genericMethod = $matches[1] . ('Records' !==
$matches[3] ? 'Record' : '') . $matches[3];
            $level = strtolower($matches[2]);
            if (method_exists($this, $genericMethod)) {
                $args[] = $level;
                return call_user_func_array([$this, $genericMethod],
$args);
            }
        }
        throw new \BadMethodCallException('Call to undefined method
' . get_class($this) . '::' . $method . '()');
    }

    public function reset()
    {
        $this->records = [];
        $this->recordsByLevel = [];
    }
}
vendor/rockettheme/toolbox/ArrayTraits/src/ArrayAccess.php000064400000003070151166614550020020
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;

/**
 * Implements ArrayAccess interface.
 *
 * @package RocketTheme\Toolbox\ArrayTraits
 * @author RocketTheme
 * @license MIT
 *
 * @property array $items
 */
trait ArrayAccess
{
    /**
     * Whether or not an offset exists.
     *
     * @param mixed $offset  An offset to check for.
     * @return bool          Returns TRUE on success or FALSE on failure.
     */
    public function offsetExists($offset)
    {
        return isset($this->items[$offset]);
    }

    /**
     * Returns the value at specified offset.
     *
     * @param mixed $offset  The offset to retrieve.
     * @return mixed         Can return all value types.
     */
    public function offsetGet($offset)
    {
        return isset($this->items[$offset]) ? $this->items[$offset] :
null;
    }

    /**
     * Assigns a value to the specified offset.
     *
     * @param mixed $offset  The offset to assign the value to.
     * @param mixed $value   The value to set.
     */
    public function offsetSet($offset, $value)
    {
        if (null === $offset) {
            $this->items[] = $value;
        } else {
            $this->items[$offset] = $value;
        }
    }

    /**
     * Unsets an offset.
     *
     * @param mixed $offset  The offset to unset.
     */
    public function offsetUnset($offset)
    {
        // Hack to make Iterator trait work together with unset.
        if (isset($this->iteratorUnset) && $offset ==
key($this->items)) {
            $this->iteratorUnset = true;
        }

        unset($this->items[$offset]);
    }
}
vendor/rockettheme/toolbox/ArrayTraits/src/ArrayAccessWithGetters.php000064400000002231151166614550022210
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;

/**
 * Implements getters and setters.
 *
 * @package RocketTheme\Toolbox\ArrayTraits
 * @author RocketTheme
 * @license MIT
 */
trait ArrayAccessWithGetters
{
    use ArrayAccess;

    /**
     * Magic setter method
     *
     * @param mixed $offset Asset name value
     * @param mixed $value  Asset value
     */
    public function __set($offset, $value)
    {
        $this->offsetSet($offset, $value);
    }

    /**
     * Magic getter method
     *
     * @param  mixed $offset Asset name value
     * @return mixed         Asset value
     */
    public function __get($offset)
    {
       return $this->offsetGet($offset);
    }

    /**
     * Magic method to determine if the attribute is set
     *
     * @param  mixed   $offset Asset name value
     * @return boolean         True if the value is set
     */
    public function __isset($offset)
    {
        return $this->offsetExists($offset);
    }

    /**
     * Magic method to unset the attribute
     *
     * @param mixed $offset The name value to unset
     */
    public function __unset($offset)
    {
        $this->offsetUnset($offset);
    }
}
vendor/rockettheme/toolbox/ArrayTraits/src/Constructor.php000064400000000723151166614550020147
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;

/**
 * Implements Constructor for setting items.
 *
 * @package RocketTheme\Toolbox\ArrayTraits
 * @author RocketTheme
 * @license MIT
 *
 * @property array $items
 */
trait Constructor
{
    /**
     * Constructor to initialize array.
     *
     * @param  array  $items  Initial items inside the iterator.
     */
    public function __construct(array $items = array())
    {
        $this->items = $items;
    }
}
vendor/rockettheme/toolbox/ArrayTraits/src/Countable.php000064400000000603151166614550017533
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;

/**
 * Implements \Countable interface.
 *
 * @package RocketTheme\Toolbox\ArrayTraits
 * @author RocketTheme
 * @license MIT
 *
 * @property array $items
 */
trait Countable
{
    /**
     * Implements Countable interface.
     *
     * @return int
     */
    public function count()
    {
        return \count($this->items);
    }
}
vendor/rockettheme/toolbox/ArrayTraits/src/Export.php000064400000002106151166614550017100
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;

use Symfony\Component\Yaml\Exception\DumpException;
use Symfony\Component\Yaml\Yaml;

/**
 * Implements ExportInterface.
 *
 * @package RocketTheme\Toolbox\ArrayTraits
 * @author RocketTheme
 * @license MIT
 *
 * @property array $items
 */
trait Export
{
    /**
     * Convert object into an array.
     *
     * @return array
     */
    public function toArray()
    {
        return $this->items;
    }

    /**
     * Convert object into YAML string.
     *
     * @param  int $inline  The level where you switch to inline YAML.
     * @param  int $indent  The amount of spaces to use for indentation of
nested nodes.
     *
     * @return string A YAML string representing the object.
     * @throws DumpException
     */
    public function toYaml($inline = 3, $indent = 2)
    {
        return Yaml::dump($this->toArray(), $inline, $indent, true,
false);
    }

    /**
     * Convert object into JSON string.
     *
     * @return string
     */
    public function toJson()
    {
        return json_encode($this->toArray());
    }
}
vendor/rockettheme/toolbox/ArrayTraits/src/ExportInterface.php000064400000001157151166614550020726
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;

/**
 * Defines Export interface.
 *
 * @package RocketTheme\Toolbox\ArrayTraits
 * @author RocketTheme
 * @license MIT
 */
interface ExportInterface
{
    /**
     * Convert object into an array.
     *
     * @return array
     */
    public function toArray();

    /**
     * Convert object into YAML string.
     *
     * @param int $inline
     * @param int $indent
     * @return string
     */
    public function toYaml($inline = 3, $indent = 2);

    /**
     * Convert object into JSON string.
     *
     * @return string
     */
    public function toJson();
}
vendor/rockettheme/toolbox/ArrayTraits/src/Iterator.php000064400000003133151166614550017411
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;

/**
 * Implements \Iterator interface.
 *
 * @package RocketTheme\Toolbox\ArrayTraits
 * @author RocketTheme
 * @license MIT
 *
 * @property array $items
 */
trait Iterator
{
    /**
     * Hack to make Iterator work together with unset().
     *
     * @var bool
     */
    private $iteratorUnset = false;

    /**
     * Returns the current element.
     *
     * @return mixed  Can return any type.
     */
    public function current()
    {
        return current($this->items);
    }

    /**
     * Returns the key of the current element.
     *
     * @return mixed  Returns scalar on success, or NULL on failure.
     */
    public function key()
    {
        return key($this->items);
    }

    /**
     * Moves the current position to the next element.
     *
     * @return void
     */
    public function next()
    {
        if ($this->iteratorUnset) {
            // If current item was unset, position is already in the next
element (do nothing).
            $this->iteratorUnset = false;
        } else {
            next($this->items);
        }
    }

    /**
     * Rewinds back to the first element of the Iterator.
     *
     * @return void
     */
    public function rewind()
    {
        $this->iteratorUnset = false;
        reset($this->items);
    }

    /**
     * This method is called after Iterator::rewind() and Iterator::next()
to check if the current position is valid.
     *
     * @return bool  Returns TRUE on success or FALSE on failure.
     */
    public function valid()
    {
        return key($this->items) !== null;
    }
}
vendor/rockettheme/toolbox/ArrayTraits/src/NestedArrayAccess.php000064400000012463151166614550021171
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;

/**
 * Implements nested ArrayAccess interface with dot notation.
 *
 * @package RocketTheme\Toolbox\ArrayTraits
 * @author RocketTheme
 * @license MIT
 *
 * @property array $items
 */
trait NestedArrayAccess
{
    protected $nestedSeparator = '.';

    /**
     * Get value by using dot notation for nested arrays/objects.
     *
     * @example $value =
$this->get('this.is.my.nested.variable');
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return mixed  Value.
     */
    public function get($name, $default = null, $separator = null)
    {
        $path = explode($separator ?: $this->nestedSeparator, $name);
        $current = $this->items;
        foreach ($path as $field) {
            if (\is_object($current) &&
isset($current->{$field})) {
                $current = $current->{$field};
            } elseif (\is_array($current) &&
isset($current[$field])) {
                $current = $current[$field];
            } else {
                return $default;
            }
        }

        return $current;
    }

    /**
     * Set value by using dot notation for nested arrays/objects.
     *
     * @example $data->set('this.is.my.nested.variable',
$value);
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $value      New value.
     * @param string  $separator  Separator, defaults to '.'
     * @return $this
     */
    public function set($name, $value, $separator = null)
    {
        $path = explode($separator ?: $this->nestedSeparator, $name);
        $current = &$this->items;
        foreach ($path as $field) {
            if (\is_object($current)) {
                // Handle objects.
                if (!isset($current->{$field})) {
                    $current->{$field} = [];
                }
                $current = &$current->{$field};
            } else {
                // Handle arrays and scalars.
                if (!\is_array($current)) {
                    $current = [$field => []];
                } elseif (!isset($current[$field])) {
                    $current[$field] = [];
                }
                $current = &$current[$field];
            }
        }

        $current = $value;

        return $this;
    }

    /**
     * Unset value by using dot notation for nested arrays/objects.
     *
     * @example $data->undef('this.is.my.nested.variable');
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param string  $separator  Separator, defaults to '.'
     * @return $this
     */
    public function undef($name, $separator = null)
    {
        if ($name === '') {
            $this->items = [];

            return $this;
        }

        $path = explode($separator ?: $this->nestedSeparator, $name);
        $var = array_pop($path);
        $current = &$this->items;

        foreach ($path as $field) {
            if (\is_object($current)) {
                // Handle objects.
                if (!isset($current->{$field})) {
                    return $this;
                }
                $current = &$current->{$field};
            } else {
                // Handle arrays and scalars.
                if (!\is_array($current) || !isset($current[$field])) {
                    return $this;
                }
                $current = &$current[$field];
            }
        }

        unset($current[$var]);

        return $this;
    }

    /**
     * Set default value by using dot notation for nested arrays/objects.
     *
     * @example $data->def('this.is.my.nested.variable',
'default');
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return $this
     */
    public function def($name, $default = null, $separator = null)
    {
        $this->set($name, $this->get($name, $default, $separator),
$separator);

        return $this;
    }

    /**
     * Whether or not an offset exists.
     *
     * @param mixed $offset  An offset to check for.
     * @return bool          Returns TRUE on success or FALSE on failure.
     */
    public function offsetExists($offset)
    {
        return $this->get($offset) !== null;
    }

    /**
     * Returns the value at specified offset.
     *
     * @param mixed $offset  The offset to retrieve.
     * @return mixed         Can return all value types.
     */
    public function offsetGet($offset)
    {
        return $this->get($offset);
    }

    /**
     * Assigns a value to the specified offset.
     *
     * @param mixed $offset  The offset to assign the value to.
     * @param mixed $value   The value to set.
     */
    public function offsetSet($offset, $value)
    {
        if (null === $offset) {
            $this->items[] = $value;
        } else {
            $this->set($offset, $value);
        }
    }

    /**
     * Unsets variable at specified offset.
     *
     * @param $offset
     */
    public function offsetUnset($offset)
    {
        if (null === $offset) {
            $this->items[] = [];
        } else {
            $this->undef($offset);
        }
    }
}
vendor/rockettheme/toolbox/ArrayTraits/src/NestedArrayAccessWithGetters.php000064400000002245151166614550023360
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;

/**
 * Implements getters and setters.
 *
 * @package RocketTheme\Toolbox\ArrayTraits
 * @author RocketTheme
 * @license MIT
 */
trait NestedArrayAccessWithGetters
{
    use NestedArrayAccess;

    /**
     * Magic setter method
     *
     * @param mixed $offset Asset name value
     * @param mixed $value  Asset value
     */
    public function __set($offset, $value)
    {
        $this->offsetSet($offset, $value);
    }

    /**
     * Magic getter method
     *
     * @param  mixed $offset Asset name value
     * @return mixed         Asset value
     */
    public function __get($offset)
    {
       return $this->offsetGet($offset);
    }

    /**
     * Magic method to determine if the attribute is set
     *
     * @param  mixed   $offset Asset name value
     * @return boolean         True if the value is set
     */
    public function __isset($offset)
    {
        return $this->offsetExists($offset);
    }

    /**
     * Magic method to unset the attribute
     *
     * @param mixed $offset The name value to unset
     */
    public function __unset($offset)
    {
        $this->offsetUnset($offset);
    }
}
vendor/rockettheme/toolbox/ArrayTraits/src/Serializable.php000064400000001323151166614550020225
0ustar00<?php
namespace RocketTheme\Toolbox\ArrayTraits;

/**
 * Implements \Serializable interface.
 *
 * @package RocketTheme\Toolbox\ArrayTraits
 * @author RocketTheme
 * @license MIT
 *
 * @property array $items
 */
trait Serializable
{
    /**
     * Returns string representation of the object.
     *
     * @return string  Returns the string representation of the object.
     */
    public function serialize()
    {
        return serialize($this->items);
    }

    /**
     * Called during unserialization of the object.
     *
     * @param string $serialized  The string representation of the object.
     */
    public function unserialize($serialized)
    {
        $this->items = unserialize($serialized);
    }
}
vendor/rockettheme/toolbox/Blueprints/src/BlueprintForm.php000064400000041067151166614550020302
0ustar00<?php
namespace RocketTheme\Toolbox\Blueprints;

use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RuntimeException;

/**
 * The Config class contains configuration information.
 *
 * @author RocketTheme
 */
abstract class BlueprintForm implements \ArrayAccess, ExportInterface
{
    use NestedArrayAccessWithGetters;
    use Export;

    /** @var array */
    protected $items;

    /** @var string */
    protected $filename;

    /** @var string */
    protected $context;

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

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

    /**
     * Load file and return its contents.
     *
     * @param string $filename
     * @return array
     */
    abstract protected function loadFile($filename);

    /**
     * Get list of blueprint form files (file and its parents for
overrides).
     *
     * @param string|array $path
     * @param string $context
     * @return array
     */
    abstract protected function getFiles($path, $context = null);

    /**
     * Constructor.
     *
     * @param string|array $filename
     * @param array $items
     */
    public function __construct($filename = null, array $items = [])
    {
        $this->nestedSeparator = '/';
        $this->filename = $filename;
        $this->items = $items;
    }

    /**
     * Set filename for the blueprint. Can also be array of files for
parent lookup.
     *
     * @param string|array $filename
     * @return $this
     */
    public function setFilename($filename)
    {
        $this->filename = $filename;

        return $this;
    }

    /**
     * Get the filename of the blueprint.
     *
     * @return array|null|string
     */
    public function getFilename()
    {
        return $this->filename;
    }

    /**
     * Set context for import@ and extend@.
     *
     * @param $context
     * @return $this
     */
    public function setContext($context)
    {
        $this->context = $context;

        return $this;
    }

    /**
     * Set custom overrides for import@ and extend@.
     *
     * @param array $overrides
     * @return $this
     */
    public function setOverrides($overrides)
    {
        $this->overrides = $overrides;

        return $this;
    }

    /**
     * Load blueprint.
     *
     * @return $this
     */
    public function load($extends = null)
    {
        // Only load and extend blueprint if it has not yet been loaded.
        if (empty($this->items) && $this->filename) {
            // Get list of files.
            $files = $this->getFiles($this->filename);

            // Load and extend blueprints.
            $data = $this->doLoad($files, $extends);

            $this->items = (array) array_shift($data);

            foreach ($data as $content) {
                $this->extend($content, true);
            }
        }

        // Import blueprints.
        $this->deepInit($this->items);

        return $this;
    }

    /**
     * Initialize blueprints with its dynamic fields.
     *
     * @return $this
     */
    public function init()
    {
        foreach ($this->dynamic as $key => $data) {
            // Locate field.
            $path = explode('/', $key);
            $current = &$this->items;

            foreach ($path as $field) {
                if (\is_object($current)) {
                    // Handle objects.
                    if (!isset($current->{$field})) {
                        $current->{$field} = [];
                    }

                    $current = &$current->{$field};
                } else {
                    // Handle arrays and scalars.
                    if (!\is_array($current)) {
                        $current = [$field => []];
                    } elseif (!isset($current[$field])) {
                        $current[$field] = [];
                    }

                    $current = &$current[$field];
                }
            }

            // Set dynamic property.
            foreach ($data as $property => $call) {
                $action = 'dynamic' .
ucfirst($call['action']);

                if (method_exists($this, $action)) {
                    $this->{$action}($current, $property, $call);
                }
            }
        }

        return $this;
    }


    /**
     * Get form.
     *
     * @return array
     */
    public function form()
    {
        return (array) $this->get('form');
    }

    /**
     * Get form fields.
     *
     * @return array
     */
    public function fields()
    {
        $fields = $this->get('form/fields');

        if ($fields === null) {
            $field = $this->get('form/field');
            $fields = $field !== null ? ['' => (array) $field]
: $fields;
        }

        return (array) $fields;
    }

    /**
     * Extend blueprint with another blueprint.
     *
     * @param BlueprintForm|array $extends
     * @param bool $append
     * @return $this
     */
    public function extend($extends, $append = false)
    {
        if ($extends instanceof self) {
            $extends = $extends->toArray();
        }

        if ($append) {
            $a = $this->items;
            $b = $extends;
        } else {
            $a = $extends;
            $b = $this->items;
        }

        $this->items = $this->deepMerge($a, $b);

        return $this;
    }

    /**
     * @param string $name
     * @param mixed $value
     * @param string $separator
     * @param bool $append
     * @return $this
     */
    public function embed($name, $value, $separator = '/',
$append = false)
    {
        $oldValue = $this->get($name, null, $separator);

        if (\is_array($oldValue) && \is_array($value)) {
            if ($append) {
                $a = $oldValue;
                $b = $value;
            } else {
                $a = $value;
                $b = $oldValue;
            }

            $value = $this->deepMerge($a, $b);
        }

        $this->set($name, $value, $separator);

        return $this;
    }

    /**
     * Get blueprints by using slash notation for nested arrays/objects.
     *
     * @example $value =
$this->resolve('this/is/my/nested/variable');
     * returns ['this/is/my', 'nested/variable']
     *
     * @param array  $path
     * @param string  $separator
     * @return array
     */
    public function resolve(array $path, $separator = '/')
    {
        $fields = false;
        $parts = [];
        $current = $this['form/fields'];
        $result = [null, null, null];

        while (($field = current($path)) !== null) {
            if (!$fields && isset($current['fields'])) {
                if (!empty($current['array'])) {
                    $result = [$current, $parts, $path ?
implode($separator, $path) : null];
                    // Skip item offset.
                    $parts[] = array_shift($path);
                }

                $current = $current['fields'];
                $fields = true;

            } elseif (isset($current[$field])) {
                $parts[] = array_shift($path);
                $current = $current[$field];
                $fields = false;

            } elseif (isset($current[$index = '.' . $field])) {
                $parts[] = array_shift($path);
                $current = $current[$index];
                $fields = false;

            } else {
                break;
            }
        }

        return $result;
    }

    /**
     * Deep merge two arrays together.
     *
     * @param array $a
     * @param array $b
     * @return array
     */
    protected function deepMerge(array $a, array $b)
    {
        $bref_stack = [&$a];
        $head_stack = [$b];

        do {
            end($bref_stack);
            $bref = &$bref_stack[key($bref_stack)];
            $head = array_pop($head_stack);
            unset($bref_stack[key($bref_stack)]);

            foreach ($head as $key => $value) {
                if (strpos($key, '@') !== false) {
                    // Remove @ from the start and the end. Key syntax
`import@2` is supported to allow multiple operations of the same type.
                    $list = explode('-',
preg_replace('/^(@*)?([^@]+)(@\d*)?$/', '\2', $key),
2);
                    $action = array_shift($list);
                    $property = array_shift($list);

                    switch ($action) {
                        case 'unset':
                        case 'replace':
                            if (!$property) {
                                $bref = ['unset@' => true];
                            } else {
                                unset($bref[$property]);
                            }
                            continue 2;
                    }
                }

                if (isset($key, $bref[$key]) &&
\is_array($bref[$key]) && \is_array($head[$key])) {
                    $bref_stack[] = &$bref[$key];
                    $head_stack[] = $head[$key];
                } else {
                    $bref = array_merge($bref, [$key => $head[$key]]);
                }
            }
        } while (\count($head_stack));

        return $a;
    }

    /**
     * @param array $items
     * @param array $path
     * @return string
     */
    protected function deepInit(array &$items, $path = [])
    {
        $ordering = '';
        $order = [];
        $field = end($path) === 'fields';

        foreach ($items as $key => &$item) {
            // Set name for nested field.
            if ($field && isset($item['type'])) {
                $item['name'] = $key;
            }

            // Handle special instructions in the form.
            if (strpos($key, '@') !== false) {
                // Remove @ from the start and the end. Key syntax
`import@2` is supported to allow multiple operations of the same type.
                $list = explode('-',
preg_replace('/^(@*)?([^@]+)(@\d*)?$/', '\2', $key),
2);
                $action = array_shift($list);
                $property = array_shift($list);

                switch ($action) {
                    case 'unset':
                        unset($items[$key]);
                        if (empty($items)) {
                            return null;
                        }
                        break;
                    case 'import':
                        unset($items[$key]);
                        $this->doImport($item, $path);
                        break;
                    case 'ordering':
                        $ordering = $item;
                        unset($items[$key]);
                        break;
                    default:
                        $this->dynamic[implode('/',
$path)][$property] = ['action' => $action, 'params'
=> $item];
                }

            } elseif (\is_array($item)) {
                // Recursively initialize form.
                $newPath = array_merge($path, [$key]);

                $location = $this->deepInit($item, $newPath);
                if ($location) {
                    $order[$key] = $location;
                } elseif ($location === null) {
                    unset($items[$key]);
                }
            }
        }
        unset($item);

        if ($order) {
            // Reorder fields if needed.
            $items = $this->doReorder($items, $order);
        }

        return $ordering;
    }

    /**
     * @param array|string $value
     * @return array|null
     */
    protected function loadImport($value)
    {
        $type = !\is_string($value) ? (!isset($value['type']) ?
null : $value['type']) : $value;
        $field = 'form';

        if ($type && strpos($type, ':') !== false) {
            list ($type, $field) = explode(':', $type, 2);
        }

        if (!$type && !$field) {
            return null;
        }

        if ($type) {
            $files = $this->getFiles($type,
isset($value['context']) ? $value['context'] : null);

            if (!$files) {
                return null;
            }

            /** @var BlueprintForm $blueprint */
            $blueprint = new static($files);
           
$blueprint->setContext($this->context)->setOverrides($this->overrides)->load();
        } else {
            $blueprint = $this;
        }

        $import = $blueprint->get($field);

        return \is_array($import) ? $import : null;
    }

    /**
     * @param array|string $value
     * @param array $path
     */
    protected function doImport($value, array &$path)
    {
        $imported = $this->loadImport($value);

        if ($imported) {
            $this->deepInit($imported, $path);
            $name = implode('/', $path);
            $this->embed($name, $imported, '/', false);
        }
    }

    /**
     * Internal function that handles loading extended blueprints.
     *
     * @param array $files
     * @param string|array|null $extends
     * @return array
     */
    protected function doLoad(array $files, $extends = null)
    {
        $filename = array_shift($files);
        $content = $this->loadFile($filename);

        $key = '';
        if (isset($content['extends@'])) {
            $key = 'extends@';
        } elseif (isset($content['@extends'])) {
            $key = '@extends';
        } elseif (isset($content['@extends@'])) {
            $key = '@extends@';
        }

        $override = (bool)$extends;
        $extends = (array)($key && !$extends ? $content[$key] :
$extends);

        unset($content['extends@'],
$content['@extends'], $content['@extends@']);

        $data = $extends ? $this->doExtend($filename, $files, $extends,
$override) : [];
        $data[] = $content;

        return $data;
    }

    /**
     * Internal function to recursively load extended blueprints.
     *
     * @param string $filename
     * @param array $parents
     * @param array $extends
     * @return array
     */
    protected function doExtend($filename, array $parents, array $extends,
$override = false)
    {
        if (\is_string(key($extends))) {
            $extends = [$extends];
        }

        $data = [[]];
        foreach ($extends as $value) {
            // Accept array of type and context or a string.
            $type = !\is_string($value) ? (!isset($value['type'])
? null : $value['type']) : $value;

            if (!$type) {
                continue;
            }

            if ($type === '@parent' || $type ===
'parent@') {
                if (!$parents) {
                    throw new RuntimeException("Parent blueprint
missing for '{$filename}'");
                }

                $files = $parents;
            } else {
                $files = $this->getFiles($type,
isset($value['context']) ? $value['context'] : null);

                if ($override && !$files) {
                    throw new RuntimeException("Blueprint
'{$type}' missing for '{$filename}'");
                }

                // Detect extend loops.
                if ($files && array_intersect($files, $parents)) {
                    // Let's check if user really meant extends@:
parent@.
                    $index = \array_search($filename, $files, true);
                    if ($index !== false) {
                        // We want to grab only the parents of the file
which is currently being loaded.
                        $files = \array_slice($files, $index + 1);
                    }
                    if ($files !== $parents) {
                        throw new RuntimeException("Loop detected
while extending blueprint file '{$filename}'");
                    }
                    if (!$parents) {
                        throw new RuntimeException("Parent blueprint
missing for '{$filename}'");
                    }
                }
            }

            if ($files) {
                $data[] = $this->doLoad($files);
            }
        }

        // TODO: In PHP 5.6+ use array_merge(...$data);
        return call_user_func_array('array_merge', $data);
    }

    /**
     * Internal function to reorder items.
     *
     * @param array $items
     * @param array $keys
     * @return array
     */
    protected function doReorder(array $items, array $keys)
    {
        $reordered = array_keys($items);

        foreach ($keys as $item => $ordering) {
            if ((string)(int) $ordering === (string) $ordering) {
                $location = array_search($item, $reordered, true);
                $rel = array_splice($reordered, $location, 1);
                array_splice($reordered, $ordering, 0, $rel);

            } elseif (isset($items[$ordering])) {
                $location = array_search($item, $reordered, true);
                $rel = array_splice($reordered, $location, 1);
                $location = array_search($ordering, $reordered, true);
                array_splice($reordered, $location + 1, 0, $rel);
            }
        }

        return array_merge(array_flip($reordered), $items);
    }
}
vendor/rockettheme/toolbox/Blueprints/src/Blueprints.php000064400000000373151166614550017634
0ustar00<?php
namespace RocketTheme\Toolbox\Blueprints;

/**
 * Deprecated class, use BlueprintSchema instead.
 *
 * @package RocketTheme\Toolbox\Blueprints
 * @author RocketTheme
 * @license MIT
 * @deprecated
 */
class Blueprints extends BlueprintSchema {}
vendor/rockettheme/toolbox/Blueprints/src/BlueprintSchema.php000064400000050327151166614550020576
0ustar00<?php
namespace RocketTheme\Toolbox\Blueprints;

/**
 * BlueprintSchema is used to define a data structure.
 *
 * @package RocketTheme\Toolbox\Blueprints
 * @author RocketTheme
 * @license MIT
 */
class BlueprintSchema
{
    /** @var array */
    protected $items = [];

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

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

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

    /** @var array */
    protected $filter = ['validation' => true];

    /** @var array */
    protected $ignoreFormKeys = ['fields' => 1];

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

    /**
     * Constructor.
     *
     * @param array $serialized  Serialized content if available.
     */
    public function __construct($serialized = null)
    {
        if (\is_array($serialized) && !empty($serialized)) {
            $this->items = (array) $serialized['items'];
            $this->rules = (array) $serialized['rules'];
            $this->nested = (array) $serialized['nested'];
            $this->dynamic = (array) $serialized['dynamic'];
            $this->filter = (array) $serialized['filter'];
        }
    }

    /**
     * @param array $types
     * @return $this
     */
    public function setTypes(array $types)
    {
        $this->types = $types;

        return $this;
    }

    /**
     * Restore Blueprints object.
     *
     * @param array $serialized
     * @return static
     */
    public static function restore(array $serialized)
    {
        return new static($serialized);
    }

    /**
     * Initialize blueprints with its dynamic fields.
     *
     * @return $this
     */
    public function init()
    {
        foreach ($this->dynamic as $key => $data) {
            $field = &$this->items[$key];

            foreach ($data as $property => $call) {
                $action = 'dynamic' .
ucfirst($call['action']);

                if (method_exists($this, $action)) {
                    $this->{$action}($field, $property, $call);
                }
            }
        }

        return $this;
    }

    /**
     * Set filter for inherited properties.
     *
     * @param array $filter     List of field names to be inherited.
     */
    public function setFilter(array $filter)
    {
        $this->filter = array_flip($filter);
    }

    /**
     * Get value by using dot notation for nested arrays/objects.
     *
     * @example $value =
$data->get('this.is.my.nested.variable');
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     *
     * @return mixed  Value.
     */
    public function get($name, $default = null, $separator = '.')
    {
        $name = $separator !== '.' ? str_replace($separator,
'.', $name) : $name;

        return isset($this->items[$name]) ? $this->items[$name] :
$default;
    }

    /**
     * Set value by using dot notation for nested arrays/objects.
     *
     * @example $value =
$data->set('this.is.my.nested.variable', $newField);
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $value      New value.
     * @param string  $separator  Separator, defaults to '.'
     */
    public function set($name, $value, $separator = '.')
    {
        $name = $separator !== '.' ? str_replace($separator,
'.', $name) : $name;

        $this->items[$name] = $value;
        $this->addProperty($name);
    }

    /**
     * Define value by using dot notation for nested arrays/objects.
     *
     * @example $value =
$data->set('this.is.my.nested.variable', true);
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $value      New value.
     * @param string  $separator  Separator, defaults to '.'
     */
    public function def($name, $value, $separator = '.')
    {
        $this->set($name, $this->get($name, $value, $separator),
$separator);
    }

    /**
     * @return array
     * @deprecated
     */
    public function toArray()
    {
        return $this->getState();
    }

    /**
     * Convert object into an array.
     *
     * @return array
     */
    public function getState()
    {
        return [
            'items' => $this->items,
            'rules' => $this->rules,
            'nested' => $this->nested,
            'dynamic' => $this->dynamic,
            'filter' => $this->filter
        ];
    }

    /**
     * Get nested structure containing default values defined in the
blueprints.
     *
     * Fields without default value are ignored in the list.
     *
     * @return array
     */
    public function getDefaults()
    {
        return $this->buildDefaults($this->nested);
    }

    /**
     * Embed an array to the blueprint.
     *
     * @param $name
     * @param array $value
     * @param string $separator
     * @param bool $merge   Merge fields instead replacing them.
     * @return $this
     */
    public function embed($name, array $value, $separator = '.',
$merge = false)
    {
        if (isset($value['rules'])) {
            $this->rules = array_merge($this->rules,
$value['rules']);
        }

        $name = $separator !== '.' ? str_replace($separator,
'.', $name) : $name;

        if (isset($value['form'])) {
            $form = array_diff_key($value['form'],
['fields' => 1, 'field' => 1]);
        } else {
            $form = [];
        }

        $items = isset($this->items[$name]) ? $this->items[$name] :
['type' => '_root', 'form_field' =>
false];

        $this->items[$name] = $items;
        $this->addProperty($name);

        $prefix = $name ? $name . '.' : '';
        $params = array_intersect_key($form, $this->filter);
        $location = [$name];

        if (isset($value['form']['field'])) {
            $this->parseFormField($name,
$value['form']['field'], $params, $prefix,
'', $merge, $location);
        } elseif (isset($value['form']['fields'])) {
           
$this->parseFormFields($value['form']['fields'],
$params, $prefix, '', $merge, $location);
        }

        $this->items[$name] += ['form' => $form];

        return $this;
    }

    /**
     * Merge two arrays by using blueprints.
     *
     * @param  array $data1
     * @param  array $data2
     * @param  string $name         Optional
     * @param  string $separator    Optional
     * @return array
     */
    public function mergeData(array $data1, array $data2, $name = null,
$separator = '.')
    {
        $nested = $this->getNested($name, $separator);

        if (!\is_array($nested)) {
            $nested = [];
        }

        return $this->mergeArrays($data1, $data2, $nested);
    }

    /**
     * Get the property with given path.
     *
     * @param string $path
     * @param string $separator
     * @return mixed
     */
    public function getProperty($path = null, $separator = '.')
    {
        $name = $this->getPropertyName($path, $separator);
        $property = $this->get($name);
        $nested = $this->getNested($name);

        return $this->getPropertyRecursion($property, $nested);
    }

    /**
     * Returns name of the property with given path.
     *
     * @param string $path
     * @param string $separator
     * @return string
     */
    public function getPropertyName($path = null, $separator =
'.')
    {
        $parts = explode($separator, $path);
        $nested = $this->nested;

        $result = [];
        while (($part = array_shift($parts)) !== null) {
            if (!isset($nested[$part])) {
                if (isset($nested['*'])) {
                    $part = '*';
                } else {
                    return implode($separator, array_merge($result,
[$part], $parts));
                }
            }
            $result[] = $part;
            $nested = $nested[$part];
        }

        return implode('.', $result);
    }

    /**
     * Return data fields that do not exist in blueprints.
     *
     * @param  array  $data
     * @param  string $prefix
     * @return array
     */
    public function extra(array $data, $prefix = '')
    {
        $rules = $this->nested;

        // Drill down to prefix level
        if (!empty($prefix)) {
            $parts = explode('.', trim($prefix, '.'));
            foreach ($parts as $part) {
                $rules = isset($rules[$part]) ? $rules[$part] : [];
            }
        }

        // Check if the form cannot have extra fields.
        if (isset($rules[''])) {
            $rule = $this->items[''];
            if (isset($rule['type']) &&
$rule['type'] !== '_root') {
                return [];
            }
        }

        return $this->extraArray($data, $rules, $prefix);
    }

    /**
     * Get the property with given path.
     *
     * @param $property
     * @param $nested
     * @return mixed
     */
    protected function getPropertyRecursion($property, $nested)
    {
        if (empty($nested) || !\is_array($nested) ||
!isset($property['type'])) {
            return $property;
        }

        if ($property['type'] === '_root') {
            foreach ($nested as $key => $value) {
                if ($key === '') {
                    continue;
                }

                $name = \is_array($value) ? $key : $value;
                $property['fields'][$key] =
$this->getPropertyRecursion($this->get($name), $value);
            }
        } elseif ($property['type'] === '_parent' ||
!empty($property['array'])) {
            foreach ($nested as $key => $value) {
                $name = \is_array($value) ?
"{$property['name']}.{$key}" : $value;
                $property['fields'][$key] =
$this->getPropertyRecursion($this->get($name), $value);
            }
        }

        return $property;
    }

    /**
     * Get property from the definition.
     *
     * @param  string  $path  Comma separated path to the property.
     * @param  string  $separator
     * @return array|string|null
     * @internal
     */
    protected function getNested($path = null, $separator = '.')
    {
        if (!$path) {
            return $this->nested;
        }
        $parts = explode($separator, $path);
        $item = array_pop($parts);

        $nested = $this->nested;
        foreach ($parts as $part) {
            if (!isset($nested[$part])) {
                $part = '*';
                if (!isset($nested[$part])) {
                    return [];
                }
            }
            $nested = $nested[$part];
        }

        return isset($nested[$item]) ? $nested[$item] :
(isset($nested['*']) ? $nested['*'] : null);
    }

    /**
     * @param array $nested
     * @return array
     */
    protected function buildDefaults(array $nested)
    {
        $defaults = [];

        foreach ($nested as $key => $value) {
            if ($key === '*') {
                // TODO: Add support for adding defaults to collections.
                continue;
            }

            if (\is_array($value)) {
                // Recursively fetch the items.
                $list = $this->buildDefaults($value);

                // Only return defaults if there are any.
                if (!empty($list)) {
                    $defaults[$key] = $list;
                }
            } else {
                // We hit a field; get default from it if it exists.
                $item = $this->get($value);

                // Only return default value if it exists.
                if (isset($item['default'])) {
                    $defaults[$key] = $item['default'];
                }
            }
        }

        return $defaults;
    }

    /**
     * @param array $data1
     * @param array $data2
     * @param array $rules
     * @return array
     * @internal
     */
    protected function mergeArrays(array $data1, array $data2, array
$rules)
    {
        foreach ($data2 as $key => $field) {
            $val = isset($rules[$key]) ? $rules[$key] : null;
            $rule = \is_string($val) ? $this->items[$val] : null;

            if ((array_key_exists($key, $data1) &&
\is_array($data1[$key]) && \is_array($field) &&
\is_array($val) && !isset($val['*']))
                || (!empty($rule['type']) &&
strpos($rule['type'], '_') === 0)) {
                // Array has been defined in blueprints and is not a
collection of items.
                $data1[$key] = $this->mergeArrays($data1[$key], $field,
$val);
            } else {
                // Otherwise just take value from the data2.
                $data1[$key] = $field;
            }
        }

        return $data1;
    }

    /**
     * Gets all field definitions from the blueprints.
     *
     * @param array  $fields    Fields to parse.
     * @param array  $params    Property parameters.
     * @param string $prefix    Property prefix.
     * @param string $parent    Parent property.
     * @param bool   $merge     Merge fields instead replacing them.
     * @param array $formPath
     */
    protected function parseFormFields(array $fields, array $params,
$prefix = '', $parent = '', $merge = false, array
$formPath = [])
    {
        if (isset($fields['type']) &&
!\is_array($fields['type'])) {
            return;
        }

        // Go though all the fields in current level.
        foreach ($fields as $key => $field) {
            $this->parseFormField($key, $field, $params, $prefix,
$parent, $merge, $formPath);
        }
    }

    /**
     * @param string $key
     * @param array $field
     * @param array $params
     * @param string $prefix
     * @param string $parent
     * @param bool $merge
     * @param array $formPath
     */
    protected function parseFormField($key, array $field, array $params,
$prefix = '', $parent = '', $merge = false, array
$formPath = [])
    {
        // Skip illegal field (needs to be an array).
        if (!\is_array($field)) {
            return;
        }

        $key = $this->getFieldKey($key, $prefix, $parent);

        $newPath = array_merge($formPath, [$key]);

        $properties = array_diff_key($field, $this->ignoreFormKeys) +
$params;
        $properties['name'] = $key;

        // Set default properties for the field type.
        $type = isset($properties['type']) ?
$properties['type'] : '';
        if (isset($this->types[$type])) {
            $properties += $this->types[$type];
        }

        // Merge properties with existing ones.
        if ($merge && isset($this->items[$key])) {
            $properties += $this->items[$key];
        }

        $isInputField = !isset($properties['input@']) ||
$properties['input@'];

        $propertyExists = isset($this->items[$key]);
        if (!$isInputField) {
            // Remove property if it exists.
            if ($propertyExists) {
                $this->removeProperty($key);
            }
        } elseif (!$propertyExists) {
            // Add missing property.
            $this->addProperty($key);
        }

        if (isset($field['fields'])) {
            // Recursively get all the nested fields.
            $isArray = !empty($properties['array']);
            $newParams = array_intersect_key($properties,
$this->filter);
            $this->parseFormFields($field['fields'],
$newParams, $prefix, $key . ($isArray ? '.*': ''),
$merge, $newPath);
        } else {
            if (!isset($this->items[$key])) {
                // Add parent rules.
                $path = explode('.', $key);
                array_pop($path);
                $parent = '';

                foreach ($path as $part) {
                    $parent .= ($parent ? '.' : '') .
$part;
                    if (!isset($this->items[$parent])) {
                        $this->items[$parent] = ['type' =>
'_parent', 'name' => $parent, 'form_field'
=> false];
                    }
                }
            }

            if ($isInputField) {
                $this->parseProperties($key, $properties);
            }
        }

        if ($isInputField) {
            $this->items[$key] = $properties;
        }
    }

    protected function getFieldKey($key, $prefix, $parent)
    {
        // Set name from the array key.
        if (strpos($key[0], '.') === 0) {
            return ($parent ?: rtrim($prefix, '.')) . $key;
        }

        return $prefix . $key;
    }

    protected function parseProperties($key, array &$properties)
    {
        $key = ltrim($key, '.');

        if (!empty($properties['data'])) {
            $this->dynamic[$key] = $properties['data'];
        }

        foreach ($properties as $name => $value) {
            if (strpos($name[0], '@') !== false) {
                $list = explode('-', trim($name, '@'),
2);
                $action = array_shift($list);
                $property = array_shift($list);

                $this->dynamic[$key][$property] = ['action'
=> $action, 'params' => $value];
            }
        }

        // Initialize predefined validation rule.
        if (isset($properties['validate']['rule'])) {
            $properties['validate'] +=
$this->getRule($properties['validate']['rule']);
        }
    }

    /**
     * Add property to the definition.
     *
     * @param  string  $path  Comma separated path to the property.
     * @internal
     */
    protected function addProperty($path)
    {
        $parts = explode('.', $path);
        $item = array_pop($parts);

        $nested = &$this->nested;
        foreach ($parts as $part) {
            if (!isset($nested[$part]) || !\is_array($nested[$part])) {
                $nested[$part] = [];
            }

            $nested = &$nested[$part];
        }

        if (!isset($nested[$item])) {
            $nested[$item] = $path;
        }
    }

    /**
     * Remove property to the definition.
     *
     * @param  string  $path  Comma separated path to the property.
     * @internal
     */
    protected function removeProperty($path)
    {
        $parts = explode('.', $path);
        $item = array_pop($parts);

        $nested = &$this->nested;
        foreach ($parts as $part) {
            if (!isset($nested[$part]) || !\is_array($nested[$part])) {
                return;
            }

            $nested = &$nested[$part];
        }

        if (isset($nested[$item])) {
            unset($nested[$item]);
        }
    }

    /**
     * @param $rule
     * @return array
     * @internal
     */
    protected function getRule($rule)
    {
        if (isset($this->rules[$rule]) &&
\is_array($this->rules[$rule])) {
            return $this->rules[$rule];
        }
        return [];
    }

    /**
     * @param array $data
     * @param array $rules
     * @param string $prefix
     * @return array
     * @internal
     */
    protected function extraArray(array $data, array $rules, $prefix)
    {
        $array = [];

        foreach ($data as $key => $field) {
            $val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
            $rule = \is_string($val) ? $this->items[$val] : null;

            if ($rule || isset($val['*'])) {
                // Item has been defined in blueprints.
            } elseif (\is_array($field) && \is_array($val)) {
                // Array has been defined in blueprints.
                $array += $this->extraArray($field, $val, $prefix . $key
. '.');
            } else {
                // Undefined/extra item.
                $array[$prefix.$key] = $field;
            }
        }
        return $array;
    }

    /**
     * @param array $field
     * @param string $property
     * @param array $call
     */
    protected function dynamicData(array &$field, $property, array
$call)
    {
        $params = $call['params'];

        if (\is_array($params)) {
            $function = array_shift($params);
        } else {
            $function = $params;
            $params = [];
        }

        $list = explode('::', $function, 2);
        $f = array_pop($list);
        $o = array_pop($list);

        if (!$o) {
            if (\function_exists($f)) {
                $data = \call_user_func_array($f, $params);
            }
        } else {
            if (method_exists($o, $f)) {
                $data = \call_user_func_array(array($o, $f), $params);
            }
        }

        // If function returns a value,
        if (isset($data)) {
            if (\is_array($data) && isset($field[$property])
&& \is_array($field[$property])) {
                // Combine field and @data-field together.
                $field[$property] += $data;
            } else {
                // Or create/replace field with @data-field.
                $field[$property] = $data;
            }
        }
    }
}
vendor/rockettheme/toolbox/Compat/src/Yaml/Exception/ExceptionInterface.php000064400000000704151166614550023216
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace RocketTheme\Toolbox\Compat\Yaml\Exception;

/**
 * Exception interface for all exceptions thrown by the component.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface ExceptionInterface
{
}
vendor/rockettheme/toolbox/Compat/src/Yaml/Exception/ParseException.php000064400000007002151166614550022366
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace RocketTheme\Toolbox\Compat\Yaml\Exception;

/**
 * Exception class thrown when an error occurs during parsing.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ParseException extends RuntimeException
{
    private $parsedFile;
    private $parsedLine;
    private $snippet;
    private $rawMessage;

    /**
     * @param string          $message    The error message
     * @param int             $parsedLine The line where the error occurred
     * @param string|null     $snippet    The snippet of code near the
problem
     * @param string|null     $parsedFile The file name where the error
occurred
     * @param \Exception|null $previous   The previous exception
     */
    public function __construct($message, $parsedLine = -1, $snippet =
null, $parsedFile = null, \Exception $previous = null)
    {
        $this->parsedFile = $parsedFile;
        $this->parsedLine = $parsedLine;
        $this->snippet = $snippet;
        $this->rawMessage = $message;

        $this->updateRepr();

        parent::__construct($this->message, 0, $previous);
    }

    /**
     * Gets the snippet of code near the error.
     *
     * @return string The snippet of code
     */
    public function getSnippet()
    {
        return $this->snippet;
    }

    /**
     * Sets the snippet of code near the error.
     *
     * @param string $snippet The code snippet
     */
    public function setSnippet($snippet)
    {
        $this->snippet = $snippet;

        $this->updateRepr();
    }

    /**
     * Gets the filename where the error occurred.
     *
     * This method returns null if a string is parsed.
     *
     * @return string The filename
     */
    public function getParsedFile()
    {
        return $this->parsedFile;
    }

    /**
     * Sets the filename where the error occurred.
     *
     * @param string $parsedFile The filename
     */
    public function setParsedFile($parsedFile)
    {
        $this->parsedFile = $parsedFile;

        $this->updateRepr();
    }

    /**
     * Gets the line where the error occurred.
     *
     * @return int The file line
     */
    public function getParsedLine()
    {
        return $this->parsedLine;
    }

    /**
     * Sets the line where the error occurred.
     *
     * @param int $parsedLine The file line
     */
    public function setParsedLine($parsedLine)
    {
        $this->parsedLine = $parsedLine;

        $this->updateRepr();
    }

    private function updateRepr()
    {
        $this->message = $this->rawMessage;

        $dot = false;
        if ('.' === substr($this->message, -1)) {
            $this->message = substr($this->message, 0, -1);
            $dot = true;
        }

        if (null !== $this->parsedFile) {
            if (\PHP_VERSION_ID >= 50400) {
                $jsonOptions = JSON_UNESCAPED_SLASHES |
JSON_UNESCAPED_UNICODE;
            } else {
                $jsonOptions = 0;
            }
            $this->message .= sprintf(' in %s',
json_encode($this->parsedFile, $jsonOptions));
        }

        if ($this->parsedLine >= 0) {
            $this->message .= sprintf(' at line %d',
$this->parsedLine);
        }

        if ($this->snippet) {
            $this->message .= sprintf(' (near
"%s")', $this->snippet);
        }

        if ($dot) {
            $this->message .= '.';
        }
    }
}
vendor/rockettheme/toolbox/Compat/src/Yaml/Exception/RuntimeException.php000064400000000756151166614550022750
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace RocketTheme\Toolbox\Compat\Yaml\Exception;

/**
 * Exception class thrown when an error occurs during parsing.
 *
 * @author Romain Neutron <imprec@gmail.com>
 */
class RuntimeException extends \RuntimeException implements
ExceptionInterface
{
}
vendor/rockettheme/toolbox/Compat/src/Yaml/Inline.php000064400000043025151166614550016722
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace RocketTheme\Toolbox\Compat\Yaml;

use RocketTheme\Toolbox\Compat\Yaml\Exception\ParseException;

/**
 * Inline implements a YAML parser/dumper for the YAML inline syntax.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Inline
{
    const REGEX_QUOTED_STRING =
'(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';

    private static $exceptionOnInvalidType = false;
    private static $objectSupport = false;
    private static $objectForMap = false;

    /**
     * Converts a YAML string to a PHP value.
     *
     * @param string $value                  A YAML string
     * @param bool   $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
     * @param bool   $objectSupport          True if object support is
enabled, false otherwise
     * @param bool   $objectForMap           True if maps should return a
stdClass instead of array()
     * @param array  $references             Mapping of variable names to
values
     *
     * @return mixed A PHP value
     *
     * @throws ParseException
     */
    public static function parse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false, $references = array())
    {
        self::$exceptionOnInvalidType = $exceptionOnInvalidType;
        self::$objectSupport = $objectSupport;
        self::$objectForMap = $objectForMap;

        $value = trim($value);

        if ('' === $value) {
            return '';
        }

        if (2 /* MB_OVERLOAD_STRING */ & (int)
ini_get('mbstring.func_overload')) {
            $mbEncoding = mb_internal_encoding();
            mb_internal_encoding('ASCII');
        }

        $i = 0;
        switch ($value[0]) {
            case '[':
                $result = self::parseSequence($value, $i, $references);
                ++$i;
                break;
            case '{':
                $result = self::parseMapping($value, $i, $references);
                ++$i;
                break;
            default:
                $result = self::parseScalar($value, null,
array('"', "'"), $i, true, $references);
        }

        // some comments are allowed at the end
        if (preg_replace('/\s+#.*$/A', '',
substr($value, $i))) {
            throw new ParseException(sprintf('Unexpected characters
near "%s".', substr($value, $i)));
        }

        if (isset($mbEncoding)) {
            mb_internal_encoding($mbEncoding);
        }

        return $result;
    }

    /**
     * Check if given array is hash or just normal indexed array.
     *
     * @internal
     *
     * @param array $value The PHP array to check
     *
     * @return bool true if value is hash array, false otherwise
     */
    public static function isHash(array $value)
    {
        $expectedKey = 0;

        foreach ($value as $key => $val) {
            if ($key !== $expectedKey++) {
                return true;
            }
        }

        return false;
    }

    /**
     * Parses a YAML scalar.
     *
     * @param string   $scalar
     * @param string[] $delimiters
     * @param string[] $stringDelimiters
     * @param int      &$i
     * @param bool     $evaluate
     * @param array    $references
     *
     * @return string
     *
     * @throws ParseException When malformed inline YAML string is parsed
     *
     * @internal
     */
    public static function parseScalar($scalar, $delimiters = null,
$stringDelimiters = array('"', "'"), &$i
= 0, $evaluate = true, $references = array())
    {
        if (in_array($scalar[$i], $stringDelimiters)) {
            // quoted scalar
            $output = self::parseQuotedScalar($scalar, $i);

            if (null !== $delimiters) {
                $tmp = ltrim(substr($scalar, $i), ' ');
                if (!in_array($tmp[0], $delimiters)) {
                    throw new ParseException(sprintf('Unexpected
characters (%s).', substr($scalar, $i)));
                }
            }
        } else {
            // "normal" string
            if (!$delimiters) {
                $output = substr($scalar, $i);
                $i += strlen($output);

                // remove comments
                if (Parser::preg_match('/[ \t]+#/', $output,
$match, PREG_OFFSET_CAPTURE)) {
                    $output = substr($output, 0, $match[0][1]);
                }
            } elseif
(Parser::preg_match('/^(.+?)('.implode('|',
$delimiters).')/', substr($scalar, $i), $match)) {
                $output = $match[1];
                $i += strlen($output);
            } else {
                throw new ParseException(sprintf('Malformed inline
YAML string: %s.', $scalar));
            }

            // a non-quoted string cannot start with @ or ` (reserved) nor
with a scalar indicator (| or >)
            if ($output && ('@' === $output[0] ||
'`' === $output[0] || '|' === $output[0] ||
'>' === $output[0])) {
                @trigger_error(sprintf('Not quoting the scalar
"%s" starting with "%s" is deprecated since Symfony 2.8
and will throw a ParseException in 3.0.', $output, $output[0]),
E_USER_DEPRECATED);

                // to be thrown in 3.0
                // throw new ParseException(sprintf('The reserved
indicator "%s" cannot start a plain scalar; you need to quote the
scalar.', $output[0]));
            }

            if ($evaluate) {
                $output = self::evaluateScalar($output, $references);
            }
        }

        return $output;
    }

    /**
     * Parses a YAML quoted scalar.
     *
     * @param string $scalar
     * @param int    &$i
     *
     * @return string
     *
     * @throws ParseException When malformed inline YAML string is parsed
     */
    private static function parseQuotedScalar($scalar, &$i)
    {
        if
(!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au',
substr($scalar, $i), $match)) {
            throw new ParseException(sprintf('Malformed inline YAML
string: %s.', substr($scalar, $i)));
        }

        $output = substr($match[0], 1, strlen($match[0]) - 2);

        $unescaper = new Unescaper();
        if ('"' == $scalar[$i]) {
            $output = $unescaper->unescapeDoubleQuotedString($output);
        } else {
            $output = $unescaper->unescapeSingleQuotedString($output);
        }

        $i += strlen($match[0]);

        return $output;
    }

    /**
     * Parses a YAML sequence.
     *
     * @param string $sequence
     * @param int    &$i
     * @param array  $references
     *
     * @return array
     *
     * @throws ParseException When malformed inline YAML string is parsed
     */
    private static function parseSequence($sequence, &$i = 0,
$references = array())
    {
        $output = array();
        $len = strlen($sequence);
        ++$i;

        // [foo, bar, ...]
        while ($i < $len) {
            switch ($sequence[$i]) {
                case '[':
                    // nested sequence
                    $output[] = self::parseSequence($sequence, $i,
$references);
                    break;
                case '{':
                    // nested mapping
                    $output[] = self::parseMapping($sequence, $i,
$references);
                    break;
                case ']':
                    return $output;
                case ',':
                case ' ':
                    break;
                default:
                    $isQuoted = in_array($sequence[$i],
array('"', "'"));
                    $value = self::parseScalar($sequence,
array(',', ']'), array('"',
"'"), $i, true, $references);

                    // the value can be an array if a reference has been
resolved to an array var
                    if (!is_array($value) && !$isQuoted &&
false !== strpos($value, ': ')) {
                        // embedded mapping?
                        try {
                            $pos = 0;
                            $value =
self::parseMapping('{'.$value.'}', $pos, $references);
                        } catch (\InvalidArgumentException $e) {
                            // no, it's not
                        }
                    }

                    $output[] = $value;

                    --$i;
            }

            ++$i;
        }

        throw new ParseException(sprintf('Malformed inline YAML
string: %s.', $sequence));
    }

    /**
     * Parses a YAML mapping.
     *
     * @param string $mapping
     * @param int    &$i
     * @param array  $references
     *
     * @return array|\stdClass
     *
     * @throws ParseException When malformed inline YAML string is parsed
     */
    private static function parseMapping($mapping, &$i = 0, $references
= array())
    {
        $output = array();
        $len = strlen($mapping);
        ++$i;
        $allowOverwrite = false;

        // {foo: bar, bar:foo, ...}
        while ($i < $len) {
            switch ($mapping[$i]) {
                case ' ':
                case ',':
                    ++$i;
                    continue 2;
                case '}':
                    if (self::$objectForMap) {
                        return (object) $output;
                    }

                    return $output;
            }

            // key
            $key = self::parseScalar($mapping, array(':', '
'), array('"', "'"), $i, false);

            if ('<<' === $key) {
                $allowOverwrite = true;
            }

            // value
            $done = false;

            while ($i < $len) {
                switch ($mapping[$i]) {
                    case '[':
                        // nested sequence
                        $value = self::parseSequence($mapping, $i,
$references);
                        // Spec: Keys MUST be unique; first one wins.
                        // Parser cannot abort this mapping earlier, since
lines
                        // are processed sequentially.
                        // But overwriting is allowed when a merge node is
used in current block.
                        if ('<<' === $key) {
                            foreach ($value as $parsedValue) {
                                $output += $parsedValue;
                            }
                        } elseif ($allowOverwrite || !isset($output[$key]))
{
                            $output[$key] = $value;
                        }
                        $done = true;
                        break;
                    case '{':
                        // nested mapping
                        $value = self::parseMapping($mapping, $i,
$references);
                        // Spec: Keys MUST be unique; first one wins.
                        // Parser cannot abort this mapping earlier, since
lines
                        // are processed sequentially.
                        // But overwriting is allowed when a merge node is
used in current block.
                        if ('<<' === $key) {
                            $output += $value;
                        } elseif ($allowOverwrite || !isset($output[$key]))
{
                            $output[$key] = $value;
                        }
                        $done = true;
                        break;
                    case ':':
                    case ' ':
                        break;
                    default:
                        $value = self::parseScalar($mapping,
array(',', '}'), array('"',
"'"), $i, true, $references);
                        // Spec: Keys MUST be unique; first one wins.
                        // Parser cannot abort this mapping earlier, since
lines
                        // are processed sequentially.
                        // But overwriting is allowed when a merge node is
used in current block.
                        if ('<<' === $key) {
                            $output += $value;
                        } elseif ($allowOverwrite || !isset($output[$key]))
{
                            $output[$key] = $value;
                        }
                        $done = true;
                        --$i;
                }

                ++$i;

                if ($done) {
                    continue 2;
                }
            }
        }

        throw new ParseException(sprintf('Malformed inline YAML
string: %s.', $mapping));
    }

    /**
     * Evaluates scalars and replaces magic values.
     *
     * @param string $scalar
     * @param array  $references
     *
     * @return mixed The evaluated YAML string
     *
     * @throws ParseException when object parsing support was disabled and
the parser detected a PHP object or when a reference could not be resolved
     */
    private static function evaluateScalar($scalar, $references = array())
    {
        $scalar = trim($scalar);
        $scalarLower = strtolower($scalar);

        if (0 === strpos($scalar, '*')) {
            if (false !== $pos = strpos($scalar, '#')) {
                $value = substr($scalar, 1, $pos - 2);
            } else {
                $value = substr($scalar, 1);
            }

            // an unquoted *
            if (false === $value || '' === $value) {
                throw new ParseException('A reference must contain at
least one character.');
            }

            if (!array_key_exists($value, $references)) {
                throw new ParseException(sprintf('Reference
"%s" does not exist.', $value));
            }

            return $references[$value];
        }

        switch (true) {
            case 'null' === $scalarLower:
            case '' === $scalar:
            case '~' === $scalar:
                return;
            case 'true' === $scalarLower:
                return true;
            case 'false' === $scalarLower:
                return false;
            // Optimise for returning strings.
            case '+' === $scalar[0] || '-' ===
$scalar[0] || '.' === $scalar[0] || '!' === $scalar[0]
|| is_numeric($scalar[0]):
                switch (true) {
                    case 0 === strpos($scalar, '!str'):
                        return (string) substr($scalar, 5);
                    case 0 === strpos($scalar, '! '):
                        return (int) self::parseScalar(substr($scalar, 2));
                    case 0 === strpos($scalar, '!php/object:'):
                        if (self::$objectSupport) {
                            return unserialize(substr($scalar, 12));
                        }

                        if (self::$exceptionOnInvalidType) {
                            throw new ParseException('Object support
when parsing a YAML file has been disabled.');
                        }

                        return;
                    case 0 === strpos($scalar, '!!php/object:'):
                        if (self::$objectSupport) {
                            return unserialize(substr($scalar, 13));
                        }

                        if (self::$exceptionOnInvalidType) {
                            throw new ParseException('Object support
when parsing a YAML file has been disabled.');
                        }

                        return;
                    case 0 === strpos($scalar, '!!float '):
                        return (float) substr($scalar, 8);
                    case ctype_digit($scalar):
                        $raw = $scalar;
                        $cast = (int) $scalar;

                        return '0' == $scalar[0] ?
octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
                    case '-' === $scalar[0] &&
ctype_digit(substr($scalar, 1)):
                        $raw = $scalar;
                        $cast = (int) $scalar;

                        return '0' == $scalar[1] ?
octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
                    case is_numeric($scalar):
                    case Parser::preg_match(self::getHexRegex(), $scalar):
                        return '0x' === $scalar[0].$scalar[1] ?
hexdec($scalar) : (float) $scalar;
                    case '.inf' === $scalarLower:
                    case '.nan' === $scalarLower:
                        return -log(0);
                    case '-.inf' === $scalarLower:
                        return log(0);
                    case
Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
                        return (float) str_replace(',',
'', $scalar);
                    case Parser::preg_match(self::getTimestampRegex(),
$scalar):
                        $timeZone = date_default_timezone_get();
                        date_default_timezone_set('UTC');
                        $time = strtotime($scalar);
                        date_default_timezone_set($timeZone);

                        return $time;
                }
                // no break
            default:
                return (string) $scalar;
        }
    }

    /**
     * Gets a regex that matches a YAML date.
     *
     * @return string The regular expression
     *
     * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
     */
    private static function getTimestampRegex()
    {
        return <<<EOF
        ~^
        (?P<year>[0-9][0-9][0-9][0-9])
        -(?P<month>[0-9][0-9]?)
        -(?P<day>[0-9][0-9]?)
        (?:(?:[Tt]|[ \t]+)
        (?P<hour>[0-9][0-9]?)
        :(?P<minute>[0-9][0-9])
        :(?P<second>[0-9][0-9])
        (?:\.(?P<fraction>[0-9]*))?
        (?:[
\t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
        (?::(?P<tz_minute>[0-9][0-9]))?))?)?
        $~x
EOF;
    }

    /**
     * Gets a regex that matches a YAML number in hexadecimal notation.
     *
     * @return string
     */
    private static function getHexRegex()
    {
        return '~^0x[0-9a-f]++$~i';
    }
}
vendor/rockettheme/toolbox/Compat/src/Yaml/Parser.php000064400000100472151166614550016740
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace RocketTheme\Toolbox\Compat\Yaml;

use RocketTheme\Toolbox\Compat\Yaml\Exception\ParseException;

/**
 * Parser parses YAML strings to convert them to PHP arrays.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Parser
{
    const BLOCK_SCALAR_HEADER_PATTERN =
'(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments>
+#.*)?';
    // BC - wrongly named
    const FOLDED_SCALAR_PATTERN = self::BLOCK_SCALAR_HEADER_PATTERN;

    private $offset = 0;
    private $totalNumberOfLines;
    private $lines = array();
    private $currentLineNb = -1;
    private $currentLine = '';
    private $refs = array();
    private $skippedLineNumbers = array();
    private $locallySkippedLineNumbers = array();

    /**
     * @param int      $offset             The offset of YAML document
(used for line numbers in error messages)
     * @param int|null $totalNumberOfLines The overall number of lines
being parsed
     * @param int[]    $skippedLineNumbers Number of comment lines that
have been skipped by the parser
     */
    public function __construct($offset = 0, $totalNumberOfLines = null,
array $skippedLineNumbers = array())
    {
        $this->offset = $offset;
        $this->totalNumberOfLines = $totalNumberOfLines;
        $this->skippedLineNumbers = $skippedLineNumbers;
    }

    /**
     * Parses a YAML string to a PHP value.
     *
     * @param string $value                  A YAML string
     * @param bool   $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
     * @param bool   $objectSupport          True if object support is
enabled, false otherwise
     * @param bool   $objectForMap           True if maps should return a
stdClass instead of array()
     *
     * @return mixed A PHP value
     *
     * @throws ParseException If the YAML is not valid
     */
    public function parse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
    {
        if (false === preg_match('//u', $value)) {
            throw new ParseException('The YAML value does not appear
to be valid UTF-8.');
        }

        $this->refs = array();

        $mbEncoding = null;
        $e = null;
        $data = null;

        if (2 /* MB_OVERLOAD_STRING */ & (int)
ini_get('mbstring.func_overload')) {
            $mbEncoding = mb_internal_encoding();
            mb_internal_encoding('UTF-8');
        }

        try {
            $data = $this->doParse($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
        } catch (\Exception $e) {
        } catch (\Throwable $e) {
        }

        if (null !== $mbEncoding) {
            mb_internal_encoding($mbEncoding);
        }

        $this->lines = array();
        $this->currentLine = '';
        $this->refs = array();
        $this->skippedLineNumbers = array();
        $this->locallySkippedLineNumbers = array();

        if (null !== $e) {
            throw $e;
        }

        return $data;
    }

    private function doParse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
    {
        $this->currentLineNb = -1;
        $this->currentLine = '';
        $value = $this->cleanup($value);
        $this->lines = explode("\n", $value);
        $this->locallySkippedLineNumbers = array();

        if (null === $this->totalNumberOfLines) {
            $this->totalNumberOfLines = count($this->lines);
        }

        $data = array();
        $context = null;
        $allowOverwrite = false;

        while ($this->moveToNextLine()) {
            if ($this->isCurrentLineEmpty()) {
                continue;
            }

            // tab?
            if ("\t" === $this->currentLine[0]) {
                throw new ParseException('A YAML file cannot contain
tabs as indentation.', $this->getRealCurrentLineNb() + 1,
$this->currentLine);
            }

            $isRef = $mergeNode = false;
            if
(self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u',
rtrim($this->currentLine), $values)) {
                if ($context && 'mapping' == $context) {
                    throw new ParseException('You cannot define a
sequence item when in a mapping', $this->getRealCurrentLineNb() +
1, $this->currentLine);
                }
                $context = 'sequence';

                if (isset($values['value']) &&
self::preg_match('#^&(?P<ref>[^ ]+)
*(?P<value>.*)#u', $values['value'], $matches)) {
                    $isRef = $matches['ref'];
                    $values['value'] =
$matches['value'];
                }

                // array
                if (!isset($values['value']) || '' ==
trim($values['value'], ' ') || 0 ===
strpos(ltrim($values['value'], ' '), '#')) {
                    $data[] =
$this->parseBlock($this->getRealCurrentLineNb() + 1,
$this->getNextEmbedBlock(null, true), $exceptionOnInvalidType,
$objectSupport, $objectForMap);
                } else {
                    if (isset($values['leadspaces'])
                        &&
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^
\'"\{\[].*?) *\:(\s+(?P<value>.+))?$#u',
rtrim($values['value']), $matches)
                    ) {
                        // this is a compact notation element, add to next
block and parse
                        $block = $values['value'];
                        if ($this->isNextLineIndented()) {
                            $block .=
"\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation()
+ strlen($values['leadspaces']) + 1);
                        }

                        $data[] =
$this->parseBlock($this->getRealCurrentLineNb(), $block,
$exceptionOnInvalidType, $objectSupport, $objectForMap);
                    } else {
                        $data[] =
$this->parseValue($values['value'], $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context);
                    }
                }
                if ($isRef) {
                    $this->refs[$isRef] = end($data);
                }
            } elseif (
               
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^
\'"\[\{].*?) *\:(\s+(?P<value>.+))?$#u',
rtrim($this->currentLine), $values)
                && (false === strpos($values['key'],
' #') || in_array($values['key'][0],
array('"', "'")))
            ) {
                if ($context && 'sequence' == $context) {
                    throw new ParseException('You cannot define a
mapping item when in a sequence', $this->currentLineNb + 1,
$this->currentLine);
                }
                $context = 'mapping';

                // force correct settings
                Inline::parse(null, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $this->refs);
                try {
                    $key = Inline::parseScalar($values['key']);
                } catch (ParseException $e) {
                    $e->setParsedLine($this->getRealCurrentLineNb() +
1);
                    $e->setSnippet($this->currentLine);

                    throw $e;
                }

                // Convert float keys to strings, to avoid being converted
to integers by PHP
                if (is_float($key)) {
                    $key = (string) $key;
                }

                if ('<<' === $key &&
(!isset($values['value']) ||
!self::preg_match('#^&(?P<ref>[^ ]+)#u',
$values['value'], $refMatches))) {
                    $mergeNode = true;
                    $allowOverwrite = true;
                    if (isset($values['value']) && 0 ===
strpos($values['value'], '*')) {
                        $refName = substr($values['value'], 1);
                        if (!array_key_exists($refName, $this->refs)) {
                            throw new
ParseException(sprintf('Reference "%s" does not
exist.', $refName), $this->getRealCurrentLineNb() + 1,
$this->currentLine);
                        }

                        $refValue = $this->refs[$refName];

                        if (!is_array($refValue)) {
                            throw new ParseException('YAML merge keys
used with a scalar value instead of an array.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
                        }

                        $data += $refValue; // array union
                    } else {
                        if (isset($values['value']) &&
'' !== $values['value']) {
                            $value = $values['value'];
                        } else {
                            $value = $this->getNextEmbedBlock();
                        }
                        $parsed =
$this->parseBlock($this->getRealCurrentLineNb() + 1, $value,
$exceptionOnInvalidType, $objectSupport, $objectForMap);

                        if (!is_array($parsed)) {
                            throw new ParseException('YAML merge keys
used with a scalar value instead of an array.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
                        }

                        if (isset($parsed[0])) {
                            // If the value associated with the merge key
is a sequence, then this sequence is expected to contain mapping nodes
                            // and each of these nodes is merged in turn
according to its order in the sequence. Keys in mapping nodes earlier
                            // in the sequence override keys specified in
later mapping nodes.
                            foreach ($parsed as $parsedItem) {
                                if (!is_array($parsedItem)) {
                                    throw new ParseException('Merge
items must be arrays.', $this->getRealCurrentLineNb() + 1,
$parsedItem);
                                }

                                $data += $parsedItem; // array union
                            }
                        } else {
                            // If the value associated with the key is a
single mapping node, each of its key/value pairs is inserted into the
                            // current mapping, unless the key already
exists in it.
                            $data += $parsed; // array union
                        }
                    }
                } elseif ('<<' !== $key &&
isset($values['value']) &&
self::preg_match('#^&(?P<ref>[^ ]+)
*(?P<value>.*)#u', $values['value'], $matches)) {
                    $isRef = $matches['ref'];
                    $values['value'] =
$matches['value'];
                }

                if ($mergeNode) {
                    // Merge keys
                } elseif (!isset($values['value']) ||
'' == trim($values['value'], ' ') || 0 ===
strpos(ltrim($values['value'], ' '), '#') ||
'<<' === $key) {
                    // hash
                    // if next line is less indented or equal, then it
means that the current value is null
                    if (!$this->isNextLineIndented() &&
!$this->isNextLineUnIndentedCollection()) {
                        // Spec: Keys MUST be unique; first one wins.
                        // But overwriting is allowed when a merge node is
used in current block.
                        if ($allowOverwrite || !isset($data[$key])) {
                            $data[$key] = null;
                        }
                    } else {
                        $value =
$this->parseBlock($this->getRealCurrentLineNb() + 1,
$this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport,
$objectForMap);

                        if ('<<' === $key) {
                            $this->refs[$refMatches['ref']] =
$value;
                            $data += $value;
                        } elseif ($allowOverwrite || !isset($data[$key])) {
                            // Spec: Keys MUST be unique; first one wins.
                            // But overwriting is allowed when a merge node
is used in current block.
                            $data[$key] = $value;
                        }
                    }
                } else {
                    $value =
$this->parseValue($values['value'], $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context);
                    // Spec: Keys MUST be unique; first one wins.
                    // But overwriting is allowed when a merge node is used
in current block.
                    if ($allowOverwrite || !isset($data[$key])) {
                        $data[$key] = $value;
                    }
                }
                if ($isRef) {
                    $this->refs[$isRef] = $data[$key];
                }
            } else {
                // multiple documents are not supported
                if ('---' === $this->currentLine) {
                    throw new ParseException('Multiple documents are
not supported.', $this->currentLineNb + 1, $this->currentLine);
                }

                // 1-liner optionally followed by newline(s)
                if (is_string($value) && $this->lines[0] ===
trim($value)) {
                    try {
                        $value = Inline::parse($this->lines[0],
$exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
                    } catch (ParseException $e) {
                       
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
                        $e->setSnippet($this->currentLine);

                        throw $e;
                    }

                    return $value;
                }

                throw new ParseException('Unable to parse.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        }

        if ($objectForMap && !is_object($data) &&
'mapping' === $context) {
            $object = new \stdClass();

            foreach ($data as $key => $value) {
                $object->$key = $value;
            }

            $data = $object;
        }

        return empty($data) ? null : $data;
    }

    private function parseBlock($offset, $yaml, $exceptionOnInvalidType,
$objectSupport, $objectForMap)
    {
        $skippedLineNumbers = $this->skippedLineNumbers;

        foreach ($this->locallySkippedLineNumbers as $lineNumber) {
            if ($lineNumber < $offset) {
                continue;
            }

            $skippedLineNumbers[] = $lineNumber;
        }

        $parser = new self($offset, $this->totalNumberOfLines,
$skippedLineNumbers);
        $parser->refs = &$this->refs;

        return $parser->doParse($yaml, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
    }

    /**
     * Returns the current line number (takes the offset into account).
     *
     * @return int The current line number
     */
    private function getRealCurrentLineNb()
    {
        $realCurrentLineNumber = $this->currentLineNb +
$this->offset;

        foreach ($this->skippedLineNumbers as $skippedLineNumber) {
            if ($skippedLineNumber > $realCurrentLineNumber) {
                break;
            }

            ++$realCurrentLineNumber;
        }

        return $realCurrentLineNumber;
    }

    /**
     * Returns the current line indentation.
     *
     * @return int The current line indentation
     */
    private function getCurrentLineIndentation()
    {
        return strlen($this->currentLine) -
strlen(ltrim($this->currentLine, ' '));
    }

    /**
     * Returns the next embed block of YAML.
     *
     * @param int  $indentation The indent level at which the block is to
be read, or null for default
     * @param bool $inSequence  True if the enclosing data structure is a
sequence
     *
     * @return string A YAML string
     *
     * @throws ParseException When indentation problem are detected
     */
    private function getNextEmbedBlock($indentation = null, $inSequence =
false)
    {
        $oldLineIndentation = $this->getCurrentLineIndentation();
        $blockScalarIndentations = array();

        if ($this->isBlockScalarHeader()) {
            $blockScalarIndentations[] =
$this->getCurrentLineIndentation();
        }

        if (!$this->moveToNextLine()) {
            return;
        }

        if (null === $indentation) {
            $newIndent = $this->getCurrentLineIndentation();

            $unindentedEmbedBlock =
$this->isStringUnIndentedCollectionItem();

            if (!$this->isCurrentLineEmpty() && 0 === $newIndent
&& !$unindentedEmbedBlock) {
                throw new ParseException('Indentation problem.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        } else {
            $newIndent = $indentation;
        }

        $data = array();
        if ($this->getCurrentLineIndentation() >= $newIndent) {
            $data[] = substr($this->currentLine, $newIndent);
        } else {
            $this->moveToPreviousLine();

            return;
        }

        if ($inSequence && $oldLineIndentation === $newIndent
&& isset($data[0][0]) && '-' === $data[0][0]) {
            // the previous line contained a dash but no item content, this
line is a sequence item with the same indentation
            // and therefore no nested list or mapping
            $this->moveToPreviousLine();

            return;
        }

        $isItUnindentedCollection =
$this->isStringUnIndentedCollectionItem();

        if (empty($blockScalarIndentations) &&
$this->isBlockScalarHeader()) {
            $blockScalarIndentations[] =
$this->getCurrentLineIndentation();
        }

        $previousLineIndentation = $this->getCurrentLineIndentation();

        while ($this->moveToNextLine()) {
            $indent = $this->getCurrentLineIndentation();

            // terminate all block scalars that are more indented than the
current line
            if (!empty($blockScalarIndentations) && $indent <
$previousLineIndentation && '' !==
trim($this->currentLine)) {
                foreach ($blockScalarIndentations as $key =>
$blockScalarIndentation) {
                    if ($blockScalarIndentation >=
$this->getCurrentLineIndentation()) {
                        unset($blockScalarIndentations[$key]);
                    }
                }
            }

            if (empty($blockScalarIndentations) &&
!$this->isCurrentLineComment() &&
$this->isBlockScalarHeader()) {
                $blockScalarIndentations[] =
$this->getCurrentLineIndentation();
            }

            $previousLineIndentation = $indent;

            if ($isItUnindentedCollection &&
!$this->isCurrentLineEmpty() &&
!$this->isStringUnIndentedCollectionItem() && $newIndent ===
$indent) {
                $this->moveToPreviousLine();
                break;
            }

            if ($this->isCurrentLineBlank()) {
                $data[] = substr($this->currentLine, $newIndent);
                continue;
            }

            // we ignore "comment" lines only when we are not
inside a scalar block
            if (empty($blockScalarIndentations) &&
$this->isCurrentLineComment()) {
                // remember ignored comment lines (they are used later in
nested
                // parser calls to determine real line numbers)
                //
                // CAUTION: beware to not populate the global property here
as it
                // will otherwise influence the getRealCurrentLineNb() call
here
                // for consecutive comment lines and subsequent embedded
blocks
                $this->locallySkippedLineNumbers[] =
$this->getRealCurrentLineNb();

                continue;
            }

            if ($indent >= $newIndent) {
                $data[] = substr($this->currentLine, $newIndent);
            } elseif (0 == $indent) {
                $this->moveToPreviousLine();

                break;
            } else {
                throw new ParseException('Indentation problem.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        }

        return implode("\n", $data);
    }

    /**
     * Moves the parser to the next line.
     *
     * @return bool
     */
    private function moveToNextLine()
    {
        if ($this->currentLineNb >= count($this->lines) - 1) {
            return false;
        }

        $this->currentLine = $this->lines[++$this->currentLineNb];

        return true;
    }

    /**
     * Moves the parser to the previous line.
     *
     * @return bool
     */
    private function moveToPreviousLine()
    {
        if ($this->currentLineNb < 1) {
            return false;
        }

        $this->currentLine = $this->lines[--$this->currentLineNb];

        return true;
    }

    /**
     * Parses a YAML value.
     *
     * @param string $value                  A YAML value
     * @param bool   $exceptionOnInvalidType True if an exception must be
thrown on invalid types false otherwise
     * @param bool   $objectSupport          True if object support is
enabled, false otherwise
     * @param bool   $objectForMap           True if maps should return a
stdClass instead of array()
     * @param string $context                The parser context (either
sequence or mapping)
     *
     * @return mixed A PHP value
     *
     * @throws ParseException When reference does not exist
     */
    private function parseValue($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context)
    {
        if (0 === strpos($value, '*')) {
            if (false !== $pos = strpos($value, '#')) {
                $value = substr($value, 1, $pos - 2);
            } else {
                $value = substr($value, 1);
            }

            if (!array_key_exists($value, $this->refs)) {
                throw new ParseException(sprintf('Reference
"%s" does not exist.', $value), $this->currentLineNb + 1,
$this->currentLine);
            }

            return $this->refs[$value];
        }

        if
(self::preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/',
$value, $matches)) {
            $modifiers = isset($matches['modifiers']) ?
$matches['modifiers'] : '';

            return
$this->parseBlockScalar($matches['separator'],
preg_replace('#\d+#', '', $modifiers), (int)
abs($modifiers));
        }

        try {
            $parsedValue = Inline::parse($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $this->refs);

            if ('mapping' === $context &&
'"' !== $value[0] && "'" !==
$value[0] && '[' !== $value[0] && '{'
!== $value[0] && '!' !== $value[0] && false !==
strpos($parsedValue, ': ')) {
                @trigger_error(sprintf('Using a colon in the unquoted
mapping value "%s" in line %d is deprecated since Symfony 2.8 and
will throw a ParseException in 3.0.', $value,
$this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);

                // to be thrown in 3.0
                // throw new ParseException('A colon cannot be used in
an unquoted mapping value.');
            }

            return $parsedValue;
        } catch (ParseException $e) {
            $e->setParsedLine($this->getRealCurrentLineNb() + 1);
            $e->setSnippet($this->currentLine);

            throw $e;
        }
    }

    /**
     * Parses a block scalar.
     *
     * @param string $style       The style indicator that was used to
begin this block scalar (| or >)
     * @param string $chomping    The chomping indicator that was used to
begin this block scalar (+ or -)
     * @param int    $indentation The indentation indicator that was used
to begin this block scalar
     *
     * @return string The text value
     */
    private function parseBlockScalar($style, $chomping = '',
$indentation = 0)
    {
        $notEOF = $this->moveToNextLine();
        if (!$notEOF) {
            return '';
        }

        $isCurrentLineBlank = $this->isCurrentLineBlank();
        $blockLines = array();

        // leading blank lines are consumed before determining indentation
        while ($notEOF && $isCurrentLineBlank) {
            // newline only if not EOF
            if ($notEOF = $this->moveToNextLine()) {
                $blockLines[] = '';
                $isCurrentLineBlank = $this->isCurrentLineBlank();
            }
        }

        // determine indentation if not specified
        if (0 === $indentation) {
            if (self::preg_match('/^ +/', $this->currentLine,
$matches)) {
                $indentation = strlen($matches[0]);
            }
        }

        if ($indentation > 0) {
            $pattern = sprintf('/^ {%d}(.*)$/', $indentation);

            while (
                $notEOF && (
                    $isCurrentLineBlank ||
                    self::preg_match($pattern, $this->currentLine,
$matches)
                )
            ) {
                if ($isCurrentLineBlank &&
strlen($this->currentLine) > $indentation) {
                    $blockLines[] = substr($this->currentLine,
$indentation);
                } elseif ($isCurrentLineBlank) {
                    $blockLines[] = '';
                } else {
                    $blockLines[] = $matches[1];
                }

                // newline only if not EOF
                if ($notEOF = $this->moveToNextLine()) {
                    $isCurrentLineBlank = $this->isCurrentLineBlank();
                }
            }
        } elseif ($notEOF) {
            $blockLines[] = '';
        }

        if ($notEOF) {
            $blockLines[] = '';
            $this->moveToPreviousLine();
        } elseif (!$notEOF &&
!$this->isCurrentLineLastLineInDocument()) {
            $blockLines[] = '';
        }

        // folded style
        if ('>' === $style) {
            $text = '';
            $previousLineIndented = false;
            $previousLineBlank = false;

            for ($i = 0, $blockLinesCount = count($blockLines); $i <
$blockLinesCount; ++$i) {
                if ('' === $blockLines[$i]) {
                    $text .= "\n";
                    $previousLineIndented = false;
                    $previousLineBlank = true;
                } elseif (' ' === $blockLines[$i][0]) {
                    $text .= "\n".$blockLines[$i];
                    $previousLineIndented = true;
                    $previousLineBlank = false;
                } elseif ($previousLineIndented) {
                    $text .= "\n".$blockLines[$i];
                    $previousLineIndented = false;
                    $previousLineBlank = false;
                } elseif ($previousLineBlank || 0 === $i) {
                    $text .= $blockLines[$i];
                    $previousLineIndented = false;
                    $previousLineBlank = false;
                } else {
                    $text .= ' '.$blockLines[$i];
                    $previousLineIndented = false;
                    $previousLineBlank = false;
                }
            }
        } else {
            $text = implode("\n", $blockLines);
        }

        // deal with trailing newlines
        if ('' === $chomping) {
            $text = preg_replace('/\n+$/', "\n",
$text);
        } elseif ('-' === $chomping) {
            $text = preg_replace('/\n+$/', '', $text);
        }

        return $text;
    }

    /**
     * Returns true if the next line is indented.
     *
     * @return bool Returns true if the next line is indented, false
otherwise
     */
    private function isNextLineIndented()
    {
        $currentIndentation = $this->getCurrentLineIndentation();
        $EOF = !$this->moveToNextLine();

        while (!$EOF && $this->isCurrentLineEmpty()) {
            $EOF = !$this->moveToNextLine();
        }

        if ($EOF) {
            return false;
        }

        $ret = $this->getCurrentLineIndentation() >
$currentIndentation;

        $this->moveToPreviousLine();

        return $ret;
    }

    /**
     * Returns true if the current line is blank or if it is a comment
line.
     *
     * @return bool Returns true if the current line is empty or if it is a
comment line, false otherwise
     */
    private function isCurrentLineEmpty()
    {
        return $this->isCurrentLineBlank() ||
$this->isCurrentLineComment();
    }

    /**
     * Returns true if the current line is blank.
     *
     * @return bool Returns true if the current line is blank, false
otherwise
     */
    private function isCurrentLineBlank()
    {
        return '' == trim($this->currentLine, ' ');
    }

    /**
     * Returns true if the current line is a comment line.
     *
     * @return bool Returns true if the current line is a comment line,
false otherwise
     */
    private function isCurrentLineComment()
    {
        //checking explicitly the first char of the trim is faster than
loops or strpos
        $ltrimmedLine = ltrim($this->currentLine, ' ');

        return '' !== $ltrimmedLine && '#' ===
$ltrimmedLine[0];
    }

    private function isCurrentLineLastLineInDocument()
    {
        return ($this->offset + $this->currentLineNb) >=
($this->totalNumberOfLines - 1);
    }

    /**
     * Cleanups a YAML string to be parsed.
     *
     * @param string $value The input YAML string
     *
     * @return string A cleaned up YAML string
     */
    private function cleanup($value)
    {
        $value = str_replace(array("\r\n", "\r"),
"\n", $value);

        // strip YAML header
        $count = 0;
        $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u',
'', $value, -1, $count);
        $this->offset += $count;

        // remove leading comments
        $trimmedValue = preg_replace('#^(\#.*?\n)+#s',
'', $value, -1, $count);
        if (1 == $count) {
            // items have been removed, update the offset
            $this->offset += substr_count($value, "\n") -
substr_count($trimmedValue, "\n");
            $value = $trimmedValue;
        }

        // remove start of the document marker (---)
        $trimmedValue = preg_replace('#^\-\-\-.*?\n#s',
'', $value, -1, $count);
        if (1 == $count) {
            // items have been removed, update the offset
            $this->offset += substr_count($value, "\n") -
substr_count($trimmedValue, "\n");
            $value = $trimmedValue;

            // remove end of the document marker (...)
            $value = preg_replace('#\.\.\.\s*$#', '',
$value);
        }

        return $value;
    }

    /**
     * Returns true if the next line starts unindented collection.
     *
     * @return bool Returns true if the next line starts unindented
collection, false otherwise
     */
    private function isNextLineUnIndentedCollection()
    {
        $currentIndentation = $this->getCurrentLineIndentation();
        $notEOF = $this->moveToNextLine();

        while ($notEOF && $this->isCurrentLineEmpty()) {
            $notEOF = $this->moveToNextLine();
        }

        if (false === $notEOF) {
            return false;
        }

        $ret = $this->getCurrentLineIndentation() ===
$currentIndentation &&
$this->isStringUnIndentedCollectionItem();

        $this->moveToPreviousLine();

        return $ret;
    }

    /**
     * Returns true if the string is un-indented collection item.
     *
     * @return bool Returns true if the string is un-indented collection
item, false otherwise
     */
    private function isStringUnIndentedCollectionItem()
    {
        return '-' === rtrim($this->currentLine) || 0 ===
strpos($this->currentLine, '- ');
    }

    /**
     * Tests whether or not the current line is the header of a block
scalar.
     *
     * @return bool
     */
    private function isBlockScalarHeader()
    {
        return (bool)
self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~',
$this->currentLine);
    }

    /**
     * A local wrapper for `preg_match` which will throw a ParseException
if there
     * is an internal error in the PCRE engine.
     *
     * This avoids us needing to check for "false" every time
PCRE is used
     * in the YAML engine
     *
     * @throws ParseException on a PCRE internal error
     *
     * @see preg_last_error()
     *
     * @internal
     */
    public static function preg_match($pattern, $subject, &$matches =
null, $flags = 0, $offset = 0)
    {
        if (false === $ret = preg_match($pattern, $subject, $matches,
$flags, $offset)) {
            switch (preg_last_error()) {
                case PREG_INTERNAL_ERROR:
                    $error = 'Internal PCRE error.';
                    break;
                case PREG_BACKTRACK_LIMIT_ERROR:
                    $error = 'pcre.backtrack_limit reached.';
                    break;
                case PREG_RECURSION_LIMIT_ERROR:
                    $error = 'pcre.recursion_limit reached.';
                    break;
                case PREG_BAD_UTF8_ERROR:
                    $error = 'Malformed UTF-8 data.';
                    break;
                case PREG_BAD_UTF8_OFFSET_ERROR:
                    $error = 'Offset doesn\'t correspond to the
begin of a valid UTF-8 code point.';
                    break;
                default:
                    $error = 'Error.';
            }

            throw new ParseException($error);
        }

        return $ret;
    }
}
vendor/rockettheme/toolbox/Compat/src/Yaml/Unescaper.php000064400000010515151166614550017427
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace RocketTheme\Toolbox\Compat\Yaml;

/**
 * Unescaper encapsulates unescaping rules for single and double-quoted
 * YAML strings.
 *
 * @author Matthew Lewinski <matthew@lewinski.org>
 *
 * @internal
 */
class Unescaper
{
    /**
     * Parser and Inline assume UTF-8 encoding, so escaped Unicode
characters
     * must be converted to that encoding.
     *
     * @deprecated since version 2.5, to be removed in 3.0
     *
     * @internal
     */
    const ENCODING = 'UTF-8';

    /**
     * Regex fragment that matches an escaped character in a double quoted
string.
     */
    const REGEX_ESCAPED_CHARACTER =
'\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)';

    /**
     * Unescapes a single quoted string.
     *
     * @param string $value A single quoted string
     *
     * @return string The unescaped string
     */
    public function unescapeSingleQuotedString($value)
    {
        return str_replace('\'\'', '\'',
$value);
    }

    /**
     * Unescapes a double quoted string.
     *
     * @param string $value A double quoted string
     *
     * @return string The unescaped string
     */
    public function unescapeDoubleQuotedString($value)
    {
        $self = $this;
        $callback = function ($match) use ($self) {
            return $self->unescapeCharacter($match[0]);
        };

        // evaluate the string
        return
preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u',
$callback, $value);
    }

    /**
     * Unescapes a character that was found in a double-quoted string.
     *
     * @param string $value An escaped character
     *
     * @return string The unescaped character
     *
     * @internal This method is public to be usable as callback. It should
not
     *           be used in user code. Should be changed in 3.0.
     */
    public function unescapeCharacter($value)
    {
        switch ($value[1]) {
            case '0':
                return "\x0";
            case 'a':
                return "\x7";
            case 'b':
                return "\x8";
            case 't':
                return "\t";
            case "\t":
                return "\t";
            case 'n':
                return "\n";
            case 'v':
                return "\xB";
            case 'f':
                return "\xC";
            case 'r':
                return "\r";
            case 'e':
                return "\x1B";
            case ' ':
                return ' ';
            case '"':
                return '"';
            case '/':
                return '/';
            case '\\':
                return '\\';
            case 'N':
                // U+0085 NEXT LINE
                return "\xC2\x85";
            case '_':
                // U+00A0 NO-BREAK SPACE
                return "\xC2\xA0";
            case 'L':
                // U+2028 LINE SEPARATOR
                return "\xE2\x80\xA8";
            case 'P':
                // U+2029 PARAGRAPH SEPARATOR
                return "\xE2\x80\xA9";
            case 'x':
                return self::utf8chr(hexdec(substr($value, 2, 2)));
            case 'u':
                return self::utf8chr(hexdec(substr($value, 2, 4)));
            case 'U':
                return self::utf8chr(hexdec(substr($value, 2, 8)));
            default:
                @trigger_error('Not escaping a backslash in a
double-quoted string is deprecated since Symfony 2.8 and will throw a
ParseException in 3.0.', E_USER_DEPRECATED);

                return $value;
        }
    }

    /**
     * Get the UTF-8 character for the given code point.
     *
     * @param int $c The unicode code point
     *
     * @return string The corresponding UTF-8 character
     */
    private static function utf8chr($c)
    {
        if (0x80 > $c %= 0x200000) {
            return chr($c);
        }
        if (0x800 > $c) {
            return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
        }
        if (0x10000 > $c) {
            return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6
& 0x3F).chr(0x80 | $c & 0x3F);
        }

        return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 &
0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
    }
}
vendor/rockettheme/toolbox/Compat/src/Yaml/Yaml.php000064400000005137151166614550016410
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace RocketTheme\Toolbox\Compat\Yaml;

use RocketTheme\Toolbox\Compat\Yaml\Exception\ParseException;

/**
 * Yaml offers convenience methods to load and dump YAML.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Yaml
{
    /**
     * Parses YAML into a PHP value.
     *
     *  Usage:
     *  <code>
     *   $array = Yaml::parse(file_get_contents('config.yml'));
     *   print_r($array);
     *  </code>
     *
     * As this method accepts both plain strings and file names as an
input,
     * you must validate the input before calling this method. Passing a
file
     * as an input is a deprecated feature and will be removed in 3.0.
     *
     * Note: the ability to pass file names to the Yaml::parse method is
deprecated since version 2.2 and will be removed in 3.0. Pass the YAML
contents of the file instead.
     *
     * @param string $input                  Path to a YAML file or a
string containing YAML
     * @param bool   $exceptionOnInvalidType True if an exception must be
thrown on invalid types false otherwise
     * @param bool   $objectSupport          True if object support is
enabled, false otherwise
     * @param bool   $objectForMap           True if maps should return a
stdClass instead of array()
     *
     * @return mixed The YAML converted to a PHP value
     *
     * @throws ParseException If the YAML is not valid
     */
    public static function parse($input, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
    {
        // if input is a file, process it
        $file = '';
        if (false === strpos($input, "\n") &&
is_file($input)) {
            @trigger_error('The ability to pass file names to the
'.__METHOD__.' method is deprecated since version 2.2 and will be
removed in 3.0. Pass the YAML contents of the file instead.',
E_USER_DEPRECATED);

            if (false === is_readable($input)) {
                throw new ParseException(sprintf('Unable to parse
"%s" as the file is not readable.', $input));
            }

            $file = $input;
            $input = file_get_contents($file);
        }

        $yaml = new Parser();

        try {
            return $yaml->parse($input, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
        } catch (ParseException $e) {
            if ($file) {
                $e->setParsedFile($file);
            }

            throw $e;
        }
    }
}
vendor/rockettheme/toolbox/composer.json000064400000002304151166614550014574
0ustar00{
    "name": "rockettheme/toolbox",
    "type": "library",
    "description": "RocketTheme Toolbox Library",
    "keywords": ["rockettheme", "php"],
    "homepage": "http://www.rockettheme.com",
    "license": "MIT",
    "require": {
        "php": ">=5.4.0",
        "ext-json": "*",
        "pimple/pimple": "~3.0",
        "symfony/yaml": ">2.5",
        "symfony/event-dispatcher": ">2.5"
    },
    "require-dev": {
        "phpunit/phpunit": "~6"
    },
    "autoload": {
        "psr-4": {
            "RocketTheme\\Toolbox\\ArrayTraits\\":
"ArrayTraits/src",
            "RocketTheme\\Toolbox\\Blueprints\\":
"Blueprints/src",
            "RocketTheme\\Toolbox\\Compat\\":
"Compat/src",
            "RocketTheme\\Toolbox\\DI\\": "DI/src",
            "RocketTheme\\Toolbox\\Event\\":
"Event/src",
            "RocketTheme\\Toolbox\\File\\": "File/src",
            "RocketTheme\\Toolbox\\ResourceLocator\\":
"ResourceLocator/src",
            "RocketTheme\\Toolbox\\Session\\":
"Session/src",
            "RocketTheme\\Toolbox\\StreamWrapper\\":
"StreamWrapper/src"
        }
    },
    "scripts": {
        "test": "vendor/bin/phpunit run unit",
        "test-windows": "vendor\\bin\\phpunit run unit"
    }
}
vendor/rockettheme/toolbox/DI/src/Container.php000064400000000376151166614550015577
0ustar00<?php
namespace RocketTheme\Toolbox\DI;

use Pimple\Container as BaseContainer;

/**
 * Implements Dependency Injection Container.
 *
 * @package RocketTheme\Toolbox\DI
 * @author RocketTheme
 * @license MIT
 */
class Container extends BaseContainer
{
}
vendor/rockettheme/toolbox/DI/src/ServiceProviderInterface.php000064400000000466151166614550020611
0ustar00<?php
namespace RocketTheme\Toolbox\DI;

use \Pimple\ServiceProviderInterface as BaseServiceProviderInterface;

/**
 * Defines ServiceProviderInterface.
 *
 * @package RocketTheme\Toolbox\DI
 * @author RocketTheme
 * @license MIT
 */
interface ServiceProviderInterface extends BaseServiceProviderInterface
{
}
vendor/rockettheme/toolbox/Event/src/Event.php000064400000001026151166614550015514
0ustar00<?php
namespace RocketTheme\Toolbox\Event;

use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Constructor;
use RocketTheme\Toolbox\ArrayTraits\Export;
use Symfony\Component\EventDispatcher\Event as BaseEvent;

/**
 * Implements Symfony Event interface.
 *
 * @package RocketTheme\Toolbox\Event
 * @author RocketTheme
 * @license MIT
 */
class Event extends BaseEvent implements \ArrayAccess
{
    use ArrayAccess, Constructor, Export;

    /**
     * @var array
     */
    protected $items = [];
}
vendor/rockettheme/toolbox/Event/src/EventDispatcher.php000064400000001247151166614550017530
0ustar00<?php
namespace RocketTheme\Toolbox\Event;

use Symfony\Component\EventDispatcher\Event as BaseEvent;
use Symfony\Component\EventDispatcher\EventDispatcher as
BaseEventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Implements Symfony EventDispatcher interface.
 *
 * @package RocketTheme\Toolbox\Event
 * @author RocketTheme
 * @license MIT
 */
class EventDispatcher extends BaseEventDispatcher implements
EventDispatcherInterface
{
    public function dispatch($eventName, BaseEvent $event = null)
    {
        if (null === $event) {
            $event = new Event();
        }

        return parent::dispatch($eventName, $event);
    }
}
vendor/rockettheme/toolbox/Event/src/EventSubscriberInterface.php000064400000000526151166614550021365
0ustar00<?php
namespace RocketTheme\Toolbox\Event;

use Symfony\Component\EventDispatcher\EventSubscriberInterface as
BaseEventSubscriberInterface;

/**
 * Defines EventSubscriberInterface.
 *
 * @package RocketTheme\Toolbox\Event
 * @author RocketTheme
 * @license MIT
 */
interface EventSubscriberInterface extends BaseEventSubscriberInterface
{
}
vendor/rockettheme/toolbox/File/src/File.php000064400000025051151166614550015114
0ustar00<?php
namespace RocketTheme\Toolbox\File;

/**
 * Implements Universal File Reader.
 *
 * @package RocketTheme\Toolbox\File
 * @author RocketTheme
 * @license MIT
 */
class File implements FileInterface
{
    /**
     * @var string
     */
    protected $filename;

    /**
     * @var resource
     */
    protected $handle;

    /**
     * @var bool|null
     */
    protected $locked;

    /**
     * @var string
     */
    protected $extension;

    /**
     * @var string  Raw file contents.
     */
    protected $raw;

    /**
     * @var array  Parsed file contents.
     */
    protected $content;

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

    /**
     * @var array|File[]
     */
    static protected $instances = [];

    /**
     * Get file instance.
     *
     * @param  string  $filename
     * @return static
     */
    public static function instance($filename)
    {
        if (!\is_string($filename) && $filename) {
            throw new \InvalidArgumentException('Filename should be
non-empty string');
        }
        if (!isset(static::$instances[$filename])) {
            static::$instances[$filename] = new static;
            static::$instances[$filename]->init($filename);
        }
        return static::$instances[$filename];
    }

    /**
     * Set/get settings.
     *
     * @param array $settings
     * @return array
     */
    public function settings(array $settings = null)
    {
        if ($settings !== null) {
            $this->settings = $settings;
        }

        return $this->settings;
    }

    /**
     * Get setting.
     *
     * @param string $setting
     * @param mixed $default
     * @return mixed
     */
    public function setting($setting, $default = null)
    {
        return isset($this->settings[$setting]) ?
$this->settings[$setting] : $default;
    }

    /**
     * Prevent constructor from being used.
     */
    protected function __construct()
    {
    }

    /**
     * Prevent cloning.
     */
    protected function __clone()
    {
        //Me not like clones! Me smash clones!
    }

    /**
     * Set filename.
     *
     * @param $filename
     */
    protected function init($filename)
    {
        $this->filename = $filename;
    }

    /**
     * Free the file instance.
     */
    public function free()
    {
        if ($this->locked) {
            $this->unlock();
        }
        $this->content = null;
        $this->raw = null;

        unset(static::$instances[$this->filename]);
    }

    /**
     * Get/set the file location.
     *
     * @param  string $var
     * @return string
     */
    public function filename($var = null)
    {
        if ($var !== null) {
            $this->filename = $var;
        }
        return $this->filename;
    }

    /**
     * Return basename of the file.
     *
     * @return string
     */
    public function basename()
    {
        return basename($this->filename, $this->extension);
    }

    /**
     * Check if file exits.
     *
     * @return bool
     */
    public function exists()
    {
        return is_file($this->filename);
    }

    /**
     * Return file modification time.
     *
     * @return int|bool Timestamp or false if file doesn't exist.
     */
    public function modified()
    {
        return is_file($this->filename) ? filemtime($this->filename)
: false;
    }

    /**
     * Lock file for writing. You need to manually unlock().
     *
     * @param bool $block  For non-blocking lock, set the parameter to
false.
     * @return bool
     * @throws \RuntimeException
     */
    public function lock($block = true)
    {
        if (!$this->handle) {
            if (!$this->mkdir(\dirname($this->filename))) {
                throw new \RuntimeException('Creating directory failed
for ' . $this->filename);
            }
            $this->handle = @fopen($this->filename, 'cb+');
            if (!$this->handle) {
                $error = error_get_last();

                throw new \RuntimeException("Opening file for writing
failed on error {$error['message']}");
            }
        }
        $lock = $block ? LOCK_EX : LOCK_EX | LOCK_NB;

        // Some filesystems do not support file locks, only fail if another
process holds the lock.
        $this->locked = flock($this->handle, $lock, $wouldblock) ||
!$wouldblock;

        return $this->locked;
    }

    /**
     * Returns true if file has been locked for writing.
     *
     * @return bool|null True = locked, false = failed, null = not locked.
     */
    public function locked()
    {
        return $this->locked;
    }

    /**
     * Unlock file.
     *
     * @return bool
     */
    public function unlock()
    {
        if (!$this->handle) {
            return false;
        }
        if ($this->locked) {
            flock($this->handle, LOCK_UN);
            $this->locked = null;
        }
        fclose($this->handle);
        $this->handle = null;

        return true;
    }

    /**
     * Check if file can be written.
     *
     * @return bool
     */
    public function writable()
    {
        return file_exists($this->filename) ?
is_writable($this->filename) && is_file($this->filename) :
$this->writableDir(\dirname($this->filename));
    }

    /**
     * (Re)Load a file and return RAW file contents.
     *
     * @return string
     */
    public function load()
    {
        $this->raw = $this->exists() ? (string)
file_get_contents($this->filename) : '';
        $this->content = null;

        return $this->raw;
    }

    /**
     * Get/set raw file contents.
     *
     * @param string $var
     * @return string
     */
    public function raw($var = null)
    {
        if ($var !== null) {
            $this->raw = (string) $var;
            $this->content = null;
        }

        if (!\is_string($this->raw)) {
            $this->raw = $this->load();
        }

        return $this->raw;
    }

    /**
     * Get/set parsed file contents.
     *
     * @param mixed $var
     * @return string|array
     * @throws \RuntimeException
     */
    public function content($var = null)
    {
        if ($var !== null) {
            $this->content = $this->check($var);

            // Update RAW, too.
            $this->raw = $this->encode($this->content);

        } elseif ($this->content === null) {
            // Decode RAW file.
            try {
                $this->content = $this->decode($this->raw());
            } catch (\Exception $e) {
                throw new \RuntimeException(sprintf('Failed to read
%s: %s', $this->filename, $e->getMessage()), 500, $e);
            }
        }

        return $this->content;
    }

    /**
     * Save file.
     *
     * @param  mixed  $data  Optional data to be saved, usually array.
     * @throws \RuntimeException
     */
    public function save($data = null)
    {
        if ($data !== null) {
            $this->content($data);
        }

        $filename = $this->filename;
        $dir = \dirname($filename);

        if (!$this->mkdir($dir)) {
            throw new \RuntimeException('Creating directory failed for
' . $filename);
        }

        try {
            if ($this->handle) {
                $tmp = true;
                // As we are using non-truncating locking, make sure that
the file is empty before writing.
                if (@ftruncate($this->handle, 0) === false ||
@fwrite($this->handle, $this->raw()) === false) {
                    // Writing file failed, throw an error.
                    $tmp = false;
                }
            } else {
                // Create file with a temporary name and rename it to make
the save action atomic.
                $tmp = $this->tempname($filename);
                if (file_put_contents($tmp, $this->raw()) === false) {
                    $tmp = false;
                } elseif (@rename($tmp, $filename) === false) {
                    @unlink($tmp);
                    $tmp = false;
                }
            }
        } catch (\Exception $e) {
            $tmp = false;
        }

        if ($tmp === false) {
            throw new \RuntimeException('Failed to save file ' .
$filename);
        }

        // Touch the directory as well, thus marking it modified.
        @touch($dir);
    }

    /**
     * Rename file in the filesystem if it exists.
     *
     * @param $filename
     * @return bool
     */
    public function rename($filename)
    {
        if ($this->exists() && !@rename($this->filename,
$filename)) {
            return false;
        }

        unset(static::$instances[$this->filename]);
        static::$instances[$filename] = $this;

        $this->filename = $filename;

        return true;
    }

    /**
     * Delete file from filesystem.
     *
     * @return bool
     */
    public function delete()
    {
        return unlink($this->filename);
    }

    /**
     * Check contents and make sure it is in correct format.
     *
     * Override in derived class.
     *
     * @param string $var
     * @return string
     */
    protected function check($var)
    {
        return (string) $var;
    }

    /**
     * Encode contents into RAW string.
     *
     * Override in derived class.
     *
     * @param string $var
     * @return string
     */
    protected function encode($var)
    {
        return (string) $var;
    }

    /**
     * Decode RAW string into contents.
     *
     * Override in derived class.
     *
     * @param string $var
     * @return string mixed
     */
    protected function decode($var)
    {
        return (string) $var;
    }

    /**
     * @param  string  $dir
     */
    private function mkdir($dir)
    {
        // Silence error for open_basedir; should fail in mkdir instead.
        if (@is_dir($dir)) {
            return true;
        }

        $success = @mkdir($dir, 0777, true);

        if (!$success) {
            // Take yet another look, make sure that the folder
doesn't exist.
            clearstatcache(true, $dir);
            if (!@is_dir($dir)) {
                return false;
            }
        }

        return true;
    }

    /**
     * @param  string  $dir
     * @return bool
     * @internal
     */
    protected function writableDir($dir)
    {
        if ($dir && !file_exists($dir)) {
            return $this->writableDir(\dirname($dir));
        }

        return $dir && is_dir($dir) && is_writable($dir);
    }

    /**
     * @param string $filename
     * @param int $length
     * @return string
     */
    protected function tempname($filename, $length = 5)
    {
        do {
            $test = $filename .
substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'),
0, $length);
        } while (file_exists($test));

        return $test;
    }
}
vendor/rockettheme/toolbox/File/src/FileInterface.php000064400000003602151166614550016733
0ustar00<?php
namespace RocketTheme\Toolbox\File;

/**
 * Defines FileInterface.
 *
 * @package RocketTheme\Toolbox\File
 * @author RocketTheme
 * @license MIT
 */
interface FileInterface
{
    /**
     * Get file instance.
     *
     * @param  string  $filename
     * @return static
     */
    public static function instance($filename);

    /**
     * Free the file instance.
     */
    public function free();

    /**
     * Check if file exits.
     *
     * @return bool
     */
    public function exists();

    /**
     * Return file modification time.
     *
     * @return int Timestamp
     */
    public function modified();

    /**
     * Lock file for writing.
     *
     * @param bool $block  For non-blocking lock, set the parameter to
false.
     * @return bool
     */
    public function lock($block = true);

    /**
     * Returns true if file has been locked for writing.
     *
     * @return bool|null True = locked, false = failed, null = not locked.
     */
    public function locked();

    /**
     * Unlock file.
     *
     * @return bool
     */
    public function unlock();

    /**
     * Check if file can be written.
     *
     * @return bool
     */
    public function writable();

    /**
     * (Re)Load a file and return its contents.
     *
     * @return string
     */
    public function load();

    /**
     * Get/set raw file contents.
     *
     * @param string $var
     * @return string
     */
    public function raw($var = null);

    /**
     * Get/set parsed file contents.
     *
     * @param string $var
     * @return string
     */
    public function content($var = null);

    /**
     * Save file.
     *
     * @param  string  $data  Optional data to be saved.
     * @throws \RuntimeException
     */
    public function save($data = null);

    /**
     * Delete file from filesystem.
     *
     * @return bool
     */
    public function delete();
}
vendor/rockettheme/toolbox/File/src/IniFile.php000064400000003200151166614550015544
0ustar00<?php
namespace RocketTheme\Toolbox\File;

/**
 * Implements INI File reader.
 *
 * @package RocketTheme\Toolbox\File
 * @author RocketTheme
 * @license MIT
 */
class IniFile extends File
{
    /**
     * @var string
     */
    protected $extension = '.ini';

    /**
     * @var array|File[]
     */
    static protected $instances = [];

    /**
     * Check contents and make sure it is in correct format.
     *
     * @param array $var
     * @return array
     * @throws \RuntimeException
     */
    protected function check($var)
    {
        if (!\is_array($var)) {
            throw new \RuntimeException('Provided data is not an
array');
        }

        return $var;
    }

    /**
     * Encode configuration object into RAW string (INI).
     *
     * @param array $var
     * @return string
     * @throws \RuntimeException
     */
    protected function encode($var)
    {
        $string = '';
        foreach ($var as $key => $value) {
            $string .= $key . '="' .  preg_replace(
                    ['/"/', '/\\\/',
"/\t/", "/\n/", "/\r/"],
                    ['\"',  '\\\\',
'\t',   '\n',   '\r'],
                    $value
                ) . "\"\n";
        }
        return $string;
    }

    /**
     * Decode INI file into contents.
     *
     * @param string $var
     * @return array
     * @throws \RuntimeException
     */
    protected function decode($var)
    {
        $decoded = file_exists($this->filename) ?
@parse_ini_file($this->filename) : [];

        if ($decoded === false) {
            throw new \RuntimeException("Decoding file
'{$this->filename}' failed'");
        }

        return $decoded;
    }
}
vendor/rockettheme/toolbox/File/src/JsonFile.php000064400000002047151166614550015746
0ustar00<?php
namespace RocketTheme\Toolbox\File;

/**
 * Implements Json File reader.
 *
 * @package RocketTheme\Toolbox\File
 * @author RocketTheme
 * @license MIT
 */
class JsonFile extends File
{
    /**
     * @var string
     */
    protected $extension = '.json';

    /**
     * @var array|File[]
     */
    static protected $instances = [];

    /**
     * Check contents and make sure it is in correct format.
     *
     * @param array $var
     * @return array
     */
    protected function check($var)
    {
        return (array) $var;
    }

    /**
     * Encode contents into RAW string.
     *
     * @param string $var
     * @param int $options
     * @return string
     */
    protected function encode($var, $options = 0)
    {
        return (string) json_encode($var, $options);
    }

    /**
     * Decode RAW string into contents.
     *
     * @param string $var
     * @param bool $assoc
     * @return array mixed
     */
    protected function decode($var, $assoc = false)
    {
        return (array) json_decode($var, $assoc);
    }
}
vendor/rockettheme/toolbox/File/src/LogFile.php000064400000002734151166614550015561
0ustar00<?php
namespace RocketTheme\Toolbox\File;

/**
 * Implements Log File reader.
 *
 * @package RocketTheme\Toolbox\File
 * @author RocketTheme
 * @license MIT
 */
class LogFile extends File
{
    /**
     * @var array|File[]
     */
    static protected $instances = [];

    /**
     * Constructor.
     */
    protected function __construct()
    {
        parent::__construct();

        $this->extension = '.log';
    }

    /**
     * Check contents and make sure it is in correct format.
     *
     * @param array $var
     * @return array
     */
    protected function check($var)
    {
        return (array) $var;
    }

    /**
     * Encode contents into RAW string (unsupported).
     *
     * @param string $var
     * @return string|void
     * @throws \BadMethodCallException
     */
    protected function encode($var)
    {
        throw new \BadMethodCallException('Saving log file is
forbidden.');
    }

    /**
     * Decode RAW string into contents.
     *
     * @param string $var
     * @return array mixed
     */
    protected function decode($var)
    {
        $lines = (array) preg_split('#(\r\n|\n|\r)#', $var);

        $results = array();
        foreach ($lines as $line) {
            preg_match('#^\[(.*)\] (.*)  @  (.*)  @@  (.*)$#',
$line, $matches);
            if ($matches) {
                $results[] = ['date' => $matches[1],
'message' => $matches[2], 'url' => $matches[3],
'file' => $matches[4]];
            }
        }

        return $results;
    }
}
vendor/rockettheme/toolbox/File/src/MarkdownFile.php000064400000010435151166614550016617
0ustar00<?php
namespace RocketTheme\Toolbox\File;

use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml as YamlParser;
use RocketTheme\Toolbox\Compat\Yaml\Yaml as FallbackYamlParser;

/**
 * Implements Markdown File reader.
 *
 * @package RocketTheme\Toolbox\File
 * @author RocketTheme
 * @license MIT
 */
class MarkdownFile extends File
{
    /**
     * @var string
     */
    protected $extension = '.md';

    /**
     * @var array|File[]
     */
    static protected $instances = [];

    /**
     * Get/set file header.
     *
     * @param array $var
     *
     * @return array
     */
    public function header(array $var = null)
    {
        $content = $this->content();

        if ($var !== null) {
            $content['header'] = $var;
            $this->content($content);
        }

        return $content['header'];
    }

    /**
     * Get/set markdown content.
     *
     * @param string $var
     *
     * @return string
     */
    public function markdown($var = null)
    {
        $content = $this->content();

        if ($var !== null) {
            $content['markdown'] = (string) $var;
            $this->content($content);
        }

        return $content['markdown'];
    }

    /**
     * Get/set frontmatter content.
     *
     * @param string $var
     *
     * @return string
     */
    public function frontmatter($var = null)
    {
        $content = $this->content();

        if ($var !== null) {
            $content['frontmatter'] = (string) $var;
            $this->content($content);
        }

        return $content['frontmatter'];
    }

    /**
     * Check contents and make sure it is in correct format.
     *
     * @param array $var
     * @return array
     */
    protected function check($var)
    {
        $var = (array) $var;
        if (!isset($var['header']) ||
!\is_array($var['header'])) {
            $var['header'] = array();
        }
        if (!isset($var['markdown']) ||
!\is_string($var['markdown'])) {
            $var['markdown'] = '';
        }

        return $var;
    }

    /**
     * Encode contents into RAW string.
     *
     * @param array $var
     * @return string
     */
    protected function encode($var)
    {
        // Create Markdown file with YAML header.
        $o = (!empty($var['header']) ? "---\n" .
trim(YamlParser::dump($var['header'], 20)) .
"\n---\n\n" : '') . $var['markdown'];

        // Normalize line endings to Unix style.
        $o = preg_replace("/(\r\n|\r)/", "\n", $o);

        return $o;
    }

    /**
     * Decode RAW string into contents.
     *
     * @param string $var
     * @return array mixed
     */
    protected function decode($var)
    {
        $content = [
            'header' => false,
            'frontmatter' => ''
        ];

        $frontmatter_regex = "/^---\n(.+?)\n---\n{0,}(.*)$/uis";

        // Normalize line endings to Unix style.
        $var = preg_replace("/(\r\n|\r)/", "\n", $var);

        // Parse header.
        preg_match($frontmatter_regex, ltrim($var), $m);
        if(!empty($m)) {
            // Normalize frontmatter.
            $content['frontmatter'] = $frontmatter =
preg_replace("/\n\t/", "\n    ", $m[1]);

            // Try native PECL YAML PHP extension first if available.
            if (\function_exists('yaml_parse') &&
$this->setting('native')) {
                // Safely decode YAML.
                $saved = @ini_get('yaml.decode_php');
                @ini_set('yaml.decode_php', 0);
                $content['header'] =
@yaml_parse("---\n" . $frontmatter . "\n...");
                @ini_set('yaml.decode_php', $saved);
            }

            if ($content['header'] === false) {
                // YAML hasn't been parsed yet (error or extension
isn't available). Fall back to Symfony parser.
                try {
                    $content['header'] = (array)
YamlParser::parse($frontmatter);
                } catch (ParseException $e) {
                    if (!$this->setting('compat', true)) {
                        throw $e;
                    }
                    $content['header'] = (array)
FallbackYamlParser::parse($frontmatter);
                }
            }
            $content['markdown'] = $m[2];
        } else {
            $content['header'] = [];
            $content['markdown'] = $var;
        }

        return $content;
    }
}
vendor/rockettheme/toolbox/File/src/MoFile.php000064400000007137151166614550015415
0ustar00<?php
namespace RocketTheme\Toolbox\File;

/**
 * Implements Gettext Mo File reader (readonly).
 *
 * @package RocketTheme\Toolbox\File
 * @author RocketTheme
 * @license MIT
 */
class MoFile extends File
{
    /**
     * @var string
     */
    protected $extension = '.mo';

    protected $pos = 0;
    protected $str;
    protected $len;
    protected $endian;

    /**
     * @var array|File[]
     */
    static protected $instances = [];

    /**
     * File can never be written.
     *
     * @return bool
     */
    public function writable()
    {
        return false;
    }

    /**
     * Prevent saving file.
     *
     * @throws \BadMethodCallException
     */
    public function save($data = null)
    {
        throw new \BadMethodCallException('save() not supported for
.mo files.');
    }

    /**
     * Prevent deleting file from filesystem.
     *
     * @return bool
     */
    public function delete()
    {
        return false;
    }

    /**
     * @param $var
     * @return array
     * @throws \RuntimeException
     */
    public function decode($var)
    {
        $this->endian = 'V';
        $this->str = $var;
        $this->len = \strlen($var);

        $magic = $this->readInt() & 0xffffffff;

        if ($magic === 0x950412de) {
            // Low endian.
            $this->endian = 'V';
        } elseif ($magic === 0xde120495) {
            // Big endian.
            $this->endian = 'N';
        } else {
            throw new \RuntimeException('Not a Gettext file
(.mo).');
        }

        // Skip revision number.
        $rev = $this->readInt();
        // Total count.
        $total = $this->readInt();
        // Offset of original table.
        $originals = $this->readInt();
        // Offset of translation table.
        $translations = $this->readInt();

        // Each table consists of string length and offset of the string.
        $this->seek($originals);
        $table_originals = $this->readIntArray($total * 2);
        $this->seek($translations);
        $table_translations = $this->readIntArray($total * 2);

        $items = [];
        for ($i = 0; $i < $total; $i++) {
            $this->seek($table_originals[$i * 2 + 2]);

            // TODO: Original string can have context concatenated on it.
We do not yet support that.
            $original = $this->read($table_originals[$i * 2 + 1]);

            if ($original) {
                $this->seek($table_translations[$i * 2 + 2]);

                // TODO: Plural forms are stored by letting the plural of
the original string follow the singular of the original string, separated
through a NUL byte.
                $translated = $this->read($table_translations[$i * 2 +
1]);
                $items[$original] = $translated;
            }
        }

        return $items;
    }

    /**
     * @return int
     */
    protected function readInt()
    {
        $read = $this->read(4);

        if ($read === false) {
            return false;
        }

        $read = unpack($this->endian, $read);

        return array_shift($read);
    }

    /**
     * @param $count
     * @return array
     */
    protected function readIntArray($count)
    {
        return unpack($this->endian . $count, $this->read(4 *
$count));
    }

    /**
     * @param $bytes
     * @return string
     */
    private function read($bytes)
    {
        $data = substr($this->str, $this->pos, $bytes);
        $this->seek($this->pos + $bytes);
        return $data;
    }

    /**
     * @param $pos
     * @return mixed
     */
    private function seek($pos)
    {
        $this->pos = $pos < $this->len ? $pos : $this->len;
        return $this->pos;
    }
}
vendor/rockettheme/toolbox/File/src/PhpFile.php000064400000005030151166614550015557
0ustar00<?php
namespace RocketTheme\Toolbox\File;

/**
 * Implements PHP File reader.
 *
 * @package RocketTheme\Toolbox\File
 * @author RocketTheme
 * @license MIT
 */
class PhpFile extends File
{
    /**
     * @var string
     */
    protected $extension = '.php';

    /**
     * @var array|File[]
     */
    static protected $instances = [];

    /**
     * Saves PHP file and invalidates opcache.
     *
     * @param  mixed  $data  Optional data to be saved, usually array.
     * @throws \RuntimeException
     */
    public function save($data = null)
    {
        parent::save($data);

        // Invalidate configuration file from the opcache.
        if (\function_exists('opcache_invalidate')) {
            // PHP 5.5.5+
            @opcache_invalidate($this->filename, true);
        } elseif (\function_exists('apc_invalidate')) {
            // APC
            @apc_invalidate($this->filename);
        }
    }

    /**
     * Check contents and make sure it is in correct format.
     *
     * @param array|object $var
     * @return array
     * @throws \RuntimeException
     */
    protected function check($var)
    {
        if (!(\is_array($var) || \is_object($var))) {
            throw new \RuntimeException('Provided data is not an
array');
        }

        return $var;
    }

    /**
     * Encode configuration object into RAW string (PHP class).
     *
     * @param array $var
     * @return string
     * @throws \RuntimeException
     */
    protected function encode($var)
    {
        // Build the object variables string
        return "<?php\nreturn {$this->encodeArray((array)
$var)};\n";
    }

    /**
     * Method to get an array as an exported string.
     *
     * @param array $a      The array to get as a string.
     * @param int   $level  Used internally to indent rows.
     *
     * @return string
     */
    protected function encodeArray(array $a, $level = 0)
    {
        $r = [];
        foreach ($a as $k => $v) {
            if (\is_array($v) || \is_object($v)) {
                $r[] = var_export($k, true) . ' => ' .
$this->encodeArray((array) $v, $level + 1);
            } else {
                $r[] = var_export($k, true) . ' => ' .
var_export($v, true);
            }
        }

        $space = str_repeat('    ', $level);
        return "[\n    {$space}" . implode(",\n   
{$space}", $r) . "\n{$space}]";
    }

    /**
     * Decode PHP file into contents.
     *
     * @param string $var
     * @return array
     */
    protected function decode($var)
    {
        return (array) include $this->filename;
    }
}
vendor/rockettheme/toolbox/File/src/YamlFile.php000064400000006213151166614550015736
0ustar00<?php
namespace RocketTheme\Toolbox\File;

use Symfony\Component\Yaml\Exception\DumpException;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml as YamlParser;
use RocketTheme\Toolbox\Compat\Yaml\Yaml as FallbackYamlParser;

/**
 * Implements YAML File reader.
 *
 * @package RocketTheme\Toolbox\File
 * @author RocketTheme
 * @license MIT
 */
class YamlFile extends File
{
    /**
     * @var array|File[]
     */
    static protected $instances = [];

    static protected $globalSettings = [
        'compat' => true,
        'native' => true
    ];

    /**
     * Set/get settings.
     *
     * @param array $settings
     * @return array
     */
    public static function globalSettings(array $settings = null)
    {
        if ($settings !== null) {
            static::$globalSettings = $settings;
        }

        return static::$globalSettings;
    }

    /**
     * Constructor.
     */
    protected function __construct()
    {
        parent::__construct();

        $this->extension = '.yaml';
    }

    /**
     * Set/get settings.
     *
     * @param array $settings
     * @return array
     */
    public function settings(array $settings = null)
    {
        if ($settings !== null) {
            $this->settings = $settings;
        }

        return $this->settings + static::$globalSettings;
    }

    /**
     * Get setting.
     *
     * @param string $setting
     * @param mixed $default
     * @return mixed
     */
    public function setting($setting, $default = null)
    {
        $value = parent::setting($setting);
        if (null === $value) {
            $value = isset(static::$globalSettings[$setting]) ?
static::$globalSettings[$setting] : $default;
        }

        return $value;
    }

    /**
     * Check contents and make sure it is in correct format.
     *
     * @param array $var
     * @return array
     */
    protected function check($var)
    {
        return (array) $var;
    }

    /**
     * Encode contents into RAW string.
     *
     * @param array $var
     * @return string
     * @throws DumpException
     */
    protected function encode($var)
    {
        return (string) YamlParser::dump($var,
$this->setting('inline', 5),
$this->setting('indent', 2), true, false);
    }

    /**
     * Decode RAW string into contents.
     *
     * @param string $var
     * @return array mixed
     * @throws ParseException
     */
    protected function decode($var)
    {
        // Try native PECL YAML PHP extension first if available.
        if (\function_exists('yaml_parse') &&
$this->setting('native', true)) {
            // Safely decode YAML.
            $saved = @ini_get('yaml.decode_php');
            @ini_set('yaml.decode_php', 0);
            $data = @yaml_parse($var);
            @ini_set('yaml.decode_php', $saved);

            if ($data !== false) {
                return (array) $data;
            }
        }

        try {
            return (array) YamlParser::parse($var);
        } catch (ParseException $e) {
            if ($this->setting('compat', true)) {
                return (array) FallbackYamlParser::parse($var);
            }

            throw $e;
        }
    }
}
vendor/rockettheme/toolbox/ResourceLocator/src/RecursiveUniformResourceIterator.php000064400000002315151166614550025220
0ustar00<?php
namespace RocketTheme\Toolbox\ResourceLocator;

/**
 * Implements recursive iterator over filesystem.
 *
 * @package RocketTheme\Toolbox\ResourceLocator
 * @author RocketTheme
 * @license MIT
 */
class RecursiveUniformResourceIterator extends UniformResourceIterator
implements \SeekableIterator, \RecursiveIterator
{
    protected $subPath;

    public function getChildren()
    {
        $subPath = $this->getSubPathName();

        return (new static($this->getUrl(), $this->flags,
$this->locator))->setSubPath($subPath);
    }

    public function hasChildren($allow_links = null)
    {
        $allow_links = (bool) ($allow_links !== null ? $allow_links :
$this->flags & \FilesystemIterator::FOLLOW_SYMLINKS);

        return $this->iterator && $this->isDir() &&
!$this->isDot() && ($allow_links || !$this->isLink());
    }

    public function getSubPath()
    {
        return $this->subPath;
    }

    public function getSubPathName()
    {
        return ($this->subPath ? $this->subPath . '/' :
'') . $this->getFilename();
    }

    /**
     * @param $path
     * @return $this
     * @internal
     */
    public function setSubPath($path)
    {
        $this->subPath = $path;

        return $this;
    }
}
vendor/rockettheme/toolbox/ResourceLocator/src/ResourceLocatorInterface.php000064400000001647151166614550023432
0ustar00<?php

namespace RocketTheme\Toolbox\ResourceLocator;

/**
 * Defines ResourceLocatorInterface.
 *
 * @package RocketTheme\Toolbox\ResourceLocator
 * @author RocketTheme
 * @license MIT
 */
interface ResourceLocatorInterface
{
    /**
     * Alias for findResource()
     *
     * @param $uri
     * @return string|bool
     */
    public function __invoke($uri);

    /**
     * Returns true if uri is resolvable by using locator.
     *
     * @param  string $uri
     * @return bool
     */
    public function isStream($uri);

    /**
     * @param  string $uri
     * @param  bool   $absolute
     * @param  bool   $first
     * @return string|bool
     */
    public function findResource($uri, $absolute = true, $first = false);

    /**
     * @param  string $uri
     * @param  bool   $absolute
     * @param  bool   $all
     * @return array
     */
    public function findResources($uri, $absolute = true, $all = false);
}
vendor/rockettheme/toolbox/ResourceLocator/src/UniformResourceIterator.php000064400000011551151166614550023332
0ustar00<?php
namespace RocketTheme\Toolbox\ResourceLocator;

use FilesystemIterator;

/**
 * Implements FilesystemIterator for uniform resource locator.
 *
 * @package RocketTheme\Toolbox\ResourceLocator
 * @author RocketTheme
 * @license MIT
 */
class UniformResourceIterator extends FilesystemIterator
{
    /**
     * @var FilesystemIterator
     */
    protected $iterator;

    /**
     * @var array
     */
    protected $found;

    /**
     * @var array
     */
    protected $stack;

    /**
     * @var string
     */
    protected $path;

    /**
     * @var int
     */
    protected $flags;

    /**
     * @var UniformResourceLocator
     */
    protected $locator;

    public function __construct($path, $flags = null,
UniformResourceLocator $locator = null)
    {
        if (!$locator) {
            throw new \BadMethodCallException('Use
$locator->getIterator() instead');
        }

        $this->path = $path;
        $this->setFlags($flags);
        $this->locator = $locator;
        $this->rewind();
    }

    public function current()
    {
        if ($this->flags & static::CURRENT_AS_SELF) {
            return $this;
        }
        return $this->iterator->current();
    }

    public function key()
    {
        return $this->iterator->key();
    }

    public function next()
    {
        do {
            $found = $this->findNext();
        } while ($found && !empty($this->found[$found]));

        if ($found) {
            // Mark the file as found.
            $this->found[$found] = true;
        }
    }

    public function valid()
    {
        return $this->iterator &&
$this->iterator->valid();
    }

    public function rewind()
    {
        $this->found = [];
        $this->stack =
$this->locator->findResources($this->path);
        $this->next();
    }

    public function getUrl()
    {
        $path = $this->path . (substr($this->path, -1, 1) ===
'/' ? '' : '/');
        return $path . $this->iterator->getFilename();
    }

    public function seek($position)
    {
        throw new \RuntimeException('Seek not implemented');
    }

    public function getATime()
    {
        return $this->iterator->getATime();
    }

    public function getBasename($suffix = null)
    {
        return $this->iterator->getBasename($suffix);
    }

    public function getCTime()
    {
        return $this->iterator->getCTime();
    }

    public function getExtension()
    {
        return $this->iterator->getExtension();
    }

    public function getFilename()
    {
        return $this->iterator->getFilename();
    }

    public function getGroup()
    {
        return $this->iterator->getGroup();
    }

    public function getInode()
    {
        return $this->iterator->getInode();
    }

    public function getMTime()
    {
        return $this->iterator->getMTime();
    }

    public function getOwner()
    {
        return $this->iterator->getOwner();
    }

    public function getPath()
    {
        return $this->iterator->getPath();
    }

    public function getPathname()
    {
        return $this->iterator->getPathname();
    }

    public function getPerms()
    {
        return $this->iterator->getPerms();
    }

    public function getSize()
    {
        return $this->iterator->getSize();
    }

    public function getType()
    {
        return $this->iterator->getType();
    }

    public function isDir()
    {
        return $this->iterator->isDir();
    }

    public function isDot()
    {
        return $this->iterator->isDot();
    }

    public function isExecutable()
    {
        return $this->iterator->isExecutable();
    }

    public function isFile()
    {
        return $this->iterator->isFile();
    }

    public function isLink()
    {
        return $this->iterator->isLink();
    }

    public function isReadable()
    {
        return $this->iterator->isReadable();
    }

    public function isWritable()
    {
        return $this->iterator->isWritable();
    }

    public function __toString()
    {
        return $this->iterator->__toString();
    }

    public function getFlags()
    {
        return $this->flags;
    }

    public function setFlags($flags = null)
    {
        $this->flags = $flags === null ? static::KEY_AS_PATHNAME |
static::CURRENT_AS_SELF | static::SKIP_DOTS : $flags;

        if ($this->iterator) {
            $this->iterator->setFlags($this->flags);
        }
    }

    protected function findNext()
    {
        if ($this->iterator) {
            $this->iterator->next();
        }

        if (!$this->valid()) {
            do {
                // Move to the next iterator if it exists.
                $path = array_shift($this->stack);

                if (!isset($path)) {
                    return null;
                }

                $this->iterator = new \FilesystemIterator($path,
$this->getFlags());
            } while (!$this->iterator->valid());
        }

        return $this->getFilename();
    }
}
vendor/rockettheme/toolbox/ResourceLocator/src/UniformResourceLocator.php000064400000034512151166614550023146
0ustar00<?php

namespace RocketTheme\Toolbox\ResourceLocator;

/**
 * Implements Uniform Resource Location.
 *
 * @package RocketTheme\Toolbox\ResourceLocator
 * @author RocketTheme
 * @license MIT
 *
 * @link
http://webmozarts.com/2013/06/19/the-power-of-uniform-resource-location-in-php/
 */
class UniformResourceLocator implements ResourceLocatorInterface
{
    /**
     * @var string  Base URL for all the streams.
     */
    public $base;

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

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

    public function __construct($base = null)
    {
        // Normalize base path.
        $this->base = rtrim(str_replace('\\', '/',
$base ?: getcwd()), '/');
    }

    /**
     * Return iterator for the resource URI.
     *
     * @param  string $uri
     * @param  int    $flags    See constants from FilesystemIterator
class.
     * @return UniformResourceIterator
     */
    public function getIterator($uri, $flags = null)
    {
        return new UniformResourceIterator($uri, $flags, $this);
    }

    /**
     * Return recursive iterator for the resource URI.
     *
     * @param  string $uri
     * @param  int    $flags    See constants from FilesystemIterator
class.
     * @return RecursiveUniformResourceIterator
     */
    public function getRecursiveIterator($uri, $flags = null)
    {
        return new RecursiveUniformResourceIterator($uri, $flags, $this);
    }

    /**
     * Reset locator by removing all the schemes.
     *
     * @return $this
     */
    public function reset()
    {
        $this->schemes = [];
        $this->cache = [];

        return $this;
    }

    /**
     * Reset a locator scheme
     *
     * @param string $scheme The scheme to reset
     *
     * @return $this
     */
    public function resetScheme($scheme)
    {
        $this->schemes[$scheme] = [];
        $this->cache = [];

        return $this;
    }

    /**
     * Add new paths to the scheme.
     *
     * @param string $scheme
     * @param string $prefix
     * @param string|array $paths
     * @param bool|string  $override  True to add path as override, string
     * @param bool  $force     True to add paths even if them do not exist.
     * @throws \BadMethodCallException
     */
    public function addPath($scheme, $prefix, $paths, $override = false,
$force = false)
    {
        $list = [];
        foreach((array) $paths as $path) {
            if (\is_array($path)) {
                // Support stream lookup in ['theme',
'path/to'] format.
                if (\count($path) !== 2 || !\is_string($path[0]) ||
!\is_string($path[1])) {
                    throw new \BadMethodCallException('Invalid stream
path given.');
                }
                $list[] = $path;
            } elseif (false !== strpos($path, '://')) {
                // Support stream lookup in 'theme://path/to'
format.
                $stream = explode('://', $path, 2);
                $stream[1] = trim($stream[1], '/');

                $list[] = $stream;
            } else {
                // Normalize path.
                $path = rtrim(str_replace('\\', '/',
$path), '/');
                if ($force ||
@file_exists("{$this->base}/{$path}") || @file_exists($path))
{
                    // Support for absolute and relative paths.
                    $list[] = $path;
                }
            }
        }

        if (isset($this->schemes[$scheme][$prefix])) {
            $paths = $this->schemes[$scheme][$prefix];
            if (!$override || $override == 1) {
                $list = $override ? array_merge($paths, $list) :
array_merge($list, $paths);
            } else {
                $location = array_search($override, $paths, true) ?:
\count($paths);
                array_splice($paths, $location, 0, $list);
                $list = $paths;
            }
        }

        $this->schemes[$scheme][$prefix] = $list;

        // Sort in reverse order to get longer prefixes to be matched
first.
        krsort($this->schemes[$scheme]);

        $this->cache = [];
    }

    /**
     * Return base directory.
     *
     * @return string
     */
    public function getBase()
    {
        return $this->base;
    }


    /**
     * Return true if scheme has been defined.
     *
     * @param string $name
     * @return bool
     */
    public function schemeExists($name)
    {
        return isset($this->schemes[$name]);
    }

    /**
     * Return defined schemes.
     *
     * @return array
     */
    public function getSchemes()
    {
        return array_keys($this->schemes);
    }

    /**
     * Return all scheme lookup paths.
     *
     * @param string $scheme
     * @return array
     */
    public function getPaths($scheme = null)
    {
        return !$scheme ? $this->schemes :
(isset($this->schemes[$scheme]) ? $this->schemes[$scheme] : []);
    }

    /**
     * @param  string $uri
     * @return string|bool
     * @throws \BadMethodCallException
     */
    public function __invoke($uri)
    {
        if (!\is_string($uri)) {
            throw new \BadMethodCallException('Invalid parameter
$uri.');
        }
        return $this->findCached($uri, false, true, false);
    }

    /**
     * Returns true if uri is resolvable by using locator.
     *
     * @param  string $uri
     * @return bool
     */
    public function isStream($uri)
    {
        try {
            list ($scheme,) = $this->normalize($uri, true, true);
        } catch (\Exception $e) {
            return false;
        }

        return $this->schemeExists($scheme);
    }

    /**
     * Returns the canonicalized URI on success. The resulting path will
have no '/./' or '/../' components.
     * Trailing delimiter `/` is kept.
     *
     * By default (if $throwException parameter is not set to true) returns
false on failure.
     *
     * @param string $uri
     * @param bool $throwException
     * @param bool $splitStream
     * @return string|array|bool
     * @throws \BadMethodCallException
     */
    public function normalize($uri, $throwException = false, $splitStream =
false)
    {
        if (!\is_string($uri)) {
            if ($throwException) {
                throw new \BadMethodCallException('Invalid parameter
$uri.');
            }

            return false;
        }

        $uri = preg_replace('|\\\|u', '/', $uri);
        $segments = explode('://', $uri, 2);
        $path = array_pop($segments);
        $scheme = array_pop($segments) ?: 'file';

        if ($path) {
            $path = preg_replace('|\\\|u', '/', $path);
            $parts = explode('/', $path);

            $list = [];
            foreach ($parts as $i => $part) {
                if ($part === '..') {
                    $part = array_pop($list);
                    if ($part === null || $part === '' || (!$list
&& strpos($part, ':'))) {
                        if ($throwException) {
                            throw new \BadMethodCallException('Invalid
parameter $uri.');
                        }

                        return false;
                    }
                } elseif (($i && $part === '') || $part
=== '.') {
                    continue;
                } else {
                    $list[] = $part;
                }
            }

            if (($l = end($parts)) === '' || $l === '.'
|| $l === '..') {
                $list[] = '';
            }

            $path = implode('/', $list);
        }

        return $splitStream ? [$scheme, $path] : ($scheme !==
'file' ? "{$scheme}://{$path}" : $path);
    }

    /**
     * Find highest priority instance from a resource.
     *
     * @param  string $uri      Input URI to be searched.
     * @param  bool   $absolute Whether to return absolute path.
     * @param  bool   $first    Whether to return first path even if it
doesn't exist.
     * @throws \BadMethodCallException
     * @return string|bool
     */
    public function findResource($uri, $absolute = true, $first = false)
    {
        if (!\is_string($uri)) {
            throw new \BadMethodCallException('Invalid parameter
$uri.');
        }
        return $this->findCached($uri, false, $absolute, $first);
    }

    /**
     * Find all instances from a resource.
     *
     * @param  string $uri      Input URI to be searched.
     * @param  bool   $absolute Whether to return absolute path.
     * @param  bool   $all      Whether to return all paths even if they
don't exist.
     * @throws \BadMethodCallException
     * @return array
     */
    public function findResources($uri, $absolute = true, $all = false)
    {
        if (!\is_string($uri)) {
            throw new \BadMethodCallException('Invalid parameter
$uri.');
        }

        return $this->findCached($uri, true, $absolute, $all);
    }

    /**
     * Find all instances from a list of resources.
     *
     * @param  array  $uris     Input URIs to be searched.
     * @param  bool   $absolute Whether to return absolute path.
     * @param  bool   $all      Whether to return all paths even if they
don't exist.
     * @throws \BadMethodCallException
     * @return array
     */
    public function mergeResources(array $uris, $absolute = true, $all =
false)
    {
        $uris = array_unique($uris);

        $lists = [[]];
        foreach ($uris as $uri) {
            $lists[] = $this->findResources($uri, $absolute, $all);
        }

        // TODO: In PHP 5.6+ use array_merge(...$list);
        return call_user_func_array('array_merge', $lists);
    }

    /**
     * Pre-fill cache by a stream.
     *
     * @param string $uri
     * @return $this
     */
    public function fillCache($uri)
    {
        $cacheKey = $uri . '@cache';

        if (!isset($this->cache[$cacheKey])) {
            $this->cache[$cacheKey] = true;

            $iterator = new
\RecursiveIteratorIterator($this->getRecursiveIterator($uri),
\RecursiveIteratorIterator::SELF_FIRST);

            /** @var UniformResourceIterator $uri */
            foreach ($iterator as $item) {
                $key = $item->getUrl() . '@010';
                $this->cache[$key] = $item->getPathname();
            }
        }

        return $this;
    }

    /**
     * Reset locator cache.
     *
     * @param string $uri
     * @return $this
     */
    public function clearCache($uri = null)
    {
        if ($uri) {
            $this->clearCached($uri, true, true, true);
            $this->clearCached($uri, true, true, false);
            $this->clearCached($uri, true, false, true);
            $this->clearCached($uri, true, false, false);
            $this->clearCached($uri, false, true, true);
            $this->clearCached($uri, false, true, false);
            $this->clearCached($uri, false, false, true);
            $this->clearCached($uri, false, false, false);
        } else {
            $this->cache = [];
        }

        return $this;
    }

    /**
     * @param string $uri
     * @param bool $array
     * @param bool $absolute
     * @param bool $all
     * @return array|string|bool
     * @throws \BadMethodCallException
     */
    protected function findCached($uri, $array, $absolute, $all)
    {
        // Local caching: make sure that the function gets only called at
once for each file.
        $key = $uri .'@'. (int) $array . (int) $absolute . (int)
$all;

        if (!isset($this->cache[$key])) {
            try {
                list ($scheme, $file) = $this->normalize($uri, true,
true);

                if (!$file && $scheme === 'file') {
                    $file = $this->base;
                }

                $this->cache[$key] = $this->find($scheme, $file,
$array, $absolute, $all);

            } catch (\BadMethodCallException $e) {
                $this->cache[$key] =  $array ? [] : false;
            }
        }

        return $this->cache[$key];
    }

    protected function clearCached($uri, $array, $absolute, $all)
    {
        // Local caching: make sure that the function gets only called at
once for each file.
        $key = $uri .'@'. (int) $array . (int) $absolute . (int)
$all;

        unset($this->cache[$key]);
    }

    /**
     * @param  string $scheme
     * @param  string $file
     * @param  bool $array
     * @param  bool $absolute
     * @param  bool $all
     *
     * @throws \InvalidArgumentException
     * @return array|string|bool
     * @internal
     */
    protected function find($scheme, $file, $array, $absolute, $all)
    {
        if (!isset($this->schemes[$scheme])) {
            throw new \InvalidArgumentException("Invalid resource
{$scheme}://");
        }

        $results = $array ? [] : false;
        foreach ($this->schemes[$scheme] as $prefix => $paths) {
            if ($prefix && strpos($file, $prefix) !== 0) {
                continue;
            }

            // Remove prefix from filename.
            $filename = '/' . trim(substr($file,
\strlen($prefix)), '\/');

            foreach ($paths as $path) {
                if (\is_array($path)) {
                    // Handle scheme lookup.
                    $relPath = trim($path[1] . $filename, '/');
                    $found = $this->find($path[0], $relPath, $array,
$absolute, $all);
                    if ($found) {
                        if (!$array) {
                            return $found;
                        }
                        $results = array_merge($results, $found);
                    }
                } else {
                    // TODO: We could provide some extra information about
the path to remove preg_match().
                    // Check absolute paths for both unix and windows
                    if (!$path || !preg_match('`^/|\w+:`',
$path)) {
                        // Handle relative path lookup.
                        $relPath = trim($path . $filename, '/');
                        $fullPath = $this->base . '/' .
$relPath;
                    } else {
                        // Handle absolute path lookup.
                        $fullPath = rtrim($path . $filename,
'/');
                        if (!$absolute) {
                            throw new
\RuntimeException("UniformResourceLocator: Absolute stream path with
relative lookup not allowed ({$prefix})", 500);
                        }
                    }

                    if ($all || file_exists($fullPath)) {
                        $current = $absolute ? $fullPath : $relPath;
                        if (!$array) {
                            return $current;
                        }
                        $results[] = $current;
                    }
                }
            }
        }

        return $results;
    }
}
vendor/rockettheme/toolbox/Session/src/Message.php000064400000003573151166614550016372
0ustar00<?php
namespace RocketTheme\Toolbox\Session;

/**
 * Implements session messages.
 *
 * @package RocketTheme\Toolbox\Session
 * @author RocketTheme
 * @license MIT
 */
class Message
{
    /**
     * @var array|string[]
     */
    protected $messages = [];

    /**
     * Add message to the queue.
     *
     * @param string $message
     * @param string $scope
     * @return $this
     */
    public function add($message, $scope = 'default')
    {
        $key = md5($scope.'~'.$message);
        $item = ['message' => $message, 'scope'
=> $scope];

        // don't add duplicates
        if (!array_key_exists($key, $this->messages)) {
            $this->messages[$key] = $item;
        }

        return $this;
    }

    /**
     * Clear message queue.
     *
     * @param string $scope
     * @return $this
     */
    public function clear($scope = null)
    {
        if ($scope === null) {
            $this->messages = array();
        } else {
            foreach ($this->messages as $key => $message) {
                if ($message['scope'] === $scope) {
                    unset($this->messages[$key]);
                }
            }
        }
        return $this;
    }

    /**
     * Fetch all messages.
     *
     * @param string $scope
     * @return array
     */
    public function all($scope = null)
    {
        if ($scope === null) {
            return array_values($this->messages);
        }

        $messages = array();
        foreach ($this->messages as $message) {
            if ($message['scope'] === $scope) {
                $messages[] = $message;
            }
        }

        return $messages;
    }

    /**
     * Fetch and clear message queue.
     *
     * @param string $scope
     * @return array
     */
    public function fetch($scope = null)
    {
        $messages = $this->all($scope);
        $this->clear($scope);

        return $messages;
    }

}
vendor/rockettheme/toolbox/Session/src/Session.php000064400000012476151166614550016433
0ustar00<?php
namespace RocketTheme\Toolbox\Session;

/**
 * Implements Session handling.
 *
 * @package RocketTheme\Toolbox\Session
 * @author RocketTheme
 * @license MIT
 */
class Session implements \IteratorAggregate
{
    /**
     * @var bool
     */
    protected $started = false;

    /**
     * @var Session
     */
    protected static $instance;


    /**
     * @param int    $lifetime Defaults to 1800 seconds.
     * @param string $path     Cookie path.
     * @param string $domain   Optional, domain for the session
     * @throws \RuntimeException
     */
    public function __construct($lifetime, $path, $domain = null)
    {
        // Session is a singleton.
        if (null !== self::$instance) {
            throw new \RuntimeException('Session has already been
initialized.', 500);
        }

        // Destroy any existing sessions started with session.auto_start
        if ($this->isSessionStarted()) {
            session_unset();
            session_destroy();
        }

        // Disable transparent sid support
        ini_set('session.use_trans_sid', 0);

        // Only allow cookies
        ini_set('session.use_cookies', 1);

        session_name('msF9kJcW');
        session_set_cookie_params($lifetime, $path, $domain);
        register_shutdown_function([$this, 'close']);
        session_cache_limiter('nocache');

        self::$instance = $this;
    }

    /**
     * Get current session instance.
     *
     * @return Session
     * @throws \RuntimeException
     */
    public function instance()
    {
        if (null === self::$instance) {
            throw new \RuntimeException("Session hasn't been
initialized.", 500);
        }

        return self::$instance;
    }

    /**
     * Starts the session storage
     *
     * @return $this
     * @throws \RuntimeException
     */
    public function start()
    {
        // Protection against invalid session cookie names throwing
exception: http://php.net/manual/en/function.session-id.php#116836
        if (isset($_COOKIE[session_name()]) &&
!preg_match('/^[-,a-zA-Z0-9]{1,128}$/',
$_COOKIE[session_name()])) {
            unset($_COOKIE[session_name()]);
        }

        if (!session_start()) {
            throw new \RuntimeException('Failed to start
session.', 500);
        }

        $this->started = true;

        return $this;
    }

    /**
     * Get session ID
     *
     * @return string|null Session ID
     */
    public function getId()
    {
        return session_id();
    }

    /**
     * Set session Id
     *
     * @param string $id Session ID
     *
     * @return $this
     */
    public function setId($id)
    {
        session_id($id);

        return $this;
    }


    /**
     * Get session name
     *
     * @return string|null
     */
    public function getName()
    {
        return session_name();
    }

    /**
     * Set session name
     *
     * @param string $name
     *
     * @return $this
     */
    public function setName($name)
    {
        session_name($name);

        return $this;
    }

    /**
     * Invalidates the current session.
     *
     * @return $this
     */
    public function invalidate()
    {
        $params = session_get_cookie_params();
        setcookie(session_name(), '', time() - 42000,
            $params['path'], $params['domain'],
            $params['secure'], $params['httponly']
        );

        session_unset();
        session_destroy();

        $this->started = false;

        return $this;
    }

    /**
     * Force the session to be saved and closed
     *
     * @return $this
     */
    public function close()
    {
        if ($this->started) {
            session_write_close();
        }

        $this->started = false;

        return $this;
    }

    /**
     * Checks if an attribute is defined.
     *
     * @param string $name The attribute name
     *
     * @return bool True if the attribute is defined, false otherwise
     */
    public function __isset($name)
    {
        return isset($_SESSION[$name]);
    }

    /**
     * Returns an attribute.
     *
     * @param string $name    The attribute name
     *
     * @return mixed
     */
    public function __get($name)
    {
        return isset($_SESSION[$name]) ? $_SESSION[$name] : null;
    }

    /**
     * Sets an attribute.
     *
     * @param string $name
     * @param mixed  $value
     */
    public function __set($name, $value)
    {
        $_SESSION[$name] = $value;
    }

    /**
     * Removes an attribute.
     *
     * @param string $name
     */
    public function __unset($name)
    {
        unset($_SESSION[$name]);
    }

    /**
     * Returns attributes.
     *
     * @return array Attributes
     */
    public function all()
    {
        return $_SESSION;
    }


    /**
     * Retrieve an external iterator
     *
     * @return \ArrayIterator Return an ArrayIterator of $_SESSION
     */
    public function getIterator()
    {
        return new \ArrayIterator($_SESSION);
    }

    /**
     * Checks if the session was started.
     *
     * @return Boolean
     */
    public function started()
    {
        return $this->started;
    }

    /**
     * http://php.net/manual/en/function.session-status.php#113468
     * Check if session is started nicely.
     * @return bool
     */
    protected function isSessionStarted()
    {
        return php_sapi_name() !== 'cli' ? session_id() !==
'' : false;
    }
}
vendor/rockettheme/toolbox/StreamWrapper/src/ReadOnlyStream.php000064400000005175151166614550021050
0ustar00<?php
namespace RocketTheme\Toolbox\StreamWrapper;

use RocketTheme\Toolbox\ResourceLocator\ResourceLocatorInterface;

/**
 * Implements Read Only Streams.
 *
 * @package RocketTheme\Toolbox\StreamWrapper
 * @author RocketTheme
 * @license MIT
 */
class ReadOnlyStream extends Stream implements StreamInterface
{
    /**
     * @var ResourceLocatorInterface
     */
    protected static $locator;

    public function stream_open($uri, $mode, $options, &$opened_url)
    {
        if (!\in_array($mode, ['r', 'rb',
'rt'], true)) {
            if ($options & STREAM_REPORT_ERRORS) {
                trigger_error(sprintf('stream_open() write modes not
allowed for %s', $uri), E_USER_WARNING);
            }

            return false;
        }

        $path = $this->getPath($uri);

        if (!$path) {
            if ($options & STREAM_REPORT_ERRORS) {
                trigger_error(sprintf('stream_open(): path for %s does
not exist', $uri), E_USER_WARNING);
            }

            return false;
        }

        $this->uri = $uri;
        $this->handle = ($options & STREAM_REPORT_ERRORS) ?
fopen($path, $mode) : @fopen($path, $mode);

        return (bool) $this->handle;
    }

    public function stream_lock($operation)
    {
        // Disallow exclusive lock or non-blocking lock requests
        if (!\in_array($operation, [LOCK_SH, LOCK_UN, LOCK_SH | LOCK_NB],
true)) {
            trigger_error(
                sprintf('stream_lock() exclusive lock operations not
allowed for %s', $this->uri),
                E_USER_WARNING
            );

            return false;
        }

        return flock($this->handle, $operation);
    }

    public function stream_metadata($uri, $option, $value)
    {
        if ($option !== STREAM_META_TOUCH) {
            throw new
\BadMethodCallException(sprintf('stream_metadata() not allowed for
%s', $uri));
        }

        return parent::stream_metadata($uri, $option, $value);
    }

    public function stream_write($data)
    {
        throw new \BadMethodCallException(sprintf('stream_write() not
allowed for %s', $this->uri));
    }

    public function unlink($uri)
    {
        throw new \BadMethodCallException(sprintf('unlink() not
allowed for %s', $uri));
    }

    public function rename($from_uri, $to_uri)
    {
        throw new \BadMethodCallException(sprintf('rename() not
allowed for %s', $from_uri));
    }

    public function mkdir($uri, $mode, $options)
    {
        throw new \BadMethodCallException(sprintf('mkdir() not allowed
for %s', $uri));
    }

    public function rmdir($uri, $options)
    {
        throw new \BadMethodCallException(sprintf('rmdir() not allowed
for %s', $uri));
    }
}
vendor/rockettheme/toolbox/StreamWrapper/src/Stream.php000064400000017057151166614550017414
0ustar00<?php
namespace RocketTheme\Toolbox\StreamWrapper;

use RocketTheme\Toolbox\ResourceLocator\ResourceLocatorInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Implements Read/Write Streams.
 *
 * @package RocketTheme\Toolbox\StreamWrapper
 * @author RocketTheme
 * @license MIT
 */
class Stream implements StreamInterface
{
    /**
     * @var string
     */
    protected $uri;

    /**
     * A generic resource handle.
     *
     * @var Resource
     */
    protected $handle = null;

    /**
     * @var ResourceLocatorInterface|UniformResourceLocator
     */
    protected static $locator;

    /**
     * @param ResourceLocatorInterface $locator
     */
    public static function setLocator(ResourceLocatorInterface $locator)
    {
        static::$locator = $locator;
    }

    public function stream_open($uri, $mode, $options, &$opened_url)
    {
        $path = $this->getPath($uri, $mode);

        if (!$path) {
            if ($options & STREAM_REPORT_ERRORS) {
                trigger_error(sprintf('stream_open(): path for %s does
not exist', $uri), E_USER_WARNING);
            }

            return false;
        }

        $this->uri = $uri;
        $this->handle = ($options & STREAM_REPORT_ERRORS) ?
fopen($path, $mode) : @fopen($path, $mode);

        if (static::$locator instanceof UniformResourceLocator &&
!\in_array($mode, ['r', 'rb', 'rt'], true)) {
            static::$locator->clearCache($this->uri);
        }

        return (bool) $this->handle;
    }

    public function stream_close()
    {
        return fclose($this->handle);
    }

    public function stream_lock($operation)
    {
        if (\in_array($operation, [LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB],
true)) {
            return flock($this->handle, $operation);
        }

        return false;
    }

    public function stream_metadata($uri, $option, $value)
    {
        $path = $this->findPath($uri);
        if ($path) {
            switch ($option) {
                case STREAM_META_TOUCH:
                    list($time, $atime) = $value;
                    return touch($path, $time, $atime);

                case STREAM_META_OWNER_NAME:
                case STREAM_META_OWNER:
                    return chown($path, $value);

                case STREAM_META_GROUP_NAME:
                case STREAM_META_GROUP:
                    return chgrp($path, $value);

                case STREAM_META_ACCESS:
                    return chmod($path, $value);
            }
        }

        return false;
    }

    public function stream_read($count)
    {
        return fread($this->handle, $count);
    }

    public function stream_write($data)
    {
        return fwrite($this->handle, $data);
    }

    public function stream_eof()
    {
        return feof($this->handle);
    }

    public function stream_seek($offset, $whence)
    {
        // fseek returns 0 on success and -1 on a failure.
        return !fseek($this->handle, $offset, $whence);
    }

    public function stream_flush()
    {
        return fflush($this->handle);
    }

    public function stream_tell()
    {
        return ftell($this->handle);
    }

    public function stream_stat()
    {
        return fstat($this->handle);
    }

    /**
     * @return bool
     */
    public function stream_set_option($option, $arg1, $arg2)
    {
        switch ((int)$option) {
            case STREAM_OPTION_BLOCKING:
                return stream_set_blocking($this->handle, (int)$arg1);
            case STREAM_OPTION_READ_TIMEOUT:
                return stream_set_timeout($this->handle, (int)$arg1,
(int)$arg2);
            case STREAM_OPTION_WRITE_BUFFER:
                return stream_set_write_buffer($this->handle,
(int)$arg2);
            default:
                return false;
        }
    }

    /**
     * @param string $uri
     * @return bool
     */
    public function unlink($uri)
    {
        $path = $this->getPath($uri);

        if (!$path) {
            return false;
        }

        return unlink($path);
    }

    public function rename($fromUri, $toUri)
    {
        $fromPath = $this->getPath($fromUri);
        $toPath = $this->getPath($toUri, 'w');

        if (!$fromPath || !$toPath) {
            return false;
        }

        if (static::$locator instanceof UniformResourceLocator) {
            static::$locator->clearCache($fromUri);
            static::$locator->clearCache($toUri);
        }

        return rename($fromPath, $toPath);
    }

    public function mkdir($uri, $mode, $options)
    {
        $recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
        $path = $this->getPath($uri, $recursive ? 'd' :
'w');

        if (!$path) {
            if ($options & STREAM_REPORT_ERRORS) {
                trigger_error(sprintf('mkdir(): Could not create
directory for %s', $uri), E_USER_WARNING);
            }

            return false;
        }

        if (static::$locator instanceof UniformResourceLocator) {
            static::$locator->clearCache($uri);
        }

        return ($options & STREAM_REPORT_ERRORS) ? mkdir($path, $mode,
$recursive) : @mkdir($path, $mode, $recursive);
    }

    public function rmdir($uri, $options)
    {
        $path = $this->getPath($uri);

        if (!$path) {
            if ($options & STREAM_REPORT_ERRORS) {
                trigger_error(sprintf('rmdir(): Directory not found
for %s', $uri), E_USER_WARNING);
            }

            return false;
        }

        if (static::$locator instanceof UniformResourceLocator) {
            static::$locator->clearCache($uri);
        }

        return ($options & STREAM_REPORT_ERRORS) ? rmdir($path) :
@rmdir($path);
    }

    public function url_stat($uri, $flags)
    {
        $path = $this->getPath($uri);

        if (!$path) {
            return false;
        }

        // Suppress warnings if requested or if the file or directory does
not
        // exist. This is consistent with PHPs plain filesystem stream
wrapper.
        return ($flags & STREAM_URL_STAT_QUIET || !file_exists($path))
? @stat($path) : stat($path);
    }

    public function dir_opendir($uri, $options)
    {
        $path = $this->getPath($uri);

        if (!$path) {
            return false;
        }

        $this->uri = $uri;
        $this->handle = opendir($path);

        return (bool) $this->handle;
    }

    public function dir_readdir()
    {
        return readdir($this->handle);
    }

    public function dir_rewinddir()
    {
        rewinddir($this->handle);

        return true;
    }

    public function dir_closedir()
    {
        closedir($this->handle);

        return true;
    }

    protected function getPath($uri, $mode = null)
    {
        if ($mode === null) {
            $mode = 'r';
        }

        $path = $this->findPath($uri);

        if ($path && file_exists($path)) {
            return $path;
        }

        if (strpos($mode[0], 'r') === 0) {
            return false;
        }

        // We are either opening a file or creating directory.
        list($scheme, $target) = explode('://', $uri, 2);

        if ($target === '') {
            return false;
        }
        $target = explode('/', $target);
        $filename = [];

        do {
            $filename[] = array_pop($target);

            $path = $this->findPath($scheme . '://' .
implode('/', $target));
        } while ($target && !$path);

        if (!$path) {
            return false;
        }

        return $path . '/' .  implode('/',
array_reverse($filename));
    }

    protected function findPath($uri)
    {
        return static::$locator &&
static::$locator->isStream($uri) ?
static::$locator->findResource($uri) : false;
    }
}
vendor/rockettheme/toolbox/StreamWrapper/src/StreamBuilder.php000064400000004175151166614550020720
0ustar00<?php
namespace RocketTheme\Toolbox\StreamWrapper;

/**
 * Class StreamBuilder
 * @package RocketTheme\Toolbox\StreamWrapper
 */
class StreamBuilder
{
    /**
     * @var array
     */
    protected $items = [];

    /**
     * StreamBuilder constructor.
     * @param StreamInterface[] $items
     * @throws \InvalidArgumentException
     */
    public function __construct(array $items = [])
    {
        foreach ($items as $scheme => $handler) {
            $this->add($scheme, $handler);
        }
    }

    /**
     * @param string $scheme
     * @param StreamInterface $handler
     * @return $this
     * @throws \InvalidArgumentException
     */
    public function add($scheme, $handler)
    {
        if (isset($this->items[$scheme])) {
            if ($handler === $this->items[$scheme]) {
                return $this;
            }
            throw new \InvalidArgumentException("Stream
'{$scheme}' has already been initialized.");
        }

        if (!is_subclass_of($handler,
'RocketTheme\Toolbox\StreamWrapper\StreamInterface')) {
            throw new \InvalidArgumentException("Stream
'{$scheme}' has unknown or invalid type.");
        }

        if (!@stream_wrapper_register($scheme, $handler)) {
            throw new \InvalidArgumentException("Stream
'{$scheme}' could not be initialized.");
        }

        $this->items[$scheme] = $handler;

        return $this;
    }

    /**
     * @param string $scheme
     * @return $this
     */
    public function remove($scheme)
    {
        if (isset($this->items[$scheme])) {
            stream_wrapper_unregister($scheme);
            unset($this->items[$scheme]);
        }

        return $this;
    }

    /**
     * @return array
     */
    public function getStreams()
    {
        return $this->items;
    }

    /**
     * @param string $scheme
     * @return bool
     */
    public function isStream($scheme)
    {
        return isset($this->items[$scheme]);
    }

    /**
     * @param string $scheme
     * @return StreamInterface|null
     */
    public function getStreamType($scheme)
    {
        return isset($this->items[$scheme]) ? $this->items[$scheme] :
null;
    }
}
vendor/rockettheme/toolbox/StreamWrapper/src/StreamInterface.php000064400000020334151166614550021225
0ustar00<?php
namespace RocketTheme\Toolbox\StreamWrapper;

/**
 * Defines Generic PHP stream wrapper interface.
 *
 * @package RocketTheme\Toolbox\StreamWrapper
 * @author RocketTheme
 * @license MIT
 *
 * @see http://www.php.net/manual/class.streamwrapper.php
 */
interface StreamInterface
{
    /**
     * Support for fopen(), file_get_contents(), file_put_contents() etc.
     *
     * @param string $uri        A string containing the URI to the file to
open.
     * @param string $mode       The file mode ("r",
"wb" etc.).
     * @param int    $options    A bit mask of STREAM_USE_PATH and
STREAM_REPORT_ERRORS.
     * @param string $opened_url A string containing the path actually
opened.
     *
     * @return bool Returns TRUE if file was opened successfully.
     * @see http://php.net/manual/streamwrapper.stream-open.php
     */
    public function stream_open($uri, $mode, $options, &$opened_url);

    /**
     * Support for fclose().
     *
     * @return bool TRUE if stream was successfully closed.
     * @see http://php.net/manual/streamwrapper.stream-close.php
     */
    public function stream_close();

    /**
     * Support for flock().
     *
     * @param $operation
     *     One of the following:
     *     - LOCK_SH to acquire a shared lock (reader).
     *     - LOCK_EX to acquire an exclusive lock (writer).
     *     - LOCK_UN to release a lock (shared or exclusive).
     *     - LOCK_NB if you don't want flock() to block while locking
(not
     *     supported on Windows).
     *
     * @return bool Always returns TRUE at the present time.
     * @see http://php.net/manual/streamwrapper.stream-lock.php
     */
    public function stream_lock($operation);

    /**
     * Support for touch(), chmod(), chown(), chgrp().
     *
     * @param $path
     *     The file path or URL to set metadata. Note that in the case of a
URL, it must be a :// delimited URL.
     *     Other URL forms are not supported.
     *
     * @param $option
     *     One of:
     *     - STREAM_META_TOUCH      The method was called in response to
touch()
     *     - STREAM_META_OWNER_NAME The method was called in response to
chown() with string parameter
     *     - STREAM_META_OWNER      The method was called in response to
chown()
     *     - STREAM_META_GROUP_NAME The method was called in response to
chgrp()
     *     - STREAM_META_GROUP      The method was called in response to
chgrp()
     *     - STREAM_META_ACCESS     The method was called in response to
chmod()
     *
     * @param $value
     *     If option is
     *     - STREAM_META_TOUCH:         Array consisting of two arguments
of the touch() function.
     *     - STREAM_META_OWNER_NAME or
     *       STREAM_META_GROUP_NAME:    The name of the owner user/group as
string.
     *     - STREAM_META_OWNER or
     *       STREAM_META_GROUP:         The value owner user/group argument
as integer.
     *     - STREAM_META_ACCESS:        The argument of the chmod() as
integer.


     *
     * @return bool
     * @see http://php.net/manual/en/streamwrapper.stream-metadata.php
     */
    public function stream_metadata($path, $option, $value);

    /**
     * Support for fread(), file_get_contents() etc.
     *
     * @param $count
     *   Maximum number of bytes to be read.
     *
     * @return string|bool The string that was read, or FALSE in case of an
error.
     * @see http://php.net/manual/streamwrapper.stream-read.php
     */
    public function stream_read($count);

    /**
     * Support for fwrite(), file_put_contents() etc.
     *
     * @param $data
     *   The string to be written.
     *
     * @return int The number of bytes written (integer).
     * @see http://php.net/manual/streamwrapper.stream-write.php
     */
    public function stream_write($data);

    /**
     * Support for feof().
     *
     * @return bool TRUE if end-of-file has been reached.
     * @see http://php.net/manual/streamwrapper.stream-eof.php
     */
    public function stream_eof();

    /**
     * Support for fseek().
     *
     * @param $offset
     *   The byte offset to got to.
     * @param $whence
     *   SEEK_SET, SEEK_CUR, or SEEK_END.
     *
     * @return bool TRUE on success.
     * @see http://php.net/manual/streamwrapper.stream-seek.php
     */
    public function stream_seek($offset, $whence);

    /**
     * Support for fflush().
     *
     * @return bool TRUE if data was successfully stored (or there was no
data to store).
     * @see http://php.net/manual/streamwrapper.stream-flush.php
     */
    public function stream_flush();

    /**
     * Support for ftell().
     *
     * @return int The current offset in bytes from the beginning of file.
     * @see http://php.net/manual/streamwrapper.stream-tell.php
     */
    public function stream_tell();

    /**
     * Support for fstat().
     *
     * @return array An array with file status, or FALSE in case of an
error - see fstat()
     * @see http://php.net/manual/streamwrapper.stream-stat.php
     */
    public function stream_stat();

    /**
     * Support for stream_set_option 
     *  - stream_set_blocking()
     *  - stream_set_timeout()
     *  - stream_set_write_buffer()
     *
     * @param int $option
     * @param int $arg1
     * @param int $arg2
     * @return bool
     *
     * @see http://php.net/manual/streamwrapper.stream-set-option.php
     */
    public function stream_set_option($option, $arg1, $arg2);

    /**
     * Support for unlink().
     *
     * @param $uri
     *   A string containing the URI to the resource to delete.
     *
     * @return
     *   TRUE if resource was successfully deleted.
     * @see http://php.net/manual/streamwrapper.unlink.php
     */
    public function unlink($uri);

    /**
     * Support for rename().
     *
     * @param $from_uri ,
     *                  The URI to the file to rename.
     * @param $to_uri
     *                  The new URI for file.
     *
     * @return bool TRUE if file was successfully renamed.
     * @see http://php.net/manual/streamwrapper.rename.php
     */
    public function rename($from_uri, $to_uri);

    /**
     * Support for mkdir().
     *
     * @param $uri
     *   A string containing the URI to the directory to create.
     * @param $mode
     *   Permission flags - see mkdir().
     * @param $options
     *   A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
     *
     * @return bool TRUE if directory was successfully created.
     * @see http://php.net/manual/streamwrapper.mkdir.php
     */
    public function mkdir($uri, $mode, $options);

    /**
     * Support for rmdir().
     *
     * @param $uri
     *   A string containing the URI to the directory to delete.
     * @param $options
     *   A bit mask of STREAM_REPORT_ERRORS.
     *
     * @return
     *   TRUE if directory was successfully removed.
     *
     * @see http://php.net/manual/streamwrapper.rmdir.php
     */
    public function rmdir($uri, $options);

    /**
     * Support for stat().
     *
     * @param $uri
     *   A string containing the URI to get information about.
     * @param $flags
     *   A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
     *
     * @return array An array with file status, or FALSE in case of an
error - see fstat()
     * @see http://php.net/manual/streamwrapper.url-stat.php
     */
    public function url_stat($uri, $flags);

    /**
     * Support for opendir().
     *
     * @param $uri
     *   A string containing the URI to the directory to open.
     * @param $options
     *   Unknown (parameter is not documented in PHP Manual).
     *
     * @return bool TRUE on success.
     * @see http://php.net/manual/streamwrapper.dir-opendir.php
     */
    public function dir_opendir($uri, $options);

    /**
     * Support for readdir().
     *
     * @return string The next filename, or FALSE if there are no more
files in the directory.
     * @see http://php.net/manual/streamwrapper.dir-readdir.php
     */
    public function dir_readdir();

    /**
     * Support for rewinddir().
     *
     * @return bool TRUE on success.
     * @see http://php.net/manual/streamwrapper.dir-rewinddir.php
     */
    public function dir_rewinddir();

    /**
     * Support for closedir().
     *
     * @return bool TRUE on success.
     * @see http://php.net/manual/streamwrapper.dir-closedir.php
     */
    public function dir_closedir();
}
vendor/symfony/event-dispatcher/composer.json000064400000002157151166614550015553
0ustar00{
    "name": "symfony/event-dispatcher",
    "type": "library",
    "description": "Symfony EventDispatcher Component",
    "keywords": [],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        },
        {
            "name": "Symfony Community",
            "homepage":
"https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=5.3.9"
    },
    "require-dev": {
        "symfony/dependency-injection": "~2.6|~3.0.0",
        "symfony/expression-language": "~2.6|~3.0.0",
        "symfony/config": "^2.0.5|~3.0.0",
        "symfony/stopwatch": "~2.3|~3.0.0",
        "psr/log": "~1.0"
    },
    "suggest": {
        "symfony/dependency-injection": "",
        "symfony/http-kernel": ""
    },
    "autoload": {
        "psr-4": {
"Symfony\\Component\\EventDispatcher\\": "" },
        "exclude-from-classmap": [
            "/Tests/"
        ]
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "2.8-dev"
        }
    }
}
vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php000064400000013657151166614550021464
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Lazily loads listeners and subscribers from the dependency injection
 * container.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Jordan Alliot <jordan.alliot@gmail.com>
 */
class ContainerAwareEventDispatcher extends EventDispatcher
{
    private $container;

    /**
     * The service IDs of the event listeners and subscribers.
     */
    private $listenerIds = array();

    /**
     * The services registered as listeners.
     */
    private $listeners = array();

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * Adds a service as event listener.
     *
     * @param string $eventName Event for which the listener is added
     * @param array  $callback  The service ID of the listener service
& the method
     *                          name that has to be called
     * @param int    $priority  The higher this value, the earlier an event
listener
     *                          will be triggered in the chain.
     *                          Defaults to 0.
     *
     * @throws \InvalidArgumentException
     */
    public function addListenerService($eventName, $callback, $priority =
0)
    {
        if (!\is_array($callback) || 2 !== \count($callback)) {
            throw new \InvalidArgumentException('Expected an
array("service", "method") argument');
        }

        $this->listenerIds[$eventName][] = array($callback[0],
$callback[1], $priority);
    }

    public function removeListener($eventName, $listener)
    {
        $this->lazyLoad($eventName);

        if (isset($this->listenerIds[$eventName])) {
            foreach ($this->listenerIds[$eventName] as $i => $args) {
                list($serviceId, $method) = $args;
                $key = $serviceId.'.'.$method;
                if (isset($this->listeners[$eventName][$key]) &&
$listener === array($this->listeners[$eventName][$key], $method)) {
                    unset($this->listeners[$eventName][$key]);
                    if (empty($this->listeners[$eventName])) {
                        unset($this->listeners[$eventName]);
                    }
                    unset($this->listenerIds[$eventName][$i]);
                    if (empty($this->listenerIds[$eventName])) {
                        unset($this->listenerIds[$eventName]);
                    }
                }
            }
        }

        parent::removeListener($eventName, $listener);
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        if (null === $eventName) {
            return $this->listenerIds || $this->listeners ||
parent::hasListeners();
        }

        if (isset($this->listenerIds[$eventName])) {
            return true;
        }

        return parent::hasListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        if (null === $eventName) {
            foreach ($this->listenerIds as $serviceEventName =>
$args) {
                $this->lazyLoad($serviceEventName);
            }
        } else {
            $this->lazyLoad($eventName);
        }

        return parent::getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListenerPriority($eventName, $listener)
    {
        $this->lazyLoad($eventName);

        return parent::getListenerPriority($eventName, $listener);
    }

    /**
     * Adds a service as event subscriber.
     *
     * @param string $serviceId The service ID of the subscriber service
     * @param string $class     The service's class name (which must
implement EventSubscriberInterface)
     */
    public function addSubscriberService($serviceId, $class)
    {
        foreach ($class::getSubscribedEvents() as $eventName => $params)
{
            if (\is_string($params)) {
                $this->listenerIds[$eventName][] = array($serviceId,
$params, 0);
            } elseif (\is_string($params[0])) {
                $this->listenerIds[$eventName][] = array($serviceId,
$params[0], isset($params[1]) ? $params[1] : 0);
            } else {
                foreach ($params as $listener) {
                    $this->listenerIds[$eventName][] = array($serviceId,
$listener[0], isset($listener[1]) ? $listener[1] : 0);
                }
            }
        }
    }

    public function getContainer()
    {
        return $this->container;
    }

    /**
     * Lazily loads listeners for this event from the dependency injection
     * container.
     *
     * @param string $eventName The name of the event to dispatch. The name
of
     *                          the event is the name of the method that is
     *                          invoked on listeners.
     */
    protected function lazyLoad($eventName)
    {
        if (isset($this->listenerIds[$eventName])) {
            foreach ($this->listenerIds[$eventName] as $args) {
                list($serviceId, $method, $priority) = $args;
                $listener = $this->container->get($serviceId);

                $key = $serviceId.'.'.$method;
                if (!isset($this->listeners[$eventName][$key])) {
                    $this->addListener($eventName, array($listener,
$method), $priority);
                } elseif ($this->listeners[$eventName][$key] !==
$listener) {
                    parent::removeListener($eventName,
array($this->listeners[$eventName][$key], $method));
                    $this->addListener($eventName, array($listener,
$method), $priority);
                }

                $this->listeners[$eventName][$key] = $listener;
            }
        }
    }
}
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php000064400000026111151166614550021457
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Debug;

use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Stopwatch\Stopwatch;

/**
 * Collects some data about event listeners.
 *
 * This event dispatcher delegates the dispatching to another one.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TraceableEventDispatcher implements TraceableEventDispatcherInterface
{
    protected $logger;
    protected $stopwatch;

    private $called;
    private $dispatcher;
    private $wrappedListeners;

    public function __construct(EventDispatcherInterface $dispatcher,
Stopwatch $stopwatch, LoggerInterface $logger = null)
    {
        $this->dispatcher = $dispatcher;
        $this->stopwatch = $stopwatch;
        $this->logger = $logger;
        $this->called = array();
        $this->wrappedListeners = array();
    }

    /**
     * {@inheritdoc}
     */
    public function addListener($eventName, $listener, $priority = 0)
    {
        $this->dispatcher->addListener($eventName, $listener,
$priority);
    }

    /**
     * {@inheritdoc}
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        $this->dispatcher->addSubscriber($subscriber);
    }

    /**
     * {@inheritdoc}
     */
    public function removeListener($eventName, $listener)
    {
        if (isset($this->wrappedListeners[$eventName])) {
            foreach ($this->wrappedListeners[$eventName] as $index =>
$wrappedListener) {
                if ($wrappedListener->getWrappedListener() ===
$listener) {
                    $listener = $wrappedListener;
                    unset($this->wrappedListeners[$eventName][$index]);
                    break;
                }
            }
        }

        return $this->dispatcher->removeListener($eventName,
$listener);
    }

    /**
     * {@inheritdoc}
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber)
    {
        return $this->dispatcher->removeSubscriber($subscriber);
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        return $this->dispatcher->getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListenerPriority($eventName, $listener)
    {
        if (!method_exists($this->dispatcher,
'getListenerPriority')) {
            return 0;
        }

        return $this->dispatcher->getListenerPriority($eventName,
$listener);
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        return $this->dispatcher->hasListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function dispatch($eventName, Event $event = null)
    {
        if (null === $event) {
            $event = new Event();
        }

        if (null !== $this->logger &&
$event->isPropagationStopped()) {
            $this->logger->debug(sprintf('The "%s"
event is already stopped. No listeners have been called.',
$eventName));
        }

        $this->preProcess($eventName);
        $this->preDispatch($eventName, $event);

        $e = $this->stopwatch->start($eventName,
'section');

        $this->dispatcher->dispatch($eventName, $event);

        if ($e->isStarted()) {
            $e->stop();
        }

        $this->postDispatch($eventName, $event);
        $this->postProcess($eventName);

        return $event;
    }

    /**
     * {@inheritdoc}
     */
    public function getCalledListeners()
    {
        $called = array();
        foreach ($this->called as $eventName => $listeners) {
            foreach ($listeners as $listener) {
                $info =
$this->getListenerInfo($listener->getWrappedListener(), $eventName);
                $called[$eventName.'.'.$info['pretty']]
= $info;
            }
        }

        return $called;
    }

    /**
     * {@inheritdoc}
     */
    public function getNotCalledListeners()
    {
        try {
            $allListeners = $this->getListeners();
        } catch (\Exception $e) {
            if (null !== $this->logger) {
                $this->logger->info('An exception was thrown
while getting the uncalled listeners.', array('exception'
=> $e));
            }

            // unable to retrieve the uncalled listeners
            return array();
        }

        $notCalled = array();
        foreach ($allListeners as $eventName => $listeners) {
            foreach ($listeners as $listener) {
                $called = false;
                if (isset($this->called[$eventName])) {
                    foreach ($this->called[$eventName] as $l) {
                        if ($l->getWrappedListener() === $listener) {
                            $called = true;

                            break;
                        }
                    }
                }

                if (!$called) {
                    $info = $this->getListenerInfo($listener,
$eventName);
                   
$notCalled[$eventName.'.'.$info['pretty']] = $info;
                }
            }
        }

        uasort($notCalled, array($this,
'sortListenersByPriority'));

        return $notCalled;
    }

    /**
     * Proxies all method calls to the original event dispatcher.
     *
     * @param string $method    The method name
     * @param array  $arguments The method arguments
     *
     * @return mixed
     */
    public function __call($method, $arguments)
    {
        return \call_user_func_array(array($this->dispatcher, $method),
$arguments);
    }

    /**
     * Called before dispatching the event.
     *
     * @param string $eventName The event name
     * @param Event  $event     The event
     */
    protected function preDispatch($eventName, Event $event)
    {
    }

    /**
     * Called after dispatching the event.
     *
     * @param string $eventName The event name
     * @param Event  $event     The event
     */
    protected function postDispatch($eventName, Event $event)
    {
    }

    private function preProcess($eventName)
    {
        foreach ($this->dispatcher->getListeners($eventName) as
$listener) {
            $info = $this->getListenerInfo($listener, $eventName);
            $name = isset($info['class']) ?
$info['class'] : $info['type'];
            $wrappedListener = new WrappedListener($listener, $name,
$this->stopwatch, $this);
            $this->wrappedListeners[$eventName][] = $wrappedListener;
            $this->dispatcher->removeListener($eventName, $listener);
            $this->dispatcher->addListener($eventName,
$wrappedListener, $info['priority']);
        }
    }

    private function postProcess($eventName)
    {
        unset($this->wrappedListeners[$eventName]);
        $skipped = false;
        foreach ($this->dispatcher->getListeners($eventName) as
$listener) {
            if (!$listener instanceof WrappedListener) { // #12845: a new
listener was added during dispatch.
                continue;
            }
            // Unwrap listener
            $priority = $this->getListenerPriority($eventName,
$listener);
            $this->dispatcher->removeListener($eventName, $listener);
            $this->dispatcher->addListener($eventName,
$listener->getWrappedListener(), $priority);

            $info =
$this->getListenerInfo($listener->getWrappedListener(), $eventName);
            if ($listener->wasCalled()) {
                if (null !== $this->logger) {
                    $this->logger->debug(sprintf('Notified event
"%s" to listener "%s".', $eventName,
$info['pretty']));
                }

                if (!isset($this->called[$eventName])) {
                    $this->called[$eventName] = new \SplObjectStorage();
                }

                $this->called[$eventName]->attach($listener);
            }

            if (null !== $this->logger && $skipped) {
                $this->logger->debug(sprintf('Listener
"%s" was not called for event "%s".',
$info['pretty'], $eventName));
            }

            if ($listener->stoppedPropagation()) {
                if (null !== $this->logger) {
                    $this->logger->debug(sprintf('Listener
"%s" stopped propagation of the event "%s".',
$info['pretty'], $eventName));
                }

                $skipped = true;
            }
        }
    }

    /**
     * Returns information about the listener.
     *
     * @param object $listener  The listener
     * @param string $eventName The event name
     *
     * @return array Information about the listener
     */
    private function getListenerInfo($listener, $eventName)
    {
        $info = array(
            'event' => $eventName,
            'priority' =>
$this->getListenerPriority($eventName, $listener),
        );

        // unwrap for correct listener info
        if ($listener instanceof WrappedListener) {
            $listener = $listener->getWrappedListener();
        }

        if ($listener instanceof \Closure) {
            $info += array(
                'type' => 'Closure',
                'pretty' => 'closure',
            );
        } elseif (\is_string($listener)) {
            try {
                $r = new \ReflectionFunction($listener);
                $file = $r->getFileName();
                $line = $r->getStartLine();
            } catch (\ReflectionException $e) {
                $file = null;
                $line = null;
            }
            $info += array(
                'type' => 'Function',
                'function' => $listener,
                'file' => $file,
                'line' => $line,
                'pretty' => $listener,
            );
        } elseif (\is_array($listener) || (\is_object($listener) &&
\is_callable($listener))) {
            if (!\is_array($listener)) {
                $listener = array($listener, '__invoke');
            }
            $class = \is_object($listener[0]) ? \get_class($listener[0]) :
$listener[0];
            try {
                $r = new \ReflectionMethod($class, $listener[1]);
                $file = $r->getFileName();
                $line = $r->getStartLine();
            } catch (\ReflectionException $e) {
                $file = null;
                $line = null;
            }
            $info += array(
                'type' => 'Method',
                'class' => $class,
                'method' => $listener[1],
                'file' => $file,
                'line' => $line,
                'pretty' =>
$class.'::'.$listener[1],
            );
        }

        return $info;
    }

    private function sortListenersByPriority($a, $b)
    {
        if (\is_int($a['priority']) &&
!\is_int($b['priority'])) {
            return 1;
        }

        if (!\is_int($a['priority']) &&
\is_int($b['priority'])) {
            return -1;
        }

        if ($a['priority'] === $b['priority']) {
            return 0;
        }

        if ($a['priority'] > $b['priority']) {
            return -1;
        }

        return 1;
    }
}
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php000064400000001443151166614550023301
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Debug;

use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface TraceableEventDispatcherInterface extends
EventDispatcherInterface
{
    /**
     * Gets the called listeners.
     *
     * @return array An array of called listeners
     */
    public function getCalledListeners();

    /**
     * Gets the not called listeners.
     *
     * @return array An array of not called listeners
     */
    public function getNotCalledListeners();
}
vendor/symfony/event-dispatcher/Debug/WrappedListener.php000064400000003326151166614550017677
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\Debug;

use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Stopwatch\Stopwatch;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class WrappedListener
{
    private $listener;
    private $name;
    private $called;
    private $stoppedPropagation;
    private $stopwatch;
    private $dispatcher;

    public function __construct($listener, $name, Stopwatch $stopwatch,
EventDispatcherInterface $dispatcher = null)
    {
        $this->listener = $listener;
        $this->name = $name;
        $this->stopwatch = $stopwatch;
        $this->dispatcher = $dispatcher;
        $this->called = false;
        $this->stoppedPropagation = false;
    }

    public function getWrappedListener()
    {
        return $this->listener;
    }

    public function wasCalled()
    {
        return $this->called;
    }

    public function stoppedPropagation()
    {
        return $this->stoppedPropagation;
    }

    public function __invoke(Event $event, $eventName,
EventDispatcherInterface $dispatcher)
    {
        $this->called = true;

        $e = $this->stopwatch->start($this->name,
'event_listener');

        \call_user_func($this->listener, $event, $eventName,
$this->dispatcher ?: $dispatcher);

        if ($e->isStarted()) {
            $e->stop();
        }

        if ($event->isPropagationStopped()) {
            $this->stoppedPropagation = true;
        }
    }
}
vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php000064400000010322151166614550023760
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher\DependencyInjection;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * Compiler pass to register tagged services for an event dispatcher.
 */
class RegisterListenersPass implements CompilerPassInterface
{
    protected $dispatcherService;
    protected $listenerTag;
    protected $subscriberTag;

    /**
     * @param string $dispatcherService Service name of the event
dispatcher in processed container
     * @param string $listenerTag       Tag name used for listener
     * @param string $subscriberTag     Tag name used for subscribers
     */
    public function __construct($dispatcherService =
'event_dispatcher', $listenerTag =
'kernel.event_listener', $subscriberTag =
'kernel.event_subscriber')
    {
        $this->dispatcherService = $dispatcherService;
        $this->listenerTag = $listenerTag;
        $this->subscriberTag = $subscriberTag;
    }

    public function process(ContainerBuilder $container)
    {
        if (!$container->hasDefinition($this->dispatcherService)
&& !$container->hasAlias($this->dispatcherService)) {
            return;
        }

        $definition =
$container->findDefinition($this->dispatcherService);

        foreach ($container->findTaggedServiceIds($this->listenerTag)
as $id => $events) {
            $def = $container->getDefinition($id);
            if (!$def->isPublic()) {
                throw new \InvalidArgumentException(sprintf('The
service "%s" must be public as event listeners are
lazy-loaded.', $id));
            }

            if ($def->isAbstract()) {
                throw new \InvalidArgumentException(sprintf('The
service "%s" must not be abstract as event listeners are
lazy-loaded.', $id));
            }

            foreach ($events as $event) {
                $priority = isset($event['priority']) ?
$event['priority'] : 0;

                if (!isset($event['event'])) {
                    throw new
\InvalidArgumentException(sprintf('Service "%s" must define
the "event" attribute on "%s" tags.', $id,
$this->listenerTag));
                }

                if (!isset($event['method'])) {
                    $event['method'] =
'on'.preg_replace_callback(array(
                        '/(?<=\b)[a-z]/i',
                        '/[^a-z0-9]/i',
                    ), function ($matches) { return
strtoupper($matches[0]); }, $event['event']);
                    $event['method'] =
preg_replace('/[^a-z0-9]/i', '',
$event['method']);
                }

               
$definition->addMethodCall('addListenerService',
array($event['event'], array($id, $event['method']),
$priority));
            }
        }

        foreach
($container->findTaggedServiceIds($this->subscriberTag) as $id =>
$attributes) {
            $def = $container->getDefinition($id);
            if (!$def->isPublic()) {
                throw new \InvalidArgumentException(sprintf('The
service "%s" must be public as event subscribers are
lazy-loaded.', $id));
            }

            if ($def->isAbstract()) {
                throw new \InvalidArgumentException(sprintf('The
service "%s" must not be abstract as event subscribers are
lazy-loaded.', $id));
            }

            // We must assume that the class value has been correctly
filled, even if the service is created by a factory
            $class =
$container->getParameterBag()->resolveValue($def->getClass());
            $interface =
'Symfony\Component\EventDispatcher\EventSubscriberInterface';

            if (!is_subclass_of($class, $interface)) {
                if (!class_exists($class, false)) {
                    throw new \InvalidArgumentException(sprintf('Class
"%s" used for service "%s" cannot be found.',
$class, $id));
                }

                throw new \InvalidArgumentException(sprintf('Service
"%s" must implement interface "%s".', $id,
$interface));
            }

            $definition->addMethodCall('addSubscriberService',
array($id, $class));
        }
    }
}
vendor/symfony/event-dispatcher/Event.php000064400000006644151166614550014630
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * Event is the base class for classes containing event data.
 *
 * This class contains no event data. It is used by events that do not pass
 * state information to an event handler when an event is raised.
 *
 * You can call the method stopPropagation() to abort the execution of
 * further listeners in your event listener.
 *
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class Event
{
    /**
     * @var bool Whether no further event listeners should be triggered
     */
    private $propagationStopped = false;

    /**
     * @var EventDispatcherInterface Dispatcher that dispatched this event
     */
    private $dispatcher;

    /**
     * @var string This event's name
     */
    private $name;

    /**
     * Returns whether further event listeners should be triggered.
     *
     * @see Event::stopPropagation()
     *
     * @return bool Whether propagation was already stopped for this event
     */
    public function isPropagationStopped()
    {
        return $this->propagationStopped;
    }

    /**
     * Stops the propagation of the event to further event listeners.
     *
     * If multiple event listeners are connected to the same event, no
     * further event listener will be triggered once any trigger calls
     * stopPropagation().
     */
    public function stopPropagation()
    {
        $this->propagationStopped = true;
    }

    /**
     * Stores the EventDispatcher that dispatches this Event.
     *
     * @param EventDispatcherInterface $dispatcher
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event
dispatcher is passed to the listener call.
     */
    public function setDispatcher(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    /**
     * Returns the EventDispatcher that dispatches this Event.
     *
     * @return EventDispatcherInterface
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event
dispatcher is passed to the listener call.
     */
    public function getDispatcher()
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since Symfony 2.4 and will be removed in 3.0. The event
dispatcher instance can be received in the listener call instead.',
E_USER_DEPRECATED);

        return $this->dispatcher;
    }

    /**
     * Gets the event's name.
     *
     * @return string
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event name
is passed to the listener call.
     */
    public function getName()
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since Symfony 2.4 and will be removed in 3.0. The event name can
be received in the listener call instead.', E_USER_DEPRECATED);

        return $this->name;
    }

    /**
     * Sets the event's name property.
     *
     * @param string $name The event name
     *
     * @deprecated since version 2.4, to be removed in 3.0. The event name
is passed to the listener call.
     */
    public function setName($name)
    {
        $this->name = $name;
    }
}
vendor/symfony/event-dispatcher/EventDispatcher.php000064400000013433151166614550016631
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * The EventDispatcherInterface is the central point of Symfony's
event listener system.
 *
 * Listeners are registered on the manager and events are dispatched
through the
 * manager.
 *
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Bernhard Schussek <bschussek@gmail.com>
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Jordi Boggiano <j.boggiano@seld.be>
 * @author Jordan Alliot <jordan.alliot@gmail.com>
 */
class EventDispatcher implements EventDispatcherInterface
{
    private $listeners = array();
    private $sorted = array();

    /**
     * {@inheritdoc}
     */
    public function dispatch($eventName, Event $event = null)
    {
        if (null === $event) {
            $event = new Event();
        }

        $event->setDispatcher($this);
        $event->setName($eventName);

        if ($listeners = $this->getListeners($eventName)) {
            $this->doDispatch($listeners, $eventName, $event);
        }

        return $event;
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        if (null !== $eventName) {
            if (!isset($this->listeners[$eventName])) {
                return array();
            }

            if (!isset($this->sorted[$eventName])) {
                $this->sortListeners($eventName);
            }

            return $this->sorted[$eventName];
        }

        foreach ($this->listeners as $eventName => $eventListeners) {
            if (!isset($this->sorted[$eventName])) {
                $this->sortListeners($eventName);
            }
        }

        return array_filter($this->sorted);
    }

    /**
     * Gets the listener priority for a specific event.
     *
     * Returns null if the event or the listener does not exist.
     *
     * @param string   $eventName The name of the event
     * @param callable $listener  The listener
     *
     * @return int|null The event listener priority
     */
    public function getListenerPriority($eventName, $listener)
    {
        if (!isset($this->listeners[$eventName])) {
            return;
        }

        foreach ($this->listeners[$eventName] as $priority =>
$listeners) {
            if (false !== \in_array($listener, $listeners, true)) {
                return $priority;
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        return (bool) $this->getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function addListener($eventName, $listener, $priority = 0)
    {
        $this->listeners[$eventName][$priority][] = $listener;
        unset($this->sorted[$eventName]);
    }

    /**
     * {@inheritdoc}
     */
    public function removeListener($eventName, $listener)
    {
        if (!isset($this->listeners[$eventName])) {
            return;
        }

        foreach ($this->listeners[$eventName] as $priority =>
$listeners) {
            if (false !== ($key = array_search($listener, $listeners,
true))) {
                unset($this->listeners[$eventName][$priority][$key],
$this->sorted[$eventName]);
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        foreach ($subscriber->getSubscribedEvents() as $eventName =>
$params) {
            if (\is_string($params)) {
                $this->addListener($eventName, array($subscriber,
$params));
            } elseif (\is_string($params[0])) {
                $this->addListener($eventName, array($subscriber,
$params[0]), isset($params[1]) ? $params[1] : 0);
            } else {
                foreach ($params as $listener) {
                    $this->addListener($eventName, array($subscriber,
$listener[0]), isset($listener[1]) ? $listener[1] : 0);
                }
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber)
    {
        foreach ($subscriber->getSubscribedEvents() as $eventName =>
$params) {
            if (\is_array($params) && \is_array($params[0])) {
                foreach ($params as $listener) {
                    $this->removeListener($eventName, array($subscriber,
$listener[0]));
                }
            } else {
                $this->removeListener($eventName, array($subscriber,
\is_string($params) ? $params : $params[0]));
            }
        }
    }

    /**
     * Triggers the listeners of an event.
     *
     * This method can be overridden to add functionality that is executed
     * for each listener.
     *
     * @param callable[] $listeners The event listeners
     * @param string     $eventName The name of the event to dispatch
     * @param Event      $event     The event object to pass to the event
handlers/listeners
     */
    protected function doDispatch($listeners, $eventName, Event $event)
    {
        foreach ($listeners as $listener) {
            if ($event->isPropagationStopped()) {
                break;
            }
            \call_user_func($listener, $event, $eventName, $this);
        }
    }

    /**
     * Sorts the internal list of listeners for the given event by
priority.
     *
     * @param string $eventName The name of the event
     */
    private function sortListeners($eventName)
    {
        krsort($this->listeners[$eventName]);
        $this->sorted[$eventName] =
\call_user_func_array('array_merge',
$this->listeners[$eventName]);
    }
}
vendor/symfony/event-dispatcher/EventDispatcherInterface.php000064400000005310151166614550020445
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * The EventDispatcherInterface is the central point of Symfony's
event listener system.
 * Listeners are registered on the manager and events are dispatched
through the
 * manager.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface EventDispatcherInterface
{
    /**
     * Dispatches an event to all registered listeners.
     *
     * @param string $eventName The name of the event to dispatch. The name
of
     *                          the event is the name of the method that is
     *                          invoked on listeners.
     * @param Event  $event     The event to pass to the event
handlers/listeners
     *                          If not supplied, an empty Event instance is
created
     *
     * @return Event
     */
    public function dispatch($eventName, Event $event = null);

    /**
     * Adds an event listener that listens on the specified events.
     *
     * @param string   $eventName The event to listen on
     * @param callable $listener  The listener
     * @param int      $priority  The higher this value, the earlier an
event
     *                            listener will be triggered in the chain
(defaults to 0)
     */
    public function addListener($eventName, $listener, $priority = 0);

    /**
     * Adds an event subscriber.
     *
     * The subscriber is asked for all the events he is
     * interested in and added as a listener for these events.
     */
    public function addSubscriber(EventSubscriberInterface $subscriber);

    /**
     * Removes an event listener from the specified events.
     *
     * @param string   $eventName The event to remove a listener from
     * @param callable $listener  The listener to remove
     */
    public function removeListener($eventName, $listener);

    public function removeSubscriber(EventSubscriberInterface $subscriber);

    /**
     * Gets the listeners of a specific event or all listeners sorted by
descending priority.
     *
     * @param string $eventName The name of the event
     *
     * @return array The event listeners for the specified event, or all
event listeners by event name
     */
    public function getListeners($eventName = null);

    /**
     * Checks whether an event has any registered listeners.
     *
     * @param string $eventName The name of the event
     *
     * @return bool true if the specified event has any listeners, false
otherwise
     */
    public function hasListeners($eventName = null);
}
vendor/symfony/event-dispatcher/EventSubscriberInterface.php000064400000003020151166614550020456
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * An EventSubscriber knows himself what events he is interested in.
 * If an EventSubscriber is added to an EventDispatcherInterface, the
manager invokes
 * {@link getSubscribedEvents} and registers the subscriber as a listener
for all
 * returned events.
 *
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author Jonathan Wage <jonwage@gmail.com>
 * @author Roman Borschel <roman@code-factory.org>
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface EventSubscriberInterface
{
    /**
     * Returns an array of event names this subscriber wants to listen to.
     *
     * The array keys are event names and the value can be:
     *
     *  * The method name to call (priority defaults to 0)
     *  * An array composed of the method name to call and the priority
     *  * An array of arrays composed of the method names to call and
respective
     *    priorities, or 0 if unset
     *
     * For instance:
     *
     *  * array('eventName' => 'methodName')
     *  * array('eventName' => array('methodName',
$priority))
     *  * array('eventName' =>
array(array('methodName1', $priority),
array('methodName2')))
     *
     * @return array The event names to listen to
     */
    public static function getSubscribedEvents();
}
vendor/symfony/event-dispatcher/GenericEvent.php000064400000007173151166614550016123
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * Event encapsulation class.
 *
 * Encapsulates events thus decoupling the observer from the subject they
encapsulate.
 *
 * @author Drak <drak@zikula.org>
 */
class GenericEvent extends Event implements \ArrayAccess,
\IteratorAggregate
{
    protected $subject;
    protected $arguments;

    /**
     * Encapsulate an event with $subject and $args.
     *
     * @param mixed $subject   The subject of the event, usually an object
or a callable
     * @param array $arguments Arguments to store in the event
     */
    public function __construct($subject = null, array $arguments =
array())
    {
        $this->subject = $subject;
        $this->arguments = $arguments;
    }

    /**
     * Getter for subject property.
     *
     * @return mixed The observer subject
     */
    public function getSubject()
    {
        return $this->subject;
    }

    /**
     * Get argument by key.
     *
     * @param string $key Key
     *
     * @return mixed Contents of array key
     *
     * @throws \InvalidArgumentException if key is not found
     */
    public function getArgument($key)
    {
        if ($this->hasArgument($key)) {
            return $this->arguments[$key];
        }

        throw new \InvalidArgumentException(sprintf('Argument
"%s" not found.', $key));
    }

    /**
     * Add argument to event.
     *
     * @param string $key   Argument name
     * @param mixed  $value Value
     *
     * @return $this
     */
    public function setArgument($key, $value)
    {
        $this->arguments[$key] = $value;

        return $this;
    }

    /**
     * Getter for all arguments.
     *
     * @return array
     */
    public function getArguments()
    {
        return $this->arguments;
    }

    /**
     * Set args property.
     *
     * @param array $args Arguments
     *
     * @return $this
     */
    public function setArguments(array $args = array())
    {
        $this->arguments = $args;

        return $this;
    }

    /**
     * Has argument.
     *
     * @param string $key Key of arguments array
     *
     * @return bool
     */
    public function hasArgument($key)
    {
        return array_key_exists($key, $this->arguments);
    }

    /**
     * ArrayAccess for argument getter.
     *
     * @param string $key Array key
     *
     * @return mixed
     *
     * @throws \InvalidArgumentException if key does not exist in
$this->args
     */
    public function offsetGet($key)
    {
        return $this->getArgument($key);
    }

    /**
     * ArrayAccess for argument setter.
     *
     * @param string $key   Array key to set
     * @param mixed  $value Value
     */
    public function offsetSet($key, $value)
    {
        $this->setArgument($key, $value);
    }

    /**
     * ArrayAccess for unset argument.
     *
     * @param string $key Array key
     */
    public function offsetUnset($key)
    {
        if ($this->hasArgument($key)) {
            unset($this->arguments[$key]);
        }
    }

    /**
     * ArrayAccess has argument.
     *
     * @param string $key Array key
     *
     * @return bool
     */
    public function offsetExists($key)
    {
        return $this->hasArgument($key);
    }

    /**
     * IteratorAggregate for iterating over the object like an array.
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->arguments);
    }
}
vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php000064400000004176151166614550020475
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\EventDispatcher;

/**
 * A read-only proxy for an event dispatcher.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
class ImmutableEventDispatcher implements EventDispatcherInterface
{
    private $dispatcher;

    public function __construct(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    /**
     * {@inheritdoc}
     */
    public function dispatch($eventName, Event $event = null)
    {
        return $this->dispatcher->dispatch($eventName, $event);
    }

    /**
     * {@inheritdoc}
     */
    public function addListener($eventName, $listener, $priority = 0)
    {
        throw new \BadMethodCallException('Unmodifiable event
dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function addSubscriber(EventSubscriberInterface $subscriber)
    {
        throw new \BadMethodCallException('Unmodifiable event
dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function removeListener($eventName, $listener)
    {
        throw new \BadMethodCallException('Unmodifiable event
dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function removeSubscriber(EventSubscriberInterface $subscriber)
    {
        throw new \BadMethodCallException('Unmodifiable event
dispatchers must not be modified.');
    }

    /**
     * {@inheritdoc}
     */
    public function getListeners($eventName = null)
    {
        return $this->dispatcher->getListeners($eventName);
    }

    /**
     * {@inheritdoc}
     */
    public function getListenerPriority($eventName, $listener)
    {
        return $this->dispatcher->getListenerPriority($eventName,
$listener);
    }

    /**
     * {@inheritdoc}
     */
    public function hasListeners($eventName = null)
    {
        return $this->dispatcher->hasListeners($eventName);
    }
}
vendor/symfony/polyfill-ctype/bootstrap.php000064400000003004151166614550015256
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Symfony\Polyfill\Ctype as p;

if (!function_exists('ctype_alnum')) {
    function ctype_alnum($input) { return p\Ctype::ctype_alnum($input); }
}
if (!function_exists('ctype_alpha')) {
    function ctype_alpha($input) { return p\Ctype::ctype_alpha($input); }
}
if (!function_exists('ctype_cntrl')) {
    function ctype_cntrl($input) { return p\Ctype::ctype_cntrl($input); }
}
if (!function_exists('ctype_digit')) {
    function ctype_digit($input) { return p\Ctype::ctype_digit($input); }
}
if (!function_exists('ctype_graph')) {
    function ctype_graph($input) { return p\Ctype::ctype_graph($input); }
}
if (!function_exists('ctype_lower')) {
    function ctype_lower($input) { return p\Ctype::ctype_lower($input); }
}
if (!function_exists('ctype_print')) {
    function ctype_print($input) { return p\Ctype::ctype_print($input); }
}
if (!function_exists('ctype_punct')) {
    function ctype_punct($input) { return p\Ctype::ctype_punct($input); }
}
if (!function_exists('ctype_space')) {
    function ctype_space($input) { return p\Ctype::ctype_space($input); }
}
if (!function_exists('ctype_upper')) {
    function ctype_upper($input) { return p\Ctype::ctype_upper($input); }
}
if (!function_exists('ctype_xdigit')) {
    function ctype_xdigit($input) { return p\Ctype::ctype_xdigit($input); }
}
vendor/symfony/polyfill-ctype/composer.json000064400000001732151166614550015260
0ustar00{
    "name": "symfony/polyfill-ctype",
    "type": "library",
    "description": "Symfony polyfill for ctype
functions",
    "keywords": ["polyfill", "compatibility",
"portable", "ctype"],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Gert de Pagter",
            "email": "BackEndTea@gmail.com"
        },
        {
            "name": "Symfony Community",
            "homepage":
"https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=5.3.3"
    },
    "autoload": {
        "psr-4": { "Symfony\\Polyfill\\Ctype\\":
"" },
        "files": [ "bootstrap.php" ]
    },
    "suggest": {
        "ext-ctype": "For best performance"
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-main": "1.19-dev"
        },
        "thanks": {
            "name": "symfony/polyfill",
            "url":
"https://github.com/symfony/polyfill"
        }
    }
}
vendor/symfony/polyfill-ctype/Ctype.php000064400000014175151166614550014340
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Polyfill\Ctype;

/**
 * Ctype implementation through regex.
 *
 * @internal
 *
 * @author Gert de Pagter <BackEndTea@gmail.com>
 */
final class Ctype
{
    /**
     * Returns TRUE if every character in text is either a letter or a
digit, FALSE otherwise.
     *
     * @see https://php.net/ctype-alnum
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_alnum($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^A-Za-z0-9]/', $text);
    }

    /**
     * Returns TRUE if every character in text is a letter, FALSE
otherwise.
     *
     * @see https://php.net/ctype-alpha
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_alpha($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^A-Za-z]/', $text);
    }

    /**
     * Returns TRUE if every character in text is a control character from
the current locale, FALSE otherwise.
     *
     * @see https://php.net/ctype-cntrl
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_cntrl($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^\x00-\x1f\x7f]/', $text);
    }

    /**
     * Returns TRUE if every character in the string text is a decimal
digit, FALSE otherwise.
     *
     * @see https://php.net/ctype-digit
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_digit($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^0-9]/', $text);
    }

    /**
     * Returns TRUE if every character in text is printable and actually
creates visible output (no white space), FALSE otherwise.
     *
     * @see https://php.net/ctype-graph
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_graph($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^!-~]/', $text);
    }

    /**
     * Returns TRUE if every character in text is a lowercase letter.
     *
     * @see https://php.net/ctype-lower
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_lower($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^a-z]/', $text);
    }

    /**
     * Returns TRUE if every character in text will actually create output
(including blanks). Returns FALSE if text contains control characters or
characters that do not have any output or control function at all.
     *
     * @see https://php.net/ctype-print
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_print($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^ -~]/', $text);
    }

    /**
     * Returns TRUE if every character in text is printable, but neither
letter, digit or blank, FALSE otherwise.
     *
     * @see https://php.net/ctype-punct
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_punct($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text);
    }

    /**
     * Returns TRUE if every character in text creates some sort of white
space, FALSE otherwise. Besides the blank character this also includes tab,
vertical tab, line feed, carriage return and form feed characters.
     *
     * @see https://php.net/ctype-space
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_space($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^\s]/', $text);
    }

    /**
     * Returns TRUE if every character in text is an uppercase letter.
     *
     * @see https://php.net/ctype-upper
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_upper($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^A-Z]/', $text);
    }

    /**
     * Returns TRUE if every character in text is a hexadecimal
'digit', that is a decimal digit or a character from [A-Fa-f] ,
FALSE otherwise.
     *
     * @see https://php.net/ctype-xdigit
     *
     * @param string|int $text
     *
     * @return bool
     */
    public static function ctype_xdigit($text)
    {
        $text = self::convert_int_to_char_for_ctype($text);

        return \is_string($text) && '' !== $text
&& !preg_match('/[^A-Fa-f0-9]/', $text);
    }

    /**
     * Converts integers to their char versions according to normal ctype
behaviour, if needed.
     *
     * If an integer between -128 and 255 inclusive is provided,
     * it is interpreted as the ASCII value of a single character
     * (negative values have 256 added in order to allow characters in the
Extended ASCII range).
     * Any other integer is interpreted as a string containing the decimal
digits of the integer.
     *
     * @param string|int $int
     *
     * @return mixed
     */
    private static function convert_int_to_char_for_ctype($int)
    {
        if (!\is_int($int)) {
            return $int;
        }

        if ($int < -128 || $int > 255) {
            return (string) $int;
        }

        if ($int < 0) {
            $int += 256;
        }

        return \chr($int);
    }
}
vendor/symfony/yaml/composer.json000064400000001427151166614550013247
0ustar00{
    "name": "symfony/yaml",
    "type": "library",
    "description": "Symfony Yaml Component",
    "keywords": [],
    "homepage": "https://symfony.com",
    "license": "MIT",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com"
        },
        {
            "name": "Symfony Community",
            "homepage":
"https://symfony.com/contributors"
        }
    ],
    "require": {
        "php": ">=5.3.9",
        "symfony/polyfill-ctype": "~1.8"
    },
    "autoload": {
        "psr-4": { "Symfony\\Component\\Yaml\\":
"" },
        "exclude-from-classmap": [
            "/Tests/"
        ]
    },
    "minimum-stability": "dev",
    "extra": {
        "branch-alias": {
            "dev-master": "2.8-dev"
        }
    }
}
vendor/symfony/yaml/Dumper.php000064400000004750151166614550012474
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

/**
 * Dumper dumps PHP variables to YAML strings.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Dumper
{
    /**
     * The amount of spaces to use for indentation of nested nodes.
     *
     * @var int
     */
    protected $indentation = 4;

    /**
     * Sets the indentation.
     *
     * @param int $num The amount of spaces to use for indentation of
nested nodes
     */
    public function setIndentation($num)
    {
        if ($num < 1) {
            throw new \InvalidArgumentException('The indentation must
be greater than zero.');
        }

        $this->indentation = (int) $num;
    }

    /**
     * Dumps a PHP value to YAML.
     *
     * @param mixed $input                  The PHP value
     * @param int   $inline                 The level where you switch to
inline YAML
     * @param int   $indent                 The level of indentation (used
internally)
     * @param bool  $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
     * @param bool  $objectSupport          True if object support is
enabled, false otherwise
     *
     * @return string The YAML representation of the PHP value
     */
    public function dump($input, $inline = 0, $indent = 0,
$exceptionOnInvalidType = false, $objectSupport = false)
    {
        $output = '';
        $prefix = $indent ? str_repeat(' ', $indent) :
'';

        if ($inline <= 0 || !\is_array($input) || empty($input)) {
            $output .= $prefix.Inline::dump($input,
$exceptionOnInvalidType, $objectSupport);
        } else {
            $isAHash = Inline::isHash($input);

            foreach ($input as $key => $value) {
                $willBeInlined = $inline - 1 <= 0 || !\is_array($value)
|| empty($value);

                $output .= sprintf('%s%s%s%s',
                    $prefix,
                    $isAHash ? Inline::dump($key, $exceptionOnInvalidType,
$objectSupport).':' : '-',
                    $willBeInlined ? ' ' : "\n",
                    $this->dump($value, $inline - 1, $willBeInlined ? 0
: $indent + $this->indentation, $exceptionOnInvalidType, $objectSupport)
                ).($willBeInlined ? "\n" : '');
            }
        }

        return $output;
    }
}
vendor/symfony/yaml/Escaper.php000064400000007550151166614550012623
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

/**
 * Escaper encapsulates escaping rules for single and double-quoted
 * YAML strings.
 *
 * @author Matthew Lewinski <matthew@lewinski.org>
 *
 * @internal
 */
class Escaper
{
    // Characters that would cause a dumped string to require double
quoting.
    const REGEX_CHARACTER_TO_ESCAPE =
"[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9";

    // Mapping arrays for escaping a double quoted string. The backslash is
    // first to ensure proper escaping because str_replace operates
iteratively
    // on the input arrays. This ordering of the characters avoids the use
of strtr,
    // which performs more slowly.
    private static $escapees = array('\\', '\\\\',
'\\"', '"',
                                     "\x00",  "\x01", 
"\x02",  "\x03",  "\x04",  "\x05", 
"\x06",  "\x07",
                                     "\x08",  "\x09", 
"\x0a",  "\x0b",  "\x0c",  "\x0d", 
"\x0e",  "\x0f",
                                     "\x10",  "\x11", 
"\x12",  "\x13",  "\x14",  "\x15", 
"\x16",  "\x17",
                                     "\x18",  "\x19", 
"\x1a",  "\x1b",  "\x1c",  "\x1d", 
"\x1e",  "\x1f",
                                     "\xc2\x85",
"\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9",
                               );
    private static $escaped = array('\\\\', '\\"',
'\\\\', '\\"',
                                     '\\0',   '\\x01',
'\\x02', '\\x03', '\\x04', '\\x05',
'\\x06', '\\a',
                                     '\\b',   '\\t',  
'\\n',   '\\v',   '\\f',   '\\r',  
'\\x0e', '\\x0f',
                                     '\\x10', '\\x11',
'\\x12', '\\x13', '\\x14', '\\x15',
'\\x16', '\\x17',
                                     '\\x18', '\\x19',
'\\x1a', '\\e',   '\\x1c', '\\x1d',
'\\x1e', '\\x1f',
                                     '\\N', '\\_',
'\\L', '\\P',
                              );

    /**
     * Determines if a PHP value would require double quoting in YAML.
     *
     * @param string $value A PHP value
     *
     * @return bool True if the value would require double quotes
     */
    public static function requiresDoubleQuoting($value)
    {
        return 0 <
preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u',
$value);
    }

    /**
     * Escapes and surrounds a PHP value with double quotes.
     *
     * @param string $value A PHP value
     *
     * @return string The quoted, escaped string
     */
    public static function escapeWithDoubleQuotes($value)
    {
        return sprintf('"%s"',
str_replace(self::$escapees, self::$escaped, $value));
    }

    /**
     * Determines if a PHP value would require single quoting in YAML.
     *
     * @param string $value A PHP value
     *
     * @return bool True if the value would require single quotes
     */
    public static function requiresSingleQuoting($value)
    {
        // Determines if a PHP value is entirely composed of a value that
would
        // require single quoting in YAML.
        if (\in_array(strtolower($value), array('null',
'~', 'true', 'false', 'y',
'n', 'yes', 'no', 'on',
'off'))) {
            return true;
        }

        // Determines if the PHP value contains any single characters that
would
        // cause it to require single quoting in YAML.
        return 0 < preg_match('/[ \s \' " \: \{ \} \[ \]
, & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value);
    }

    /**
     * Escapes and surrounds a PHP value with single quotes.
     *
     * @param string $value A PHP value
     *
     * @return string The quoted, escaped string
     */
    public static function escapeWithSingleQuotes($value)
    {
        return sprintf("'%s'",
str_replace('\'', '\'\'', $value));
    }
}
vendor/symfony/yaml/Exception/DumpException.php000064400000000707151166614550015760
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception class thrown when an error occurs during dumping.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DumpException extends RuntimeException
{
}
vendor/symfony/yaml/Exception/ExceptionInterface.php000064400000000673151166614550016755
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception interface for all exceptions thrown by the component.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface ExceptionInterface
{
}
vendor/symfony/yaml/Exception/ParseException.php000064400000006771151166614550016134
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception class thrown when an error occurs during parsing.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ParseException extends RuntimeException
{
    private $parsedFile;
    private $parsedLine;
    private $snippet;
    private $rawMessage;

    /**
     * @param string          $message    The error message
     * @param int             $parsedLine The line where the error occurred
     * @param string|null     $snippet    The snippet of code near the
problem
     * @param string|null     $parsedFile The file name where the error
occurred
     * @param \Exception|null $previous   The previous exception
     */
    public function __construct($message, $parsedLine = -1, $snippet =
null, $parsedFile = null, \Exception $previous = null)
    {
        $this->parsedFile = $parsedFile;
        $this->parsedLine = $parsedLine;
        $this->snippet = $snippet;
        $this->rawMessage = $message;

        $this->updateRepr();

        parent::__construct($this->message, 0, $previous);
    }

    /**
     * Gets the snippet of code near the error.
     *
     * @return string The snippet of code
     */
    public function getSnippet()
    {
        return $this->snippet;
    }

    /**
     * Sets the snippet of code near the error.
     *
     * @param string $snippet The code snippet
     */
    public function setSnippet($snippet)
    {
        $this->snippet = $snippet;

        $this->updateRepr();
    }

    /**
     * Gets the filename where the error occurred.
     *
     * This method returns null if a string is parsed.
     *
     * @return string The filename
     */
    public function getParsedFile()
    {
        return $this->parsedFile;
    }

    /**
     * Sets the filename where the error occurred.
     *
     * @param string $parsedFile The filename
     */
    public function setParsedFile($parsedFile)
    {
        $this->parsedFile = $parsedFile;

        $this->updateRepr();
    }

    /**
     * Gets the line where the error occurred.
     *
     * @return int The file line
     */
    public function getParsedLine()
    {
        return $this->parsedLine;
    }

    /**
     * Sets the line where the error occurred.
     *
     * @param int $parsedLine The file line
     */
    public function setParsedLine($parsedLine)
    {
        $this->parsedLine = $parsedLine;

        $this->updateRepr();
    }

    private function updateRepr()
    {
        $this->message = $this->rawMessage;

        $dot = false;
        if ('.' === substr($this->message, -1)) {
            $this->message = substr($this->message, 0, -1);
            $dot = true;
        }

        if (null !== $this->parsedFile) {
            if (\PHP_VERSION_ID >= 50400) {
                $jsonOptions = JSON_UNESCAPED_SLASHES |
JSON_UNESCAPED_UNICODE;
            } else {
                $jsonOptions = 0;
            }
            $this->message .= sprintf(' in %s',
json_encode($this->parsedFile, $jsonOptions));
        }

        if ($this->parsedLine >= 0) {
            $this->message .= sprintf(' at line %d',
$this->parsedLine);
        }

        if ($this->snippet) {
            $this->message .= sprintf(' (near
"%s")', $this->snippet);
        }

        if ($dot) {
            $this->message .= '.';
        }
    }
}
vendor/symfony/yaml/Exception/RuntimeException.php000064400000000745151166614550016500
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml\Exception;

/**
 * Exception class thrown when an error occurs during parsing.
 *
 * @author Romain Neutron <imprec@gmail.com>
 */
class RuntimeException extends \RuntimeException implements
ExceptionInterface
{
}
vendor/symfony/yaml/Inline.php000064400000053711151166614550012457
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\DumpException;
use Symfony\Component\Yaml\Exception\ParseException;

/**
 * Inline implements a YAML parser/dumper for the YAML inline syntax.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Inline
{
    const REGEX_QUOTED_STRING =
'(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';

    private static $exceptionOnInvalidType = false;
    private static $objectSupport = false;
    private static $objectForMap = false;

    /**
     * Converts a YAML string to a PHP value.
     *
     * @param string $value                  A YAML string
     * @param bool   $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
     * @param bool   $objectSupport          True if object support is
enabled, false otherwise
     * @param bool   $objectForMap           True if maps should return a
stdClass instead of array()
     * @param array  $references             Mapping of variable names to
values
     *
     * @return mixed A PHP value
     *
     * @throws ParseException
     */
    public static function parse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false, $references = array())
    {
        self::$exceptionOnInvalidType = $exceptionOnInvalidType;
        self::$objectSupport = $objectSupport;
        self::$objectForMap = $objectForMap;

        $value = trim($value);

        if ('' === $value) {
            return '';
        }

        if (2 /* MB_OVERLOAD_STRING */ & (int)
ini_get('mbstring.func_overload')) {
            $mbEncoding = mb_internal_encoding();
            mb_internal_encoding('ASCII');
        }

        $i = 0;
        switch ($value[0]) {
            case '[':
                $result = self::parseSequence($value, $i, $references);
                ++$i;
                break;
            case '{':
                $result = self::parseMapping($value, $i, $references);
                ++$i;
                break;
            default:
                $result = self::parseScalar($value, null,
array('"', "'"), $i, true, $references);
        }

        // some comments are allowed at the end
        if (preg_replace('/\s+#.*$/A', '',
substr($value, $i))) {
            throw new ParseException(sprintf('Unexpected characters
near "%s".', substr($value, $i)));
        }

        if (isset($mbEncoding)) {
            mb_internal_encoding($mbEncoding);
        }

        return $result;
    }

    /**
     * Dumps a given PHP variable to a YAML string.
     *
     * @param mixed $value                  The PHP variable to convert
     * @param bool  $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
     * @param bool  $objectSupport          True if object support is
enabled, false otherwise
     *
     * @return string The YAML string representing the PHP value
     *
     * @throws DumpException When trying to dump PHP resource
     */
    public static function dump($value, $exceptionOnInvalidType = false,
$objectSupport = false)
    {
        switch (true) {
            case \is_resource($value):
                if ($exceptionOnInvalidType) {
                    throw new DumpException(sprintf('Unable to dump
PHP resources in a YAML file ("%s").',
get_resource_type($value)));
                }

                return 'null';
            case \is_object($value):
                if ($objectSupport) {
                    return '!php/object:'.serialize($value);
                }

                if ($exceptionOnInvalidType) {
                    throw new DumpException('Object support when
dumping a YAML file has been disabled.');
                }

                return 'null';
            case \is_array($value):
                return self::dumpArray($value, $exceptionOnInvalidType,
$objectSupport);
            case null === $value:
                return 'null';
            case true === $value:
                return 'true';
            case false === $value:
                return 'false';
            case ctype_digit($value):
                return \is_string($value) ? "'$value'"
: (int) $value;
            case is_numeric($value):
                $locale = setlocale(LC_NUMERIC, 0);
                if (false !== $locale) {
                    setlocale(LC_NUMERIC, 'C');
                }
                if (\is_float($value)) {
                    $repr = (string) $value;
                    if (is_infinite($value)) {
                        $repr = str_ireplace('INF',
'.Inf', $repr);
                    } elseif (floor($value) == $value && $repr ==
$value) {
                        // Preserve float data type since storing a whole
number will result in integer value.
                        $repr = '!!float '.$repr;
                    }
                } else {
                    $repr = \is_string($value) ?
"'$value'" : (string) $value;
                }
                if (false !== $locale) {
                    setlocale(LC_NUMERIC, $locale);
                }

                return $repr;
            case '' == $value:
                return "''";
            case Escaper::requiresDoubleQuoting($value):
                return Escaper::escapeWithDoubleQuotes($value);
            case Escaper::requiresSingleQuoting($value):
            case Parser::preg_match(self::getHexRegex(), $value):
            case Parser::preg_match(self::getTimestampRegex(), $value):
                return Escaper::escapeWithSingleQuotes($value);
            default:
                return $value;
        }
    }

    /**
     * Check if given array is hash or just normal indexed array.
     *
     * @internal
     *
     * @param array $value The PHP array to check
     *
     * @return bool true if value is hash array, false otherwise
     */
    public static function isHash(array $value)
    {
        $expectedKey = 0;

        foreach ($value as $key => $val) {
            if ($key !== $expectedKey++) {
                return true;
            }
        }

        return false;
    }

    /**
     * Dumps a PHP array to a YAML string.
     *
     * @param array $value                  The PHP array to dump
     * @param bool  $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
     * @param bool  $objectSupport          True if object support is
enabled, false otherwise
     *
     * @return string The YAML string representing the PHP array
     */
    private static function dumpArray($value, $exceptionOnInvalidType,
$objectSupport)
    {
        // array
        if ($value && !self::isHash($value)) {
            $output = array();
            foreach ($value as $val) {
                $output[] = self::dump($val, $exceptionOnInvalidType,
$objectSupport);
            }

            return sprintf('[%s]', implode(', ',
$output));
        }

        // hash
        $output = array();
        foreach ($value as $key => $val) {
            $output[] = sprintf('%s: %s', self::dump($key,
$exceptionOnInvalidType, $objectSupport), self::dump($val,
$exceptionOnInvalidType, $objectSupport));
        }

        return sprintf('{ %s }', implode(', ',
$output));
    }

    /**
     * Parses a YAML scalar.
     *
     * @param string   $scalar
     * @param string[] $delimiters
     * @param string[] $stringDelimiters
     * @param int      &$i
     * @param bool     $evaluate
     * @param array    $references
     *
     * @return string
     *
     * @throws ParseException When malformed inline YAML string is parsed
     *
     * @internal
     */
    public static function parseScalar($scalar, $delimiters = null,
$stringDelimiters = array('"', "'"), &$i
= 0, $evaluate = true, $references = array())
    {
        if (\in_array($scalar[$i], $stringDelimiters)) {
            // quoted scalar
            $output = self::parseQuotedScalar($scalar, $i);

            if (null !== $delimiters) {
                $tmp = ltrim(substr($scalar, $i), ' ');
                if ('' === $tmp) {
                    throw new ParseException(sprintf('Unexpected end
of line, expected one of "%s".', implode('',
$delimiters)));
                }
                if (!\in_array($tmp[0], $delimiters)) {
                    throw new ParseException(sprintf('Unexpected
characters (%s).', substr($scalar, $i)));
                }
            }
        } else {
            // "normal" string
            if (!$delimiters) {
                $output = substr($scalar, $i);
                $i += \strlen($output);

                // remove comments
                if (Parser::preg_match('/[ \t]+#/', $output,
$match, PREG_OFFSET_CAPTURE)) {
                    $output = substr($output, 0, $match[0][1]);
                }
            } elseif
(Parser::preg_match('/^(.+?)('.implode('|',
$delimiters).')/', substr($scalar, $i), $match)) {
                $output = $match[1];
                $i += \strlen($output);
            } else {
                throw new ParseException(sprintf('Malformed inline
YAML string: %s.', $scalar));
            }

            // a non-quoted string cannot start with @ or ` (reserved) nor
with a scalar indicator (| or >)
            if ($output && ('@' === $output[0] ||
'`' === $output[0] || '|' === $output[0] ||
'>' === $output[0])) {
                @trigger_error(sprintf('Not quoting the scalar
"%s" starting with "%s" is deprecated since Symfony 2.8
and will throw a ParseException in 3.0.', $output, $output[0]),
E_USER_DEPRECATED);

                // to be thrown in 3.0
                // throw new ParseException(sprintf('The reserved
indicator "%s" cannot start a plain scalar; you need to quote the
scalar.', $output[0]));
            }

            if ($evaluate) {
                $output = self::evaluateScalar($output, $references);
            }
        }

        return $output;
    }

    /**
     * Parses a YAML quoted scalar.
     *
     * @param string $scalar
     * @param int    &$i
     *
     * @return string
     *
     * @throws ParseException When malformed inline YAML string is parsed
     */
    private static function parseQuotedScalar($scalar, &$i)
    {
        if
(!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au',
substr($scalar, $i), $match)) {
            throw new ParseException(sprintf('Malformed inline YAML
string: %s.', substr($scalar, $i)));
        }

        $output = substr($match[0], 1, \strlen($match[0]) - 2);

        $unescaper = new Unescaper();
        if ('"' == $scalar[$i]) {
            $output = $unescaper->unescapeDoubleQuotedString($output);
        } else {
            $output = $unescaper->unescapeSingleQuotedString($output);
        }

        $i += \strlen($match[0]);

        return $output;
    }

    /**
     * Parses a YAML sequence.
     *
     * @param string $sequence
     * @param int    &$i
     * @param array  $references
     *
     * @return array
     *
     * @throws ParseException When malformed inline YAML string is parsed
     */
    private static function parseSequence($sequence, &$i = 0,
$references = array())
    {
        $output = array();
        $len = \strlen($sequence);
        ++$i;

        // [foo, bar, ...]
        while ($i < $len) {
            switch ($sequence[$i]) {
                case '[':
                    // nested sequence
                    $output[] = self::parseSequence($sequence, $i,
$references);
                    break;
                case '{':
                    // nested mapping
                    $output[] = self::parseMapping($sequence, $i,
$references);
                    break;
                case ']':
                    return $output;
                case ',':
                case ' ':
                    break;
                default:
                    $isQuoted = \in_array($sequence[$i],
array('"', "'"));
                    $value = self::parseScalar($sequence,
array(',', ']'), array('"',
"'"), $i, true, $references);

                    // the value can be an array if a reference has been
resolved to an array var
                    if (!\is_array($value) && !$isQuoted &&
false !== strpos($value, ': ')) {
                        // embedded mapping?
                        try {
                            $pos = 0;
                            $value =
self::parseMapping('{'.$value.'}', $pos, $references);
                        } catch (\InvalidArgumentException $e) {
                            // no, it's not
                        }
                    }

                    $output[] = $value;

                    --$i;
            }

            ++$i;
        }

        throw new ParseException(sprintf('Malformed inline YAML
string: %s.', $sequence));
    }

    /**
     * Parses a YAML mapping.
     *
     * @param string $mapping
     * @param int    &$i
     * @param array  $references
     *
     * @return array|\stdClass
     *
     * @throws ParseException When malformed inline YAML string is parsed
     */
    private static function parseMapping($mapping, &$i = 0, $references
= array())
    {
        $output = array();
        $len = \strlen($mapping);
        ++$i;
        $allowOverwrite = false;

        // {foo: bar, bar:foo, ...}
        while ($i < $len) {
            switch ($mapping[$i]) {
                case ' ':
                case ',':
                    ++$i;
                    continue 2;
                case '}':
                    if (self::$objectForMap) {
                        return (object) $output;
                    }

                    return $output;
            }

            // key
            $key = self::parseScalar($mapping, array(':', '
'), array('"', "'"), $i, false);

            if ('<<' === $key) {
                $allowOverwrite = true;
            }

            // value
            $done = false;

            while ($i < $len) {
                switch ($mapping[$i]) {
                    case '[':
                        // nested sequence
                        $value = self::parseSequence($mapping, $i,
$references);
                        // Spec: Keys MUST be unique; first one wins.
                        // Parser cannot abort this mapping earlier, since
lines
                        // are processed sequentially.
                        // But overwriting is allowed when a merge node is
used in current block.
                        if ('<<' === $key) {
                            foreach ($value as $parsedValue) {
                                $output += $parsedValue;
                            }
                        } elseif ($allowOverwrite || !isset($output[$key]))
{
                            $output[$key] = $value;
                        }
                        $done = true;
                        break;
                    case '{':
                        // nested mapping
                        $value = self::parseMapping($mapping, $i,
$references);
                        // Spec: Keys MUST be unique; first one wins.
                        // Parser cannot abort this mapping earlier, since
lines
                        // are processed sequentially.
                        // But overwriting is allowed when a merge node is
used in current block.
                        if ('<<' === $key) {
                            $output += $value;
                        } elseif ($allowOverwrite || !isset($output[$key]))
{
                            $output[$key] = $value;
                        }
                        $done = true;
                        break;
                    case ':':
                    case ' ':
                        break;
                    default:
                        $value = self::parseScalar($mapping,
array(',', '}'), array('"',
"'"), $i, true, $references);
                        // Spec: Keys MUST be unique; first one wins.
                        // Parser cannot abort this mapping earlier, since
lines
                        // are processed sequentially.
                        // But overwriting is allowed when a merge node is
used in current block.
                        if ('<<' === $key) {
                            $output += $value;
                        } elseif ($allowOverwrite || !isset($output[$key]))
{
                            $output[$key] = $value;
                        }
                        $done = true;
                        --$i;
                }

                ++$i;

                if ($done) {
                    continue 2;
                }
            }
        }

        throw new ParseException(sprintf('Malformed inline YAML
string: %s.', $mapping));
    }

    /**
     * Evaluates scalars and replaces magic values.
     *
     * @param string $scalar
     * @param array  $references
     *
     * @return mixed The evaluated YAML string
     *
     * @throws ParseException when object parsing support was disabled and
the parser detected a PHP object or when a reference could not be resolved
     */
    private static function evaluateScalar($scalar, $references = array())
    {
        $scalar = trim($scalar);
        $scalarLower = strtolower($scalar);

        if (0 === strpos($scalar, '*')) {
            if (false !== $pos = strpos($scalar, '#')) {
                $value = substr($scalar, 1, $pos - 2);
            } else {
                $value = substr($scalar, 1);
            }

            // an unquoted *
            if (false === $value || '' === $value) {
                throw new ParseException('A reference must contain at
least one character.');
            }

            if (!array_key_exists($value, $references)) {
                throw new ParseException(sprintf('Reference
"%s" does not exist.', $value));
            }

            return $references[$value];
        }

        switch (true) {
            case 'null' === $scalarLower:
            case '' === $scalar:
            case '~' === $scalar:
                return;
            case 'true' === $scalarLower:
                return true;
            case 'false' === $scalarLower:
                return false;
            // Optimise for returning strings.
            case '+' === $scalar[0] || '-' ===
$scalar[0] || '.' === $scalar[0] || '!' === $scalar[0]
|| is_numeric($scalar[0]):
                switch (true) {
                    case 0 === strpos($scalar, '!str'):
                        return (string) substr($scalar, 5);
                    case 0 === strpos($scalar, '! '):
                        return (int) self::parseScalar(substr($scalar, 2));
                    case 0 === strpos($scalar, '!php/object:'):
                        if (self::$objectSupport) {
                            return unserialize(substr($scalar, 12));
                        }

                        if (self::$exceptionOnInvalidType) {
                            throw new ParseException('Object support
when parsing a YAML file has been disabled.');
                        }

                        return;
                    case 0 === strpos($scalar, '!!php/object:'):
                        if (self::$objectSupport) {
                            return unserialize(substr($scalar, 13));
                        }

                        if (self::$exceptionOnInvalidType) {
                            throw new ParseException('Object support
when parsing a YAML file has been disabled.');
                        }

                        return;
                    case 0 === strpos($scalar, '!!float '):
                        return (float) substr($scalar, 8);
                    case ctype_digit($scalar):
                        $raw = $scalar;
                        $cast = (int) $scalar;

                        return '0' == $scalar[0] ?
octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
                    case '-' === $scalar[0] &&
ctype_digit(substr($scalar, 1)):
                        $raw = $scalar;
                        $cast = (int) $scalar;

                        return '0' == $scalar[1] ?
octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
                    case is_numeric($scalar):
                    case Parser::preg_match(self::getHexRegex(), $scalar):
                        return '0x' === $scalar[0].$scalar[1] ?
hexdec($scalar) : (float) $scalar;
                    case '.inf' === $scalarLower:
                    case '.nan' === $scalarLower:
                        return -log(0);
                    case '-.inf' === $scalarLower:
                        return log(0);
                    case
Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
                        return (float) str_replace(',',
'', $scalar);
                    case Parser::preg_match(self::getTimestampRegex(),
$scalar):
                        $timeZone = date_default_timezone_get();
                        date_default_timezone_set('UTC');
                        $time = strtotime($scalar);
                        date_default_timezone_set($timeZone);

                        return $time;
                }
                // no break
            default:
                return (string) $scalar;
        }
    }

    /**
     * Gets a regex that matches a YAML date.
     *
     * @return string The regular expression
     *
     * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
     */
    private static function getTimestampRegex()
    {
        return <<<EOF
        ~^
        (?P<year>[0-9][0-9][0-9][0-9])
        -(?P<month>[0-9][0-9]?)
        -(?P<day>[0-9][0-9]?)
        (?:(?:[Tt]|[ \t]+)
        (?P<hour>[0-9][0-9]?)
        :(?P<minute>[0-9][0-9])
        :(?P<second>[0-9][0-9])
        (?:\.(?P<fraction>[0-9]*))?
        (?:[
\t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
        (?::(?P<tz_minute>[0-9][0-9]))?))?)?
        $~x
EOF;
    }

    /**
     * Gets a regex that matches a YAML number in hexadecimal notation.
     *
     * @return string
     */
    private static function getHexRegex()
    {
        return '~^0x[0-9a-f]++$~i';
    }
}
vendor/symfony/yaml/Parser.php000064400000100467151166614550012476
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\ParseException;

/**
 * Parser parses YAML strings to convert them to PHP arrays.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Parser
{
    const BLOCK_SCALAR_HEADER_PATTERN =
'(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments>
+#.*)?';
    // BC - wrongly named
    const FOLDED_SCALAR_PATTERN = self::BLOCK_SCALAR_HEADER_PATTERN;

    private $offset = 0;
    private $totalNumberOfLines;
    private $lines = array();
    private $currentLineNb = -1;
    private $currentLine = '';
    private $refs = array();
    private $skippedLineNumbers = array();
    private $locallySkippedLineNumbers = array();

    /**
     * @param int      $offset             The offset of YAML document
(used for line numbers in error messages)
     * @param int|null $totalNumberOfLines The overall number of lines
being parsed
     * @param int[]    $skippedLineNumbers Number of comment lines that
have been skipped by the parser
     */
    public function __construct($offset = 0, $totalNumberOfLines = null,
array $skippedLineNumbers = array())
    {
        $this->offset = $offset;
        $this->totalNumberOfLines = $totalNumberOfLines;
        $this->skippedLineNumbers = $skippedLineNumbers;
    }

    /**
     * Parses a YAML string to a PHP value.
     *
     * @param string $value                  A YAML string
     * @param bool   $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
     * @param bool   $objectSupport          True if object support is
enabled, false otherwise
     * @param bool   $objectForMap           True if maps should return a
stdClass instead of array()
     *
     * @return mixed A PHP value
     *
     * @throws ParseException If the YAML is not valid
     */
    public function parse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
    {
        if (false === preg_match('//u', $value)) {
            throw new ParseException('The YAML value does not appear
to be valid UTF-8.');
        }

        $this->refs = array();

        $mbEncoding = null;
        $e = null;
        $data = null;

        if (2 /* MB_OVERLOAD_STRING */ & (int)
ini_get('mbstring.func_overload')) {
            $mbEncoding = mb_internal_encoding();
            mb_internal_encoding('UTF-8');
        }

        try {
            $data = $this->doParse($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
        } catch (\Exception $e) {
        } catch (\Throwable $e) {
        }

        if (null !== $mbEncoding) {
            mb_internal_encoding($mbEncoding);
        }

        $this->lines = array();
        $this->currentLine = '';
        $this->refs = array();
        $this->skippedLineNumbers = array();
        $this->locallySkippedLineNumbers = array();

        if (null !== $e) {
            throw $e;
        }

        return $data;
    }

    private function doParse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
    {
        $this->currentLineNb = -1;
        $this->currentLine = '';
        $value = $this->cleanup($value);
        $this->lines = explode("\n", $value);
        $this->locallySkippedLineNumbers = array();

        if (null === $this->totalNumberOfLines) {
            $this->totalNumberOfLines = \count($this->lines);
        }

        $data = array();
        $context = null;
        $allowOverwrite = false;

        while ($this->moveToNextLine()) {
            if ($this->isCurrentLineEmpty()) {
                continue;
            }

            // tab?
            if ("\t" === $this->currentLine[0]) {
                throw new ParseException('A YAML file cannot contain
tabs as indentation.', $this->getRealCurrentLineNb() + 1,
$this->currentLine);
            }

            $isRef = $mergeNode = false;
            if
(self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u',
rtrim($this->currentLine), $values)) {
                if ($context && 'mapping' == $context) {
                    throw new ParseException('You cannot define a
sequence item when in a mapping', $this->getRealCurrentLineNb() +
1, $this->currentLine);
                }
                $context = 'sequence';

                if (isset($values['value']) &&
self::preg_match('#^&(?P<ref>[^ ]+)
*(?P<value>.*)#u', $values['value'], $matches)) {
                    $isRef = $matches['ref'];
                    $values['value'] =
$matches['value'];
                }

                // array
                if (!isset($values['value']) || '' ==
trim($values['value'], ' ') || 0 ===
strpos(ltrim($values['value'], ' '), '#')) {
                    $data[] =
$this->parseBlock($this->getRealCurrentLineNb() + 1,
$this->getNextEmbedBlock(null, true), $exceptionOnInvalidType,
$objectSupport, $objectForMap);
                } else {
                    if (isset($values['leadspaces'])
                        &&
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^
\'"\{\[].*?) *\:(\s+(?P<value>.+))?$#u',
rtrim($values['value']), $matches)
                    ) {
                        // this is a compact notation element, add to next
block and parse
                        $block = $values['value'];
                        if ($this->isNextLineIndented()) {
                            $block .=
"\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation()
+ \strlen($values['leadspaces']) + 1);
                        }

                        $data[] =
$this->parseBlock($this->getRealCurrentLineNb(), $block,
$exceptionOnInvalidType, $objectSupport, $objectForMap);
                    } else {
                        $data[] =
$this->parseValue($values['value'], $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context);
                    }
                }
                if ($isRef) {
                    $this->refs[$isRef] = end($data);
                }
            } elseif (
               
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^
\'"\[\{].*?) *\:(\s+(?P<value>.+))?$#u',
rtrim($this->currentLine), $values)
                && (false === strpos($values['key'],
' #') || \in_array($values['key'][0],
array('"', "'")))
            ) {
                if ($context && 'sequence' == $context) {
                    throw new ParseException('You cannot define a
mapping item when in a sequence', $this->currentLineNb + 1,
$this->currentLine);
                }
                $context = 'mapping';

                // force correct settings
                Inline::parse(null, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $this->refs);
                try {
                    $key = Inline::parseScalar($values['key']);
                } catch (ParseException $e) {
                    $e->setParsedLine($this->getRealCurrentLineNb() +
1);
                    $e->setSnippet($this->currentLine);

                    throw $e;
                }

                // Convert float keys to strings, to avoid being converted
to integers by PHP
                if (\is_float($key)) {
                    $key = (string) $key;
                }

                if ('<<' === $key &&
(!isset($values['value']) ||
!self::preg_match('#^&(?P<ref>[^ ]+)#u',
$values['value'], $refMatches))) {
                    $mergeNode = true;
                    $allowOverwrite = true;
                    if (isset($values['value']) && 0 ===
strpos($values['value'], '*')) {
                        $refName = substr($values['value'], 1);
                        if (!array_key_exists($refName, $this->refs)) {
                            throw new
ParseException(sprintf('Reference "%s" does not
exist.', $refName), $this->getRealCurrentLineNb() + 1,
$this->currentLine);
                        }

                        $refValue = $this->refs[$refName];

                        if (!\is_array($refValue)) {
                            throw new ParseException('YAML merge keys
used with a scalar value instead of an array.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
                        }

                        $data += $refValue; // array union
                    } else {
                        if (isset($values['value']) &&
'' !== $values['value']) {
                            $value = $values['value'];
                        } else {
                            $value = $this->getNextEmbedBlock();
                        }
                        $parsed =
$this->parseBlock($this->getRealCurrentLineNb() + 1, $value,
$exceptionOnInvalidType, $objectSupport, $objectForMap);

                        if (!\is_array($parsed)) {
                            throw new ParseException('YAML merge keys
used with a scalar value instead of an array.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
                        }

                        if (isset($parsed[0])) {
                            // If the value associated with the merge key
is a sequence, then this sequence is expected to contain mapping nodes
                            // and each of these nodes is merged in turn
according to its order in the sequence. Keys in mapping nodes earlier
                            // in the sequence override keys specified in
later mapping nodes.
                            foreach ($parsed as $parsedItem) {
                                if (!\is_array($parsedItem)) {
                                    throw new ParseException('Merge
items must be arrays.', $this->getRealCurrentLineNb() + 1,
$parsedItem);
                                }

                                $data += $parsedItem; // array union
                            }
                        } else {
                            // If the value associated with the key is a
single mapping node, each of its key/value pairs is inserted into the
                            // current mapping, unless the key already
exists in it.
                            $data += $parsed; // array union
                        }
                    }
                } elseif ('<<' !== $key &&
isset($values['value']) &&
self::preg_match('#^&(?P<ref>[^ ]+)
*(?P<value>.*)#u', $values['value'], $matches)) {
                    $isRef = $matches['ref'];
                    $values['value'] =
$matches['value'];
                }

                if ($mergeNode) {
                    // Merge keys
                } elseif (!isset($values['value']) ||
'' == trim($values['value'], ' ') || 0 ===
strpos(ltrim($values['value'], ' '), '#') ||
'<<' === $key) {
                    // hash
                    // if next line is less indented or equal, then it
means that the current value is null
                    if (!$this->isNextLineIndented() &&
!$this->isNextLineUnIndentedCollection()) {
                        // Spec: Keys MUST be unique; first one wins.
                        // But overwriting is allowed when a merge node is
used in current block.
                        if ($allowOverwrite || !isset($data[$key])) {
                            $data[$key] = null;
                        }
                    } else {
                        $value =
$this->parseBlock($this->getRealCurrentLineNb() + 1,
$this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport,
$objectForMap);

                        if ('<<' === $key) {
                            $this->refs[$refMatches['ref']] =
$value;
                            $data += $value;
                        } elseif ($allowOverwrite || !isset($data[$key])) {
                            // Spec: Keys MUST be unique; first one wins.
                            // But overwriting is allowed when a merge node
is used in current block.
                            $data[$key] = $value;
                        }
                    }
                } else {
                    $value =
$this->parseValue($values['value'], $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context);
                    // Spec: Keys MUST be unique; first one wins.
                    // But overwriting is allowed when a merge node is used
in current block.
                    if ($allowOverwrite || !isset($data[$key])) {
                        $data[$key] = $value;
                    }
                }
                if ($isRef) {
                    $this->refs[$isRef] = $data[$key];
                }
            } else {
                // multiple documents are not supported
                if ('---' === $this->currentLine) {
                    throw new ParseException('Multiple documents are
not supported.', $this->currentLineNb + 1, $this->currentLine);
                }

                // 1-liner optionally followed by newline(s)
                if (\is_string($value) && $this->lines[0] ===
trim($value)) {
                    try {
                        $value = Inline::parse($this->lines[0],
$exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
                    } catch (ParseException $e) {
                       
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
                        $e->setSnippet($this->currentLine);

                        throw $e;
                    }

                    return $value;
                }

                throw new ParseException('Unable to parse.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        }

        if ($objectForMap && !\is_object($data) &&
'mapping' === $context) {
            $object = new \stdClass();

            foreach ($data as $key => $value) {
                $object->$key = $value;
            }

            $data = $object;
        }

        return empty($data) ? null : $data;
    }

    private function parseBlock($offset, $yaml, $exceptionOnInvalidType,
$objectSupport, $objectForMap)
    {
        $skippedLineNumbers = $this->skippedLineNumbers;

        foreach ($this->locallySkippedLineNumbers as $lineNumber) {
            if ($lineNumber < $offset) {
                continue;
            }

            $skippedLineNumbers[] = $lineNumber;
        }

        $parser = new self($offset, $this->totalNumberOfLines,
$skippedLineNumbers);
        $parser->refs = &$this->refs;

        return $parser->doParse($yaml, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
    }

    /**
     * Returns the current line number (takes the offset into account).
     *
     * @return int The current line number
     */
    private function getRealCurrentLineNb()
    {
        $realCurrentLineNumber = $this->currentLineNb +
$this->offset;

        foreach ($this->skippedLineNumbers as $skippedLineNumber) {
            if ($skippedLineNumber > $realCurrentLineNumber) {
                break;
            }

            ++$realCurrentLineNumber;
        }

        return $realCurrentLineNumber;
    }

    /**
     * Returns the current line indentation.
     *
     * @return int The current line indentation
     */
    private function getCurrentLineIndentation()
    {
        return \strlen($this->currentLine) -
\strlen(ltrim($this->currentLine, ' '));
    }

    /**
     * Returns the next embed block of YAML.
     *
     * @param int  $indentation The indent level at which the block is to
be read, or null for default
     * @param bool $inSequence  True if the enclosing data structure is a
sequence
     *
     * @return string A YAML string
     *
     * @throws ParseException When indentation problem are detected
     */
    private function getNextEmbedBlock($indentation = null, $inSequence =
false)
    {
        $oldLineIndentation = $this->getCurrentLineIndentation();
        $blockScalarIndentations = array();

        if ($this->isBlockScalarHeader()) {
            $blockScalarIndentations[] =
$this->getCurrentLineIndentation();
        }

        if (!$this->moveToNextLine()) {
            return;
        }

        if (null === $indentation) {
            $newIndent = $this->getCurrentLineIndentation();

            $unindentedEmbedBlock =
$this->isStringUnIndentedCollectionItem();

            if (!$this->isCurrentLineEmpty() && 0 === $newIndent
&& !$unindentedEmbedBlock) {
                throw new ParseException('Indentation problem.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        } else {
            $newIndent = $indentation;
        }

        $data = array();
        if ($this->getCurrentLineIndentation() >= $newIndent) {
            $data[] = substr($this->currentLine, $newIndent);
        } else {
            $this->moveToPreviousLine();

            return;
        }

        if ($inSequence && $oldLineIndentation === $newIndent
&& isset($data[0][0]) && '-' === $data[0][0]) {
            // the previous line contained a dash but no item content, this
line is a sequence item with the same indentation
            // and therefore no nested list or mapping
            $this->moveToPreviousLine();

            return;
        }

        $isItUnindentedCollection =
$this->isStringUnIndentedCollectionItem();

        if (empty($blockScalarIndentations) &&
$this->isBlockScalarHeader()) {
            $blockScalarIndentations[] =
$this->getCurrentLineIndentation();
        }

        $previousLineIndentation = $this->getCurrentLineIndentation();

        while ($this->moveToNextLine()) {
            $indent = $this->getCurrentLineIndentation();

            // terminate all block scalars that are more indented than the
current line
            if (!empty($blockScalarIndentations) && $indent <
$previousLineIndentation && '' !==
trim($this->currentLine)) {
                foreach ($blockScalarIndentations as $key =>
$blockScalarIndentation) {
                    if ($blockScalarIndentation >=
$this->getCurrentLineIndentation()) {
                        unset($blockScalarIndentations[$key]);
                    }
                }
            }

            if (empty($blockScalarIndentations) &&
!$this->isCurrentLineComment() &&
$this->isBlockScalarHeader()) {
                $blockScalarIndentations[] =
$this->getCurrentLineIndentation();
            }

            $previousLineIndentation = $indent;

            if ($isItUnindentedCollection &&
!$this->isCurrentLineEmpty() &&
!$this->isStringUnIndentedCollectionItem() && $newIndent ===
$indent) {
                $this->moveToPreviousLine();
                break;
            }

            if ($this->isCurrentLineBlank()) {
                $data[] = substr($this->currentLine, $newIndent);
                continue;
            }

            // we ignore "comment" lines only when we are not
inside a scalar block
            if (empty($blockScalarIndentations) &&
$this->isCurrentLineComment()) {
                // remember ignored comment lines (they are used later in
nested
                // parser calls to determine real line numbers)
                //
                // CAUTION: beware to not populate the global property here
as it
                // will otherwise influence the getRealCurrentLineNb() call
here
                // for consecutive comment lines and subsequent embedded
blocks
                $this->locallySkippedLineNumbers[] =
$this->getRealCurrentLineNb();

                continue;
            }

            if ($indent >= $newIndent) {
                $data[] = substr($this->currentLine, $newIndent);
            } elseif (0 == $indent) {
                $this->moveToPreviousLine();

                break;
            } else {
                throw new ParseException('Indentation problem.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
            }
        }

        return implode("\n", $data);
    }

    /**
     * Moves the parser to the next line.
     *
     * @return bool
     */
    private function moveToNextLine()
    {
        if ($this->currentLineNb >= \count($this->lines) - 1) {
            return false;
        }

        $this->currentLine = $this->lines[++$this->currentLineNb];

        return true;
    }

    /**
     * Moves the parser to the previous line.
     *
     * @return bool
     */
    private function moveToPreviousLine()
    {
        if ($this->currentLineNb < 1) {
            return false;
        }

        $this->currentLine = $this->lines[--$this->currentLineNb];

        return true;
    }

    /**
     * Parses a YAML value.
     *
     * @param string $value                  A YAML value
     * @param bool   $exceptionOnInvalidType True if an exception must be
thrown on invalid types false otherwise
     * @param bool   $objectSupport          True if object support is
enabled, false otherwise
     * @param bool   $objectForMap           True if maps should return a
stdClass instead of array()
     * @param string $context                The parser context (either
sequence or mapping)
     *
     * @return mixed A PHP value
     *
     * @throws ParseException When reference does not exist
     */
    private function parseValue($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context)
    {
        if (0 === strpos($value, '*')) {
            if (false !== $pos = strpos($value, '#')) {
                $value = substr($value, 1, $pos - 2);
            } else {
                $value = substr($value, 1);
            }

            if (!array_key_exists($value, $this->refs)) {
                throw new ParseException(sprintf('Reference
"%s" does not exist.', $value), $this->currentLineNb + 1,
$this->currentLine);
            }

            return $this->refs[$value];
        }

        if
(self::preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/',
$value, $matches)) {
            $modifiers = isset($matches['modifiers']) ?
$matches['modifiers'] : '';

            return
$this->parseBlockScalar($matches['separator'],
preg_replace('#\d+#', '', $modifiers), (int)
abs($modifiers));
        }

        try {
            $parsedValue = Inline::parse($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $this->refs);

            if ('mapping' === $context &&
'"' !== $value[0] && "'" !==
$value[0] && '[' !== $value[0] && '{'
!== $value[0] && '!' !== $value[0] && false !==
strpos($parsedValue, ': ')) {
                @trigger_error(sprintf('Using a colon in the unquoted
mapping value "%s" in line %d is deprecated since Symfony 2.8 and
will throw a ParseException in 3.0.', $value,
$this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);

                // to be thrown in 3.0
                // throw new ParseException('A colon cannot be used in
an unquoted mapping value.');
            }

            return $parsedValue;
        } catch (ParseException $e) {
            $e->setParsedLine($this->getRealCurrentLineNb() + 1);
            $e->setSnippet($this->currentLine);

            throw $e;
        }
    }

    /**
     * Parses a block scalar.
     *
     * @param string $style       The style indicator that was used to
begin this block scalar (| or >)
     * @param string $chomping    The chomping indicator that was used to
begin this block scalar (+ or -)
     * @param int    $indentation The indentation indicator that was used
to begin this block scalar
     *
     * @return string The text value
     */
    private function parseBlockScalar($style, $chomping = '',
$indentation = 0)
    {
        $notEOF = $this->moveToNextLine();
        if (!$notEOF) {
            return '';
        }

        $isCurrentLineBlank = $this->isCurrentLineBlank();
        $blockLines = array();

        // leading blank lines are consumed before determining indentation
        while ($notEOF && $isCurrentLineBlank) {
            // newline only if not EOF
            if ($notEOF = $this->moveToNextLine()) {
                $blockLines[] = '';
                $isCurrentLineBlank = $this->isCurrentLineBlank();
            }
        }

        // determine indentation if not specified
        if (0 === $indentation) {
            if (self::preg_match('/^ +/', $this->currentLine,
$matches)) {
                $indentation = \strlen($matches[0]);
            }
        }

        if ($indentation > 0) {
            $pattern = sprintf('/^ {%d}(.*)$/', $indentation);

            while (
                $notEOF && (
                    $isCurrentLineBlank ||
                    self::preg_match($pattern, $this->currentLine,
$matches)
                )
            ) {
                if ($isCurrentLineBlank &&
\strlen($this->currentLine) > $indentation) {
                    $blockLines[] = substr($this->currentLine,
$indentation);
                } elseif ($isCurrentLineBlank) {
                    $blockLines[] = '';
                } else {
                    $blockLines[] = $matches[1];
                }

                // newline only if not EOF
                if ($notEOF = $this->moveToNextLine()) {
                    $isCurrentLineBlank = $this->isCurrentLineBlank();
                }
            }
        } elseif ($notEOF) {
            $blockLines[] = '';
        }

        if ($notEOF) {
            $blockLines[] = '';
            $this->moveToPreviousLine();
        } elseif (!$notEOF &&
!$this->isCurrentLineLastLineInDocument()) {
            $blockLines[] = '';
        }

        // folded style
        if ('>' === $style) {
            $text = '';
            $previousLineIndented = false;
            $previousLineBlank = false;

            for ($i = 0, $blockLinesCount = \count($blockLines); $i <
$blockLinesCount; ++$i) {
                if ('' === $blockLines[$i]) {
                    $text .= "\n";
                    $previousLineIndented = false;
                    $previousLineBlank = true;
                } elseif (' ' === $blockLines[$i][0]) {
                    $text .= "\n".$blockLines[$i];
                    $previousLineIndented = true;
                    $previousLineBlank = false;
                } elseif ($previousLineIndented) {
                    $text .= "\n".$blockLines[$i];
                    $previousLineIndented = false;
                    $previousLineBlank = false;
                } elseif ($previousLineBlank || 0 === $i) {
                    $text .= $blockLines[$i];
                    $previousLineIndented = false;
                    $previousLineBlank = false;
                } else {
                    $text .= ' '.$blockLines[$i];
                    $previousLineIndented = false;
                    $previousLineBlank = false;
                }
            }
        } else {
            $text = implode("\n", $blockLines);
        }

        // deal with trailing newlines
        if ('' === $chomping) {
            $text = preg_replace('/\n+$/', "\n",
$text);
        } elseif ('-' === $chomping) {
            $text = preg_replace('/\n+$/', '', $text);
        }

        return $text;
    }

    /**
     * Returns true if the next line is indented.
     *
     * @return bool Returns true if the next line is indented, false
otherwise
     */
    private function isNextLineIndented()
    {
        $currentIndentation = $this->getCurrentLineIndentation();
        $EOF = !$this->moveToNextLine();

        while (!$EOF && $this->isCurrentLineEmpty()) {
            $EOF = !$this->moveToNextLine();
        }

        if ($EOF) {
            return false;
        }

        $ret = $this->getCurrentLineIndentation() >
$currentIndentation;

        $this->moveToPreviousLine();

        return $ret;
    }

    /**
     * Returns true if the current line is blank or if it is a comment
line.
     *
     * @return bool Returns true if the current line is empty or if it is a
comment line, false otherwise
     */
    private function isCurrentLineEmpty()
    {
        return $this->isCurrentLineBlank() ||
$this->isCurrentLineComment();
    }

    /**
     * Returns true if the current line is blank.
     *
     * @return bool Returns true if the current line is blank, false
otherwise
     */
    private function isCurrentLineBlank()
    {
        return '' == trim($this->currentLine, ' ');
    }

    /**
     * Returns true if the current line is a comment line.
     *
     * @return bool Returns true if the current line is a comment line,
false otherwise
     */
    private function isCurrentLineComment()
    {
        //checking explicitly the first char of the trim is faster than
loops or strpos
        $ltrimmedLine = ltrim($this->currentLine, ' ');

        return '' !== $ltrimmedLine && '#' ===
$ltrimmedLine[0];
    }

    private function isCurrentLineLastLineInDocument()
    {
        return ($this->offset + $this->currentLineNb) >=
($this->totalNumberOfLines - 1);
    }

    /**
     * Cleanups a YAML string to be parsed.
     *
     * @param string $value The input YAML string
     *
     * @return string A cleaned up YAML string
     */
    private function cleanup($value)
    {
        $value = str_replace(array("\r\n", "\r"),
"\n", $value);

        // strip YAML header
        $count = 0;
        $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u',
'', $value, -1, $count);
        $this->offset += $count;

        // remove leading comments
        $trimmedValue = preg_replace('#^(\#.*?\n)+#s',
'', $value, -1, $count);
        if (1 == $count) {
            // items have been removed, update the offset
            $this->offset += substr_count($value, "\n") -
substr_count($trimmedValue, "\n");
            $value = $trimmedValue;
        }

        // remove start of the document marker (---)
        $trimmedValue = preg_replace('#^\-\-\-.*?\n#s',
'', $value, -1, $count);
        if (1 == $count) {
            // items have been removed, update the offset
            $this->offset += substr_count($value, "\n") -
substr_count($trimmedValue, "\n");
            $value = $trimmedValue;

            // remove end of the document marker (...)
            $value = preg_replace('#\.\.\.\s*$#', '',
$value);
        }

        return $value;
    }

    /**
     * Returns true if the next line starts unindented collection.
     *
     * @return bool Returns true if the next line starts unindented
collection, false otherwise
     */
    private function isNextLineUnIndentedCollection()
    {
        $currentIndentation = $this->getCurrentLineIndentation();
        $notEOF = $this->moveToNextLine();

        while ($notEOF && $this->isCurrentLineEmpty()) {
            $notEOF = $this->moveToNextLine();
        }

        if (false === $notEOF) {
            return false;
        }

        $ret = $this->getCurrentLineIndentation() ===
$currentIndentation &&
$this->isStringUnIndentedCollectionItem();

        $this->moveToPreviousLine();

        return $ret;
    }

    /**
     * Returns true if the string is un-indented collection item.
     *
     * @return bool Returns true if the string is un-indented collection
item, false otherwise
     */
    private function isStringUnIndentedCollectionItem()
    {
        return '-' === rtrim($this->currentLine) || 0 ===
strpos($this->currentLine, '- ');
    }

    /**
     * Tests whether or not the current line is the header of a block
scalar.
     *
     * @return bool
     */
    private function isBlockScalarHeader()
    {
        return (bool)
self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~',
$this->currentLine);
    }

    /**
     * A local wrapper for `preg_match` which will throw a ParseException
if there
     * is an internal error in the PCRE engine.
     *
     * This avoids us needing to check for "false" every time
PCRE is used
     * in the YAML engine
     *
     * @throws ParseException on a PCRE internal error
     *
     * @see preg_last_error()
     *
     * @internal
     */
    public static function preg_match($pattern, $subject, &$matches =
null, $flags = 0, $offset = 0)
    {
        if (false === $ret = preg_match($pattern, $subject, $matches,
$flags, $offset)) {
            switch (preg_last_error()) {
                case PREG_INTERNAL_ERROR:
                    $error = 'Internal PCRE error.';
                    break;
                case PREG_BACKTRACK_LIMIT_ERROR:
                    $error = 'pcre.backtrack_limit reached.';
                    break;
                case PREG_RECURSION_LIMIT_ERROR:
                    $error = 'pcre.recursion_limit reached.';
                    break;
                case PREG_BAD_UTF8_ERROR:
                    $error = 'Malformed UTF-8 data.';
                    break;
                case PREG_BAD_UTF8_OFFSET_ERROR:
                    $error = 'Offset doesn\'t correspond to the
begin of a valid UTF-8 code point.';
                    break;
                default:
                    $error = 'Error.';
            }

            throw new ParseException($error);
        }

        return $ret;
    }
}
vendor/symfony/yaml/Unescaper.php000064400000010516151166614550013162
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

/**
 * Unescaper encapsulates unescaping rules for single and double-quoted
 * YAML strings.
 *
 * @author Matthew Lewinski <matthew@lewinski.org>
 *
 * @internal
 */
class Unescaper
{
    /**
     * Parser and Inline assume UTF-8 encoding, so escaped Unicode
characters
     * must be converted to that encoding.
     *
     * @deprecated since version 2.5, to be removed in 3.0
     *
     * @internal
     */
    const ENCODING = 'UTF-8';

    /**
     * Regex fragment that matches an escaped character in a double quoted
string.
     */
    const REGEX_ESCAPED_CHARACTER =
'\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)';

    /**
     * Unescapes a single quoted string.
     *
     * @param string $value A single quoted string
     *
     * @return string The unescaped string
     */
    public function unescapeSingleQuotedString($value)
    {
        return str_replace('\'\'', '\'',
$value);
    }

    /**
     * Unescapes a double quoted string.
     *
     * @param string $value A double quoted string
     *
     * @return string The unescaped string
     */
    public function unescapeDoubleQuotedString($value)
    {
        $self = $this;
        $callback = function ($match) use ($self) {
            return $self->unescapeCharacter($match[0]);
        };

        // evaluate the string
        return
preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u',
$callback, $value);
    }

    /**
     * Unescapes a character that was found in a double-quoted string.
     *
     * @param string $value An escaped character
     *
     * @return string The unescaped character
     *
     * @internal This method is public to be usable as callback. It should
not
     *           be used in user code. Should be changed in 3.0.
     */
    public function unescapeCharacter($value)
    {
        switch ($value[1]) {
            case '0':
                return "\x0";
            case 'a':
                return "\x7";
            case 'b':
                return "\x8";
            case 't':
                return "\t";
            case "\t":
                return "\t";
            case 'n':
                return "\n";
            case 'v':
                return "\xB";
            case 'f':
                return "\xC";
            case 'r':
                return "\r";
            case 'e':
                return "\x1B";
            case ' ':
                return ' ';
            case '"':
                return '"';
            case '/':
                return '/';
            case '\\':
                return '\\';
            case 'N':
                // U+0085 NEXT LINE
                return "\xC2\x85";
            case '_':
                // U+00A0 NO-BREAK SPACE
                return "\xC2\xA0";
            case 'L':
                // U+2028 LINE SEPARATOR
                return "\xE2\x80\xA8";
            case 'P':
                // U+2029 PARAGRAPH SEPARATOR
                return "\xE2\x80\xA9";
            case 'x':
                return self::utf8chr(hexdec(substr($value, 2, 2)));
            case 'u':
                return self::utf8chr(hexdec(substr($value, 2, 4)));
            case 'U':
                return self::utf8chr(hexdec(substr($value, 2, 8)));
            default:
                @trigger_error('Not escaping a backslash in a
double-quoted string is deprecated since Symfony 2.8 and will throw a
ParseException in 3.0.', E_USER_DEPRECATED);

                return $value;
        }
    }

    /**
     * Get the UTF-8 character for the given code point.
     *
     * @param int $c The unicode code point
     *
     * @return string The corresponding UTF-8 character
     */
    private static function utf8chr($c)
    {
        if (0x80 > $c %= 0x200000) {
            return \chr($c);
        }
        if (0x800 > $c) {
            return \chr(0xC0 | $c >> 6).\chr(0x80 | $c & 0x3F);
        }
        if (0x10000 > $c) {
            return \chr(0xE0 | $c >> 12).\chr(0x80 | $c >> 6
& 0x3F).\chr(0x80 | $c & 0x3F);
        }

        return \chr(0xF0 | $c >> 18).\chr(0x80 | $c >> 12 &
0x3F).\chr(0x80 | $c >> 6 & 0x3F).\chr(0x80 | $c & 0x3F);
    }
}
vendor/symfony/yaml/Yaml.php000064400000007327151166614550012145
0ustar00<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Yaml;

use Symfony\Component\Yaml\Exception\ParseException;

/**
 * Yaml offers convenience methods to load and dump YAML.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Yaml
{
    /**
     * Parses YAML into a PHP value.
     *
     * Usage:
     *
     *     $array = Yaml::parse(file_get_contents('config.yml'));
     *     print_r($array);
     *
     * As this method accepts both plain strings and file names as an
input,
     * you must validate the input before calling this method. Passing a
file
     * as an input is a deprecated feature and will be removed in 3.0.
     *
     * Note: the ability to pass file names to the Yaml::parse method is
deprecated since Symfony 2.2 and will be removed in 3.0. Pass the YAML
contents of the file instead.
     *
     * @param string $input                  Path to a YAML file or a
string containing YAML
     * @param bool   $exceptionOnInvalidType True if an exception must be
thrown on invalid types false otherwise
     * @param bool   $objectSupport          True if object support is
enabled, false otherwise
     * @param bool   $objectForMap           True if maps should return a
stdClass instead of array()
     *
     * @return mixed The YAML converted to a PHP value
     *
     * @throws ParseException If the YAML is not valid
     */
    public static function parse($input, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
    {
        // if input is a file, process it
        $file = '';
        if (false === strpos($input, "\n") &&
is_file($input)) {
            @trigger_error('The ability to pass file names to the
'.__METHOD__.' method is deprecated since Symfony 2.2 and will be
removed in 3.0. Pass the YAML contents of the file instead.',
E_USER_DEPRECATED);

            if (false === is_readable($input)) {
                throw new ParseException(sprintf('Unable to parse
"%s" as the file is not readable.', $input));
            }

            $file = $input;
            $input = file_get_contents($file);
        }

        $yaml = new Parser();

        try {
            return $yaml->parse($input, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
        } catch (ParseException $e) {
            if ($file) {
                $e->setParsedFile($file);
            }

            throw $e;
        }
    }

    /**
     * Dumps a PHP value to a YAML string.
     *
     * The dump method, when supplied with an array, will do its best
     * to convert the array into friendly YAML.
     *
     * @param mixed $input                  The PHP value
     * @param int   $inline                 The level where you switch to
inline YAML
     * @param int   $indent                 The amount of spaces to use for
indentation of nested nodes
     * @param bool  $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
     * @param bool  $objectSupport          True if object support is
enabled, false otherwise
     *
     * @return string A YAML string representing the original PHP value
     */
    public static function dump($input, $inline = 2, $indent = 4,
$exceptionOnInvalidType = false, $objectSupport = false)
    {
        if ($indent < 1) {
            throw new \InvalidArgumentException('The indentation must
be greater than zero.');
        }

        $yaml = new Dumper();
        $yaml->setIndentation($indent);

        return $yaml->dump($input, $inline, 0, $exceptionOnInvalidType,
$objectSupport);
    }
}
vendor/twig/twig/.php_cs.dist000064400000001437151166614550012223
0ustar00<?php

return PhpCsFixer\Config::create()
    ->setRules([
        '@Symfony' => true,
        '@Symfony:risky' => true,
        '@PHPUnit75Migration:risky' => true,
        'php_unit_dedicate_assert' => ['target'
=> '5.6'],
        'array_syntax' => ['syntax' =>
'short'],
        'php_unit_fqcn_annotation' => true,
        'no_unreachable_default_argument_value' => false,
        'braces' => ['allow_single_line_closure'
=> true],
        'heredoc_to_nowdoc' => false,
        'ordered_imports' => true,
        'phpdoc_types_order' => ['null_adjustment'
=> 'always_last', 'sort_algorithm' =>
'none'],
        'native_function_invocation' => ['include'
=> ['@compiler_optimized'], 'scope' =>
'all'],
    ])
    ->setRiskyAllowed(true)
    ->setFinder(PhpCsFixer\Finder::create()->in(__DIR__))
;
vendor/twig/twig/composer.json000064400000002323151166614550012521
0ustar00{
    "name": "twig/twig",
    "type": "library",
    "description": "Twig, the flexible, fast, and secure
template language for PHP",
    "keywords": ["templating"],
    "homepage": "https://twig.symfony.com",
    "license": "BSD-3-Clause",
    "authors": [
        {
            "name": "Fabien Potencier",
            "email": "fabien@symfony.com",
            "homepage": "http://fabien.potencier.org",
            "role": "Lead Developer"
        },
        {
            "name": "Twig Team",
            "role": "Contributors"
        },
        {
            "name": "Armin Ronacher",
            "email": "armin.ronacher@active-4.com",
            "role": "Project Founder"
        }
    ],
    "require": {
        "php": ">=5.5.0",
        "symfony/polyfill-ctype": "^1.8"
    },
    "require-dev": {
        "symfony/phpunit-bridge": "^4.4|^5.0",
        "psr/container": "^1.0"
    },
    "autoload": {
        "psr-0" : {
            "Twig_" : "lib/"
        },
        "psr-4" : {
            "Twig\\" : "src/"
        }
    },
    "autoload-dev": {
        "psr-4" : {
            "Twig\\Tests\\" : "tests"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.42-dev"
        }
    }
}
vendor/twig/twig/lib/Twig/Autoloader.php000064400000002710151166614550014267
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

@trigger_error('The Twig_Autoloader class is deprecated since version
1.21 and will be removed in 2.0. Use Composer instead.',
E_USER_DEPRECATED);

/**
 * Autoloads Twig classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.21 and will be removed in 2.0. Use Composer instead.
2.0.
 */
class Twig_Autoloader
{
    /**
     * Registers Twig_Autoloader as an SPL autoloader.
     *
     * @param bool $prepend whether to prepend the autoloader or not
     */
    public static function register($prepend = false)
    {
        @trigger_error('Using Twig_Autoloader is deprecated since
version 1.21. Use Composer instead.', E_USER_DEPRECATED);

        spl_autoload_register([__CLASS__, 'autoload'], true,
$prepend);
    }

    /**
     * Handles autoloading of classes.
     *
     * @param string $class a class name
     */
    public static function autoload($class)
    {
        if (0 !== strpos($class, 'Twig')) {
            return;
        }

        if (is_file($file =
__DIR__.'/../'.str_replace(['_', "\0"],
['/', ''], $class).'.php')) {
            require $file;
        } elseif (is_file($file =
__DIR__.'/../../src/'.str_replace(['Twig\\',
'\\', "\0"], ['', '/',
''], $class).'.php')) {
            require $file;
        }
    }
}
vendor/twig/twig/lib/Twig/BaseNodeVisitor.php000064400000000300151166614550015221
0ustar00<?php

use Twig\NodeVisitor\AbstractNodeVisitor;

class_exists('Twig\NodeVisitor\AbstractNodeVisitor');

if (\false) {
    class Twig_BaseNodeVisitor extends AbstractNodeVisitor
    {
    }
}
vendor/twig/twig/lib/Twig/Cache/Filesystem.php000064400000000251151166614550015315
0ustar00<?php

use Twig\Cache\FilesystemCache;

class_exists('Twig\Cache\FilesystemCache');

if (\false) {
    class Twig_Cache_Filesystem extends FilesystemCache
    {
    }
}
vendor/twig/twig/lib/Twig/Cache/Null.php000064400000000221151166614550014100
0ustar00<?php

use Twig\Cache\NullCache;

class_exists('Twig\Cache\NullCache');

if (\false) {
    class Twig_Cache_Null extends NullCache
    {
    }
}
vendor/twig/twig/lib/Twig/CacheInterface.php000064400000000244151166614550015014
0ustar00<?php

use Twig\Cache\CacheInterface;

class_exists('Twig\Cache\CacheInterface');

if (\false) {
    class Twig_CacheInterface extends CacheInterface
    {
    }
}
vendor/twig/twig/lib/Twig/Compiler.php000064400000000200151166614550013732
0ustar00<?php

use Twig\Compiler;

class_exists('Twig\Compiler');

if (\false) {
    class Twig_Compiler extends Compiler
    {
    }
}
vendor/twig/twig/lib/Twig/CompilerInterface.php000064400000001230151166614550015557
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Interface implemented by compiler classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 3.0)
 */
interface Twig_CompilerInterface
{
    /**
     * Compiles a node.
     *
     * @return $this
     */
    public function compile(Twig_NodeInterface $node);

    /**
     * Gets the current PHP code after compilation.
     *
     * @return string The PHP code
     */
    public function getSource();
}
vendor/twig/twig/lib/Twig/ContainerRuntimeLoader.php000064400000000324151166614550016604
0ustar00<?php

use Twig\RuntimeLoader\ContainerRuntimeLoader;

class_exists('Twig\RuntimeLoader\ContainerRuntimeLoader');

if (\false) {
    class Twig_ContainerRuntimeLoader extends ContainerRuntimeLoader
    {
    }
}
vendor/twig/twig/lib/Twig/Environment.php000064400000000214151166614550014471
0ustar00<?php

use Twig\Environment;

class_exists('Twig\Environment');

if (\false) {
    class Twig_Environment extends Environment
    {
    }
}
vendor/twig/twig/lib/Twig/Error/Loader.php000064400000000231151166614550014463
0ustar00<?php

use Twig\Error\LoaderError;

class_exists('Twig\Error\LoaderError');

if (\false) {
    class Twig_Error_Loader extends LoaderError
    {
    }
}
vendor/twig/twig/lib/Twig/Error/Runtime.php000064400000000235151166614550014704
0ustar00<?php

use Twig\Error\RuntimeError;

class_exists('Twig\Error\RuntimeError');

if (\false) {
    class Twig_Error_Runtime extends RuntimeError
    {
    }
}
vendor/twig/twig/lib/Twig/Error/Syntax.php000064400000000231151166614550014543
0ustar00<?php

use Twig\Error\SyntaxError;

class_exists('Twig\Error\SyntaxError');

if (\false) {
    class Twig_Error_Syntax extends SyntaxError
    {
    }
}
vendor/twig/twig/lib/Twig/Error.php000064400000000200151166614550013251
0ustar00<?php

use Twig\Error\Error;

class_exists('Twig\Error\Error');

if (\false) {
    class Twig_Error extends Error
    {
    }
}
vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php000064400000000302151166614550016412
0ustar00<?php

use Twig\Loader\ExistsLoaderInterface;

class_exists('Twig\Loader\ExistsLoaderInterface');

if (\false) {
    class Twig_ExistsLoaderInterface extends ExistsLoaderInterface
    {
    }
}
vendor/twig/twig/lib/Twig/ExpressionParser.php000064400000000240151166614550015500
0ustar00<?php

use Twig\ExpressionParser;

class_exists('Twig\ExpressionParser');

if (\false) {
    class Twig_ExpressionParser extends ExpressionParser
    {
    }
}
vendor/twig/twig/lib/Twig/Extension/Core.php000064400000000251151166614550015032
0ustar00<?php

use Twig\Extension\CoreExtension;

class_exists('Twig\Extension\CoreExtension');

if (\false) {
    class Twig_Extension_Core extends CoreExtension
    {
    }
}
vendor/twig/twig/lib/Twig/Extension/Debug.php000064400000000255151166614550015174
0ustar00<?php

use Twig\Extension\DebugExtension;

class_exists('Twig\Extension\DebugExtension');

if (\false) {
    class Twig_Extension_Debug extends DebugExtension
    {
    }
}
vendor/twig/twig/lib/Twig/Extension/Escaper.php000064400000000265151166614550015531
0ustar00<?php

use Twig\Extension\EscaperExtension;

class_exists('Twig\Extension\EscaperExtension');

if (\false) {
    class Twig_Extension_Escaper extends EscaperExtension
    {
    }
}
vendor/twig/twig/lib/Twig/Extension/GlobalsInterface.php000064400000000276151166614570017357
0ustar00<?php

use Twig\Extension\GlobalsInterface;

class_exists('Twig\Extension\GlobalsInterface');

if (\false) {
    class Twig_Extension_GlobalsInterface extends GlobalsInterface
    {
    }
}
vendor/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php000064400000000316151166614570020236
0ustar00<?php

use Twig\Extension\InitRuntimeInterface;

class_exists('Twig\Extension\InitRuntimeInterface');

if (\false) {
    class Twig_Extension_InitRuntimeInterface extends InitRuntimeInterface
    {
    }
}
vendor/twig/twig/lib/Twig/Extension/Optimizer.php000064400000000275151166614570016134
0ustar00<?php

use Twig\Extension\OptimizerExtension;

class_exists('Twig\Extension\OptimizerExtension');

if (\false) {
    class Twig_Extension_Optimizer extends OptimizerExtension
    {
    }
}
vendor/twig/twig/lib/Twig/Extension/Profiler.php000064400000000271151166614570015730
0ustar00<?php

use Twig\Extension\ProfilerExtension;

class_exists('Twig\Extension\ProfilerExtension');

if (\false) {
    class Twig_Extension_Profiler extends ProfilerExtension
    {
    }
}
vendor/twig/twig/lib/Twig/Extension/Sandbox.php000064400000000265151166614570015547
0ustar00<?php

use Twig\Extension\SandboxExtension;

class_exists('Twig\Extension\SandboxExtension');

if (\false) {
    class Twig_Extension_Sandbox extends SandboxExtension
    {
    }
}
vendor/twig/twig/lib/Twig/Extension/Staging.php000064400000000265151166614570015545
0ustar00<?php

use Twig\Extension\StagingExtension;

class_exists('Twig\Extension\StagingExtension');

if (\false) {
    class Twig_Extension_Staging extends StagingExtension
    {
    }
}
vendor/twig/twig/lib/Twig/Extension/StringLoader.php000064400000000311151166614570016536
0ustar00<?php

use Twig\Extension\StringLoaderExtension;

class_exists('Twig\Extension\StringLoaderExtension');

if (\false) {
    class Twig_Extension_StringLoader extends StringLoaderExtension
    {
    }
}
vendor/twig/twig/lib/Twig/Extension.php000064400000000260151166614570014144
0ustar00<?php

use Twig\Extension\AbstractExtension;

class_exists('Twig\Extension\AbstractExtension');

if (\false) {
    class Twig_Extension extends AbstractExtension
    {
    }
}
vendor/twig/twig/lib/Twig/ExtensionInterface.php000064400000000274151166614570015772
0ustar00<?php

use Twig\Extension\ExtensionInterface;

class_exists('Twig\Extension\ExtensionInterface');

if (\false) {
    class Twig_ExtensionInterface extends ExtensionInterface
    {
    }
}
vendor/twig/twig/lib/Twig/FactoryRuntimeLoader.php000064400000000314151166614570016272
0ustar00<?php

use Twig\RuntimeLoader\FactoryRuntimeLoader;

class_exists('Twig\RuntimeLoader\FactoryRuntimeLoader');

if (\false) {
    class Twig_FactoryRuntimeLoader extends FactoryRuntimeLoader
    {
    }
}
vendor/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php000064400000000324151166614570020142
0ustar00<?php

use Twig\FileExtensionEscapingStrategy;

class_exists('Twig\FileExtensionEscapingStrategy');

if (\false) {
    class Twig_FileExtensionEscapingStrategy extends
FileExtensionEscapingStrategy
    {
    }
}
vendor/twig/twig/lib/Twig/Filter/Function.php000064400000001605151166614570015206
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

@trigger_error('The Twig_Filter_Function class is deprecated since
version 1.12 and will be removed in 2.0. Use \Twig\TwigFilter
instead.', E_USER_DEPRECATED);

/**
 * Represents a function template filter.
 *
 * Use \Twig\TwigFilter instead.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
class Twig_Filter_Function extends Twig_Filter
{
    protected $function;

    public function __construct($function, array $options = [])
    {
        $options['callable'] = $function;

        parent::__construct($options);

        $this->function = $function;
    }

    public function compile()
    {
        return $this->function;
    }
}
vendor/twig/twig/lib/Twig/Filter/Method.php000064400000002130151166614570014633
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Extension\ExtensionInterface;

@trigger_error('The Twig_Filter_Method class is deprecated since
version 1.12 and will be removed in 2.0. Use \Twig\TwigFilter
instead.', E_USER_DEPRECATED);

/**
 * Represents a method template filter.
 *
 * Use \Twig\TwigFilter instead.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
class Twig_Filter_Method extends Twig_Filter
{
    protected $extension;
    protected $method;

    public function __construct(ExtensionInterface $extension, $method,
array $options = [])
    {
        $options['callable'] = [$extension, $method];

        parent::__construct($options);

        $this->extension = $extension;
        $this->method = $method;
    }

    public function compile()
    {
        return
sprintf('$this->env->getExtension(\'%s\')->%s',
\get_class($this->extension), $this->method);
    }
}
vendor/twig/twig/lib/Twig/Filter/Node.php000064400000001560151166614570014306
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

@trigger_error('The Twig_Filter_Node class is deprecated since version
1.12 and will be removed in 2.0. Use \Twig\TwigFilter instead.',
E_USER_DEPRECATED);

/**
 * Represents a template filter as a node.
 *
 * Use \Twig\TwigFilter instead.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
class Twig_Filter_Node extends Twig_Filter
{
    protected $class;

    public function __construct($class, array $options = [])
    {
        parent::__construct($options);

        $this->class = $class;
    }

    public function getClass()
    {
        return $this->class;
    }

    public function compile()
    {
    }
}
vendor/twig/twig/lib/Twig/Filter.php000064400000003717151166614570013427
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Node\Node;

@trigger_error('The Twig_Filter class is deprecated since version 1.12
and will be removed in 2.0. Use \Twig\TwigFilter instead.',
E_USER_DEPRECATED);

/**
 * Represents a template filter.
 *
 * Use \Twig\TwigFilter instead.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
abstract class Twig_Filter implements Twig_FilterInterface,
Twig_FilterCallableInterface
{
    protected $options;
    protected $arguments = [];

    public function __construct(array $options = [])
    {
        $this->options = array_merge([
            'needs_environment' => false,
            'needs_context' => false,
            'pre_escape' => null,
            'preserves_safety' => null,
            'callable' => null,
        ], $options);
    }

    public function setArguments($arguments)
    {
        $this->arguments = $arguments;
    }

    public function getArguments()
    {
        return $this->arguments;
    }

    public function needsEnvironment()
    {
        return $this->options['needs_environment'];
    }

    public function needsContext()
    {
        return $this->options['needs_context'];
    }

    public function getSafe(Node $filterArgs)
    {
        if (isset($this->options['is_safe'])) {
            return $this->options['is_safe'];
        }

        if (isset($this->options['is_safe_callback'])) {
            return
\call_user_func($this->options['is_safe_callback'],
$filterArgs);
        }
    }

    public function getPreservesSafety()
    {
        return $this->options['preserves_safety'];
    }

    public function getPreEscape()
    {
        return $this->options['pre_escape'];
    }

    public function getCallable()
    {
        return $this->options['callable'];
    }
}
vendor/twig/twig/lib/Twig/FilterCallableInterface.php000064400000000726151166614570016665
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Represents a callable template filter.
 *
 * Use \Twig\TwigFilter instead.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
interface Twig_FilterCallableInterface
{
    public function getCallable();
}
vendor/twig/twig/lib/Twig/FilterInterface.php000064400000001533151166614570015242
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Node\Node;

/**
 * Represents a template filter.
 *
 * Use \Twig\TwigFilter instead.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
interface Twig_FilterInterface
{
    /**
     * Compiles a filter.
     *
     * @return string The PHP code for the filter
     */
    public function compile();

    public function needsEnvironment();

    public function needsContext();

    public function getSafe(Node $filterArgs);

    public function getPreservesSafety();

    public function getPreEscape();

    public function setArguments($arguments);

    public function getArguments();
}
vendor/twig/twig/lib/Twig/Function/Function.php000064400000001650151166614570015546
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Arnaud Le Blanc
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

@trigger_error('The Twig_Function_Function class is deprecated since
version 1.12 and will be removed in 2.0. Use \Twig\TwigFunction
instead.', E_USER_DEPRECATED);

/**
 * Represents a function template function.
 *
 * Use \Twig\TwigFunction instead.
 *
 * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
class Twig_Function_Function extends Twig_Function
{
    protected $function;

    public function __construct($function, array $options = [])
    {
        $options['callable'] = $function;

        parent::__construct($options);

        $this->function = $function;
    }

    public function compile()
    {
        return $this->function;
    }
}
vendor/twig/twig/lib/Twig/Function/Method.php000064400000002173151166614570015202
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Arnaud Le Blanc
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Extension\ExtensionInterface;

@trigger_error('The Twig_Function_Method class is deprecated since
version 1.12 and will be removed in 2.0. Use \Twig\TwigFunction
instead.', E_USER_DEPRECATED);

/**
 * Represents a method template function.
 *
 * Use \Twig\TwigFunction instead.
 *
 * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
class Twig_Function_Method extends Twig_Function
{
    protected $extension;
    protected $method;

    public function __construct(ExtensionInterface $extension, $method,
array $options = [])
    {
        $options['callable'] = [$extension, $method];

        parent::__construct($options);

        $this->extension = $extension;
        $this->method = $method;
    }

    public function compile()
    {
        return
sprintf('$this->env->getExtension(\'%s\')->%s',
\get_class($this->extension), $this->method);
    }
}
vendor/twig/twig/lib/Twig/Function/Node.php000064400000001574151166614570014653
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

@trigger_error('The Twig_Function_Node class is deprecated since
version 1.12 and will be removed in 2.0. Use \Twig\TwigFunction
instead.', E_USER_DEPRECATED);

/**
 * Represents a template function as a node.
 *
 * Use \Twig\TwigFunction instead.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
class Twig_Function_Node extends Twig_Function
{
    protected $class;

    public function __construct($class, array $options = [])
    {
        parent::__construct($options);

        $this->class = $class;
    }

    public function getClass()
    {
        return $this->class;
    }

    public function compile()
    {
    }
}
vendor/twig/twig/lib/Twig/Function.php000064400000003345151166614570013764
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Node\Node;

@trigger_error('The Twig_Function class is deprecated since version
1.12 and will be removed in 2.0. Use \Twig\TwigFunction instead.',
E_USER_DEPRECATED);

/**
 * Represents a template function.
 *
 * Use \Twig\TwigFunction instead.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
abstract class Twig_Function implements Twig_FunctionInterface,
Twig_FunctionCallableInterface
{
    protected $options;
    protected $arguments = [];

    public function __construct(array $options = [])
    {
        $this->options = array_merge([
            'needs_environment' => false,
            'needs_context' => false,
            'callable' => null,
        ], $options);
    }

    public function setArguments($arguments)
    {
        $this->arguments = $arguments;
    }

    public function getArguments()
    {
        return $this->arguments;
    }

    public function needsEnvironment()
    {
        return $this->options['needs_environment'];
    }

    public function needsContext()
    {
        return $this->options['needs_context'];
    }

    public function getSafe(Node $functionArgs)
    {
        if (isset($this->options['is_safe'])) {
            return $this->options['is_safe'];
        }

        if (isset($this->options['is_safe_callback'])) {
            return
\call_user_func($this->options['is_safe_callback'],
$functionArgs);
        }

        return [];
    }

    public function getCallable()
    {
        return $this->options['callable'];
    }
}
vendor/twig/twig/lib/Twig/FunctionCallableInterface.php000064400000000734151166614570017224
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Represents a callable template function.
 *
 * Use \Twig\TwigFunction instead.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
interface Twig_FunctionCallableInterface
{
    public function getCallable();
}
vendor/twig/twig/lib/Twig/FunctionInterface.php000064400000001454151166614570015604
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Arnaud Le Blanc
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Node\Node;

/**
 * Represents a template function.
 *
 * Use \Twig\TwigFunction instead.
 *
 * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
interface Twig_FunctionInterface
{
    /**
     * Compiles a function.
     *
     * @return string The PHP code for the function
     */
    public function compile();

    public function needsEnvironment();

    public function needsContext();

    public function getSafe(Node $filterArgs);

    public function setArguments($arguments);

    public function getArguments();
}
vendor/twig/twig/lib/Twig/Lexer.php000064400000000164151166614570013252
0ustar00<?php

use Twig\Lexer;

class_exists('Twig\Lexer');

if (\false) {
    class Twig_Lexer extends Lexer
    {
    }
}
vendor/twig/twig/lib/Twig/LexerInterface.php000064400000001432151166614570015072
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Error\SyntaxError;
use Twig\Source;
use Twig\TokenStream;

/**
 * Interface implemented by lexer classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 3.0)
 */
interface Twig_LexerInterface
{
    /**
     * Tokenizes a source code.
     *
     * @param string|Source $code The source code
     * @param string        $name A unique identifier for the source code
     *
     * @return TokenStream
     *
     * @throws SyntaxError When the code is syntactically wrong
     */
    public function tokenize($code, $name = null);
}
vendor/twig/twig/lib/Twig/Loader/Array.php000064400000000233151166614570014454
0ustar00<?php

use Twig\Loader\ArrayLoader;

class_exists('Twig\Loader\ArrayLoader');

if (\false) {
    class Twig_Loader_Array extends ArrayLoader
    {
    }
}
vendor/twig/twig/lib/Twig/Loader/Chain.php000064400000000233151166614570014420
0ustar00<?php

use Twig\Loader\ChainLoader;

class_exists('Twig\Loader\ChainLoader');

if (\false) {
    class Twig_Loader_Chain extends ChainLoader
    {
    }
}
vendor/twig/twig/lib/Twig/Loader/Filesystem.php000064400000000257151166614570015530
0ustar00<?php

use Twig\Loader\FilesystemLoader;

class_exists('Twig\Loader\FilesystemLoader');

if (\false) {
    class Twig_Loader_Filesystem extends FilesystemLoader
    {
    }
}
vendor/twig/twig/lib/Twig/Loader/String.php000064400000003367151166614570014657
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Loader\ExistsLoaderInterface;
use Twig\Loader\LoaderInterface;
use Twig\Loader\SourceContextLoaderInterface;
use Twig\Source;

@trigger_error('The Twig_Loader_String class is deprecated since
version 1.18.1 and will be removed in 2.0. Use
"Twig\Loader\ArrayLoader" instead or
"Twig\Environment::createTemplate()".', E_USER_DEPRECATED);

/**
 * Loads a template from a string.
 *
 * This loader should NEVER be used. It only exists for Twig internal
purposes.
 *
 * When using this loader with a cache mechanism, you should know that a
new cache
 * key is generated each time a template content "changes" (the
cache key being the
 * source code of the template). If you don't want to see your cache
grows out of
 * control, you need to take care of clearing the old cache file by
yourself.
 *
 * @deprecated since 1.18.1 (to be removed in 2.0)
 *
 * @internal
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Twig_Loader_String implements LoaderInterface, ExistsLoaderInterface,
SourceContextLoaderInterface
{
    public function getSource($name)
    {
        @trigger_error(sprintf('Calling "getSource" on
"%s" is deprecated since 1.27. Use getSourceContext()
instead.', \get_class($this)), E_USER_DEPRECATED);

        return $name;
    }

    public function getSourceContext($name)
    {
        return new Source($name, $name);
    }

    public function exists($name)
    {
        return true;
    }

    public function getCacheKey($name)
    {
        return $name;
    }

    public function isFresh($name, $time)
    {
        return true;
    }
}
vendor/twig/twig/lib/Twig/LoaderInterface.php000064400000000252151166614570015220
0ustar00<?php

use Twig\Loader\LoaderInterface;

class_exists('Twig\Loader\LoaderInterface');

if (\false) {
    class Twig_LoaderInterface extends LoaderInterface
    {
    }
}
vendor/twig/twig/lib/Twig/Markup.php000064400000000170151166614570013427
0ustar00<?php

use Twig\Markup;

class_exists('Twig\Markup');

if (\false) {
    class Twig_Markup extends Markup
    {
    }
}
vendor/twig/twig/lib/Twig/Node/AutoEscape.php000064400000000243151166614570015107
0ustar00<?php

use Twig\Node\AutoEscapeNode;

class_exists('Twig\Node\AutoEscapeNode');

if (\false) {
    class Twig_Node_AutoEscape extends AutoEscapeNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Block.php000064400000000217151166614570014111
0ustar00<?php

use Twig\Node\BlockNode;

class_exists('Twig\Node\BlockNode');

if (\false) {
    class Twig_Node_Block extends BlockNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/BlockReference.php000064400000000263151166614570015731
0ustar00<?php

use Twig\Node\BlockReferenceNode;

class_exists('Twig\Node\BlockReferenceNode');

if (\false) {
    class Twig_Node_BlockReference extends BlockReferenceNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Body.php000064400000000213151166614570013750
0ustar00<?php

use Twig\Node\BodyNode;

class_exists('Twig\Node\BodyNode');

if (\false) {
    class Twig_Node_Body extends BodyNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/CheckSecurity.php000064400000000257151166614570015630
0ustar00<?php

use Twig\Node\CheckSecurityNode;

class_exists('Twig\Node\CheckSecurityNode');

if (\false) {
    class Twig_Node_CheckSecurity extends CheckSecurityNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Deprecated.php000064400000000243151166614570015116
0ustar00<?php

use Twig\Node\DeprecatedNode;

class_exists('Twig\Node\DeprecatedNode');

if (\false) {
    class Twig_Node_Deprecated extends DeprecatedNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Do.php000064400000000203151166614570013414
0ustar00<?php

use Twig\Node\DoNode;

class_exists('Twig\Node\DoNode');

if (\false) {
    class Twig_Node_Do extends DoNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Embed.php000064400000000217151166614570014073
0ustar00<?php

use Twig\Node\EmbedNode;

class_exists('Twig\Node\EmbedNode');

if (\false) {
    class Twig_Node_Embed extends EmbedNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Array.php000064400000000302151166614570016267
0ustar00<?php

use Twig\Node\Expression\ArrayExpression;

class_exists('Twig\Node\Expression\ArrayExpression');

if (\false) {
    class Twig_Node_Expression_Array extends ArrayExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php000064400000000326151166614570017244
0ustar00<?php

use Twig\Node\Expression\AssignNameExpression;

class_exists('Twig\Node\Expression\AssignNameExpression');

if (\false) {
    class Twig_Node_Expression_AssignName extends AssignNameExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php000064400000000303151166614570017126
0ustar00<?php

use Twig\Node\Expression\Binary\AddBinary;

class_exists('Twig\Node\Expression\Binary\AddBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Add extends AddBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php000064400000000303151166614570017140
0ustar00<?php

use Twig\Node\Expression\Binary\AndBinary;

class_exists('Twig\Node\Expression\Binary\AndBinary');

if (\false) {
    class Twig_Node_Expression_Binary_And extends AndBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php000064400000000337151166614570020476
0ustar00<?php

use Twig\Node\Expression\Binary\BitwiseAndBinary;

class_exists('Twig\Node\Expression\Binary\BitwiseAndBinary');

if (\false) {
    class Twig_Node_Expression_Binary_BitwiseAnd extends BitwiseAndBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php000064400000000333151166614570020350
0ustar00<?php

use Twig\Node\Expression\Binary\BitwiseOrBinary;

class_exists('Twig\Node\Expression\Binary\BitwiseOrBinary');

if (\false) {
    class Twig_Node_Expression_Binary_BitwiseOr extends BitwiseOrBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php000064400000000337151166614570020544
0ustar00<?php

use Twig\Node\Expression\Binary\BitwiseXorBinary;

class_exists('Twig\Node\Expression\Binary\BitwiseXorBinary');

if (\false) {
    class Twig_Node_Expression_Binary_BitwiseXor extends BitwiseXorBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php000064400000000317151166614570017652
0ustar00<?php

use Twig\Node\Expression\Binary\ConcatBinary;

class_exists('Twig\Node\Expression\Binary\ConcatBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Concat extends ConcatBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php000064400000000303151166614570017160
0ustar00<?php

use Twig\Node\Expression\Binary\DivBinary;

class_exists('Twig\Node\Expression\Binary\DivBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Div extends DivBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php000064400000000327151166614570020171
0ustar00<?php

use Twig\Node\Expression\Binary\EndsWithBinary;

class_exists('Twig\Node\Expression\Binary\EndsWithBinary');

if (\false) {
    class Twig_Node_Expression_Binary_EndsWith extends EndsWithBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php000064400000000313151166614570017506
0ustar00<?php

use Twig\Node\Expression\Binary\EqualBinary;

class_exists('Twig\Node\Expression\Binary\EqualBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Equal extends EqualBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php000064400000000327151166614570020170
0ustar00<?php

use Twig\Node\Expression\Binary\FloorDivBinary;

class_exists('Twig\Node\Expression\Binary\FloorDivBinary');

if (\false) {
    class Twig_Node_Expression_Binary_FloorDiv extends FloorDivBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php000064400000000323151166614570020031
0ustar00<?php

use Twig\Node\Expression\Binary\GreaterBinary;

class_exists('Twig\Node\Expression\Binary\GreaterBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Greater extends GreaterBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php000064400000000347151166614570021027
0ustar00<?php

use Twig\Node\Expression\Binary\GreaterEqualBinary;

class_exists('Twig\Node\Expression\Binary\GreaterEqualBinary');

if (\false) {
    class Twig_Node_Expression_Binary_GreaterEqual extends
GreaterEqualBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php000064400000000277151166614570017016
0ustar00<?php

use Twig\Node\Expression\Binary\InBinary;

class_exists('Twig\Node\Expression\Binary\InBinary');

if (\false) {
    class Twig_Node_Expression_Binary_In extends InBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php000064400000000307151166614570017350
0ustar00<?php

use Twig\Node\Expression\Binary\LessBinary;

class_exists('Twig\Node\Expression\Binary\LessBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Less extends LessBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php000064400000000333151166614570020337
0ustar00<?php

use Twig\Node\Expression\Binary\LessEqualBinary;

class_exists('Twig\Node\Expression\Binary\LessEqualBinary');

if (\false) {
    class Twig_Node_Expression_Binary_LessEqual extends LessEqualBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php000064400000000323151166614570020024
0ustar00<?php

use Twig\Node\Expression\Binary\MatchesBinary;

class_exists('Twig\Node\Expression\Binary\MatchesBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Matches extends MatchesBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php000064400000000303151166614570017155
0ustar00<?php

use Twig\Node\Expression\Binary\ModBinary;

class_exists('Twig\Node\Expression\Binary\ModBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Mod extends ModBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php000064400000000303151166614610017166
0ustar00<?php

use Twig\Node\Expression\Binary\MulBinary;

class_exists('Twig\Node\Expression\Binary\MulBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Mul extends MulBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php000064400000000327151166614610020167
0ustar00<?php

use Twig\Node\Expression\Binary\NotEqualBinary;

class_exists('Twig\Node\Expression\Binary\NotEqualBinary');

if (\false) {
    class Twig_Node_Expression_Binary_NotEqual extends NotEqualBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php000064400000000313151166614610017461
0ustar00<?php

use Twig\Node\Expression\Binary\NotInBinary;

class_exists('Twig\Node\Expression\Binary\NotInBinary');

if (\false) {
    class Twig_Node_Expression_Binary_NotIn extends NotInBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php000064400000000277151166614610017023
0ustar00<?php

use Twig\Node\Expression\Binary\OrBinary;

class_exists('Twig\Node\Expression\Binary\OrBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Or extends OrBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php000064400000000313151166614610017526
0ustar00<?php

use Twig\Node\Expression\Binary\PowerBinary;

class_exists('Twig\Node\Expression\Binary\PowerBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Power extends PowerBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php000064400000000313151166614610017466
0ustar00<?php

use Twig\Node\Expression\Binary\RangeBinary;

class_exists('Twig\Node\Expression\Binary\RangeBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Range extends RangeBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php000064400000000337151166614610020554
0ustar00<?php

use Twig\Node\Expression\Binary\StartsWithBinary;

class_exists('Twig\Node\Expression\Binary\StartsWithBinary');

if (\false) {
    class Twig_Node_Expression_Binary_StartsWith extends StartsWithBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php000064400000000303151166614610017162
0ustar00<?php

use Twig\Node\Expression\Binary\SubBinary;

class_exists('Twig\Node\Expression\Binary\SubBinary');

if (\false) {
    class Twig_Node_Expression_Binary_Sub extends SubBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Binary.php000064400000000316151166614610016435
0ustar00<?php

use Twig\Node\Expression\Binary\AbstractBinary;

class_exists('Twig\Node\Expression\Binary\AbstractBinary');

if (\false) {
    class Twig_Node_Expression_Binary extends AbstractBinary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php000064400000000346151166614610020065
0ustar00<?php

use Twig\Node\Expression\BlockReferenceExpression;

class_exists('Twig\Node\Expression\BlockReferenceExpression');

if (\false) {
    class Twig_Node_Expression_BlockReference extends
BlockReferenceExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Call.php000064400000000276151166614610016071
0ustar00<?php

use Twig\Node\Expression\CallExpression;

class_exists('Twig\Node\Expression\CallExpression');

if (\false) {
    class Twig_Node_Expression_Call extends CallExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php000064400000000332151166614610017452
0ustar00<?php

use Twig\Node\Expression\ConditionalExpression;

class_exists('Twig\Node\Expression\ConditionalExpression');

if (\false) {
    class Twig_Node_Expression_Conditional extends ConditionalExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Constant.php000064400000000316151166614610017002
0ustar00<?php

use Twig\Node\Expression\ConstantExpression;

class_exists('Twig\Node\Expression\ConstantExpression');

if (\false) {
    class Twig_Node_Expression_Constant extends ConstantExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php000064400000001663151166614610021012
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;

@trigger_error('The Twig_Node_Expression_ExtensionReference class is
deprecated since version 1.23 and will be removed in 2.0.',
E_USER_DEPRECATED);

/**
 * Represents an extension call node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.23 and will be removed in 2.0.
 */
class Twig_Node_Expression_ExtensionReference extends AbstractExpression
{
    public function __construct($name, $lineno, $tag = null)
    {
        parent::__construct([], ['name' => $name], $lineno,
$tag);
    }

    public function compile(Compiler $compiler)
    {
       
$compiler->raw(sprintf("\$this->env->getExtension('%s')",
$this->getAttribute('name')));
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php000064400000000323151166614610020020
0ustar00<?php

use Twig\Node\Expression\Filter\DefaultFilter;

class_exists('Twig\Node\Expression\Filter\DefaultFilter');

if (\false) {
    class Twig_Node_Expression_Filter_Default extends DefaultFilter
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Filter.php000064400000000306151166614610016435
0ustar00<?php

use Twig\Node\Expression\FilterExpression;

class_exists('Twig\Node\Expression\FilterExpression');

if (\false) {
    class Twig_Node_Expression_Filter extends FilterExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Function.php000064400000000316151166614620016777
0ustar00<?php

use Twig\Node\Expression\FunctionExpression;

class_exists('Twig\Node\Expression\FunctionExpression');

if (\false) {
    class Twig_Node_Expression_Function extends FunctionExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php000064400000000312151166614630016561
0ustar00<?php

use Twig\Node\Expression\GetAttrExpression;

class_exists('Twig\Node\Expression\GetAttrExpression');

if (\false) {
    class Twig_Node_Expression_GetAttr extends GetAttrExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php000064400000000326151166614630017230
0ustar00<?php

use Twig\Node\Expression\MethodCallExpression;

class_exists('Twig\Node\Expression\MethodCallExpression');

if (\false) {
    class Twig_Node_Expression_MethodCall extends MethodCallExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Name.php000064400000000276151166614630016100
0ustar00<?php

use Twig\Node\Expression\NameExpression;

class_exists('Twig\Node\Expression\NameExpression');

if (\false) {
    class Twig_Node_Expression_Name extends NameExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php000064400000000336151166614630017566
0ustar00<?php

use Twig\Node\Expression\NullCoalesceExpression;

class_exists('Twig\Node\Expression\NullCoalesceExpression');

if (\false) {
    class Twig_Node_Expression_NullCoalesce extends NullCoalesceExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Parent.php000064400000000306151166614630016443
0ustar00<?php

use Twig\Node\Expression\ParentExpression;

class_exists('Twig\Node\Expression\ParentExpression');

if (\false) {
    class Twig_Node_Expression_Parent extends ParentExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/TempName.php000064400000000316151166614630016721
0ustar00<?php

use Twig\Node\Expression\TempNameExpression;

class_exists('Twig\Node\Expression\TempNameExpression');

if (\false) {
    class Twig_Node_Expression_TempName extends TempNameExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php000064400000000313151166614630017720
0ustar00<?php

use Twig\Node\Expression\Test\ConstantTest;

class_exists('Twig\Node\Expression\Test\ConstantTest');

if (\false) {
    class Twig_Node_Expression_Test_Constant extends ConstantTest
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php000064400000000307151166614630017470
0ustar00<?php

use Twig\Node\Expression\Test\DefinedTest;

class_exists('Twig\Node\Expression\Test\DefinedTest');

if (\false) {
    class Twig_Node_Expression_Test_Defined extends DefinedTest
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php000064400000000327151166614630020401
0ustar00<?php

use Twig\Node\Expression\Test\DivisiblebyTest;

class_exists('Twig\Node\Expression\Test\DivisiblebyTest');

if (\false) {
    class Twig_Node_Expression_Test_Divisibleby extends DivisiblebyTest
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php000064400000000273151166614630017031
0ustar00<?php

use Twig\Node\Expression\Test\EvenTest;

class_exists('Twig\Node\Expression\Test\EvenTest');

if (\false) {
    class Twig_Node_Expression_Test_Even extends EvenTest
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php000064400000000273151166614630017046
0ustar00<?php

use Twig\Node\Expression\Test\NullTest;

class_exists('Twig\Node\Expression\Test\NullTest');

if (\false) {
    class Twig_Node_Expression_Test_Null extends NullTest
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php000064400000000267151166614630016645
0ustar00<?php

use Twig\Node\Expression\Test\OddTest;

class_exists('Twig\Node\Expression\Test\OddTest');

if (\false) {
    class Twig_Node_Expression_Test_Odd extends OddTest
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php000064400000000303151166614630017337
0ustar00<?php

use Twig\Node\Expression\Test\SameasTest;

class_exists('Twig\Node\Expression\Test\SameasTest');

if (\false) {
    class Twig_Node_Expression_Test_Sameas extends SameasTest
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Test.php000064400000000276151166614630016137
0ustar00<?php

use Twig\Node\Expression\TestExpression;

class_exists('Twig\Node\Expression\TestExpression');

if (\false) {
    class Twig_Node_Expression_Test extends TestExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php000064400000000275151166614630017026
0ustar00<?php

use Twig\Node\Expression\Unary\NegUnary;

class_exists('Twig\Node\Expression\Unary\NegUnary');

if (\false) {
    class Twig_Node_Expression_Unary_Neg extends NegUnary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php000064400000000275151166614630017055
0ustar00<?php

use Twig\Node\Expression\Unary\NotUnary;

class_exists('Twig\Node\Expression\Unary\NotUnary');

if (\false) {
    class Twig_Node_Expression_Unary_Not extends NotUnary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php000064400000000275151166614630017056
0ustar00<?php

use Twig\Node\Expression\Unary\PosUnary;

class_exists('Twig\Node\Expression\Unary\PosUnary');

if (\false) {
    class Twig_Node_Expression_Unary_Pos extends PosUnary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression/Unary.php000064400000000310151166614630016303
0ustar00<?php

use Twig\Node\Expression\Unary\AbstractUnary;

class_exists('Twig\Node\Expression\Unary\AbstractUnary');

if (\false) {
    class Twig_Node_Expression_Unary extends AbstractUnary
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Expression.php000064400000000305151166614630015211
0ustar00<?php

use Twig\Node\Expression\AbstractExpression;

class_exists('Twig\Node\Expression\AbstractExpression');

if (\false) {
    class Twig_Node_Expression extends AbstractExpression
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Flush.php000064400000000217151166614630014135
0ustar00<?php

use Twig\Node\FlushNode;

class_exists('Twig\Node\FlushNode');

if (\false) {
    class Twig_Node_Flush extends FlushNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/For.php000064400000000207151166614630013601
0ustar00<?php

use Twig\Node\ForNode;

class_exists('Twig\Node\ForNode');

if (\false) {
    class Twig_Node_For extends ForNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/ForLoop.php000064400000000227151166614630014435
0ustar00<?php

use Twig\Node\ForLoopNode;

class_exists('Twig\Node\ForLoopNode');

if (\false) {
    class Twig_Node_ForLoop extends ForLoopNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/If.php000064400000000203151166614630013405
0ustar00<?php

use Twig\Node\IfNode;

class_exists('Twig\Node\IfNode');

if (\false) {
    class Twig_Node_If extends IfNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Import.php000064400000000223151166614630014323
0ustar00<?php

use Twig\Node\ImportNode;

class_exists('Twig\Node\ImportNode');

if (\false) {
    class Twig_Node_Import extends ImportNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Include.php000064400000000227151166614630014440
0ustar00<?php

use Twig\Node\IncludeNode;

class_exists('Twig\Node\IncludeNode');

if (\false) {
    class Twig_Node_Include extends IncludeNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Macro.php000064400000000217151166614630014115
0ustar00<?php

use Twig\Node\MacroNode;

class_exists('Twig\Node\MacroNode');

if (\false) {
    class Twig_Node_Macro extends MacroNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Module.php000064400000000223151166614630014276
0ustar00<?php

use Twig\Node\ModuleNode;

class_exists('Twig\Node\ModuleNode');

if (\false) {
    class Twig_Node_Module extends ModuleNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Print.php000064400000000217151166614630014150
0ustar00<?php

use Twig\Node\PrintNode;

class_exists('Twig\Node\PrintNode');

if (\false) {
    class Twig_Node_Print extends PrintNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Sandbox.php000064400000000227151166614630014453
0ustar00<?php

use Twig\Node\SandboxNode;

class_exists('Twig\Node\SandboxNode');

if (\false) {
    class Twig_Node_Sandbox extends SandboxNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php000064400000000263151166614630016001
0ustar00<?php

use Twig\Node\SandboxedPrintNode;

class_exists('Twig\Node\SandboxedPrintNode');

if (\false) {
    class Twig_Node_SandboxedPrint extends SandboxedPrintNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Set.php000064400000000207151166614630013606
0ustar00<?php

use Twig\Node\SetNode;

class_exists('Twig\Node\SetNode');

if (\false) {
    class Twig_Node_Set extends SetNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/SetTemp.php000064400000000227151166614630014436
0ustar00<?php

use Twig\Node\SetTempNode;

class_exists('Twig\Node\SetTempNode');

if (\false) {
    class Twig_Node_SetTemp extends SetTempNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Spaceless.php000064400000000237151166614630015000
0ustar00<?php

use Twig\Node\SpacelessNode;

class_exists('Twig\Node\SpacelessNode');

if (\false) {
    class Twig_Node_Spaceless extends SpacelessNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/Text.php000064400000000213151166614630013774
0ustar00<?php

use Twig\Node\TextNode;

class_exists('Twig\Node\TextNode');

if (\false) {
    class Twig_Node_Text extends TextNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node/With.php000064400000000213151166614630013763
0ustar00<?php

use Twig\Node\WithNode;

class_exists('Twig\Node\WithNode');

if (\false) {
    class Twig_Node_With extends WithNode
    {
    }
}
vendor/twig/twig/lib/Twig/Node.php000064400000000172151166614630013054
0ustar00<?php

use Twig\Node\Node;

class_exists('Twig\Node\Node');

if (\false) {
    class Twig_Node extends Node
    {
    }
}
vendor/twig/twig/lib/Twig/NodeCaptureInterface.php000064400000000272151166614630016222
0ustar00<?php

use Twig\Node\NodeCaptureInterface;

class_exists('Twig\Node\NodeCaptureInterface');

if (\false) {
    class Twig_NodeCaptureInterface extends NodeCaptureInterface
    {
    }
}
vendor/twig/twig/lib/Twig/NodeInterface.php000064400000001241151166614640014674
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Compiler;

/**
 * Represents a node in the AST.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 3.0)
 */
interface Twig_NodeInterface extends \Countable, \IteratorAggregate
{
    /**
     * Compiles the node to PHP.
     */
    public function compile(Compiler $compiler);

    /**
     * @deprecated since 1.27 (to be removed in 2.0)
     */
    public function getLine();

    public function getNodeTag();
}
vendor/twig/twig/lib/Twig/NodeOutputInterface.php000064400000000266151166614640016123
0ustar00<?php

use Twig\Node\NodeOutputInterface;

class_exists('Twig\Node\NodeOutputInterface');

if (\false) {
    class Twig_NodeOutputInterface extends NodeOutputInterface
    {
    }
}
vendor/twig/twig/lib/Twig/NodeTraverser.php000064400000000224151166614640014751
0ustar00<?php

use Twig\NodeTraverser;

class_exists('Twig\NodeTraverser');

if (\false) {
    class Twig_NodeTraverser extends NodeTraverser
    {
    }
}
vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php000064400000000301151166614640016011
0ustar00<?php

use Twig\NodeVisitor\EscaperNodeVisitor;

class_exists('Twig\NodeVisitor\EscaperNodeVisitor');

if (\false) {
    class Twig_NodeVisitor_Escaper extends EscaperNodeVisitor
    {
    }
}
vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php000064400000000311151166614640016412
0ustar00<?php

use Twig\NodeVisitor\OptimizerNodeVisitor;

class_exists('Twig\NodeVisitor\OptimizerNodeVisitor');

if (\false) {
    class Twig_NodeVisitor_Optimizer extends OptimizerNodeVisitor
    {
    }
}
vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php000064400000000325151166614640017017
0ustar00<?php

use Twig\NodeVisitor\SafeAnalysisNodeVisitor;

class_exists('Twig\NodeVisitor\SafeAnalysisNodeVisitor');

if (\false) {
    class Twig_NodeVisitor_SafeAnalysis extends SafeAnalysisNodeVisitor
    {
    }
}
vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php000064400000000301151166614640016025
0ustar00<?php

use Twig\NodeVisitor\SandboxNodeVisitor;

class_exists('Twig\NodeVisitor\SandboxNodeVisitor');

if (\false) {
    class Twig_NodeVisitor_Sandbox extends SandboxNodeVisitor
    {
    }
}
vendor/twig/twig/lib/Twig/NodeVisitorInterface.php000064400000000310151166614640016250
0ustar00<?php

use Twig\NodeVisitor\NodeVisitorInterface;

class_exists('Twig\NodeVisitor\NodeVisitorInterface');

if (\false) {
    class Twig_NodeVisitorInterface extends NodeVisitorInterface
    {
    }
}
vendor/twig/twig/lib/Twig/Parser.php000064400000000170151166614640013422
0ustar00<?php

use Twig\Parser;

class_exists('Twig\Parser');

if (\false) {
    class Twig_Parser extends Parser
    {
    }
}
vendor/twig/twig/lib/Twig/ParserInterface.php000064400000001305151166614640015244
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Error\SyntaxError;
use Twig\Node\ModuleNode;
use Twig\TokenStream;

/**
 * Interface implemented by parser classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 3.0)
 */
interface Twig_ParserInterface
{
    /**
     * Converts a token stream to a node tree.
     *
     * @return ModuleNode
     *
     * @throws SyntaxError When the token stream is syntactically or
semantically wrong
     */
    public function parse(TokenStream $stream);
}
vendor/twig/twig/lib/Twig/Profiler/Dumper/Base.php000064400000000262151166614640016060
0ustar00<?php

use Twig\Profiler\Dumper\BaseDumper;

class_exists('Twig\Profiler\Dumper\BaseDumper');

if (\false) {
    class Twig_Profiler_Dumper_Base extends BaseDumper
    {
    }
}
vendor/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php000064400000000306151166614640017067
0ustar00<?php

use Twig\Profiler\Dumper\BlackfireDumper;

class_exists('Twig\Profiler\Dumper\BlackfireDumper');

if (\false) {
    class Twig_Profiler_Dumper_Blackfire extends BlackfireDumper
    {
    }
}
vendor/twig/twig/lib/Twig/Profiler/Dumper/Html.php000064400000000262151166614650016113
0ustar00<?php

use Twig\Profiler\Dumper\HtmlDumper;

class_exists('Twig\Profiler\Dumper\HtmlDumper');

if (\false) {
    class Twig_Profiler_Dumper_Html extends HtmlDumper
    {
    }
}
vendor/twig/twig/lib/Twig/Profiler/Dumper/Text.php000064400000000262151166614650016133
0ustar00<?php

use Twig\Profiler\Dumper\TextDumper;

class_exists('Twig\Profiler\Dumper\TextDumper');

if (\false) {
    class Twig_Profiler_Dumper_Text extends TextDumper
    {
    }
}
vendor/twig/twig/lib/Twig/Profiler/Node/EnterProfile.php000064400000000306151166614650017235
0ustar00<?php

use Twig\Profiler\Node\EnterProfileNode;

class_exists('Twig\Profiler\Node\EnterProfileNode');

if (\false) {
    class Twig_Profiler_Node_EnterProfile extends EnterProfileNode
    {
    }
}
vendor/twig/twig/lib/Twig/Profiler/Node/LeaveProfile.php000064400000000306151166614650017214
0ustar00<?php

use Twig\Profiler\Node\LeaveProfileNode;

class_exists('Twig\Profiler\Node\LeaveProfileNode');

if (\false) {
    class Twig_Profiler_Node_LeaveProfile extends LeaveProfileNode
    {
    }
}
vendor/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php000064400000000340151166614650017777
0ustar00<?php

use Twig\Profiler\NodeVisitor\ProfilerNodeVisitor;

class_exists('Twig\Profiler\NodeVisitor\ProfilerNodeVisitor');

if (\false) {
    class Twig_Profiler_NodeVisitor_Profiler extends ProfilerNodeVisitor
    {
    }
}
vendor/twig/twig/lib/Twig/Profiler/Profile.php000064400000000227151166614650015354
0ustar00<?php

use Twig\Profiler\Profile;

class_exists('Twig\Profiler\Profile');

if (\false) {
    class Twig_Profiler_Profile extends Profile
    {
    }
}
vendor/twig/twig/lib/Twig/RuntimeLoaderInterface.php000064400000000324151166614650016563
0ustar00<?php

use Twig\RuntimeLoader\RuntimeLoaderInterface;

class_exists('Twig\RuntimeLoader\RuntimeLoaderInterface');

if (\false) {
    class Twig_RuntimeLoaderInterface extends RuntimeLoaderInterface
    {
    }
}
vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php000064400000000254151166614650016411
0ustar00<?php

use Twig\Sandbox\SecurityError;

class_exists('Twig\Sandbox\SecurityError');

if (\false) {
    class Twig_Sandbox_SecurityError extends SecurityError
    {
    }
}
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php000064400000000354151166614650021551
0ustar00<?php

use Twig\Sandbox\SecurityNotAllowedFilterError;

class_exists('Twig\Sandbox\SecurityNotAllowedFilterError');

if (\false) {
    class Twig_Sandbox_SecurityNotAllowedFilterError extends
SecurityNotAllowedFilterError
    {
    }
}
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php000064400000000364151166614660022113
0ustar00<?php

use Twig\Sandbox\SecurityNotAllowedFunctionError;

class_exists('Twig\Sandbox\SecurityNotAllowedFunctionError');

if (\false) {
    class Twig_Sandbox_SecurityNotAllowedFunctionError extends
SecurityNotAllowedFunctionError
    {
    }
}
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php000064400000000354151166614660021545
0ustar00<?php

use Twig\Sandbox\SecurityNotAllowedMethodError;

class_exists('Twig\Sandbox\SecurityNotAllowedMethodError');

if (\false) {
    class Twig_Sandbox_SecurityNotAllowedMethodError extends
SecurityNotAllowedMethodError
    {
    }
}
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php000064400000000364151166614670022153
0ustar00<?php

use Twig\Sandbox\SecurityNotAllowedPropertyError;

class_exists('Twig\Sandbox\SecurityNotAllowedPropertyError');

if (\false) {
    class Twig_Sandbox_SecurityNotAllowedPropertyError extends
SecurityNotAllowedPropertyError
    {
    }
}
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedTagError.php000064400000000340151166614670021034
0ustar00<?php

use Twig\Sandbox\SecurityNotAllowedTagError;

class_exists('Twig\Sandbox\SecurityNotAllowedTagError');

if (\false) {
    class Twig_Sandbox_SecurityNotAllowedTagError extends
SecurityNotAllowedTagError
    {
    }
}
vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php000064400000000260151166614670016556
0ustar00<?php

use Twig\Sandbox\SecurityPolicy;

class_exists('Twig\Sandbox\SecurityPolicy');

if (\false) {
    class Twig_Sandbox_SecurityPolicy extends SecurityPolicy
    {
    }
}
vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php000064400000000324151166614670020400
0ustar00<?php

use Twig\Sandbox\SecurityPolicyInterface;

class_exists('Twig\Sandbox\SecurityPolicyInterface');

if (\false) {
    class Twig_Sandbox_SecurityPolicyInterface extends
SecurityPolicyInterface
    {
    }
}
vendor/twig/twig/lib/Twig/SimpleFilter.php000064400000000212151166614670014565
0ustar00<?php

use Twig\TwigFilter;

class_exists('Twig\TwigFilter');

if (\false) {
    class Twig_SimpleFilter extends TwigFilter
    {
    }
}
vendor/twig/twig/lib/Twig/SimpleFunction.php000064400000000222151166614670015126
0ustar00<?php

use Twig\TwigFunction;

class_exists('Twig\TwigFunction');

if (\false) {
    class Twig_SimpleFunction extends TwigFunction
    {
    }
}
vendor/twig/twig/lib/Twig/SimpleTest.php000064400000000202151166614670014256
0ustar00<?php

use Twig\TwigTest;

class_exists('Twig\TwigTest');

if (\false) {
    class Twig_SimpleTest extends TwigTest
    {
    }
}
vendor/twig/twig/lib/Twig/Source.php000064400000000170151166614670013431
0ustar00<?php

use Twig\Source;

class_exists('Twig\Source');

if (\false) {
    class Twig_Source extends Source
    {
    }
}
vendor/twig/twig/lib/Twig/SourceContextLoaderInterface.php000064400000000336151166614670017752
0ustar00<?php

use Twig\Loader\SourceContextLoaderInterface;

class_exists('Twig\Loader\SourceContextLoaderInterface');

if (\false) {
    class Twig_SourceContextLoaderInterface extends
SourceContextLoaderInterface
    {
    }
}
vendor/twig/twig/lib/Twig/Template.php000064400000000200151166614700013730
0ustar00<?php

use Twig\Template;

class_exists('Twig\Template');

if (\false) {
    class Twig_Template extends Template
    {
    }
}
vendor/twig/twig/lib/Twig/TemplateInterface.php000064400000002313151166614700015560
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Environment;

/**
 * Interface implemented by all compiled templates.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 3.0)
 */
interface Twig_TemplateInterface
{
    const ANY_CALL = 'any';
    const ARRAY_CALL = 'array';
    const METHOD_CALL = 'method';

    /**
     * Renders the template with the given context and returns it as
string.
     *
     * @param array $context An array of parameters to pass to the template
     *
     * @return string The rendered template
     */
    public function render(array $context);

    /**
     * Displays the template with the given context.
     *
     * @param array $context An array of parameters to pass to the template
     * @param array $blocks  An array of blocks to pass to the template
     */
    public function display(array $context, array $blocks = []);

    /**
     * Returns the bound environment for this template.
     *
     * @return Environment
     */
    public function getEnvironment();
}
vendor/twig/twig/lib/Twig/TemplateWrapper.php000064400000000234151166614700015300
0ustar00<?php

use Twig\TemplateWrapper;

class_exists('Twig\TemplateWrapper');

if (\false) {
    class Twig_TemplateWrapper extends TemplateWrapper
    {
    }
}
vendor/twig/twig/lib/Twig/Test/Function.php000064400000001527151166614700014676
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

@trigger_error('The Twig_Test_Function class is deprecated since
version 1.12 and will be removed in 2.0. Use \Twig\TwigTest instead.',
E_USER_DEPRECATED);

/**
 * Represents a function template test.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
class Twig_Test_Function extends Twig_Test
{
    protected $function;

    public function __construct($function, array $options = [])
    {
        $options['callable'] = $function;

        parent::__construct($options);

        $this->function = $function;
    }

    public function compile()
    {
        return $this->function;
    }
}
vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php000064400000000273151166614700017025
0ustar00<?php

use Twig\Test\IntegrationTestCase;

class_exists('Twig\Test\IntegrationTestCase');

if (\false) {
    class Twig_Test_IntegrationTestCase extends IntegrationTestCase
    {
    }
}
vendor/twig/twig/lib/Twig/Test/Method.php000064400000002052151166614700014323
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\Extension\ExtensionInterface;

@trigger_error('The Twig_Test_Method class is deprecated since version
1.12 and will be removed in 2.0. Use \Twig\TwigTest instead.',
E_USER_DEPRECATED);

/**
 * Represents a method template test.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
class Twig_Test_Method extends Twig_Test
{
    protected $extension;
    protected $method;

    public function __construct(ExtensionInterface $extension, $method,
array $options = [])
    {
        $options['callable'] = [$extension, $method];

        parent::__construct($options);

        $this->extension = $extension;
        $this->method = $method;
    }

    public function compile()
    {
        return
sprintf('$this->env->getExtension(\'%s\')->%s',
\get_class($this->extension), $this->method);
    }
}
vendor/twig/twig/lib/Twig/Test/Node.php000064400000001446151166614700013776
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

@trigger_error('The Twig_Test_Node class is deprecated since version
1.12 and will be removed in 2.0.', E_USER_DEPRECATED);

/**
 * Represents a template test as a Node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
class Twig_Test_Node extends Twig_Test
{
    protected $class;

    public function __construct($class, array $options = [])
    {
        parent::__construct($options);

        $this->class = $class;
    }

    public function getClass()
    {
        return $this->class;
    }

    public function compile()
    {
    }
}
vendor/twig/twig/lib/Twig/Test/NodeTestCase.php000064400000000237151166614700015427
0ustar00<?php

use Twig\Test\NodeTestCase;

class_exists('Twig\Test\NodeTestCase');

if (\false) {
    class Twig_Test_NodeTestCase extends NodeTestCase
    {
    }
}
vendor/twig/twig/lib/Twig/Test.php000064400000001564151166614700013112
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

@trigger_error('The Twig_Test class is deprecated since version 1.12
and will be removed in 2.0. Use \Twig\TwigTest instead.',
E_USER_DEPRECATED);

/**
 * Represents a template test.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
abstract class Twig_Test implements Twig_TestInterface,
Twig_TestCallableInterface
{
    protected $options;
    protected $arguments = [];

    public function __construct(array $options = [])
    {
        $this->options = array_merge([
            'callable' => null,
        ], $options);
    }

    public function getCallable()
    {
        return $this->options['callable'];
    }
}
vendor/twig/twig/lib/Twig/TestCallableInterface.php000064400000000656151166614700016354
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Represents a callable template test.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
interface Twig_TestCallableInterface
{
    public function getCallable();
}
vendor/twig/twig/lib/Twig/TestInterface.php000064400000000770151166614710014732
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Represents a template test.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
interface Twig_TestInterface
{
    /**
     * Compiles a test.
     *
     * @return string The PHP code for the test
     */
    public function compile();
}
vendor/twig/twig/lib/Twig/Token.php000064400000000164151166614710013247
0ustar00<?php

use Twig\Token;

class_exists('Twig\Token');

if (\false) {
    class Twig_Token extends Token
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php000064400000000315151166614710016453
0ustar00<?php

use Twig\TokenParser\AutoEscapeTokenParser;

class_exists('Twig\TokenParser\AutoEscapeTokenParser');

if (\false) {
    class Twig_TokenParser_AutoEscape extends AutoEscapeTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Block.php000064400000000271151166614710015455
0ustar00<?php

use Twig\TokenParser\BlockTokenParser;

class_exists('Twig\TokenParser\BlockTokenParser');

if (\false) {
    class Twig_TokenParser_Block extends BlockTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Deprecated.php000064400000000315151166614710016462
0ustar00<?php

use Twig\TokenParser\DeprecatedTokenParser;

class_exists('Twig\TokenParser\DeprecatedTokenParser');

if (\false) {
    class Twig_TokenParser_Deprecated extends DeprecatedTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Do.php000064400000000255151166614710014767
0ustar00<?php

use Twig\TokenParser\DoTokenParser;

class_exists('Twig\TokenParser\DoTokenParser');

if (\false) {
    class Twig_TokenParser_Do extends DoTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Embed.php000064400000000271151166614710015437
0ustar00<?php

use Twig\TokenParser\EmbedTokenParser;

class_exists('Twig\TokenParser\EmbedTokenParser');

if (\false) {
    class Twig_TokenParser_Embed extends EmbedTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Extends.php000064400000000301151166614710016027
0ustar00<?php

use Twig\TokenParser\ExtendsTokenParser;

class_exists('Twig\TokenParser\ExtendsTokenParser');

if (\false) {
    class Twig_TokenParser_Extends extends ExtendsTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Filter.php000064400000000275151166614710015654
0ustar00<?php

use Twig\TokenParser\FilterTokenParser;

class_exists('Twig\TokenParser\FilterTokenParser');

if (\false) {
    class Twig_TokenParser_Filter extends FilterTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Flush.php000064400000000271151166614710015504
0ustar00<?php

use Twig\TokenParser\FlushTokenParser;

class_exists('Twig\TokenParser\FlushTokenParser');

if (\false) {
    class Twig_TokenParser_Flush extends FlushTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/For.php000064400000000261151166614710015150
0ustar00<?php

use Twig\TokenParser\ForTokenParser;

class_exists('Twig\TokenParser\ForTokenParser');

if (\false) {
    class Twig_TokenParser_For extends ForTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/From.php000064400000000265151166614710015331
0ustar00<?php

use Twig\TokenParser\FromTokenParser;

class_exists('Twig\TokenParser\FromTokenParser');

if (\false) {
    class Twig_TokenParser_From extends FromTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/If.php000064400000000255151166614710014763
0ustar00<?php

use Twig\TokenParser\IfTokenParser;

class_exists('Twig\TokenParser\IfTokenParser');

if (\false) {
    class Twig_TokenParser_If extends IfTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Import.php000064400000000275151166614710015701
0ustar00<?php

use Twig\TokenParser\ImportTokenParser;

class_exists('Twig\TokenParser\ImportTokenParser');

if (\false) {
    class Twig_TokenParser_Import extends ImportTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Include.php000064400000000301151166614710016000
0ustar00<?php

use Twig\TokenParser\IncludeTokenParser;

class_exists('Twig\TokenParser\IncludeTokenParser');

if (\false) {
    class Twig_TokenParser_Include extends IncludeTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Macro.php000064400000000271151166614710015464
0ustar00<?php

use Twig\TokenParser\MacroTokenParser;

class_exists('Twig\TokenParser\MacroTokenParser');

if (\false) {
    class Twig_TokenParser_Macro extends MacroTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php000064400000000301151166614710016013
0ustar00<?php

use Twig\TokenParser\SandboxTokenParser;

class_exists('Twig\TokenParser\SandboxTokenParser');

if (\false) {
    class Twig_TokenParser_Sandbox extends SandboxTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Set.php000064400000000261151166614710015155
0ustar00<?php

use Twig\TokenParser\SetTokenParser;

class_exists('Twig\TokenParser\SetTokenParser');

if (\false) {
    class Twig_TokenParser_Set extends SetTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php000064400000000311151166614710016340
0ustar00<?php

use Twig\TokenParser\SpacelessTokenParser;

class_exists('Twig\TokenParser\SpacelessTokenParser');

if (\false) {
    class Twig_TokenParser_Spaceless extends SpacelessTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/Use.php000064400000000261151166614710015156
0ustar00<?php

use Twig\TokenParser\UseTokenParser;

class_exists('Twig\TokenParser\UseTokenParser');

if (\false) {
    class Twig_TokenParser_Use extends UseTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser/With.php000064400000000265151166614710015341
0ustar00<?php

use Twig\TokenParser\WithTokenParser;

class_exists('Twig\TokenParser\WithTokenParser');

if (\false) {
    class Twig_TokenParser_With extends WithTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParser.php000064400000000274151166614710014426
0ustar00<?php

use Twig\TokenParser\AbstractTokenParser;

class_exists('Twig\TokenParser\AbstractTokenParser');

if (\false) {
    class Twig_TokenParser extends AbstractTokenParser
    {
    }
}
vendor/twig/twig/lib/Twig/TokenParserBroker.php000064400000007000151166614710015565
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Arnaud Le Blanc
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\TokenParser\TokenParserInterface;

/**
 * Default implementation of a token parser broker.
 *
 * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface
{
    protected $parser;
    protected $parsers = [];
    protected $brokers = [];

    /**
     * @param array|\Traversable $parsers                 A \Traversable of
Twig_TokenParserInterface instances
     * @param array|\Traversable $brokers                 A \Traversable of
Twig_TokenParserBrokerInterface instances
     * @param bool               $triggerDeprecationError
     */
    public function __construct($parsers = [], $brokers = [],
$triggerDeprecationError = true)
    {
        if ($triggerDeprecationError) {
            @trigger_error('The '.__CLASS__.' class is
deprecated since version 1.12 and will be removed in 2.0.',
E_USER_DEPRECATED);
        }

        foreach ($parsers as $parser) {
            if (!$parser instanceof TokenParserInterface) {
                throw new \LogicException('$parsers must a an array of
Twig_TokenParserInterface.');
            }
            $this->parsers[$parser->getTag()] = $parser;
        }
        foreach ($brokers as $broker) {
            if (!$broker instanceof Twig_TokenParserBrokerInterface) {
                throw new \LogicException('$brokers must a an array of
Twig_TokenParserBrokerInterface.');
            }
            $this->brokers[] = $broker;
        }
    }

    public function addTokenParser(TokenParserInterface $parser)
    {
        $this->parsers[$parser->getTag()] = $parser;
    }

    public function removeTokenParser(TokenParserInterface $parser)
    {
        $name = $parser->getTag();
        if (isset($this->parsers[$name]) && $parser ===
$this->parsers[$name]) {
            unset($this->parsers[$name]);
        }
    }

    public function addTokenParserBroker(self $broker)
    {
        $this->brokers[] = $broker;
    }

    public function removeTokenParserBroker(self $broker)
    {
        if (false !== $pos = array_search($broker, $this->brokers)) {
            unset($this->brokers[$pos]);
        }
    }

    /**
     * Gets a suitable TokenParser for a tag.
     *
     * First looks in parsers, then in brokers.
     *
     * @param string $tag A tag name
     *
     * @return TokenParserInterface|null A Twig_TokenParserInterface or
null if no suitable TokenParser was found
     */
    public function getTokenParser($tag)
    {
        if (isset($this->parsers[$tag])) {
            return $this->parsers[$tag];
        }
        $broker = end($this->brokers);
        while (false !== $broker) {
            $parser = $broker->getTokenParser($tag);
            if (null !== $parser) {
                return $parser;
            }
            $broker = prev($this->brokers);
        }
    }

    public function getParsers()
    {
        return $this->parsers;
    }

    public function getParser()
    {
        return $this->parser;
    }

    public function setParser(Twig_ParserInterface $parser)
    {
        $this->parser = $parser;
        foreach ($this->parsers as $tokenParser) {
            $tokenParser->setParser($parser);
        }
        foreach ($this->brokers as $broker) {
            $broker->setParser($parser);
        }
    }
}
vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php000064400000002317151166614710017414
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Arnaud Le Blanc
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

use Twig\TokenParser\TokenParserInterface;

/**
 * Interface implemented by token parser brokers.
 *
 * Token parser brokers allows to implement custom logic in the process of
resolving a token parser for a given tag name.
 *
 * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
 *
 * @deprecated since 1.12 (to be removed in 2.0)
 */
interface Twig_TokenParserBrokerInterface
{
    /**
     * Gets a TokenParser suitable for a tag.
     *
     * @param string $tag A tag name
     *
     * @return TokenParserInterface|null A Twig_TokenParserInterface or
null if no suitable TokenParser was found
     */
    public function getTokenParser($tag);

    /**
     * Calls Twig\TokenParser\TokenParserInterface::setParser on all
parsers the implementation knows of.
     */
    public function setParser(Twig_ParserInterface $parser);

    /**
     * Gets the Twig_ParserInterface.
     *
     * @return Twig_ParserInterface|null A Twig_ParserInterface instance or
null
     */
    public function getParser();
}
vendor/twig/twig/lib/Twig/TokenParserInterface.php000064400000000310151166614710016236
0ustar00<?php

use Twig\TokenParser\TokenParserInterface;

class_exists('Twig\TokenParser\TokenParserInterface');

if (\false) {
    class Twig_TokenParserInterface extends TokenParserInterface
    {
    }
}
vendor/twig/twig/lib/Twig/TokenStream.php000064400000000214151166614710014417
0ustar00<?php

use Twig\TokenStream;

class_exists('Twig\TokenStream');

if (\false) {
    class Twig_TokenStream extends TokenStream
    {
    }
}
vendor/twig/twig/lib/Twig/Util/DeprecationCollector.php000064400000000277151166614710017215
0ustar00<?php

use Twig\Util\DeprecationCollector;

class_exists('Twig\Util\DeprecationCollector');

if (\false) {
    class Twig_Util_DeprecationCollector extends DeprecationCollector
    {
    }
}
vendor/twig/twig/lib/Twig/Util/TemplateDirIterator.php000064400000000273151166614710017031
0ustar00<?php

use Twig\Util\TemplateDirIterator;

class_exists('Twig\Util\TemplateDirIterator');

if (\false) {
    class Twig_Util_TemplateDirIterator extends TemplateDirIterator
    {
    }
}
vendor/twig/twig/src/Cache/CacheInterface.php000064400000002676151166614710015137
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Cache;

/**
 * Interface implemented by cache classes.
 *
 * It is highly recommended to always store templates on the filesystem to
 * benefit from the PHP opcode cache. This interface is mostly useful if
you
 * need to implement a custom strategy for storing templates on the
filesystem.
 *
 * @author Andrew Tch <andrew@noop.lv>
 */
interface CacheInterface
{
    /**
     * Generates a cache key for the given template class name.
     *
     * @param string $name      The template name
     * @param string $className The template class name
     *
     * @return string
     */
    public function generateKey($name, $className);

    /**
     * Writes the compiled template to cache.
     *
     * @param string $key     The cache key
     * @param string $content The template representation as a PHP class
     */
    public function write($key, $content);

    /**
     * Loads a template from the cache.
     *
     * @param string $key The cache key
     */
    public function load($key);

    /**
     * Returns the modification timestamp of a key.
     *
     * @param string $key The cache key
     *
     * @return int
     */
    public function getTimestamp($key);
}

class_alias('Twig\Cache\CacheInterface',
'Twig_CacheInterface');
vendor/twig/twig/src/Cache/FilesystemCache.php000064400000005060151166614720015352
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Cache;

/**
 * Implements a cache on the filesystem.
 *
 * @author Andrew Tch <andrew@noop.lv>
 */
class FilesystemCache implements CacheInterface
{
    const FORCE_BYTECODE_INVALIDATION = 1;

    private $directory;
    private $options;

    /**
     * @param string $directory The root cache directory
     * @param int    $options   A set of options
     */
    public function __construct($directory, $options = 0)
    {
        $this->directory = rtrim($directory,
'\/').'/';
        $this->options = $options;
    }

    public function generateKey($name, $className)
    {
        $hash = hash('sha256', $className);

        return
$this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
    }

    public function load($key)
    {
        if (file_exists($key)) {
            @include_once $key;
        }
    }

    public function write($key, $content)
    {
        $dir = \dirname($key);
        if (!is_dir($dir)) {
            if (false === @mkdir($dir, 0777, true)) {
                clearstatcache(true, $dir);
                if (!is_dir($dir)) {
                    throw new \RuntimeException(sprintf('Unable to
create the cache directory (%s).', $dir));
                }
            }
        } elseif (!is_writable($dir)) {
            throw new \RuntimeException(sprintf('Unable to write in
the cache directory (%s).', $dir));
        }

        $tmpFile = tempnam($dir, basename($key));
        if (false !== @file_put_contents($tmpFile, $content) &&
@rename($tmpFile, $key)) {
            @chmod($key, 0666 & ~umask());

            if (self::FORCE_BYTECODE_INVALIDATION == ($this->options
& self::FORCE_BYTECODE_INVALIDATION)) {
                // Compile cached file into bytecode cache
                if (\function_exists('opcache_invalidate')
&& filter_var(ini_get('opcache.enable'),
FILTER_VALIDATE_BOOLEAN)) {
                    @opcache_invalidate($key, true);
                } elseif (\function_exists('apc_compile_file')) {
                    apc_compile_file($key);
                }
            }

            return;
        }

        throw new \RuntimeException(sprintf('Failed to write cache
file "%s".', $key));
    }

    public function getTimestamp($key)
    {
        if (!file_exists($key)) {
            return 0;
        }

        return (int) @filemtime($key);
    }
}

class_alias('Twig\Cache\FilesystemCache',
'Twig_Cache_Filesystem');
vendor/twig/twig/src/Cache/NullCache.php000064400000001257151166614720014144
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Cache;

/**
 * Implements a no-cache strategy.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class NullCache implements CacheInterface
{
    public function generateKey($name, $className)
    {
        return '';
    }

    public function write($key, $content)
    {
    }

    public function load($key)
    {
    }

    public function getTimestamp($key)
    {
        return 0;
    }
}

class_alias('Twig\Cache\NullCache', 'Twig_Cache_Null');
vendor/twig/twig/src/Compiler.php000064400000016172151166614720013057
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

use Twig\Node\ModuleNode;

/**
 * Compiles a node to PHP code.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Compiler implements \Twig_CompilerInterface
{
    protected $lastLine;
    protected $source;
    protected $indentation;
    protected $env;
    protected $debugInfo = [];
    protected $sourceOffset;
    protected $sourceLine;
    protected $filename;
    private $varNameSalt = 0;

    public function __construct(Environment $env)
    {
        $this->env = $env;
    }

    /**
     * @deprecated since 1.25 (to be removed in 2.0)
     */
    public function getFilename()
    {
        @trigger_error(sprintf('The %s() method is deprecated since
version 1.25 and will be removed in 2.0.', __FUNCTION__),
E_USER_DEPRECATED);

        return $this->filename;
    }

    /**
     * Returns the environment instance related to this compiler.
     *
     * @return Environment
     */
    public function getEnvironment()
    {
        return $this->env;
    }

    /**
     * Gets the current PHP code after compilation.
     *
     * @return string The PHP code
     */
    public function getSource()
    {
        return $this->source;
    }

    /**
     * Compiles a node.
     *
     * @param int $indentation The current indentation
     *
     * @return $this
     */
    public function compile(\Twig_NodeInterface $node, $indentation = 0)
    {
        $this->lastLine = null;
        $this->source = '';
        $this->debugInfo = [];
        $this->sourceOffset = 0;
        // source code starts at 1 (as we then increment it when we
encounter new lines)
        $this->sourceLine = 1;
        $this->indentation = $indentation;
        $this->varNameSalt = 0;

        if ($node instanceof ModuleNode) {
            // to be removed in 2.0
            $this->filename = $node->getTemplateName();
        }

        $node->compile($this);

        return $this;
    }

    public function subcompile(\Twig_NodeInterface $node, $raw = true)
    {
        if (false === $raw) {
            $this->source .= str_repeat(' ',
$this->indentation * 4);
        }

        $node->compile($this);

        return $this;
    }

    /**
     * Adds a raw string to the compiled code.
     *
     * @param string $string The string
     *
     * @return $this
     */
    public function raw($string)
    {
        $this->source .= $string;

        return $this;
    }

    /**
     * Writes a string to the compiled code by adding indentation.
     *
     * @return $this
     */
    public function write()
    {
        $strings = \func_get_args();
        foreach ($strings as $string) {
            $this->source .= str_repeat(' ',
$this->indentation * 4).$string;
        }

        return $this;
    }

    /**
     * Appends an indentation to the current PHP code after compilation.
     *
     * @return $this
     *
     * @deprecated since 1.27 (to be removed in 2.0).
     */
    public function addIndentation()
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since version 1.27 and will be removed in 2.0. Use
write(\'\') instead.', E_USER_DEPRECATED);

        $this->source .= str_repeat(' ', $this->indentation
* 4);

        return $this;
    }

    /**
     * Adds a quoted string to the compiled code.
     *
     * @param string $value The string
     *
     * @return $this
     */
    public function string($value)
    {
        $this->source .= sprintf('"%s"',
addcslashes($value, "\0\t\"\$\\"));

        return $this;
    }

    /**
     * Returns a PHP representation of a given value.
     *
     * @param mixed $value The value to convert
     *
     * @return $this
     */
    public function repr($value)
    {
        if (\is_int($value) || \is_float($value)) {
            if (false !== $locale = setlocale(LC_NUMERIC, '0')) {
                setlocale(LC_NUMERIC, 'C');
            }

            $this->raw(var_export($value, true));

            if (false !== $locale) {
                setlocale(LC_NUMERIC, $locale);
            }
        } elseif (null === $value) {
            $this->raw('null');
        } elseif (\is_bool($value)) {
            $this->raw($value ? 'true' : 'false');
        } elseif (\is_array($value)) {
            $this->raw('[');
            $first = true;
            foreach ($value as $key => $v) {
                if (!$first) {
                    $this->raw(', ');
                }
                $first = false;
                $this->repr($key);
                $this->raw(' => ');
                $this->repr($v);
            }
            $this->raw(']');
        } else {
            $this->string($value);
        }

        return $this;
    }

    /**
     * Adds debugging information.
     *
     * @return $this
     */
    public function addDebugInfo(\Twig_NodeInterface $node)
    {
        if ($node->getTemplateLine() != $this->lastLine) {
            $this->write(sprintf("// line %d\n",
$node->getTemplateLine()));

            // when mbstring.func_overload is set to 2
            // mb_substr_count() replaces substr_count()
            // but they have different signatures!
            if (((int) ini_get('mbstring.func_overload')) &
2) {
                @trigger_error('Support for having
"mbstring.func_overload" different from 0 is deprecated version
1.29 and will be removed in 2.0.', E_USER_DEPRECATED);

                // this is much slower than the "right" version
                $this->sourceLine +=
mb_substr_count(mb_substr($this->source, $this->sourceOffset),
"\n");
            } else {
                $this->sourceLine += substr_count($this->source,
"\n", $this->sourceOffset);
            }
            $this->sourceOffset = \strlen($this->source);
            $this->debugInfo[$this->sourceLine] =
$node->getTemplateLine();

            $this->lastLine = $node->getTemplateLine();
        }

        return $this;
    }

    public function getDebugInfo()
    {
        ksort($this->debugInfo);

        return $this->debugInfo;
    }

    /**
     * Indents the generated code.
     *
     * @param int $step The number of indentation to add
     *
     * @return $this
     */
    public function indent($step = 1)
    {
        $this->indentation += $step;

        return $this;
    }

    /**
     * Outdents the generated code.
     *
     * @param int $step The number of indentation to remove
     *
     * @return $this
     *
     * @throws \LogicException When trying to outdent too much so the
indentation would become negative
     */
    public function outdent($step = 1)
    {
        // can't outdent by more steps than the current indentation
level
        if ($this->indentation < $step) {
            throw new \LogicException('Unable to call outdent() as the
indentation would become negative.');
        }

        $this->indentation -= $step;

        return $this;
    }

    public function getVarName()
    {
        return sprintf('__internal_%s', hash('sha256',
__METHOD__.$this->varNameSalt++));
    }
}

class_alias('Twig\Compiler', 'Twig_Compiler');
vendor/twig/twig/src/Environment.php000064400000147340151166614720013613
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

use Twig\Cache\CacheInterface;
use Twig\Cache\FilesystemCache;
use Twig\Cache\NullCache;
use Twig\Error\Error;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\Extension\CoreExtension;
use Twig\Extension\EscaperExtension;
use Twig\Extension\ExtensionInterface;
use Twig\Extension\GlobalsInterface;
use Twig\Extension\InitRuntimeInterface;
use Twig\Extension\OptimizerExtension;
use Twig\Extension\StagingExtension;
use Twig\Loader\ArrayLoader;
use Twig\Loader\ChainLoader;
use Twig\Loader\LoaderInterface;
use Twig\Loader\SourceContextLoaderInterface;
use Twig\Node\ModuleNode;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\RuntimeLoader\RuntimeLoaderInterface;
use Twig\TokenParser\TokenParserInterface;

/**
 * Stores the Twig configuration.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Environment
{
    const VERSION = '1.42.5';
    const VERSION_ID = 14205;
    const MAJOR_VERSION = 1;
    const MINOR_VERSION = 42;
    const RELEASE_VERSION = 5;
    const EXTRA_VERSION = '';

    protected $charset;
    protected $loader;
    protected $debug;
    protected $autoReload;
    protected $cache;
    protected $lexer;
    protected $parser;
    protected $compiler;
    protected $baseTemplateClass;
    protected $extensions;
    protected $parsers;
    protected $visitors;
    protected $filters;
    protected $tests;
    protected $functions;
    protected $globals;
    protected $runtimeInitialized = false;
    protected $extensionInitialized = false;
    protected $loadedTemplates;
    protected $strictVariables;
    protected $unaryOperators;
    protected $binaryOperators;
    protected $templateClassPrefix = '__TwigTemplate_';
    protected $functionCallbacks = [];
    protected $filterCallbacks = [];
    protected $staging;

    private $originalCache;
    private $bcWriteCacheFile = false;
    private $bcGetCacheFilename = false;
    private $lastModifiedExtension = 0;
    private $extensionsByClass = [];
    private $runtimeLoaders = [];
    private $runtimes = [];
    private $optionsHash;

    /**
     * Constructor.
     *
     * Available options:
     *
     *  * debug: When set to true, it automatically set
"auto_reload" to true as
     *           well (default to false).
     *
     *  * charset: The charset used by the templates (default to UTF-8).
     *
     *  * base_template_class: The base template class to use for generated
     *                         templates (default to \Twig\Template).
     *
     *  * cache: An absolute path where to store the compiled templates,
     *           a \Twig\Cache\CacheInterface implementation,
     *           or false to disable compilation cache (default).
     *
     *  * auto_reload: Whether to reload the template if the original
source changed.
     *                 If you don't provide the auto_reload option, it
will be
     *                 determined automatically based on the debug value.
     *
     *  * strict_variables: Whether to ignore invalid variables in
templates
     *                      (default to false).
     *
     *  * autoescape: Whether to enable auto-escaping (default to html):
     *                  * false: disable auto-escaping
     *                  * true: equivalent to html
     *                  * html, js: set the autoescaping to one of the
supported strategies
     *                  * name: set the autoescaping strategy based on the
template name extension
     *                  * PHP callback: a PHP callback that returns an
escaping strategy based on the template "name"
     *
     *  * optimizations: A flag that indicates which optimizations to apply
     *                   (default to -1 which means that all optimizations
are enabled;
     *                   set it to 0 to disable).
     */
    public function __construct(LoaderInterface $loader = null, $options =
[])
    {
        if (null !== $loader) {
            $this->setLoader($loader);
        } else {
            @trigger_error('Not passing a
"Twig\Lodaer\LoaderInterface" as the first constructor argument
of "Twig\Environment" is deprecated since version 1.21.',
E_USER_DEPRECATED);
        }

        $options = array_merge([
            'debug' => false,
            'charset' => 'UTF-8',
            'base_template_class' =>
'\Twig\Template',
            'strict_variables' => false,
            'autoescape' => 'html',
            'cache' => false,
            'auto_reload' => null,
            'optimizations' => -1,
        ], $options);

        $this->debug = (bool) $options['debug'];
        $this->charset = strtoupper($options['charset']);
        $this->baseTemplateClass =
$options['base_template_class'];
        $this->autoReload = null === $options['auto_reload'] ?
$this->debug : (bool) $options['auto_reload'];
        $this->strictVariables = (bool)
$options['strict_variables'];
        $this->setCache($options['cache']);

        $this->addExtension(new CoreExtension());
        $this->addExtension(new
EscaperExtension($options['autoescape']));
        $this->addExtension(new
OptimizerExtension($options['optimizations']));
        $this->staging = new StagingExtension();

        // For BC
        if (\is_string($this->originalCache)) {
            $r = new \ReflectionMethod($this, 'writeCacheFile');
            if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
                @trigger_error('The Twig\Environment::writeCacheFile
method is deprecated since version 1.22 and will be removed in Twig
2.0.', E_USER_DEPRECATED);

                $this->bcWriteCacheFile = true;
            }

            $r = new \ReflectionMethod($this,
'getCacheFilename');
            if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
                @trigger_error('The Twig\Environment::getCacheFilename
method is deprecated since version 1.22 and will be removed in Twig
2.0.', E_USER_DEPRECATED);

                $this->bcGetCacheFilename = true;
            }
        }
    }

    /**
     * Gets the base template class for compiled templates.
     *
     * @return string The base template class name
     */
    public function getBaseTemplateClass()
    {
        return $this->baseTemplateClass;
    }

    /**
     * Sets the base template class for compiled templates.
     *
     * @param string $class The base template class name
     */
    public function setBaseTemplateClass($class)
    {
        $this->baseTemplateClass = $class;
        $this->updateOptionsHash();
    }

    /**
     * Enables debugging mode.
     */
    public function enableDebug()
    {
        $this->debug = true;
        $this->updateOptionsHash();
    }

    /**
     * Disables debugging mode.
     */
    public function disableDebug()
    {
        $this->debug = false;
        $this->updateOptionsHash();
    }

    /**
     * Checks if debug mode is enabled.
     *
     * @return bool true if debug mode is enabled, false otherwise
     */
    public function isDebug()
    {
        return $this->debug;
    }

    /**
     * Enables the auto_reload option.
     */
    public function enableAutoReload()
    {
        $this->autoReload = true;
    }

    /**
     * Disables the auto_reload option.
     */
    public function disableAutoReload()
    {
        $this->autoReload = false;
    }

    /**
     * Checks if the auto_reload option is enabled.
     *
     * @return bool true if auto_reload is enabled, false otherwise
     */
    public function isAutoReload()
    {
        return $this->autoReload;
    }

    /**
     * Enables the strict_variables option.
     */
    public function enableStrictVariables()
    {
        $this->strictVariables = true;
        $this->updateOptionsHash();
    }

    /**
     * Disables the strict_variables option.
     */
    public function disableStrictVariables()
    {
        $this->strictVariables = false;
        $this->updateOptionsHash();
    }

    /**
     * Checks if the strict_variables option is enabled.
     *
     * @return bool true if strict_variables is enabled, false otherwise
     */
    public function isStrictVariables()
    {
        return $this->strictVariables;
    }

    /**
     * Gets the current cache implementation.
     *
     * @param bool $original Whether to return the original cache option or
the real cache instance
     *
     * @return CacheInterface|string|false A Twig\Cache\CacheInterface
implementation,
     *                                     an absolute path to the compiled
templates,
     *                                     or false to disable cache
     */
    public function getCache($original = true)
    {
        return $original ? $this->originalCache : $this->cache;
    }

    /**
     * Sets the current cache implementation.
     *
     * @param CacheInterface|string|false $cache A
Twig\Cache\CacheInterface implementation,
     *                                           an absolute path to the
compiled templates,
     *                                           or false to disable cache
     */
    public function setCache($cache)
    {
        if (\is_string($cache)) {
            $this->originalCache = $cache;
            $this->cache = new FilesystemCache($cache);
        } elseif (false === $cache) {
            $this->originalCache = $cache;
            $this->cache = new NullCache();
        } elseif (null === $cache) {
            @trigger_error('Using "null" as the cache
strategy is deprecated since version 1.23 and will be removed in Twig
2.0.', E_USER_DEPRECATED);
            $this->originalCache = false;
            $this->cache = new NullCache();
        } elseif ($cache instanceof CacheInterface) {
            $this->originalCache = $this->cache = $cache;
        } else {
            throw new \LogicException(sprintf('Cache can only be a
string, false, or a \Twig\Cache\CacheInterface implementation.'));
        }
    }

    /**
     * Gets the cache filename for a given template.
     *
     * @param string $name The template name
     *
     * @return string|false The cache file name or false when caching is
disabled
     *
     * @deprecated since 1.22 (to be removed in 2.0)
     */
    public function getCacheFilename($name)
    {
        @trigger_error(sprintf('The %s method is deprecated since
version 1.22 and will be removed in Twig 2.0.', __METHOD__),
E_USER_DEPRECATED);

        $key = $this->cache->generateKey($name,
$this->getTemplateClass($name));

        return !$key ? false : $key;
    }

    /**
     * Gets the template class associated with the given string.
     *
     * The generated template class is based on the following parameters:
     *
     *  * The cache key for the given template;
     *  * The currently enabled extensions;
     *  * Whether the Twig C extension is available or not;
     *  * PHP version;
     *  * Twig version;
     *  * Options with what environment was created.
     *
     * @param string   $name  The name for which to calculate the template
class name
     * @param int|null $index The index if it is an embedded template
     *
     * @return string The template class name
     */
    public function getTemplateClass($name, $index = null)
    {
        $key =
$this->getLoader()->getCacheKey($name).$this->optionsHash;

        return $this->templateClassPrefix.hash('sha256',
$key).(null === $index ? '' : '___'.$index);
    }

    /**
     * Gets the template class prefix.
     *
     * @return string The template class prefix
     *
     * @deprecated since 1.22 (to be removed in 2.0)
     */
    public function getTemplateClassPrefix()
    {
        @trigger_error(sprintf('The %s method is deprecated since
version 1.22 and will be removed in Twig 2.0.', __METHOD__),
E_USER_DEPRECATED);

        return $this->templateClassPrefix;
    }

    /**
     * Renders a template.
     *
     * @param string|TemplateWrapper $name    The template name
     * @param array                  $context An array of parameters to
pass to the template
     *
     * @return string The rendered template
     *
     * @throws LoaderError  When the template cannot be found
     * @throws SyntaxError  When an error occurred during compilation
     * @throws RuntimeError When an error occurred during rendering
     */
    public function render($name, array $context = [])
    {
        return $this->load($name)->render($context);
    }

    /**
     * Displays a template.
     *
     * @param string|TemplateWrapper $name    The template name
     * @param array                  $context An array of parameters to
pass to the template
     *
     * @throws LoaderError  When the template cannot be found
     * @throws SyntaxError  When an error occurred during compilation
     * @throws RuntimeError When an error occurred during rendering
     */
    public function display($name, array $context = [])
    {
        $this->load($name)->display($context);
    }

    /**
     * Loads a template.
     *
     * @param string|TemplateWrapper|\Twig\Template $name The template name
     *
     * @throws LoaderError  When the template cannot be found
     * @throws RuntimeError When a previously generated cache is corrupted
     * @throws SyntaxError  When an error occurred during compilation
     *
     * @return TemplateWrapper
     */
    public function load($name)
    {
        if ($name instanceof TemplateWrapper) {
            return $name;
        }

        if ($name instanceof Template) {
            return new TemplateWrapper($this, $name);
        }

        return new TemplateWrapper($this, $this->loadTemplate($name));
    }

    /**
     * Loads a template internal representation.
     *
     * This method is for internal use only and should never be called
     * directly.
     *
     * @param string $name  The template name
     * @param int    $index The index if it is an embedded template
     *
     * @return \Twig_TemplateInterface A template instance representing the
given template name
     *
     * @throws LoaderError  When the template cannot be found
     * @throws RuntimeError When a previously generated cache is corrupted
     * @throws SyntaxError  When an error occurred during compilation
     *
     * @internal
     */
    public function loadTemplate($name, $index = null)
    {
        return $this->loadClass($this->getTemplateClass($name),
$name, $index);
    }

    /**
     * @internal
     */
    public function loadClass($cls, $name, $index = null)
    {
        $mainCls = $cls;
        if (null !== $index) {
            $cls .= '___'.$index;
        }

        if (isset($this->loadedTemplates[$cls])) {
            return $this->loadedTemplates[$cls];
        }

        if (!class_exists($cls, false)) {
            if ($this->bcGetCacheFilename) {
                $key = $this->getCacheFilename($name);
            } else {
                $key = $this->cache->generateKey($name, $mainCls);
            }

            if (!$this->isAutoReload() ||
$this->isTemplateFresh($name, $this->cache->getTimestamp($key))) {
                $this->cache->load($key);
            }

            $source = null;
            if (!class_exists($cls, false)) {
                $loader = $this->getLoader();
                if (!$loader instanceof SourceContextLoaderInterface) {
                    $source = new Source($loader->getSource($name),
$name);
                } else {
                    $source = $loader->getSourceContext($name);
                }

                $content = $this->compileSource($source);

                if ($this->bcWriteCacheFile) {
                    $this->writeCacheFile($key, $content);
                } else {
                    $this->cache->write($key, $content);
                    $this->cache->load($key);
                }

                if (!class_exists($mainCls, false)) {
                    /* Last line of defense if either
$this->bcWriteCacheFile was used,
                     * $this->cache is implemented as a no-op or we have
a race condition
                     * where the cache was cleared between the above calls
to write to and load from
                     * the cache.
                     */
                    eval('?>'.$content);
                }
            }

            if (!class_exists($cls, false)) {
                throw new RuntimeError(sprintf('Failed to load Twig
template "%s", index "%s": cache might be
corrupted.', $name, $index), -1, $source);
            }
        }

        if (!$this->runtimeInitialized) {
            $this->initRuntime();
        }

        return $this->loadedTemplates[$cls] = new $cls($this);
    }

    /**
     * Creates a template from source.
     *
     * This method should not be used as a generic way to load templates.
     *
     * @param string $template The template source
     * @param string $name     An optional name of the template to be used
in error messages
     *
     * @return TemplateWrapper A template instance representing the given
template name
     *
     * @throws LoaderError When the template cannot be found
     * @throws SyntaxError When an error occurred during compilation
     */
    public function createTemplate($template, $name = null)
    {
        $hash = hash('sha256', $template, false);
        if (null !== $name) {
            $name = sprintf('%s (string template %s)', $name,
$hash);
        } else {
            $name = sprintf('__string_template__%s', $hash);
        }

        $loader = new ChainLoader([
            new ArrayLoader([$name => $template]),
            $current = $this->getLoader(),
        ]);

        $this->setLoader($loader);
        try {
            $template = new TemplateWrapper($this,
$this->loadTemplate($name));
        } catch (\Exception $e) {
            $this->setLoader($current);

            throw $e;
        } catch (\Throwable $e) {
            $this->setLoader($current);

            throw $e;
        }
        $this->setLoader($current);

        return $template;
    }

    /**
     * Returns true if the template is still fresh.
     *
     * Besides checking the loader for freshness information,
     * this method also checks if the enabled extensions have
     * not changed.
     *
     * @param string $name The template name
     * @param int    $time The last modification time of the cached
template
     *
     * @return bool true if the template is fresh, false otherwise
     */
    public function isTemplateFresh($name, $time)
    {
        if (0 === $this->lastModifiedExtension) {
            foreach ($this->extensions as $extension) {
                $r = new \ReflectionObject($extension);
                if (file_exists($r->getFileName()) &&
($extensionTime = filemtime($r->getFileName())) >
$this->lastModifiedExtension) {
                    $this->lastModifiedExtension = $extensionTime;
                }
            }
        }

        return $this->lastModifiedExtension <= $time &&
$this->getLoader()->isFresh($name, $time);
    }

    /**
     * Tries to load a template consecutively from an array.
     *
     * Similar to load() but it also accepts instances of \Twig\Template
and
     * \Twig\TemplateWrapper, and an array of templates where each is tried
to be loaded.
     *
     * @param string|Template|\Twig\TemplateWrapper|array $names A template
or an array of templates to try consecutively
     *
     * @return TemplateWrapper|Template
     *
     * @throws LoaderError When none of the templates can be found
     * @throws SyntaxError When an error occurred during compilation
     */
    public function resolveTemplate($names)
    {
        if (!\is_array($names)) {
            $names = [$names];
        }

        foreach ($names as $name) {
            if ($name instanceof Template) {
                return $name;
            }
            if ($name instanceof TemplateWrapper) {
                return $name;
            }

            try {
                return $this->loadTemplate($name);
            } catch (LoaderError $e) {
                if (1 === \count($names)) {
                    throw $e;
                }
            }
        }

        throw new LoaderError(sprintf('Unable to find one of the
following templates: "%s".', implode('",
"', $names)));
    }

    /**
     * Clears the internal template cache.
     *
     * @deprecated since 1.18.3 (to be removed in 2.0)
     */
    public function clearTemplateCache()
    {
        @trigger_error(sprintf('The %s method is deprecated since
version 1.18.3 and will be removed in Twig 2.0.', __METHOD__),
E_USER_DEPRECATED);

        $this->loadedTemplates = [];
    }

    /**
     * Clears the template cache files on the filesystem.
     *
     * @deprecated since 1.22 (to be removed in 2.0)
     */
    public function clearCacheFiles()
    {
        @trigger_error(sprintf('The %s method is deprecated since
version 1.22 and will be removed in Twig 2.0.', __METHOD__),
E_USER_DEPRECATED);

        if (\is_string($this->originalCache)) {
            foreach (new \RecursiveIteratorIterator(new
\RecursiveDirectoryIterator($this->originalCache),
\RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
                if ($file->isFile()) {
                    @unlink($file->getPathname());
                }
            }
        }
    }

    /**
     * Gets the Lexer instance.
     *
     * @return \Twig_LexerInterface
     *
     * @deprecated since 1.25 (to be removed in 2.0)
     */
    public function getLexer()
    {
        @trigger_error(sprintf('The %s() method is deprecated since
version 1.25 and will be removed in 2.0.', __FUNCTION__),
E_USER_DEPRECATED);

        if (null === $this->lexer) {
            $this->lexer = new Lexer($this);
        }

        return $this->lexer;
    }

    public function setLexer(\Twig_LexerInterface $lexer)
    {
        $this->lexer = $lexer;
    }

    /**
     * Tokenizes a source code.
     *
     * @param string|Source $source The template source code
     * @param string        $name   The template name (deprecated)
     *
     * @return TokenStream
     *
     * @throws SyntaxError When the code is syntactically wrong
     */
    public function tokenize($source, $name = null)
    {
        if (!$source instanceof Source) {
            @trigger_error(sprintf('Passing a string as the $source
argument of %s() is deprecated since version 1.27. Pass a Twig\Source
instance instead.', __METHOD__), E_USER_DEPRECATED);
            $source = new Source($source, $name);
        }

        if (null === $this->lexer) {
            $this->lexer = new Lexer($this);
        }

        return $this->lexer->tokenize($source);
    }

    /**
     * Gets the Parser instance.
     *
     * @return \Twig_ParserInterface
     *
     * @deprecated since 1.25 (to be removed in 2.0)
     */
    public function getParser()
    {
        @trigger_error(sprintf('The %s() method is deprecated since
version 1.25 and will be removed in 2.0.', __FUNCTION__),
E_USER_DEPRECATED);

        if (null === $this->parser) {
            $this->parser = new Parser($this);
        }

        return $this->parser;
    }

    public function setParser(\Twig_ParserInterface $parser)
    {
        $this->parser = $parser;
    }

    /**
     * Converts a token stream to a node tree.
     *
     * @return ModuleNode
     *
     * @throws SyntaxError When the token stream is syntactically or
semantically wrong
     */
    public function parse(TokenStream $stream)
    {
        if (null === $this->parser) {
            $this->parser = new Parser($this);
        }

        return $this->parser->parse($stream);
    }

    /**
     * Gets the Compiler instance.
     *
     * @return \Twig_CompilerInterface
     *
     * @deprecated since 1.25 (to be removed in 2.0)
     */
    public function getCompiler()
    {
        @trigger_error(sprintf('The %s() method is deprecated since
version 1.25 and will be removed in 2.0.', __FUNCTION__),
E_USER_DEPRECATED);

        if (null === $this->compiler) {
            $this->compiler = new Compiler($this);
        }

        return $this->compiler;
    }

    public function setCompiler(\Twig_CompilerInterface $compiler)
    {
        $this->compiler = $compiler;
    }

    /**
     * Compiles a node and returns the PHP code.
     *
     * @return string The compiled PHP source code
     */
    public function compile(\Twig_NodeInterface $node)
    {
        if (null === $this->compiler) {
            $this->compiler = new Compiler($this);
        }

        return $this->compiler->compile($node)->getSource();
    }

    /**
     * Compiles a template source code.
     *
     * @param string|Source $source The template source code
     * @param string        $name   The template name (deprecated)
     *
     * @return string The compiled PHP source code
     *
     * @throws SyntaxError When there was an error during tokenizing,
parsing or compiling
     */
    public function compileSource($source, $name = null)
    {
        if (!$source instanceof Source) {
            @trigger_error(sprintf('Passing a string as the $source
argument of %s() is deprecated since version 1.27. Pass a Twig\Source
instance instead.', __METHOD__), E_USER_DEPRECATED);
            $source = new Source($source, $name);
        }

        try {
            return
$this->compile($this->parse($this->tokenize($source)));
        } catch (Error $e) {
            $e->setSourceContext($source);
            throw $e;
        } catch (\Exception $e) {
            throw new SyntaxError(sprintf('An exception has been
thrown during the compilation of a template ("%s").',
$e->getMessage()), -1, $source, $e);
        }
    }

    public function setLoader(LoaderInterface $loader)
    {
        if (!$loader instanceof SourceContextLoaderInterface && 0
!== strpos(\get_class($loader), 'Mock_')) {
            @trigger_error(sprintf('Twig loader "%s" should
implement Twig\Loader\SourceContextLoaderInterface since version
1.27.', \get_class($loader)), E_USER_DEPRECATED);
        }

        $this->loader = $loader;
    }

    /**
     * Gets the Loader instance.
     *
     * @return LoaderInterface
     */
    public function getLoader()
    {
        if (null === $this->loader) {
            throw new \LogicException('You must set a loader
first.');
        }

        return $this->loader;
    }

    /**
     * Sets the default template charset.
     *
     * @param string $charset The default charset
     */
    public function setCharset($charset)
    {
        $this->charset = strtoupper($charset);
    }

    /**
     * Gets the default template charset.
     *
     * @return string The default charset
     */
    public function getCharset()
    {
        return $this->charset;
    }

    /**
     * Initializes the runtime environment.
     *
     * @deprecated since 1.23 (to be removed in 2.0)
     */
    public function initRuntime()
    {
        $this->runtimeInitialized = true;

        foreach ($this->getExtensions() as $name => $extension) {
            if (!$extension instanceof InitRuntimeInterface) {
                $m = new \ReflectionMethod($extension,
'initRuntime');

                $parentClass = $m->getDeclaringClass()->getName();
                if ('Twig_Extension' !== $parentClass &&
'Twig\Extension\AbstractExtension' !== $parentClass) {
                    @trigger_error(sprintf('Defining the initRuntime()
method in the "%s" extension is deprecated since version 1.23.
Use the `needs_environment` option to get the \Twig_Environment instance in
filters, functions, or tests; or explicitly implement
Twig\Extension\InitRuntimeInterface if needed (not recommended).',
$name), E_USER_DEPRECATED);
                }
            }

            $extension->initRuntime($this);
        }
    }

    /**
     * Returns true if the given extension is registered.
     *
     * @param string $class The extension class name
     *
     * @return bool Whether the extension is registered or not
     */
    public function hasExtension($class)
    {
        $class = ltrim($class, '\\');
        if (!isset($this->extensionsByClass[$class]) &&
class_exists($class, false)) {
            // For BC/FC with namespaced aliases
            $class = new \ReflectionClass($class);
            $class = $class->name;
        }

        if (isset($this->extensions[$class])) {
            if ($class !== \get_class($this->extensions[$class])) {
                @trigger_error(sprintf('Referencing the "%s"
extension by its name (defined by getName()) is deprecated since 1.26 and
will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name
instead.', $class), E_USER_DEPRECATED);
            }

            return true;
        }

        return isset($this->extensionsByClass[$class]);
    }

    /**
     * Adds a runtime loader.
     */
    public function addRuntimeLoader(RuntimeLoaderInterface $loader)
    {
        $this->runtimeLoaders[] = $loader;
    }

    /**
     * Gets an extension by class name.
     *
     * @param string $class The extension class name
     *
     * @return ExtensionInterface
     */
    public function getExtension($class)
    {
        $class = ltrim($class, '\\');
        if (!isset($this->extensionsByClass[$class]) &&
class_exists($class, false)) {
            // For BC/FC with namespaced aliases
            $class = new \ReflectionClass($class);
            $class = $class->name;
        }

        if (isset($this->extensions[$class])) {
            if ($class !== \get_class($this->extensions[$class])) {
                @trigger_error(sprintf('Referencing the "%s"
extension by its name (defined by getName()) is deprecated since 1.26 and
will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name
instead.', $class), E_USER_DEPRECATED);
            }

            return $this->extensions[$class];
        }

        if (!isset($this->extensionsByClass[$class])) {
            throw new RuntimeError(sprintf('The "%s"
extension is not enabled.', $class));
        }

        return $this->extensionsByClass[$class];
    }

    /**
     * Returns the runtime implementation of a Twig element
(filter/function/test).
     *
     * @param string $class A runtime class name
     *
     * @return object The runtime implementation
     *
     * @throws RuntimeError When the template cannot be found
     */
    public function getRuntime($class)
    {
        if (isset($this->runtimes[$class])) {
            return $this->runtimes[$class];
        }

        foreach ($this->runtimeLoaders as $loader) {
            if (null !== $runtime = $loader->load($class)) {
                return $this->runtimes[$class] = $runtime;
            }
        }

        throw new RuntimeError(sprintf('Unable to load the
"%s" runtime.', $class));
    }

    public function addExtension(ExtensionInterface $extension)
    {
        if ($this->extensionInitialized) {
            throw new \LogicException(sprintf('Unable to register
extension "%s" as extensions have already been
initialized.', $extension->getName()));
        }

        $class = \get_class($extension);
        if ($class !== $extension->getName()) {
            if (isset($this->extensions[$extension->getName()])) {
                unset($this->extensions[$extension->getName()],
$this->extensionsByClass[$class]);
                @trigger_error(sprintf('The possibility to register
the same extension twice ("%s") is deprecated since version 1.23
and will be removed in Twig 2.0. Use proper PHP inheritance instead.',
$extension->getName()), E_USER_DEPRECATED);
            }
        }

        $this->lastModifiedExtension = 0;
        $this->extensionsByClass[$class] = $extension;
        $this->extensions[$extension->getName()] = $extension;
        $this->updateOptionsHash();
    }

    /**
     * Removes an extension by name.
     *
     * This method is deprecated and you should not use it.
     *
     * @param string $name The extension name
     *
     * @deprecated since 1.12 (to be removed in 2.0)
     */
    public function removeExtension($name)
    {
        @trigger_error(sprintf('The %s method is deprecated since
version 1.12 and will be removed in Twig 2.0.', __METHOD__),
E_USER_DEPRECATED);

        if ($this->extensionInitialized) {
            throw new \LogicException(sprintf('Unable to remove
extension "%s" as extensions have already been
initialized.', $name));
        }

        $class = ltrim($name, '\\');
        if (!isset($this->extensionsByClass[$class]) &&
class_exists($class, false)) {
            // For BC/FC with namespaced aliases
            $class = new \ReflectionClass($class);
            $class = $class->name;
        }

        if (isset($this->extensions[$class])) {
            if ($class !== \get_class($this->extensions[$class])) {
                @trigger_error(sprintf('Referencing the "%s"
extension by its name (defined by getName()) is deprecated since 1.26 and
will be removed in Twig 2.0. Use the Fully Qualified Extension Class Name
instead.', $class), E_USER_DEPRECATED);
            }

            unset($this->extensions[$class]);
        }

        unset($this->extensions[$class]);
        $this->updateOptionsHash();
    }

    /**
     * Registers an array of extensions.
     *
     * @param array $extensions An array of extensions
     */
    public function setExtensions(array $extensions)
    {
        foreach ($extensions as $extension) {
            $this->addExtension($extension);
        }
    }

    /**
     * Returns all registered extensions.
     *
     * @return ExtensionInterface[] An array of extensions (keys are for
internal usage only and should not be relied on)
     */
    public function getExtensions()
    {
        return $this->extensions;
    }

    public function addTokenParser(TokenParserInterface $parser)
    {
        if ($this->extensionInitialized) {
            throw new \LogicException('Unable to add a token parser as
extensions have already been initialized.');
        }

        $this->staging->addTokenParser($parser);
    }

    /**
     * Gets the registered Token Parsers.
     *
     * @return \Twig_TokenParserBrokerInterface
     *
     * @internal
     */
    public function getTokenParsers()
    {
        if (!$this->extensionInitialized) {
            $this->initExtensions();
        }

        return $this->parsers;
    }

    /**
     * Gets registered tags.
     *
     * Be warned that this method cannot return tags defined by
\Twig_TokenParserBrokerInterface classes.
     *
     * @return TokenParserInterface[]
     *
     * @internal
     */
    public function getTags()
    {
        $tags = [];
        foreach ($this->getTokenParsers()->getParsers() as $parser) {
            if ($parser instanceof TokenParserInterface) {
                $tags[$parser->getTag()] = $parser;
            }
        }

        return $tags;
    }

    public function addNodeVisitor(NodeVisitorInterface $visitor)
    {
        if ($this->extensionInitialized) {
            throw new \LogicException('Unable to add a node visitor as
extensions have already been initialized.');
        }

        $this->staging->addNodeVisitor($visitor);
    }

    /**
     * Gets the registered Node Visitors.
     *
     * @return NodeVisitorInterface[]
     *
     * @internal
     */
    public function getNodeVisitors()
    {
        if (!$this->extensionInitialized) {
            $this->initExtensions();
        }

        return $this->visitors;
    }

    /**
     * Registers a Filter.
     *
     * @param string|TwigFilter                $name   The filter name or a
\Twig_SimpleFilter instance
     * @param \Twig_FilterInterface|TwigFilter $filter
     */
    public function addFilter($name, $filter = null)
    {
        if (!$name instanceof TwigFilter && !($filter instanceof
TwigFilter || $filter instanceof \Twig_FilterInterface)) {
            throw new \LogicException('A filter must be an instance of
\Twig_FilterInterface or \Twig_SimpleFilter.');
        }

        if ($name instanceof TwigFilter) {
            $filter = $name;
            $name = $filter->getName();
        } else {
            @trigger_error(sprintf('Passing a name as a first argument
to the %s method is deprecated since version 1.21. Pass an instance of
"Twig_SimpleFilter" instead when defining filter
"%s".', __METHOD__, $name), E_USER_DEPRECATED);
        }

        if ($this->extensionInitialized) {
            throw new \LogicException(sprintf('Unable to add filter
"%s" as extensions have already been initialized.', $name));
        }

        $this->staging->addFilter($name, $filter);
    }

    /**
     * Get a filter by name.
     *
     * Subclasses may override this method and load filters differently;
     * so no list of filters is available.
     *
     * @param string $name The filter name
     *
     * @return \Twig_Filter|false
     *
     * @internal
     */
    public function getFilter($name)
    {
        if (!$this->extensionInitialized) {
            $this->initExtensions();
        }

        if (isset($this->filters[$name])) {
            return $this->filters[$name];
        }

        foreach ($this->filters as $pattern => $filter) {
            $pattern = str_replace('\\*', '(.*?)',
preg_quote($pattern, '#'), $count);

            if ($count) {
                if (preg_match('#^'.$pattern.'$#',
$name, $matches)) {
                    array_shift($matches);
                    $filter->setArguments($matches);

                    return $filter;
                }
            }
        }

        foreach ($this->filterCallbacks as $callback) {
            if (false !== $filter = \call_user_func($callback, $name)) {
                return $filter;
            }
        }

        return false;
    }

    public function registerUndefinedFilterCallback($callable)
    {
        $this->filterCallbacks[] = $callable;
    }

    /**
     * Gets the registered Filters.
     *
     * Be warned that this method cannot return filters defined with
registerUndefinedFilterCallback.
     *
     * @return \Twig_FilterInterface[]
     *
     * @see registerUndefinedFilterCallback
     *
     * @internal
     */
    public function getFilters()
    {
        if (!$this->extensionInitialized) {
            $this->initExtensions();
        }

        return $this->filters;
    }

    /**
     * Registers a Test.
     *
     * @param string|TwigTest              $name The test name or a
\Twig_SimpleTest instance
     * @param \Twig_TestInterface|TwigTest $test A \Twig_TestInterface
instance or a \Twig_SimpleTest instance
     */
    public function addTest($name, $test = null)
    {
        if (!$name instanceof TwigTest && !($test instanceof
TwigTest || $test instanceof \Twig_TestInterface)) {
            throw new \LogicException('A test must be an instance of
\Twig_TestInterface or \Twig_SimpleTest.');
        }

        if ($name instanceof TwigTest) {
            $test = $name;
            $name = $test->getName();
        } else {
            @trigger_error(sprintf('Passing a name as a first argument
to the %s method is deprecated since version 1.21. Pass an instance of
"Twig_SimpleTest" instead when defining test
"%s".', __METHOD__, $name), E_USER_DEPRECATED);
        }

        if ($this->extensionInitialized) {
            throw new \LogicException(sprintf('Unable to add test
"%s" as extensions have already been initialized.', $name));
        }

        $this->staging->addTest($name, $test);
    }

    /**
     * Gets the registered Tests.
     *
     * @return \Twig_TestInterface[]
     *
     * @internal
     */
    public function getTests()
    {
        if (!$this->extensionInitialized) {
            $this->initExtensions();
        }

        return $this->tests;
    }

    /**
     * Gets a test by name.
     *
     * @param string $name The test name
     *
     * @return \Twig_Test|false
     *
     * @internal
     */
    public function getTest($name)
    {
        if (!$this->extensionInitialized) {
            $this->initExtensions();
        }

        if (isset($this->tests[$name])) {
            return $this->tests[$name];
        }

        foreach ($this->tests as $pattern => $test) {
            $pattern = str_replace('\\*', '(.*?)',
preg_quote($pattern, '#'), $count);

            if ($count) {
                if (preg_match('#^'.$pattern.'$#',
$name, $matches)) {
                    array_shift($matches);
                    $test->setArguments($matches);

                    return $test;
                }
            }
        }

        return false;
    }

    /**
     * Registers a Function.
     *
     * @param string|TwigFunction                  $name     The function
name or a \Twig_SimpleFunction instance
     * @param \Twig_FunctionInterface|TwigFunction $function
     */
    public function addFunction($name, $function = null)
    {
        if (!$name instanceof TwigFunction && !($function
instanceof TwigFunction || $function instanceof \Twig_FunctionInterface)) {
            throw new \LogicException('A function must be an instance
of \Twig_FunctionInterface or \Twig_SimpleFunction.');
        }

        if ($name instanceof TwigFunction) {
            $function = $name;
            $name = $function->getName();
        } else {
            @trigger_error(sprintf('Passing a name as a first argument
to the %s method is deprecated since version 1.21. Pass an instance of
"Twig_SimpleFunction" instead when defining function
"%s".', __METHOD__, $name), E_USER_DEPRECATED);
        }

        if ($this->extensionInitialized) {
            throw new \LogicException(sprintf('Unable to add function
"%s" as extensions have already been initialized.', $name));
        }

        $this->staging->addFunction($name, $function);
    }

    /**
     * Get a function by name.
     *
     * Subclasses may override this method and load functions differently;
     * so no list of functions is available.
     *
     * @param string $name function name
     *
     * @return \Twig_Function|false
     *
     * @internal
     */
    public function getFunction($name)
    {
        if (!$this->extensionInitialized) {
            $this->initExtensions();
        }

        if (isset($this->functions[$name])) {
            return $this->functions[$name];
        }

        foreach ($this->functions as $pattern => $function) {
            $pattern = str_replace('\\*', '(.*?)',
preg_quote($pattern, '#'), $count);

            if ($count) {
                if (preg_match('#^'.$pattern.'$#',
$name, $matches)) {
                    array_shift($matches);
                    $function->setArguments($matches);

                    return $function;
                }
            }
        }

        foreach ($this->functionCallbacks as $callback) {
            if (false !== $function = \call_user_func($callback, $name)) {
                return $function;
            }
        }

        return false;
    }

    public function registerUndefinedFunctionCallback($callable)
    {
        $this->functionCallbacks[] = $callable;
    }

    /**
     * Gets registered functions.
     *
     * Be warned that this method cannot return functions defined with
registerUndefinedFunctionCallback.
     *
     * @return \Twig_FunctionInterface[]
     *
     * @see registerUndefinedFunctionCallback
     *
     * @internal
     */
    public function getFunctions()
    {
        if (!$this->extensionInitialized) {
            $this->initExtensions();
        }

        return $this->functions;
    }

    /**
     * Registers a Global.
     *
     * New globals can be added before compiling or rendering a template;
     * but after, you can only update existing globals.
     *
     * @param string $name  The global name
     * @param mixed  $value The global value
     */
    public function addGlobal($name, $value)
    {
        if ($this->extensionInitialized || $this->runtimeInitialized)
{
            if (null === $this->globals) {
                $this->globals = $this->initGlobals();
            }

            if (!\array_key_exists($name, $this->globals)) {
                // The deprecation notice must be turned into the following
exception in Twig 2.0
                @trigger_error(sprintf('Registering global variable
"%s" at runtime or when the extensions have already been
initialized is deprecated since version 1.21.', $name),
E_USER_DEPRECATED);
                //throw new \LogicException(sprintf('Unable to add
global "%s" as the runtime or the extensions have already been
initialized.', $name));
            }
        }

        if ($this->extensionInitialized || $this->runtimeInitialized)
{
            // update the value
            $this->globals[$name] = $value;
        } else {
            $this->staging->addGlobal($name, $value);
        }
    }

    /**
     * Gets the registered Globals.
     *
     * @return array An array of globals
     *
     * @internal
     */
    public function getGlobals()
    {
        if (!$this->runtimeInitialized &&
!$this->extensionInitialized) {
            return $this->initGlobals();
        }

        if (null === $this->globals) {
            $this->globals = $this->initGlobals();
        }

        return $this->globals;
    }

    /**
     * Merges a context with the defined globals.
     *
     * @param array $context An array representing the context
     *
     * @return array The context merged with the globals
     */
    public function mergeGlobals(array $context)
    {
        // we don't use array_merge as the context being generally
        // bigger than globals, this code is faster.
        foreach ($this->getGlobals() as $key => $value) {
            if (!\array_key_exists($key, $context)) {
                $context[$key] = $value;
            }
        }

        return $context;
    }

    /**
     * Gets the registered unary Operators.
     *
     * @return array An array of unary operators
     *
     * @internal
     */
    public function getUnaryOperators()
    {
        if (!$this->extensionInitialized) {
            $this->initExtensions();
        }

        return $this->unaryOperators;
    }

    /**
     * Gets the registered binary Operators.
     *
     * @return array An array of binary operators
     *
     * @internal
     */
    public function getBinaryOperators()
    {
        if (!$this->extensionInitialized) {
            $this->initExtensions();
        }

        return $this->binaryOperators;
    }

    /**
     * @deprecated since 1.23 (to be removed in 2.0)
     */
    public function computeAlternatives($name, $items)
    {
        @trigger_error(sprintf('The %s method is deprecated since
version 1.23 and will be removed in Twig 2.0.', __METHOD__),
E_USER_DEPRECATED);

        return SyntaxError::computeAlternatives($name, $items);
    }

    /**
     * @internal
     */
    protected function initGlobals()
    {
        $globals = [];
        foreach ($this->extensions as $name => $extension) {
            if (!$extension instanceof GlobalsInterface) {
                $m = new \ReflectionMethod($extension,
'getGlobals');

                $parentClass = $m->getDeclaringClass()->getName();
                if ('Twig_Extension' !== $parentClass &&
'Twig\Extension\AbstractExtension' !== $parentClass) {
                    @trigger_error(sprintf('Defining the getGlobals()
method in the "%s" extension without explicitly implementing
Twig\Extension\GlobalsInterface is deprecated since version 1.23.',
$name), E_USER_DEPRECATED);
                }
            }

            $extGlob = $extension->getGlobals();
            if (!\is_array($extGlob)) {
                throw new
\UnexpectedValueException(sprintf('"%s::getGlobals()" must
return an array of globals.', \get_class($extension)));
            }

            $globals[] = $extGlob;
        }

        $globals[] = $this->staging->getGlobals();

        return \call_user_func_array('array_merge', $globals);
    }

    /**
     * @internal
     */
    protected function initExtensions()
    {
        if ($this->extensionInitialized) {
            return;
        }

        $this->parsers = new \Twig_TokenParserBroker([], [], false);
        $this->filters = [];
        $this->functions = [];
        $this->tests = [];
        $this->visitors = [];
        $this->unaryOperators = [];
        $this->binaryOperators = [];

        foreach ($this->extensions as $extension) {
            $this->initExtension($extension);
        }
        $this->initExtension($this->staging);
        // Done at the end only, so that an exception during initialization
does not mark the environment as initialized when catching the exception
        $this->extensionInitialized = true;
    }

    /**
     * @internal
     */
    protected function initExtension(ExtensionInterface $extension)
    {
        // filters
        foreach ($extension->getFilters() as $name => $filter) {
            if ($filter instanceof TwigFilter) {
                $name = $filter->getName();
            } else {
                @trigger_error(sprintf('Using an instance of
"%s" for filter "%s" is deprecated since version 1.21.
Use \Twig_SimpleFilter instead.', \get_class($filter), $name),
E_USER_DEPRECATED);
            }

            $this->filters[$name] = $filter;
        }

        // functions
        foreach ($extension->getFunctions() as $name => $function) {
            if ($function instanceof TwigFunction) {
                $name = $function->getName();
            } else {
                @trigger_error(sprintf('Using an instance of
"%s" for function "%s" is deprecated since version
1.21. Use \Twig_SimpleFunction instead.', \get_class($function),
$name), E_USER_DEPRECATED);
            }

            $this->functions[$name] = $function;
        }

        // tests
        foreach ($extension->getTests() as $name => $test) {
            if ($test instanceof TwigTest) {
                $name = $test->getName();
            } else {
                @trigger_error(sprintf('Using an instance of
"%s" for test "%s" is deprecated since version 1.21.
Use \Twig_SimpleTest instead.', \get_class($test), $name),
E_USER_DEPRECATED);
            }

            $this->tests[$name] = $test;
        }

        // token parsers
        foreach ($extension->getTokenParsers() as $parser) {
            if ($parser instanceof TokenParserInterface) {
                $this->parsers->addTokenParser($parser);
            } elseif ($parser instanceof \Twig_TokenParserBrokerInterface)
{
                @trigger_error('Registering a
\Twig_TokenParserBrokerInterface instance is deprecated since version
1.21.', E_USER_DEPRECATED);

                $this->parsers->addTokenParserBroker($parser);
            } else {
                throw new \LogicException('getTokenParsers() must
return an array of \Twig_TokenParserInterface or
\Twig_TokenParserBrokerInterface instances.');
            }
        }

        // node visitors
        foreach ($extension->getNodeVisitors() as $visitor) {
            $this->visitors[] = $visitor;
        }

        // operators
        if ($operators = $extension->getOperators()) {
            if (!\is_array($operators)) {
                throw new
\InvalidArgumentException(sprintf('"%s::getOperators()" must
return an array with operators, got "%s".',
\get_class($extension), \is_object($operators) ? \get_class($operators) :
\gettype($operators).(\is_resource($operators) ? '' :
'#'.$operators)));
            }

            if (2 !== \count($operators)) {
                throw new
\InvalidArgumentException(sprintf('"%s::getOperators()" must
return an array of 2 elements, got %d.', \get_class($extension),
\count($operators)));
            }

            $this->unaryOperators =
array_merge($this->unaryOperators, $operators[0]);
            $this->binaryOperators =
array_merge($this->binaryOperators, $operators[1]);
        }
    }

    /**
     * @deprecated since 1.22 (to be removed in 2.0)
     */
    protected function writeCacheFile($file, $content)
    {
        $this->cache->write($file, $content);
    }

    private function updateOptionsHash()
    {
        $hashParts = array_merge(
            array_keys($this->extensions),
            [
                (int)
\function_exists('twig_template_get_attributes'),
                PHP_MAJOR_VERSION,
                PHP_MINOR_VERSION,
                self::VERSION,
                (int) $this->debug,
                $this->baseTemplateClass,
                (int) $this->strictVariables,
            ]
        );
        $this->optionsHash = implode(':', $hashParts);
    }
}

class_alias('Twig\Environment', 'Twig_Environment');
vendor/twig/twig/src/Error/Error.php000064400000023330151166614720013461
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Error;

use Twig\Source;
use Twig\Template;

/**
 * Twig base exception.
 *
 * This exception class and its children must only be used when
 * an error occurs during the loading of a template, when a syntax error
 * is detected in a template, or when rendering a template. Other
 * errors must use regular PHP exception classes (like when the template
 * cache directory is not writable for instance).
 *
 * To help debugging template issues, this class tracks the original
template
 * name and line where the error occurred.
 *
 * Whenever possible, you must set these information (original template
name
 * and line number) yourself by passing them to the constructor. If some or
all
 * these information are not available from where you throw the exception,
then
 * this class will guess them automatically (when the line number is set to
-1
 * and/or the name is set to null). As this is a costly operation, this
 * can be disabled by passing false for both the name and the line number
 * when creating a new instance of this class.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Error extends \Exception
{
    protected $lineno;
    // to be renamed to name in 2.0
    protected $filename;
    protected $rawMessage;

    private $sourcePath;
    private $sourceCode;

    /**
     * Constructor.
     *
     * Set the line number to -1 to enable its automatic guessing.
     * Set the name to null to enable its automatic guessing.
     *
     * @param string             $message  The error message
     * @param int                $lineno   The template line where the
error occurred
     * @param Source|string|null $source   The source context where the
error occurred
     * @param \Exception         $previous The previous exception
     */
    public function __construct($message, $lineno = -1, $source = null,
\Exception $previous = null)
    {
        if (null === $source) {
            $name = null;
        } elseif (!$source instanceof Source) {
            // for compat with the Twig C ext., passing the template name
as string is accepted
            $name = $source;
        } else {
            $name = $source->getName();
            $this->sourceCode = $source->getCode();
            $this->sourcePath = $source->getPath();
        }
        parent::__construct('', 0, $previous);

        $this->lineno = $lineno;
        $this->filename = $name;
        $this->rawMessage = $message;
        $this->updateRepr();
    }

    /**
     * Gets the raw message.
     *
     * @return string The raw message
     */
    public function getRawMessage()
    {
        return $this->rawMessage;
    }

    /**
     * Gets the logical name where the error occurred.
     *
     * @return string The name
     *
     * @deprecated since 1.27 (to be removed in 2.0). Use
getSourceContext() instead.
     */
    public function getTemplateFile()
    {
        @trigger_error(sprintf('The "%s" method is
deprecated since version 1.27 and will be removed in 2.0. Use
getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);

        return $this->filename;
    }

    /**
     * Sets the logical name where the error occurred.
     *
     * @param string $name The name
     *
     * @deprecated since 1.27 (to be removed in 2.0). Use
setSourceContext() instead.
     */
    public function setTemplateFile($name)
    {
        @trigger_error(sprintf('The "%s" method is
deprecated since version 1.27 and will be removed in 2.0. Use
setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);

        $this->filename = $name;

        $this->updateRepr();
    }

    /**
     * Gets the logical name where the error occurred.
     *
     * @return string The name
     *
     * @deprecated since 1.29 (to be removed in 2.0). Use
getSourceContext() instead.
     */
    public function getTemplateName()
    {
        @trigger_error(sprintf('The "%s" method is
deprecated since version 1.29 and will be removed in 2.0. Use
getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);

        return $this->filename;
    }

    /**
     * Sets the logical name where the error occurred.
     *
     * @param string $name The name
     *
     * @deprecated since 1.29 (to be removed in 2.0). Use
setSourceContext() instead.
     */
    public function setTemplateName($name)
    {
        @trigger_error(sprintf('The "%s" method is
deprecated since version 1.29 and will be removed in 2.0. Use
setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);

        $this->filename = $name;
        $this->sourceCode = $this->sourcePath = null;

        $this->updateRepr();
    }

    /**
     * Gets the template line where the error occurred.
     *
     * @return int The template line
     */
    public function getTemplateLine()
    {
        return $this->lineno;
    }

    /**
     * Sets the template line where the error occurred.
     *
     * @param int $lineno The template line
     */
    public function setTemplateLine($lineno)
    {
        $this->lineno = $lineno;

        $this->updateRepr();
    }

    /**
     * Gets the source context of the Twig template where the error
occurred.
     *
     * @return Source|null
     */
    public function getSourceContext()
    {
        return $this->filename ? new Source($this->sourceCode,
$this->filename, $this->sourcePath) : null;
    }

    /**
     * Sets the source context of the Twig template where the error
occurred.
     */
    public function setSourceContext(Source $source = null)
    {
        if (null === $source) {
            $this->sourceCode = $this->filename =
$this->sourcePath = null;
        } else {
            $this->sourceCode = $source->getCode();
            $this->filename = $source->getName();
            $this->sourcePath = $source->getPath();
        }

        $this->updateRepr();
    }

    public function guess()
    {
        $this->guessTemplateInfo();
        $this->updateRepr();
    }

    public function appendMessage($rawMessage)
    {
        $this->rawMessage .= $rawMessage;
        $this->updateRepr();
    }

    /**
     * @internal
     */
    protected function updateRepr()
    {
        $this->message = $this->rawMessage;

        if ($this->sourcePath && $this->lineno > 0) {
            $this->file = $this->sourcePath;
            $this->line = $this->lineno;

            return;
        }

        $dot = false;
        if ('.' === substr($this->message, -1)) {
            $this->message = substr($this->message, 0, -1);
            $dot = true;
        }

        $questionMark = false;
        if ('?' === substr($this->message, -1)) {
            $this->message = substr($this->message, 0, -1);
            $questionMark = true;
        }

        if ($this->filename) {
            if (\is_string($this->filename) ||
(\is_object($this->filename) &&
method_exists($this->filename, '__toString'))) {
                $name = sprintf('"%s"',
$this->filename);
            } else {
                $name = json_encode($this->filename);
            }
            $this->message .= sprintf(' in %s', $name);
        }

        if ($this->lineno && $this->lineno >= 0) {
            $this->message .= sprintf(' at line %d',
$this->lineno);
        }

        if ($dot) {
            $this->message .= '.';
        }

        if ($questionMark) {
            $this->message .= '?';
        }
    }

    /**
     * @internal
     */
    protected function guessTemplateInfo()
    {
        $template = null;
        $templateClass = null;

        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS |
DEBUG_BACKTRACE_PROVIDE_OBJECT);
        foreach ($backtrace as $trace) {
            if (isset($trace['object']) &&
$trace['object'] instanceof Template &&
'Twig_Template' !== \get_class($trace['object'])) {
                $currentClass = \get_class($trace['object']);
                $isEmbedContainer = 0 === strpos($templateClass,
$currentClass);
                if (null === $this->filename || ($this->filename ==
$trace['object']->getTemplateName() &&
!$isEmbedContainer)) {
                    $template = $trace['object'];
                    $templateClass =
\get_class($trace['object']);
                }
            }
        }

        // update template name
        if (null !== $template && null === $this->filename) {
            $this->filename = $template->getTemplateName();
        }

        // update template path if any
        if (null !== $template && null === $this->sourcePath) {
            $src = $template->getSourceContext();
            $this->sourceCode = $src->getCode();
            $this->sourcePath = $src->getPath();
        }

        if (null === $template || $this->lineno > -1) {
            return;
        }

        $r = new \ReflectionObject($template);
        $file = $r->getFileName();

        $exceptions = [$e = $this];
        while ($e instanceof self && $e = $e->getPrevious()) {
            $exceptions[] = $e;
        }

        while ($e = array_pop($exceptions)) {
            $traces = $e->getTrace();
            array_unshift($traces, ['file' =>
$e->getFile(), 'line' => $e->getLine()]);

            while ($trace = array_shift($traces)) {
                if (!isset($trace['file']) ||
!isset($trace['line']) || $file != $trace['file']) {
                    continue;
                }

                foreach ($template->getDebugInfo() as $codeLine =>
$templateLine) {
                    if ($codeLine <= $trace['line']) {
                        // update template line
                        $this->lineno = $templateLine;

                        return;
                    }
                }
            }
        }
    }
}

class_alias('Twig\Error\Error', 'Twig_Error');
vendor/twig/twig/src/Error/LoaderError.php000064400000000700151166614720014604
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Error;

/**
 * Exception thrown when an error occurs during template loading.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class LoaderError extends Error
{
}

class_alias('Twig\Error\LoaderError',
'Twig_Error_Loader');
vendor/twig/twig/src/Error/RuntimeError.php000064400000000714151166614720015026
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Error;

/**
 * Exception thrown when an error occurs at runtime.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class RuntimeError extends Error
{
}

class_alias('Twig\Error\RuntimeError',
'Twig_Error_Runtime');
vendor/twig/twig/src/Error/SyntaxError.php000064400000002706151166614720014674
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Error;

/**
 * \Exception thrown when a syntax error occurs during lexing or parsing of
a template.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class SyntaxError extends Error
{
    /**
     * Tweaks the error message to include suggestions.
     *
     * @param string $name  The original name of the item that does not
exist
     * @param array  $items An array of possible items
     */
    public function addSuggestions($name, array $items)
    {
        if (!$alternatives = self::computeAlternatives($name, $items)) {
            return;
        }

        $this->appendMessage(sprintf(' Did you mean
"%s"?', implode('", "',
$alternatives)));
    }

    /**
     * @internal
     *
     * To be merged with the addSuggestions() method in 2.0.
     */
    public static function computeAlternatives($name, $items)
    {
        $alternatives = [];
        foreach ($items as $item) {
            $lev = levenshtein($name, $item);
            if ($lev <= \strlen($name) / 3 || false !== strpos($item,
$name)) {
                $alternatives[$item] = $lev;
            }
        }
        asort($alternatives);

        return array_keys($alternatives);
    }
}

class_alias('Twig\Error\SyntaxError',
'Twig_Error_Syntax');
vendor/twig/twig/src/ExpressionParser.php000064400000077767151166614720014642
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

use Twig\Error\SyntaxError;
use Twig\Node\Expression\ArrayExpression;
use Twig\Node\Expression\ArrowFunctionExpression;
use Twig\Node\Expression\AssignNameExpression;
use Twig\Node\Expression\Binary\ConcatBinary;
use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\ConditionalExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\ParentExpression;
use Twig\Node\Expression\Unary\NegUnary;
use Twig\Node\Expression\Unary\NotUnary;
use Twig\Node\Expression\Unary\PosUnary;
use Twig\Node\Node;

/**
 * Parses expressions.
 *
 * This parser implements a "Precedence climbing" algorithm.
 *
 * @see https://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
 * @see https://en.wikipedia.org/wiki/Operator-precedence_parser
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @internal
 */
class ExpressionParser
{
    const OPERATOR_LEFT = 1;
    const OPERATOR_RIGHT = 2;

    protected $parser;
    protected $unaryOperators;
    protected $binaryOperators;

    private $env;

    public function __construct(Parser $parser, $env = null)
    {
        $this->parser = $parser;

        if ($env instanceof Environment) {
            $this->env = $env;
            $this->unaryOperators = $env->getUnaryOperators();
            $this->binaryOperators = $env->getBinaryOperators();
        } else {
            @trigger_error('Passing the operators as constructor
arguments to '.__METHOD__.' is deprecated since version 1.27.
Pass the environment instead.', E_USER_DEPRECATED);

            $this->env = $parser->getEnvironment();
            $this->unaryOperators = func_get_arg(1);
            $this->binaryOperators = func_get_arg(2);
        }
    }

    public function parseExpression($precedence = 0, $allowArrow = false)
    {
        if ($allowArrow && $arrow = $this->parseArrow()) {
            return $arrow;
        }

        $expr = $this->getPrimary();
        $token = $this->parser->getCurrentToken();
        while ($this->isBinary($token) &&
$this->binaryOperators[$token->getValue()]['precedence']
>= $precedence) {
            $op = $this->binaryOperators[$token->getValue()];
            $this->parser->getStream()->next();

            if ('is not' === $token->getValue()) {
                $expr = $this->parseNotTestExpression($expr);
            } elseif ('is' === $token->getValue()) {
                $expr = $this->parseTestExpression($expr);
            } elseif (isset($op['callable'])) {
                $expr = \call_user_func($op['callable'],
$this->parser, $expr);
            } else {
                $expr1 = $this->parseExpression(self::OPERATOR_LEFT ===
$op['associativity'] ? $op['precedence'] + 1 :
$op['precedence']);
                $class = $op['class'];
                $expr = new $class($expr, $expr1, $token->getLine());
            }

            $token = $this->parser->getCurrentToken();
        }

        if (0 === $precedence) {
            return $this->parseConditionalExpression($expr);
        }

        return $expr;
    }

    /**
     * @return ArrowFunctionExpression|null
     */
    private function parseArrow()
    {
        $stream = $this->parser->getStream();

        // short array syntax (one argument, no parentheses)?
        if ($stream->look(1)->test(Token::ARROW_TYPE)) {
            $line = $stream->getCurrent()->getLine();
            $token = $stream->expect(Token::NAME_TYPE);
            $names = [new AssignNameExpression($token->getValue(),
$token->getLine())];
            $stream->expect(Token::ARROW_TYPE);

            return new
ArrowFunctionExpression($this->parseExpression(0), new Node($names),
$line);
        }

        // first, determine if we are parsing an arrow function by finding
=> (long form)
        $i = 0;
        if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE,
'(')) {
            return null;
        }
        ++$i;
        while (true) {
            // variable name
            ++$i;
            if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE,
',')) {
                break;
            }
            ++$i;
        }
        if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE,
')')) {
            return null;
        }
        ++$i;
        if (!$stream->look($i)->test(Token::ARROW_TYPE)) {
            return null;
        }

        // yes, let's parse it properly
        $token = $stream->expect(Token::PUNCTUATION_TYPE,
'(');
        $line = $token->getLine();

        $names = [];
        while (true) {
            $token = $stream->expect(Token::NAME_TYPE);
            $names[] = new AssignNameExpression($token->getValue(),
$token->getLine());

            if (!$stream->nextIf(Token::PUNCTUATION_TYPE,
',')) {
                break;
            }
        }
        $stream->expect(Token::PUNCTUATION_TYPE, ')');
        $stream->expect(Token::ARROW_TYPE);

        return new ArrowFunctionExpression($this->parseExpression(0),
new Node($names), $line);
    }

    protected function getPrimary()
    {
        $token = $this->parser->getCurrentToken();

        if ($this->isUnary($token)) {
            $operator = $this->unaryOperators[$token->getValue()];
            $this->parser->getStream()->next();
            $expr =
$this->parseExpression($operator['precedence']);
            $class = $operator['class'];

            return $this->parsePostfixExpression(new $class($expr,
$token->getLine()));
        } elseif ($token->test(Token::PUNCTUATION_TYPE, '('))
{
            $this->parser->getStream()->next();
            $expr = $this->parseExpression();
           
$this->parser->getStream()->expect(Token::PUNCTUATION_TYPE,
')', 'An opened parenthesis is not properly closed');

            return $this->parsePostfixExpression($expr);
        }

        return $this->parsePrimaryExpression();
    }

    protected function parseConditionalExpression($expr)
    {
        while
($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE,
'?')) {
            if
(!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE,
':')) {
                $expr2 = $this->parseExpression();
                if
($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE,
':')) {
                    $expr3 = $this->parseExpression();
                } else {
                    $expr3 = new ConstantExpression('',
$this->parser->getCurrentToken()->getLine());
                }
            } else {
                $expr2 = $expr;
                $expr3 = $this->parseExpression();
            }

            $expr = new ConditionalExpression($expr, $expr2, $expr3,
$this->parser->getCurrentToken()->getLine());
        }

        return $expr;
    }

    protected function isUnary(Token $token)
    {
        return $token->test(Token::OPERATOR_TYPE) &&
isset($this->unaryOperators[$token->getValue()]);
    }

    protected function isBinary(Token $token)
    {
        return $token->test(Token::OPERATOR_TYPE) &&
isset($this->binaryOperators[$token->getValue()]);
    }

    public function parsePrimaryExpression()
    {
        $token = $this->parser->getCurrentToken();
        switch ($token->getType()) {
            case Token::NAME_TYPE:
                $this->parser->getStream()->next();
                switch ($token->getValue()) {
                    case 'true':
                    case 'TRUE':
                        $node = new ConstantExpression(true,
$token->getLine());
                        break;

                    case 'false':
                    case 'FALSE':
                        $node = new ConstantExpression(false,
$token->getLine());
                        break;

                    case 'none':
                    case 'NONE':
                    case 'null':
                    case 'NULL':
                        $node = new ConstantExpression(null,
$token->getLine());
                        break;

                    default:
                        if ('(' ===
$this->parser->getCurrentToken()->getValue()) {
                            $node =
$this->getFunctionNode($token->getValue(), $token->getLine());
                        } else {
                            $node = new
NameExpression($token->getValue(), $token->getLine());
                        }
                }
                break;

            case Token::NUMBER_TYPE:
                $this->parser->getStream()->next();
                $node = new ConstantExpression($token->getValue(),
$token->getLine());
                break;

            case Token::STRING_TYPE:
            case Token::INTERPOLATION_START_TYPE:
                $node = $this->parseStringExpression();
                break;

            case Token::OPERATOR_TYPE:
                if (preg_match(Lexer::REGEX_NAME, $token->getValue(),
$matches) && $matches[0] == $token->getValue()) {
                    // in this context, string operators are variable names
                    $this->parser->getStream()->next();
                    $node = new NameExpression($token->getValue(),
$token->getLine());
                    break;
                } elseif
(isset($this->unaryOperators[$token->getValue()])) {
                    $class =
$this->unaryOperators[$token->getValue()]['class'];

                    $ref = new \ReflectionClass($class);
                    $negClass =
'Twig\Node\Expression\Unary\NegUnary';
                    $posClass =
'Twig\Node\Expression\Unary\PosUnary';
                    if (!(\in_array($ref->getName(), [$negClass,
$posClass, 'Twig_Node_Expression_Unary_Neg',
'Twig_Node_Expression_Unary_Pos'])
                        || $ref->isSubclassOf($negClass) ||
$ref->isSubclassOf($posClass)
                        ||
$ref->isSubclassOf('Twig_Node_Expression_Unary_Neg') ||
$ref->isSubclassOf('Twig_Node_Expression_Unary_Pos'))
                    ) {
                        throw new SyntaxError(sprintf('Unexpected
unary operator "%s".', $token->getValue()),
$token->getLine(),
$this->parser->getStream()->getSourceContext());
                    }

                    $this->parser->getStream()->next();
                    $expr = $this->parsePrimaryExpression();

                    $node = new $class($expr, $token->getLine());
                    break;
                }

                // no break
            default:
                if ($token->test(Token::PUNCTUATION_TYPE,
'[')) {
                    $node = $this->parseArrayExpression();
                } elseif ($token->test(Token::PUNCTUATION_TYPE,
'{')) {
                    $node = $this->parseHashExpression();
                } elseif ($token->test(Token::OPERATOR_TYPE,
'=') && ('==' ===
$this->parser->getStream()->look(-1)->getValue() ||
'!=' ===
$this->parser->getStream()->look(-1)->getValue())) {
                    throw new SyntaxError(sprintf('Unexpected operator
of value "%s". Did you try to use "===" or
"!==" for strict comparison? Use "is same as(value)"
instead.', $token->getValue()), $token->getLine(),
$this->parser->getStream()->getSourceContext());
                } else {
                    throw new SyntaxError(sprintf('Unexpected token
"%s" of value "%s".',
Token::typeToEnglish($token->getType()), $token->getValue()),
$token->getLine(),
$this->parser->getStream()->getSourceContext());
                }
        }

        return $this->parsePostfixExpression($node);
    }

    public function parseStringExpression()
    {
        $stream = $this->parser->getStream();

        $nodes = [];
        // a string cannot be followed by another string in a single
expression
        $nextCanBeString = true;
        while (true) {
            if ($nextCanBeString && $token =
$stream->nextIf(Token::STRING_TYPE)) {
                $nodes[] = new ConstantExpression($token->getValue(),
$token->getLine());
                $nextCanBeString = false;
            } elseif ($stream->nextIf(Token::INTERPOLATION_START_TYPE))
{
                $nodes[] = $this->parseExpression();
                $stream->expect(Token::INTERPOLATION_END_TYPE);
                $nextCanBeString = true;
            } else {
                break;
            }
        }

        $expr = array_shift($nodes);
        foreach ($nodes as $node) {
            $expr = new ConcatBinary($expr, $node,
$node->getTemplateLine());
        }

        return $expr;
    }

    public function parseArrayExpression()
    {
        $stream = $this->parser->getStream();
        $stream->expect(Token::PUNCTUATION_TYPE, '[', 'An
array element was expected');

        $node = new ArrayExpression([],
$stream->getCurrent()->getLine());
        $first = true;
        while (!$stream->test(Token::PUNCTUATION_TYPE, ']')) {
            if (!$first) {
                $stream->expect(Token::PUNCTUATION_TYPE, ',',
'An array element must be followed by a comma');

                // trailing ,?
                if ($stream->test(Token::PUNCTUATION_TYPE,
']')) {
                    break;
                }
            }
            $first = false;

            $node->addElement($this->parseExpression());
        }
        $stream->expect(Token::PUNCTUATION_TYPE, ']', 'An
opened array is not properly closed');

        return $node;
    }

    public function parseHashExpression()
    {
        $stream = $this->parser->getStream();
        $stream->expect(Token::PUNCTUATION_TYPE, '{', 'A
hash element was expected');

        $node = new ArrayExpression([],
$stream->getCurrent()->getLine());
        $first = true;
        while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) {
            if (!$first) {
                $stream->expect(Token::PUNCTUATION_TYPE, ',',
'A hash value must be followed by a comma');

                // trailing ,?
                if ($stream->test(Token::PUNCTUATION_TYPE,
'}')) {
                    break;
                }
            }
            $first = false;

            // a hash key can be:
            //
            //  * a number -- 12
            //  * a string -- 'a'
            //  * a name, which is equivalent to a string -- a
            //  * an expression, which must be enclosed in parentheses --
(1 + 2)
            if (($token = $stream->nextIf(Token::STRING_TYPE)) ||
($token = $stream->nextIf(Token::NAME_TYPE)) || $token =
$stream->nextIf(Token::NUMBER_TYPE)) {
                $key = new ConstantExpression($token->getValue(),
$token->getLine());
            } elseif ($stream->test(Token::PUNCTUATION_TYPE,
'(')) {
                $key = $this->parseExpression();
            } else {
                $current = $stream->getCurrent();

                throw new SyntaxError(sprintf('A hash key must be a
quoted string, a number, a name, or an expression enclosed in parentheses
(unexpected token "%s" of value "%s".',
Token::typeToEnglish($current->getType()), $current->getValue()),
$current->getLine(), $stream->getSourceContext());
            }

            $stream->expect(Token::PUNCTUATION_TYPE, ':',
'A hash key must be followed by a colon (:)');
            $value = $this->parseExpression();

            $node->addElement($value, $key);
        }
        $stream->expect(Token::PUNCTUATION_TYPE, '}', 'An
opened hash is not properly closed');

        return $node;
    }

    public function parsePostfixExpression($node)
    {
        while (true) {
            $token = $this->parser->getCurrentToken();
            if (Token::PUNCTUATION_TYPE == $token->getType()) {
                if ('.' == $token->getValue() || '['
== $token->getValue()) {
                    $node = $this->parseSubscriptExpression($node);
                } elseif ('|' == $token->getValue()) {
                    $node = $this->parseFilterExpression($node);
                } else {
                    break;
                }
            } else {
                break;
            }
        }

        return $node;
    }

    public function getFunctionNode($name, $line)
    {
        switch ($name) {
            case 'parent':
                $this->parseArguments();
                if (!\count($this->parser->getBlockStack())) {
                    throw new SyntaxError('Calling "parent"
outside a block is forbidden.', $line,
$this->parser->getStream()->getSourceContext());
                }

                if (!$this->parser->getParent() &&
!$this->parser->hasTraits()) {
                    throw new SyntaxError('Calling "parent"
on a template that does not extend nor "use" another template is
forbidden.', $line,
$this->parser->getStream()->getSourceContext());
                }

                return new
ParentExpression($this->parser->peekBlockStack(), $line);
            case 'block':
                $args = $this->parseArguments();
                if (\count($args) < 1) {
                    throw new SyntaxError('The "block"
function takes one argument (the block name).', $line,
$this->parser->getStream()->getSourceContext());
                }

                return new BlockReferenceExpression($args->getNode(0),
\count($args) > 1 ? $args->getNode(1) : null, $line);
            case 'attribute':
                $args = $this->parseArguments();
                if (\count($args) < 2) {
                    throw new SyntaxError('The "attribute"
function takes at least two arguments (the variable and the
attributes).', $line,
$this->parser->getStream()->getSourceContext());
                }

                return new GetAttrExpression($args->getNode(0),
$args->getNode(1), \count($args) > 2 ? $args->getNode(2) : null,
Template::ANY_CALL, $line);
            default:
                if (null !== $alias =
$this->parser->getImportedSymbol('function', $name)) {
                    $arguments = new ArrayExpression([], $line);
                    foreach ($this->parseArguments() as $n) {
                        $arguments->addElement($n);
                    }

                    $node = new
MethodCallExpression($alias['node'], $alias['name'],
$arguments, $line);
                    $node->setAttribute('safe', true);

                    return $node;
                }

                $args = $this->parseArguments(true);
                $class = $this->getFunctionNodeClass($name, $line);

                return new $class($name, $args, $line);
        }
    }

    public function parseSubscriptExpression($node)
    {
        $stream = $this->parser->getStream();
        $token = $stream->next();
        $lineno = $token->getLine();
        $arguments = new ArrayExpression([], $lineno);
        $type = Template::ANY_CALL;
        if ('.' == $token->getValue()) {
            $token = $stream->next();
            if (
                Token::NAME_TYPE == $token->getType()
                ||
                Token::NUMBER_TYPE == $token->getType()
                ||
                (Token::OPERATOR_TYPE == $token->getType() &&
preg_match(Lexer::REGEX_NAME, $token->getValue()))
            ) {
                $arg = new ConstantExpression($token->getValue(),
$lineno);

                if ($stream->test(Token::PUNCTUATION_TYPE,
'(')) {
                    $type = Template::METHOD_CALL;
                    foreach ($this->parseArguments() as $n) {
                        $arguments->addElement($n);
                    }
                }
            } else {
                throw new SyntaxError('Expected name or number.',
$lineno, $stream->getSourceContext());
            }

            if ($node instanceof NameExpression && null !==
$this->parser->getImportedSymbol('template',
$node->getAttribute('name'))) {
                if (!$arg instanceof ConstantExpression) {
                    throw new SyntaxError(sprintf('Dynamic macro names
are not supported (called on "%s").',
$node->getAttribute('name')), $token->getLine(),
$stream->getSourceContext());
                }

                $name = $arg->getAttribute('value');

                if ($this->parser->isReservedMacroName($name)) {
                    throw new SyntaxError(sprintf('"%s"
cannot be called as macro as it is a reserved keyword.', $name),
$token->getLine(), $stream->getSourceContext());
                }

                $node = new MethodCallExpression($node,
'get'.$name, $arguments, $lineno);
                $node->setAttribute('safe', true);

                return $node;
            }
        } else {
            $type = Template::ARRAY_CALL;

            // slice?
            $slice = false;
            if ($stream->test(Token::PUNCTUATION_TYPE, ':')) {
                $slice = true;
                $arg = new ConstantExpression(0, $token->getLine());
            } else {
                $arg = $this->parseExpression();
            }

            if ($stream->nextIf(Token::PUNCTUATION_TYPE, ':'))
{
                $slice = true;
            }

            if ($slice) {
                if ($stream->test(Token::PUNCTUATION_TYPE,
']')) {
                    $length = new ConstantExpression(null,
$token->getLine());
                } else {
                    $length = $this->parseExpression();
                }

                $class = $this->getFilterNodeClass('slice',
$token->getLine());
                $arguments = new Node([$arg, $length]);
                $filter = new $class($node, new
ConstantExpression('slice', $token->getLine()), $arguments,
$token->getLine());

                $stream->expect(Token::PUNCTUATION_TYPE, ']');

                return $filter;
            }

            $stream->expect(Token::PUNCTUATION_TYPE, ']');
        }

        return new GetAttrExpression($node, $arg, $arguments, $type,
$lineno);
    }

    public function parseFilterExpression($node)
    {
        $this->parser->getStream()->next();

        return $this->parseFilterExpressionRaw($node);
    }

    public function parseFilterExpressionRaw($node, $tag = null)
    {
        while (true) {
            $token =
$this->parser->getStream()->expect(Token::NAME_TYPE);

            $name = new ConstantExpression($token->getValue(),
$token->getLine());
            if
(!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE,
'(')) {
                $arguments = new Node();
            } else {
                $arguments = $this->parseArguments(true, false, true);
            }

            $class =
$this->getFilterNodeClass($name->getAttribute('value'),
$token->getLine());

            $node = new $class($node, $name, $arguments,
$token->getLine(), $tag);

            if
(!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE,
'|')) {
                break;
            }

            $this->parser->getStream()->next();
        }

        return $node;
    }

    /**
     * Parses arguments.
     *
     * @param bool $namedArguments Whether to allow named arguments or not
     * @param bool $definition     Whether we are parsing arguments for a
function definition
     *
     * @return Node
     *
     * @throws SyntaxError
     */
    public function parseArguments($namedArguments = false, $definition =
false, $allowArrow = false)
    {
        $args = [];
        $stream = $this->parser->getStream();

        $stream->expect(Token::PUNCTUATION_TYPE, '(', 'A
list of arguments must begin with an opening parenthesis');
        while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) {
            if (!empty($args)) {
                $stream->expect(Token::PUNCTUATION_TYPE, ',',
'Arguments must be separated by a comma');
            }

            if ($definition) {
                $token = $stream->expect(Token::NAME_TYPE, null,
'An argument must be a name');
                $value = new NameExpression($token->getValue(),
$this->parser->getCurrentToken()->getLine());
            } else {
                $value = $this->parseExpression(0, $allowArrow);
            }

            $name = null;
            if ($namedArguments && $token =
$stream->nextIf(Token::OPERATOR_TYPE, '=')) {
                if (!$value instanceof NameExpression) {
                    throw new SyntaxError(sprintf('A parameter name
must be a string, "%s" given.', \get_class($value)),
$token->getLine(), $stream->getSourceContext());
                }
                $name = $value->getAttribute('name');

                if ($definition) {
                    $value = $this->parsePrimaryExpression();

                    if (!$this->checkConstantExpression($value)) {
                        throw new SyntaxError(sprintf('A default value
for an argument must be a constant (a boolean, a string, a number, or an
array).'), $token->getLine(), $stream->getSourceContext());
                    }
                } else {
                    $value = $this->parseExpression(0, $allowArrow);
                }
            }

            if ($definition) {
                if (null === $name) {
                    $name = $value->getAttribute('name');
                    $value = new ConstantExpression(null,
$this->parser->getCurrentToken()->getLine());
                }
                $args[$name] = $value;
            } else {
                if (null === $name) {
                    $args[] = $value;
                } else {
                    $args[$name] = $value;
                }
            }
        }
        $stream->expect(Token::PUNCTUATION_TYPE, ')', 'A
list of arguments must be closed by a parenthesis');

        return new Node($args);
    }

    public function parseAssignmentExpression()
    {
        $stream = $this->parser->getStream();
        $targets = [];
        while (true) {
            $token = $this->parser->getCurrentToken();
            if ($stream->test(Token::OPERATOR_TYPE) &&
preg_match(Lexer::REGEX_NAME, $token->getValue())) {
                // in this context, string operators are variable names
                $this->parser->getStream()->next();
            } else {
                $stream->expect(Token::NAME_TYPE, null, 'Only
variables can be assigned to');
            }
            $value = $token->getValue();
            if (\in_array(strtr($value,
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz'), ['true',
'false', 'none', 'null'])) {
                throw new SyntaxError(sprintf('You cannot assign a
value to "%s".', $value), $token->getLine(),
$stream->getSourceContext());
            }
            $targets[] = new AssignNameExpression($value,
$token->getLine());

            if (!$stream->nextIf(Token::PUNCTUATION_TYPE,
',')) {
                break;
            }
        }

        return new Node($targets);
    }

    public function parseMultitargetExpression()
    {
        $targets = [];
        while (true) {
            $targets[] = $this->parseExpression();
            if
(!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE,
',')) {
                break;
            }
        }

        return new Node($targets);
    }

    private function parseNotTestExpression(\Twig_NodeInterface $node)
    {
        return new NotUnary($this->parseTestExpression($node),
$this->parser->getCurrentToken()->getLine());
    }

    private function parseTestExpression(\Twig_NodeInterface $node)
    {
        $stream = $this->parser->getStream();
        list($name, $test) =
$this->getTest($node->getTemplateLine());

        $class = $this->getTestNodeClass($test);
        $arguments = null;
        if ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
            $arguments = $this->parseArguments(true);
        }

        return new $class($node, $name, $arguments,
$this->parser->getCurrentToken()->getLine());
    }

    private function getTest($line)
    {
        $stream = $this->parser->getStream();
        $name = $stream->expect(Token::NAME_TYPE)->getValue();

        if ($test = $this->env->getTest($name)) {
            return [$name, $test];
        }

        if ($stream->test(Token::NAME_TYPE)) {
            // try 2-words tests
            $name = $name.'
'.$this->parser->getCurrentToken()->getValue();

            if ($test = $this->env->getTest($name)) {
                $stream->next();

                return [$name, $test];
            }
        }

        $e = new SyntaxError(sprintf('Unknown "%s"
test.', $name), $line, $stream->getSourceContext());
        $e->addSuggestions($name,
array_keys($this->env->getTests()));

        throw $e;
    }

    private function getTestNodeClass($test)
    {
        if ($test instanceof TwigTest && $test->isDeprecated())
{
            $stream = $this->parser->getStream();
            $message = sprintf('Twig Test "%s" is
deprecated', $test->getName());
            if (!\is_bool($test->getDeprecatedVersion())) {
                $message .= sprintf(' since version %s',
$test->getDeprecatedVersion());
            }
            if ($test->getAlternative()) {
                $message .= sprintf('. Use "%s"
instead', $test->getAlternative());
            }
            $src = $stream->getSourceContext();
            $message .= sprintf(' in %s at line %d.',
$src->getPath() ? $src->getPath() : $src->getName(),
$stream->getCurrent()->getLine());

            @trigger_error($message, E_USER_DEPRECATED);
        }

        if ($test instanceof TwigTest) {
            return $test->getNodeClass();
        }

        return $test instanceof \Twig_Test_Node ? $test->getClass() :
'Twig\Node\Expression\TestExpression';
    }

    protected function getFunctionNodeClass($name, $line)
    {
        if (false === $function = $this->env->getFunction($name)) {
            $e = new SyntaxError(sprintf('Unknown "%s"
function.', $name), $line,
$this->parser->getStream()->getSourceContext());
            $e->addSuggestions($name,
array_keys($this->env->getFunctions()));

            throw $e;
        }

        if ($function instanceof TwigFunction &&
$function->isDeprecated()) {
            $message = sprintf('Twig Function "%s" is
deprecated', $function->getName());
            if (!\is_bool($function->getDeprecatedVersion())) {
                $message .= sprintf(' since version %s',
$function->getDeprecatedVersion());
            }
            if ($function->getAlternative()) {
                $message .= sprintf('. Use "%s"
instead', $function->getAlternative());
            }
            $src = $this->parser->getStream()->getSourceContext();
            $message .= sprintf(' in %s at line %d.',
$src->getPath() ? $src->getPath() : $src->getName(), $line);

            @trigger_error($message, E_USER_DEPRECATED);
        }

        if ($function instanceof TwigFunction) {
            return $function->getNodeClass();
        }

        return $function instanceof \Twig_Function_Node ?
$function->getClass() :
'Twig\Node\Expression\FunctionExpression';
    }

    protected function getFilterNodeClass($name, $line)
    {
        if (false === $filter = $this->env->getFilter($name)) {
            $e = new SyntaxError(sprintf('Unknown "%s"
filter.', $name), $line,
$this->parser->getStream()->getSourceContext());
            $e->addSuggestions($name,
array_keys($this->env->getFilters()));

            throw $e;
        }

        if ($filter instanceof TwigFilter &&
$filter->isDeprecated()) {
            $message = sprintf('Twig Filter "%s" is
deprecated', $filter->getName());
            if (!\is_bool($filter->getDeprecatedVersion())) {
                $message .= sprintf(' since version %s',
$filter->getDeprecatedVersion());
            }
            if ($filter->getAlternative()) {
                $message .= sprintf('. Use "%s"
instead', $filter->getAlternative());
            }
            $src = $this->parser->getStream()->getSourceContext();
            $message .= sprintf(' in %s at line %d.',
$src->getPath() ? $src->getPath() : $src->getName(), $line);

            @trigger_error($message, E_USER_DEPRECATED);
        }

        if ($filter instanceof TwigFilter) {
            return $filter->getNodeClass();
        }

        return $filter instanceof \Twig_Filter_Node ?
$filter->getClass() : 'Twig\Node\Expression\FilterExpression';
    }

    // checks that the node only contains "constant" elements
    protected function checkConstantExpression(\Twig_NodeInterface $node)
    {
        if (!($node instanceof ConstantExpression || $node instanceof
ArrayExpression
            || $node instanceof NegUnary || $node instanceof PosUnary
        )) {
            return false;
        }

        foreach ($node as $n) {
            if (!$this->checkConstantExpression($n)) {
                return false;
            }
        }

        return true;
    }
}

class_alias('Twig\ExpressionParser',
'Twig_ExpressionParser');
vendor/twig/twig/src/Extension/AbstractExtension.php000064400000002501151166614720016710
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension;

use Twig\Environment;

abstract class AbstractExtension implements ExtensionInterface
{
    /**
     * @deprecated since 1.23 (to be removed in 2.0), implement
\Twig_Extension_InitRuntimeInterface instead
     */
    public function initRuntime(Environment $environment)
    {
    }

    public function getTokenParsers()
    {
        return [];
    }

    public function getNodeVisitors()
    {
        return [];
    }

    public function getFilters()
    {
        return [];
    }

    public function getTests()
    {
        return [];
    }

    public function getFunctions()
    {
        return [];
    }

    public function getOperators()
    {
        return [];
    }

    /**
     * @deprecated since 1.23 (to be removed in 2.0), implement
\Twig_Extension_GlobalsInterface instead
     */
    public function getGlobals()
    {
        return [];
    }

    /**
     * @deprecated since 1.26 (to be removed in 2.0), not used anymore
internally
     */
    public function getName()
    {
        return \get_class($this);
    }
}

class_alias('Twig\Extension\AbstractExtension',
'Twig_Extension');
vendor/twig/twig/src/Extension/CoreExtension.php000064400000153077151166614720016054
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension {
use Twig\ExpressionParser;
use Twig\TokenParser\ApplyTokenParser;
use Twig\TokenParser\BlockTokenParser;
use Twig\TokenParser\DeprecatedTokenParser;
use Twig\TokenParser\DoTokenParser;
use Twig\TokenParser\EmbedTokenParser;
use Twig\TokenParser\ExtendsTokenParser;
use Twig\TokenParser\FilterTokenParser;
use Twig\TokenParser\FlushTokenParser;
use Twig\TokenParser\ForTokenParser;
use Twig\TokenParser\FromTokenParser;
use Twig\TokenParser\IfTokenParser;
use Twig\TokenParser\ImportTokenParser;
use Twig\TokenParser\IncludeTokenParser;
use Twig\TokenParser\MacroTokenParser;
use Twig\TokenParser\SetTokenParser;
use Twig\TokenParser\SpacelessTokenParser;
use Twig\TokenParser\UseTokenParser;
use Twig\TokenParser\WithTokenParser;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\TwigTest;

/**
 * @final
 */
class CoreExtension extends AbstractExtension
{
    protected $dateFormats = ['F j, Y H:i', '%d days'];
    protected $numberFormat = [0, '.', ','];
    protected $timezone = null;
    protected $escapers = [];

    /**
     * Defines a new escaper to be used via the escape filter.
     *
     * @param string   $strategy The strategy name that should be used as a
strategy in the escape call
     * @param callable $callable A valid PHP callable
     */
    public function setEscaper($strategy, $callable)
    {
        $this->escapers[$strategy] = $callable;
    }

    /**
     * Gets all defined escapers.
     *
     * @return array An array of escapers
     */
    public function getEscapers()
    {
        return $this->escapers;
    }

    /**
     * Sets the default format to be used by the date filter.
     *
     * @param string $format             The default date format string
     * @param string $dateIntervalFormat The default date interval format
string
     */
    public function setDateFormat($format = null, $dateIntervalFormat =
null)
    {
        if (null !== $format) {
            $this->dateFormats[0] = $format;
        }

        if (null !== $dateIntervalFormat) {
            $this->dateFormats[1] = $dateIntervalFormat;
        }
    }

    /**
     * Gets the default format to be used by the date filter.
     *
     * @return array The default date format string and the default date
interval format string
     */
    public function getDateFormat()
    {
        return $this->dateFormats;
    }

    /**
     * Sets the default timezone to be used by the date filter.
     *
     * @param \DateTimeZone|string $timezone The default timezone string or
a \DateTimeZone object
     */
    public function setTimezone($timezone)
    {
        $this->timezone = $timezone instanceof \DateTimeZone ? $timezone
: new \DateTimeZone($timezone);
    }

    /**
     * Gets the default timezone to be used by the date filter.
     *
     * @return \DateTimeZone The default timezone currently in use
     */
    public function getTimezone()
    {
        if (null === $this->timezone) {
            $this->timezone = new
\DateTimeZone(date_default_timezone_get());
        }

        return $this->timezone;
    }

    /**
     * Sets the default format to be used by the number_format filter.
     *
     * @param int    $decimal      the number of decimal places to use
     * @param string $decimalPoint the character(s) to use for the decimal
point
     * @param string $thousandSep  the character(s) to use for the
thousands separator
     */
    public function setNumberFormat($decimal, $decimalPoint, $thousandSep)
    {
        $this->numberFormat = [$decimal, $decimalPoint, $thousandSep];
    }

    /**
     * Get the default format used by the number_format filter.
     *
     * @return array The arguments for number_format()
     */
    public function getNumberFormat()
    {
        return $this->numberFormat;
    }

    public function getTokenParsers()
    {
        return [
            new ApplyTokenParser(),
            new ForTokenParser(),
            new IfTokenParser(),
            new ExtendsTokenParser(),
            new IncludeTokenParser(),
            new BlockTokenParser(),
            new UseTokenParser(),
            new FilterTokenParser(),
            new MacroTokenParser(),
            new ImportTokenParser(),
            new FromTokenParser(),
            new SetTokenParser(),
            new SpacelessTokenParser(),
            new FlushTokenParser(),
            new DoTokenParser(),
            new EmbedTokenParser(),
            new WithTokenParser(),
            new DeprecatedTokenParser(),
        ];
    }

    public function getFilters()
    {
        $filters = [
            // formatting filters
            new TwigFilter('date',
'twig_date_format_filter', ['needs_environment' =>
true]),
            new TwigFilter('date_modify',
'twig_date_modify_filter', ['needs_environment' =>
true]),
            new TwigFilter('format', 'sprintf'),
            new TwigFilter('replace',
'twig_replace_filter'),
            new TwigFilter('number_format',
'twig_number_format_filter', ['needs_environment' =>
true]),
            new TwigFilter('abs', 'abs'),
            new TwigFilter('round', 'twig_round'),

            // encoding
            new TwigFilter('url_encode',
'twig_urlencode_filter'),
            new TwigFilter('json_encode',
'twig_jsonencode_filter'),
            new TwigFilter('convert_encoding',
'twig_convert_encoding'),

            // string filters
            new TwigFilter('title',
'twig_title_string_filter', ['needs_environment' =>
true]),
            new TwigFilter('capitalize',
'twig_capitalize_string_filter', ['needs_environment'
=> true]),
            new TwigFilter('upper', 'strtoupper'),
            new TwigFilter('lower', 'strtolower'),
            new TwigFilter('striptags', 'strip_tags'),
            new TwigFilter('trim', 'twig_trim_filter'),
            new TwigFilter('nl2br', 'nl2br',
['pre_escape' => 'html', 'is_safe' =>
['html']]),
            new TwigFilter('spaceless',
'twig_spaceless', ['is_safe' =>
['html']]),

            // array helpers
            new TwigFilter('join', 'twig_join_filter'),
            new TwigFilter('split',
'twig_split_filter', ['needs_environment' => true]),
            new TwigFilter('sort', 'twig_sort_filter'),
            new TwigFilter('merge',
'twig_array_merge'),
            new TwigFilter('batch',
'twig_array_batch'),
            new TwigFilter('filter',
'twig_array_filter'),
            new TwigFilter('map', 'twig_array_map'),
            new TwigFilter('reduce',
'twig_array_reduce'),

            // string/array filters
            new TwigFilter('reverse',
'twig_reverse_filter', ['needs_environment' =>
true]),
            new TwigFilter('length',
'twig_length_filter', ['needs_environment' =>
true]),
            new TwigFilter('slice', 'twig_slice',
['needs_environment' => true]),
            new TwigFilter('first', 'twig_first',
['needs_environment' => true]),
            new TwigFilter('last', 'twig_last',
['needs_environment' => true]),

            // iteration and runtime
            new TwigFilter('default',
'_twig_default_filter', ['node_class' =>
'\Twig\Node\Expression\Filter\DefaultFilter']),
            new TwigFilter('keys',
'twig_get_array_keys_filter'),

            // escaping
            new TwigFilter('escape',
'twig_escape_filter', ['needs_environment' => true,
'is_safe_callback' =>
'twig_escape_filter_is_safe']),
            new TwigFilter('e', 'twig_escape_filter',
['needs_environment' => true, 'is_safe_callback'
=> 'twig_escape_filter_is_safe']),
        ];

        if (\function_exists('mb_get_info')) {
            $filters[] = new TwigFilter('upper',
'twig_upper_filter', ['needs_environment' => true]);
            $filters[] = new TwigFilter('lower',
'twig_lower_filter', ['needs_environment' => true]);
        }

        return $filters;
    }

    public function getFunctions()
    {
        return [
            new TwigFunction('max', 'max'),
            new TwigFunction('min', 'min'),
            new TwigFunction('range', 'range'),
            new TwigFunction('constant',
'twig_constant'),
            new TwigFunction('cycle', 'twig_cycle'),
            new TwigFunction('random', 'twig_random',
['needs_environment' => true]),
            new TwigFunction('date',
'twig_date_converter', ['needs_environment' =>
true]),
            new TwigFunction('include', 'twig_include',
['needs_environment' => true, 'needs_context' =>
true, 'is_safe' => ['all']]),
            new TwigFunction('source', 'twig_source',
['needs_environment' => true, 'is_safe' =>
['all']]),
        ];
    }

    public function getTests()
    {
        return [
            new TwigTest('even', null, ['node_class'
=> '\Twig\Node\Expression\Test\EvenTest']),
            new TwigTest('odd', null, ['node_class'
=> '\Twig\Node\Expression\Test\OddTest']),
            new TwigTest('defined', null, ['node_class'
=> '\Twig\Node\Expression\Test\DefinedTest']),
            new TwigTest('sameas', null, ['node_class'
=> '\Twig\Node\Expression\Test\SameasTest',
'deprecated' => '1.21', 'alternative'
=> 'same as']),
            new TwigTest('same as', null, ['node_class'
=> '\Twig\Node\Expression\Test\SameasTest']),
            new TwigTest('none', null, ['node_class'
=> '\Twig\Node\Expression\Test\NullTest']),
            new TwigTest('null', null, ['node_class'
=> '\Twig\Node\Expression\Test\NullTest']),
            new TwigTest('divisibleby', null,
['node_class' =>
'\Twig\Node\Expression\Test\DivisiblebyTest',
'deprecated' => '1.21', 'alternative'
=> 'divisible by']),
            new TwigTest('divisible by', null,
['node_class' =>
'\Twig\Node\Expression\Test\DivisiblebyTest']),
            new TwigTest('constant', null,
['node_class' =>
'\Twig\Node\Expression\Test\ConstantTest']),
            new TwigTest('empty', 'twig_test_empty'),
            new TwigTest('iterable',
'twig_test_iterable'),
        ];
    }

    public function getOperators()
    {
        return [
            [
                'not' => ['precedence' => 50,
'class' => '\Twig\Node\Expression\Unary\NotUnary'],
                '-' => ['precedence' => 500,
'class' => '\Twig\Node\Expression\Unary\NegUnary'],
                '+' => ['precedence' => 500,
'class' => '\Twig\Node\Expression\Unary\PosUnary'],
            ],
            [
                'or' => ['precedence' => 10,
'class' => '\Twig\Node\Expression\Binary\OrBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'and' => ['precedence' => 15,
'class' => '\Twig\Node\Expression\Binary\AndBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'b-or' => ['precedence' => 16,
'class' =>
'\Twig\Node\Expression\Binary\BitwiseOrBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'b-xor' => ['precedence' => 17,
'class' =>
'\Twig\Node\Expression\Binary\BitwiseXorBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'b-and' => ['precedence' => 18,
'class' =>
'\Twig\Node\Expression\Binary\BitwiseAndBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '==' => ['precedence' => 20,
'class' =>
'\Twig\Node\Expression\Binary\EqualBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '!=' => ['precedence' => 20,
'class' =>
'\Twig\Node\Expression\Binary\NotEqualBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '<' => ['precedence' => 20,
'class' =>
'\Twig\Node\Expression\Binary\LessBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '>' => ['precedence' => 20,
'class' =>
'\Twig\Node\Expression\Binary\GreaterBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '>=' => ['precedence' => 20,
'class' =>
'\Twig\Node\Expression\Binary\GreaterEqualBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '<=' => ['precedence' => 20,
'class' =>
'\Twig\Node\Expression\Binary\LessEqualBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'not in' => ['precedence' => 20,
'class' =>
'\Twig\Node\Expression\Binary\NotInBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'in' => ['precedence' => 20,
'class' => '\Twig\Node\Expression\Binary\InBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'matches' => ['precedence' => 20,
'class' =>
'\Twig\Node\Expression\Binary\MatchesBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'starts with' => ['precedence' =>
20, 'class' =>
'\Twig\Node\Expression\Binary\StartsWithBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'ends with' => ['precedence' =>
20, 'class' =>
'\Twig\Node\Expression\Binary\EndsWithBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '..' => ['precedence' => 25,
'class' =>
'\Twig\Node\Expression\Binary\RangeBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '+' => ['precedence' => 30,
'class' => '\Twig\Node\Expression\Binary\AddBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '-' => ['precedence' => 30,
'class' => '\Twig\Node\Expression\Binary\SubBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '~' => ['precedence' => 40,
'class' =>
'\Twig\Node\Expression\Binary\ConcatBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '*' => ['precedence' => 60,
'class' => '\Twig\Node\Expression\Binary\MulBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '/' => ['precedence' => 60,
'class' => '\Twig\Node\Expression\Binary\DivBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '//' => ['precedence' => 60,
'class' =>
'\Twig\Node\Expression\Binary\FloorDivBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '%' => ['precedence' => 60,
'class' => '\Twig\Node\Expression\Binary\ModBinary',
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'is' => ['precedence' => 100,
'associativity' => ExpressionParser::OPERATOR_LEFT],
                'is not' => ['precedence' => 100,
'associativity' => ExpressionParser::OPERATOR_LEFT],
                '**' => ['precedence' => 200,
'class' =>
'\Twig\Node\Expression\Binary\PowerBinary',
'associativity' => ExpressionParser::OPERATOR_RIGHT],
                '??' => ['precedence' => 300,
'class' =>
'\Twig\Node\Expression\NullCoalesceExpression',
'associativity' => ExpressionParser::OPERATOR_RIGHT],
            ],
        ];
    }

    public function getName()
    {
        return 'core';
    }
}

class_alias('Twig\Extension\CoreExtension',
'Twig_Extension_Core');
}

namespace {
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Loader\SourceContextLoaderInterface;
use Twig\Markup;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Node;

/**
 * Cycles over a value.
 *
 * @param \ArrayAccess|array $values
 * @param int                $position The cycle position
 *
 * @return string The next value in the cycle
 */
function twig_cycle($values, $position)
{
    if (!\is_array($values) && !$values instanceof \ArrayAccess) {
        return $values;
    }

    return $values[$position % \count($values)];
}

/**
 * Returns a random value depending on the supplied parameter type:
 * - a random item from a \Traversable or array
 * - a random character from a string
 * - a random integer between 0 and the integer parameter.
 *
 * @param \Traversable|array|int|float|string $values The values to pick a
random item from
 * @param int|null                            $max    Maximum value used
when $values is an int
 *
 * @throws RuntimeError when $values is an empty array (does not apply to
an empty string which is returned as is)
 *
 * @return mixed A random value from the given sequence
 */
function twig_random(Environment $env, $values = null, $max = null)
{
    if (null === $values) {
        return null === $max ? mt_rand() : mt_rand(0, $max);
    }

    if (\is_int($values) || \is_float($values)) {
        if (null === $max) {
            if ($values < 0) {
                $max = 0;
                $min = $values;
            } else {
                $max = $values;
                $min = 0;
            }
        } else {
            $min = $values;
            $max = $max;
        }

        return mt_rand($min, $max);
    }

    if (\is_string($values)) {
        if ('' === $values) {
            return '';
        }
        if (null !== $charset = $env->getCharset()) {
            if ('UTF-8' !== $charset) {
                $values = twig_convert_encoding($values, 'UTF-8',
$charset);
            }

            // unicode version of str_split()
            // split at all positions, but not after the start and not
before the end
            $values = preg_split('/(?<!^)(?!$)/u', $values);

            if ('UTF-8' !== $charset) {
                foreach ($values as $i => $value) {
                    $values[$i] = twig_convert_encoding($value, $charset,
'UTF-8');
                }
            }
        } else {
            return $values[mt_rand(0, \strlen($values) - 1)];
        }
    }

    if (!twig_test_iterable($values)) {
        return $values;
    }

    $values = twig_to_array($values);

    if (0 === \count($values)) {
        throw new RuntimeError('The random function cannot pick from
an empty array.');
    }

    return $values[array_rand($values, 1)];
}

/**
 * Converts a date to the given format.
 *
 *   {{ post.published_at|date("m/d/Y") }}
 *
 * @param \DateTime|\DateTimeInterface|\DateInterval|string $date     A
date
 * @param string|null                                       $format   The
target format, null to use the default
 * @param \DateTimeZone|string|false|null                   $timezone The
target timezone, null to use the default, false to leave unchanged
 *
 * @return string The formatted date
 */
function twig_date_format_filter(Environment $env, $date, $format = null,
$timezone = null)
{
    if (null === $format) {
        $formats =
$env->getExtension('\Twig\Extension\CoreExtension')->getDateFormat();
        $format = $date instanceof \DateInterval ? $formats[1] :
$formats[0];
    }

    if ($date instanceof \DateInterval) {
        return $date->format($format);
    }

    return twig_date_converter($env, $date, $timezone)->format($format);
}

/**
 * Returns a new date object modified.
 *
 *   {{
post.published_at|date_modify("-1day")|date("m/d/Y") }}
 *
 * @param \DateTime|string $date     A date
 * @param string           $modifier A modifier string
 *
 * @return \DateTime
 */
function twig_date_modify_filter(Environment $env, $date, $modifier)
{
    $date = twig_date_converter($env, $date, false);
    $resultDate = $date->modify($modifier);

    // This is a hack to ensure PHP 5.2 support and support for
\DateTimeImmutable
    // \DateTime::modify does not return the modified \DateTime object <
5.3.0
    // and \DateTimeImmutable does not modify $date.
    return null === $resultDate ? $date : $resultDate;
}

/**
 * Converts an input to a \DateTime instance.
 *
 *    {% if date(user.created_at) < date('+2days') %}
 *      {# do something #}
 *    {% endif %}
 *
 * @param \DateTime|\DateTimeInterface|string|null $date     A date
 * @param \DateTimeZone|string|false|null          $timezone The target
timezone, null to use the default, false to leave unchanged
 *
 * @return \DateTimeInterface
 */
function twig_date_converter(Environment $env, $date = null, $timezone =
null)
{
    // determine the timezone
    if (false !== $timezone) {
        if (null === $timezone) {
            $timezone =
$env->getExtension('\Twig\Extension\CoreExtension')->getTimezone();
        } elseif (!$timezone instanceof \DateTimeZone) {
            $timezone = new \DateTimeZone($timezone);
        }
    }

    // immutable dates
    if ($date instanceof \DateTimeImmutable) {
        return false !== $timezone ? $date->setTimezone($timezone) :
$date;
    }

    if ($date instanceof \DateTime || $date instanceof \DateTimeInterface)
{
        $date = clone $date;
        if (false !== $timezone) {
            $date->setTimezone($timezone);
        }

        return $date;
    }

    if (null === $date || 'now' === $date) {
        return new \DateTime($date, false !== $timezone ? $timezone :
$env->getExtension('\Twig\Extension\CoreExtension')->getTimezone());
    }

    $asString = (string) $date;
    if (ctype_digit($asString) || (!empty($asString) &&
'-' === $asString[0] && ctype_digit(substr($asString,
1)))) {
        $date = new \DateTime('@'.$date);
    } else {
        $date = new \DateTime($date,
$env->getExtension('\Twig\Extension\CoreExtension')->getTimezone());
    }

    if (false !== $timezone) {
        $date->setTimezone($timezone);
    }

    return $date;
}

/**
 * Replaces strings within a string.
 *
 * @param string             $str  String to replace in
 * @param array|\Traversable $from Replace values
 * @param string|null        $to   Replace to, deprecated (@see
https://secure.php.net/manual/en/function.strtr.php)
 *
 * @return string
 */
function twig_replace_filter($str, $from, $to = null)
{
    if (\is_string($from) && \is_string($to)) {
        @trigger_error('Using "replace" with character by
character replacement is deprecated since version 1.22 and will be removed
in Twig 2.0', E_USER_DEPRECATED);

        return strtr($str, $from, $to);
    }

    if (!twig_test_iterable($from)) {
        throw new RuntimeError(sprintf('The "replace" filter
expects an array or "Traversable" as replace values, got
"%s".', \is_object($from) ? \get_class($from) :
\gettype($from)));
    }

    return strtr($str, twig_to_array($from));
}

/**
 * Rounds a number.
 *
 * @param int|float $value     The value to round
 * @param int|float $precision The rounding precision
 * @param string    $method    The method to use for rounding
 *
 * @return int|float The rounded number
 */
function twig_round($value, $precision = 0, $method = 'common')
{
    if ('common' == $method) {
        return round($value, $precision);
    }

    if ('ceil' != $method && 'floor' !=
$method) {
        throw new RuntimeError('The round filter only supports the
"common", "ceil", and "floor"
methods.');
    }

    return $method($value * pow(10, $precision)) / pow(10, $precision);
}

/**
 * Number format filter.
 *
 * All of the formatting options can be left null, in that case the
defaults will
 * be used.  Supplying any of the parameters will override the defaults set
in the
 * environment object.
 *
 * @param mixed  $number       A float/int/string of the number to format
 * @param int    $decimal      the number of decimal points to display
 * @param string $decimalPoint the character(s) to use for the decimal
point
 * @param string $thousandSep  the character(s) to use for the thousands
separator
 *
 * @return string The formatted number
 */
function twig_number_format_filter(Environment $env, $number, $decimal =
null, $decimalPoint = null, $thousandSep = null)
{
    $defaults =
$env->getExtension('\Twig\Extension\CoreExtension')->getNumberFormat();
    if (null === $decimal) {
        $decimal = $defaults[0];
    }

    if (null === $decimalPoint) {
        $decimalPoint = $defaults[1];
    }

    if (null === $thousandSep) {
        $thousandSep = $defaults[2];
    }

    return number_format((float) $number, $decimal, $decimalPoint,
$thousandSep);
}

/**
 * URL encodes (RFC 3986) a string as a path segment or an array as a query
string.
 *
 * @param string|array $url A URL or an array of query parameters
 *
 * @return string The URL encoded value
 */
function twig_urlencode_filter($url)
{
    if (\is_array($url)) {
        if (\defined('PHP_QUERY_RFC3986')) {
            return http_build_query($url, '', '&',
PHP_QUERY_RFC3986);
        }

        return http_build_query($url, '', '&');
    }

    return rawurlencode($url);
}

/**
 * JSON encodes a variable.
 *
 * @param mixed $value   the value to encode
 * @param int   $options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG,
JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT,
JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT
 *
 * @return mixed The JSON encoded value
 */
function twig_jsonencode_filter($value, $options = 0)
{
    if ($value instanceof Markup) {
        $value = (string) $value;
    } elseif (\is_array($value)) {
        array_walk_recursive($value, '_twig_markup2string');
    }

    return json_encode($value, $options);
}

function _twig_markup2string(&$value)
{
    if ($value instanceof Markup) {
        $value = (string) $value;
    }
}

/**
 * Merges an array with another one.
 *
 *  {% set items = { 'apple': 'fruit',
'orange': 'fruit' } %}
 *
 *  {% set items = items|merge({ 'peugeot': 'car' }) %}
 *
 *  {# items now contains { 'apple': 'fruit',
'orange': 'fruit', 'peugeot': 'car'
} #}
 *
 * @param array|\Traversable $arr1 An array
 * @param array|\Traversable $arr2 An array
 *
 * @return array The merged array
 */
function twig_array_merge($arr1, $arr2)
{
    if (!twig_test_iterable($arr1)) {
        throw new RuntimeError(sprintf('The merge filter only works
with arrays or "Traversable", got "%s" as first
argument.', \gettype($arr1)));
    }

    if (!twig_test_iterable($arr2)) {
        throw new RuntimeError(sprintf('The merge filter only works
with arrays or "Traversable", got "%s" as second
argument.', \gettype($arr2)));
    }

    return array_merge(twig_to_array($arr1), twig_to_array($arr2));
}

/**
 * Slices a variable.
 *
 * @param mixed $item         A variable
 * @param int   $start        Start of the slice
 * @param int   $length       Size of the slice
 * @param bool  $preserveKeys Whether to preserve key or not (when the
input is an array)
 *
 * @return mixed The sliced variable
 */
function twig_slice(Environment $env, $item, $start, $length = null,
$preserveKeys = false)
{
    if ($item instanceof \Traversable) {
        while ($item instanceof \IteratorAggregate) {
            $item = $item->getIterator();
        }

        if ($start >= 0 && $length >= 0 && $item
instanceof \Iterator) {
            try {
                return iterator_to_array(new \LimitIterator($item, $start,
null === $length ? -1 : $length), $preserveKeys);
            } catch (\OutOfBoundsException $e) {
                return [];
            }
        }

        $item = iterator_to_array($item, $preserveKeys);
    }

    if (\is_array($item)) {
        return \array_slice($item, $start, $length, $preserveKeys);
    }

    $item = (string) $item;

    if (\function_exists('mb_get_info') && null !==
$charset = $env->getCharset()) {
        return (string) mb_substr($item, $start, null === $length ?
mb_strlen($item, $charset) - $start : $length, $charset);
    }

    return (string) (null === $length ? substr($item, $start) :
substr($item, $start, $length));
}

/**
 * Returns the first element of the item.
 *
 * @param mixed $item A variable
 *
 * @return mixed The first element of the item
 */
function twig_first(Environment $env, $item)
{
    $elements = twig_slice($env, $item, 0, 1, false);

    return \is_string($elements) ? $elements : current($elements);
}

/**
 * Returns the last element of the item.
 *
 * @param mixed $item A variable
 *
 * @return mixed The last element of the item
 */
function twig_last(Environment $env, $item)
{
    $elements = twig_slice($env, $item, -1, 1, false);

    return \is_string($elements) ? $elements : current($elements);
}

/**
 * Joins the values to a string.
 *
 * The separators between elements are empty strings per default, you can
define them with the optional parameters.
 *
 *  {{ [1, 2, 3]|join(', ', ' and ') }}
 *  {# returns 1, 2 and 3 #}
 *
 *  {{ [1, 2, 3]|join('|') }}
 *  {# returns 1|2|3 #}
 *
 *  {{ [1, 2, 3]|join }}
 *  {# returns 123 #}
 *
 * @param array       $value An array
 * @param string      $glue  The separator
 * @param string|null $and   The separator for the last pair
 *
 * @return string The concatenated string
 */
function twig_join_filter($value, $glue = '', $and = null)
{
    if (!twig_test_iterable($value)) {
        $value = (array) $value;
    }

    $value = twig_to_array($value, false);

    if (0 === \count($value)) {
        return '';
    }

    if (null === $and || $and === $glue) {
        return implode($glue, $value);
    }

    if (1 === \count($value)) {
        return $value[0];
    }

    return implode($glue, \array_slice($value, 0,
-1)).$and.$value[\count($value) - 1];
}

/**
 * Splits the string into an array.
 *
 *  {{ "one,two,three"|split(',') }}
 *  {# returns [one, two, three] #}
 *
 *  {{ "one,two,three,four,five"|split(',', 3) }}
 *  {# returns [one, two, "three,four,five"] #}
 *
 *  {{ "123"|split('') }}
 *  {# returns [1, 2, 3] #}
 *
 *  {{ "aabbcc"|split('', 2) }}
 *  {# returns [aa, bb, cc] #}
 *
 * @param string $value     A string
 * @param string $delimiter The delimiter
 * @param int    $limit     The limit
 *
 * @return array The split string as an array
 */
function twig_split_filter(Environment $env, $value, $delimiter, $limit =
null)
{
    if (\strlen($delimiter) > 0) {
        return null === $limit ? explode($delimiter, $value) :
explode($delimiter, $value, $limit);
    }

    if (!\function_exists('mb_get_info') || null === $charset =
$env->getCharset()) {
        return str_split($value, null === $limit ? 1 : $limit);
    }

    if ($limit <= 1) {
        return preg_split('/(?<!^)(?!$)/u', $value);
    }

    $length = mb_strlen($value, $charset);
    if ($length < $limit) {
        return [$value];
    }

    $r = [];
    for ($i = 0; $i < $length; $i += $limit) {
        $r[] = mb_substr($value, $i, $limit, $charset);
    }

    return $r;
}

// The '_default' filter is used internally to avoid using the
ternary operator
// which costs a lot for big contexts (before PHP 5.4). So, on average,
// a function call is cheaper.
/**
 * @internal
 */
function _twig_default_filter($value, $default = '')
{
    if (twig_test_empty($value)) {
        return $default;
    }

    return $value;
}

/**
 * Returns the keys for the given array.
 *
 * It is useful when you want to iterate over the keys of an array:
 *
 *  {% for key in array|keys %}
 *      {# ... #}
 *  {% endfor %}
 *
 * @param array $array An array
 *
 * @return array The keys
 */
function twig_get_array_keys_filter($array)
{
    if ($array instanceof \Traversable) {
        while ($array instanceof \IteratorAggregate) {
            $array = $array->getIterator();
        }

        if ($array instanceof \Iterator) {
            $keys = [];
            $array->rewind();
            while ($array->valid()) {
                $keys[] = $array->key();
                $array->next();
            }

            return $keys;
        }

        $keys = [];
        foreach ($array as $key => $item) {
            $keys[] = $key;
        }

        return $keys;
    }

    if (!\is_array($array)) {
        return [];
    }

    return array_keys($array);
}

/**
 * Reverses a variable.
 *
 * @param array|\Traversable|string $item         An array, a \Traversable
instance, or a string
 * @param bool                      $preserveKeys Whether to preserve key
or not
 *
 * @return mixed The reversed input
 */
function twig_reverse_filter(Environment $env, $item, $preserveKeys =
false)
{
    if ($item instanceof \Traversable) {
        return array_reverse(iterator_to_array($item), $preserveKeys);
    }

    if (\is_array($item)) {
        return array_reverse($item, $preserveKeys);
    }

    if (null !== $charset = $env->getCharset()) {
        $string = (string) $item;

        if ('UTF-8' !== $charset) {
            $item = twig_convert_encoding($string, 'UTF-8',
$charset);
        }

        preg_match_all('/./us', $item, $matches);

        $string = implode('', array_reverse($matches[0]));

        if ('UTF-8' !== $charset) {
            $string = twig_convert_encoding($string, $charset,
'UTF-8');
        }

        return $string;
    }

    return strrev((string) $item);
}

/**
 * Sorts an array.
 *
 * @param array|\Traversable $array
 *
 * @return array
 */
function twig_sort_filter($array)
{
    if ($array instanceof \Traversable) {
        $array = iterator_to_array($array);
    } elseif (!\is_array($array)) {
        throw new RuntimeError(sprintf('The sort filter only works
with arrays or "Traversable", got "%s".',
\gettype($array)));
    }

    asort($array);

    return $array;
}

/**
 * @internal
 */
function twig_in_filter($value, $compare)
{
    if ($value instanceof Markup) {
        $value = (string) $value;
    }
    if ($compare instanceof Markup) {
        $compare = (string) $compare;
    }

    if (\is_array($compare)) {
        return \in_array($value, $compare, \is_object($value) ||
\is_resource($value));
    } elseif (\is_string($compare) && (\is_string($value) ||
\is_int($value) || \is_float($value))) {
        return '' === $value || false !== strpos($compare,
(string) $value);
    } elseif ($compare instanceof \Traversable) {
        if (\is_object($value) || \is_resource($value)) {
            foreach ($compare as $item) {
                if ($item === $value) {
                    return true;
                }
            }
        } else {
            foreach ($compare as $item) {
                if ($item == $value) {
                    return true;
                }
            }
        }

        return false;
    }

    return false;
}

/**
 * Returns a trimmed string.
 *
 * @return string
 *
 * @throws RuntimeError When an invalid trimming side is used (not a string
or not 'left', 'right', or 'both')
 */
function twig_trim_filter($string, $characterMask = null, $side =
'both')
{
    if (null === $characterMask) {
        $characterMask = " \t\n\r\0\x0B";
    }

    switch ($side) {
        case 'both':
            return trim($string, $characterMask);
        case 'left':
            return ltrim($string, $characterMask);
        case 'right':
            return rtrim($string, $characterMask);
        default:
            throw new RuntimeError('Trimming side must be
"left", "right" or "both".');
    }
}

/**
 * Removes whitespaces between HTML tags.
 *
 * @return string
 */
function twig_spaceless($content)
{
    return trim(preg_replace('/>\s+</',
'><', $content));
}

/**
 * Escapes a string.
 *
 * @param mixed  $string     The value to be escaped
 * @param string $strategy   The escaping strategy
 * @param string $charset    The charset
 * @param bool   $autoescape Whether the function is called by the
auto-escaping feature (true) or by the developer (false)
 *
 * @return string
 */
function twig_escape_filter(Environment $env, $string, $strategy =
'html', $charset = null, $autoescape = false)
{
    if ($autoescape && $string instanceof Markup) {
        return $string;
    }

    if (!\is_string($string)) {
        if (\is_object($string) && method_exists($string,
'__toString')) {
            $string = (string) $string;
        } elseif (\in_array($strategy, ['html', 'js',
'css', 'html_attr', 'url'])) {
            return $string;
        }
    }

    if ('' === $string) {
        return '';
    }

    if (null === $charset) {
        $charset = $env->getCharset();
    }

    switch ($strategy) {
        case 'html':
            // see https://secure.php.net/htmlspecialchars

            // Using a static variable to avoid initializing the array
            // each time the function is called. Moving the declaration on
the
            // top of the function slow downs other escaping strategies.
            static $htmlspecialcharsCharsets = [
                'ISO-8859-1' => true, 'ISO8859-1'
=> true,
                'ISO-8859-15' => true, 'ISO8859-15'
=> true,
                'utf-8' => true, 'UTF-8' => true,
                'CP866' => true, 'IBM866' =>
true, '866' => true,
                'CP1251' => true, 'WINDOWS-1251'
=> true, 'WIN-1251' => true,
                '1251' => true,
                'CP1252' => true, 'WINDOWS-1252'
=> true, '1252' => true,
                'KOI8-R' => true, 'KOI8-RU' =>
true, 'KOI8R' => true,
                'BIG5' => true, '950' => true,
                'GB2312' => true, '936' => true,
                'BIG5-HKSCS' => true,
                'SHIFT_JIS' => true, 'SJIS' =>
true, '932' => true,
                'EUC-JP' => true, 'EUCJP' =>
true,
                'ISO8859-5' => true, 'ISO-8859-5'
=> true, 'MACROMAN' => true,
            ];

            if (isset($htmlspecialcharsCharsets[$charset])) {
                return htmlspecialchars($string, ENT_QUOTES |
ENT_SUBSTITUTE, $charset);
            }

            if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
                // cache the lowercase variant for future iterations
                $htmlspecialcharsCharsets[$charset] = true;

                return htmlspecialchars($string, ENT_QUOTES |
ENT_SUBSTITUTE, $charset);
            }

            $string = twig_convert_encoding($string, 'UTF-8',
$charset);
            $string = htmlspecialchars($string, ENT_QUOTES |
ENT_SUBSTITUTE, 'UTF-8');

            return twig_convert_encoding($string, $charset,
'UTF-8');

        case 'js':
            // escape all non-alphanumeric characters
            // into their \x or \uHHHH representations
            if ('UTF-8' !== $charset) {
                $string = twig_convert_encoding($string, 'UTF-8',
$charset);
            }

            if (!preg_match('//u', $string)) {
                throw new RuntimeError('The string to escape is not a
valid UTF-8 string.');
            }

            $string =
preg_replace_callback('#[^a-zA-Z0-9,\._]#Su',
'_twig_escape_js_callback', $string);

            if ('UTF-8' !== $charset) {
                $string = twig_convert_encoding($string, $charset,
'UTF-8');
            }

            return $string;

        case 'css':
            if ('UTF-8' !== $charset) {
                $string = twig_convert_encoding($string, 'UTF-8',
$charset);
            }

            if (!preg_match('//u', $string)) {
                throw new RuntimeError('The string to escape is not a
valid UTF-8 string.');
            }

            $string = preg_replace_callback('#[^a-zA-Z0-9]#Su',
'_twig_escape_css_callback', $string);

            if ('UTF-8' !== $charset) {
                $string = twig_convert_encoding($string, $charset,
'UTF-8');
            }

            return $string;

        case 'html_attr':
            if ('UTF-8' !== $charset) {
                $string = twig_convert_encoding($string, 'UTF-8',
$charset);
            }

            if (!preg_match('//u', $string)) {
                throw new RuntimeError('The string to escape is not a
valid UTF-8 string.');
            }

            $string =
preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su',
'_twig_escape_html_attr_callback', $string);

            if ('UTF-8' !== $charset) {
                $string = twig_convert_encoding($string, $charset,
'UTF-8');
            }

            return $string;

        case 'url':
            return rawurlencode($string);

        default:
            static $escapers;

            if (null === $escapers) {
                $escapers =
$env->getExtension('\Twig\Extension\CoreExtension')->getEscapers();
            }

            if (isset($escapers[$strategy])) {
                return \call_user_func($escapers[$strategy], $env, $string,
$charset);
            }

            $validStrategies = implode(', ',
array_merge(['html', 'js', 'url',
'css', 'html_attr'], array_keys($escapers)));

            throw new RuntimeError(sprintf('Invalid escaping strategy
"%s" (valid ones: %s).', $strategy, $validStrategies));
    }
}

/**
 * @internal
 */
function twig_escape_filter_is_safe(Node $filterArgs)
{
    foreach ($filterArgs as $arg) {
        if ($arg instanceof ConstantExpression) {
            return [$arg->getAttribute('value')];
        }

        return [];
    }

    return ['html'];
}

if (\function_exists('mb_convert_encoding')) {
    function twig_convert_encoding($string, $to, $from)
    {
        return mb_convert_encoding($string, $to, $from);
    }
} elseif (\function_exists('iconv')) {
    function twig_convert_encoding($string, $to, $from)
    {
        return iconv($from, $to, $string);
    }
} else {
    function twig_convert_encoding($string, $to, $from)
    {
        throw new RuntimeError('No suitable convert encoding function
(use UTF-8 as your encoding or install the iconv or mbstring
extension).');
    }
}

if (\function_exists('mb_ord')) {
    function twig_ord($string)
    {
        return mb_ord($string, 'UTF-8');
    }
} else {
    function twig_ord($string)
    {
        $code = ($string = unpack('C*', substr($string, 0, 4))) ?
$string[1] : 0;
        if (0xF0 <= $code) {
            return (($code - 0xF0) << 18) + (($string[2] - 0x80)
<< 12) + (($string[3] - 0x80) << 6) + $string[4] - 0x80;
        }
        if (0xE0 <= $code) {
            return (($code - 0xE0) << 12) + (($string[2] - 0x80)
<< 6) + $string[3] - 0x80;
        }
        if (0xC0 <= $code) {
            return (($code - 0xC0) << 6) + $string[2] - 0x80;
        }

        return $code;
    }
}

function _twig_escape_js_callback($matches)
{
    $char = $matches[0];

    /*
     * A few characters have short escape sequences in JSON and JavaScript.
     * Escape sequences supported only by JavaScript, not JSON, are
ommitted.
     * \" is also supported but omitted, because the resulting string
is not HTML safe.
     */
    static $shortMap = [
        '\\' => '\\\\',
        '/' => '\\/',
        "\x08" => '\b',
        "\x0C" => '\f',
        "\x0A" => '\n',
        "\x0D" => '\r',
        "\x09" => '\t',
    ];

    if (isset($shortMap[$char])) {
        return $shortMap[$char];
    }

    // \uHHHH
    $char = twig_convert_encoding($char, 'UTF-16BE',
'UTF-8');
    $char = strtoupper(bin2hex($char));

    if (4 >= \strlen($char)) {
        return sprintf('\u%04s', $char);
    }

    return sprintf('\u%04s\u%04s', substr($char, 0, -4),
substr($char, -4));
}

function _twig_escape_css_callback($matches)
{
    $char = $matches[0];

    return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) :
twig_ord($char));
}

/**
 * This function is adapted from code coming from Zend Framework.
 *
 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc.
(https://www.zend.com)
 * @license   https://framework.zend.com/license/new-bsd New BSD License
 */
function _twig_escape_html_attr_callback($matches)
{
    $chr = $matches[0];
    $ord = \ord($chr);

    /*
     * The following replaces characters undefined in HTML with the
     * hex entity for the Unicode replacement character.
     */
    if (($ord <= 0x1f && "\t" != $chr &&
"\n" != $chr && "\r" != $chr) || ($ord >=
0x7f && $ord <= 0x9f)) {
        return '&#xFFFD;';
    }

    /*
     * Check if the current character to escape has a name entity we should
     * replace it with while grabbing the hex value of the character.
     */
    if (1 == \strlen($chr)) {
        /*
         * While HTML supports far more named entities, the lowest common
denominator
         * has become HTML5's XML Serialisation which is restricted to
the those named
         * entities that XML supports. Using HTML entities would result in
this error:
         *     XML Parsing Error: undefined entity
         */
        static $entityMap = [
            34 => '&quot;', /* quotation mark */
            38 => '&amp;',  /* ampersand */
            60 => '&lt;',   /* less-than sign */
            62 => '&gt;',   /* greater-than sign */
        ];

        if (isset($entityMap[$ord])) {
            return $entityMap[$ord];
        }

        return sprintf('&#x%02X;', $ord);
    }

    /*
     * Per OWASP recommendations, we'll use hex entities for any other
     * characters where a named entity does not exist.
     */
    return sprintf('&#x%04X;', twig_ord($chr));
}

// add multibyte extensions if possible
if (\function_exists('mb_get_info')) {
    /**
     * Returns the length of a variable.
     *
     * @param mixed $thing A variable
     *
     * @return int The length of the value
     */
    function twig_length_filter(Environment $env, $thing)
    {
        if (null === $thing) {
            return 0;
        }

        if (is_scalar($thing)) {
            return mb_strlen($thing, $env->getCharset());
        }

        if ($thing instanceof \Countable || \is_array($thing) || $thing
instanceof \SimpleXMLElement) {
            return \count($thing);
        }

        if ($thing instanceof \Traversable) {
            return iterator_count($thing);
        }

        if (\is_object($thing) && method_exists($thing,
'__toString')) {
            return mb_strlen((string) $thing, $env->getCharset());
        }

        return 1;
    }

    /**
     * Converts a string to uppercase.
     *
     * @param string $string A string
     *
     * @return string The uppercased string
     */
    function twig_upper_filter(Environment $env, $string)
    {
        if (null !== $charset = $env->getCharset()) {
            return mb_strtoupper($string, $charset);
        }

        return strtoupper($string);
    }

    /**
     * Converts a string to lowercase.
     *
     * @param string $string A string
     *
     * @return string The lowercased string
     */
    function twig_lower_filter(Environment $env, $string)
    {
        if (null !== $charset = $env->getCharset()) {
            return mb_strtolower($string, $charset);
        }

        return strtolower($string);
    }

    /**
     * Returns a titlecased string.
     *
     * @param string $string A string
     *
     * @return string The titlecased string
     */
    function twig_title_string_filter(Environment $env, $string)
    {
        if (null !== $charset = $env->getCharset()) {
            return mb_convert_case($string, MB_CASE_TITLE, $charset);
        }

        return ucwords(strtolower($string));
    }

    /**
     * Returns a capitalized string.
     *
     * @param string $string A string
     *
     * @return string The capitalized string
     */
    function twig_capitalize_string_filter(Environment $env, $string)
    {
        if (null !== $charset = $env->getCharset()) {
            return mb_strtoupper(mb_substr($string, 0, 1, $charset),
$charset).mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset),
$charset), $charset);
        }

        return ucfirst(strtolower($string));
    }
}
// and byte fallback
else {
    /**
     * Returns the length of a variable.
     *
     * @param mixed $thing A variable
     *
     * @return int The length of the value
     */
    function twig_length_filter(Environment $env, $thing)
    {
        if (null === $thing) {
            return 0;
        }

        if (is_scalar($thing)) {
            return \strlen($thing);
        }

        if ($thing instanceof \SimpleXMLElement) {
            return \count($thing);
        }

        if (\is_object($thing) && method_exists($thing,
'__toString') && !$thing instanceof \Countable) {
            return \strlen((string) $thing);
        }

        if ($thing instanceof \Countable || \is_array($thing)) {
            return \count($thing);
        }

        if ($thing instanceof \IteratorAggregate) {
            return iterator_count($thing);
        }

        return 1;
    }

    /**
     * Returns a titlecased string.
     *
     * @param string $string A string
     *
     * @return string The titlecased string
     */
    function twig_title_string_filter(Environment $env, $string)
    {
        return ucwords(strtolower($string));
    }

    /**
     * Returns a capitalized string.
     *
     * @param string $string A string
     *
     * @return string The capitalized string
     */
    function twig_capitalize_string_filter(Environment $env, $string)
    {
        return ucfirst(strtolower($string));
    }
}

/**
 * @internal
 */
function twig_ensure_traversable($seq)
{
    if ($seq instanceof \Traversable || \is_array($seq)) {
        return $seq;
    }

    return [];
}

/**
 * @internal
 */
function twig_to_array($seq, $preserveKeys = true)
{
    if ($seq instanceof \Traversable) {
        return iterator_to_array($seq, $preserveKeys);
    }

    if (!\is_array($seq)) {
        return $seq;
    }

    return $preserveKeys ? $seq : array_values($seq);
}

/**
 * Checks if a variable is empty.
 *
 *    {# evaluates to true if the foo variable is null, false, or the empty
string #}
 *    {% if foo is empty %}
 *        {# ... #}
 *    {% endif %}
 *
 * @param mixed $value A variable
 *
 * @return bool true if the value is empty, false otherwise
 */
function twig_test_empty($value)
{
    if ($value instanceof \Countable) {
        return 0 == \count($value);
    }

    if ($value instanceof \Traversable) {
        return !iterator_count($value);
    }

    if (\is_object($value) && method_exists($value,
'__toString')) {
        return '' === (string) $value;
    }

    return '' === $value || false === $value || null === $value
|| [] === $value;
}

/**
 * Checks if a variable is traversable.
 *
 *    {# evaluates to true if the foo variable is an array or a traversable
object #}
 *    {% if foo is iterable %}
 *        {# ... #}
 *    {% endif %}
 *
 * @param mixed $value A variable
 *
 * @return bool true if the value is traversable
 */
function twig_test_iterable($value)
{
    return $value instanceof \Traversable || \is_array($value);
}

/**
 * Renders a template.
 *
 * @param array        $context
 * @param string|array $template      The template to render or an array of
templates to try consecutively
 * @param array        $variables     The variables to pass to the template
 * @param bool         $withContext
 * @param bool         $ignoreMissing Whether to ignore missing templates
or not
 * @param bool         $sandboxed     Whether to sandbox the template or
not
 *
 * @return string The rendered template
 */
function twig_include(Environment $env, $context, $template, $variables =
[], $withContext = true, $ignoreMissing = false, $sandboxed = false)
{
    $alreadySandboxed = false;
    $sandbox = null;
    if ($withContext) {
        $variables = array_merge($context, $variables);
    }

    if ($isSandboxed = $sandboxed &&
$env->hasExtension('\Twig\Extension\SandboxExtension')) {
        $sandbox =
$env->getExtension('\Twig\Extension\SandboxExtension');
        if (!$alreadySandboxed = $sandbox->isSandboxed()) {
            $sandbox->enableSandbox();
        }
    }

    $loaded = null;
    try {
        $loaded = $env->resolveTemplate($template);
    } catch (LoaderError $e) {
        if (!$ignoreMissing) {
            if ($isSandboxed && !$alreadySandboxed) {
                $sandbox->disableSandbox();
            }

            throw $e;
        }
    } catch (\Throwable $e) {
        if ($isSandboxed && !$alreadySandboxed) {
            $sandbox->disableSandbox();
        }

        throw $e;
    } catch (\Exception $e) {
        if ($isSandboxed && !$alreadySandboxed) {
            $sandbox->disableSandbox();
        }

        throw $e;
    }

    try {
        $ret = $loaded ? $loaded->render($variables) : '';
    } catch (\Exception $e) {
        if ($isSandboxed && !$alreadySandboxed) {
            $sandbox->disableSandbox();
        }

        throw $e;
    }

    if ($isSandboxed && !$alreadySandboxed) {
        $sandbox->disableSandbox();
    }

    return $ret;
}

/**
 * Returns a template content without rendering it.
 *
 * @param string $name          The template name
 * @param bool   $ignoreMissing Whether to ignore missing templates or not
 *
 * @return string The template source
 */
function twig_source(Environment $env, $name, $ignoreMissing = false)
{
    $loader = $env->getLoader();
    try {
        if (!$loader instanceof SourceContextLoaderInterface) {
            return $loader->getSource($name);
        } else {
            return $loader->getSourceContext($name)->getCode();
        }
    } catch (LoaderError $e) {
        if (!$ignoreMissing) {
            throw $e;
        }
    }
}

/**
 * Provides the ability to get constants from instances as well as
class/global constants.
 *
 * @param string      $constant The name of the constant
 * @param object|null $object   The object to get the constant from
 *
 * @return string
 */
function twig_constant($constant, $object = null)
{
    if (null !== $object) {
        $constant = \get_class($object).'::'.$constant;
    }

    return \constant($constant);
}

/**
 * Checks if a constant exists.
 *
 * @param string      $constant The name of the constant
 * @param object|null $object   The object to get the constant from
 *
 * @return bool
 */
function twig_constant_is_defined($constant, $object = null)
{
    if (null !== $object) {
        $constant = \get_class($object).'::'.$constant;
    }

    return \defined($constant);
}

/**
 * Batches item.
 *
 * @param array $items An array of items
 * @param int   $size  The size of the batch
 * @param mixed $fill  A value used to fill missing items
 *
 * @return array
 */
function twig_array_batch($items, $size, $fill = null, $preserveKeys =
true)
{
    if (!twig_test_iterable($items)) {
        throw new RuntimeError(sprintf('The "batch" filter
expects an array or "Traversable", got "%s".',
\is_object($items) ? \get_class($items) : \gettype($items)));
    }

    $size = ceil($size);

    $result = array_chunk(twig_to_array($items, $preserveKeys), $size,
$preserveKeys);

    if (null !== $fill && $result) {
        $last = \count($result) - 1;
        if ($fillCount = $size - \count($result[$last])) {
            for ($i = 0; $i < $fillCount; ++$i) {
                $result[$last][] = $fill;
            }
        }
    }

    return $result;
}

function twig_array_filter($array, $arrow)
{
    if (\is_array($array)) {
        if (\PHP_VERSION_ID >= 50600) {
            return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH);
        }

        return array_filter($array, $arrow);
    }

    // the IteratorIterator wrapping is needed as some internal PHP classes
are \Traversable but do not implement \Iterator
    return new \CallbackFilterIterator(new \IteratorIterator($array),
$arrow);
}

function twig_array_map($array, $arrow)
{
    $r = [];
    foreach ($array as $k => $v) {
        $r[$k] = $arrow($v, $k);
    }

    return $r;
}

function twig_array_reduce($array, $arrow, $initial = null)
{
    if (!\is_array($array)) {
        $array = iterator_to_array($array);
    }

    return array_reduce($array, $arrow, $initial);
}
}
vendor/twig/twig/src/Extension/DebugExtension.php000064400000003640151166614720016200
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension {
use Twig\TwigFunction;

/**
 * @final
 */
class DebugExtension extends AbstractExtension
{
    public function getFunctions()
    {
        // dump is safe if var_dump is overridden by xdebug
        $isDumpOutputHtmlSafe = \extension_loaded('xdebug')
            // false means that it was not set (and the default is on) or
it explicitly enabled
            && (false ===
ini_get('xdebug.overload_var_dump') ||
ini_get('xdebug.overload_var_dump'))
            // false means that it was not set (and the default is on) or
it explicitly enabled
            // xdebug.overload_var_dump produces HTML only when html_errors
is also enabled
            && (false === ini_get('html_errors') ||
ini_get('html_errors'))
            || 'cli' === \PHP_SAPI
        ;

        return [
            new TwigFunction('dump', 'twig_var_dump',
['is_safe' => $isDumpOutputHtmlSafe ? ['html'] : [],
'needs_context' => true, 'needs_environment' =>
true, 'is_variadic' => true]),
        ];
    }

    public function getName()
    {
        return 'debug';
    }
}

class_alias('Twig\Extension\DebugExtension',
'Twig_Extension_Debug');
}

namespace {
use Twig\Environment;
use Twig\Template;
use Twig\TemplateWrapper;

function twig_var_dump(Environment $env, $context, array $vars = [])
{
    if (!$env->isDebug()) {
        return;
    }

    ob_start();

    if (!$vars) {
        $vars = [];
        foreach ($context as $key => $value) {
            if (!$value instanceof Template && !$value instanceof
TemplateWrapper) {
                $vars[$key] = $value;
            }
        }

        var_dump($vars);
    } else {
        foreach ($vars as $var) {
            var_dump($var);
        }
    }

    return ob_get_clean();
}
}
vendor/twig/twig/src/Extension/EscaperExtension.php000064400000005731151166614720016537
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension {
use Twig\NodeVisitor\EscaperNodeVisitor;
use Twig\TokenParser\AutoEscapeTokenParser;
use Twig\TwigFilter;

/**
 * @final
 */
class EscaperExtension extends AbstractExtension
{
    protected $defaultStrategy;

    /**
     * @param string|false|callable $defaultStrategy An escaping strategy
     *
     * @see setDefaultStrategy()
     */
    public function __construct($defaultStrategy = 'html')
    {
        $this->setDefaultStrategy($defaultStrategy);
    }

    public function getTokenParsers()
    {
        return [new AutoEscapeTokenParser()];
    }

    public function getNodeVisitors()
    {
        return [new EscaperNodeVisitor()];
    }

    public function getFilters()
    {
        return [
            new TwigFilter('raw', 'twig_raw_filter',
['is_safe' => ['all']]),
        ];
    }

    /**
     * Sets the default strategy to use when not defined by the user.
     *
     * The strategy can be a valid PHP callback that takes the template
     * name as an argument and returns the strategy to use.
     *
     * @param string|false|callable $defaultStrategy An escaping strategy
     */
    public function setDefaultStrategy($defaultStrategy)
    {
        // for BC
        if (true === $defaultStrategy) {
            @trigger_error('Using "true" as the default
strategy is deprecated since version 1.21. Use "html"
instead.', E_USER_DEPRECATED);

            $defaultStrategy = 'html';
        }

        if ('filename' === $defaultStrategy) {
            @trigger_error('Using "filename" as the default
strategy is deprecated since version 1.27. Use "name"
instead.', E_USER_DEPRECATED);

            $defaultStrategy = 'name';
        }

        if ('name' === $defaultStrategy) {
            $defaultStrategy =
['\Twig\FileExtensionEscapingStrategy', 'guess'];
        }

        $this->defaultStrategy = $defaultStrategy;
    }

    /**
     * Gets the default strategy to use when not defined by the user.
     *
     * @param string $name The template name
     *
     * @return string|false The default strategy to use for the template
     */
    public function getDefaultStrategy($name)
    {
        // disable string callables to avoid calling a function named html
or js,
        // or any other upcoming escaping strategy
        if (!\is_string($this->defaultStrategy) && false !==
$this->defaultStrategy) {
            return \call_user_func($this->defaultStrategy, $name);
        }

        return $this->defaultStrategy;
    }

    public function getName()
    {
        return 'escaper';
    }
}

class_alias('Twig\Extension\EscaperExtension',
'Twig_Extension_Escaper');
}

namespace {
/**
 * Marks a variable as being safe.
 *
 * @param string $string A PHP variable
 *
 * @return string
 */
function twig_raw_filter($string)
{
    return $string;
}
}
vendor/twig/twig/src/Extension/ExtensionInterface.php000064400000005102151166614720017045
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension;

use Twig\Environment;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\TwigTest;

/**
 * Interface implemented by extension classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface ExtensionInterface
{
    /**
     * Initializes the runtime environment.
     *
     * This is where you can load some file that contains filter functions
for instance.
     *
     * @deprecated since 1.23 (to be removed in 2.0), implement
\Twig_Extension_InitRuntimeInterface instead
     */
    public function initRuntime(Environment $environment);

    /**
     * Returns the token parser instances to add to the existing list.
     *
     * @return TokenParserInterface[]
     */
    public function getTokenParsers();

    /**
     * Returns the node visitor instances to add to the existing list.
     *
     * @return NodeVisitorInterface[]
     */
    public function getNodeVisitors();

    /**
     * Returns a list of filters to add to the existing list.
     *
     * @return TwigFilter[]
     */
    public function getFilters();

    /**
     * Returns a list of tests to add to the existing list.
     *
     * @return TwigTest[]
     */
    public function getTests();

    /**
     * Returns a list of functions to add to the existing list.
     *
     * @return TwigFunction[]
     */
    public function getFunctions();

    /**
     * Returns a list of operators to add to the existing list.
     *
     * @return array<array> First array of unary operators, second
array of binary operators
     */
    public function getOperators();

    /**
     * Returns a list of global variables to add to the existing list.
     *
     * @return array An array of global variables
     *
     * @deprecated since 1.23 (to be removed in 2.0), implement
\Twig_Extension_GlobalsInterface instead
     */
    public function getGlobals();

    /**
     * Returns the name of the extension.
     *
     * @return string The extension name
     *
     * @deprecated since 1.26 (to be removed in 2.0), not used anymore
internally
     */
    public function getName();
}

class_alias('Twig\Extension\ExtensionInterface',
'Twig_ExtensionInterface');

// Ensure that the aliased name is loaded to keep BC for classes
implementing the typehint with the old aliased name.
class_exists('Twig\Environment');
vendor/twig/twig/src/Extension/GlobalsInterface.php000064400000001162151166614720016456
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension;

/**
 * Enables usage of the deprecated
Twig\Extension\AbstractExtension::getGlobals() method.
 *
 * Explicitly implement this interface if you really need to implement the
 * deprecated getGlobals() method in your extensions.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface GlobalsInterface
{
}

class_alias('Twig\Extension\GlobalsInterface',
'Twig_Extension_GlobalsInterface');
vendor/twig/twig/src/Extension/InitRuntimeInterface.php000064400000001200151166614720017333
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension;

/**
 * Enables usage of the deprecated
Twig\Extension\AbstractExtension::initRuntime() method.
 *
 * Explicitly implement this interface if you really need to implement the
 * deprecated initRuntime() method in your extensions.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface InitRuntimeInterface
{
}

class_alias('Twig\Extension\InitRuntimeInterface',
'Twig_Extension_InitRuntimeInterface');
vendor/twig/twig/src/Extension/OptimizerExtension.php000064400000001344151166614720017133
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension;

use Twig\NodeVisitor\OptimizerNodeVisitor;

/**
 * @final
 */
class OptimizerExtension extends AbstractExtension
{
    protected $optimizers;

    public function __construct($optimizers = -1)
    {
        $this->optimizers = $optimizers;
    }

    public function getNodeVisitors()
    {
        return [new OptimizerNodeVisitor($this->optimizers)];
    }

    public function getName()
    {
        return 'optimizer';
    }
}

class_alias('Twig\Extension\OptimizerExtension',
'Twig_Extension_Optimizer');
vendor/twig/twig/src/Extension/ProfilerExtension.php000064400000002137151166614720016734
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension;

use Twig\Profiler\NodeVisitor\ProfilerNodeVisitor;
use Twig\Profiler\Profile;

class ProfilerExtension extends AbstractExtension
{
    private $actives = [];

    public function __construct(Profile $profile)
    {
        $this->actives[] = $profile;
    }

    public function enter(Profile $profile)
    {
        $this->actives[0]->addProfile($profile);
        array_unshift($this->actives, $profile);
    }

    public function leave(Profile $profile)
    {
        $profile->leave();
        array_shift($this->actives);

        if (1 === \count($this->actives)) {
            $this->actives[0]->leave();
        }
    }

    public function getNodeVisitors()
    {
        return [new ProfilerNodeVisitor(\get_class($this))];
    }

    public function getName()
    {
        return 'profiler';
    }
}

class_alias('Twig\Extension\ProfilerExtension',
'Twig_Extension_Profiler');
vendor/twig/twig/src/Extension/RuntimeExtensionInterface.php000064400000000506151166614720020414
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension;

/**
 * @author Grégoire Pineau <lyrixx@lyrixx.info>
 */
interface RuntimeExtensionInterface
{
}
vendor/twig/twig/src/Extension/SandboxExtension.php000064400000004524151166614720016552
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension;

use Twig\NodeVisitor\SandboxNodeVisitor;
use Twig\Sandbox\SecurityPolicyInterface;
use Twig\TokenParser\SandboxTokenParser;

/**
 * @final
 */
class SandboxExtension extends AbstractExtension
{
    protected $sandboxedGlobally;
    protected $sandboxed;
    protected $policy;

    public function __construct(SecurityPolicyInterface $policy, $sandboxed
= false)
    {
        $this->policy = $policy;
        $this->sandboxedGlobally = $sandboxed;
    }

    public function getTokenParsers()
    {
        return [new SandboxTokenParser()];
    }

    public function getNodeVisitors()
    {
        return [new SandboxNodeVisitor()];
    }

    public function enableSandbox()
    {
        $this->sandboxed = true;
    }

    public function disableSandbox()
    {
        $this->sandboxed = false;
    }

    public function isSandboxed()
    {
        return $this->sandboxedGlobally || $this->sandboxed;
    }

    public function isSandboxedGlobally()
    {
        return $this->sandboxedGlobally;
    }

    public function setSecurityPolicy(SecurityPolicyInterface $policy)
    {
        $this->policy = $policy;
    }

    public function getSecurityPolicy()
    {
        return $this->policy;
    }

    public function checkSecurity($tags, $filters, $functions)
    {
        if ($this->isSandboxed()) {
            $this->policy->checkSecurity($tags, $filters,
$functions);
        }
    }

    public function checkMethodAllowed($obj, $method)
    {
        if ($this->isSandboxed()) {
            $this->policy->checkMethodAllowed($obj, $method);
        }
    }

    public function checkPropertyAllowed($obj, $method)
    {
        if ($this->isSandboxed()) {
            $this->policy->checkPropertyAllowed($obj, $method);
        }
    }

    public function ensureToStringAllowed($obj)
    {
        if ($this->isSandboxed() && \is_object($obj) &&
method_exists($obj, '__toString')) {
            $this->policy->checkMethodAllowed($obj,
'__toString');
        }

        return $obj;
    }

    public function getName()
    {
        return 'sandbox';
    }
}

class_alias('Twig\Extension\SandboxExtension',
'Twig_Extension_Sandbox');
vendor/twig/twig/src/Extension/StagingExtension.php000064400000005702151166614720016547
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension;

use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;

/**
 * Internal class.
 *
 * This class is used by \Twig\Environment as a staging area and must not
be used directly.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @internal
 */
class StagingExtension extends AbstractExtension
{
    protected $functions = [];
    protected $filters = [];
    protected $visitors = [];
    protected $tokenParsers = [];
    protected $globals = [];
    protected $tests = [];

    public function addFunction($name, $function)
    {
        if (isset($this->functions[$name])) {
            @trigger_error(sprintf('Overriding function "%s"
that is already registered is deprecated since version 1.30 and won\'t
be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
        }

        $this->functions[$name] = $function;
    }

    public function getFunctions()
    {
        return $this->functions;
    }

    public function addFilter($name, $filter)
    {
        if (isset($this->filters[$name])) {
            @trigger_error(sprintf('Overriding filter "%s"
that is already registered is deprecated since version 1.30 and won\'t
be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
        }

        $this->filters[$name] = $filter;
    }

    public function getFilters()
    {
        return $this->filters;
    }

    public function addNodeVisitor(NodeVisitorInterface $visitor)
    {
        $this->visitors[] = $visitor;
    }

    public function getNodeVisitors()
    {
        return $this->visitors;
    }

    public function addTokenParser(TokenParserInterface $parser)
    {
        if (isset($this->tokenParsers[$parser->getTag()])) {
            @trigger_error(sprintf('Overriding tag "%s" that
is already registered is deprecated since version 1.30 and won\'t be
possible anymore in 2.0.', $parser->getTag()), E_USER_DEPRECATED);
        }

        $this->tokenParsers[$parser->getTag()] = $parser;
    }

    public function getTokenParsers()
    {
        return $this->tokenParsers;
    }

    public function addGlobal($name, $value)
    {
        $this->globals[$name] = $value;
    }

    public function getGlobals()
    {
        return $this->globals;
    }

    public function addTest($name, $test)
    {
        if (isset($this->tests[$name])) {
            @trigger_error(sprintf('Overriding test "%s"
that is already registered is deprecated since version 1.30 and won\'t
be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
        }

        $this->tests[$name] = $test;
    }

    public function getTests()
    {
        return $this->tests;
    }

    public function getName()
    {
        return 'staging';
    }
}

class_alias('Twig\Extension\StagingExtension',
'Twig_Extension_Staging');
vendor/twig/twig/src/Extension/StringLoaderExtension.php000064400000002263151166614720017547
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Extension {
use Twig\TwigFunction;

/**
 * @final
 */
class StringLoaderExtension extends AbstractExtension
{
    public function getFunctions()
    {
        return [
            new TwigFunction('template_from_string',
'twig_template_from_string', ['needs_environment' =>
true]),
        ];
    }

    public function getName()
    {
        return 'string_loader';
    }
}

class_alias('Twig\Extension\StringLoaderExtension',
'Twig_Extension_StringLoader');
}

namespace {
use Twig\Environment;
use Twig\TemplateWrapper;

/**
 * Loads a template from a string.
 *
 *     {{ include(template_from_string("Hello {{ name }}")) }}
 *
 * @param string $template A template as a string or object implementing
__toString()
 * @param string $name     An optional name of the template to be used in
error messages
 *
 * @return TemplateWrapper
 */
function twig_template_from_string(Environment $env, $template, $name =
null)
{
    return $env->createTemplate((string) $template, $name);
}
}
vendor/twig/twig/src/FileExtensionEscapingStrategy.php000064400000002761151166614720017255
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

/**
 * Default autoescaping strategy based on file names.
 *
 * This strategy sets the HTML as the default autoescaping strategy,
 * but changes it based on the template name.
 *
 * Note that there is no runtime performance impact as the
 * default autoescaping strategy is set at compilation time.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class FileExtensionEscapingStrategy
{
    /**
     * Guesses the best autoescaping strategy based on the file name.
     *
     * @param string $name The template name
     *
     * @return string|false The escaping strategy name to use or false to
disable
     */
    public static function guess($name)
    {
        if (\in_array(substr($name, -1), ['/', '\\']))
{
            return 'html'; // return html for directories
        }

        if ('.twig' === substr($name, -5)) {
            $name = substr($name, 0, -5);
        }

        $extension = pathinfo($name, PATHINFO_EXTENSION);

        switch ($extension) {
            case 'js':
                return 'js';

            case 'css':
                return 'css';

            case 'txt':
                return false;

            default:
                return 'html';
        }
    }
}

class_alias('Twig\FileExtensionEscapingStrategy',
'Twig_FileExtensionEscapingStrategy');
vendor/twig/twig/src/Lexer.php000064400000050144151166614730012362
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

use Twig\Error\SyntaxError;

/**
 * Lexes a template string.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Lexer implements \Twig_LexerInterface
{
    protected $tokens;
    protected $code;
    protected $cursor;
    protected $lineno;
    protected $end;
    protected $state;
    protected $states;
    protected $brackets;
    protected $env;
    // to be renamed to $name in 2.0 (where it is private)
    protected $filename;
    protected $options;
    protected $regexes;
    protected $position;
    protected $positions;
    protected $currentVarBlockLine;

    private $source;

    const STATE_DATA = 0;
    const STATE_BLOCK = 1;
    const STATE_VAR = 2;
    const STATE_STRING = 3;
    const STATE_INTERPOLATION = 4;

    const REGEX_NAME =
'/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
    const REGEX_NUMBER =
'/[0-9]+(?:\.[0-9]+)?([Ee][\+\-][0-9]+)?/A';
    const REGEX_STRING =
'/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
    const REGEX_DQ_STRING_DELIM = '/"/A';
    const REGEX_DQ_STRING_PART =
'/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
    const PUNCTUATION = '()[]{}?:.,|';

    public function __construct(Environment $env, array $options = [])
    {
        $this->env = $env;

        $this->options = array_merge([
            'tag_comment' => ['{#', '#}'],
            'tag_block' => ['{%', '%}'],
            'tag_variable' => ['{{',
'}}'],
            'whitespace_trim' => '-',
            'whitespace_line_trim' => '~',
            'whitespace_line_chars' => ' \t\0\x0B',
            'interpolation' => ['#{',
'}'],
        ], $options);

        // when PHP 7.3 is the min version, we will be able to remove the
'#' part in preg_quote as it's part of the default
        $this->regexes = [
            // }}
            'lex_var' => '{
                \s*
                (?:'.
                   
preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1],
'#').'\s*'. // -}}\s*
                    '|'.
                   
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_variable'][1],
'#').'['.$this->options['whitespace_line_chars'].']*'.
// ~}}[ \t\0\x0B]*
                    '|'.
                   
preg_quote($this->options['tag_variable'][1], '#').
// }}
                ')
            }Ax',

            // %}
            'lex_block' => '{
                \s*
                (?:'.
                   
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1],
'#').'\s*\n?'. // -%}\s*\n?
                    '|'.
                   
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1],
'#').'['.$this->options['whitespace_line_chars'].']*'.
// ~%}[ \t\0\x0B]*
                    '|'.
                    preg_quote($this->options['tag_block'][1],
'#').'\n?'. // %}\n?
                ')
            }Ax',

            // {% endverbatim %}
            'lex_raw_data' => '{'.
                preg_quote($this->options['tag_block'][0],
'#'). // {%
                '('.
                    $this->options['whitespace_trim']. // -
                    '|'.
                    $this->options['whitespace_line_trim']. //
~
                ')?\s*'.
                '(?:end%s)'. // endraw or endverbatim
                '\s*'.
                '(?:'.
                   
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1],
'#').'\s*'. // -%}
                    '|'.
                   
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1],
'#').'['.$this->options['whitespace_line_chars'].']*'.
// ~%}[ \t\0\x0B]*
                    '|'.
                    preg_quote($this->options['tag_block'][1],
'#'). // %}
                ')
            }sx',

            'operator' => $this->getOperatorRegex(),

            // #}
            'lex_comment' => '{
                (?:'.
                   
preg_quote($this->options['whitespace_trim']).preg_quote($this->options['tag_comment'][1],
'#').'\s*\n?'. // -#}\s*\n?
                    '|'.
                   
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_comment'][1],
'#').'['.$this->options['whitespace_line_chars'].']*'.
// ~#}[ \t\0\x0B]*
                    '|'.
                   
preg_quote($this->options['tag_comment'][1],
'#').'\n?'. // #}\n?
                ')
            }sx',

            // verbatim %}
            'lex_block_raw' => '{
                \s*
                (raw|verbatim)
                \s*
                (?:'.
                   
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1],
'#').'\s*'. // -%}\s*
                    '|'.
                   
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1],
'#').'['.$this->options['whitespace_line_chars'].']*'.
// ~%}[ \t\0\x0B]*
                    '|'.
                    preg_quote($this->options['tag_block'][1],
'#'). // %}
                ')
            }Asx',

            'lex_block_line' =>
'{\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1],
'#').'}As',

            // {{ or {% or {#
            'lex_tokens_start' => '{
                ('.
                   
preg_quote($this->options['tag_variable'][0], '#').
// {{
                    '|'.
                    preg_quote($this->options['tag_block'][0],
'#'). // {%
                    '|'.
                   
preg_quote($this->options['tag_comment'][0], '#').
// {#
                ')('.
                   
preg_quote($this->options['whitespace_trim'], '#').
// -
                    '|'.
                   
preg_quote($this->options['whitespace_line_trim'],
'#'). // ~
                ')?
            }sx',
            'interpolation_start' =>
'{'.preg_quote($this->options['interpolation'][0],
'#').'\s*}A',
            'interpolation_end' =>
'{\s*'.preg_quote($this->options['interpolation'][1],
'#').'}A',
        ];
    }

    public function tokenize($code, $name = null)
    {
        if (!$code instanceof Source) {
            @trigger_error(sprintf('Passing a string as the $code
argument of %s() is deprecated since version 1.27 and will be removed in
2.0. Pass a \Twig\Source instance instead.', __METHOD__),
E_USER_DEPRECATED);
            $this->source = new Source($code, $name);
        } else {
            $this->source = $code;
        }

        if (((int) ini_get('mbstring.func_overload')) & 2) {
            @trigger_error('Support for having
"mbstring.func_overload" different from 0 is deprecated version
1.29 and will be removed in 2.0.', E_USER_DEPRECATED);
        }

        if (\function_exists('mb_internal_encoding') &&
((int) ini_get('mbstring.func_overload')) & 2) {
            $mbEncoding = mb_internal_encoding();
            mb_internal_encoding('ASCII');
        } else {
            $mbEncoding = null;
        }

        $this->code = str_replace(["\r\n", "\r"],
"\n", $this->source->getCode());
        $this->filename = $this->source->getName();
        $this->cursor = 0;
        $this->lineno = 1;
        $this->end = \strlen($this->code);
        $this->tokens = [];
        $this->state = self::STATE_DATA;
        $this->states = [];
        $this->brackets = [];
        $this->position = -1;

        // find all token starts in one go
        preg_match_all($this->regexes['lex_tokens_start'],
$this->code, $matches, PREG_OFFSET_CAPTURE);
        $this->positions = $matches;

        while ($this->cursor < $this->end) {
            // dispatch to the lexing functions depending
            // on the current state
            switch ($this->state) {
                case self::STATE_DATA:
                    $this->lexData();
                    break;

                case self::STATE_BLOCK:
                    $this->lexBlock();
                    break;

                case self::STATE_VAR:
                    $this->lexVar();
                    break;

                case self::STATE_STRING:
                    $this->lexString();
                    break;

                case self::STATE_INTERPOLATION:
                    $this->lexInterpolation();
                    break;
            }
        }

        $this->pushToken(Token::EOF_TYPE);

        if (!empty($this->brackets)) {
            list($expect, $lineno) = array_pop($this->brackets);
            throw new SyntaxError(sprintf('Unclosed
"%s".', $expect), $lineno, $this->source);
        }

        if ($mbEncoding) {
            mb_internal_encoding($mbEncoding);
        }

        return new TokenStream($this->tokens, $this->source);
    }

    protected function lexData()
    {
        // if no matches are left we return the rest of the template as
simple text token
        if ($this->position == \count($this->positions[0]) - 1) {
            $this->pushToken(Token::TEXT_TYPE, substr($this->code,
$this->cursor));
            $this->cursor = $this->end;

            return;
        }

        // Find the first token after the current cursor
        $position = $this->positions[0][++$this->position];
        while ($position[1] < $this->cursor) {
            if ($this->position == \count($this->positions[0]) - 1) {
                return;
            }
            $position = $this->positions[0][++$this->position];
        }

        // push the template text first
        $text = $textContent = substr($this->code, $this->cursor,
$position[1] - $this->cursor);

        // trim?
        if (isset($this->positions[2][$this->position][0])) {
            if ($this->options['whitespace_trim'] ===
$this->positions[2][$this->position][0]) {
                // whitespace_trim detected ({%-, {{- or {#-)
                $text = rtrim($text);
            } elseif ($this->options['whitespace_line_trim']
=== $this->positions[2][$this->position][0]) {
                // whitespace_line_trim detected ({%~, {{~ or {#~)
                // don't trim \r and \n
                $text = rtrim($text, " \t\0\x0B");
            }
        }
        $this->pushToken(Token::TEXT_TYPE, $text);
        $this->moveCursor($textContent.$position[0]);

        switch ($this->positions[1][$this->position][0]) {
            case $this->options['tag_comment'][0]:
                $this->lexComment();
                break;

            case $this->options['tag_block'][0]:
                // raw data?
                if
(preg_match($this->regexes['lex_block_raw'], $this->code,
$match, 0, $this->cursor)) {
                    $this->moveCursor($match[0]);
                    $this->lexRawData($match[1]);
                // {% line \d+ %}
                } elseif
(preg_match($this->regexes['lex_block_line'], $this->code,
$match, 0, $this->cursor)) {
                    $this->moveCursor($match[0]);
                    $this->lineno = (int) $match[1];
                } else {
                    $this->pushToken(Token::BLOCK_START_TYPE);
                    $this->pushState(self::STATE_BLOCK);
                    $this->currentVarBlockLine = $this->lineno;
                }
                break;

            case $this->options['tag_variable'][0]:
                $this->pushToken(Token::VAR_START_TYPE);
                $this->pushState(self::STATE_VAR);
                $this->currentVarBlockLine = $this->lineno;
                break;
        }
    }

    protected function lexBlock()
    {
        if (empty($this->brackets) &&
preg_match($this->regexes['lex_block'], $this->code,
$match, 0, $this->cursor)) {
            $this->pushToken(Token::BLOCK_END_TYPE);
            $this->moveCursor($match[0]);
            $this->popState();
        } else {
            $this->lexExpression();
        }
    }

    protected function lexVar()
    {
        if (empty($this->brackets) &&
preg_match($this->regexes['lex_var'], $this->code, $match,
0, $this->cursor)) {
            $this->pushToken(Token::VAR_END_TYPE);
            $this->moveCursor($match[0]);
            $this->popState();
        } else {
            $this->lexExpression();
        }
    }

    protected function lexExpression()
    {
        // whitespace
        if (preg_match('/\s+/A', $this->code, $match, 0,
$this->cursor)) {
            $this->moveCursor($match[0]);

            if ($this->cursor >= $this->end) {
                throw new SyntaxError(sprintf('Unclosed
"%s".', self::STATE_BLOCK === $this->state ?
'block' : 'variable'), $this->currentVarBlockLine,
$this->source);
            }
        }

        // arrow function
        if ('=' === $this->code[$this->cursor] &&
'>' === $this->code[$this->cursor + 1]) {
            $this->pushToken(Token::ARROW_TYPE, '=>');
            $this->moveCursor('=>');
        }
        // operators
        elseif (preg_match($this->regexes['operator'],
$this->code, $match, 0, $this->cursor)) {
            $this->pushToken(Token::OPERATOR_TYPE,
preg_replace('/\s+/', ' ', $match[0]));
            $this->moveCursor($match[0]);
        }
        // names
        elseif (preg_match(self::REGEX_NAME, $this->code, $match, 0,
$this->cursor)) {
            $this->pushToken(Token::NAME_TYPE, $match[0]);
            $this->moveCursor($match[0]);
        }
        // numbers
        elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, 0,
$this->cursor)) {
            $number = (float) $match[0];  // floats
            if (ctype_digit($match[0]) && $number <=
PHP_INT_MAX) {
                $number = (int) $match[0]; // integers lower than the
maximum
            }
            $this->pushToken(Token::NUMBER_TYPE, $number);
            $this->moveCursor($match[0]);
        }
        // punctuation
        elseif (false !== strpos(self::PUNCTUATION,
$this->code[$this->cursor])) {
            // opening bracket
            if (false !== strpos('([{',
$this->code[$this->cursor])) {
                $this->brackets[] = [$this->code[$this->cursor],
$this->lineno];
            }
            // closing bracket
            elseif (false !== strpos(')]}',
$this->code[$this->cursor])) {
                if (empty($this->brackets)) {
                    throw new SyntaxError(sprintf('Unexpected
"%s".', $this->code[$this->cursor]), $this->lineno,
$this->source);
                }

                list($expect, $lineno) = array_pop($this->brackets);
                if ($this->code[$this->cursor] != strtr($expect,
'([{', ')]}')) {
                    throw new SyntaxError(sprintf('Unclosed
"%s".', $expect), $lineno, $this->source);
                }
            }

            $this->pushToken(Token::PUNCTUATION_TYPE,
$this->code[$this->cursor]);
            ++$this->cursor;
        }
        // strings
        elseif (preg_match(self::REGEX_STRING, $this->code, $match, 0,
$this->cursor)) {
            $this->pushToken(Token::STRING_TYPE,
stripcslashes(substr($match[0], 1, -1)));
            $this->moveCursor($match[0]);
        }
        // opening double quoted string
        elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code,
$match, 0, $this->cursor)) {
            $this->brackets[] = ['"', $this->lineno];
            $this->pushState(self::STATE_STRING);
            $this->moveCursor($match[0]);
        }
        // unlexable
        else {
            throw new SyntaxError(sprintf('Unexpected character
"%s".', $this->code[$this->cursor]), $this->lineno,
$this->source);
        }
    }

    protected function lexRawData($tag)
    {
        if ('raw' === $tag) {
            @trigger_error(sprintf('Twig Tag "raw" is
deprecated since version 1.21. Use "verbatim" instead in %s at
line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED);
        }

        if (!preg_match(str_replace('%s', $tag,
$this->regexes['lex_raw_data']), $this->code, $match,
PREG_OFFSET_CAPTURE, $this->cursor)) {
            throw new SyntaxError(sprintf('Unexpected end of file:
Unclosed "%s" block.', $tag), $this->lineno,
$this->source);
        }

        $text = substr($this->code, $this->cursor, $match[0][1] -
$this->cursor);
        $this->moveCursor($text.$match[0][0]);

        // trim?
        if (isset($match[1][0])) {
            if ($this->options['whitespace_trim'] ===
$match[1][0]) {
                // whitespace_trim detected ({%-, {{- or {#-)
                $text = rtrim($text);
            } else {
                // whitespace_line_trim detected ({%~, {{~ or {#~)
                // don't trim \r and \n
                $text = rtrim($text, " \t\0\x0B");
            }
        }

        $this->pushToken(Token::TEXT_TYPE, $text);
    }

    protected function lexComment()
    {
        if (!preg_match($this->regexes['lex_comment'],
$this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
            throw new SyntaxError('Unclosed comment.',
$this->lineno, $this->source);
        }

        $this->moveCursor(substr($this->code, $this->cursor,
$match[0][1] - $this->cursor).$match[0][0]);
    }

    protected function lexString()
    {
        if (preg_match($this->regexes['interpolation_start'],
$this->code, $match, 0, $this->cursor)) {
            $this->brackets[] =
[$this->options['interpolation'][0], $this->lineno];
            $this->pushToken(Token::INTERPOLATION_START_TYPE);
            $this->moveCursor($match[0]);
            $this->pushState(self::STATE_INTERPOLATION);
        } elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code,
$match, 0, $this->cursor) && \strlen($match[0]) > 0) {
            $this->pushToken(Token::STRING_TYPE,
stripcslashes($match[0]));
            $this->moveCursor($match[0]);
        } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code,
$match, 0, $this->cursor)) {
            list($expect, $lineno) = array_pop($this->brackets);
            if ('"' != $this->code[$this->cursor]) {
                throw new SyntaxError(sprintf('Unclosed
"%s".', $expect), $lineno, $this->source);
            }

            $this->popState();
            ++$this->cursor;
        } else {
            // unlexable
            throw new SyntaxError(sprintf('Unexpected character
"%s".', $this->code[$this->cursor]), $this->lineno,
$this->source);
        }
    }

    protected function lexInterpolation()
    {
        $bracket = end($this->brackets);
        if ($this->options['interpolation'][0] === $bracket[0]
&& preg_match($this->regexes['interpolation_end'],
$this->code, $match, 0, $this->cursor)) {
            array_pop($this->brackets);
            $this->pushToken(Token::INTERPOLATION_END_TYPE);
            $this->moveCursor($match[0]);
            $this->popState();
        } else {
            $this->lexExpression();
        }
    }

    protected function pushToken($type, $value = '')
    {
        // do not push empty text tokens
        if (Token::TEXT_TYPE === $type && '' === $value)
{
            return;
        }

        $this->tokens[] = new Token($type, $value, $this->lineno);
    }

    protected function moveCursor($text)
    {
        $this->cursor += \strlen($text);
        $this->lineno += substr_count($text, "\n");
    }

    protected function getOperatorRegex()
    {
        $operators = array_merge(
            ['='],
            array_keys($this->env->getUnaryOperators()),
            array_keys($this->env->getBinaryOperators())
        );

        $operators = array_combine($operators,
array_map('strlen', $operators));
        arsort($operators);

        $regex = [];
        foreach ($operators as $operator => $length) {
            // an operator that ends with a character must be followed by
            // a whitespace or a parenthesis
            if (ctype_alpha($operator[$length - 1])) {
                $r = preg_quote($operator,
'/').'(?=[\s()])';
            } else {
                $r = preg_quote($operator, '/');
            }

            // an operator with a space can be any amount of whitespaces
            $r = preg_replace('/\s+/', '\s+', $r);

            $regex[] = $r;
        }

        return '/'.implode('|', $regex).'/A';
    }

    protected function pushState($state)
    {
        $this->states[] = $this->state;
        $this->state = $state;
    }

    protected function popState()
    {
        if (0 === \count($this->states)) {
            throw new \LogicException('Cannot pop state without a
previous state.');
        }

        $this->state = array_pop($this->states);
    }
}

class_alias('Twig\Lexer', 'Twig_Lexer');
vendor/twig/twig/src/Loader/ArrayLoader.php000064400000005373151166614730014722
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Loader;

use Twig\Error\LoaderError;
use Twig\Source;

/**
 * Loads a template from an array.
 *
 * When using this loader with a cache mechanism, you should know that a
new cache
 * key is generated each time a template content "changes" (the
cache key being the
 * source code of the template). If you don't want to see your cache
grows out of
 * control, you need to take care of clearing the old cache file by
yourself.
 *
 * This loader should only be used for unit testing.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ArrayLoader implements LoaderInterface, ExistsLoaderInterface,
SourceContextLoaderInterface
{
    protected $templates = [];

    /**
     * @param array $templates An array of templates (keys are the names,
and values are the source code)
     */
    public function __construct(array $templates = [])
    {
        $this->templates = $templates;
    }

    /**
     * Adds or overrides a template.
     *
     * @param string $name     The template name
     * @param string $template The template source
     */
    public function setTemplate($name, $template)
    {
        $this->templates[(string) $name] = $template;
    }

    public function getSource($name)
    {
        @trigger_error(sprintf('Calling "getSource" on
"%s" is deprecated since 1.27. Use getSourceContext()
instead.', \get_class($this)), E_USER_DEPRECATED);

        $name = (string) $name;
        if (!isset($this->templates[$name])) {
            throw new LoaderError(sprintf('Template "%s" is
not defined.', $name));
        }

        return $this->templates[$name];
    }

    public function getSourceContext($name)
    {
        $name = (string) $name;
        if (!isset($this->templates[$name])) {
            throw new LoaderError(sprintf('Template "%s" is
not defined.', $name));
        }

        return new Source($this->templates[$name], $name);
    }

    public function exists($name)
    {
        return isset($this->templates[(string) $name]);
    }

    public function getCacheKey($name)
    {
        $name = (string) $name;
        if (!isset($this->templates[$name])) {
            throw new LoaderError(sprintf('Template "%s" is
not defined.', $name));
        }

        return $name.':'.$this->templates[$name];
    }

    public function isFresh($name, $time)
    {
        $name = (string) $name;
        if (!isset($this->templates[$name])) {
            throw new LoaderError(sprintf('Template "%s" is
not defined.', $name));
        }

        return true;
    }
}

class_alias('Twig\Loader\ArrayLoader',
'Twig_Loader_Array');
vendor/twig/twig/src/Loader/ChainLoader.php000064400000011051151166614740014655
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Loader;

use Twig\Error\LoaderError;
use Twig\Source;

/**
 * Loads templates from other loaders.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ChainLoader implements LoaderInterface, ExistsLoaderInterface,
SourceContextLoaderInterface
{
    private $hasSourceCache = [];
    protected $loaders = [];

    /**
     * @param LoaderInterface[] $loaders
     */
    public function __construct(array $loaders = [])
    {
        foreach ($loaders as $loader) {
            $this->addLoader($loader);
        }
    }

    public function addLoader(LoaderInterface $loader)
    {
        $this->loaders[] = $loader;
        $this->hasSourceCache = [];
    }

    /**
     * @return LoaderInterface[]
     */
    public function getLoaders()
    {
        return $this->loaders;
    }

    public function getSource($name)
    {
        @trigger_error(sprintf('Calling "getSource" on
"%s" is deprecated since 1.27. Use getSourceContext()
instead.', \get_class($this)), E_USER_DEPRECATED);

        $exceptions = [];
        foreach ($this->loaders as $loader) {
            if ($loader instanceof ExistsLoaderInterface &&
!$loader->exists($name)) {
                continue;
            }

            try {
                return $loader->getSource($name);
            } catch (LoaderError $e) {
                $exceptions[] = $e->getMessage();
            }
        }

        throw new LoaderError(sprintf('Template "%s" is not
defined%s.', $name, $exceptions ? ' ('.implode(',
', $exceptions).')' : ''));
    }

    public function getSourceContext($name)
    {
        $exceptions = [];
        foreach ($this->loaders as $loader) {
            if ($loader instanceof ExistsLoaderInterface &&
!$loader->exists($name)) {
                continue;
            }

            try {
                if ($loader instanceof SourceContextLoaderInterface) {
                    return $loader->getSourceContext($name);
                }

                return new Source($loader->getSource($name), $name);
            } catch (LoaderError $e) {
                $exceptions[] = $e->getMessage();
            }
        }

        throw new LoaderError(sprintf('Template "%s" is not
defined%s.', $name, $exceptions ? ' ('.implode(',
', $exceptions).')' : ''));
    }

    public function exists($name)
    {
        $name = (string) $name;

        if (isset($this->hasSourceCache[$name])) {
            return $this->hasSourceCache[$name];
        }

        foreach ($this->loaders as $loader) {
            if ($loader instanceof ExistsLoaderInterface) {
                if ($loader->exists($name)) {
                    return $this->hasSourceCache[$name] = true;
                }

                continue;
            }

            try {
                if ($loader instanceof SourceContextLoaderInterface) {
                    $loader->getSourceContext($name);
                } else {
                    $loader->getSource($name);
                }

                return $this->hasSourceCache[$name] = true;
            } catch (LoaderError $e) {
            }
        }

        return $this->hasSourceCache[$name] = false;
    }

    public function getCacheKey($name)
    {
        $exceptions = [];
        foreach ($this->loaders as $loader) {
            if ($loader instanceof ExistsLoaderInterface &&
!$loader->exists($name)) {
                continue;
            }

            try {
                return $loader->getCacheKey($name);
            } catch (LoaderError $e) {
                $exceptions[] = \get_class($loader).':
'.$e->getMessage();
            }
        }

        throw new LoaderError(sprintf('Template "%s" is not
defined%s.', $name, $exceptions ? ' ('.implode(',
', $exceptions).')' : ''));
    }

    public function isFresh($name, $time)
    {
        $exceptions = [];
        foreach ($this->loaders as $loader) {
            if ($loader instanceof ExistsLoaderInterface &&
!$loader->exists($name)) {
                continue;
            }

            try {
                return $loader->isFresh($name, $time);
            } catch (LoaderError $e) {
                $exceptions[] = \get_class($loader).':
'.$e->getMessage();
            }
        }

        throw new LoaderError(sprintf('Template "%s" is not
defined%s.', $name, $exceptions ? ' ('.implode(',
', $exceptions).')' : ''));
    }
}

class_alias('Twig\Loader\ChainLoader',
'Twig_Loader_Chain');
vendor/twig/twig/src/Loader/ExistsLoaderInterface.php000064400000001423151166614740016735
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Loader;

/**
 * Adds an exists() method for loaders.
 *
 * @author Florin Patan <florinpatan@gmail.com>
 *
 * @deprecated since 1.12 (to be removed in 3.0)
 */
interface ExistsLoaderInterface
{
    /**
     * Check if we have the source code of a template, given its name.
     *
     * @param string $name The name of the template to check if we can load
     *
     * @return bool If the template source code is handled by this loader
or not
     */
    public function exists($name);
}

class_alias('Twig\Loader\ExistsLoaderInterface',
'Twig_ExistsLoaderInterface');
vendor/twig/twig/src/Loader/FilesystemLoader.php000064400000022263151166614740015766
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Loader;

use Twig\Error\LoaderError;
use Twig\Source;

/**
 * Loads template from the filesystem.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface,
SourceContextLoaderInterface
{
    /** Identifier of the main namespace. */
    const MAIN_NAMESPACE = '__main__';

    protected $paths = [];
    protected $cache = [];
    protected $errorCache = [];

    private $rootPath;

    /**
     * @param string|array $paths    A path or an array of paths where to
look for templates
     * @param string|null  $rootPath The root path common to all relative
paths (null for getcwd())
     */
    public function __construct($paths = [], $rootPath = null)
    {
        $this->rootPath = (null === $rootPath ? getcwd() :
$rootPath).\DIRECTORY_SEPARATOR;
        if (false !== $realPath = realpath($rootPath)) {
            $this->rootPath = $realPath.\DIRECTORY_SEPARATOR;
        }

        if ($paths) {
            $this->setPaths($paths);
        }
    }

    /**
     * Returns the paths to the templates.
     *
     * @param string $namespace A path namespace
     *
     * @return array The array of paths where to look for templates
     */
    public function getPaths($namespace = self::MAIN_NAMESPACE)
    {
        return isset($this->paths[$namespace]) ?
$this->paths[$namespace] : [];
    }

    /**
     * Returns the path namespaces.
     *
     * The main namespace is always defined.
     *
     * @return array The array of defined namespaces
     */
    public function getNamespaces()
    {
        return array_keys($this->paths);
    }

    /**
     * Sets the paths where templates are stored.
     *
     * @param string|array $paths     A path or an array of paths where to
look for templates
     * @param string       $namespace A path namespace
     */
    public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
    {
        if (!\is_array($paths)) {
            $paths = [$paths];
        }

        $this->paths[$namespace] = [];
        foreach ($paths as $path) {
            $this->addPath($path, $namespace);
        }
    }

    /**
     * Adds a path where templates are stored.
     *
     * @param string $path      A path where to look for templates
     * @param string $namespace A path namespace
     *
     * @throws LoaderError
     */
    public function addPath($path, $namespace = self::MAIN_NAMESPACE)
    {
        // invalidate the cache
        $this->cache = $this->errorCache = [];

        $checkPath = $this->isAbsolutePath($path) ? $path :
$this->rootPath.$path;
        if (!is_dir($checkPath)) {
            throw new LoaderError(sprintf('The "%s"
directory does not exist ("%s").', $path, $checkPath));
        }

        $this->paths[$namespace][] = rtrim($path, '/\\');
    }

    /**
     * Prepends a path where templates are stored.
     *
     * @param string $path      A path where to look for templates
     * @param string $namespace A path namespace
     *
     * @throws LoaderError
     */
    public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
    {
        // invalidate the cache
        $this->cache = $this->errorCache = [];

        $checkPath = $this->isAbsolutePath($path) ? $path :
$this->rootPath.$path;
        if (!is_dir($checkPath)) {
            throw new LoaderError(sprintf('The "%s"
directory does not exist ("%s").', $path, $checkPath));
        }

        $path = rtrim($path, '/\\');

        if (!isset($this->paths[$namespace])) {
            $this->paths[$namespace][] = $path;
        } else {
            array_unshift($this->paths[$namespace], $path);
        }
    }

    public function getSource($name)
    {
        @trigger_error(sprintf('Calling "getSource" on
"%s" is deprecated since 1.27. Use getSourceContext()
instead.', \get_class($this)), E_USER_DEPRECATED);

        if (null === ($path = $this->findTemplate($name)) || false ===
$path) {
            return '';
        }

        return file_get_contents($path);
    }

    public function getSourceContext($name)
    {
        if (null === ($path = $this->findTemplate($name)) || false ===
$path) {
            return new Source('', $name, '');
        }

        return new Source(file_get_contents($path), $name, $path);
    }

    public function getCacheKey($name)
    {
        if (null === ($path = $this->findTemplate($name)) || false ===
$path) {
            return '';
        }
        $len = \strlen($this->rootPath);
        if (0 === strncmp($this->rootPath, $path, $len)) {
            return substr($path, $len);
        }

        return $path;
    }

    public function exists($name)
    {
        $name = $this->normalizeName($name);

        if (isset($this->cache[$name])) {
            return true;
        }

        try {
            return null !== ($path = $this->findTemplate($name, false))
&& false !== $path;
        } catch (LoaderError $e) {
            @trigger_error(sprintf('In %s::findTemplate(), you must
accept a second argument that when set to "false" returns
"false" instead of throwing an exception. Not supporting this
argument is deprecated since version 1.27.', \get_class($this)),
E_USER_DEPRECATED);

            return false;
        }
    }

    public function isFresh($name, $time)
    {
        // false support to be removed in 3.0
        if (null === ($path = $this->findTemplate($name)) || false ===
$path) {
            return false;
        }

        return filemtime($path) < $time;
    }

    /**
     * Checks if the template can be found.
     *
     * @param string $name The template name
     *
     * @return string|false|null The template name or false/null
     */
    protected function findTemplate($name)
    {
        $throw = \func_num_args() > 1 ? func_get_arg(1) : true;
        $name = $this->normalizeName($name);

        if (isset($this->cache[$name])) {
            return $this->cache[$name];
        }

        if (isset($this->errorCache[$name])) {
            if (!$throw) {
                return false;
            }

            throw new LoaderError($this->errorCache[$name]);
        }

        try {
            $this->validateName($name);

            list($namespace, $shortname) = $this->parseName($name);
        } catch (LoaderError $e) {
            if (!$throw) {
                return false;
            }

            throw $e;
        }

        if (!isset($this->paths[$namespace])) {
            $this->errorCache[$name] = sprintf('There are no
registered paths for namespace "%s".', $namespace);

            if (!$throw) {
                return false;
            }

            throw new LoaderError($this->errorCache[$name]);
        }

        foreach ($this->paths[$namespace] as $path) {
            if (!$this->isAbsolutePath($path)) {
                $path = $this->rootPath.$path;
            }

            if (is_file($path.'/'.$shortname)) {
                if (false !== $realpath =
realpath($path.'/'.$shortname)) {
                    return $this->cache[$name] = $realpath;
                }

                return $this->cache[$name] =
$path.'/'.$shortname;
            }
        }

        $this->errorCache[$name] = sprintf('Unable to find template
"%s" (looked into: %s).', $name, implode(', ',
$this->paths[$namespace]));

        if (!$throw) {
            return false;
        }

        throw new LoaderError($this->errorCache[$name]);
    }

    protected function parseName($name, $default = self::MAIN_NAMESPACE)
    {
        if (isset($name[0]) && '@' == $name[0]) {
            if (false === $pos = strpos($name, '/')) {
                throw new LoaderError(sprintf('Malformed namespaced
template name "%s" (expecting
"@namespace/template_name").', $name));
            }

            $namespace = substr($name, 1, $pos - 1);
            $shortname = substr($name, $pos + 1);

            return [$namespace, $shortname];
        }

        return [$default, $name];
    }

    protected function normalizeName($name)
    {
        return preg_replace('#/{2,}#', '/',
str_replace('\\', '/', (string) $name));
    }

    protected function validateName($name)
    {
        if (false !== strpos($name, "\0")) {
            throw new LoaderError('A template name cannot contain NUL
bytes.');
        }

        $name = ltrim($name, '/');
        $parts = explode('/', $name);
        $level = 0;
        foreach ($parts as $part) {
            if ('..' === $part) {
                --$level;
            } elseif ('.' !== $part) {
                ++$level;
            }

            if ($level < 0) {
                throw new LoaderError(sprintf('Looks like you try to
load a template outside configured directories (%s).', $name));
            }
        }
    }

    private function isAbsolutePath($file)
    {
        return strspn($file, '/\\', 0, 1)
            || (\strlen($file) > 3 && ctype_alpha($file[0])
                && ':' === substr($file, 1, 1)
                && strspn($file, '/\\', 2, 1)
            )
            || null !== parse_url($file, PHP_URL_SCHEME)
        ;
    }
}

class_alias('Twig\Loader\FilesystemLoader',
'Twig_Loader_Filesystem');
vendor/twig/twig/src/Loader/LoaderInterface.php000064400000003043151166614740015535
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Loader;

use Twig\Error\LoaderError;

/**
 * Interface all loaders must implement.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface LoaderInterface
{
    /**
     * Gets the source code of a template, given its name.
     *
     * @param string $name The name of the template to load
     *
     * @return string The template source code
     *
     * @throws LoaderError When $name is not found
     *
     * @deprecated since 1.27 (to be removed in 2.0), implement
Twig\Loader\SourceContextLoaderInterface
     */
    public function getSource($name);

    /**
     * Gets the cache key to use for the cache for a given template name.
     *
     * @param string $name The name of the template to load
     *
     * @return string The cache key
     *
     * @throws LoaderError When $name is not found
     */
    public function getCacheKey($name);

    /**
     * Returns true if the template is still fresh.
     *
     * @param string $name The template name
     * @param int    $time Timestamp of the last modification time of the
     *                     cached template
     *
     * @return bool true if the template is fresh, false otherwise
     *
     * @throws LoaderError When $name is not found
     */
    public function isFresh($name, $time);
}

class_alias('Twig\Loader\LoaderInterface',
'Twig_LoaderInterface');
vendor/twig/twig/src/Loader/SourceContextLoaderInterface.php000064400000001520151166614740020261
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Loader;

use Twig\Error\LoaderError;
use Twig\Source;

/**
 * Adds a getSourceContext() method for loaders.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @deprecated since 1.27 (to be removed in 3.0)
 */
interface SourceContextLoaderInterface
{
    /**
     * Returns the source context for a given template logical name.
     *
     * @param string $name The template logical name
     *
     * @return Source
     *
     * @throws LoaderError When $name is not found
     */
    public function getSourceContext($name);
}

class_alias('Twig\Loader\SourceContextLoaderInterface',
'Twig_SourceContextLoaderInterface');
vendor/twig/twig/src/Markup.php000064400000001462151166614740012542
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

/**
 * Marks a content as safe.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Markup implements \Countable
{
    protected $content;
    protected $charset;

    public function __construct($content, $charset)
    {
        $this->content = (string) $content;
        $this->charset = $charset;
    }

    public function __toString()
    {
        return $this->content;
    }

    public function count()
    {
        return \function_exists('mb_get_info') ?
mb_strlen($this->content, $this->charset) :
\strlen($this->content);
    }
}

class_alias('Twig\Markup', 'Twig_Markup');
vendor/twig/twig/src/Node/AutoEscapeNode.php000064400000001627151166614740015032
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * Represents an autoescape node.
 *
 * The value is the escaping strategy (can be html, js, ...)
 *
 * The true value is equivalent to html.
 *
 * If autoescaping is disabled, then the value is false.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class AutoEscapeNode extends Node
{
    public function __construct($value, \Twig_NodeInterface $body, $lineno,
$tag = 'autoescape')
    {
        parent::__construct(['body' => $body],
['value' => $value], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler->subcompile($this->getNode('body'));
    }
}

class_alias('Twig\Node\AutoEscapeNode',
'Twig_Node_AutoEscape');
vendor/twig/twig/src/Node/BlockNode.php000064400000002005151166614740014022
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * Represents a block node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class BlockNode extends Node
{
    public function __construct($name, \Twig_NodeInterface $body, $lineno,
$tag = null)
    {
        parent::__construct(['body' => $body],
['name' => $name], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write(sprintf("public function block_%s(\$context,
array \$blocks = [])\n", $this->getAttribute('name')),
"{\n")
            ->indent()
        ;

        $compiler
            ->subcompile($this->getNode('body'))
            ->outdent()
            ->write("}\n\n")
        ;
    }
}

class_alias('Twig\Node\BlockNode', 'Twig_Node_Block');
vendor/twig/twig/src/Node/BlockReferenceNode.php000064400000001561151166614740015647
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * Represents a block call node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class BlockReferenceNode extends Node implements NodeOutputInterface
{
    public function __construct($name, $lineno, $tag = null)
    {
        parent::__construct([], ['name' => $name], $lineno,
$tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
           
->write(sprintf("\$this->displayBlock('%s', \$context,
\$blocks);\n", $this->getAttribute('name')))
        ;
    }
}

class_alias('Twig\Node\BlockReferenceNode',
'Twig_Node_BlockReference');
vendor/twig/twig/src/Node/BodyNode.php000064400000000615151166614740013672
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

/**
 * Represents a body node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class BodyNode extends Node
{
}

class_alias('Twig\Node\BodyNode', 'Twig_Node_Body');
vendor/twig/twig/src/Node/CheckSecurityNode.php000064400000006025151166614740015543
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class CheckSecurityNode extends Node
{
    protected $usedFilters;
    protected $usedTags;
    protected $usedFunctions;

    public function __construct(array $usedFilters, array $usedTags, array
$usedFunctions)
    {
        $this->usedFilters = $usedFilters;
        $this->usedTags = $usedTags;
        $this->usedFunctions = $usedFunctions;

        parent::__construct();
    }

    public function compile(Compiler $compiler)
    {
        $tags = $filters = $functions = [];
        foreach (['tags', 'filters',
'functions'] as $type) {
            foreach ($this->{'used'.ucfirst($type)} as $name
=> $node) {
                if ($node instanceof Node) {
                    ${$type}[$name] = $node->getTemplateLine();
                } else {
                    ${$type}[$node] = null;
                }
            }
        }

        $compiler
            ->write("\$this->sandbox =
\$this->env->getExtension('\Twig\Extension\SandboxExtension');\n")
            ->write('$tags =
')->repr(array_filter($tags))->raw(";\n")
            ->write('$filters =
')->repr(array_filter($filters))->raw(";\n")
            ->write('$functions =
')->repr(array_filter($functions))->raw(";\n\n")
            ->write("try {\n")
            ->indent()
            ->write("\$this->sandbox->checkSecurity(\n")
            ->indent()
            ->write(!$tags ? "[],\n" :
"['".implode("', '",
array_keys($tags))."'],\n")
            ->write(!$filters ? "[],\n" :
"['".implode("', '",
array_keys($filters))."'],\n")
            ->write(!$functions ? "[]\n" :
"['".implode("', '",
array_keys($functions))."']\n")
            ->outdent()
            ->write(");\n")
            ->outdent()
            ->write("} catch (SecurityError \$e) {\n")
            ->indent()
           
->write("\$e->setSourceContext(\$this->getSourceContext());\n\n")
            ->write("if (\$e instanceof SecurityNotAllowedTagError
&& isset(\$tags[\$e->getTagName()])) {\n")
            ->indent()
           
->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n")
            ->outdent()
            ->write("} elseif (\$e instanceof
SecurityNotAllowedFilterError &&
isset(\$filters[\$e->getFilterName()])) {\n")
            ->indent()
           
->write("\$e->setTemplateLine(\$filters[\$e->getFilterName()]);\n")
            ->outdent()
            ->write("} elseif (\$e instanceof
SecurityNotAllowedFunctionError &&
isset(\$functions[\$e->getFunctionName()])) {\n")
            ->indent()
           
->write("\$e->setTemplateLine(\$functions[\$e->getFunctionName()]);\n")
            ->outdent()
            ->write("}\n\n")
            ->write("throw \$e;\n")
            ->outdent()
            ->write("}\n\n")
        ;
    }
}

class_alias('Twig\Node\CheckSecurityNode',
'Twig_Node_CheckSecurity');
vendor/twig/twig/src/Node/CheckToStringNode.php000064400000002163151166614740015504
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;

/**
 * Checks if casting an expression to __toString() is allowed by the
sandbox.
 *
 * For instance, when there is a simple Print statement, like {{ article
}},
 * and if the sandbox is enabled, we need to check that the __toString()
 * method is allowed if 'article' is an object. The same goes for
{{ article|upper }}
 * or {{ random(article) }}
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class CheckToStringNode extends AbstractExpression
{
    public function __construct(AbstractExpression $expr)
    {
        parent::__construct(['expr' => $expr], [],
$expr->getTemplateLine(), $expr->getNodeTag());
    }

    public function compile(Compiler $compiler)
    {
        $compiler
           
->raw('$this->sandbox->ensureToStringAllowed(')
            ->subcompile($this->getNode('expr'))
            ->raw(')')
        ;
    }
}
vendor/twig/twig/src/Node/DeprecatedNode.php000064400000002632151166614740015036
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ConstantExpression;

/**
 * Represents a deprecated node.
 *
 * @author Yonel Ceruto <yonelceruto@gmail.com>
 */
class DeprecatedNode extends Node
{
    public function __construct(AbstractExpression $expr, $lineno, $tag =
null)
    {
        parent::__construct(['expr' => $expr], [], $lineno,
$tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        $expr = $this->getNode('expr');

        if ($expr instanceof ConstantExpression) {
            $compiler->write('@trigger_error(')
                ->subcompile($expr);
        } else {
            $varName = $compiler->getVarName();
            $compiler->write(sprintf('$%s = ', $varName))
                ->subcompile($expr)
                ->raw(";\n")
                ->write(sprintf('@trigger_error($%s',
$varName));
        }

        $compiler
            ->raw('.')
            ->string(sprintf(' ("%s" at line %d).',
$this->getTemplateName(), $this->getTemplateLine()))
            ->raw(", E_USER_DEPRECATED);\n")
        ;
    }
}

class_alias('Twig\Node\DeprecatedNode',
'Twig_Node_Deprecated');
vendor/twig/twig/src/Node/DoNode.php000064400000001502151166614740013333
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;

/**
 * Represents a do node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DoNode extends Node
{
    public function __construct(AbstractExpression $expr, $lineno, $tag =
null)
    {
        parent::__construct(['expr' => $expr], [], $lineno,
$tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('')
            ->subcompile($this->getNode('expr'))
            ->raw(";\n")
        ;
    }
}

class_alias('Twig\Node\DoNode', 'Twig_Node_Do');
vendor/twig/twig/src/Node/EmbedNode.php000064400000003015151166614740014006
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ConstantExpression;

/**
 * Represents an embed node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class EmbedNode extends IncludeNode
{
    // we don't inject the module to avoid node visitors to traverse
it twice (as it will be already visited in the main module)
    public function __construct($name, $index, AbstractExpression
$variables = null, $only = false, $ignoreMissing = false, $lineno, $tag =
null)
    {
        parent::__construct(new ConstantExpression('not_used',
$lineno), $variables, $only, $ignoreMissing, $lineno, $tag);

        $this->setAttribute('name', $name);
        // to be removed in 2.0, used name instead
        $this->setAttribute('filename', $name);
        $this->setAttribute('index', $index);
    }

    protected function addGetTemplate(Compiler $compiler)
    {
        $compiler
            ->write('$this->loadTemplate(')
            ->string($this->getAttribute('name'))
            ->raw(', ')
            ->repr($this->getTemplateName())
            ->raw(', ')
            ->repr($this->getTemplateLine())
            ->raw(', ')
            ->string($this->getAttribute('index'))
            ->raw(')')
        ;
    }
}

class_alias('Twig\Node\EmbedNode', 'Twig_Node_Embed');
vendor/twig/twig/src/Node/Expression/AbstractExpression.php000064400000001025151166614740020145
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Node\Node;

/**
 * Abstract class for all nodes that represents an expression.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class AbstractExpression extends Node
{
}

class_alias('Twig\Node\Expression\AbstractExpression',
'Twig_Node_Expression');
vendor/twig/twig/src/Node/Expression/ArrayExpression.php000064400000004415151166614740017466
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;

class ArrayExpression extends AbstractExpression
{
    protected $index;

    public function __construct(array $elements, $lineno)
    {
        parent::__construct($elements, [], $lineno);

        $this->index = -1;
        foreach ($this->getKeyValuePairs() as $pair) {
            if ($pair['key'] instanceof ConstantExpression
&& ctype_digit((string)
$pair['key']->getAttribute('value')) &&
$pair['key']->getAttribute('value') >
$this->index) {
                $this->index =
$pair['key']->getAttribute('value');
            }
        }
    }

    public function getKeyValuePairs()
    {
        $pairs = [];

        foreach (array_chunk($this->nodes, 2) as $pair) {
            $pairs[] = [
                'key' => $pair[0],
                'value' => $pair[1],
            ];
        }

        return $pairs;
    }

    public function hasElement(AbstractExpression $key)
    {
        foreach ($this->getKeyValuePairs() as $pair) {
            // we compare the string representation of the keys
            // to avoid comparing the line numbers which are not relevant
here.
            if ((string) $key === (string) $pair['key']) {
                return true;
            }
        }

        return false;
    }

    public function addElement(AbstractExpression $value,
AbstractExpression $key = null)
    {
        if (null === $key) {
            $key = new ConstantExpression(++$this->index,
$value->getTemplateLine());
        }

        array_push($this->nodes, $key, $value);
    }

    public function compile(Compiler $compiler)
    {
        $compiler->raw('[');
        $first = true;
        foreach ($this->getKeyValuePairs() as $pair) {
            if (!$first) {
                $compiler->raw(', ');
            }
            $first = false;

            $compiler
                ->subcompile($pair['key'])
                ->raw(' => ')
                ->subcompile($pair['value'])
            ;
        }
        $compiler->raw(']');
    }
}

class_alias('Twig\Node\Expression\ArrayExpression',
'Twig_Node_Expression_Array');
vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php000064400000003034151166614740021204
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;
use Twig\Node\Node;

/**
 * Represents an arrow function.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ArrowFunctionExpression extends AbstractExpression
{
    public function __construct(AbstractExpression $expr, Node $names,
$lineno, $tag = null)
    {
        parent::__construct(['expr' => $expr,
'names' => $names], [], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->raw('function (')
        ;
        foreach ($this->getNode('names') as $i => $name) {
            if ($i) {
                $compiler->raw(', ');
            }

            $compiler
                ->raw('$__')
                ->raw($name->getAttribute('name'))
                ->raw('__')
            ;
        }
        $compiler
            ->raw(') use ($context) { ')
        ;
        foreach ($this->getNode('names') as $name) {
            $compiler
                ->raw('$context["')
                ->raw($name->getAttribute('name'))
                ->raw('"] = $__')
                ->raw($name->getAttribute('name'))
                ->raw('__; ')
            ;
        }
        $compiler
            ->raw('return ')
            ->subcompile($this->getNode('expr'))
            ->raw('; }')
        ;
    }
}
vendor/twig/twig/src/Node/Expression/AssignNameExpression.php000064400000001151151166614740020427
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;

class AssignNameExpression extends NameExpression
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('$context[')
            ->string($this->getAttribute('name'))
            ->raw(']')
        ;
    }
}

class_alias('Twig\Node\Expression\AssignNameExpression',
'Twig_Node_Expression_AssignName');
vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php000064400000002061151166614740020457
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;

abstract class AbstractBinary extends AbstractExpression
{
    public function __construct(\Twig_NodeInterface $left,
\Twig_NodeInterface $right, $lineno)
    {
        parent::__construct(['left' => $left,
'right' => $right], [], $lineno);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('(')
            ->subcompile($this->getNode('left'))
            ->raw(' ')
        ;
        $this->operator($compiler);
        $compiler
            ->raw(' ')
            ->subcompile($this->getNode('right'))
            ->raw(')')
        ;
    }

    abstract public function operator(Compiler $compiler);
}

class_alias('Twig\Node\Expression\Binary\AbstractBinary',
'Twig_Node_Expression_Binary');
vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php000064400000001002151166614740017376
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class AddBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('+');
    }
}

class_alias('Twig\Node\Expression\Binary\AddBinary',
'Twig_Node_Expression_Binary_Add');
vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php000064400000001003151166614740017411
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class AndBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('&&');
    }
}

class_alias('Twig\Node\Expression\Binary\AndBinary',
'Twig_Node_Expression_Binary_And');
vendor/twig/twig/src/Node/Expression/Binary/BitwiseAndBinary.php000064400000001027151166614740020746
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class BitwiseAndBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('&');
    }
}

class_alias('Twig\Node\Expression\Binary\BitwiseAndBinary',
'Twig_Node_Expression_Binary_BitwiseAnd');
vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php000064400000001024151166614750020622
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class BitwiseOrBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('|');
    }
}

class_alias('Twig\Node\Expression\Binary\BitwiseOrBinary',
'Twig_Node_Expression_Binary_BitwiseOr');
vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php000064400000001027151166614750021015
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class BitwiseXorBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('^');
    }
}

class_alias('Twig\Node\Expression\Binary\BitwiseXorBinary',
'Twig_Node_Expression_Binary_BitwiseXor');
vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php000064400000001013151166614750020120
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class ConcatBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('.');
    }
}

class_alias('Twig\Node\Expression\Binary\ConcatBinary',
'Twig_Node_Expression_Binary_Concat');
vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php000064400000001002151166614750017431
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class DivBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('/');
    }
}

class_alias('Twig\Node\Expression\Binary\DivBinary',
'Twig_Node_Expression_Binary_Div');
vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php000064400000001753151166614750020451
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class EndsWithBinary extends AbstractBinary
{
    public function compile(Compiler $compiler)
    {
        $left = $compiler->getVarName();
        $right = $compiler->getVarName();
        $compiler
            ->raw(sprintf('(is_string($%s = ', $left))
            ->subcompile($this->getNode('left'))
            ->raw(sprintf(') && is_string($%s = ',
$right))
            ->subcompile($this->getNode('right'))
            ->raw(sprintf(') && (\'\' === $%2$s
|| $%2$s === substr($%1$s, -strlen($%2$s))))', $left, $right))
        ;
    }

    public function operator(Compiler $compiler)
    {
        return $compiler->raw('');
    }
}

class_alias('Twig\Node\Expression\Binary\EndsWithBinary',
'Twig_Node_Expression_Binary_EndsWith');
vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php000064400000000763151166614750017773
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class EqualBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('==');
    }
}

class_alias('Twig\Node\Expression\Binary\EqualBinary',
'Twig_Node_Expression_Binary_Equal');
vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php000064400000001241151166614750020440
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class FloorDivBinary extends AbstractBinary
{
    public function compile(Compiler $compiler)
    {
        $compiler->raw('(int) floor(');
        parent::compile($compiler);
        $compiler->raw(')');
    }

    public function operator(Compiler $compiler)
    {
        return $compiler->raw('/');
    }
}

class_alias('Twig\Node\Expression\Binary\FloorDivBinary',
'Twig_Node_Expression_Binary_FloorDiv');
vendor/twig/twig/src/Node/Expression/Binary/GreaterBinary.php000064400000000770151166614750020313
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class GreaterBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('>');
    }
}

class_alias('Twig\Node\Expression\Binary\GreaterBinary',
'Twig_Node_Expression_Binary_Greater');
vendor/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php000064400000001010151166614750021267
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class GreaterEqualBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('>=');
    }
}

class_alias('Twig\Node\Expression\Binary\GreaterEqualBinary',
'Twig_Node_Expression_Binary_GreaterEqual');
vendor/twig/twig/src/Node/Expression/Binary/InBinary.php000064400000001372151166614750017267
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class InBinary extends AbstractBinary
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('twig_in_filter(')
            ->subcompile($this->getNode('left'))
            ->raw(', ')
            ->subcompile($this->getNode('right'))
            ->raw(')')
        ;
    }

    public function operator(Compiler $compiler)
    {
        return $compiler->raw('in');
    }
}

class_alias('Twig\Node\Expression\Binary\InBinary',
'Twig_Node_Expression_Binary_In');
vendor/twig/twig/src/Node/Expression/Binary/LessBinary.php000064400000000757151166614750017635
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class LessBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('<');
    }
}

class_alias('Twig\Node\Expression\Binary\LessBinary',
'Twig_Node_Expression_Binary_Less');
vendor/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php000064400000000777151166614750020627
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class LessEqualBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('<=');
    }
}

class_alias('Twig\Node\Expression\Binary\LessEqualBinary',
'Twig_Node_Expression_Binary_LessEqual');
vendor/twig/twig/src/Node/Expression/Binary/MatchesBinary.php000064400000001403151166614750020300
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class MatchesBinary extends AbstractBinary
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('preg_match(')
            ->subcompile($this->getNode('right'))
            ->raw(', ')
            ->subcompile($this->getNode('left'))
            ->raw(')')
        ;
    }

    public function operator(Compiler $compiler)
    {
        return $compiler->raw('');
    }
}

class_alias('Twig\Node\Expression\Binary\MatchesBinary',
'Twig_Node_Expression_Binary_Matches');
vendor/twig/twig/src/Node/Expression/Binary/ModBinary.php000064400000001002151166614750017426
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class ModBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('%');
    }
}

class_alias('Twig\Node\Expression\Binary\ModBinary',
'Twig_Node_Expression_Binary_Mod');
vendor/twig/twig/src/Node/Expression/Binary/MulBinary.php000064400000001002151166614750017444
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class MulBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('*');
    }
}

class_alias('Twig\Node\Expression\Binary\MulBinary',
'Twig_Node_Expression_Binary_Mul');
vendor/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php000064400000000774151166614750020456
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class NotEqualBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('!=');
    }
}

class_alias('Twig\Node\Expression\Binary\NotEqualBinary',
'Twig_Node_Expression_Binary_NotEqual');
vendor/twig/twig/src/Node/Expression/Binary/NotInBinary.php000064400000001410151166614750017741
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class NotInBinary extends AbstractBinary
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('!twig_in_filter(')
            ->subcompile($this->getNode('left'))
            ->raw(', ')
            ->subcompile($this->getNode('right'))
            ->raw(')')
        ;
    }

    public function operator(Compiler $compiler)
    {
        return $compiler->raw('not in');
    }
}

class_alias('Twig\Node\Expression\Binary\NotInBinary',
'Twig_Node_Expression_Binary_NotIn');
vendor/twig/twig/src/Node/Expression/Binary/OrBinary.php000064400000001000151166614750017265
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class OrBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('||');
    }
}

class_alias('Twig\Node\Expression\Binary\OrBinary',
'Twig_Node_Expression_Binary_Or');
vendor/twig/twig/src/Node/Expression/Binary/PowerBinary.php000064400000001532151166614750020013
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class PowerBinary extends AbstractBinary
{
    public function compile(Compiler $compiler)
    {
        if (\PHP_VERSION_ID >= 50600) {
            return parent::compile($compiler);
        }

        $compiler
            ->raw('pow(')
            ->subcompile($this->getNode('left'))
            ->raw(', ')
            ->subcompile($this->getNode('right'))
            ->raw(')')
        ;
    }

    public function operator(Compiler $compiler)
    {
        return $compiler->raw('**');
    }
}

class_alias('Twig\Node\Expression\Binary\PowerBinary',
'Twig_Node_Expression_Binary_Power');
vendor/twig/twig/src/Node/Expression/Binary/RangeBinary.php000064400000001372151166614750017755
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class RangeBinary extends AbstractBinary
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('range(')
            ->subcompile($this->getNode('left'))
            ->raw(', ')
            ->subcompile($this->getNode('right'))
            ->raw(')')
        ;
    }

    public function operator(Compiler $compiler)
    {
        return $compiler->raw('..');
    }
}

class_alias('Twig\Node\Expression\Binary\RangeBinary',
'Twig_Node_Expression_Binary_Range');
vendor/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php000064400000001744151166614750021040
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class StartsWithBinary extends AbstractBinary
{
    public function compile(Compiler $compiler)
    {
        $left = $compiler->getVarName();
        $right = $compiler->getVarName();
        $compiler
            ->raw(sprintf('(is_string($%s = ', $left))
            ->subcompile($this->getNode('left'))
            ->raw(sprintf(') && is_string($%s = ',
$right))
            ->subcompile($this->getNode('right'))
            ->raw(sprintf(') && (\'\' === $%2$s
|| 0 === strpos($%1$s, $%2$s)))', $left, $right))
        ;
    }

    public function operator(Compiler $compiler)
    {
        return $compiler->raw('');
    }
}

class_alias('Twig\Node\Expression\Binary\StartsWithBinary',
'Twig_Node_Expression_Binary_StartsWith');
vendor/twig/twig/src/Node/Expression/Binary/SubBinary.php000064400000001002151166614750017440
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Binary;

use Twig\Compiler;

class SubBinary extends AbstractBinary
{
    public function operator(Compiler $compiler)
    {
        return $compiler->raw('-');
    }
}

class_alias('Twig\Node\Expression\Binary\SubBinary',
'Twig_Node_Expression_Binary_Sub');
vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php000064400000005202151166614750021255
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;
use Twig\Node\Node;

/**
 * Represents a block call node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class BlockReferenceExpression extends AbstractExpression
{
    /**
     * @param Node|null $template
     */
    public function __construct(\Twig_NodeInterface $name, $template =
null, $lineno, $tag = null)
    {
        if (\is_bool($template)) {
            @trigger_error(sprintf('The %s method
"$asString" argument is deprecated since version 1.28 and will be
removed in 2.0.', __METHOD__), E_USER_DEPRECATED);

            $template = null;
        }

        $nodes = ['name' => $name];
        if (null !== $template) {
            $nodes['template'] = $template;
        }

        parent::__construct($nodes, ['is_defined_test' =>
false, 'output' => false], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        if ($this->getAttribute('is_defined_test')) {
            $this->compileTemplateCall($compiler, 'hasBlock');
        } else {
            if ($this->getAttribute('output')) {
                $compiler->addDebugInfo($this);

                $this
                    ->compileTemplateCall($compiler,
'displayBlock')
                    ->raw(";\n");
            } else {
                $this->compileTemplateCall($compiler,
'renderBlock');
            }
        }
    }

    private function compileTemplateCall(Compiler $compiler, $method)
    {
        if (!$this->hasNode('template')) {
            $compiler->write('$this');
        } else {
            $compiler
                ->write('$this->loadTemplate(')
                ->subcompile($this->getNode('template'))
                ->raw(', ')
                ->repr($this->getTemplateName())
                ->raw(', ')
                ->repr($this->getTemplateLine())
                ->raw(')')
            ;
        }

        $compiler->raw(sprintf('->%s', $method));
        $this->compileBlockArguments($compiler);

        return $compiler;
    }

    private function compileBlockArguments(Compiler $compiler)
    {
        $compiler
            ->raw('(')
            ->subcompile($this->getNode('name'))
            ->raw(', $context');

        if (!$this->hasNode('template')) {
            $compiler->raw(', $blocks');
        }

        return $compiler->raw(')');
    }
}

class_alias('Twig\Node\Expression\BlockReferenceExpression',
'Twig_Node_Expression_BlockReference');
vendor/twig/twig/src/Node/Expression/CallExpression.php000064400000026750151166614750017272
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;
use Twig\Error\SyntaxError;
use Twig\Extension\ExtensionInterface;
use Twig\Node\Node;

abstract class CallExpression extends AbstractExpression
{
    private $reflector;

    protected function compileCallable(Compiler $compiler)
    {
        $closingParenthesis = false;
        $isArray = false;
        if ($this->hasAttribute('callable') &&
$callable = $this->getAttribute('callable')) {
            if (\is_string($callable) && false ===
strpos($callable, '::')) {
                $compiler->raw($callable);
            } else {
                list($r, $callable) = $this->reflectCallable($callable);
                if ($r instanceof \ReflectionMethod &&
\is_string($callable[0])) {
                    if ($r->isStatic()) {
                        $compiler->raw(sprintf('%s::%s',
$callable[0], $callable[1]));
                    } else {
                       
$compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s',
$callable[0], $callable[1]));
                    }
                } elseif ($r instanceof \ReflectionMethod &&
$callable[0] instanceof ExtensionInterface) {
                   
$compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s',
\get_class($callable[0]), $callable[1]));
                } else {
                    $type =
ucfirst($this->getAttribute('type'));
                   
$compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(),
', $type, $this->getAttribute('name')));
                    $closingParenthesis = true;
                    $isArray = true;
                }
            }
        } else {
           
$compiler->raw($this->getAttribute('thing')->compile());
        }

        $this->compileArguments($compiler, $isArray);

        if ($closingParenthesis) {
            $compiler->raw(')');
        }
    }

    protected function compileArguments(Compiler $compiler, $isArray =
false)
    {
        $compiler->raw($isArray ? '[' : '(');

        $first = true;

        if ($this->hasAttribute('needs_environment')
&& $this->getAttribute('needs_environment')) {
            $compiler->raw('$this->env');
            $first = false;
        }

        if ($this->hasAttribute('needs_context') &&
$this->getAttribute('needs_context')) {
            if (!$first) {
                $compiler->raw(', ');
            }
            $compiler->raw('$context');
            $first = false;
        }

        if ($this->hasAttribute('arguments')) {
            foreach ($this->getAttribute('arguments') as
$argument) {
                if (!$first) {
                    $compiler->raw(', ');
                }
                $compiler->string($argument);
                $first = false;
            }
        }

        if ($this->hasNode('node')) {
            if (!$first) {
                $compiler->raw(', ');
            }
            $compiler->subcompile($this->getNode('node'));
            $first = false;
        }

        if ($this->hasNode('arguments')) {
            $callable = $this->hasAttribute('callable') ?
$this->getAttribute('callable') : null;

            $arguments = $this->getArguments($callable,
$this->getNode('arguments'));

            foreach ($arguments as $node) {
                if (!$first) {
                    $compiler->raw(', ');
                }
                $compiler->subcompile($node);
                $first = false;
            }
        }

        $compiler->raw($isArray ? ']' : ')');
    }

    protected function getArguments($callable, $arguments)
    {
        $callType = $this->getAttribute('type');
        $callName = $this->getAttribute('name');

        $parameters = [];
        $named = false;
        foreach ($arguments as $name => $node) {
            if (!\is_int($name)) {
                $named = true;
                $name = $this->normalizeName($name);
            } elseif ($named) {
                throw new SyntaxError(sprintf('Positional arguments
cannot be used after named arguments for %s "%s".',
$callType, $callName), $this->getTemplateLine(),
$this->getSourceContext());
            }

            $parameters[$name] = $node;
        }

        $isVariadic = $this->hasAttribute('is_variadic')
&& $this->getAttribute('is_variadic');
        if (!$named && !$isVariadic) {
            return $parameters;
        }

        if (!$callable) {
            if ($named) {
                $message = sprintf('Named arguments are not supported
for %s "%s".', $callType, $callName);
            } else {
                $message = sprintf('Arbitrary positional arguments are
not supported for %s "%s".', $callType, $callName);
            }

            throw new \LogicException($message);
        }

        $callableParameters = $this->getCallableParameters($callable,
$isVariadic);
        $arguments = [];
        $names = [];
        $missingArguments = [];
        $optionalArguments = [];
        $pos = 0;
        foreach ($callableParameters as $callableParameter) {
            $names[] = $name =
$this->normalizeName($callableParameter->name);

            if (\array_key_exists($name, $parameters)) {
                if (\array_key_exists($pos, $parameters)) {
                    throw new SyntaxError(sprintf('Argument
"%s" is defined twice for %s "%s".', $name,
$callType, $callName), $this->getTemplateLine(),
$this->getSourceContext());
                }

                if (\count($missingArguments)) {
                    throw new SyntaxError(sprintf(
                        'Argument "%s" could not be assigned
for %s "%s(%s)" because it is mapped to an internal PHP function
which cannot determine default value for optional argument%s
"%s".',
                        $name, $callType, $callName, implode(',
', $names), \count($missingArguments) > 1 ? 's' :
'', implode('", "', $missingArguments)
                    ), $this->getTemplateLine(),
$this->getSourceContext());
                }

                $arguments = array_merge($arguments, $optionalArguments);
                $arguments[] = $parameters[$name];
                unset($parameters[$name]);
                $optionalArguments = [];
            } elseif (\array_key_exists($pos, $parameters)) {
                $arguments = array_merge($arguments, $optionalArguments);
                $arguments[] = $parameters[$pos];
                unset($parameters[$pos]);
                $optionalArguments = [];
                ++$pos;
            } elseif ($callableParameter->isDefaultValueAvailable()) {
                $optionalArguments[] = new
ConstantExpression($callableParameter->getDefaultValue(), -1);
            } elseif ($callableParameter->isOptional()) {
                if (empty($parameters)) {
                    break;
                } else {
                    $missingArguments[] = $name;
                }
            } else {
                throw new SyntaxError(sprintf('Value for argument
"%s" is required for %s "%s".', $name, $callType,
$callName), $this->getTemplateLine(), $this->getSourceContext());
            }
        }

        if ($isVariadic) {
            $arbitraryArguments = new ArrayExpression([], -1);
            foreach ($parameters as $key => $value) {
                if (\is_int($key)) {
                    $arbitraryArguments->addElement($value);
                } else {
                    $arbitraryArguments->addElement($value, new
ConstantExpression($key, -1));
                }
                unset($parameters[$key]);
            }

            if ($arbitraryArguments->count()) {
                $arguments = array_merge($arguments, $optionalArguments);
                $arguments[] = $arbitraryArguments;
            }
        }

        if (!empty($parameters)) {
            $unknownParameter = null;
            foreach ($parameters as $parameter) {
                if ($parameter instanceof Node) {
                    $unknownParameter = $parameter;
                    break;
                }
            }

            throw new SyntaxError(
                sprintf(
                    'Unknown argument%s "%s" for %s
"%s(%s)".',
                    \count($parameters) > 1 ? 's' :
'', implode('", "', array_keys($parameters)),
$callType, $callName, implode(', ', $names)
                ),
                $unknownParameter ? $unknownParameter->getTemplateLine()
: $this->getTemplateLine(),
                $unknownParameter ?
$unknownParameter->getSourceContext() : $this->getSourceContext()
            );
        }

        return $arguments;
    }

    protected function normalizeName($name)
    {
        return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/',
'/([a-z\d])([A-Z])/'], ['\\1_\\2',
'\\1_\\2'], $name));
    }

    private function getCallableParameters($callable, $isVariadic)
    {
        list($r) = $this->reflectCallable($callable);
        if (null === $r) {
            return [];
        }

        $parameters = $r->getParameters();
        if ($this->hasNode('node')) {
            array_shift($parameters);
        }
        if ($this->hasAttribute('needs_environment')
&& $this->getAttribute('needs_environment')) {
            array_shift($parameters);
        }
        if ($this->hasAttribute('needs_context') &&
$this->getAttribute('needs_context')) {
            array_shift($parameters);
        }
        if ($this->hasAttribute('arguments') && null
!== $this->getAttribute('arguments')) {
            foreach ($this->getAttribute('arguments') as
$argument) {
                array_shift($parameters);
            }
        }
        if ($isVariadic) {
            $argument = end($parameters);
            if ($argument && $argument->isArray() &&
$argument->isDefaultValueAvailable() && [] ===
$argument->getDefaultValue()) {
                array_pop($parameters);
            } else {
                $callableName = $r->name;
                if ($r instanceof \ReflectionMethod) {
                    $callableName =
$r->getDeclaringClass()->name.'::'.$callableName;
                }

                throw new \LogicException(sprintf('The last parameter
of "%s" for %s "%s" must be an array with default
value, eg. "array $arg = []".', $callableName,
$this->getAttribute('type'),
$this->getAttribute('name')));
            }
        }

        return $parameters;
    }

    private function reflectCallable($callable)
    {
        if (null !== $this->reflector) {
            return $this->reflector;
        }

        if (\is_array($callable)) {
            if (!method_exists($callable[0], $callable[1])) {
                // __call()
                return [null, []];
            }
            $r = new \ReflectionMethod($callable[0], $callable[1]);
        } elseif (\is_object($callable) && !$callable instanceof
\Closure) {
            $r = new \ReflectionObject($callable);
            $r = $r->getMethod('__invoke');
            $callable = [$callable, '__invoke'];
        } elseif (\is_string($callable) && false !== $pos =
strpos($callable, '::')) {
            $class = substr($callable, 0, $pos);
            $method = substr($callable, $pos + 2);
            if (!method_exists($class, $method)) {
                // __staticCall()
                return [null, []];
            }
            $r = new \ReflectionMethod($callable);
            $callable = [$class, $method];
        } else {
            $r = new \ReflectionFunction($callable);
        }

        return $this->reflector = [$r, $callable];
    }
}

class_alias('Twig\Node\Expression\CallExpression',
'Twig_Node_Expression_Call');
vendor/twig/twig/src/Node/Expression/ConditionalExpression.php000064400000001760151166614750020654
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;

class ConditionalExpression extends AbstractExpression
{
    public function __construct(AbstractExpression $expr1,
AbstractExpression $expr2, AbstractExpression $expr3, $lineno)
    {
        parent::__construct(['expr1' => $expr1,
'expr2' => $expr2, 'expr3' => $expr3], [],
$lineno);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('((')
            ->subcompile($this->getNode('expr1'))
            ->raw(') ? (')
            ->subcompile($this->getNode('expr2'))
            ->raw(') : (')
            ->subcompile($this->getNode('expr3'))
            ->raw('))')
        ;
    }
}

class_alias('Twig\Node\Expression\ConditionalExpression',
'Twig_Node_Expression_Conditional');
vendor/twig/twig/src/Node/Expression/ConstantExpression.php000064400000001227151166614750020200
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;

class ConstantExpression extends AbstractExpression
{
    public function __construct($value, $lineno)
    {
        parent::__construct([], ['value' => $value], $lineno);
    }

    public function compile(Compiler $compiler)
    {
        $compiler->repr($this->getAttribute('value'));
    }
}

class_alias('Twig\Node\Expression\ConstantExpression',
'Twig_Node_Expression_Constant');
vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php000064400000003556151166614750020315
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Filter;

use Twig\Compiler;
use Twig\Node\Expression\ConditionalExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\Test\DefinedTest;
use Twig\Node\Node;

/**
 * Returns the value or the default value when it is undefined or empty.
 *
 *  {{ var.foo|default('foo item on var is not defined') }}
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DefaultFilter extends FilterExpression
{
    public function __construct(\Twig_NodeInterface $node,
ConstantExpression $filterName, \Twig_NodeInterface $arguments, $lineno,
$tag = null)
    {
        $default = new FilterExpression($node, new
ConstantExpression('default', $node->getTemplateLine()),
$arguments, $node->getTemplateLine());

        if ('default' ===
$filterName->getAttribute('value') && ($node
instanceof NameExpression || $node instanceof GetAttrExpression)) {
            $test = new DefinedTest(clone $node, 'defined', new
Node(), $node->getTemplateLine());
            $false = \count($arguments) ? $arguments->getNode(0) : new
ConstantExpression('', $node->getTemplateLine());

            $node = new ConditionalExpression($test, $default, $false,
$node->getTemplateLine());
        } else {
            $node = $default;
        }

        parent::__construct($node, $filterName, $arguments, $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler->subcompile($this->getNode('node'));
    }
}

class_alias('Twig\Node\Expression\Filter\DefaultFilter',
'Twig_Node_Expression_Filter_Default');
vendor/twig/twig/src/Node/Expression/FilterExpression.php000064400000003103151166614750017627
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;
use Twig\TwigFilter;

class FilterExpression extends CallExpression
{
    public function __construct(\Twig_NodeInterface $node,
ConstantExpression $filterName, \Twig_NodeInterface $arguments, $lineno,
$tag = null)
    {
        parent::__construct(['node' => $node,
'filter' => $filterName, 'arguments' =>
$arguments], [], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $name =
$this->getNode('filter')->getAttribute('value');
        $filter = $compiler->getEnvironment()->getFilter($name);

        $this->setAttribute('name', $name);
        $this->setAttribute('type', 'filter');
        $this->setAttribute('thing', $filter);
        $this->setAttribute('needs_environment',
$filter->needsEnvironment());
        $this->setAttribute('needs_context',
$filter->needsContext());
        $this->setAttribute('arguments',
$filter->getArguments());
        if ($filter instanceof \Twig_FilterCallableInterface || $filter
instanceof TwigFilter) {
            $this->setAttribute('callable',
$filter->getCallable());
        }
        if ($filter instanceof TwigFilter) {
            $this->setAttribute('is_variadic',
$filter->isVariadic());
        }

        $this->compileCallable($compiler);
    }
}

class_alias('Twig\Node\Expression\FilterExpression',
'Twig_Node_Expression_Filter');
vendor/twig/twig/src/Node/Expression/FunctionExpression.php000064400000003265151166614750020200
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;
use Twig\TwigFunction;

class FunctionExpression extends CallExpression
{
    public function __construct($name, \Twig_NodeInterface $arguments,
$lineno)
    {
        parent::__construct(['arguments' => $arguments],
['name' => $name, 'is_defined_test' => false],
$lineno);
    }

    public function compile(Compiler $compiler)
    {
        $name = $this->getAttribute('name');
        $function = $compiler->getEnvironment()->getFunction($name);

        $this->setAttribute('name', $name);
        $this->setAttribute('type', 'function');
        $this->setAttribute('thing', $function);
        $this->setAttribute('needs_environment',
$function->needsEnvironment());
        $this->setAttribute('needs_context',
$function->needsContext());
        $this->setAttribute('arguments',
$function->getArguments());
        if ($function instanceof \Twig_FunctionCallableInterface ||
$function instanceof TwigFunction) {
            $callable = $function->getCallable();
            if ('constant' === $name &&
$this->getAttribute('is_defined_test')) {
                $callable = 'twig_constant_is_defined';
            }

            $this->setAttribute('callable', $callable);
        }
        if ($function instanceof TwigFunction) {
            $this->setAttribute('is_variadic',
$function->isVariadic());
        }

        $this->compileCallable($compiler);
    }
}

class_alias('Twig\Node\Expression\FunctionExpression',
'Twig_Node_Expression_Function');
vendor/twig/twig/src/Node/Expression/GetAttrExpression.php000064400000005252151166614750017763
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;
use Twig\Template;

class GetAttrExpression extends AbstractExpression
{
    public function __construct(AbstractExpression $node,
AbstractExpression $attribute, AbstractExpression $arguments = null, $type,
$lineno)
    {
        $nodes = ['node' => $node, 'attribute' =>
$attribute];
        if (null !== $arguments) {
            $nodes['arguments'] = $arguments;
        }

        parent::__construct($nodes, ['type' => $type,
'is_defined_test' => false, 'ignore_strict_check'
=> false, 'disable_c_ext' => false], $lineno);
    }

    public function compile(Compiler $compiler)
    {
        if ($this->getAttribute('disable_c_ext')) {
            @trigger_error(sprintf('Using the
"disable_c_ext" attribute on %s is deprecated since version 1.30
and will be removed in 2.0.', __CLASS__), E_USER_DEPRECATED);
        }

        if (\function_exists('twig_template_get_attributes')
&& !$this->getAttribute('disable_c_ext')) {
            $compiler->raw('twig_template_get_attributes($this,
');
        } else {
            $compiler->raw('$this->getAttribute(');
        }

        if ($this->getAttribute('ignore_strict_check')) {
           
$this->getNode('node')->setAttribute('ignore_strict_check',
true);
        }

        $compiler->subcompile($this->getNode('node'));

        $compiler->raw(',
')->subcompile($this->getNode('attribute'));

        // only generate optional arguments when needed (to make generated
code more readable)
        $needFourth =
$this->getAttribute('ignore_strict_check');
        $needThird = $needFourth ||
$this->getAttribute('is_defined_test');
        $needSecond = $needThird || Template::ANY_CALL !==
$this->getAttribute('type');
        $needFirst = $needSecond ||
$this->hasNode('arguments');

        if ($needFirst) {
            if ($this->hasNode('arguments')) {
                $compiler->raw(',
')->subcompile($this->getNode('arguments'));
            } else {
                $compiler->raw(', []');
            }
        }

        if ($needSecond) {
            $compiler->raw(',
')->repr($this->getAttribute('type'));
        }

        if ($needThird) {
            $compiler->raw(',
')->repr($this->getAttribute('is_defined_test'));
        }

        if ($needFourth) {
            $compiler->raw(',
')->repr($this->getAttribute('ignore_strict_check'));
        }

        $compiler->raw(')');
    }
}

class_alias('Twig\Node\Expression\GetAttrExpression',
'Twig_Node_Expression_GetAttr');
vendor/twig/twig/src/Node/Expression/InlinePrint.php000064400000001233151166614750016557
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;
use Twig\Node\Node;

/**
 * @internal
 */
final class InlinePrint extends AbstractExpression
{
    public function __construct(Node $node, $lineno)
    {
        parent::__construct(['node' => $node], [], $lineno);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('print (')
            ->subcompile($this->getNode('node'))
            ->raw(')')
        ;
    }
}
vendor/twig/twig/src/Node/Expression/MethodCallExpression.php000064400000002416151166614750020424
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;

class MethodCallExpression extends AbstractExpression
{
    public function __construct(AbstractExpression $node, $method,
ArrayExpression $arguments, $lineno)
    {
        parent::__construct(['node' => $node,
'arguments' => $arguments], ['method' => $method,
'safe' => false], $lineno);

        if ($node instanceof NameExpression) {
            $node->setAttribute('always_defined', true);
        }
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->subcompile($this->getNode('node'))
            ->raw('->')
            ->raw($this->getAttribute('method'))
            ->raw('(')
        ;
        $first = true;
        foreach
($this->getNode('arguments')->getKeyValuePairs() as $pair)
{
            if (!$first) {
                $compiler->raw(', ');
            }
            $first = false;

            $compiler->subcompile($pair['value']);
        }
        $compiler->raw(')');
    }
}

class_alias('Twig\Node\Expression\MethodCallExpression',
'Twig_Node_Expression_MethodCall');
vendor/twig/twig/src/Node/Expression/NameExpression.php000064400000007173151166614750017275
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;

class NameExpression extends AbstractExpression
{
    protected $specialVars = [
        '_self' => '$this',
        '_context' => '$context',
        '_charset' =>
'$this->env->getCharset()',
    ];

    public function __construct($name, $lineno)
    {
        parent::__construct([], ['name' => $name,
'is_defined_test' => false, 'ignore_strict_check'
=> false, 'always_defined' => false], $lineno);
    }

    public function compile(Compiler $compiler)
    {
        $name = $this->getAttribute('name');

        $compiler->addDebugInfo($this);

        if ($this->getAttribute('is_defined_test')) {
            if ($this->isSpecial()) {
                $compiler->repr(true);
            } elseif (\PHP_VERSION_ID >= 700400) {
                $compiler
                    ->raw('array_key_exists(')
                    ->string($name)
                    ->raw(', $context)')
                ;
            } else {
                $compiler
                    ->raw('(isset($context[')
                    ->string($name)
                    ->raw(']) || array_key_exists(')
                    ->string($name)
                    ->raw(', $context))')
                ;
            }
        } elseif ($this->isSpecial()) {
            $compiler->raw($this->specialVars[$name]);
        } elseif ($this->getAttribute('always_defined')) {
            $compiler
                ->raw('$context[')
                ->string($name)
                ->raw(']')
            ;
        } else {
            if (\PHP_VERSION_ID >= 70000) {
                // use PHP 7 null coalescing operator
                $compiler
                    ->raw('($context[')
                    ->string($name)
                    ->raw('] ?? ')
                ;

                if ($this->getAttribute('ignore_strict_check')
|| !$compiler->getEnvironment()->isStrictVariables()) {
                    $compiler->raw('null)');
                } else {
                    $compiler->raw('$this->getContext($context,
')->string($name)->raw('))');
                }
            } elseif (\PHP_VERSION_ID >= 50400) {
                // PHP 5.4 ternary operator performance was optimized
                $compiler
                    ->raw('(isset($context[')
                    ->string($name)
                    ->raw(']) ? $context[')
                    ->string($name)
                    ->raw('] : ')
                ;

                if ($this->getAttribute('ignore_strict_check')
|| !$compiler->getEnvironment()->isStrictVariables()) {
                    $compiler->raw('null)');
                } else {
                    $compiler->raw('$this->getContext($context,
')->string($name)->raw('))');
                }
            } else {
                $compiler
                    ->raw('$this->getContext($context, ')
                    ->string($name)
                ;

                if
($this->getAttribute('ignore_strict_check')) {
                    $compiler->raw(', true');
                }

                $compiler
                    ->raw(')')
                ;
            }
        }
    }

    public function isSpecial()
    {
        return
isset($this->specialVars[$this->getAttribute('name')]);
    }

    public function isSimple()
    {
        return !$this->isSpecial() &&
!$this->getAttribute('is_defined_test');
    }
}

class_alias('Twig\Node\Expression\NameExpression',
'Twig_Node_Expression_Name');
vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php000064400000004310151166614750020754
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;
use Twig\Node\Expression\Binary\AndBinary;
use Twig\Node\Expression\Test\DefinedTest;
use Twig\Node\Expression\Test\NullTest;
use Twig\Node\Expression\Unary\NotUnary;
use Twig\Node\Node;

class NullCoalesceExpression extends ConditionalExpression
{
    public function __construct(\Twig_NodeInterface $left,
\Twig_NodeInterface $right, $lineno)
    {
        $test = new DefinedTest(clone $left, 'defined', new
Node(), $left->getTemplateLine());
        // for "block()", we don't need the null test as the
return value is always a string
        if (!$left instanceof BlockReferenceExpression) {
            $test = new AndBinary(
                $test,
                new NotUnary(new NullTest($left, 'null', new
Node(), $left->getTemplateLine()), $left->getTemplateLine()),
                $left->getTemplateLine()
            );
        }

        parent::__construct($test, $left, $right, $lineno);
    }

    public function compile(Compiler $compiler)
    {
        /*
         * This optimizes only one case. PHP 7 also supports more complex
expressions
         * that can return null. So, for instance, if log is defined,
log("foo") ?? "..." works,
         * but log($a["foo"]) ?? "..." does not if
$a["foo"] is not defined. More advanced
         * cases might be implemented as an optimizer node visitor, but has
not been done
         * as benefits are probably not worth the added complexity.
         */
        if (\PHP_VERSION_ID >= 70000 &&
$this->getNode('expr2') instanceof NameExpression) {
           
$this->getNode('expr2')->setAttribute('always_defined',
true);
            $compiler
                ->raw('((')
                ->subcompile($this->getNode('expr2'))
                ->raw(') ?? (')
                ->subcompile($this->getNode('expr3'))
                ->raw('))')
            ;
        } else {
            parent::compile($compiler);
        }
    }
}

class_alias('Twig\Node\Expression\NullCoalesceExpression',
'Twig_Node_Expression_NullCoalesce');
vendor/twig/twig/src/Node/Expression/ParentExpression.php000064400000002302151166614750017633
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;

/**
 * Represents a parent node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ParentExpression extends AbstractExpression
{
    public function __construct($name, $lineno, $tag = null)
    {
        parent::__construct([], ['output' => false,
'name' => $name], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        if ($this->getAttribute('output')) {
            $compiler
                ->addDebugInfo($this)
                ->write('$this->displayParentBlock(')
                ->string($this->getAttribute('name'))
                ->raw(", \$context, \$blocks);\n")
            ;
        } else {
            $compiler
                ->raw('$this->renderParentBlock(')
                ->string($this->getAttribute('name'))
                ->raw(', $context, $blocks)')
            ;
        }
    }
}

class_alias('Twig\Node\Expression\ParentExpression',
'Twig_Node_Expression_Parent');
vendor/twig/twig/src/Node/Expression/TempNameExpression.php000064400000001301151166614750020106
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;

class TempNameExpression extends AbstractExpression
{
    public function __construct($name, $lineno)
    {
        parent::__construct([], ['name' => $name], $lineno);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('$_')
            ->raw($this->getAttribute('name'))
            ->raw('_')
        ;
    }
}

class_alias('Twig\Node\Expression\TempNameExpression',
'Twig_Node_Expression_TempName');
vendor/twig/twig/src/Node/Expression/Test/ConstantTest.php000064400000002363151166614750017701
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Test;

use Twig\Compiler;
use Twig\Node\Expression\TestExpression;

/**
 * Checks if a variable is the exact same value as a constant.
 *
 *    {% if post.status is constant('Post::PUBLISHED') %}
 *      the status attribute is exactly the same as Post::PUBLISHED
 *    {% endif %}
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ConstantTest extends TestExpression
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('(')
            ->subcompile($this->getNode('node'))
            ->raw(' === constant(')
        ;

        if ($this->getNode('arguments')->hasNode(1)) {
            $compiler
                ->raw('get_class(')
               
->subcompile($this->getNode('arguments')->getNode(1))
                ->raw(')."::".')
            ;
        }

        $compiler
           
->subcompile($this->getNode('arguments')->getNode(0))
            ->raw('))')
        ;
    }
}

class_alias('Twig\Node\Expression\Test\ConstantTest',
'Twig_Node_Expression_Test_Constant');
vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php000064400000004606151166614750017450
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Test;

use Twig\Compiler;
use Twig\Error\SyntaxError;
use Twig\Node\Expression\ArrayExpression;
use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\TestExpression;

/**
 * Checks if a variable is defined in the current context.
 *
 *    {# defined works with variable names and variable attributes #}
 *    {% if foo is defined %}
 *        {# ... #}
 *    {% endif %}
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DefinedTest extends TestExpression
{
    public function __construct(\Twig_NodeInterface $node, $name,
\Twig_NodeInterface $arguments = null, $lineno)
    {
        if ($node instanceof NameExpression) {
            $node->setAttribute('is_defined_test', true);
        } elseif ($node instanceof GetAttrExpression) {
            $node->setAttribute('is_defined_test', true);
            $this->changeIgnoreStrictCheck($node);
        } elseif ($node instanceof BlockReferenceExpression) {
            $node->setAttribute('is_defined_test', true);
        } elseif ($node instanceof FunctionExpression &&
'constant' === $node->getAttribute('name')) {
            $node->setAttribute('is_defined_test', true);
        } elseif ($node instanceof ConstantExpression || $node instanceof
ArrayExpression) {
            $node = new ConstantExpression(true,
$node->getTemplateLine());
        } else {
            throw new SyntaxError('The "defined" test only
works with simple variables.', $lineno);
        }

        parent::__construct($node, $name, $arguments, $lineno);
    }

    protected function changeIgnoreStrictCheck(GetAttrExpression $node)
    {
        $node->setAttribute('ignore_strict_check', true);

        if ($node->getNode('node') instanceof
GetAttrExpression) {
           
$this->changeIgnoreStrictCheck($node->getNode('node'));
        }
    }

    public function compile(Compiler $compiler)
    {
        $compiler->subcompile($this->getNode('node'));
    }
}

class_alias('Twig\Node\Expression\Test\DefinedTest',
'Twig_Node_Expression_Test_Defined');
vendor/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php000064400000001565151166614750020360
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Test;

use Twig\Compiler;
use Twig\Node\Expression\TestExpression;

/**
 * Checks if a variable is divisible by a number.
 *
 *  {% if loop.index is divisible by(3) %}
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class DivisiblebyTest extends TestExpression
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('(0 == ')
            ->subcompile($this->getNode('node'))
            ->raw(' % ')
           
->subcompile($this->getNode('arguments')->getNode(0))
            ->raw(')')
        ;
    }
}

class_alias('Twig\Node\Expression\Test\DivisiblebyTest',
'Twig_Node_Expression_Test_Divisibleby');
vendor/twig/twig/src/Node/Expression/Test/EvenTest.php000064400000001367151166614750017010
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Test;

use Twig\Compiler;
use Twig\Node\Expression\TestExpression;

/**
 * Checks if a number is even.
 *
 *  {{ var is even }}
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class EvenTest extends TestExpression
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('(')
            ->subcompile($this->getNode('node'))
            ->raw(' % 2 == 0')
            ->raw(')')
        ;
    }
}

class_alias('Twig\Node\Expression\Test\EvenTest',
'Twig_Node_Expression_Test_Even');
vendor/twig/twig/src/Node/Expression/Test/NullTest.php000064400000001345151166614750017021
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Test;

use Twig\Compiler;
use Twig\Node\Expression\TestExpression;

/**
 * Checks that a variable is null.
 *
 *  {{ var is none }}
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class NullTest extends TestExpression
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('(null === ')
            ->subcompile($this->getNode('node'))
            ->raw(')')
        ;
    }
}

class_alias('Twig\Node\Expression\Test\NullTest',
'Twig_Node_Expression_Test_Null');
vendor/twig/twig/src/Node/Expression/Test/OddTest.php000064400000001362151166614750016614
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Test;

use Twig\Compiler;
use Twig\Node\Expression\TestExpression;

/**
 * Checks if a number is odd.
 *
 *  {{ var is odd }}
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class OddTest extends TestExpression
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('(')
            ->subcompile($this->getNode('node'))
            ->raw(' % 2 == 1')
            ->raw(')')
        ;
    }
}

class_alias('Twig\Node\Expression\Test\OddTest',
'Twig_Node_Expression_Test_Odd');
vendor/twig/twig/src/Node/Expression/Test/SameasTest.php000064400000001504151166614750017315
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Test;

use Twig\Compiler;
use Twig\Node\Expression\TestExpression;

/**
 * Checks if a variable is the same as another one (=== in PHP).
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class SameasTest extends TestExpression
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->raw('(')
            ->subcompile($this->getNode('node'))
            ->raw(' === ')
           
->subcompile($this->getNode('arguments')->getNode(0))
            ->raw(')')
        ;
    }
}

class_alias('Twig\Node\Expression\Test\SameasTest',
'Twig_Node_Expression_Test_Sameas');
vendor/twig/twig/src/Node/Expression/TestExpression.php000064400000002703151166614750017326
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression;

use Twig\Compiler;
use Twig\TwigTest;

class TestExpression extends CallExpression
{
    public function __construct(\Twig_NodeInterface $node, $name,
\Twig_NodeInterface $arguments = null, $lineno)
    {
        $nodes = ['node' => $node];
        if (null !== $arguments) {
            $nodes['arguments'] = $arguments;
        }

        parent::__construct($nodes, ['name' => $name],
$lineno);
    }

    public function compile(Compiler $compiler)
    {
        $name = $this->getAttribute('name');
        $test = $compiler->getEnvironment()->getTest($name);

        $this->setAttribute('name', $name);
        $this->setAttribute('type', 'test');
        $this->setAttribute('thing', $test);
        if ($test instanceof TwigTest) {
            $this->setAttribute('arguments',
$test->getArguments());
        }
        if ($test instanceof \Twig_TestCallableInterface || $test
instanceof TwigTest) {
            $this->setAttribute('callable',
$test->getCallable());
        }
        if ($test instanceof TwigTest) {
            $this->setAttribute('is_variadic',
$test->isVariadic());
        }

        $this->compileCallable($compiler);
    }
}

class_alias('Twig\Node\Expression\TestExpression',
'Twig_Node_Expression_Test');
vendor/twig/twig/src/Node/Expression/Unary/AbstractUnary.php000064400000001532151166614750020206
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Unary;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;

abstract class AbstractUnary extends AbstractExpression
{
    public function __construct(\Twig_NodeInterface $node, $lineno)
    {
        parent::__construct(['node' => $node], [], $lineno);
    }

    public function compile(Compiler $compiler)
    {
        $compiler->raw(' ');
        $this->operator($compiler);
        $compiler->subcompile($this->getNode('node'));
    }

    abstract public function operator(Compiler $compiler);
}

class_alias('Twig\Node\Expression\Unary\AbstractUnary',
'Twig_Node_Expression_Unary');
vendor/twig/twig/src/Node/Expression/Unary/NegUnary.php000064400000000765151166614750017163
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Unary;

use Twig\Compiler;

class NegUnary extends AbstractUnary
{
    public function operator(Compiler $compiler)
    {
        $compiler->raw('-');
    }
}

class_alias('Twig\Node\Expression\Unary\NegUnary',
'Twig_Node_Expression_Unary_Neg');
vendor/twig/twig/src/Node/Expression/Unary/NotUnary.php000064400000000765151166614750017212
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Unary;

use Twig\Compiler;

class NotUnary extends AbstractUnary
{
    public function operator(Compiler $compiler)
    {
        $compiler->raw('!');
    }
}

class_alias('Twig\Node\Expression\Unary\NotUnary',
'Twig_Node_Expression_Unary_Not');
vendor/twig/twig/src/Node/Expression/Unary/PosUnary.php000064400000000765151166614750017213
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node\Expression\Unary;

use Twig\Compiler;

class PosUnary extends AbstractUnary
{
    public function operator(Compiler $compiler)
    {
        $compiler->raw('+');
    }
}

class_alias('Twig\Node\Expression\Unary\PosUnary',
'Twig_Node_Expression_Unary_Pos');
vendor/twig/twig/src/Node/FlushNode.php000064400000001261151166614750014055
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * Represents a flush node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class FlushNode extends Node
{
    public function __construct($lineno, $tag)
    {
        parent::__construct([], [], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write("flush();\n")
        ;
    }
}

class_alias('Twig\Node\FlushNode', 'Twig_Node_Flush');
vendor/twig/twig/src/Node/ForLoopNode.php000064400000003061151166614750014354
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * Internal node used by the for node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ForLoopNode extends Node
{
    public function __construct($lineno, $tag = null)
    {
        parent::__construct([], ['with_loop' => false,
'ifexpr' => false, 'else' => false], $lineno,
$tag);
    }

    public function compile(Compiler $compiler)
    {
        if ($this->getAttribute('else')) {
            $compiler->write("\$context['_iterated'] =
true;\n");
        }

        if ($this->getAttribute('with_loop')) {
            $compiler
               
->write("++\$context['loop']['index0'];\n")
               
->write("++\$context['loop']['index'];\n")
               
->write("\$context['loop']['first'] =
false;\n")
            ;

            if (!$this->getAttribute('ifexpr')) {
                $compiler
                    ->write("if
(isset(\$context['loop']['length'])) {\n")
                    ->indent()
                   
->write("--\$context['loop']['revindex0'];\n")
                   
->write("--\$context['loop']['revindex'];\n")
                   
->write("\$context['loop']['last'] = 0 ===
\$context['loop']['revindex0'];\n")
                    ->outdent()
                    ->write("}\n")
                ;
            }
        }
    }
}

class_alias('Twig\Node\ForLoopNode',
'Twig_Node_ForLoop');
vendor/twig/twig/src/Node/ForNode.php000064400000010365151166614750013527
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\AssignNameExpression;

/**
 * Represents a for node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ForNode extends Node
{
    protected $loop;

    public function __construct(AssignNameExpression $keyTarget,
AssignNameExpression $valueTarget, AbstractExpression $seq,
AbstractExpression $ifexpr = null, \Twig_NodeInterface $body,
\Twig_NodeInterface $else = null, $lineno, $tag = null)
    {
        $body = new Node([$body, $this->loop = new ForLoopNode($lineno,
$tag)]);

        if (null !== $ifexpr) {
            $body = new IfNode(new Node([$ifexpr, $body]), null, $lineno,
$tag);
        }

        $nodes = ['key_target' => $keyTarget,
'value_target' => $valueTarget, 'seq' => $seq,
'body' => $body];
        if (null !== $else) {
            $nodes['else'] = $else;
        }

        parent::__construct($nodes, ['with_loop' => true,
'ifexpr' => null !== $ifexpr], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write("\$context['_parent'] =
\$context;\n")
            ->write("\$context['_seq'] =
twig_ensure_traversable(")
            ->subcompile($this->getNode('seq'))
            ->raw(");\n")
        ;

        if ($this->hasNode('else')) {
            $compiler->write("\$context['_iterated'] =
false;\n");
        }

        if ($this->getAttribute('with_loop')) {
            $compiler
                ->write("\$context['loop'] = [\n")
                ->write("  'parent' =>
\$context['_parent'],\n")
                ->write("  'index0' => 0,\n")
                ->write("  'index'  => 1,\n")
                ->write("  'first'  => true,\n")
                ->write("];\n")
            ;

            if (!$this->getAttribute('ifexpr')) {
                $compiler
                    ->write("if
(is_array(\$context['_seq']) ||
(is_object(\$context['_seq']) &&
\$context['_seq'] instanceof \Countable)) {\n")
                    ->indent()
                    ->write("\$length =
count(\$context['_seq']);\n")
                   
->write("\$context['loop']['revindex0'] =
\$length - 1;\n")
                   
->write("\$context['loop']['revindex'] =
\$length;\n")
                   
->write("\$context['loop']['length'] =
\$length;\n")
                   
->write("\$context['loop']['last'] = 1 ===
\$length;\n")
                    ->outdent()
                    ->write("}\n")
                ;
            }
        }

        $this->loop->setAttribute('else',
$this->hasNode('else'));
        $this->loop->setAttribute('with_loop',
$this->getAttribute('with_loop'));
        $this->loop->setAttribute('ifexpr',
$this->getAttribute('ifexpr'));

        $compiler
            ->write("foreach (\$context['_seq'] as
")
            ->subcompile($this->getNode('key_target'))
            ->raw(' => ')
            ->subcompile($this->getNode('value_target'))
            ->raw(") {\n")
            ->indent()
            ->subcompile($this->getNode('body'))
            ->outdent()
            ->write("}\n")
        ;

        if ($this->hasNode('else')) {
            $compiler
                ->write("if (!\$context['_iterated'])
{\n")
                ->indent()
                ->subcompile($this->getNode('else'))
                ->outdent()
                ->write("}\n")
            ;
        }

        $compiler->write("\$_parent =
\$context['_parent'];\n");

        // remove some "private" loop variables (needed for
nested loops)
        $compiler->write('unset($context[\'_seq\'],
$context[\'_iterated\'],
$context[\''.$this->getNode('key_target')->getAttribute('name').'\'],
$context[\''.$this->getNode('value_target')->getAttribute('name').'\'],
$context[\'_parent\'],
$context[\'loop\']);'."\n");

        // keep the values set in the inner context for variables defined
in the outer context
        $compiler->write("\$context =
array_intersect_key(\$context, \$_parent) + \$_parent;\n");
    }
}

class_alias('Twig\Node\ForNode', 'Twig_Node_For');
vendor/twig/twig/src/Node/IfNode.php000064400000003273151166614750013337
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * Represents an if node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class IfNode extends Node
{
    public function __construct(\Twig_NodeInterface $tests,
\Twig_NodeInterface $else = null, $lineno, $tag = null)
    {
        $nodes = ['tests' => $tests];
        if (null !== $else) {
            $nodes['else'] = $else;
        }

        parent::__construct($nodes, [], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler->addDebugInfo($this);
        for ($i = 0, $count = \count($this->getNode('tests'));
$i < $count; $i += 2) {
            if ($i > 0) {
                $compiler
                    ->outdent()
                    ->write('} elseif (')
                ;
            } else {
                $compiler
                    ->write('if (')
                ;
            }

            $compiler
               
->subcompile($this->getNode('tests')->getNode($i))
                ->raw(") {\n")
                ->indent()
               
->subcompile($this->getNode('tests')->getNode($i + 1))
            ;
        }

        if ($this->hasNode('else')) {
            $compiler
                ->outdent()
                ->write("} else {\n")
                ->indent()
                ->subcompile($this->getNode('else'))
            ;
        }

        $compiler
            ->outdent()
            ->write("}\n");
    }
}

class_alias('Twig\Node\IfNode', 'Twig_Node_If');
vendor/twig/twig/src/Node/ImportNode.php000064400000002706151166614750014253
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\NameExpression;

/**
 * Represents an import node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ImportNode extends Node
{
    public function __construct(AbstractExpression $expr,
AbstractExpression $var, $lineno, $tag = null)
    {
        parent::__construct(['expr' => $expr, 'var'
=> $var], [], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('')
            ->subcompile($this->getNode('var'))
            ->raw(' = ')
        ;

        if ($this->getNode('expr') instanceof NameExpression
&& '_self' ===
$this->getNode('expr')->getAttribute('name')) {
            $compiler->raw('$this');
        } else {
            $compiler
                ->raw('$this->loadTemplate(')
                ->subcompile($this->getNode('expr'))
                ->raw(', ')
                ->repr($this->getTemplateName())
                ->raw(', ')
                ->repr($this->getTemplateLine())
                ->raw(')->unwrap()')
            ;
        }

        $compiler->raw(";\n");
    }
}

class_alias('Twig\Node\ImportNode',
'Twig_Node_Import');
vendor/twig/twig/src/Node/IncludeNode.php000064400000006174151166614750014367
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;

/**
 * Represents an include node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class IncludeNode extends Node implements NodeOutputInterface
{
    public function __construct(AbstractExpression $expr,
AbstractExpression $variables = null, $only = false, $ignoreMissing =
false, $lineno, $tag = null)
    {
        $nodes = ['expr' => $expr];
        if (null !== $variables) {
            $nodes['variables'] = $variables;
        }

        parent::__construct($nodes, ['only' => (bool) $only,
'ignore_missing' => (bool) $ignoreMissing], $lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        if ($this->getAttribute('ignore_missing')) {
            $template = $compiler->getVarName();

            $compiler
                ->write(sprintf("$%s = null;\n", $template))
                ->write("try {\n")
                ->indent()
                ->write(sprintf('$%s = ', $template))
            ;

            $this->addGetTemplate($compiler);

            $compiler
                ->raw(";\n")
                ->outdent()
                ->write("} catch (LoaderError \$e) {\n")
                ->indent()
                ->write("// ignore missing template\n")
                ->outdent()
                ->write("}\n")
                ->write(sprintf("if ($%s) {\n", $template))
                ->indent()
                ->write(sprintf('$%s->display(',
$template))
            ;
            $this->addTemplateArguments($compiler);
            $compiler
                ->raw(");\n")
                ->outdent()
                ->write("}\n")
            ;
        } else {
            $this->addGetTemplate($compiler);
            $compiler->raw('->display(');
            $this->addTemplateArguments($compiler);
            $compiler->raw(");\n");
        }
    }

    protected function addGetTemplate(Compiler $compiler)
    {
        $compiler
            ->write('$this->loadTemplate(')
            ->subcompile($this->getNode('expr'))
            ->raw(', ')
            ->repr($this->getTemplateName())
            ->raw(', ')
            ->repr($this->getTemplateLine())
            ->raw(')')
        ;
    }

    protected function addTemplateArguments(Compiler $compiler)
    {
        if (!$this->hasNode('variables')) {
            $compiler->raw(false ===
$this->getAttribute('only') ? '$context' :
'[]');
        } elseif (false === $this->getAttribute('only')) {
            $compiler
                ->raw('twig_array_merge($context, ')
                ->subcompile($this->getNode('variables'))
                ->raw(')')
            ;
        } else {
            $compiler->raw('twig_to_array(');
           
$compiler->subcompile($this->getNode('variables'));
            $compiler->raw(')');
        }
    }
}

class_alias('Twig\Node\IncludeNode',
'Twig_Node_Include');
vendor/twig/twig/src/Node/MacroNode.php000064400000007425151166614750014045
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Error\SyntaxError;

/**
 * Represents a macro node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class MacroNode extends Node
{
    const VARARGS_NAME = 'varargs';

    public function __construct($name, \Twig_NodeInterface $body,
\Twig_NodeInterface $arguments, $lineno, $tag = null)
    {
        foreach ($arguments as $argumentName => $argument) {
            if (self::VARARGS_NAME === $argumentName) {
                throw new SyntaxError(sprintf('The argument
"%s" in macro "%s" cannot be defined because the
variable "%s" is reserved for arbitrary arguments.',
self::VARARGS_NAME, $name, self::VARARGS_NAME),
$argument->getTemplateLine(), $argument->getSourceContext());
            }
        }

        parent::__construct(['body' => $body,
'arguments' => $arguments], ['name' => $name],
$lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write(sprintf('public function get%s(',
$this->getAttribute('name')))
        ;

        $count = \count($this->getNode('arguments'));
        $pos = 0;
        foreach ($this->getNode('arguments') as $name =>
$default) {
            $compiler
                ->raw('$__'.$name.'__ = ')
                ->subcompile($default)
            ;

            if (++$pos < $count) {
                $compiler->raw(', ');
            }
        }

        if (\PHP_VERSION_ID >= 50600) {
            if ($count) {
                $compiler->raw(', ');
            }

            $compiler->raw('...$__varargs__');
        }

        $compiler
            ->raw(")\n")
            ->write("{\n")
            ->indent()
        ;

        $compiler
            ->write("\$context =
\$this->env->mergeGlobals([\n")
            ->indent()
        ;

        foreach ($this->getNode('arguments') as $name =>
$default) {
            $compiler
                ->write('')
                ->string($name)
                ->raw(' => $__'.$name.'__')
                ->raw(",\n")
            ;
        }

        $compiler
            ->write('')
            ->string(self::VARARGS_NAME)
            ->raw(' => ')
        ;

        if (\PHP_VERSION_ID >= 50600) {
            $compiler->raw("\$__varargs__,\n");
        } else {
            $compiler
                ->raw('func_num_args() > ')
                ->repr($count)
                ->raw(' ? array_slice(func_get_args(), ')
                ->repr($count)
                ->raw(") : [],\n")
            ;
        }

        $compiler
            ->outdent()
            ->write("]);\n\n")
            ->write("\$blocks = [];\n\n")
        ;
        if ($compiler->getEnvironment()->isDebug()) {
            $compiler->write("ob_start();\n");
        } else {
            $compiler->write("ob_start(function () { return
''; });\n");
        }
        $compiler
            ->write("try {\n")
            ->indent()
            ->subcompile($this->getNode('body'))
            ->outdent()
            ->write("} catch (\Exception \$e) {\n")
            ->indent()
            ->write("ob_end_clean();\n\n")
            ->write("throw \$e;\n")
            ->outdent()
            ->write("} catch (\Throwable \$e) {\n")
            ->indent()
            ->write("ob_end_clean();\n\n")
            ->write("throw \$e;\n")
            ->outdent()
            ->write("}\n\n")
            ->write("return ('' === \$tmp =
ob_get_clean()) ? '' : new Markup(\$tmp,
\$this->env->getCharset());\n")
            ->outdent()
            ->write("}\n\n")
        ;
    }
}

class_alias('Twig\Node\MacroNode', 'Twig_Node_Macro');
vendor/twig/twig/src/Node/ModuleNode.php000064400000037414151166614750014232
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Source;

/**
 * Represents a module node.
 *
 * Consider this class as being final. If you need to customize the
behavior of
 * the generated class, consider adding nodes to the following nodes:
display_start,
 * display_end, constructor_start, constructor_end, and class_end.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class ModuleNode extends Node
{
    public function __construct(\Twig_NodeInterface $body,
AbstractExpression $parent = null, \Twig_NodeInterface $blocks,
\Twig_NodeInterface $macros, \Twig_NodeInterface $traits,
$embeddedTemplates, $name, $source = '')
    {
        if (!$name instanceof Source) {
            @trigger_error(sprintf('Passing a string as the $name
argument of %s() is deprecated since version 1.27. Pass a \Twig\Source
instance instead.', __METHOD__), E_USER_DEPRECATED);
            $source = new Source($source, $name);
        } else {
            $source = $name;
        }

        $nodes = [
            'body' => $body,
            'blocks' => $blocks,
            'macros' => $macros,
            'traits' => $traits,
            'display_start' => new Node(),
            'display_end' => new Node(),
            'constructor_start' => new Node(),
            'constructor_end' => new Node(),
            'class_end' => new Node(),
        ];
        if (null !== $parent) {
            $nodes['parent'] = $parent;
        }

        // embedded templates are set as attributes so that they are only
visited once by the visitors
        parent::__construct($nodes, [
            // source to be remove in 2.0
            'source' => $source->getCode(),
            // filename to be remove in 2.0 (use getTemplateName() instead)
            'filename' => $source->getName(),
            'index' => null,
            'embedded_templates' => $embeddedTemplates,
        ], 1);

        // populate the template name of all node children
        $this->setTemplateName($source->getName());
        $this->setSourceContext($source);
    }

    public function setIndex($index)
    {
        $this->setAttribute('index', $index);
    }

    public function compile(Compiler $compiler)
    {
        $this->compileTemplate($compiler);

        foreach ($this->getAttribute('embedded_templates') as
$template) {
            $compiler->subcompile($template);
        }
    }

    protected function compileTemplate(Compiler $compiler)
    {
        if (!$this->getAttribute('index')) {
            $compiler->write('<?php');
        }

        $this->compileClassHeader($compiler);

        if (
            \count($this->getNode('blocks'))
            || \count($this->getNode('traits'))
            || !$this->hasNode('parent')
            || $this->getNode('parent') instanceof
ConstantExpression
            || \count($this->getNode('constructor_start'))
            || \count($this->getNode('constructor_end'))
        ) {
            $this->compileConstructor($compiler);
        }

        $this->compileGetParent($compiler);

        $this->compileDisplay($compiler);

        $compiler->subcompile($this->getNode('blocks'));

        $this->compileMacros($compiler);

        $this->compileGetTemplateName($compiler);

        $this->compileIsTraitable($compiler);

        $this->compileDebugInfo($compiler);

        $this->compileGetSource($compiler);

        $this->compileGetSourceContext($compiler);

        $this->compileClassFooter($compiler);
    }

    protected function compileGetParent(Compiler $compiler)
    {
        if (!$this->hasNode('parent')) {
            return;
        }
        $parent = $this->getNode('parent');

        $compiler
            ->write("protected function doGetParent(array
\$context)\n", "{\n")
            ->indent()
            ->addDebugInfo($parent)
            ->write('return ')
        ;

        if ($parent instanceof ConstantExpression) {
            $compiler->subcompile($parent);
        } else {
            $compiler
                ->raw('$this->loadTemplate(')
                ->subcompile($parent)
                ->raw(', ')
                ->repr($this->getSourceContext()->getName())
                ->raw(', ')
                ->repr($parent->getTemplateLine())
                ->raw(')')
            ;
        }

        $compiler
            ->raw(";\n")
            ->outdent()
            ->write("}\n\n")
        ;
    }

    protected function compileClassHeader(Compiler $compiler)
    {
        $compiler
            ->write("\n\n")
        ;
        if (!$this->getAttribute('index')) {
            $compiler
                ->write("use Twig\Environment;\n")
                ->write("use Twig\Error\LoaderError;\n")
                ->write("use Twig\Error\RuntimeError;\n")
                ->write("use Twig\Markup;\n")
                ->write("use Twig\Sandbox\SecurityError;\n")
                ->write("use
Twig\Sandbox\SecurityNotAllowedTagError;\n")
                ->write("use
Twig\Sandbox\SecurityNotAllowedFilterError;\n")
                ->write("use
Twig\Sandbox\SecurityNotAllowedFunctionError;\n")
                ->write("use Twig\Source;\n")
                ->write("use Twig\Template;\n\n")
            ;
        }
        $compiler
            // if the template name contains */, add a blank to avoid a PHP
parse error
            ->write('/* '.str_replace('*/', '*
/', $this->getSourceContext()->getName())." */\n")
            ->write('class
'.$compiler->getEnvironment()->getTemplateClass($this->getSourceContext()->getName(),
$this->getAttribute('index')))
            ->raw(sprintf(" extends %s\n",
$compiler->getEnvironment()->getBaseTemplateClass()))
            ->write("{\n")
            ->indent()
        ;
    }

    protected function compileConstructor(Compiler $compiler)
    {
        $compiler
            ->write("public function __construct(Environment
\$env)\n", "{\n")
            ->indent()
           
->subcompile($this->getNode('constructor_start'))
            ->write("parent::__construct(\$env);\n\n")
        ;

        // parent
        if (!$this->hasNode('parent')) {
            $compiler->write("\$this->parent =
false;\n\n");
        }

        $countTraits = \count($this->getNode('traits'));
        if ($countTraits) {
            // traits
            foreach ($this->getNode('traits') as $i =>
$trait) {
                $this->compileLoadTemplate($compiler,
$trait->getNode('template'), sprintf('$_trait_%s',
$i));

                $node = $trait->getNode('template');
                $compiler
                    ->addDebugInfo($node)
                    ->write(sprintf("if
(!\$_trait_%s->isTraitable()) {\n", $i))
                    ->indent()
                    ->write("throw new RuntimeError('Template
\"'.")
                   
->subcompile($trait->getNode('template'))
                    ->raw(".'\" cannot be used as a
trait.', ")
                    ->repr($node->getTemplateLine())
                    ->raw(",
\$this->getSourceContext());\n")
                    ->outdent()
                    ->write("}\n")
                    ->write(sprintf("\$_trait_%s_blocks =
\$_trait_%s->getBlocks();\n\n", $i, $i))
                ;

                foreach ($trait->getNode('targets') as $key
=> $value) {
                    $compiler
                        ->write(sprintf('if
(!isset($_trait_%s_blocks[', $i))
                        ->string($key)
                        ->raw("])) {\n")
                        ->indent()
                        ->write("throw new
RuntimeError(sprintf('Block ")
                        ->string($key)
                        ->raw(' is not defined in trait ')
                       
->subcompile($trait->getNode('template'))
                        ->raw(".'), ")
                        ->repr($node->getTemplateLine())
                        ->raw(",
\$this->getSourceContext());\n")
                        ->outdent()
                        ->write("}\n\n")

                        ->write(sprintf('$_trait_%s_blocks[',
$i))
                        ->subcompile($value)
                        ->raw(sprintf('] =
$_trait_%s_blocks[', $i))
                        ->string($key)
                        ->raw(sprintf('];
unset($_trait_%s_blocks[', $i))
                        ->string($key)
                        ->raw("]);\n\n")
                    ;
                }
            }

            if ($countTraits > 1) {
                $compiler
                    ->write("\$this->traits =
array_merge(\n")
                    ->indent()
                ;

                for ($i = 0; $i < $countTraits; ++$i) {
                    $compiler
                       
->write(sprintf('$_trait_%s_blocks'.($i == $countTraits - 1 ?
'' : ',')."\n", $i))
                    ;
                }

                $compiler
                    ->outdent()
                    ->write(");\n\n")
                ;
            } else {
                $compiler
                    ->write("\$this->traits =
\$_trait_0_blocks;\n\n")
                ;
            }

            $compiler
                ->write("\$this->blocks = array_merge(\n")
                ->indent()
                ->write("\$this->traits,\n")
                ->write("[\n")
            ;
        } else {
            $compiler
                ->write("\$this->blocks = [\n")
            ;
        }

        // blocks
        $compiler
            ->indent()
        ;

        foreach ($this->getNode('blocks') as $name =>
$node) {
            $compiler
                ->write(sprintf("'%s' => [\$this,
'block_%s'],\n", $name, $name))
            ;
        }

        if ($countTraits) {
            $compiler
                ->outdent()
                ->write("]\n")
                ->outdent()
                ->write(");\n")
            ;
        } else {
            $compiler
                ->outdent()
                ->write("];\n")
            ;
        }

        $compiler
            ->subcompile($this->getNode('constructor_end'))
            ->outdent()
            ->write("}\n\n")
        ;
    }

    protected function compileDisplay(Compiler $compiler)
    {
        $compiler
            ->write("protected function doDisplay(array \$context,
array \$blocks = [])\n", "{\n")
            ->indent()
            ->subcompile($this->getNode('display_start'))
            ->subcompile($this->getNode('body'))
        ;

        if ($this->hasNode('parent')) {
            $parent = $this->getNode('parent');

            $compiler->addDebugInfo($parent);
            if ($parent instanceof ConstantExpression) {
                $compiler
                    ->write('$this->parent =
$this->loadTemplate(')
                    ->subcompile($parent)
                    ->raw(', ')
                    ->repr($this->getSourceContext()->getName())
                    ->raw(', ')
                    ->repr($parent->getTemplateLine())
                    ->raw(");\n")
                ;
                $compiler->write('$this->parent');
            } else {
               
$compiler->write('$this->getParent($context)');
            }
            $compiler->raw("->display(\$context,
array_merge(\$this->blocks, \$blocks));\n");
        }

        $compiler
            ->subcompile($this->getNode('display_end'))
            ->outdent()
            ->write("}\n\n")
        ;
    }

    protected function compileClassFooter(Compiler $compiler)
    {
        $compiler
            ->subcompile($this->getNode('class_end'))
            ->outdent()
            ->write("}\n")
        ;
    }

    protected function compileMacros(Compiler $compiler)
    {
        $compiler->subcompile($this->getNode('macros'));
    }

    protected function compileGetTemplateName(Compiler $compiler)
    {
        $compiler
            ->write("public function getTemplateName()\n",
"{\n")
            ->indent()
            ->write('return ')
            ->repr($this->getSourceContext()->getName())
            ->raw(";\n")
            ->outdent()
            ->write("}\n\n")
        ;
    }

    protected function compileIsTraitable(Compiler $compiler)
    {
        // A template can be used as a trait if:
        //   * it has no parent
        //   * it has no macros
        //   * it has no body
        //
        // Put another way, a template can be used as a trait if it
        // only contains blocks and use statements.
        $traitable = !$this->hasNode('parent') && 0
=== \count($this->getNode('macros'));
        if ($traitable) {
            if ($this->getNode('body') instanceof BodyNode) {
                $nodes =
$this->getNode('body')->getNode(0);
            } else {
                $nodes = $this->getNode('body');
            }

            if (!\count($nodes)) {
                $nodes = new Node([$nodes]);
            }

            foreach ($nodes as $node) {
                if (!\count($node)) {
                    continue;
                }

                if ($node instanceof TextNode &&
ctype_space($node->getAttribute('data'))) {
                    continue;
                }

                if ($node instanceof BlockReferenceNode) {
                    continue;
                }

                $traitable = false;
                break;
            }
        }

        if ($traitable) {
            return;
        }

        $compiler
            ->write("public function isTraitable()\n",
"{\n")
            ->indent()
            ->write(sprintf("return %s;\n", $traitable ?
'true' : 'false'))
            ->outdent()
            ->write("}\n\n")
        ;
    }

    protected function compileDebugInfo(Compiler $compiler)
    {
        $compiler
            ->write("public function getDebugInfo()\n",
"{\n")
            ->indent()
            ->write(sprintf("return %s;\n",
str_replace("\n", '',
var_export(array_reverse($compiler->getDebugInfo(), true), true))))
            ->outdent()
            ->write("}\n\n")
        ;
    }

    protected function compileGetSource(Compiler $compiler)
    {
        $compiler
            ->write("/** @deprecated since 1.27 (to be removed in
2.0). Use getSourceContext() instead */\n")
            ->write("public function getSource()\n",
"{\n")
            ->indent()
            ->write("@trigger_error('The
'.__METHOD__.' method is deprecated since version 1.27 and will
be removed in 2.0. Use getSourceContext() instead.',
E_USER_DEPRECATED);\n\n")
            ->write('return
$this->getSourceContext()->getCode();')
            ->raw("\n")
            ->outdent()
            ->write("}\n\n")
        ;
    }

    protected function compileGetSourceContext(Compiler $compiler)
    {
        $compiler
            ->write("public function getSourceContext()\n",
"{\n")
            ->indent()
            ->write('return new Source(')
            ->string($compiler->getEnvironment()->isDebug() ?
$this->getSourceContext()->getCode() : '')
            ->raw(', ')
            ->string($this->getSourceContext()->getName())
            ->raw(', ')
            ->string($this->getSourceContext()->getPath())
            ->raw(");\n")
            ->outdent()
            ->write("}\n")
        ;
    }

    protected function compileLoadTemplate(Compiler $compiler, $node, $var)
    {
        if ($node instanceof ConstantExpression) {
            $compiler
                ->write(sprintf('%s =
$this->loadTemplate(', $var))
                ->subcompile($node)
                ->raw(', ')
                ->repr($node->getTemplateName())
                ->raw(', ')
                ->repr($node->getTemplateLine())
                ->raw(");\n")
            ;
        } else {
            throw new \LogicException('Trait templates can only be
constant nodes.');
        }
    }
}

class_alias('Twig\Node\ModuleNode',
'Twig_Node_Module');
vendor/twig/twig/src/Node/Node.php000064400000016545151166614750013066
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Source;

/**
 * Represents a node in the AST.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Node implements \Twig_NodeInterface
{
    protected $nodes;
    protected $attributes;
    protected $lineno;
    protected $tag;

    private $name;
    private $sourceContext;

    /**
     * @param array  $nodes      An array of named nodes
     * @param array  $attributes An array of attributes (should not be
nodes)
     * @param int    $lineno     The line number
     * @param string $tag        The tag name associated with the Node
     */
    public function __construct(array $nodes = [], array $attributes = [],
$lineno = 0, $tag = null)
    {
        foreach ($nodes as $name => $node) {
            if (!$node instanceof \Twig_NodeInterface) {
                @trigger_error(sprintf('Using "%s" for the
value of node "%s" of "%s" is deprecated since version
1.25 and will be removed in 2.0.', \is_object($node) ?
\get_class($node) : (null === $node ? 'null' : \gettype($node)),
$name, \get_class($this)), E_USER_DEPRECATED);
            }
        }
        $this->nodes = $nodes;
        $this->attributes = $attributes;
        $this->lineno = $lineno;
        $this->tag = $tag;
    }

    public function __toString()
    {
        $attributes = [];
        foreach ($this->attributes as $name => $value) {
            $attributes[] = sprintf('%s: %s', $name,
str_replace("\n", '', var_export($value, true)));
        }

        $repr = [\get_class($this).'('.implode(', ',
$attributes)];

        if (\count($this->nodes)) {
            foreach ($this->nodes as $name => $node) {
                $len = \strlen($name) + 4;
                $noderepr = [];
                foreach (explode("\n", (string) $node) as $line)
{
                    $noderepr[] = str_repeat(' ', $len).$line;
                }

                $repr[] = sprintf('  %s: %s', $name,
ltrim(implode("\n", $noderepr)));
            }

            $repr[] = ')';
        } else {
            $repr[0] .= ')';
        }

        return implode("\n", $repr);
    }

    /**
     * @deprecated since 1.16.1 (to be removed in 2.0)
     */
    public function toXml($asDom = false)
    {
        @trigger_error(sprintf('%s is deprecated since version 1.16.1
and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED);

        $dom = new \DOMDocument('1.0', 'UTF-8');
        $dom->formatOutput = true;
        $dom->appendChild($xml =
$dom->createElement('twig'));

        $xml->appendChild($node =
$dom->createElement('node'));
        $node->setAttribute('class', \get_class($this));

        foreach ($this->attributes as $name => $value) {
            $node->appendChild($attribute =
$dom->createElement('attribute'));
            $attribute->setAttribute('name', $name);
            $attribute->appendChild($dom->createTextNode($value));
        }

        foreach ($this->nodes as $name => $n) {
            if (null === $n) {
                continue;
            }

            $child =
$n->toXml(true)->getElementsByTagName('node')->item(0);
            $child = $dom->importNode($child, true);
            $child->setAttribute('name', $name);

            $node->appendChild($child);
        }

        return $asDom ? $dom : $dom->saveXML();
    }

    public function compile(Compiler $compiler)
    {
        foreach ($this->nodes as $node) {
            $node->compile($compiler);
        }
    }

    public function getTemplateLine()
    {
        return $this->lineno;
    }

    /**
     * @deprecated since 1.27 (to be removed in 2.0)
     */
    public function getLine()
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since version 1.27 and will be removed in 2.0. Use
getTemplateLine() instead.', E_USER_DEPRECATED);

        return $this->lineno;
    }

    public function getNodeTag()
    {
        return $this->tag;
    }

    /**
     * @return bool
     */
    public function hasAttribute($name)
    {
        return \array_key_exists($name, $this->attributes);
    }

    /**
     * @return mixed
     */
    public function getAttribute($name)
    {
        if (!\array_key_exists($name, $this->attributes)) {
            throw new \LogicException(sprintf('Attribute
"%s" does not exist for Node "%s".', $name,
\get_class($this)));
        }

        return $this->attributes[$name];
    }

    /**
     * @param string $name
     * @param mixed  $value
     */
    public function setAttribute($name, $value)
    {
        $this->attributes[$name] = $value;
    }

    public function removeAttribute($name)
    {
        unset($this->attributes[$name]);
    }

    /**
     * @return bool
     */
    public function hasNode($name)
    {
        return \array_key_exists($name, $this->nodes);
    }

    /**
     * @return Node
     */
    public function getNode($name)
    {
        if (!\array_key_exists($name, $this->nodes)) {
            throw new \LogicException(sprintf('Node "%s"
does not exist for Node "%s".', $name, \get_class($this)));
        }

        return $this->nodes[$name];
    }

    public function setNode($name, $node = null)
    {
        if (!$node instanceof \Twig_NodeInterface) {
            @trigger_error(sprintf('Using "%s" for the value
of node "%s" of "%s" is deprecated since version 1.25
and will be removed in 2.0.', \is_object($node) ? \get_class($node) :
(null === $node ? 'null' : \gettype($node)), $name,
\get_class($this)), E_USER_DEPRECATED);
        }

        $this->nodes[$name] = $node;
    }

    public function removeNode($name)
    {
        unset($this->nodes[$name]);
    }

    public function count()
    {
        return \count($this->nodes);
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->nodes);
    }

    public function setTemplateName($name)
    {
        $this->name = $name;
        foreach ($this->nodes as $node) {
            if (null !== $node) {
                $node->setTemplateName($name);
            }
        }
    }

    public function getTemplateName()
    {
        return $this->name;
    }

    public function setSourceContext(Source $source)
    {
        $this->sourceContext = $source;
        foreach ($this->nodes as $node) {
            if ($node instanceof Node) {
                $node->setSourceContext($source);
            }
        }
    }

    public function getSourceContext()
    {
        return $this->sourceContext;
    }

    /**
     * @deprecated since 1.27 (to be removed in 2.0)
     */
    public function setFilename($name)
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since version 1.27 and will be removed in 2.0. Use
setTemplateName() instead.', E_USER_DEPRECATED);

        $this->setTemplateName($name);
    }

    /**
     * @deprecated since 1.27 (to be removed in 2.0)
     */
    public function getFilename()
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since version 1.27 and will be removed in 2.0. Use
getTemplateName() instead.', E_USER_DEPRECATED);

        return $this->name;
    }
}

class_alias('Twig\Node\Node', 'Twig_Node');

// Ensure that the aliased name is loaded to keep BC for classes
implementing the typehint with the old aliased name.
class_exists('Twig\Compiler');
vendor/twig/twig/src/Node/NodeCaptureInterface.php000064400000000715151166614750016223
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

/**
 * Represents a node that captures any nested displayable nodes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface NodeCaptureInterface
{
}

class_alias('Twig\Node\NodeCaptureInterface',
'Twig_NodeCaptureInterface');
vendor/twig/twig/src/Node/NodeOutputInterface.php000064400000000666151166614750016125
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

/**
 * Represents a displayable node in the AST.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface NodeOutputInterface
{
}

class_alias('Twig\Node\NodeOutputInterface',
'Twig_NodeOutputInterface');
vendor/twig/twig/src/Node/PrintNode.php000064400000001635151166614750014075
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;

/**
 * Represents a node that outputs an expression.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class PrintNode extends Node implements NodeOutputInterface
{
    public function __construct(AbstractExpression $expr, $lineno, $tag =
null)
    {
        parent::__construct(['expr' => $expr], [], $lineno,
$tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('echo ')
            ->subcompile($this->getNode('expr'))
            ->raw(";\n")
        ;
    }
}

class_alias('Twig\Node\PrintNode', 'Twig_Node_Print');
vendor/twig/twig/src/Node/SandboxedPrintNode.php000064400000003457151166614750015731
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;

/**
 * Adds a check for the __toString() method when the variable is an object
and the sandbox is activated.
 *
 * When there is a simple Print statement, like {{ article }},
 * and if the sandbox is enabled, we need to check that the __toString()
 * method is allowed if 'article' is an object.
 *
 * Not used anymore, to be deprecated in 2.x and removed in 3.0
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class SandboxedPrintNode extends PrintNode
{
    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('echo ')
        ;
        $expr = $this->getNode('expr');
        if ($expr instanceof ConstantExpression) {
            $compiler
                ->subcompile($expr)
                ->raw(";\n")
            ;
        } else {
            $compiler
               
->write('$this->env->getExtension(\'\Twig\Extension\SandboxExtension\')->ensureToStringAllowed(')
                ->subcompile($expr)
                ->raw(");\n")
            ;
        }
    }

    /**
     * Removes node filters.
     *
     * This is mostly needed when another visitor adds filters (like the
escaper one).
     *
     * @return Node
     */
    protected function removeNodeFilter(Node $node)
    {
        if ($node instanceof FilterExpression) {
            return
$this->removeNodeFilter($node->getNode('node'));
        }

        return $node;
    }
}

class_alias('Twig\Node\SandboxedPrintNode',
'Twig_Node_SandboxedPrint');
vendor/twig/twig/src/Node/SandboxNode.php000064400000002215151166614750014372
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * Represents a sandbox node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class SandboxNode extends Node
{
    public function __construct(\Twig_NodeInterface $body, $lineno, $tag =
null)
    {
        parent::__construct(['body' => $body], [], $lineno,
$tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write("if (!\$alreadySandboxed =
\$this->sandbox->isSandboxed()) {\n")
            ->indent()
           
->write("\$this->sandbox->enableSandbox();\n")
            ->outdent()
            ->write("}\n")
            ->subcompile($this->getNode('body'))
            ->write("if (!\$alreadySandboxed) {\n")
            ->indent()
           
->write("\$this->sandbox->disableSandbox();\n")
            ->outdent()
            ->write("}\n")
        ;
    }
}

class_alias('Twig\Node\SandboxNode',
'Twig_Node_Sandbox');
vendor/twig/twig/src/Node/SetNode.php000064400000006547151166614750013543
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;
use Twig\Node\Expression\ConstantExpression;

/**
 * Represents a set node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class SetNode extends Node implements NodeCaptureInterface
{
    public function __construct($capture, \Twig_NodeInterface $names,
\Twig_NodeInterface $values, $lineno, $tag = null)
    {
        parent::__construct(['names' => $names,
'values' => $values], ['capture' => $capture,
'safe' => false], $lineno, $tag);

        /*
         * Optimizes the node when capture is used for a large block of
text.
         *
         * {% set foo %}foo{% endset %} is compiled to
$context['foo'] = new Twig\Markup("foo");
         */
        if ($this->getAttribute('capture')) {
            $this->setAttribute('safe', true);

            $values = $this->getNode('values');
            if ($values instanceof TextNode) {
                $this->setNode('values', new
ConstantExpression($values->getAttribute('data'),
$values->getTemplateLine()));
                $this->setAttribute('capture', false);
            }
        }
    }

    public function compile(Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        if (\count($this->getNode('names')) > 1) {
            $compiler->write('list(');
            foreach ($this->getNode('names') as $idx =>
$node) {
                if ($idx) {
                    $compiler->raw(', ');
                }

                $compiler->subcompile($node);
            }
            $compiler->raw(')');
        } else {
            if ($this->getAttribute('capture')) {
                if ($compiler->getEnvironment()->isDebug()) {
                    $compiler->write("ob_start();\n");
                } else {
                    $compiler->write("ob_start(function () { return
''; });\n");
                }
                $compiler
                    ->subcompile($this->getNode('values'))
                ;
            }

            $compiler->subcompile($this->getNode('names'),
false);

            if ($this->getAttribute('capture')) {
                $compiler->raw(" = ('' === \$tmp =
ob_get_clean()) ? '' : new Markup(\$tmp,
\$this->env->getCharset())");
            }
        }

        if (!$this->getAttribute('capture')) {
            $compiler->raw(' = ');

            if (\count($this->getNode('names')) > 1) {
                $compiler->write('[');
                foreach ($this->getNode('values') as $idx
=> $value) {
                    if ($idx) {
                        $compiler->raw(', ');
                    }

                    $compiler->subcompile($value);
                }
                $compiler->raw(']');
            } else {
                if ($this->getAttribute('safe')) {
                    $compiler
                        ->raw("('' === \$tmp = ")
                       
->subcompile($this->getNode('values'))
                        ->raw(") ? '' : new Markup(\$tmp,
\$this->env->getCharset())")
                    ;
                } else {
                   
$compiler->subcompile($this->getNode('values'));
                }
            }
        }

        $compiler->raw(";\n");
    }
}

class_alias('Twig\Node\SetNode', 'Twig_Node_Set');
vendor/twig/twig/src/Node/SetTempNode.php000064400000001644151166614750014362
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * @internal
 */
class SetTempNode extends Node
{
    public function __construct($name, $lineno)
    {
        parent::__construct([], ['name' => $name], $lineno);
    }

    public function compile(Compiler $compiler)
    {
        $name = $this->getAttribute('name');
        $compiler
            ->addDebugInfo($this)
            ->write('if (isset($context[')
            ->string($name)
            ->raw('])) { $_')
            ->raw($name)
            ->raw('_ = $context[')
            ->repr($name)
            ->raw(']; } else { $_')
            ->raw($name)
            ->raw("_ = null; }\n")
        ;
    }
}

class_alias('Twig\Node\SetTempNode',
'Twig_Node_SetTemp');
vendor/twig/twig/src/Node/SpacelessNode.php000064400000002153151166614750014717
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * Represents a spaceless node.
 *
 * It removes spaces between HTML tags.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class SpacelessNode extends Node
{
    public function __construct(\Twig_NodeInterface $body, $lineno, $tag =
'spaceless')
    {
        parent::__construct(['body' => $body], [], $lineno,
$tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
        ;
        if ($compiler->getEnvironment()->isDebug()) {
            $compiler->write("ob_start();\n");
        } else {
            $compiler->write("ob_start(function () { return
''; });\n");
        }
        $compiler
            ->subcompile($this->getNode('body'))
            ->write("echo
trim(preg_replace('/>\s+</', '><',
ob_get_clean()));\n")
        ;
    }
}

class_alias('Twig\Node\SpacelessNode',
'Twig_Node_Spaceless');
vendor/twig/twig/src/Node/TextNode.php000064400000001462151166614750013723
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * Represents a text node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TextNode extends Node implements NodeOutputInterface
{
    public function __construct($data, $lineno)
    {
        parent::__construct([], ['data' => $data], $lineno);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('echo ')
            ->string($this->getAttribute('data'))
            ->raw(";\n")
        ;
    }
}

class_alias('Twig\Node\TextNode', 'Twig_Node_Text');
vendor/twig/twig/src/Node/WithNode.php000064400000004241151166614750013710
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Node;

use Twig\Compiler;

/**
 * Represents a nested "with" scope.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class WithNode extends Node
{
    public function __construct(Node $body, Node $variables = null, $only =
false, $lineno, $tag = null)
    {
        $nodes = ['body' => $body];
        if (null !== $variables) {
            $nodes['variables'] = $variables;
        }

        parent::__construct($nodes, ['only' => (bool) $only],
$lineno, $tag);
    }

    public function compile(Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        if ($this->hasNode('variables')) {
            $node = $this->getNode('variables');
            $varsName = $compiler->getVarName();
            $compiler
                ->write(sprintf('$%s = ', $varsName))
                ->subcompile($node)
                ->raw(";\n")
                ->write(sprintf("if (!twig_test_iterable(\$%s))
{\n", $varsName))
                ->indent()
                ->write("throw new RuntimeError('Variables
passed to the \"with\" tag must be a hash.', ")
                ->repr($node->getTemplateLine())
                ->raw(", \$this->getSourceContext());\n")
                ->outdent()
                ->write("}\n")
                ->write(sprintf("\$%s =
twig_to_array(\$%s);\n", $varsName, $varsName))
            ;

            if ($this->getAttribute('only')) {
                $compiler->write("\$context = ['_parent'
=> \$context];\n");
            } else {
                $compiler->write("\$context['_parent'] =
\$context;\n");
            }

            $compiler->write(sprintf("\$context =
\$this->env->mergeGlobals(array_merge(\$context, \$%s));\n",
$varsName));
        } else {
            $compiler->write("\$context['_parent'] =
\$context;\n");
        }

        $compiler
            ->subcompile($this->getNode('body'))
            ->write("\$context =
\$context['_parent'];\n")
        ;
    }
}

class_alias('Twig\Node\WithNode', 'Twig_Node_With');
vendor/twig/twig/src/NodeTraverser.php000064400000004040151166614750014062
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

use Twig\NodeVisitor\NodeVisitorInterface;

/**
 * A node traverser.
 *
 * It visits all nodes and their children and calls the given visitor for
each.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class NodeTraverser
{
    protected $env;
    protected $visitors = [];

    /**
     * @param NodeVisitorInterface[] $visitors
     */
    public function __construct(Environment $env, array $visitors = [])
    {
        $this->env = $env;
        foreach ($visitors as $visitor) {
            $this->addVisitor($visitor);
        }
    }

    public function addVisitor(NodeVisitorInterface $visitor)
    {
        $this->visitors[$visitor->getPriority()][] = $visitor;
    }

    /**
     * Traverses a node and calls the registered visitors.
     *
     * @return \Twig_NodeInterface
     */
    public function traverse(\Twig_NodeInterface $node)
    {
        ksort($this->visitors);
        foreach ($this->visitors as $visitors) {
            foreach ($visitors as $visitor) {
                $node = $this->traverseForVisitor($visitor, $node);
            }
        }

        return $node;
    }

    protected function traverseForVisitor(NodeVisitorInterface $visitor,
\Twig_NodeInterface $node = null)
    {
        if (null === $node) {
            return;
        }

        $node = $visitor->enterNode($node, $this->env);

        foreach ($node as $k => $n) {
            if (null === $n) {
                continue;
            }

            if (false !== ($m = $this->traverseForVisitor($visitor, $n))
&& null !== $m) {
                if ($m !== $n) {
                    $node->setNode($k, $m);
                }
            } else {
                $node->removeNode($k);
            }
        }

        return $visitor->leaveNode($node, $this->env);
    }
}

class_alias('Twig\NodeTraverser',
'Twig_NodeTraverser');
vendor/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php000064400000003061151166614750017477
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\NodeVisitor;

use Twig\Environment;
use Twig\Node\Node;

/**
 * Used to make node visitors compatible with Twig 1.x and 2.x.
 *
 * To be removed in Twig 3.1.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class AbstractNodeVisitor implements NodeVisitorInterface
{
    final public function enterNode(\Twig_NodeInterface $node, Environment
$env)
    {
        if (!$node instanceof Node) {
            throw new \LogicException(sprintf('%s only supports
\Twig\Node\Node instances.', __CLASS__));
        }

        return $this->doEnterNode($node, $env);
    }

    final public function leaveNode(\Twig_NodeInterface $node, Environment
$env)
    {
        if (!$node instanceof Node) {
            throw new \LogicException(sprintf('%s only supports
\Twig\Node\Node instances.', __CLASS__));
        }

        return $this->doLeaveNode($node, $env);
    }

    /**
     * Called before child nodes are visited.
     *
     * @return Node The modified node
     */
    abstract protected function doEnterNode(Node $node, Environment $env);

    /**
     * Called after child nodes are visited.
     *
     * @return Node|false|null The modified node or null if the node must
be removed
     */
    abstract protected function doLeaveNode(Node $node, Environment $env);
}

class_alias('Twig\NodeVisitor\AbstractNodeVisitor',
'Twig_BaseNodeVisitor');
vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php000064400000016054151166614750017324
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\NodeVisitor;

use Twig\Environment;
use Twig\Node\AutoEscapeNode;
use Twig\Node\BlockNode;
use Twig\Node\BlockReferenceNode;
use Twig\Node\DoNode;
use Twig\Node\Expression\ConditionalExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\InlinePrint;
use Twig\Node\ImportNode;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\NodeTraverser;

/**
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class EscaperNodeVisitor extends AbstractNodeVisitor
{
    protected $statusStack = [];
    protected $blocks = [];
    protected $safeAnalysis;
    protected $traverser;
    protected $defaultStrategy = false;
    protected $safeVars = [];

    public function __construct()
    {
        $this->safeAnalysis = new SafeAnalysisNodeVisitor();
    }

    protected function doEnterNode(Node $node, Environment $env)
    {
        if ($node instanceof ModuleNode) {
            if
($env->hasExtension('\Twig\Extension\EscaperExtension')
&& $defaultStrategy =
$env->getExtension('\Twig\Extension\EscaperExtension')->getDefaultStrategy($node->getTemplateName()))
{
                $this->defaultStrategy = $defaultStrategy;
            }
            $this->safeVars = [];
            $this->blocks = [];
        } elseif ($node instanceof AutoEscapeNode) {
            $this->statusStack[] =
$node->getAttribute('value');
        } elseif ($node instanceof BlockNode) {
            $this->statusStack[] =
isset($this->blocks[$node->getAttribute('name')]) ?
$this->blocks[$node->getAttribute('name')] :
$this->needEscaping($env);
        } elseif ($node instanceof ImportNode) {
            $this->safeVars[] =
$node->getNode('var')->getAttribute('name');
        }

        return $node;
    }

    protected function doLeaveNode(Node $node, Environment $env)
    {
        if ($node instanceof ModuleNode) {
            $this->defaultStrategy = false;
            $this->safeVars = [];
            $this->blocks = [];
        } elseif ($node instanceof FilterExpression) {
            return $this->preEscapeFilterNode($node, $env);
        } elseif ($node instanceof PrintNode && false !== $type =
$this->needEscaping($env)) {
            $expression = $node->getNode('expr');
            if ($expression instanceof ConditionalExpression &&
$this->shouldUnwrapConditional($expression, $env, $type)) {
                return new DoNode($this->unwrapConditional($expression,
$env, $type), $expression->getTemplateLine());
            }

            return $this->escapePrintNode($node, $env, $type);
        }

        if ($node instanceof AutoEscapeNode || $node instanceof BlockNode)
{
            array_pop($this->statusStack);
        } elseif ($node instanceof BlockReferenceNode) {
            $this->blocks[$node->getAttribute('name')] =
$this->needEscaping($env);
        }

        return $node;
    }

    private function shouldUnwrapConditional(ConditionalExpression
$expression, Environment $env, $type)
    {
        $expr2Safe = $this->isSafeFor($type,
$expression->getNode('expr2'), $env);
        $expr3Safe = $this->isSafeFor($type,
$expression->getNode('expr3'), $env);

        return $expr2Safe !== $expr3Safe;
    }

    private function unwrapConditional(ConditionalExpression $expression,
Environment $env, $type)
    {
        // convert "echo a ? b : c" to "a ? echo b : echo
c" recursively
        $expr2 = $expression->getNode('expr2');
        if ($expr2 instanceof ConditionalExpression &&
$this->shouldUnwrapConditional($expr2, $env, $type)) {
            $expr2 = $this->unwrapConditional($expr2, $env, $type);
        } else {
            $expr2 = $this->escapeInlinePrintNode(new
InlinePrint($expr2, $expr2->getTemplateLine()), $env, $type);
        }
        $expr3 = $expression->getNode('expr3');
        if ($expr3 instanceof ConditionalExpression &&
$this->shouldUnwrapConditional($expr3, $env, $type)) {
            $expr3 = $this->unwrapConditional($expr3, $env, $type);
        } else {
            $expr3 = $this->escapeInlinePrintNode(new
InlinePrint($expr3, $expr3->getTemplateLine()), $env, $type);
        }

        return new
ConditionalExpression($expression->getNode('expr1'), $expr2,
$expr3, $expression->getTemplateLine());
    }

    private function escapeInlinePrintNode(InlinePrint $node, Environment
$env, $type)
    {
        $expression = $node->getNode('node');

        if ($this->isSafeFor($type, $expression, $env)) {
            return $node;
        }

        return new InlinePrint($this->getEscaperFilter($type,
$expression), $node->getTemplateLine());
    }

    protected function escapePrintNode(PrintNode $node, Environment $env,
$type)
    {
        if (false === $type) {
            return $node;
        }

        $expression = $node->getNode('expr');

        if ($this->isSafeFor($type, $expression, $env)) {
            return $node;
        }

        $class = \get_class($node);

        return new $class($this->getEscaperFilter($type, $expression),
$node->getTemplateLine());
    }

    protected function preEscapeFilterNode(FilterExpression $filter,
Environment $env)
    {
        $name =
$filter->getNode('filter')->getAttribute('value');

        $type = $env->getFilter($name)->getPreEscape();
        if (null === $type) {
            return $filter;
        }

        $node = $filter->getNode('node');
        if ($this->isSafeFor($type, $node, $env)) {
            return $filter;
        }

        $filter->setNode('node',
$this->getEscaperFilter($type, $node));

        return $filter;
    }

    protected function isSafeFor($type, \Twig_NodeInterface $expression,
$env)
    {
        $safe = $this->safeAnalysis->getSafe($expression);

        if (null === $safe) {
            if (null === $this->traverser) {
                $this->traverser = new NodeTraverser($env,
[$this->safeAnalysis]);
            }

            $this->safeAnalysis->setSafeVars($this->safeVars);

            $this->traverser->traverse($expression);
            $safe = $this->safeAnalysis->getSafe($expression);
        }

        return \in_array($type, $safe) || \in_array('all',
$safe);
    }

    protected function needEscaping(Environment $env)
    {
        if (\count($this->statusStack)) {
            return $this->statusStack[\count($this->statusStack) -
1];
        }

        return $this->defaultStrategy ? $this->defaultStrategy :
false;
    }

    protected function getEscaperFilter($type, \Twig_NodeInterface $node)
    {
        $line = $node->getTemplateLine();
        $name = new ConstantExpression('escape', $line);
        $args = new Node([new ConstantExpression((string) $type, $line),
new ConstantExpression(null, $line), new ConstantExpression(true, $line)]);

        return new FilterExpression($node, $name, $args, $line);
    }

    public function getPriority()
    {
        return 0;
    }
}

class_alias('Twig\NodeVisitor\EscaperNodeVisitor',
'Twig_NodeVisitor_Escaper');
vendor/twig/twig/src/NodeVisitor/NodeVisitorInterface.php000064400000002403151166614750017633
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\NodeVisitor;

use Twig\Environment;

/**
 * Interface for node visitor classes.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface NodeVisitorInterface
{
    /**
     * Called before child nodes are visited.
     *
     * @return \Twig_NodeInterface The modified node
     */
    public function enterNode(\Twig_NodeInterface $node, Environment $env);

    /**
     * Called after child nodes are visited.
     *
     * @return \Twig_NodeInterface|false|null The modified node or null if
the node must be removed
     */
    public function leaveNode(\Twig_NodeInterface $node, Environment $env);

    /**
     * Returns the priority for this visitor.
     *
     * Priority should be between -10 and 10 (0 is the default).
     *
     * @return int The priority level
     */
    public function getPriority();
}

class_alias('Twig\NodeVisitor\NodeVisitorInterface',
'Twig_NodeVisitorInterface');

// Ensure that the aliased name is loaded to keep BC for classes
implementing the typehint with the old aliased name.
class_exists('Twig\Environment');
vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php000064400000021370151166614750017721
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\NodeVisitor;

use Twig\Environment;
use Twig\Node\BlockReferenceNode;
use Twig\Node\BodyNode;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\ParentExpression;
use Twig\Node\Expression\TempNameExpression;
use Twig\Node\ForNode;
use Twig\Node\IncludeNode;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\Node\SetTempNode;

/**
 * Tries to optimize the AST.
 *
 * This visitor is always the last registered one.
 *
 * You can configure which optimizations you want to activate via the
 * optimizer mode.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class OptimizerNodeVisitor extends AbstractNodeVisitor
{
    const OPTIMIZE_ALL = -1;
    const OPTIMIZE_NONE = 0;
    const OPTIMIZE_FOR = 2;
    const OPTIMIZE_RAW_FILTER = 4;
    const OPTIMIZE_VAR_ACCESS = 8;

    protected $loops = [];
    protected $loopsTargets = [];
    protected $optimizers;
    protected $prependedNodes = [];
    protected $inABody = false;

    /**
     * @param int $optimizers The optimizer mode
     */
    public function __construct($optimizers = -1)
    {
        if (!\is_int($optimizers) || $optimizers > (self::OPTIMIZE_FOR |
self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_VAR_ACCESS)) {
            throw new \InvalidArgumentException(sprintf('Optimizer
mode "%s" is not valid.', $optimizers));
        }

        $this->optimizers = $optimizers;
    }

    protected function doEnterNode(Node $node, Environment $env)
    {
        if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR &
$this->optimizers)) {
            $this->enterOptimizeFor($node, $env);
        }

        if (\PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS
=== (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) &&
!$env->isStrictVariables() &&
!$env->hasExtension('\Twig\Extension\SandboxExtension')) {
            if ($this->inABody) {
                if (!$node instanceof AbstractExpression) {
                    if ('Twig_Node' !== \get_class($node)) {
                        array_unshift($this->prependedNodes, []);
                    }
                } else {
                    $node = $this->optimizeVariables($node, $env);
                }
            } elseif ($node instanceof BodyNode) {
                $this->inABody = true;
            }
        }

        return $node;
    }

    protected function doLeaveNode(Node $node, Environment $env)
    {
        $expression = $node instanceof AbstractExpression;

        if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR &
$this->optimizers)) {
            $this->leaveOptimizeFor($node, $env);
        }

        if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER &
$this->optimizers)) {
            $node = $this->optimizeRawFilter($node, $env);
        }

        $node = $this->optimizePrintNode($node, $env);

        if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS &
$this->optimizers) && !$env->isStrictVariables() &&
!$env->hasExtension('\Twig\Extension\SandboxExtension')) {
            if ($node instanceof BodyNode) {
                $this->inABody = false;
            } elseif ($this->inABody) {
                if (!$expression && 'Twig_Node' !==
\get_class($node) && $prependedNodes =
array_shift($this->prependedNodes)) {
                    $nodes = [];
                    foreach (array_unique($prependedNodes) as $name) {
                        $nodes[] = new SetTempNode($name,
$node->getTemplateLine());
                    }

                    $nodes[] = $node;
                    $node = new Node($nodes);
                }
            }
        }

        return $node;
    }

    protected function optimizeVariables(\Twig_NodeInterface $node,
Environment $env)
    {
        if ('Twig_Node_Expression_Name' === \get_class($node)
&& $node->isSimple()) {
            $this->prependedNodes[0][] =
$node->getAttribute('name');

            return new
TempNameExpression($node->getAttribute('name'),
$node->getTemplateLine());
        }

        return $node;
    }

    /**
     * Optimizes print nodes.
     *
     * It replaces:
     *
     *   * "echo $this->render(Parent)Block()" with
"$this->display(Parent)Block()"
     *
     * @return \Twig_NodeInterface
     */
    protected function optimizePrintNode(\Twig_NodeInterface $node,
Environment $env)
    {
        if (!$node instanceof PrintNode) {
            return $node;
        }

        $exprNode = $node->getNode('expr');
        if (
            $exprNode instanceof BlockReferenceExpression ||
            $exprNode instanceof ParentExpression
        ) {
            $exprNode->setAttribute('output', true);

            return $exprNode;
        }

        return $node;
    }

    /**
     * Removes "raw" filters.
     *
     * @return \Twig_NodeInterface
     */
    protected function optimizeRawFilter(\Twig_NodeInterface $node,
Environment $env)
    {
        if ($node instanceof FilterExpression && 'raw' ==
$node->getNode('filter')->getAttribute('value'))
{
            return $node->getNode('node');
        }

        return $node;
    }

    /**
     * Optimizes "for" tag by removing the "loop"
variable creation whenever possible.
     */
    protected function enterOptimizeFor(\Twig_NodeInterface $node,
Environment $env)
    {
        if ($node instanceof ForNode) {
            // disable the loop variable by default
            $node->setAttribute('with_loop', false);
            array_unshift($this->loops, $node);
            array_unshift($this->loopsTargets,
$node->getNode('value_target')->getAttribute('name'));
            array_unshift($this->loopsTargets,
$node->getNode('key_target')->getAttribute('name'));
        } elseif (!$this->loops) {
            // we are outside a loop
            return;
        }

        // when do we need to add the loop variable back?

        // the loop variable is referenced for the current loop
        elseif ($node instanceof NameExpression && 'loop'
=== $node->getAttribute('name')) {
            $node->setAttribute('always_defined', true);
            $this->addLoopToCurrent();
        }

        // optimize access to loop targets
        elseif ($node instanceof NameExpression &&
\in_array($node->getAttribute('name'),
$this->loopsTargets)) {
            $node->setAttribute('always_defined', true);
        }

        // block reference
        elseif ($node instanceof BlockReferenceNode || $node instanceof
BlockReferenceExpression) {
            $this->addLoopToCurrent();
        }

        // include without the only attribute
        elseif ($node instanceof IncludeNode &&
!$node->getAttribute('only')) {
            $this->addLoopToAll();
        }

        // include function without the with_context=false parameter
        elseif ($node instanceof FunctionExpression
            && 'include' ===
$node->getAttribute('name')
            &&
(!$node->getNode('arguments')->hasNode('with_context')
                 || false !==
$node->getNode('arguments')->getNode('with_context')->getAttribute('value')
               )
        ) {
            $this->addLoopToAll();
        }

        // the loop variable is referenced via an attribute
        elseif ($node instanceof GetAttrExpression
            && (!$node->getNode('attribute')
instanceof ConstantExpression
                || 'parent' ===
$node->getNode('attribute')->getAttribute('value')
               )
            && (true ===
$this->loops[0]->getAttribute('with_loop')
                || ($node->getNode('node') instanceof
NameExpression
                    && 'loop' ===
$node->getNode('node')->getAttribute('name')
                   )
               )
        ) {
            $this->addLoopToAll();
        }
    }

    /**
     * Optimizes "for" tag by removing the "loop"
variable creation whenever possible.
     */
    protected function leaveOptimizeFor(\Twig_NodeInterface $node,
Environment $env)
    {
        if ($node instanceof ForNode) {
            array_shift($this->loops);
            array_shift($this->loopsTargets);
            array_shift($this->loopsTargets);
        }
    }

    protected function addLoopToCurrent()
    {
        $this->loops[0]->setAttribute('with_loop', true);
    }

    protected function addLoopToAll()
    {
        foreach ($this->loops as $loop) {
            $loop->setAttribute('with_loop', true);
        }
    }

    public function getPriority()
    {
        return 255;
    }
}

class_alias('Twig\NodeVisitor\OptimizerNodeVisitor',
'Twig_NodeVisitor_Optimizer');
vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php000064400000012016151166614750020316
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\NodeVisitor;

use Twig\Environment;
use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\ConditionalExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\ParentExpression;
use Twig\Node\Node;

/**
 * @final
 */
class SafeAnalysisNodeVisitor extends AbstractNodeVisitor
{
    protected $data = [];
    protected $safeVars = [];

    public function setSafeVars($safeVars)
    {
        $this->safeVars = $safeVars;
    }

    public function getSafe(\Twig_NodeInterface $node)
    {
        $hash = spl_object_hash($node);
        if (!isset($this->data[$hash])) {
            return;
        }

        foreach ($this->data[$hash] as $bucket) {
            if ($bucket['key'] !== $node) {
                continue;
            }

            if (\in_array('html_attr',
$bucket['value'])) {
                $bucket['value'][] = 'html';
            }

            return $bucket['value'];
        }
    }

    protected function setSafe(\Twig_NodeInterface $node, array $safe)
    {
        $hash = spl_object_hash($node);
        if (isset($this->data[$hash])) {
            foreach ($this->data[$hash] as &$bucket) {
                if ($bucket['key'] === $node) {
                    $bucket['value'] = $safe;

                    return;
                }
            }
        }
        $this->data[$hash][] = [
            'key' => $node,
            'value' => $safe,
        ];
    }

    protected function doEnterNode(Node $node, Environment $env)
    {
        return $node;
    }

    protected function doLeaveNode(Node $node, Environment $env)
    {
        if ($node instanceof ConstantExpression) {
            // constants are marked safe for all
            $this->setSafe($node, ['all']);
        } elseif ($node instanceof BlockReferenceExpression) {
            // blocks are safe by definition
            $this->setSafe($node, ['all']);
        } elseif ($node instanceof ParentExpression) {
            // parent block is safe by definition
            $this->setSafe($node, ['all']);
        } elseif ($node instanceof ConditionalExpression) {
            // intersect safeness of both operands
            $safe =
$this->intersectSafe($this->getSafe($node->getNode('expr2')),
$this->getSafe($node->getNode('expr3')));
            $this->setSafe($node, $safe);
        } elseif ($node instanceof FilterExpression) {
            // filter expression is safe when the filter is safe
            $name =
$node->getNode('filter')->getAttribute('value');
            $args = $node->getNode('arguments');
            if (false !== $filter = $env->getFilter($name)) {
                $safe = $filter->getSafe($args);
                if (null === $safe) {
                    $safe =
$this->intersectSafe($this->getSafe($node->getNode('node')),
$filter->getPreservesSafety());
                }
                $this->setSafe($node, $safe);
            } else {
                $this->setSafe($node, []);
            }
        } elseif ($node instanceof FunctionExpression) {
            // function expression is safe when the function is safe
            $name = $node->getAttribute('name');
            $args = $node->getNode('arguments');
            $function = $env->getFunction($name);
            if (false !== $function) {
                $this->setSafe($node, $function->getSafe($args));
            } else {
                $this->setSafe($node, []);
            }
        } elseif ($node instanceof MethodCallExpression) {
            if ($node->getAttribute('safe')) {
                $this->setSafe($node, ['all']);
            } else {
                $this->setSafe($node, []);
            }
        } elseif ($node instanceof GetAttrExpression &&
$node->getNode('node') instanceof NameExpression) {
            $name =
$node->getNode('node')->getAttribute('name');
            // attributes on template instances are safe
            if ('_self' == $name || \in_array($name,
$this->safeVars)) {
                $this->setSafe($node, ['all']);
            } else {
                $this->setSafe($node, []);
            }
        } else {
            $this->setSafe($node, []);
        }

        return $node;
    }

    protected function intersectSafe(array $a = null, array $b = null)
    {
        if (null === $a || null === $b) {
            return [];
        }

        if (\in_array('all', $a)) {
            return $b;
        }

        if (\in_array('all', $b)) {
            return $a;
        }

        return array_intersect($a, $b);
    }

    public function getPriority()
    {
        return 0;
    }
}

class_alias('Twig\NodeVisitor\SafeAnalysisNodeVisitor',
'Twig_NodeVisitor_SafeAnalysis');
vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php000064400000010321151166614750017327
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\NodeVisitor;

use Twig\Environment;
use Twig\Node\CheckSecurityNode;
use Twig\Node\CheckToStringNode;
use Twig\Node\Expression\Binary\ConcatBinary;
use Twig\Node\Expression\Binary\RangeBinary;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\Node\SetNode;

/**
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class SandboxNodeVisitor extends AbstractNodeVisitor
{
    protected $inAModule = false;
    protected $tags;
    protected $filters;
    protected $functions;

    private $needsToStringWrap = false;

    protected function doEnterNode(Node $node, Environment $env)
    {
        if ($node instanceof ModuleNode) {
            $this->inAModule = true;
            $this->tags = [];
            $this->filters = [];
            $this->functions = [];

            return $node;
        } elseif ($this->inAModule) {
            // look for tags
            if ($node->getNodeTag() &&
!isset($this->tags[$node->getNodeTag()])) {
                $this->tags[$node->getNodeTag()] = $node;
            }

            // look for filters
            if ($node instanceof FilterExpression &&
!isset($this->filters[$node->getNode('filter')->getAttribute('value')]))
{
               
$this->filters[$node->getNode('filter')->getAttribute('value')]
= $node;
            }

            // look for functions
            if ($node instanceof FunctionExpression &&
!isset($this->functions[$node->getAttribute('name')])) {
               
$this->functions[$node->getAttribute('name')] = $node;
            }

            // the .. operator is equivalent to the range() function
            if ($node instanceof RangeBinary &&
!isset($this->functions['range'])) {
                $this->functions['range'] = $node;
            }

            if ($node instanceof PrintNode) {
                $this->needsToStringWrap = true;
                $this->wrapNode($node, 'expr');
            }

            if ($node instanceof SetNode &&
!$node->getAttribute('capture')) {
                $this->needsToStringWrap = true;
            }

            // wrap outer nodes that can implicitly call __toString()
            if ($this->needsToStringWrap) {
                if ($node instanceof ConcatBinary) {
                    $this->wrapNode($node, 'left');
                    $this->wrapNode($node, 'right');
                }
                if ($node instanceof FilterExpression) {
                    $this->wrapNode($node, 'node');
                    $this->wrapArrayNode($node, 'arguments');
                }
                if ($node instanceof FunctionExpression) {
                    $this->wrapArrayNode($node, 'arguments');
                }
            }
        }

        return $node;
    }

    protected function doLeaveNode(Node $node, Environment $env)
    {
        if ($node instanceof ModuleNode) {
            $this->inAModule = false;

           
$node->getNode('constructor_end')->setNode('_security_check',
new Node([new CheckSecurityNode($this->filters, $this->tags,
$this->functions), $node->getNode('display_start')]));
        } elseif ($this->inAModule) {
            if ($node instanceof PrintNode || $node instanceof SetNode) {
                $this->needsToStringWrap = false;
            }
        }

        return $node;
    }

    private function wrapNode(Node $node, $name)
    {
        $expr = $node->getNode($name);
        if ($expr instanceof NameExpression || $expr instanceof
GetAttrExpression) {
            $node->setNode($name, new CheckToStringNode($expr));
        }
    }

    private function wrapArrayNode(Node $node, $name)
    {
        $args = $node->getNode($name);
        foreach ($args as $name => $_) {
            $this->wrapNode($args, $name);
        }
    }

    public function getPriority()
    {
        return 0;
    }
}

class_alias('Twig\NodeVisitor\SandboxNodeVisitor',
'Twig_NodeVisitor_Sandbox');
vendor/twig/twig/src/Parser.php000064400000032630151166614750012541
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

use Twig\Error\SyntaxError;
use Twig\Node\BlockNode;
use Twig\Node\BlockReferenceNode;
use Twig\Node\BodyNode;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\MacroNode;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
use Twig\Node\NodeCaptureInterface;
use Twig\Node\NodeOutputInterface;
use Twig\Node\PrintNode;
use Twig\Node\TextNode;
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\TokenParser\TokenParserInterface;

/**
 * Default parser implementation.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Parser implements \Twig_ParserInterface
{
    protected $stack = [];
    protected $stream;
    protected $parent;
    protected $handlers;
    protected $visitors;
    protected $expressionParser;
    protected $blocks;
    protected $blockStack;
    protected $macros;
    protected $env;
    protected $reservedMacroNames;
    protected $importedSymbols;
    protected $traits;
    protected $embeddedTemplates = [];
    private $varNameSalt = 0;

    public function __construct(Environment $env)
    {
        $this->env = $env;
    }

    /**
     * @deprecated since 1.27 (to be removed in 2.0)
     */
    public function getEnvironment()
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since version 1.27 and will be removed in 2.0.',
E_USER_DEPRECATED);

        return $this->env;
    }

    public function getVarName()
    {
        return sprintf('__internal_%s', hash('sha256',
__METHOD__.$this->stream->getSourceContext()->getCode().$this->varNameSalt++));
    }

    /**
     * @deprecated since 1.27 (to be removed in 2.0). Use
$parser->getStream()->getSourceContext()->getPath() instead.
     */
    public function getFilename()
    {
        @trigger_error(sprintf('The "%s" method is
deprecated since version 1.27 and will be removed in 2.0. Use
$parser->getStream()->getSourceContext()->getPath()
instead.', __METHOD__), E_USER_DEPRECATED);

        return $this->stream->getSourceContext()->getName();
    }

    public function parse(TokenStream $stream, $test = null, $dropNeedle =
false)
    {
        // push all variables into the stack to keep the current state of
the parser
        // using get_object_vars() instead of foreach would lead to
https://bugs.php.net/71336
        // This hack can be removed when min version if PHP 7.0
        $vars = [];
        foreach ($this as $k => $v) {
            $vars[$k] = $v;
        }

        unset($vars['stack'], $vars['env'],
$vars['handlers'], $vars['visitors'],
$vars['expressionParser'],
$vars['reservedMacroNames']);
        $this->stack[] = $vars;

        // tag handlers
        if (null === $this->handlers) {
            $this->handlers = $this->env->getTokenParsers();
            $this->handlers->setParser($this);
        }

        // node visitors
        if (null === $this->visitors) {
            $this->visitors = $this->env->getNodeVisitors();
        }

        if (null === $this->expressionParser) {
            $this->expressionParser = new ExpressionParser($this,
$this->env);
        }

        $this->stream = $stream;
        $this->parent = null;
        $this->blocks = [];
        $this->macros = [];
        $this->traits = [];
        $this->blockStack = [];
        $this->importedSymbols = [[]];
        $this->embeddedTemplates = [];
        $this->varNameSalt = 0;

        try {
            $body = $this->subparse($test, $dropNeedle);

            if (null !== $this->parent && null === $body =
$this->filterBodyNodes($body)) {
                $body = new Node();
            }
        } catch (SyntaxError $e) {
            if (!$e->getSourceContext()) {
               
$e->setSourceContext($this->stream->getSourceContext());
            }

            if (!$e->getTemplateLine()) {
               
$e->setTemplateLine($this->stream->getCurrent()->getLine());
            }

            throw $e;
        }

        $node = new ModuleNode(new BodyNode([$body]), $this->parent, new
Node($this->blocks), new Node($this->macros), new
Node($this->traits), $this->embeddedTemplates,
$stream->getSourceContext());

        $traverser = new NodeTraverser($this->env, $this->visitors);

        $node = $traverser->traverse($node);

        // restore previous stack so previous parse() call can resume
working
        foreach (array_pop($this->stack) as $key => $val) {
            $this->$key = $val;
        }

        return $node;
    }

    public function subparse($test, $dropNeedle = false)
    {
        $lineno = $this->getCurrentToken()->getLine();
        $rv = [];
        while (!$this->stream->isEOF()) {
            switch ($this->getCurrentToken()->getType()) {
                case Token::TEXT_TYPE:
                    $token = $this->stream->next();
                    $rv[] = new TextNode($token->getValue(),
$token->getLine());
                    break;

                case Token::VAR_START_TYPE:
                    $token = $this->stream->next();
                    $expr =
$this->expressionParser->parseExpression();
                    $this->stream->expect(Token::VAR_END_TYPE);
                    $rv[] = new PrintNode($expr, $token->getLine());
                    break;

                case Token::BLOCK_START_TYPE:
                    $this->stream->next();
                    $token = $this->getCurrentToken();

                    if (Token::NAME_TYPE !== $token->getType()) {
                        throw new SyntaxError('A block must start with
a tag name.', $token->getLine(),
$this->stream->getSourceContext());
                    }

                    if (null !== $test && \call_user_func($test,
$token)) {
                        if ($dropNeedle) {
                            $this->stream->next();
                        }

                        if (1 === \count($rv)) {
                            return $rv[0];
                        }

                        return new Node($rv, [], $lineno);
                    }

                    $subparser =
$this->handlers->getTokenParser($token->getValue());
                    if (null === $subparser) {
                        if (null !== $test) {
                            $e = new SyntaxError(sprintf('Unexpected
"%s" tag', $token->getValue()), $token->getLine(),
$this->stream->getSourceContext());

                            if (\is_array($test) && isset($test[0])
&& $test[0] instanceof TokenParserInterface) {
                                $e->appendMessage(sprintf('
(expecting closing tag for the "%s" tag defined near line
%s).', $test[0]->getTag(), $lineno));
                            }
                        } else {
                            $e = new SyntaxError(sprintf('Unknown
"%s" tag.', $token->getValue()), $token->getLine(),
$this->stream->getSourceContext());
                            $e->addSuggestions($token->getValue(),
array_keys($this->env->getTags()));
                        }

                        throw $e;
                    }

                    $this->stream->next();

                    $node = $subparser->parse($token);
                    if (null !== $node) {
                        $rv[] = $node;
                    }
                    break;

                default:
                    throw new SyntaxError('Lexer or parser ended up in
unsupported state.', $this->getCurrentToken()->getLine(),
$this->stream->getSourceContext());
            }
        }

        if (1 === \count($rv)) {
            return $rv[0];
        }

        return new Node($rv, [], $lineno);
    }

    /**
     * @deprecated since 1.27 (to be removed in 2.0)
     */
    public function addHandler($name, $class)
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since version 1.27 and will be removed in 2.0.',
E_USER_DEPRECATED);

        $this->handlers[$name] = $class;
    }

    /**
     * @deprecated since 1.27 (to be removed in 2.0)
     */
    public function addNodeVisitor(NodeVisitorInterface $visitor)
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since version 1.27 and will be removed in 2.0.',
E_USER_DEPRECATED);

        $this->visitors[] = $visitor;
    }

    public function getBlockStack()
    {
        return $this->blockStack;
    }

    public function peekBlockStack()
    {
        return isset($this->blockStack[\count($this->blockStack) -
1]) ? $this->blockStack[\count($this->blockStack) - 1] : null;
    }

    public function popBlockStack()
    {
        array_pop($this->blockStack);
    }

    public function pushBlockStack($name)
    {
        $this->blockStack[] = $name;
    }

    public function hasBlock($name)
    {
        return isset($this->blocks[$name]);
    }

    public function getBlock($name)
    {
        return $this->blocks[$name];
    }

    public function setBlock($name, BlockNode $value)
    {
        $this->blocks[$name] = new BodyNode([$value], [],
$value->getTemplateLine());
    }

    public function hasMacro($name)
    {
        return isset($this->macros[$name]);
    }

    public function setMacro($name, MacroNode $node)
    {
        if ($this->isReservedMacroName($name)) {
            throw new SyntaxError(sprintf('"%s" cannot be
used as a macro name as it is a reserved keyword.', $name),
$node->getTemplateLine(), $this->stream->getSourceContext());
        }

        $this->macros[$name] = $node;
    }

    public function isReservedMacroName($name)
    {
        if (null === $this->reservedMacroNames) {
            $this->reservedMacroNames = [];
            $r = new
\ReflectionClass($this->env->getBaseTemplateClass());
            foreach ($r->getMethods() as $method) {
                $methodName = strtr($method->getName(),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz');

                if ('get' === substr($methodName, 0, 3)
&& isset($methodName[3])) {
                    $this->reservedMacroNames[] = substr($methodName,
3);
                }
            }
        }

        return \in_array(strtr($name,
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz'), $this->reservedMacroNames);
    }

    public function addTrait($trait)
    {
        $this->traits[] = $trait;
    }

    public function hasTraits()
    {
        return \count($this->traits) > 0;
    }

    public function embedTemplate(ModuleNode $template)
    {
        $template->setIndex(mt_rand());

        $this->embeddedTemplates[] = $template;
    }

    public function addImportedSymbol($type, $alias, $name = null,
AbstractExpression $node = null)
    {
        $this->importedSymbols[0][$type][$alias] = ['name'
=> $name, 'node' => $node];
    }

    public function getImportedSymbol($type, $alias)
    {
        if (null !== $this->peekBlockStack()) {
            foreach ($this->importedSymbols as $functions) {
                if (isset($functions[$type][$alias])) {
                    if (\count($this->blockStack) > 1) {
                        return null;
                    }

                    return $functions[$type][$alias];
                }
            }
        } else {
            return isset($this->importedSymbols[0][$type][$alias]) ?
$this->importedSymbols[0][$type][$alias] : null;
        }
    }

    public function isMainScope()
    {
        return 1 === \count($this->importedSymbols);
    }

    public function pushLocalScope()
    {
        array_unshift($this->importedSymbols, []);
    }

    public function popLocalScope()
    {
        array_shift($this->importedSymbols);
    }

    /**
     * @return ExpressionParser
     */
    public function getExpressionParser()
    {
        return $this->expressionParser;
    }

    public function getParent()
    {
        return $this->parent;
    }

    public function setParent($parent)
    {
        $this->parent = $parent;
    }

    /**
     * @return TokenStream
     */
    public function getStream()
    {
        return $this->stream;
    }

    /**
     * @return Token
     */
    public function getCurrentToken()
    {
        return $this->stream->getCurrent();
    }

    protected function filterBodyNodes(\Twig_NodeInterface $node)
    {
        // check that the body does not contain non-empty output nodes
        if (
            ($node instanceof TextNode &&
!ctype_space($node->getAttribute('data')))
            ||
            (!$node instanceof TextNode && !$node instanceof
BlockReferenceNode && $node instanceof NodeOutputInterface)
        ) {
            if (false !== strpos((string) $node,
\chr(0xEF).\chr(0xBB).\chr(0xBF))) {
                $t = substr($node->getAttribute('data'), 3);
                if ('' === $t || ctype_space($t)) {
                    // bypass empty nodes starting with a BOM
                    return;
                }
            }

            throw new SyntaxError('A template that extends another one
cannot include content outside Twig blocks. Did you forget to put the
content inside a {% block %} tag?', $node->getTemplateLine(),
$this->stream->getSourceContext());
        }

        // bypass nodes that will "capture" the output
        if ($node instanceof NodeCaptureInterface) {
            return $node;
        }

        if ($node instanceof NodeOutputInterface) {
            return;
        }

        foreach ($node as $k => $n) {
            if (null !== $n && null ===
$this->filterBodyNodes($n)) {
                $node->removeNode($k);
            }
        }

        return $node;
    }
}

class_alias('Twig\Parser', 'Twig_Parser');
vendor/twig/twig/src/Profiler/Dumper/BaseDumper.php000064400000003346151166614750016354
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Profiler\Dumper;

use Twig\Profiler\Profile;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class BaseDumper
{
    private $root;

    public function dump(Profile $profile)
    {
        return $this->dumpProfile($profile);
    }

    abstract protected function formatTemplate(Profile $profile, $prefix);

    abstract protected function formatNonTemplate(Profile $profile,
$prefix);

    abstract protected function formatTime(Profile $profile, $percent);

    private function dumpProfile(Profile $profile, $prefix = '',
$sibling = false)
    {
        if ($profile->isRoot()) {
            $this->root = $profile->getDuration();
            $start = $profile->getName();
        } else {
            if ($profile->isTemplate()) {
                $start = $this->formatTemplate($profile, $prefix);
            } else {
                $start = $this->formatNonTemplate($profile, $prefix);
            }
            $prefix .= $sibling ? '│ ' : '  ';
        }

        $percent = $this->root ? $profile->getDuration() /
$this->root * 100 : 0;

        if ($profile->getDuration() * 1000 < 1) {
            $str = $start."\n";
        } else {
            $str = sprintf("%s %s\n", $start,
$this->formatTime($profile, $percent));
        }

        $nCount = \count($profile->getProfiles());
        foreach ($profile as $i => $p) {
            $str .= $this->dumpProfile($p, $prefix, $i + 1 !== $nCount);
        }

        return $str;
    }
}

class_alias('Twig\Profiler\Dumper\BaseDumper',
'Twig_Profiler_Dumper_Base');
vendor/twig/twig/src/Profiler/Dumper/BlackfireDumper.php000064400000004016151166614750017357
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Profiler\Dumper;

use Twig\Profiler\Profile;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @final
 */
class BlackfireDumper
{
    public function dump(Profile $profile)
    {
        $data = [];
        $this->dumpProfile('main()', $profile, $data);
        $this->dumpChildren('main()', $profile, $data);

        $start = sprintf('%f', microtime(true));
        $str = <<<EOF
file-format: BlackfireProbe
cost-dimensions: wt mu pmu
request-start: {$start}


EOF;

        foreach ($data as $name => $values) {
            $str .= "{$name}//{$values['ct']}
{$values['wt']} {$values['mu']}
{$values['pmu']}\n";
        }

        return $str;
    }

    private function dumpChildren($parent, Profile $profile, &$data)
    {
        foreach ($profile as $p) {
            if ($p->isTemplate()) {
                $name = $p->getTemplate();
            } else {
                $name = sprintf('%s::%s(%s)',
$p->getTemplate(), $p->getType(), $p->getName());
            }
            $this->dumpProfile(sprintf('%s==>%s', $parent,
$name), $p, $data);
            $this->dumpChildren($name, $p, $data);
        }
    }

    private function dumpProfile($edge, Profile $profile, &$data)
    {
        if (isset($data[$edge])) {
            ++$data[$edge]['ct'];
            $data[$edge]['wt'] +=
floor($profile->getDuration() * 1000000);
            $data[$edge]['mu'] += $profile->getMemoryUsage();
            $data[$edge]['pmu'] +=
$profile->getPeakMemoryUsage();
        } else {
            $data[$edge] = [
                'ct' => 1,
                'wt' => floor($profile->getDuration() *
1000000),
                'mu' => $profile->getMemoryUsage(),
                'pmu' => $profile->getPeakMemoryUsage(),
            ];
        }
    }
}

class_alias('Twig\Profiler\Dumper\BlackfireDumper',
'Twig_Profiler_Dumper_Blackfire');
vendor/twig/twig/src/Profiler/Dumper/HtmlDumper.php000064400000002727151166614750016410
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Profiler\Dumper;

use Twig\Profiler\Profile;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @final
 */
class HtmlDumper extends BaseDumper
{
    private static $colors = [
        'block' => '#dfd',
        'macro' => '#ddf',
        'template' => '#ffd',
        'big' => '#d44',
    ];

    public function dump(Profile $profile)
    {
        return
'<pre>'.parent::dump($profile).'</pre>';
    }

    protected function formatTemplate(Profile $profile, $prefix)
    {
        return sprintf('%sâ”” <span style="background-color:
%s">%s</span>', $prefix,
self::$colors['template'], $profile->getTemplate());
    }

    protected function formatNonTemplate(Profile $profile, $prefix)
    {
        return sprintf('%sâ”” %s::%s(<span
style="background-color: %s">%s</span>)', $prefix,
$profile->getTemplate(), $profile->getType(),
isset(self::$colors[$profile->getType()]) ?
self::$colors[$profile->getType()] : 'auto',
$profile->getName());
    }

    protected function formatTime(Profile $profile, $percent)
    {
        return sprintf('<span style="color:
%s">%.2fms/%.0f%%</span>', $percent > 20 ?
self::$colors['big'] : 'auto',
$profile->getDuration() * 1000, $percent);
    }
}

class_alias('Twig\Profiler\Dumper\HtmlDumper',
'Twig_Profiler_Dumper_Html');
vendor/twig/twig/src/Profiler/Dumper/TextDumper.php000064400000001675151166614750016431
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Profiler\Dumper;

use Twig\Profiler\Profile;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @final
 */
class TextDumper extends BaseDumper
{
    protected function formatTemplate(Profile $profile, $prefix)
    {
        return sprintf('%sâ”” %s', $prefix,
$profile->getTemplate());
    }

    protected function formatNonTemplate(Profile $profile, $prefix)
    {
        return sprintf('%sâ”” %s::%s(%s)', $prefix,
$profile->getTemplate(), $profile->getType(),
$profile->getName());
    }

    protected function formatTime(Profile $profile, $percent)
    {
        return sprintf('%.2fms/%.0f%%',
$profile->getDuration() * 1000, $percent);
    }
}

class_alias('Twig\Profiler\Dumper\TextDumper',
'Twig_Profiler_Dumper_Text');
vendor/twig/twig/src/Profiler/Node/EnterProfileNode.php000064400000002432151166614750017155
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Profiler\Node;

use Twig\Compiler;
use Twig\Node\Node;

/**
 * Represents a profile enter node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class EnterProfileNode extends Node
{
    public function __construct($extensionName, $type, $name, $varName)
    {
        parent::__construct([], ['extension_name' =>
$extensionName, 'name' => $name, 'type' => $type,
'var_name' => $varName]);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->write(sprintf('$%s =
$this->env->getExtension(',
$this->getAttribute('var_name')))
            ->repr($this->getAttribute('extension_name'))
            ->raw(");\n")
            ->write(sprintf('$%s->enter($%s = new
\Twig\Profiler\Profile($this->getTemplateName(), ',
$this->getAttribute('var_name'),
$this->getAttribute('var_name').'_prof'))
            ->repr($this->getAttribute('type'))
            ->raw(', ')
            ->repr($this->getAttribute('name'))
            ->raw("));\n\n")
        ;
    }
}

class_alias('Twig\Profiler\Node\EnterProfileNode',
'Twig_Profiler_Node_EnterProfile');
vendor/twig/twig/src/Profiler/Node/LeaveProfileNode.php000064400000001526151166614750017137
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Profiler\Node;

use Twig\Compiler;
use Twig\Node\Node;

/**
 * Represents a profile leave node.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class LeaveProfileNode extends Node
{
    public function __construct($varName)
    {
        parent::__construct([], ['var_name' => $varName]);
    }

    public function compile(Compiler $compiler)
    {
        $compiler
            ->write("\n")
            ->write(sprintf("\$%s->leave(\$%s);\n\n",
$this->getAttribute('var_name'),
$this->getAttribute('var_name').'_prof'))
        ;
    }
}

class_alias('Twig\Profiler\Node\LeaveProfileNode',
'Twig_Profiler_Node_LeaveProfile');
vendor/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php000064400000004563151166614750021310
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Profiler\NodeVisitor;

use Twig\Environment;
use Twig\Node\BlockNode;
use Twig\Node\BodyNode;
use Twig\Node\MacroNode;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
use Twig\NodeVisitor\AbstractNodeVisitor;
use Twig\Profiler\Node\EnterProfileNode;
use Twig\Profiler\Node\LeaveProfileNode;
use Twig\Profiler\Profile;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @final
 */
class ProfilerNodeVisitor extends AbstractNodeVisitor
{
    private $extensionName;

    public function __construct($extensionName)
    {
        $this->extensionName = $extensionName;
    }

    protected function doEnterNode(Node $node, Environment $env)
    {
        return $node;
    }

    protected function doLeaveNode(Node $node, Environment $env)
    {
        if ($node instanceof ModuleNode) {
            $varName = $this->getVarName();
            $node->setNode('display_start', new Node([new
EnterProfileNode($this->extensionName, Profile::TEMPLATE,
$node->getTemplateName(), $varName),
$node->getNode('display_start')]));
            $node->setNode('display_end', new Node([new
LeaveProfileNode($varName), $node->getNode('display_end')]));
        } elseif ($node instanceof BlockNode) {
            $varName = $this->getVarName();
            $node->setNode('body', new BodyNode([
                new EnterProfileNode($this->extensionName,
Profile::BLOCK, $node->getAttribute('name'), $varName),
                $node->getNode('body'),
                new LeaveProfileNode($varName),
            ]));
        } elseif ($node instanceof MacroNode) {
            $varName = $this->getVarName();
            $node->setNode('body', new BodyNode([
                new EnterProfileNode($this->extensionName,
Profile::MACRO, $node->getAttribute('name'), $varName),
                $node->getNode('body'),
                new LeaveProfileNode($varName),
            ]));
        }

        return $node;
    }

    private function getVarName()
    {
        return sprintf('__internal_%s', hash('sha256',
$this->extensionName));
    }

    public function getPriority()
    {
        return 0;
    }
}

class_alias('Twig\Profiler\NodeVisitor\ProfilerNodeVisitor',
'Twig_Profiler_NodeVisitor_Profiler');
vendor/twig/twig/src/Profiler/Profile.php000064400000007772151166614750014500
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Profiler;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @final
 */
class Profile implements \IteratorAggregate, \Serializable
{
    const ROOT = 'ROOT';
    const BLOCK = 'block';
    const TEMPLATE = 'template';
    const MACRO = 'macro';

    private $template;
    private $name;
    private $type;
    private $starts = [];
    private $ends = [];
    private $profiles = [];

    public function __construct($template = 'main', $type =
self::ROOT, $name = 'main')
    {
        $this->template = $template;
        $this->type = $type;
        $this->name = 0 === strpos($name, '__internal_') ?
'INTERNAL' : $name;
        $this->enter();
    }

    public function getTemplate()
    {
        return $this->template;
    }

    public function getType()
    {
        return $this->type;
    }

    public function getName()
    {
        return $this->name;
    }

    public function isRoot()
    {
        return self::ROOT === $this->type;
    }

    public function isTemplate()
    {
        return self::TEMPLATE === $this->type;
    }

    public function isBlock()
    {
        return self::BLOCK === $this->type;
    }

    public function isMacro()
    {
        return self::MACRO === $this->type;
    }

    public function getProfiles()
    {
        return $this->profiles;
    }

    public function addProfile(self $profile)
    {
        $this->profiles[] = $profile;
    }

    /**
     * Returns the duration in microseconds.
     *
     * @return float
     */
    public function getDuration()
    {
        if ($this->isRoot() && $this->profiles) {
            // for the root node with children, duration is the sum of all
child durations
            $duration = 0;
            foreach ($this->profiles as $profile) {
                $duration += $profile->getDuration();
            }

            return $duration;
        }

        return isset($this->ends['wt']) &&
isset($this->starts['wt']) ? $this->ends['wt'] -
$this->starts['wt'] : 0;
    }

    /**
     * Returns the memory usage in bytes.
     *
     * @return int
     */
    public function getMemoryUsage()
    {
        return isset($this->ends['mu']) &&
isset($this->starts['mu']) ? $this->ends['mu'] -
$this->starts['mu'] : 0;
    }

    /**
     * Returns the peak memory usage in bytes.
     *
     * @return int
     */
    public function getPeakMemoryUsage()
    {
        return isset($this->ends['pmu']) &&
isset($this->starts['pmu']) ? $this->ends['pmu']
- $this->starts['pmu'] : 0;
    }

    /**
     * Starts the profiling.
     */
    public function enter()
    {
        $this->starts = [
            'wt' => microtime(true),
            'mu' => memory_get_usage(),
            'pmu' => memory_get_peak_usage(),
        ];
    }

    /**
     * Stops the profiling.
     */
    public function leave()
    {
        $this->ends = [
            'wt' => microtime(true),
            'mu' => memory_get_usage(),
            'pmu' => memory_get_peak_usage(),
        ];
    }

    public function reset()
    {
        $this->starts = $this->ends = $this->profiles = [];
        $this->enter();
    }

    public function getIterator()
    {
        return new \ArrayIterator($this->profiles);
    }

    public function serialize()
    {
        return serialize($this->__serialize());
    }

    public function unserialize($data)
    {
        $this->__unserialize(unserialize($data));
    }

    /**
     * @internal
     */
    public function __serialize()
    {
        return [$this->template, $this->name, $this->type,
$this->starts, $this->ends, $this->profiles];
    }

    /**
     * @internal
     */
    public function __unserialize(array $data)
    {
        list($this->template, $this->name, $this->type,
$this->starts, $this->ends, $this->profiles) = $data;
    }
}

class_alias('Twig\Profiler\Profile',
'Twig_Profiler_Profile');
vendor/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php000064400000001722151166614750020472
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\RuntimeLoader;

use Psr\Container\ContainerInterface;

/**
 * Lazily loads Twig runtime implementations from a PSR-11 container.
 *
 * Note that the runtime services MUST use their class names as
identifiers.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Robin Chalas <robin.chalas@gmail.com>
 */
class ContainerRuntimeLoader implements RuntimeLoaderInterface
{
    private $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function load($class)
    {
        if ($this->container->has($class)) {
            return $this->container->get($class);
        }
    }
}

class_alias('Twig\RuntimeLoader\ContainerRuntimeLoader',
'Twig_ContainerRuntimeLoader');
vendor/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php000064400000001603151166614750020155
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\RuntimeLoader;

/**
 * Lazy loads the runtime implementations for a Twig element.
 *
 * @author Robin Chalas <robin.chalas@gmail.com>
 */
class FactoryRuntimeLoader implements RuntimeLoaderInterface
{
    private $map;

    /**
     * @param array $map An array where keys are class names and values
factory callables
     */
    public function __construct($map = [])
    {
        $this->map = $map;
    }

    public function load($class)
    {
        if (isset($this->map[$class])) {
            $runtimeFactory = $this->map[$class];

            return $runtimeFactory();
        }
    }
}

class_alias('Twig\RuntimeLoader\FactoryRuntimeLoader',
'Twig_FactoryRuntimeLoader');
vendor/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php000064400000001461151166614750020450
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\RuntimeLoader;

/**
 * Creates runtime implementations for Twig elements
(filters/functions/tests).
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface RuntimeLoaderInterface
{
    /**
     * Creates the runtime implementation of a Twig element
(filter/function/test).
     *
     * @param string $class A runtime class
     *
     * @return object|null The runtime instance or null if the loader does
not know how to create the runtime for this class
     */
    public function load($class);
}

class_alias('Twig\RuntimeLoader\RuntimeLoaderInterface',
'Twig_RuntimeLoaderInterface');
vendor/twig/twig/src/Sandbox/SecurityError.php000064400000000743151166614750015524
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Sandbox;

use Twig\Error\Error;

/**
 * Exception thrown when a security error occurs at runtime.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class SecurityError extends Error
{
}

class_alias('Twig\Sandbox\SecurityError',
'Twig_Sandbox_SecurityError');
vendor/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php000064400000001555151166614750020665
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Sandbox;

/**
 * Exception thrown when a not allowed filter is used in a template.
 *
 * @author Martin Hasoň <martin.hason@gmail.com>
 */
class SecurityNotAllowedFilterError extends SecurityError
{
    private $filterName;

    public function __construct($message, $functionName, $lineno = -1,
$filename = null, \Exception $previous = null)
    {
        parent::__construct($message, $lineno, $filename, $previous);
        $this->filterName = $functionName;
    }

    public function getFilterName()
    {
        return $this->filterName;
    }
}

class_alias('Twig\Sandbox\SecurityNotAllowedFilterError',
'Twig_Sandbox_SecurityNotAllowedFilterError');
vendor/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php000064400000001575151166614750021227
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Sandbox;

/**
 * Exception thrown when a not allowed function is used in a template.
 *
 * @author Martin Hasoň <martin.hason@gmail.com>
 */
class SecurityNotAllowedFunctionError extends SecurityError
{
    private $functionName;

    public function __construct($message, $functionName, $lineno = -1,
$filename = null, \Exception $previous = null)
    {
        parent::__construct($message, $lineno, $filename, $previous);
        $this->functionName = $functionName;
    }

    public function getFunctionName()
    {
        return $this->functionName;
    }
}

class_alias('Twig\Sandbox\SecurityNotAllowedFunctionError',
'Twig_Sandbox_SecurityNotAllowedFunctionError');
vendor/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php000064400000002007151166614750020651
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Sandbox;

/**
 * Exception thrown when a not allowed class method is used in a template.
 *
 * @author Kit Burton-Senior <mail@kitbs.com>
 */
class SecurityNotAllowedMethodError extends SecurityError
{
    private $className;
    private $methodName;

    public function __construct($message, $className, $methodName, $lineno
= -1, $filename = null, \Exception $previous = null)
    {
        parent::__construct($message, $lineno, $filename, $previous);
        $this->className = $className;
        $this->methodName = $methodName;
    }

    public function getClassName()
    {
        return $this->className;
    }

    public function getMethodName()
    {
        return $this->methodName;
    }
}

class_alias('Twig\Sandbox\SecurityNotAllowedMethodError',
'Twig_Sandbox_SecurityNotAllowedMethodError');
vendor/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php000064400000002033151166614750021254
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Sandbox;

/**
 * Exception thrown when a not allowed class property is used in a
template.
 *
 * @author Kit Burton-Senior <mail@kitbs.com>
 */
class SecurityNotAllowedPropertyError extends SecurityError
{
    private $className;
    private $propertyName;

    public function __construct($message, $className, $propertyName,
$lineno = -1, $filename = null, \Exception $previous = null)
    {
        parent::__construct($message, $lineno, $filename, $previous);
        $this->className = $className;
        $this->propertyName = $propertyName;
    }

    public function getClassName()
    {
        return $this->className;
    }

    public function getPropertyName()
    {
        return $this->propertyName;
    }
}

class_alias('Twig\Sandbox\SecurityNotAllowedPropertyError',
'Twig_Sandbox_SecurityNotAllowedPropertyError');
vendor/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php000064400000001513151166614750020145
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Sandbox;

/**
 * Exception thrown when a not allowed tag is used in a template.
 *
 * @author Martin Hasoň <martin.hason@gmail.com>
 */
class SecurityNotAllowedTagError extends SecurityError
{
    private $tagName;

    public function __construct($message, $tagName, $lineno = -1, $filename
= null, \Exception $previous = null)
    {
        parent::__construct($message, $lineno, $filename, $previous);
        $this->tagName = $tagName;
    }

    public function getTagName()
    {
        return $this->tagName;
    }
}

class_alias('Twig\Sandbox\SecurityNotAllowedTagError',
'Twig_Sandbox_SecurityNotAllowedTagError');
vendor/twig/twig/src/Sandbox/SecurityPolicy.php000064400000007723151166614750015677
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Sandbox;

use Twig\Markup;

/**
 * Represents a security policy which need to be enforced when sandbox mode
is enabled.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class SecurityPolicy implements SecurityPolicyInterface
{
    protected $allowedTags;
    protected $allowedFilters;
    protected $allowedMethods;
    protected $allowedProperties;
    protected $allowedFunctions;

    public function __construct(array $allowedTags = [], array
$allowedFilters = [], array $allowedMethods = [], array $allowedProperties
= [], array $allowedFunctions = [])
    {
        $this->allowedTags = $allowedTags;
        $this->allowedFilters = $allowedFilters;
        $this->setAllowedMethods($allowedMethods);
        $this->allowedProperties = $allowedProperties;
        $this->allowedFunctions = $allowedFunctions;
    }

    public function setAllowedTags(array $tags)
    {
        $this->allowedTags = $tags;
    }

    public function setAllowedFilters(array $filters)
    {
        $this->allowedFilters = $filters;
    }

    public function setAllowedMethods(array $methods)
    {
        $this->allowedMethods = [];
        foreach ($methods as $class => $m) {
            $this->allowedMethods[$class] = array_map(function ($value)
{ return strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz'); }, \is_array($m) ? $m : [$m]);
        }
    }

    public function setAllowedProperties(array $properties)
    {
        $this->allowedProperties = $properties;
    }

    public function setAllowedFunctions(array $functions)
    {
        $this->allowedFunctions = $functions;
    }

    public function checkSecurity($tags, $filters, $functions)
    {
        foreach ($tags as $tag) {
            if (!\in_array($tag, $this->allowedTags)) {
                throw new SecurityNotAllowedTagError(sprintf('Tag
"%s" is not allowed.', $tag), $tag);
            }
        }

        foreach ($filters as $filter) {
            if (!\in_array($filter, $this->allowedFilters)) {
                throw new
SecurityNotAllowedFilterError(sprintf('Filter "%s" is not
allowed.', $filter), $filter);
            }
        }

        foreach ($functions as $function) {
            if (!\in_array($function, $this->allowedFunctions)) {
                throw new
SecurityNotAllowedFunctionError(sprintf('Function "%s" is
not allowed.', $function), $function);
            }
        }
    }

    public function checkMethodAllowed($obj, $method)
    {
        if ($obj instanceof \Twig_TemplateInterface || $obj instanceof
Markup) {
            return;
        }

        $allowed = false;
        $method = strtr($method, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz');
        foreach ($this->allowedMethods as $class => $methods) {
            if ($obj instanceof $class) {
                $allowed = \in_array($method, $methods);

                break;
            }
        }

        if (!$allowed) {
            $class = \get_class($obj);
            throw new SecurityNotAllowedMethodError(sprintf('Calling
"%s" method on a "%s" object is not allowed.',
$method, $class), $class, $method);
        }
    }

    public function checkPropertyAllowed($obj, $property)
    {
        $allowed = false;
        foreach ($this->allowedProperties as $class => $properties) {
            if ($obj instanceof $class) {
                $allowed = \in_array($property, \is_array($properties) ?
$properties : [$properties]);

                break;
            }
        }

        if (!$allowed) {
            $class = \get_class($obj);
            throw new SecurityNotAllowedPropertyError(sprintf('Calling
"%s" property on a "%s" object is not allowed.',
$property, $class), $class, $property);
        }
    }
}

class_alias('Twig\Sandbox\SecurityPolicy',
'Twig_Sandbox_SecurityPolicy');
vendor/twig/twig/src/Sandbox/SecurityPolicyInterface.php000064400000001224151166614750017506
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Sandbox;

/**
 * Interface that all security policy classes must implements.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface SecurityPolicyInterface
{
    public function checkSecurity($tags, $filters, $functions);

    public function checkMethodAllowed($obj, $method);

    public function checkPropertyAllowed($obj, $method);
}

class_alias('Twig\Sandbox\SecurityPolicyInterface',
'Twig_Sandbox_SecurityPolicyInterface');
vendor/twig/twig/src/Source.php000064400000002005151166614760012537
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

/**
 * Holds information about a non-compiled Twig template.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class Source
{
    private $code;
    private $name;
    private $path;

    /**
     * @param string $code The template source code
     * @param string $name The template logical name
     * @param string $path The filesystem path of the template if any
     */
    public function __construct($code, $name, $path = '')
    {
        $this->code = $code;
        $this->name = $name;
        $this->path = $path;
    }

    public function getCode()
    {
        return $this->code;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getPath()
    {
        return $this->path;
    }
}

class_alias('Twig\Source', 'Twig_Source');
vendor/twig/twig/src/Template.php000064400000062163151166614760013065
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

use Twig\Error\Error;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;

/**
 * Default base class for compiled templates.
 *
 * This class is an implementation detail of how template compilation
currently
 * works, which might change. It should never be used directly. Use
$twig->load()
 * instead, which returns an instance of \Twig\TemplateWrapper.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @internal
 */
abstract class Template implements \Twig_TemplateInterface
{
    /**
     * @internal
     */
    protected static $cache = [];

    protected $parent;
    protected $parents = [];
    protected $env;
    protected $blocks = [];
    protected $traits = [];
    protected $sandbox;

    public function __construct(Environment $env)
    {
        $this->env = $env;
    }

    /**
     * @internal this method will be removed in 2.0 and is only used
internally to provide an upgrade path from 1.x to 2.0
     */
    public function __toString()
    {
        return $this->getTemplateName();
    }

    /**
     * Returns the template name.
     *
     * @return string The template name
     */
    abstract public function getTemplateName();

    /**
     * Returns debug information about the template.
     *
     * @return array Debug information
     */
    public function getDebugInfo()
    {
        return [];
    }

    /**
     * Returns the template source code.
     *
     * @return string The template source code
     *
     * @deprecated since 1.27 (to be removed in 2.0). Use
getSourceContext() instead
     */
    public function getSource()
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since version 1.27 and will be removed in 2.0. Use
getSourceContext() instead.', E_USER_DEPRECATED);

        return '';
    }

    /**
     * Returns information about the original template source code.
     *
     * @return Source
     */
    public function getSourceContext()
    {
        return new Source('', $this->getTemplateName());
    }

    /**
     * @deprecated since 1.20 (to be removed in 2.0)
     */
    public function getEnvironment()
    {
        @trigger_error('The '.__METHOD__.' method is
deprecated since version 1.20 and will be removed in 2.0.',
E_USER_DEPRECATED);

        return $this->env;
    }

    /**
     * Returns the parent template.
     *
     * This method is for internal use only and should never be called
     * directly.
     *
     * @param array $context
     *
     * @return \Twig_TemplateInterface|TemplateWrapper|false The parent
template or false if there is no parent
     *
     * @internal
     */
    public function getParent(array $context)
    {
        if (null !== $this->parent) {
            return $this->parent;
        }

        try {
            $parent = $this->doGetParent($context);

            if (false === $parent) {
                return false;
            }

            if ($parent instanceof self || $parent instanceof
TemplateWrapper) {
                return
$this->parents[$parent->getSourceContext()->getName()] = $parent;
            }

            if (!isset($this->parents[$parent])) {
                $this->parents[$parent] =
$this->loadTemplate($parent);
            }
        } catch (LoaderError $e) {
            $e->setSourceContext(null);
            $e->guess();

            throw $e;
        }

        return $this->parents[$parent];
    }

    protected function doGetParent(array $context)
    {
        return false;
    }

    public function isTraitable()
    {
        return true;
    }

    /**
     * Displays a parent block.
     *
     * This method is for internal use only and should never be called
     * directly.
     *
     * @param string $name    The block name to display from the parent
     * @param array  $context The context
     * @param array  $blocks  The current set of blocks
     */
    public function displayParentBlock($name, array $context, array $blocks
= [])
    {
        $name = (string) $name;

        if (isset($this->traits[$name])) {
            $this->traits[$name][0]->displayBlock($name, $context,
$blocks, false);
        } elseif (false !== $parent = $this->getParent($context)) {
            $parent->displayBlock($name, $context, $blocks, false);
        } else {
            throw new RuntimeError(sprintf('The template has no parent
and no traits defining the "%s" block.', $name), -1,
$this->getSourceContext());
        }
    }

    /**
     * Displays a block.
     *
     * This method is for internal use only and should never be called
     * directly.
     *
     * @param string $name      The block name to display
     * @param array  $context   The context
     * @param array  $blocks    The current set of blocks
     * @param bool   $useBlocks Whether to use the current set of blocks
     */
    public function displayBlock($name, array $context, array $blocks = [],
$useBlocks = true)
    {
        $name = (string) $name;

        if ($useBlocks && isset($blocks[$name])) {
            $template = $blocks[$name][0];
            $block = $blocks[$name][1];
        } elseif (isset($this->blocks[$name])) {
            $template = $this->blocks[$name][0];
            $block = $this->blocks[$name][1];
        } else {
            $template = null;
            $block = null;
        }

        // avoid RCEs when sandbox is enabled
        if (null !== $template && !$template instanceof self) {
            throw new \LogicException('A block must be a method on a
\Twig\Template instance.');
        }

        if (null !== $template) {
            try {
                $template->$block($context, $blocks);
            } catch (Error $e) {
                if (!$e->getSourceContext()) {
                   
$e->setSourceContext($template->getSourceContext());
                }

                // this is mostly useful for \Twig\Error\LoaderError
exceptions
                // see \Twig\Error\LoaderError
                if (-1 === $e->getTemplateLine()) {
                    $e->guess();
                }

                throw $e;
            } catch (\Exception $e) {
                $e = new RuntimeError(sprintf('An exception has been
thrown during the rendering of a template ("%s").',
$e->getMessage()), -1, $template->getSourceContext(), $e);
                $e->guess();

                throw $e;
            }
        } elseif (false !== $parent = $this->getParent($context)) {
            $parent->displayBlock($name, $context,
array_merge($this->blocks, $blocks), false);
        } else {
            @trigger_error(sprintf('Silent display of undefined block
"%s" in template "%s" is deprecated since version 1.29
and will throw an exception in 2.0. Use the "block(\'%s\')
is defined" expression to test for block existence.', $name,
$this->getTemplateName(), $name), E_USER_DEPRECATED);
        }
    }

    /**
     * Renders a parent block.
     *
     * This method is for internal use only and should never be called
     * directly.
     *
     * @param string $name    The block name to render from the parent
     * @param array  $context The context
     * @param array  $blocks  The current set of blocks
     *
     * @return string The rendered block
     */
    public function renderParentBlock($name, array $context, array $blocks
= [])
    {
        if ($this->env->isDebug()) {
            ob_start();
        } else {
            ob_start(function () { return ''; });
        }
        $this->displayParentBlock($name, $context, $blocks);

        return ob_get_clean();
    }

    /**
     * Renders a block.
     *
     * This method is for internal use only and should never be called
     * directly.
     *
     * @param string $name      The block name to render
     * @param array  $context   The context
     * @param array  $blocks    The current set of blocks
     * @param bool   $useBlocks Whether to use the current set of blocks
     *
     * @return string The rendered block
     */
    public function renderBlock($name, array $context, array $blocks = [],
$useBlocks = true)
    {
        if ($this->env->isDebug()) {
            ob_start();
        } else {
            ob_start(function () { return ''; });
        }
        $this->displayBlock($name, $context, $blocks, $useBlocks);

        return ob_get_clean();
    }

    /**
     * Returns whether a block exists or not in the current context of the
template.
     *
     * This method checks blocks defined in the current template
     * or defined in "used" traits or defined in parent
templates.
     *
     * @param string $name    The block name
     * @param array  $context The context
     * @param array  $blocks  The current set of blocks
     *
     * @return bool true if the block exists, false otherwise
     */
    public function hasBlock($name, array $context = null, array $blocks =
[])
    {
        if (null === $context) {
            @trigger_error('The '.__METHOD__.' method is
internal and should never be called; calling it directly is deprecated
since version 1.28 and won\'t be possible anymore in 2.0.',
E_USER_DEPRECATED);

            return isset($this->blocks[(string) $name]);
        }

        if (isset($blocks[$name])) {
            return $blocks[$name][0] instanceof self;
        }

        if (isset($this->blocks[$name])) {
            return true;
        }

        if (false !== $parent = $this->getParent($context)) {
            return $parent->hasBlock($name, $context);
        }

        return false;
    }

    /**
     * Returns all block names in the current context of the template.
     *
     * This method checks blocks defined in the current template
     * or defined in "used" traits or defined in parent
templates.
     *
     * @param array $context The context
     * @param array $blocks  The current set of blocks
     *
     * @return array An array of block names
     */
    public function getBlockNames(array $context = null, array $blocks =
[])
    {
        if (null === $context) {
            @trigger_error('The '.__METHOD__.' method is
internal and should never be called; calling it directly is deprecated
since version 1.28 and won\'t be possible anymore in 2.0.',
E_USER_DEPRECATED);

            return array_keys($this->blocks);
        }

        $names = array_merge(array_keys($blocks),
array_keys($this->blocks));

        if (false !== $parent = $this->getParent($context)) {
            $names = array_merge($names,
$parent->getBlockNames($context));
        }

        return array_unique($names);
    }

    /**
     * @return Template|TemplateWrapper
     */
    protected function loadTemplate($template, $templateName = null, $line
= null, $index = null)
    {
        try {
            if (\is_array($template)) {
                return $this->env->resolveTemplate($template);
            }

            if ($template instanceof self || $template instanceof
TemplateWrapper) {
                return $template;
            }

            if ($template === $this->getTemplateName()) {
                $class = \get_class($this);
                if (false !== $pos = strrpos($class, '___', -1))
{
                    $class = substr($class, 0, $pos);
                }

                return $this->env->loadClass($class, $template,
$index);
            }

            return $this->env->loadTemplate($template, $index);
        } catch (Error $e) {
            if (!$e->getSourceContext()) {
                $e->setSourceContext($templateName ? new
Source('', $templateName) : $this->getSourceContext());
            }

            if ($e->getTemplateLine() > 0) {
                throw $e;
            }

            if (!$line) {
                $e->guess();
            } else {
                $e->setTemplateLine($line);
            }

            throw $e;
        }
    }

    /**
     * @internal
     *
     * @return Template
     */
    protected function unwrap()
    {
        return $this;
    }

    /**
     * Returns all blocks.
     *
     * This method is for internal use only and should never be called
     * directly.
     *
     * @return array An array of blocks
     */
    public function getBlocks()
    {
        return $this->blocks;
    }

    public function display(array $context, array $blocks = [])
    {
       
$this->displayWithErrorHandling($this->env->mergeGlobals($context),
array_merge($this->blocks, $blocks));
    }

    public function render(array $context)
    {
        $level = ob_get_level();
        if ($this->env->isDebug()) {
            ob_start();
        } else {
            ob_start(function () { return ''; });
        }
        try {
            $this->display($context);
        } catch (\Exception $e) {
            while (ob_get_level() > $level) {
                ob_end_clean();
            }

            throw $e;
        } catch (\Throwable $e) {
            while (ob_get_level() > $level) {
                ob_end_clean();
            }

            throw $e;
        }

        return ob_get_clean();
    }

    protected function displayWithErrorHandling(array $context, array
$blocks = [])
    {
        try {
            $this->doDisplay($context, $blocks);
        } catch (Error $e) {
            if (!$e->getSourceContext()) {
                $e->setSourceContext($this->getSourceContext());
            }

            // this is mostly useful for \Twig\Error\LoaderError exceptions
            // see \Twig\Error\LoaderError
            if (-1 === $e->getTemplateLine()) {
                $e->guess();
            }

            throw $e;
        } catch (\Exception $e) {
            $e = new RuntimeError(sprintf('An exception has been
thrown during the rendering of a template ("%s").',
$e->getMessage()), -1, $this->getSourceContext(), $e);
            $e->guess();

            throw $e;
        }
    }

    /**
     * Auto-generated method to display the template with the given
context.
     *
     * @param array $context An array of parameters to pass to the template
     * @param array $blocks  An array of blocks to pass to the template
     */
    abstract protected function doDisplay(array $context, array $blocks =
[]);

    /**
     * Returns a variable from the context.
     *
     * This method is for internal use only and should never be called
     * directly.
     *
     * This method should not be overridden in a sub-class as this is an
     * implementation detail that has been introduced to optimize variable
     * access for versions of PHP before 5.4. This is not a way to override
     * the way to get a variable value.
     *
     * @param array  $context           The context
     * @param string $item              The variable to return from the
context
     * @param bool   $ignoreStrictCheck Whether to ignore the strict
variable check or not
     *
     * @return mixed The content of the context variable
     *
     * @throws RuntimeError if the variable does not exist and Twig is
running in strict mode
     *
     * @internal
     */
    final protected function getContext($context, $item, $ignoreStrictCheck
= false)
    {
        if (!\array_key_exists($item, $context)) {
            if ($ignoreStrictCheck ||
!$this->env->isStrictVariables()) {
                return;
            }

            throw new RuntimeError(sprintf('Variable "%s"
does not exist.', $item), -1, $this->getSourceContext());
        }

        return $context[$item];
    }

    /**
     * Returns the attribute value for a given array/object.
     *
     * @param mixed  $object            The object or array from where to
get the item
     * @param mixed  $item              The item to get from the array or
object
     * @param array  $arguments         An array of arguments to pass if
the item is an object method
     * @param string $type              The type of attribute (@see
\Twig\Template constants)
     * @param bool   $isDefinedTest     Whether this is only a defined
check
     * @param bool   $ignoreStrictCheck Whether to ignore the strict
attribute check or not
     *
     * @return mixed The attribute value, or a Boolean when $isDefinedTest
is true, or null when the attribute is not set and $ignoreStrictCheck is
true
     *
     * @throws RuntimeError if the attribute does not exist and Twig is
running in strict mode and $isDefinedTest is false
     *
     * @internal
     */
    protected function getAttribute($object, $item, array $arguments = [],
$type = self::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
    {
        // array
        if (self::METHOD_CALL !== $type) {
            $arrayItem = \is_bool($item) || \is_float($item) ? (int) $item
: $item;

            if (((\is_array($object) || $object instanceof \ArrayObject)
&& (isset($object[$arrayItem]) || \array_key_exists($arrayItem,
(array) $object)))
                || ($object instanceof \ArrayAccess &&
isset($object[$arrayItem]))
            ) {
                if ($isDefinedTest) {
                    return true;
                }

                return $object[$arrayItem];
            }

            if (self::ARRAY_CALL === $type || !\is_object($object)) {
                if ($isDefinedTest) {
                    return false;
                }

                if ($ignoreStrictCheck ||
!$this->env->isStrictVariables()) {
                    return;
                }

                if ($object instanceof \ArrayAccess) {
                    $message = sprintf('Key "%s" in object
with ArrayAccess of class "%s" does not exist.', $arrayItem,
\get_class($object));
                } elseif (\is_object($object)) {
                    $message = sprintf('Impossible to access a key
"%s" on an object of class "%s" that does not implement
ArrayAccess interface.', $item, \get_class($object));
                } elseif (\is_array($object)) {
                    if (empty($object)) {
                        $message = sprintf('Key "%s" does
not exist as the array is empty.', $arrayItem);
                    } else {
                        $message = sprintf('Key "%s" for
array with keys "%s" does not exist.', $arrayItem,
implode(', ', array_keys($object)));
                    }
                } elseif (self::ARRAY_CALL === $type) {
                    if (null === $object) {
                        $message = sprintf('Impossible to access a key
("%s") on a null variable.', $item);
                    } else {
                        $message = sprintf('Impossible to access a key
("%s") on a %s variable ("%s").', $item,
\gettype($object), $object);
                    }
                } elseif (null === $object) {
                    $message = sprintf('Impossible to access an
attribute ("%s") on a null variable.', $item);
                } else {
                    $message = sprintf('Impossible to access an
attribute ("%s") on a %s variable ("%s").', $item,
\gettype($object), $object);
                }

                throw new RuntimeError($message, -1,
$this->getSourceContext());
            }
        }

        if (!\is_object($object)) {
            if ($isDefinedTest) {
                return false;
            }

            if ($ignoreStrictCheck ||
!$this->env->isStrictVariables()) {
                return;
            }

            if (null === $object) {
                $message = sprintf('Impossible to invoke a method
("%s") on a null variable.', $item);
            } elseif (\is_array($object)) {
                $message = sprintf('Impossible to invoke a method
("%s") on an array.', $item);
            } else {
                $message = sprintf('Impossible to invoke a method
("%s") on a %s variable ("%s").', $item,
\gettype($object), $object);
            }

            throw new RuntimeError($message, -1,
$this->getSourceContext());
        }

        // object property
        if (self::METHOD_CALL !== $type && !$object instanceof
self) { // \Twig\Template does not have public properties, and we
don't want to allow access to internal ones
            if (isset($object->$item) || \array_key_exists((string)
$item, (array) $object)) {
                if ($isDefinedTest) {
                    return true;
                }

                if
($this->env->hasExtension('\Twig\Extension\SandboxExtension'))
{
                   
$this->env->getExtension('\Twig\Extension\SandboxExtension')->checkPropertyAllowed($object,
$item);
                }

                return $object->$item;
            }
        }

        $class = \get_class($object);

        // object method
        if (!isset(self::$cache[$class])) {
            // get_class_methods returns all methods accessible in the
scope, but we only want public ones to be accessible in templates
            if ($object instanceof self) {
                $ref = new \ReflectionClass($class);
                $methods = [];

                foreach ($ref->getMethods(\ReflectionMethod::IS_PUBLIC)
as $refMethod) {
                    // Accessing the environment from templates is
forbidden to prevent untrusted changes to the environment
                    if ('getenvironment' !==
strtr($refMethod->name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz')) {
                        $methods[] = $refMethod->name;
                    }
                }
            } else {
                $methods = get_class_methods($object);
            }
            // sort values to have consistent behavior, so that
"get" methods win precedence over "is" methods
            sort($methods);

            $cache = [];

            foreach ($methods as $method) {
                $cache[$method] = $method;
                $cache[$lcName = strtr($method,
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz')] = $method;

                if ('g' === $lcName[0] && 0 ===
strpos($lcName, 'get')) {
                    $name = substr($method, 3);
                    $lcName = substr($lcName, 3);
                } elseif ('i' === $lcName[0] && 0 ===
strpos($lcName, 'is')) {
                    $name = substr($method, 2);
                    $lcName = substr($lcName, 2);
                } else {
                    continue;
                }

                // skip get() and is() methods (in which case, $name is
empty)
                if ($name) {
                    if (!isset($cache[$name])) {
                        $cache[$name] = $method;
                    }
                    if (!isset($cache[$lcName])) {
                        $cache[$lcName] = $method;
                    }
                }
            }
            self::$cache[$class] = $cache;
        }

        $call = false;
        if (isset(self::$cache[$class][$item])) {
            $method = self::$cache[$class][$item];
        } elseif (isset(self::$cache[$class][$lcItem = strtr($item,
'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz')])) {
            $method = self::$cache[$class][$lcItem];
        } elseif (isset(self::$cache[$class]['__call'])) {
            $method = $item;
            $call = true;
        } else {
            if ($isDefinedTest) {
                return false;
            }

            if ($ignoreStrictCheck ||
!$this->env->isStrictVariables()) {
                return;
            }

            throw new RuntimeError(sprintf('Neither the property
"%1$s" nor one of the methods "%1$s()",
"get%1$s()"/"is%1$s()" or "__call()" exist
and have public access in class "%2$s".', $item, $class),
-1, $this->getSourceContext());
        }

        if ($isDefinedTest) {
            return true;
        }

        if
($this->env->hasExtension('\Twig\Extension\SandboxExtension'))
{
           
$this->env->getExtension('\Twig\Extension\SandboxExtension')->checkMethodAllowed($object,
$method);
        }

        // Some objects throw exceptions when they have __call, and the
method we try
        // to call is not supported. If ignoreStrictCheck is true, we
should return null.
        try {
            if (!$arguments) {
                $ret = $object->$method();
            } else {
                $ret = \call_user_func_array([$object, $method],
$arguments);
            }
        } catch (\BadMethodCallException $e) {
            if ($call && ($ignoreStrictCheck ||
!$this->env->isStrictVariables())) {
                return;
            }
            throw $e;
        }

        // @deprecated in 1.28
        if ($object instanceof \Twig_TemplateInterface) {
            $self = $object->getTemplateName() ===
$this->getTemplateName();
            $message = sprintf('Calling "%s" on template
"%s" from template "%s" is deprecated since version
1.28 and won\'t be supported anymore in 2.0.', $item,
$object->getTemplateName(), $this->getTemplateName());
            if ('renderBlock' === $method ||
'displayBlock' === $method) {
                $message .= sprintf(' Use block("%s"%s)
instead).', $arguments[0], $self ? '' : ',
template');
            } elseif ('hasBlock' === $method) {
                $message .= sprintf(' Use
"block("%s"%s) is defined" instead).',
$arguments[0], $self ? '' : ', template');
            } elseif ('render' === $method || 'display'
=== $method) {
                $message .= sprintf(' Use include("%s")
instead).', $object->getTemplateName());
            }
            @trigger_error($message, E_USER_DEPRECATED);

            return '' === $ret ? '' : new Markup($ret,
$this->env->getCharset());
        }

        return $ret;
    }
}

class_alias('Twig\Template', 'Twig_Template');
vendor/twig/twig/src/TemplateWrapper.php000064400000007636151166614760014432
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

/**
 * Exposes a template to userland.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class TemplateWrapper
{
    private $env;
    private $template;

    /**
     * This method is for internal use only and should never be called
     * directly (use Twig\Environment::load() instead).
     *
     * @internal
     */
    public function __construct(Environment $env, Template $template)
    {
        $this->env = $env;
        $this->template = $template;
    }

    /**
     * Renders the template.
     *
     * @param array $context An array of parameters to pass to the template
     *
     * @return string The rendered template
     */
    public function render($context = [])
    {
        // using func_get_args() allows to not expose the blocks argument
        // as it should only be used by internal code
        return $this->template->render($context, \func_num_args()
> 1 ? func_get_arg(1) : []);
    }

    /**
     * Displays the template.
     *
     * @param array $context An array of parameters to pass to the template
     */
    public function display($context = [])
    {
        // using func_get_args() allows to not expose the blocks argument
        // as it should only be used by internal code
        $this->template->display($context, \func_num_args() > 1 ?
func_get_arg(1) : []);
    }

    /**
     * Checks if a block is defined.
     *
     * @param string $name    The block name
     * @param array  $context An array of parameters to pass to the
template
     *
     * @return bool
     */
    public function hasBlock($name, $context = [])
    {
        return $this->template->hasBlock($name, $context);
    }

    /**
     * Returns defined block names in the template.
     *
     * @param array $context An array of parameters to pass to the template
     *
     * @return string[] An array of defined template block names
     */
    public function getBlockNames($context = [])
    {
        return $this->template->getBlockNames($context);
    }

    /**
     * Renders a template block.
     *
     * @param string $name    The block name to render
     * @param array  $context An array of parameters to pass to the
template
     *
     * @return string The rendered block
     */
    public function renderBlock($name, $context = [])
    {
        $context = $this->env->mergeGlobals($context);
        $level = ob_get_level();
        if ($this->env->isDebug()) {
            ob_start();
        } else {
            ob_start(function () { return ''; });
        }
        try {
            $this->template->displayBlock($name, $context);
        } catch (\Exception $e) {
            while (ob_get_level() > $level) {
                ob_end_clean();
            }

            throw $e;
        } catch (\Throwable $e) {
            while (ob_get_level() > $level) {
                ob_end_clean();
            }

            throw $e;
        }

        return ob_get_clean();
    }

    /**
     * Displays a template block.
     *
     * @param string $name    The block name to render
     * @param array  $context An array of parameters to pass to the
template
     */
    public function displayBlock($name, $context = [])
    {
        $this->template->displayBlock($name,
$this->env->mergeGlobals($context));
    }

    /**
     * @return Source
     */
    public function getSourceContext()
    {
        return $this->template->getSourceContext();
    }

    /**
     * @return string
     */
    public function getTemplateName()
    {
        return $this->template->getTemplateName();
    }

    /**
     * @internal
     *
     * @return Template
     */
    public function unwrap()
    {
        return $this->template;
    }
}

class_alias('Twig\TemplateWrapper',
'Twig_TemplateWrapper');
vendor/twig/twig/src/Test/IntegrationTestCase.php000064400000020401151166614760016135
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Test;

use PHPUnit\Framework\TestCase;
use Twig\Environment;
use Twig\Error\Error;
use Twig\Extension\ExtensionInterface;
use Twig\Loader\ArrayLoader;
use Twig\Loader\SourceContextLoaderInterface;
use Twig\RuntimeLoader\RuntimeLoaderInterface;
use Twig\Source;
use Twig\TwigFilter;
use Twig\TwigFunction;
use Twig\TwigTest;

/**
 * Integration test helper.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 * @author Karma Dordrak <drak@zikula.org>
 */
abstract class IntegrationTestCase extends TestCase
{
    /**
     * @return string
     */
    abstract protected function getFixturesDir();

    /**
     * @return RuntimeLoaderInterface[]
     */
    protected function getRuntimeLoaders()
    {
        return [];
    }

    /**
     * @return ExtensionInterface[]
     */
    protected function getExtensions()
    {
        return [];
    }

    /**
     * @return TwigFilter[]
     */
    protected function getTwigFilters()
    {
        return [];
    }

    /**
     * @return TwigFunction[]
     */
    protected function getTwigFunctions()
    {
        return [];
    }

    /**
     * @return TwigTest[]
     */
    protected function getTwigTests()
    {
        return [];
    }

    /**
     * @dataProvider getTests
     */
    public function testIntegration($file, $message, $condition,
$templates, $exception, $outputs)
    {
        $this->doIntegrationTest($file, $message, $condition,
$templates, $exception, $outputs);
    }

    /**
     * @dataProvider getLegacyTests
     * @group legacy
     */
    public function testLegacyIntegration($file, $message, $condition,
$templates, $exception, $outputs)
    {
        $this->doIntegrationTest($file, $message, $condition,
$templates, $exception, $outputs);
    }

    public function getTests($name, $legacyTests = false)
    {
        $fixturesDir = realpath($this->getFixturesDir());
        $tests = [];

        foreach (new \RecursiveIteratorIterator(new
\RecursiveDirectoryIterator($fixturesDir),
\RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
            if (!preg_match('/\.test$/', $file)) {
                continue;
            }

            if ($legacyTests xor false !== strpos($file->getRealpath(),
'.legacy.test')) {
                continue;
            }

            $test = file_get_contents($file->getRealpath());

            if
(preg_match('/--TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)\s*(?:--DATA--\s*(.*))?\s*--EXCEPTION--\s*(.*)/sx',
$test, $match)) {
                $message = $match[1];
                $condition = $match[2];
                $templates = self::parseTemplates($match[3]);
                $exception = $match[5];
                $outputs = [[null, $match[4], null, '']];
            } elseif
(preg_match('/--TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)--DATA--.*?--EXPECT--.*/s',
$test, $match)) {
                $message = $match[1];
                $condition = $match[2];
                $templates = self::parseTemplates($match[3]);
                $exception = false;
               
preg_match_all('/--DATA--(.*?)(?:--CONFIG--(.*?))?--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s',
$test, $outputs, PREG_SET_ORDER);
            } else {
                throw new \InvalidArgumentException(sprintf('Test
"%s" is not valid.', str_replace($fixturesDir.'/',
'', $file)));
            }

            $tests[] = [str_replace($fixturesDir.'/',
'', $file), $message, $condition, $templates, $exception,
$outputs];
        }

        if ($legacyTests && empty($tests)) {
            // add a dummy test to avoid a PHPUnit message
            return [['not', '-', '', [],
'', []]];
        }

        return $tests;
    }

    public function getLegacyTests()
    {
        return $this->getTests('testLegacyIntegration', true);
    }

    protected function doIntegrationTest($file, $message, $condition,
$templates, $exception, $outputs)
    {
        if (!$outputs) {
            $this->markTestSkipped('no tests to run');
        }

        if ($condition) {
            eval('$ret = '.$condition.';');
            if (!$ret) {
                $this->markTestSkipped($condition);
            }
        }

        $loader = new ArrayLoader($templates);

        foreach ($outputs as $i => $match) {
            $config = array_merge([
                'cache' => false,
                'strict_variables' => true,
            ], $match[2] ? eval($match[2].';') : []);
            $twig = new Environment($loader, $config);
            $twig->addGlobal('global', 'global');
            foreach ($this->getRuntimeLoaders() as $runtimeLoader) {
                $twig->addRuntimeLoader($runtimeLoader);
            }

            foreach ($this->getExtensions() as $extension) {
                $twig->addExtension($extension);
            }

            foreach ($this->getTwigFilters() as $filter) {
                $twig->addFilter($filter);
            }

            foreach ($this->getTwigTests() as $test) {
                $twig->addTest($test);
            }

            foreach ($this->getTwigFunctions() as $function) {
                $twig->addFunction($function);
            }

            $p = new \ReflectionProperty($twig,
'templateClassPrefix');
            $p->setAccessible(true);
            $p->setValue($twig,
'__TwigTemplate_'.hash('sha256', uniqid(mt_rand(),
true), false).'_');

            try {
                $template = $twig->load('index.twig');
            } catch (\Exception $e) {
                if (false !== $exception) {
                    $message = $e->getMessage();
                    $this->assertSame(trim($exception),
trim(sprintf('%s: %s', \get_class($e), $message)));
                    $last = substr($message, \strlen($message) - 1);
                    $this->assertTrue('.' === $last ||
'?' === $last, 'Exception message must end with a dot or a
question mark.');

                    return;
                }

                throw new Error(sprintf('%s: %s', \get_class($e),
$e->getMessage()), -1, null, $e);
            }

            try {
                $output =
trim($template->render(eval($match[1].';')), "\n ");
            } catch (\Exception $e) {
                if (false !== $exception) {
                    $this->assertSame(trim($exception),
trim(sprintf('%s: %s', \get_class($e), $e->getMessage())));

                    return;
                }

                $e = new Error(sprintf('%s: %s', \get_class($e),
$e->getMessage()), -1, null, $e);

                $output = trim(sprintf('%s: %s', \get_class($e),
$e->getMessage()));
            }

            if (false !== $exception) {
                list($class) = explode(':', $exception);
                $constraintClass =
class_exists('PHPUnit\Framework\Constraint\Exception') ?
'PHPUnit\Framework\Constraint\Exception' :
'PHPUnit_Framework_Constraint_Exception';
                $this->assertThat(null, new $constraintClass($class));
            }

            $expected = trim($match[3], "\n ");

            if ($expected !== $output) {
                printf("Compiled templates that failed on case
%d:\n", $i + 1);

                foreach (array_keys($templates) as $name) {
                    echo "Template: $name\n";
                    $loader = $twig->getLoader();
                    if (!$loader instanceof SourceContextLoaderInterface) {
                        $source = new Source($loader->getSource($name),
$name);
                    } else {
                        $source = $loader->getSourceContext($name);
                    }
                    echo
$twig->compile($twig->parse($twig->tokenize($source)));
                }
            }
            $this->assertEquals($expected, $output, $message.' (in
'.$file.')');
        }
    }

    protected static function parseTemplates($test)
    {
        $templates = [];
       
preg_match_all('/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=\-\-TEMPLATE|$)/s',
$test, $matches, PREG_SET_ORDER);
        foreach ($matches as $match) {
            $templates[($match[1] ? $match[1] : 'index.twig')] =
$match[2];
        }

        return $templates;
    }
}

class_alias('Twig\Test\IntegrationTestCase',
'Twig_Test_IntegrationTestCase');
vendor/twig/twig/src/Test/NodeTestCase.php000064400000004152151166614760014544
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Test;

use PHPUnit\Framework\TestCase;
use Twig\Compiler;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\Node\Node;

abstract class NodeTestCase extends TestCase
{
    abstract public function getTests();

    /**
     * @dataProvider getTests
     */
    public function testCompile($node, $source, $environment = null,
$isPattern = false)
    {
        $this->assertNodeCompilation($source, $node, $environment,
$isPattern);
    }

    public function assertNodeCompilation($source, Node $node, Environment
$environment = null, $isPattern = false)
    {
        $compiler = $this->getCompiler($environment);
        $compiler->compile($node);

        if ($isPattern) {
            $this->assertStringMatchesFormat($source,
trim($compiler->getSource()));
        } else {
            $this->assertEquals($source,
trim($compiler->getSource()));
        }
    }

    protected function getCompiler(Environment $environment = null)
    {
        return new Compiler(null === $environment ?
$this->getEnvironment() : $environment);
    }

    protected function getEnvironment()
    {
        return new Environment(new ArrayLoader([]));
    }

    protected function getVariableGetter($name, $line = false)
    {
        $line = $line > 0 ? "// line {$line}\n" :
'';

        if (\PHP_VERSION_ID >= 70000) {
            return sprintf('%s($context["%s"] ??
null)', $line, $name);
        }

        if (\PHP_VERSION_ID >= 50400) {
            return sprintf('%s(isset($context["%s"]) ?
$context["%s"] : null)', $line, $name, $name);
        }

        return sprintf('%s$this->getContext($context,
"%s")', $line, $name);
    }

    protected function getAttributeGetter()
    {
        if (\function_exists('twig_template_get_attributes')) {
            return 'twig_template_get_attributes($this, ';
        }

        return '$this->getAttribute(';
    }
}

class_alias('Twig\Test\NodeTestCase',
'Twig_Test_NodeTestCase');
vendor/twig/twig/src/Token.php000064400000013551151166614760012367
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

/**
 * Represents a Token.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @final
 */
class Token
{
    protected $value;
    protected $type;
    protected $lineno;

    const EOF_TYPE = -1;
    const TEXT_TYPE = 0;
    const BLOCK_START_TYPE = 1;
    const VAR_START_TYPE = 2;
    const BLOCK_END_TYPE = 3;
    const VAR_END_TYPE = 4;
    const NAME_TYPE = 5;
    const NUMBER_TYPE = 6;
    const STRING_TYPE = 7;
    const OPERATOR_TYPE = 8;
    const PUNCTUATION_TYPE = 9;
    const INTERPOLATION_START_TYPE = 10;
    const INTERPOLATION_END_TYPE = 11;
    const ARROW_TYPE = 12;

    /**
     * @param int    $type   The type of the token
     * @param string $value  The token value
     * @param int    $lineno The line position in the source
     */
    public function __construct($type, $value, $lineno)
    {
        $this->type = $type;
        $this->value = $value;
        $this->lineno = $lineno;
    }

    public function __toString()
    {
        return sprintf('%s(%s)',
self::typeToString($this->type, true), $this->value);
    }

    /**
     * Tests the current token for a type and/or a value.
     *
     * Parameters may be:
     *  * just type
     *  * type and value (or array of possible values)
     *  * just value (or array of possible values) (NAME_TYPE is used as
type)
     *
     * @param array|string|int  $type   The type to test
     * @param array|string|null $values The token value
     *
     * @return bool
     */
    public function test($type, $values = null)
    {
        if (null === $values && !\is_int($type)) {
            $values = $type;
            $type = self::NAME_TYPE;
        }

        return ($this->type === $type) && (
            null === $values ||
            (\is_array($values) && \in_array($this->value,
$values)) ||
            $this->value == $values
        );
    }

    /**
     * @return int
     */
    public function getLine()
    {
        return $this->lineno;
    }

    /**
     * @return int
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @return string
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * Returns the constant representation (internal) of a given type.
     *
     * @param int  $type  The type as an integer
     * @param bool $short Whether to return a short representation or not
     *
     * @return string The string representation
     */
    public static function typeToString($type, $short = false)
    {
        switch ($type) {
            case self::EOF_TYPE:
                $name = 'EOF_TYPE';
                break;
            case self::TEXT_TYPE:
                $name = 'TEXT_TYPE';
                break;
            case self::BLOCK_START_TYPE:
                $name = 'BLOCK_START_TYPE';
                break;
            case self::VAR_START_TYPE:
                $name = 'VAR_START_TYPE';
                break;
            case self::BLOCK_END_TYPE:
                $name = 'BLOCK_END_TYPE';
                break;
            case self::VAR_END_TYPE:
                $name = 'VAR_END_TYPE';
                break;
            case self::NAME_TYPE:
                $name = 'NAME_TYPE';
                break;
            case self::NUMBER_TYPE:
                $name = 'NUMBER_TYPE';
                break;
            case self::STRING_TYPE:
                $name = 'STRING_TYPE';
                break;
            case self::OPERATOR_TYPE:
                $name = 'OPERATOR_TYPE';
                break;
            case self::PUNCTUATION_TYPE:
                $name = 'PUNCTUATION_TYPE';
                break;
            case self::INTERPOLATION_START_TYPE:
                $name = 'INTERPOLATION_START_TYPE';
                break;
            case self::INTERPOLATION_END_TYPE:
                $name = 'INTERPOLATION_END_TYPE';
                break;
            case self::ARROW_TYPE:
                $name = 'ARROW_TYPE';
                break;
            default:
                throw new \LogicException(sprintf('Token of type
"%s" does not exist.', $type));
        }

        return $short ? $name : 'Twig\Token::'.$name;
    }

    /**
     * Returns the English representation of a given type.
     *
     * @param int $type The type as an integer
     *
     * @return string The string representation
     */
    public static function typeToEnglish($type)
    {
        switch ($type) {
            case self::EOF_TYPE:
                return 'end of template';
            case self::TEXT_TYPE:
                return 'text';
            case self::BLOCK_START_TYPE:
                return 'begin of statement block';
            case self::VAR_START_TYPE:
                return 'begin of print statement';
            case self::BLOCK_END_TYPE:
                return 'end of statement block';
            case self::VAR_END_TYPE:
                return 'end of print statement';
            case self::NAME_TYPE:
                return 'name';
            case self::NUMBER_TYPE:
                return 'number';
            case self::STRING_TYPE:
                return 'string';
            case self::OPERATOR_TYPE:
                return 'operator';
            case self::PUNCTUATION_TYPE:
                return 'punctuation';
            case self::INTERPOLATION_START_TYPE:
                return 'begin of string interpolation';
            case self::INTERPOLATION_END_TYPE:
                return 'end of string interpolation';
            case self::ARROW_TYPE:
                return 'arrow function';
            default:
                throw new \LogicException(sprintf('Token of type
"%s" does not exist.', $type));
        }
    }
}

class_alias('Twig\Token', 'Twig_Token');
vendor/twig/twig/src/TokenParser/AbstractTokenParser.php000064400000001201151166614760017452
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Parser;

/**
 * Base class for all token parsers.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class AbstractTokenParser implements TokenParserInterface
{
    /**
     * @var Parser
     */
    protected $parser;

    public function setParser(Parser $parser)
    {
        $this->parser = $parser;
    }
}

class_alias('Twig\TokenParser\AbstractTokenParser',
'Twig_TokenParser');
vendor/twig/twig/src/TokenParser/ApplyTokenParser.php000064400000002661151166614760017007
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Node\Expression\TempNameExpression;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\Node\SetNode;
use Twig\Token;

/**
 * Applies filters on a section of a template.
 *
 *   {% apply upper %}
 *      This text becomes uppercase
 *   {% endapplys %}
 */
final class ApplyTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $lineno = $token->getLine();
        $name = $this->parser->getVarName();

        $ref = new TempNameExpression($name, $lineno);
        $ref->setAttribute('always_defined', true);

        $filter =
$this->parser->getExpressionParser()->parseFilterExpressionRaw($ref,
$this->getTag());

        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse([$this,
'decideApplyEnd'], true);
        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

        return new Node([
            new SetNode(true, $ref, $body, $lineno, $this->getTag()),
            new PrintNode($filter, $lineno, $this->getTag()),
        ]);
    }

    public function decideApplyEnd(Token $token)
    {
        return $token->test('endapply');
    }

    public function getTag()
    {
        return 'apply';
    }
}
vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php000064400000005132151166614760017747
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Node\AutoEscapeNode;
use Twig\Node\Expression\ConstantExpression;
use Twig\Token;

/**
 * Marks a section of a template to be escaped or not.
 *
 *   {% autoescape true %}
 *     Everything will be automatically escaped in this block
 *   {% endautoescape %}
 *
 *   {% autoescape false %}
 *     Everything will be outputed as is in this block
 *   {% endautoescape %}
 *
 *   {% autoescape true js %}
 *     Everything will be automatically escaped in this block
 *     using the js escaping strategy
 *   {% endautoescape %}
 *
 * @final
 */
class AutoEscapeTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        if ($stream->test(Token::BLOCK_END_TYPE)) {
            $value = 'html';
        } else {
            $expr =
$this->parser->getExpressionParser()->parseExpression();
            if (!$expr instanceof ConstantExpression) {
                throw new SyntaxError('An escaping strategy must be a
string or a bool.', $stream->getCurrent()->getLine(),
$stream->getSourceContext());
            }
            $value = $expr->getAttribute('value');

            $compat = true === $value || false === $value;

            if (true === $value) {
                $value = 'html';
            }

            if ($compat && $stream->test(Token::NAME_TYPE)) {
                @trigger_error('Using the autoescape tag with
"true" or "false" before the strategy name is
deprecated since version 1.21.', E_USER_DEPRECATED);

                if (false === $value) {
                    throw new SyntaxError('Unexpected escaping
strategy as you set autoescaping to false.',
$stream->getCurrent()->getLine(), $stream->getSourceContext());
                }

                $value = $stream->next()->getValue();
            }
        }

        $stream->expect(Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse([$this,
'decideBlockEnd'], true);
        $stream->expect(Token::BLOCK_END_TYPE);

        return new AutoEscapeNode($value, $body, $lineno,
$this->getTag());
    }

    public function decideBlockEnd(Token $token)
    {
        return $token->test('endautoescape');
    }

    public function getTag()
    {
        return 'autoescape';
    }
}

class_alias('Twig\TokenParser\AutoEscapeTokenParser',
'Twig_TokenParser_AutoEscape');
vendor/twig/twig/src/TokenParser/BlockTokenParser.php000064400000004677151166614760016765
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Node\BlockNode;
use Twig\Node\BlockReferenceNode;
use Twig\Node\Node;
use Twig\Node\PrintNode;
use Twig\Token;

/**
 * Marks a section of a template as being reusable.
 *
 *  {% block head %}
 *    <link rel="stylesheet" href="style.css" />
 *    <title>{% block title %}{% endblock %} - My
Webpage</title>
 *  {% endblock %}
 *
 * @final
 */
class BlockTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();
        $name = $stream->expect(Token::NAME_TYPE)->getValue();
        if ($this->parser->hasBlock($name)) {
            throw new SyntaxError(sprintf("The block '%s'
has already been defined line %d.", $name,
$this->parser->getBlock($name)->getTemplateLine()),
$stream->getCurrent()->getLine(), $stream->getSourceContext());
        }
        $this->parser->setBlock($name, $block = new BlockNode($name,
new Node([]), $lineno));
        $this->parser->pushLocalScope();
        $this->parser->pushBlockStack($name);

        if ($stream->nextIf(Token::BLOCK_END_TYPE)) {
            $body = $this->parser->subparse([$this,
'decideBlockEnd'], true);
            if ($token = $stream->nextIf(Token::NAME_TYPE)) {
                $value = $token->getValue();

                if ($value != $name) {
                    throw new SyntaxError(sprintf('Expected endblock
for block "%s" (but "%s" given).', $name, $value),
$stream->getCurrent()->getLine(), $stream->getSourceContext());
                }
            }
        } else {
            $body = new Node([
                new
PrintNode($this->parser->getExpressionParser()->parseExpression(),
$lineno),
            ]);
        }
        $stream->expect(Token::BLOCK_END_TYPE);

        $block->setNode('body', $body);
        $this->parser->popBlockStack();
        $this->parser->popLocalScope();

        return new BlockReferenceNode($name, $lineno, $this->getTag());
    }

    public function decideBlockEnd(Token $token)
    {
        return $token->test('endblock');
    }

    public function getTag()
    {
        return 'block';
    }
}

class_alias('Twig\TokenParser\BlockTokenParser',
'Twig_TokenParser_Block');
vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php000064400000001765151166614760017766
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Node\DeprecatedNode;
use Twig\Token;

/**
 * Deprecates a section of a template.
 *
 *    {% deprecated 'The "base.twig" template is deprecated,
use "layout.twig" instead.' %}
 *    {% extends 'layout.html.twig' %}
 *
 * @author Yonel Ceruto <yonelceruto@gmail.com>
 *
 * @final
 */
class DeprecatedTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $expr =
$this->parser->getExpressionParser()->parseExpression();

        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

        return new DeprecatedNode($expr, $token->getLine(),
$this->getTag());
    }

    public function getTag()
    {
        return 'deprecated';
    }
}

class_alias('Twig\TokenParser\DeprecatedTokenParser',
'Twig_TokenParser_Deprecated');
vendor/twig/twig/src/TokenParser/DoTokenParser.php000064400000001440151166614760016256
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Node\DoNode;
use Twig\Token;

/**
 * Evaluates an expression, discarding the returned value.
 *
 * @final
 */
class DoTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $expr =
$this->parser->getExpressionParser()->parseExpression();

        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

        return new DoNode($expr, $token->getLine(), $this->getTag());
    }

    public function getTag()
    {
        return 'do';
    }
}

class_alias('Twig\TokenParser\DoTokenParser',
'Twig_TokenParser_Do');
vendor/twig/twig/src/TokenParser/EmbedTokenParser.php000064400000004304151166614760016732
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Node\EmbedNode;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Token;

/**
 * Embeds a template.
 *
 * @final
 */
class EmbedTokenParser extends IncludeTokenParser
{
    public function parse(Token $token)
    {
        $stream = $this->parser->getStream();

        $parent =
$this->parser->getExpressionParser()->parseExpression();

        list($variables, $only, $ignoreMissing) =
$this->parseArguments();

        $parentToken = $fakeParentToken = new Token(Token::STRING_TYPE,
'__parent__', $token->getLine());
        if ($parent instanceof ConstantExpression) {
            $parentToken = new Token(Token::STRING_TYPE,
$parent->getAttribute('value'), $token->getLine());
        } elseif ($parent instanceof NameExpression) {
            $parentToken = new Token(Token::NAME_TYPE,
$parent->getAttribute('name'), $token->getLine());
        }

        // inject a fake parent to make the parent() function work
        $stream->injectTokens([
            new Token(Token::BLOCK_START_TYPE, '',
$token->getLine()),
            new Token(Token::NAME_TYPE, 'extends',
$token->getLine()),
            $parentToken,
            new Token(Token::BLOCK_END_TYPE, '',
$token->getLine()),
        ]);

        $module = $this->parser->parse($stream, [$this,
'decideBlockEnd'], true);

        // override the parent with the correct one
        if ($fakeParentToken === $parentToken) {
            $module->setNode('parent', $parent);
        }

        $this->parser->embedTemplate($module);

        $stream->expect(Token::BLOCK_END_TYPE);

        return new EmbedNode($module->getTemplateName(),
$module->getAttribute('index'), $variables, $only,
$ignoreMissing, $token->getLine(), $this->getTag());
    }

    public function decideBlockEnd(Token $token)
    {
        return $token->test('endembed');
    }

    public function getTag()
    {
        return 'embed';
    }
}

class_alias('Twig\TokenParser\EmbedTokenParser',
'Twig_TokenParser_Embed');
vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php000064400000002604151166614760017331
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Node\Node;
use Twig\Token;

/**
 * Extends a template by another one.
 *
 *  {% extends "base.html" %}
 *
 * @final
 */
class ExtendsTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $stream = $this->parser->getStream();

        if ($this->parser->peekBlockStack()) {
            throw new SyntaxError('Cannot use "extend" in a
block.', $token->getLine(), $stream->getSourceContext());
        } elseif (!$this->parser->isMainScope()) {
            throw new SyntaxError('Cannot use "extend" in a
macro.', $token->getLine(), $stream->getSourceContext());
        }

        if (null !== $this->parser->getParent()) {
            throw new SyntaxError('Multiple extends tags are
forbidden.', $token->getLine(), $stream->getSourceContext());
        }
       
$this->parser->setParent($this->parser->getExpressionParser()->parseExpression());

        $stream->expect(Token::BLOCK_END_TYPE);

        return new Node();
    }

    public function getTag()
    {
        return 'extends';
    }
}

class_alias('Twig\TokenParser\ExtendsTokenParser',
'Twig_TokenParser_Extends');
vendor/twig/twig/src/TokenParser/FilterTokenParser.php000064400000003100151166614760017134
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Node\BlockNode;
use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\PrintNode;
use Twig\Token;

/**
 * Filters a section of a template by applying filters.
 *
 *   {% filter upper %}
 *      This text becomes uppercase
 *   {% endfilter %}
 *
 * @final
 */
class FilterTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $name = $this->parser->getVarName();
        $ref = new BlockReferenceExpression(new ConstantExpression($name,
$token->getLine()), null, $token->getLine(), $this->getTag());

        $filter =
$this->parser->getExpressionParser()->parseFilterExpressionRaw($ref,
$this->getTag());
        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

        $body = $this->parser->subparse([$this,
'decideBlockEnd'], true);
        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

        $block = new BlockNode($name, $body, $token->getLine());
        $this->parser->setBlock($name, $block);

        return new PrintNode($filter, $token->getLine(),
$this->getTag());
    }

    public function decideBlockEnd(Token $token)
    {
        return $token->test('endfilter');
    }

    public function getTag()
    {
        return 'filter';
    }
}

class_alias('Twig\TokenParser\FilterTokenParser',
'Twig_TokenParser_Filter');
vendor/twig/twig/src/TokenParser/FlushTokenParser.php000064400000001336151166614760017001
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Node\FlushNode;
use Twig\Token;

/**
 * Flushes the output to the client.
 *
 * @see flush()
 *
 * @final
 */
class FlushTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

        return new FlushNode($token->getLine(), $this->getTag());
    }

    public function getTag()
    {
        return 'flush';
    }
}

class_alias('Twig\TokenParser\FlushTokenParser',
'Twig_TokenParser_Flush');
vendor/twig/twig/src/TokenParser/ForTokenParser.php000064400000011152151166614760016443
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Node\Expression\AssignNameExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\ForNode;
use Twig\Token;
use Twig\TokenStream;

/**
 * Loops over each item of a sequence.
 *
 *   <ul>
 *    {% for user in users %}
 *      <li>{{ user.username|e }}</li>
 *    {% endfor %}
 *   </ul>
 *
 * @final
 */
class ForTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();
        $targets =
$this->parser->getExpressionParser()->parseAssignmentExpression();
        $stream->expect(Token::OPERATOR_TYPE, 'in');
        $seq =
$this->parser->getExpressionParser()->parseExpression();

        $ifexpr = null;
        if ($stream->nextIf(Token::NAME_TYPE, 'if')) {
            $ifexpr =
$this->parser->getExpressionParser()->parseExpression();
        }

        $stream->expect(Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse([$this,
'decideForFork']);
        if ('else' == $stream->next()->getValue()) {
            $stream->expect(Token::BLOCK_END_TYPE);
            $else = $this->parser->subparse([$this,
'decideForEnd'], true);
        } else {
            $else = null;
        }
        $stream->expect(Token::BLOCK_END_TYPE);

        if (\count($targets) > 1) {
            $keyTarget = $targets->getNode(0);
            $keyTarget = new
AssignNameExpression($keyTarget->getAttribute('name'),
$keyTarget->getTemplateLine());
            $valueTarget = $targets->getNode(1);
            $valueTarget = new
AssignNameExpression($valueTarget->getAttribute('name'),
$valueTarget->getTemplateLine());
        } else {
            $keyTarget = new AssignNameExpression('_key',
$lineno);
            $valueTarget = $targets->getNode(0);
            $valueTarget = new
AssignNameExpression($valueTarget->getAttribute('name'),
$valueTarget->getTemplateLine());
        }

        if ($ifexpr) {
            $this->checkLoopUsageCondition($stream, $ifexpr);
            $this->checkLoopUsageBody($stream, $body);
        }

        return new ForNode($keyTarget, $valueTarget, $seq, $ifexpr, $body,
$else, $lineno, $this->getTag());
    }

    public function decideForFork(Token $token)
    {
        return $token->test(['else', 'endfor']);
    }

    public function decideForEnd(Token $token)
    {
        return $token->test('endfor');
    }

    // the loop variable cannot be used in the condition
    protected function checkLoopUsageCondition(TokenStream $stream,
\Twig_NodeInterface $node)
    {
        if ($node instanceof GetAttrExpression &&
$node->getNode('node') instanceof NameExpression &&
'loop' ==
$node->getNode('node')->getAttribute('name')) {
            throw new SyntaxError('The "loop" variable
cannot be used in a looping condition.', $node->getTemplateLine(),
$stream->getSourceContext());
        }

        foreach ($node as $n) {
            if (!$n) {
                continue;
            }

            $this->checkLoopUsageCondition($stream, $n);
        }
    }

    // check usage of non-defined loop-items
    // it does not catch all problems (for instance when a for is included
into another or when the variable is used in an include)
    protected function checkLoopUsageBody(TokenStream $stream,
\Twig_NodeInterface $node)
    {
        if ($node instanceof GetAttrExpression &&
$node->getNode('node') instanceof NameExpression &&
'loop' ==
$node->getNode('node')->getAttribute('name')) {
            $attribute = $node->getNode('attribute');
            if ($attribute instanceof ConstantExpression &&
\in_array($attribute->getAttribute('value'),
['length', 'revindex0', 'revindex',
'last'])) {
                throw new SyntaxError(sprintf('The "loop.%s"
variable is not defined when looping with a condition.',
$attribute->getAttribute('value')),
$node->getTemplateLine(), $stream->getSourceContext());
            }
        }

        // should check for parent.loop.XXX usage
        if ($node instanceof ForNode) {
            return;
        }

        foreach ($node as $n) {
            if (!$n) {
                continue;
            }

            $this->checkLoopUsageBody($stream, $n);
        }
    }

    public function getTag()
    {
        return 'for';
    }
}

class_alias('Twig\TokenParser\ForTokenParser',
'Twig_TokenParser_For');
vendor/twig/twig/src/TokenParser/FromTokenParser.php000064400000003570151166614760016625
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Node\Expression\AssignNameExpression;
use Twig\Node\ImportNode;
use Twig\Token;

/**
 * Imports macros.
 *
 *   {% from 'forms.html' import forms %}
 *
 * @final
 */
class FromTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $macro =
$this->parser->getExpressionParser()->parseExpression();
        $stream = $this->parser->getStream();
        $stream->expect(Token::NAME_TYPE, 'import');

        $targets = [];
        do {
            $name = $stream->expect(Token::NAME_TYPE)->getValue();

            $alias = $name;
            if ($stream->nextIf('as')) {
                $alias =
$stream->expect(Token::NAME_TYPE)->getValue();
            }

            $targets[$name] = $alias;

            if (!$stream->nextIf(Token::PUNCTUATION_TYPE,
',')) {
                break;
            }
        } while (true);

        $stream->expect(Token::BLOCK_END_TYPE);

        $var = new AssignNameExpression($this->parser->getVarName(),
$token->getLine());
        $node = new ImportNode($macro, $var, $token->getLine(),
$this->getTag());

        foreach ($targets as $name => $alias) {
            if ($this->parser->isReservedMacroName($name)) {
                throw new SyntaxError(sprintf('"%s" cannot
be an imported macro as it is a reserved keyword.', $name),
$token->getLine(), $stream->getSourceContext());
            }

            $this->parser->addImportedSymbol('function',
$alias, 'get'.$name, $var);
        }

        return $node;
    }

    public function getTag()
    {
        return 'from';
    }
}

class_alias('Twig\TokenParser\FromTokenParser',
'Twig_TokenParser_From');
vendor/twig/twig/src/TokenParser/IfTokenParser.php000064400000004713151166614760016260
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Node\IfNode;
use Twig\Node\Node;
use Twig\Token;

/**
 * Tests a condition.
 *
 *   {% if users %}
 *    <ul>
 *      {% for user in users %}
 *        <li>{{ user.username|e }}</li>
 *      {% endfor %}
 *    </ul>
 *   {% endif %}
 *
 * @final
 */
class IfTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $lineno = $token->getLine();
        $expr =
$this->parser->getExpressionParser()->parseExpression();
        $stream = $this->parser->getStream();
        $stream->expect(Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse([$this,
'decideIfFork']);
        $tests = [$expr, $body];
        $else = null;

        $end = false;
        while (!$end) {
            switch ($stream->next()->getValue()) {
                case 'else':
                    $stream->expect(Token::BLOCK_END_TYPE);
                    $else = $this->parser->subparse([$this,
'decideIfEnd']);
                    break;

                case 'elseif':
                    $expr =
$this->parser->getExpressionParser()->parseExpression();
                    $stream->expect(Token::BLOCK_END_TYPE);
                    $body = $this->parser->subparse([$this,
'decideIfFork']);
                    $tests[] = $expr;
                    $tests[] = $body;
                    break;

                case 'endif':
                    $end = true;
                    break;

                default:
                    throw new SyntaxError(sprintf('Unexpected end of
template. Twig was looking for the following tags "else",
"elseif", or "endif" to close the "if" block
started at line %d).', $lineno),
$stream->getCurrent()->getLine(), $stream->getSourceContext());
            }
        }

        $stream->expect(Token::BLOCK_END_TYPE);

        return new IfNode(new Node($tests), $else, $lineno,
$this->getTag());
    }

    public function decideIfFork(Token $token)
    {
        return $token->test(['elseif', 'else',
'endif']);
    }

    public function decideIfEnd(Token $token)
    {
        return $token->test(['endif']);
    }

    public function getTag()
    {
        return 'if';
    }
}

class_alias('Twig\TokenParser\IfTokenParser',
'Twig_TokenParser_If');
vendor/twig/twig/src/TokenParser/ImportTokenParser.php000064400000002206151166614760017167
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Node\Expression\AssignNameExpression;
use Twig\Node\ImportNode;
use Twig\Token;

/**
 * Imports macros.
 *
 *   {% import 'forms.html' as forms %}
 *
 * @final
 */
class ImportTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $macro =
$this->parser->getExpressionParser()->parseExpression();
        $this->parser->getStream()->expect(Token::NAME_TYPE,
'as');
        $var = new
AssignNameExpression($this->parser->getStream()->expect(Token::NAME_TYPE)->getValue(),
$token->getLine());
        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

        $this->parser->addImportedSymbol('template',
$var->getAttribute('name'));

        return new ImportNode($macro, $var, $token->getLine(),
$this->getTag());
    }

    public function getTag()
    {
        return 'import';
    }
}

class_alias('Twig\TokenParser\ImportTokenParser',
'Twig_TokenParser_Import');
vendor/twig/twig/src/TokenParser/IncludeTokenParser.php000064400000003123151166614760017277
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Node\IncludeNode;
use Twig\Token;

/**
 * Includes a template.
 *
 *   {% include 'header.html' %}
 *     Body
 *   {% include 'footer.html' %}
 */
class IncludeTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $expr =
$this->parser->getExpressionParser()->parseExpression();

        list($variables, $only, $ignoreMissing) =
$this->parseArguments();

        return new IncludeNode($expr, $variables, $only, $ignoreMissing,
$token->getLine(), $this->getTag());
    }

    protected function parseArguments()
    {
        $stream = $this->parser->getStream();

        $ignoreMissing = false;
        if ($stream->nextIf(Token::NAME_TYPE, 'ignore')) {
            $stream->expect(Token::NAME_TYPE, 'missing');

            $ignoreMissing = true;
        }

        $variables = null;
        if ($stream->nextIf(Token::NAME_TYPE, 'with')) {
            $variables =
$this->parser->getExpressionParser()->parseExpression();
        }

        $only = false;
        if ($stream->nextIf(Token::NAME_TYPE, 'only')) {
            $only = true;
        }

        $stream->expect(Token::BLOCK_END_TYPE);

        return [$variables, $only, $ignoreMissing];
    }

    public function getTag()
    {
        return 'include';
    }
}

class_alias('Twig\TokenParser\IncludeTokenParser',
'Twig_TokenParser_Include');
vendor/twig/twig/src/TokenParser/MacroTokenParser.php000064400000003565151166614760016767
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Node\BodyNode;
use Twig\Node\MacroNode;
use Twig\Node\Node;
use Twig\Token;

/**
 * Defines a macro.
 *
 *   {% macro input(name, value, type, size) %}
 *      <input type="{{ type|default('text') }}"
name="{{ name }}" value="{{ value|e }}" size="{{
size|default(20) }}" />
 *   {% endmacro %}
 *
 * @final
 */
class MacroTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();
        $name = $stream->expect(Token::NAME_TYPE)->getValue();

        $arguments =
$this->parser->getExpressionParser()->parseArguments(true, true);

        $stream->expect(Token::BLOCK_END_TYPE);
        $this->parser->pushLocalScope();
        $body = $this->parser->subparse([$this,
'decideBlockEnd'], true);
        if ($token = $stream->nextIf(Token::NAME_TYPE)) {
            $value = $token->getValue();

            if ($value != $name) {
                throw new SyntaxError(sprintf('Expected endmacro for
macro "%s" (but "%s" given).', $name, $value),
$stream->getCurrent()->getLine(), $stream->getSourceContext());
            }
        }
        $this->parser->popLocalScope();
        $stream->expect(Token::BLOCK_END_TYPE);

        $this->parser->setMacro($name, new MacroNode($name, new
BodyNode([$body]), $arguments, $lineno, $this->getTag()));

        return new Node();
    }

    public function decideBlockEnd(Token $token)
    {
        return $token->test('endmacro');
    }

    public function getTag()
    {
        return 'macro';
    }
}

class_alias('Twig\TokenParser\MacroTokenParser',
'Twig_TokenParser_Macro');
vendor/twig/twig/src/TokenParser/SandboxTokenParser.php000064400000003444151166614760017320
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Node\IncludeNode;
use Twig\Node\SandboxNode;
use Twig\Node\TextNode;
use Twig\Token;

/**
 * Marks a section of a template as untrusted code that must be evaluated
in the sandbox mode.
 *
 *    {% sandbox %}
 *        {% include 'user.html' %}
 *    {% endsandbox %}
 *
 * @see https://twig.symfony.com/doc/api.html#sandbox-extension for details
 *
 * @final
 */
class SandboxTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $stream = $this->parser->getStream();
        $stream->expect(Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse([$this,
'decideBlockEnd'], true);
        $stream->expect(Token::BLOCK_END_TYPE);

        // in a sandbox tag, only include tags are allowed
        if (!$body instanceof IncludeNode) {
            foreach ($body as $node) {
                if ($node instanceof TextNode &&
ctype_space($node->getAttribute('data'))) {
                    continue;
                }

                if (!$node instanceof IncludeNode) {
                    throw new SyntaxError('Only "include"
tags are allowed within a "sandbox" section.',
$node->getTemplateLine(), $stream->getSourceContext());
                }
            }
        }

        return new SandboxNode($body, $token->getLine(),
$this->getTag());
    }

    public function decideBlockEnd(Token $token)
    {
        return $token->test('endsandbox');
    }

    public function getTag()
    {
        return 'sandbox';
    }
}

class_alias('Twig\TokenParser\SandboxTokenParser',
'Twig_TokenParser_Sandbox');
vendor/twig/twig/src/TokenParser/SetTokenParser.php000064400000004037151166614760016454
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Node\SetNode;
use Twig\Token;

/**
 * Defines a variable.
 *
 *  {% set foo = 'foo' %}
 *  {% set foo = [1, 2] %}
 *  {% set foo = {'foo': 'bar'} %}
 *  {% set foo = 'foo' ~ 'bar' %}
 *  {% set foo, bar = 'foo', 'bar' %}
 *  {% set foo %}Some content{% endset %}
 *
 * @final
 */
class SetTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();
        $names =
$this->parser->getExpressionParser()->parseAssignmentExpression();

        $capture = false;
        if ($stream->nextIf(Token::OPERATOR_TYPE, '=')) {
            $values =
$this->parser->getExpressionParser()->parseMultitargetExpression();

            $stream->expect(Token::BLOCK_END_TYPE);

            if (\count($names) !== \count($values)) {
                throw new SyntaxError('When using set, you must have
the same number of variables and assignments.',
$stream->getCurrent()->getLine(), $stream->getSourceContext());
            }
        } else {
            $capture = true;

            if (\count($names) > 1) {
                throw new SyntaxError('When using set with a block,
you cannot have a multi-target.',
$stream->getCurrent()->getLine(), $stream->getSourceContext());
            }

            $stream->expect(Token::BLOCK_END_TYPE);

            $values = $this->parser->subparse([$this,
'decideBlockEnd'], true);
            $stream->expect(Token::BLOCK_END_TYPE);
        }

        return new SetNode($capture, $names, $values, $lineno,
$this->getTag());
    }

    public function decideBlockEnd(Token $token)
    {
        return $token->test('endset');
    }

    public function getTag()
    {
        return 'set';
    }
}

class_alias('Twig\TokenParser\SetTokenParser',
'Twig_TokenParser_Set');
vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php000064400000002262151166614760017641
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Node\SpacelessNode;
use Twig\Token;

/**
 * Remove whitespaces between HTML tags.
 *
 *   {% spaceless %}
 *      <div>
 *          <strong>foo</strong>
 *      </div>
 *   {% endspaceless %}
 *   {# output will be
<div><strong>foo</strong></div> #}
 *
 * @final
 */
class SpacelessTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $lineno = $token->getLine();

        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse([$this,
'decideSpacelessEnd'], true);
        $this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

        return new SpacelessNode($body, $lineno, $this->getTag());
    }

    public function decideSpacelessEnd(Token $token)
    {
        return $token->test('endspaceless');
    }

    public function getTag()
    {
        return 'spaceless';
    }
}

class_alias('Twig\TokenParser\SpacelessTokenParser',
'Twig_TokenParser_Spaceless');
vendor/twig/twig/src/TokenParser/TokenParserInterface.php000064400000002161151166614760017615
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Parser;
use Twig\Token;

/**
 * Interface implemented by token parsers.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
interface TokenParserInterface
{
    /**
     * Sets the parser associated with this token parser.
     */
    public function setParser(Parser $parser);

    /**
     * Parses a token and returns a node.
     *
     * @return \Twig_NodeInterface
     *
     * @throws SyntaxError
     */
    public function parse(Token $token);

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag();
}

class_alias('Twig\TokenParser\TokenParserInterface',
'Twig_TokenParserInterface');

// Ensure that the aliased name is loaded to keep BC for classes
implementing the typehint with the old aliased name.
class_exists('Twig\Token');
class_exists('Twig\Parser');
vendor/twig/twig/src/TokenParser/UseTokenParser.php000064400000003712151166614760016454
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Error\SyntaxError;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Node;
use Twig\Token;

/**
 * Imports blocks defined in another template into the current template.
 *
 *    {% extends "base.html" %}
 *
 *    {% use "blocks.html" %}
 *
 *    {% block title %}{% endblock %}
 *    {% block content %}{% endblock %}
 *
 * @see https://twig.symfony.com/doc/templates.html#horizontal-reuse for
details.
 *
 * @final
 */
class UseTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $template =
$this->parser->getExpressionParser()->parseExpression();
        $stream = $this->parser->getStream();

        if (!$template instanceof ConstantExpression) {
            throw new SyntaxError('The template references in a
"use" statement must be a string.',
$stream->getCurrent()->getLine(), $stream->getSourceContext());
        }

        $targets = [];
        if ($stream->nextIf('with')) {
            do {
                $name =
$stream->expect(Token::NAME_TYPE)->getValue();

                $alias = $name;
                if ($stream->nextIf('as')) {
                    $alias =
$stream->expect(Token::NAME_TYPE)->getValue();
                }

                $targets[$name] = new ConstantExpression($alias, -1);

                if (!$stream->nextIf(Token::PUNCTUATION_TYPE,
',')) {
                    break;
                }
            } while (true);
        }

        $stream->expect(Token::BLOCK_END_TYPE);

        $this->parser->addTrait(new Node(['template' =>
$template, 'targets' => new Node($targets)]));

        return new Node();
    }

    public function getTag()
    {
        return 'use';
    }
}

class_alias('Twig\TokenParser\UseTokenParser',
'Twig_TokenParser_Use');
vendor/twig/twig/src/TokenParser/WithTokenParser.php000064400000002410151166614760016625
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\TokenParser;

use Twig\Node\WithNode;
use Twig\Token;

/**
 * Creates a nested scope.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @final
 */
class WithTokenParser extends AbstractTokenParser
{
    public function parse(Token $token)
    {
        $stream = $this->parser->getStream();

        $variables = null;
        $only = false;
        if (!$stream->test(Token::BLOCK_END_TYPE)) {
            $variables =
$this->parser->getExpressionParser()->parseExpression();
            $only = $stream->nextIf(Token::NAME_TYPE, 'only');
        }

        $stream->expect(Token::BLOCK_END_TYPE);

        $body = $this->parser->subparse([$this,
'decideWithEnd'], true);

        $stream->expect(Token::BLOCK_END_TYPE);

        return new WithNode($body, $variables, $only, $token->getLine(),
$this->getTag());
    }

    public function decideWithEnd(Token $token)
    {
        return $token->test('endwith');
    }

    public function getTag()
    {
        return 'with';
    }
}

class_alias('Twig\TokenParser\WithTokenParser',
'Twig_TokenParser_With');
vendor/twig/twig/src/TokenStream.php000064400000012564151166614760013546
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 * (c) Armin Ronacher
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

use Twig\Error\SyntaxError;

/**
 * Represents a token stream.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TokenStream
{
    protected $tokens;
    protected $current = 0;
    protected $filename;

    private $source;

    /**
     * @param array       $tokens An array of tokens
     * @param string|null $name   The name of the template which tokens are
associated with
     * @param string|null $source The source code associated with the
tokens
     */
    public function __construct(array $tokens, $name = null, $source =
null)
    {
        if (!$name instanceof Source) {
            if (null !== $name || null !== $source) {
                @trigger_error(sprintf('Passing a string as the $name
argument of %s() is deprecated since version 1.27. Pass a \Twig\Source
instance instead.', __METHOD__), E_USER_DEPRECATED);
            }
            $this->source = new Source($source, $name);
        } else {
            $this->source = $name;
        }

        $this->tokens = $tokens;

        // deprecated, not used anymore, to be removed in 2.0
        $this->filename = $this->source->getName();
    }

    public function __toString()
    {
        return implode("\n", $this->tokens);
    }

    public function injectTokens(array $tokens)
    {
        $this->tokens = array_merge(\array_slice($this->tokens, 0,
$this->current), $tokens, \array_slice($this->tokens,
$this->current));
    }

    /**
     * Sets the pointer to the next token and returns the old one.
     *
     * @return Token
     */
    public function next()
    {
        if (!isset($this->tokens[++$this->current])) {
            throw new SyntaxError('Unexpected end of template.',
$this->tokens[$this->current - 1]->getLine(), $this->source);
        }

        return $this->tokens[$this->current - 1];
    }

    /**
     * Tests a token, sets the pointer to the next one and returns it or
throws a syntax error.
     *
     * @return Token|null The next token if the condition is true, null
otherwise
     */
    public function nextIf($primary, $secondary = null)
    {
        if ($this->tokens[$this->current]->test($primary,
$secondary)) {
            return $this->next();
        }
    }

    /**
     * Tests a token and returns it or throws a syntax error.
     *
     * @return Token
     */
    public function expect($type, $value = null, $message = null)
    {
        $token = $this->tokens[$this->current];
        if (!$token->test($type, $value)) {
            $line = $token->getLine();
            throw new SyntaxError(sprintf('%sUnexpected token
"%s"%s ("%s" expected%s).',
                $message ? $message.'. ' : '',
                Token::typeToEnglish($token->getType()),
                $token->getValue() ? sprintf(' of value
"%s"', $token->getValue()) : '',
                Token::typeToEnglish($type), $value ? sprintf(' with
value "%s"', $value) : ''),
                $line,
                $this->source
            );
        }
        $this->next();

        return $token;
    }

    /**
     * Looks at the next token.
     *
     * @param int $number
     *
     * @return Token
     */
    public function look($number = 1)
    {
        if (!isset($this->tokens[$this->current + $number])) {
            throw new SyntaxError('Unexpected end of template.',
$this->tokens[$this->current + $number - 1]->getLine(),
$this->source);
        }

        return $this->tokens[$this->current + $number];
    }

    /**
     * Tests the current token.
     *
     * @return bool
     */
    public function test($primary, $secondary = null)
    {
        return $this->tokens[$this->current]->test($primary,
$secondary);
    }

    /**
     * Checks if end of stream was reached.
     *
     * @return bool
     */
    public function isEOF()
    {
        return Token::EOF_TYPE ===
$this->tokens[$this->current]->getType();
    }

    /**
     * @return Token
     */
    public function getCurrent()
    {
        return $this->tokens[$this->current];
    }

    /**
     * Gets the name associated with this stream (null if not defined).
     *
     * @return string|null
     *
     * @deprecated since 1.27 (to be removed in 2.0)
     */
    public function getFilename()
    {
        @trigger_error(sprintf('The %s() method is deprecated since
version 1.27 and will be removed in 2.0. Use getSourceContext()
instead.', __METHOD__), E_USER_DEPRECATED);

        return $this->source->getName();
    }

    /**
     * Gets the source code associated with this stream.
     *
     * @return string
     *
     * @internal Don't use this as it might be empty depending on the
environment configuration
     *
     * @deprecated since 1.27 (to be removed in 2.0)
     */
    public function getSource()
    {
        @trigger_error(sprintf('The %s() method is deprecated since
version 1.27 and will be removed in 2.0. Use getSourceContext()
instead.', __METHOD__), E_USER_DEPRECATED);

        return $this->source->getCode();
    }

    /**
     * Gets the source associated with this stream.
     *
     * @return Source
     *
     * @internal
     */
    public function getSourceContext()
    {
        return $this->source;
    }
}

class_alias('Twig\TokenStream', 'Twig_TokenStream');
vendor/twig/twig/src/TwigFilter.php000064400000005375151166614760013374
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

use Twig\Node\Node;

/**
 * Represents a template filter.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TwigFilter
{
    protected $name;
    protected $callable;
    protected $options;
    protected $arguments = [];

    public function __construct($name, $callable, array $options = [])
    {
        $this->name = $name;
        $this->callable = $callable;
        $this->options = array_merge([
            'needs_environment' => false,
            'needs_context' => false,
            'is_variadic' => false,
            'is_safe' => null,
            'is_safe_callback' => null,
            'pre_escape' => null,
            'preserves_safety' => null,
            'node_class' =>
'\Twig\Node\Expression\FilterExpression',
            'deprecated' => false,
            'alternative' => null,
        ], $options);
    }

    public function getName()
    {
        return $this->name;
    }

    public function getCallable()
    {
        return $this->callable;
    }

    public function getNodeClass()
    {
        return $this->options['node_class'];
    }

    public function setArguments($arguments)
    {
        $this->arguments = $arguments;
    }

    public function getArguments()
    {
        return $this->arguments;
    }

    public function needsEnvironment()
    {
        return $this->options['needs_environment'];
    }

    public function needsContext()
    {
        return $this->options['needs_context'];
    }

    public function getSafe(Node $filterArgs)
    {
        if (null !== $this->options['is_safe']) {
            return $this->options['is_safe'];
        }

        if (null !== $this->options['is_safe_callback']) {
            return
\call_user_func($this->options['is_safe_callback'],
$filterArgs);
        }
    }

    public function getPreservesSafety()
    {
        return $this->options['preserves_safety'];
    }

    public function getPreEscape()
    {
        return $this->options['pre_escape'];
    }

    public function isVariadic()
    {
        return $this->options['is_variadic'];
    }

    public function isDeprecated()
    {
        return (bool) $this->options['deprecated'];
    }

    public function getDeprecatedVersion()
    {
        return $this->options['deprecated'];
    }

    public function getAlternative()
    {
        return $this->options['alternative'];
    }
}

class_alias('Twig\TwigFilter', 'Twig_SimpleFilter');

// Ensure that the aliased name is loaded to keep BC for classes
implementing the typehint with the old aliased name.
class_exists('Twig\Node\Node');
vendor/twig/twig/src/TwigFunction.php000064400000005017151166614760013725
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

use Twig\Node\Node;

/**
 * Represents a template function.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TwigFunction
{
    protected $name;
    protected $callable;
    protected $options;
    protected $arguments = [];

    public function __construct($name, $callable, array $options = [])
    {
        $this->name = $name;
        $this->callable = $callable;
        $this->options = array_merge([
            'needs_environment' => false,
            'needs_context' => false,
            'is_variadic' => false,
            'is_safe' => null,
            'is_safe_callback' => null,
            'node_class' =>
'\Twig\Node\Expression\FunctionExpression',
            'deprecated' => false,
            'alternative' => null,
        ], $options);
    }

    public function getName()
    {
        return $this->name;
    }

    public function getCallable()
    {
        return $this->callable;
    }

    public function getNodeClass()
    {
        return $this->options['node_class'];
    }

    public function setArguments($arguments)
    {
        $this->arguments = $arguments;
    }

    public function getArguments()
    {
        return $this->arguments;
    }

    public function needsEnvironment()
    {
        return $this->options['needs_environment'];
    }

    public function needsContext()
    {
        return $this->options['needs_context'];
    }

    public function getSafe(Node $functionArgs)
    {
        if (null !== $this->options['is_safe']) {
            return $this->options['is_safe'];
        }

        if (null !== $this->options['is_safe_callback']) {
            return
\call_user_func($this->options['is_safe_callback'],
$functionArgs);
        }

        return [];
    }

    public function isVariadic()
    {
        return $this->options['is_variadic'];
    }

    public function isDeprecated()
    {
        return (bool) $this->options['deprecated'];
    }

    public function getDeprecatedVersion()
    {
        return $this->options['deprecated'];
    }

    public function getAlternative()
    {
        return $this->options['alternative'];
    }
}

class_alias('Twig\TwigFunction',
'Twig_SimpleFunction');

// Ensure that the aliased name is loaded to keep BC for classes
implementing the typehint with the old aliased name.
class_exists('Twig\Node\Node');
vendor/twig/twig/src/TwigTest.php000064400000003227151166614760013060
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig;

/**
 * Represents a template test.
 *
 * @final
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TwigTest
{
    protected $name;
    protected $callable;
    protected $options;

    private $arguments = [];

    public function __construct($name, $callable, array $options = [])
    {
        $this->name = $name;
        $this->callable = $callable;
        $this->options = array_merge([
            'is_variadic' => false,
            'node_class' =>
'\Twig\Node\Expression\TestExpression',
            'deprecated' => false,
            'alternative' => null,
        ], $options);
    }

    public function getName()
    {
        return $this->name;
    }

    public function getCallable()
    {
        return $this->callable;
    }

    public function getNodeClass()
    {
        return $this->options['node_class'];
    }

    public function isVariadic()
    {
        return $this->options['is_variadic'];
    }

    public function isDeprecated()
    {
        return (bool) $this->options['deprecated'];
    }

    public function getDeprecatedVersion()
    {
        return $this->options['deprecated'];
    }

    public function getAlternative()
    {
        return $this->options['alternative'];
    }

    public function setArguments($arguments)
    {
        $this->arguments = $arguments;
    }

    public function getArguments()
    {
        return $this->arguments;
    }
}

class_alias('Twig\TwigTest', 'Twig_SimpleTest');
vendor/twig/twig/src/Util/DeprecationCollector.php000064400000004352151166614760016327
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Util;

use Twig\Environment;
use Twig\Error\SyntaxError;
use Twig\Source;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 *
 * @final
 */
class DeprecationCollector
{
    private $twig;
    private $deprecations;

    public function __construct(Environment $twig)
    {
        $this->twig = $twig;
    }

    /**
     * Returns deprecations for templates contained in a directory.
     *
     * @param string $dir A directory where templates are stored
     * @param string $ext Limit the loaded templates by extension
     *
     * @return array An array of deprecations
     */
    public function collectDir($dir, $ext = '.twig')
    {
        $iterator = new \RegexIterator(
            new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($dir),
\RecursiveIteratorIterator::LEAVES_ONLY
            ), '{'.preg_quote($ext).'$}'
        );

        return $this->collect(new TemplateDirIterator($iterator));
    }

    /**
     * Returns deprecations for passed templates.
     *
     * @param \Traversable $iterator An iterator of templates (where keys
are template names and values the contents of the template)
     *
     * @return array An array of deprecations
     */
    public function collect(\Traversable $iterator)
    {
        $this->deprecations = [];

        set_error_handler([$this, 'errorHandler']);

        foreach ($iterator as $name => $contents) {
            try {
                $this->twig->parse($this->twig->tokenize(new
Source($contents, $name)));
            } catch (SyntaxError $e) {
                // ignore templates containing syntax errors
            }
        }

        restore_error_handler();

        $deprecations = $this->deprecations;
        $this->deprecations = [];

        return $deprecations;
    }

    /**
     * @internal
     */
    public function errorHandler($type, $msg)
    {
        if (E_USER_DEPRECATED === $type) {
            $this->deprecations[] = $msg;
        }
    }
}

class_alias('Twig\Util\DeprecationCollector',
'Twig_Util_DeprecationCollector');
vendor/twig/twig/src/Util/TemplateDirIterator.php000064400000001116151166614760016142
0ustar00<?php

/*
 * This file is part of Twig.
 *
 * (c) Fabien Potencier
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Twig\Util;

/**
 * @author Fabien Potencier <fabien@symfony.com>
 */
class TemplateDirIterator extends \IteratorIterator
{
    public function current()
    {
        return file_get_contents(parent::current());
    }

    public function key()
    {
        return (string) parent::key();
    }
}

class_alias('Twig\Util\TemplateDirIterator',
'Twig_Util_TemplateDirIterator');