.=< { Star Gans Tq } >=.

  • Home

  • Killme
  • Download
  • Current Path : /home/m/e/h/meharicl/www/phpBB3/vendor/s9e/text-formatter/src/Configurator/
    Upload File
    @Command ~ $  
    Current File : /home/m/e/h/meharicl/www/phpBB3/vendor/s9e/text-formatter/src/Configurator/JavaScript.php

    <?php
    
    /**
    * @package   s9e\TextFormatter
    * @copyright Copyright (c) 2010-2020 The s9e authors
    * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
    */
    namespace s9e\TextFormatter\Configurator;
    
    use ReflectionClass;
    use s9e\TextFormatter\Configurator;
    use s9e\TextFormatter\Configurator\Helpers\ConfigHelper;
    use s9e\TextFormatter\Configurator\JavaScript\CallbackGenerator;
    use s9e\TextFormatter\Configurator\JavaScript\Code;
    use s9e\TextFormatter\Configurator\JavaScript\ConfigOptimizer;
    use s9e\TextFormatter\Configurator\JavaScript\Dictionary;
    use s9e\TextFormatter\Configurator\JavaScript\Encoder;
    use s9e\TextFormatter\Configurator\JavaScript\HintGenerator;
    use s9e\TextFormatter\Configurator\JavaScript\Minifier;
    use s9e\TextFormatter\Configurator\JavaScript\Minifiers\Noop;
    use s9e\TextFormatter\Configurator\JavaScript\RegexpConvertor;
    use s9e\TextFormatter\Configurator\JavaScript\StylesheetCompressor;
    use s9e\TextFormatter\Configurator\RendererGenerators\XSLT;
    
    class JavaScript
    {
    	/**
    	* @var CallbackGenerator
    	*/
    	protected $callbackGenerator;
    
    	/**
    	* @var array Configuration, filtered for JavaScript
    	*/
    	protected $config;
    
    	/**
    	* @var ConfigOptimizer
    	*/
    	protected $configOptimizer;
    
    	/**
    	* @var Configurator Configurator this instance belongs to
    	*/
    	protected $configurator;
    
    	/**
    	* @var Encoder
    	*/
    	public $encoder;
    
    	/**
    	* @var array List of methods and properties to be exported in the s9e.TextFormatter object
    	*/
    	public $exports = [
    		'disablePlugin',
    		'disableTag',
    		'enablePlugin',
    		'enableTag',
    		'getLogger',
    		'parse',
    		'preview',
    		'registeredVars',
    		'setNestingLimit',
    		'setParameter',
    		'setTagLimit'
    	];
    
    	/**
    	* @var HintGenerator
    	*/
    	protected $hintGenerator;
    
    	/**
    	* @var Minifier Instance of Minifier used to minify the JavaScript parser
    	*/
    	protected $minifier;
    
    	/**
    	* @var StylesheetCompressor
    	*/
    	protected $stylesheetCompressor;
    
    	/**
    	* @var string Stylesheet used for rendering
    	*/
    	protected $xsl;
    
    	/**
    	* Constructor
    	*
    	* @param  Configurator $configurator Configurator
    	*/
    	public function __construct(Configurator $configurator)
    	{
    		$this->encoder              = new Encoder;
    		$this->callbackGenerator    = new CallbackGenerator;
    		$this->configOptimizer      = new ConfigOptimizer($this->encoder);
    		$this->configurator         = $configurator;
    		$this->hintGenerator        = new HintGenerator;
    		$this->stylesheetCompressor = new StylesheetCompressor;
    	}
    
    	/**
    	* Return the cached instance of Minifier (creates one if necessary)
    	*
    	* @return Minifier
    	*/
    	public function getMinifier()
    	{
    		if (!isset($this->minifier))
    		{
    			$this->minifier = new Noop;
    		}
    
    		return $this->minifier;
    	}
    
    	/**
    	* Get a JavaScript parser
    	*
    	* @param  array  $config Config array returned by the configurator
    	* @return string         JavaScript parser
    	*/
    	public function getParser(array $config = null)
    	{
    		$this->configOptimizer->reset();
    
    		// Get the stylesheet used for rendering
    		$xslt      = new XSLT;
    		$xslt->normalizer->remove('RemoveLivePreviewAttributes');
    		$this->xsl = $xslt->getXSL($this->configurator->rendering);
    
    		// Prepare the parser's config
    		$this->config = (isset($config)) ? $config : $this->configurator->asConfig();
    		$this->config = ConfigHelper::filterConfig($this->config, 'JS');
    		$this->config = $this->callbackGenerator->replaceCallbacks($this->config);
    
    		// Get the parser's source and inject its config
    		$src = $this->getHints() . $this->injectConfig($this->getSource());
    
    		// Export the public API
    		$src .= "if (!window['s9e']) window['s9e'] = {};\n" . $this->getExports();
    
    		// Minify the source
    		$src = $this->getMinifier()->get($src);
    
    		// Wrap the source in a function to protect the global scope
    		$src = '(function(){' . $src . '})();';
    
    		return $src;
    	}
    
    	/**
    	* Set the cached instance of Minifier
    	*
    	* Extra arguments will be passed to the minifier's constructor
    	*
    	* @param  string|Minifier $minifier Name of a supported minifier, or an instance of Minifier
    	* @return Minifier                  The new minifier
    	*/
    	public function setMinifier($minifier)
    	{
    		if (is_string($minifier))
    		{
    			$className = __NAMESPACE__ . '\\JavaScript\\Minifiers\\' . $minifier;
    
    			// Pass the extra argument to the constructor, if applicable
    			$args = array_slice(func_get_args(), 1);
    			if (!empty($args))
    			{
    				$reflection = new ReflectionClass($className);
    				$minifier   = $reflection->newInstanceArgs($args);
    			}
    			else
    			{
    				$minifier = new $className;
    			}
    		}
    
    		$this->minifier = $minifier;
    
    		return $minifier;
    	}
    
    	//==========================================================================
    	// Internal
    	//==========================================================================
    
    	/**
    	* Encode a PHP value into an equivalent JavaScript representation
    	*
    	* @param  mixed  $value Original value
    	* @return string        JavaScript representation
    	*/
    	protected function encode($value)
    	{
    		return $this->encoder->encode($value);
    	}
    
    	/**
    	* Generate and return the public API
    	*
    	* @return string JavaScript Code
    	*/
    	protected function getExports()
    	{
    		if (empty($this->exports))
    		{
    			return '';
    		}
    
    		$exports = [];
    		foreach ($this->exports as $export)
    		{
    			$exports[] = "'" . $export . "':" . $export;
    		}
    		sort($exports);
    
    		return "window['s9e']['TextFormatter'] = {" . implode(',', $exports) . '};';
    	}
    
    	/**
    	* Generate a HINT object that contains informations about the configuration
    	*
    	* @return string JavaScript Code
    	*/
    	protected function getHints()
    	{
    		$this->hintGenerator->setConfig($this->config);
    		$this->hintGenerator->setPlugins($this->configurator->plugins);
    		$this->hintGenerator->setXSL($this->xsl);
    
    		return $this->hintGenerator->getHints();
    	}
    
    	/**
    	* Return the plugins' config
    	*
    	* @return Dictionary
    	*/
    	protected function getPluginsConfig()
    	{
    		$plugins = new Dictionary;
    
    		foreach ($this->config['plugins'] as $pluginName => $pluginConfig)
    		{
    			if (!isset($pluginConfig['js']))
    			{
    				// Skip this plugin
    				continue;
    			}
    			$js = $pluginConfig['js'];
    			unset($pluginConfig['js']);
    
    			// Not needed in JavaScript
    			unset($pluginConfig['className']);
    
    			// Ensure that quickMatch is UTF-8 if present
    			if (isset($pluginConfig['quickMatch']))
    			{
    				// Well-formed UTF-8 sequences
    				$valid = [
    					'[[:ascii:]]',
    					// [1100 0000-1101 1111] [1000 0000-1011 1111]
    					'[\\xC0-\\xDF][\\x80-\\xBF]',
    					// [1110 0000-1110 1111] [1000 0000-1011 1111]{2}
    					'[\\xE0-\\xEF][\\x80-\\xBF]{2}',
    					// [1111 0000-1111 0111] [1000 0000-1011 1111]{3}
    					'[\\xF0-\\xF7][\\x80-\\xBF]{3}'
    				];
    
    				$regexp = '#(?>' . implode('|', $valid) . ')+#';
    
    				// Keep only the first valid sequence of UTF-8, or unset quickMatch if none is found
    				if (preg_match($regexp, $pluginConfig['quickMatch'], $m))
    				{
    					$pluginConfig['quickMatch'] = $m[0];
    				}
    				else
    				{
    					unset($pluginConfig['quickMatch']);
    				}
    			}
    
    			/**
    			* @var array Keys of elements that are kept in the global scope. Everything else will be
    			*            moved into the plugin's parser
    			*/
    			$globalKeys = [
    				'quickMatch'  => 1,
    				'regexp'      => 1,
    				'regexpLimit' => 1
    			];
    
    			$globalConfig = array_intersect_key($pluginConfig, $globalKeys);
    			$localConfig  = array_diff_key($pluginConfig, $globalKeys);
    
    			if (isset($globalConfig['regexp']) && !($globalConfig['regexp'] instanceof Code))
    			{
    				$globalConfig['regexp'] = new Code(RegexpConvertor::toJS($globalConfig['regexp'], true));
    			}
    
    			$globalConfig['parser'] = new Code(
    				'/**
    				* @param {string}          text
    				* @param {!Array.<!Array>} matches
    				*/
    				function(text, matches)
    				{
    					/** @const */
    					var config=' . $this->encode($localConfig) . ';
    					' . $js . '
    				}'
    			);
    
    			$plugins[$pluginName] = $globalConfig;
    		}
    
    		return $plugins;
    	}
    
    	/**
    	* Return the registeredVars config
    	*
    	* @return Dictionary
    	*/
    	protected function getRegisteredVarsConfig()
    	{
    		$registeredVars = $this->config['registeredVars'];
    
    		// Remove cacheDir from the registered vars. Not only it is useless in JavaScript, it could
    		// leak some informations about the server
    		unset($registeredVars['cacheDir']);
    
    		return new Dictionary($registeredVars);
    	}
    
    	/**
    	* Return the root context config
    	*
    	* @return array
    	*/
    	protected function getRootContext()
    	{
    		return $this->config['rootContext'];
    	}
    
    	/**
    	* Return the parser's source
    	*
    	* @return string
    	*/
    	protected function getSource()
    	{
    		$rootDir = __DIR__ . '/..';
    		$src     = '';
    
    		// If getLogger() is not exported we use a dummy Logger that can be optimized away
    		$logger = (in_array('getLogger', $this->exports)) ? 'Logger.js' : 'NullLogger.js';
    
    		// Prepare the list of files
    		$files   = glob($rootDir . '/Parser/AttributeFilters/*.js');
    		$files[] = $rootDir . '/Parser/utils.js';
    		$files[] = $rootDir . '/Parser/FilterProcessing.js';
    		$files[] = $rootDir . '/Parser/' . $logger;
    		$files[] = $rootDir . '/Parser/Tag.js';
    		$files[] = $rootDir . '/Parser.js';
    
    		// Append render.js if we export the preview method
    		if (in_array('preview', $this->exports, true))
    		{
    			$files[] = $rootDir . '/render.js';
    			$src .= '/** @const */ var xsl=' . $this->getStylesheet() . ";\n";
    		}
    
    		$src .= implode("\n", array_map('file_get_contents', $files));
    
    		return $src;
    	}
    
    	/**
    	* Return the JavaScript representation of the stylesheet
    	*
    	* @return string
    	*/
    	protected function getStylesheet()
    	{
    		return $this->stylesheetCompressor->encode($this->xsl);
    	}
    
    	/**
    	* Return the tags' config
    	*
    	* @return Dictionary
    	*/
    	protected function getTagsConfig()
    	{
    		// Prepare a Dictionary that will preserve tags' names
    		$tags = new Dictionary;
    		foreach ($this->config['tags'] as $tagName => $tagConfig)
    		{
    			if (isset($tagConfig['attributes']))
    			{
    				// Make the attributes array a Dictionary, to preserve the attributes' names
    				$tagConfig['attributes'] = new Dictionary($tagConfig['attributes']);
    			}
    
    			$tags[$tagName] = $tagConfig;
    		}
    
    		return $tags;
    	}
    
    	/**
    	* Inject the parser config into given source
    	*
    	* @param  string $src Parser's source
    	* @return string      Modified source
    	*/
    	protected function injectConfig($src)
    	{
    		$config = array_map(
    			[$this, 'encode'],
    			$this->configOptimizer->optimize(
    				[
    					'plugins'        => $this->getPluginsConfig(),
    					'registeredVars' => $this->getRegisteredVarsConfig(),
    					'rootContext'    => $this->getRootContext(),
    					'tagsConfig'     => $this->getTagsConfig()
    				]
    			)
    		);
    
    		$src = preg_replace_callback(
    			'/(\\nvar (' . implode('|', array_keys($config)) . '))(;)/',
    			function ($m) use ($config)
    			{
    				return $m[1] . '=' . $config[$m[2]] . $m[3];
    			},
    			$src
    		);
    
    		// Prepend the deduplicated objects
    		$src = $this->configOptimizer->getVarDeclarations() . $src;
    
    		return $src;
    	}
    }