[ Index ]

PHP Cross Reference of phpBB-3.3.3-deutsch

title

Body

[close]

/vendor/s9e/text-formatter/src/Configurator/ -> JavaScript.php (source)

   1  <?php
   2  
   3  /**
   4  * @package   s9e\TextFormatter
   5  * @copyright Copyright (c) 2010-2020 The s9e authors
   6  * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
   7  */
   8  namespace s9e\TextFormatter\Configurator;
   9  
  10  use ReflectionClass;
  11  use s9e\TextFormatter\Configurator;
  12  use s9e\TextFormatter\Configurator\Helpers\ConfigHelper;
  13  use s9e\TextFormatter\Configurator\JavaScript\CallbackGenerator;
  14  use s9e\TextFormatter\Configurator\JavaScript\Code;
  15  use s9e\TextFormatter\Configurator\JavaScript\ConfigOptimizer;
  16  use s9e\TextFormatter\Configurator\JavaScript\Dictionary;
  17  use s9e\TextFormatter\Configurator\JavaScript\Encoder;
  18  use s9e\TextFormatter\Configurator\JavaScript\HintGenerator;
  19  use s9e\TextFormatter\Configurator\JavaScript\Minifier;
  20  use s9e\TextFormatter\Configurator\JavaScript\Minifiers\Noop;
  21  use s9e\TextFormatter\Configurator\JavaScript\RegexpConvertor;
  22  use s9e\TextFormatter\Configurator\JavaScript\StylesheetCompressor;
  23  use s9e\TextFormatter\Configurator\RendererGenerators\XSLT;
  24  
  25  class JavaScript
  26  {
  27      /**
  28      * @var CallbackGenerator
  29      */
  30      protected $callbackGenerator;
  31  
  32      /**
  33      * @var array Configuration, filtered for JavaScript
  34      */
  35      protected $config;
  36  
  37      /**
  38      * @var ConfigOptimizer
  39      */
  40      protected $configOptimizer;
  41  
  42      /**
  43      * @var Configurator Configurator this instance belongs to
  44      */
  45      protected $configurator;
  46  
  47      /**
  48      * @var Encoder
  49      */
  50      public $encoder;
  51  
  52      /**
  53      * @var array List of methods and properties to be exported in the s9e.TextFormatter object
  54      */
  55      public $exports = [
  56          'disablePlugin',
  57          'disableTag',
  58          'enablePlugin',
  59          'enableTag',
  60          'getLogger',
  61          'parse',
  62          'preview',
  63          'registeredVars',
  64          'setNestingLimit',
  65          'setParameter',
  66          'setTagLimit'
  67      ];
  68  
  69      /**
  70      * @var HintGenerator
  71      */
  72      protected $hintGenerator;
  73  
  74      /**
  75      * @var Minifier Instance of Minifier used to minify the JavaScript parser
  76      */
  77      protected $minifier;
  78  
  79      /**
  80      * @var StylesheetCompressor
  81      */
  82      protected $stylesheetCompressor;
  83  
  84      /**
  85      * @var string Stylesheet used for rendering
  86      */
  87      protected $xsl;
  88  
  89      /**
  90      * Constructor
  91      *
  92      * @param  Configurator $configurator Configurator
  93      */
  94  	public function __construct(Configurator $configurator)
  95      {
  96          $this->encoder              = new Encoder;
  97          $this->callbackGenerator    = new CallbackGenerator;
  98          $this->configOptimizer      = new ConfigOptimizer($this->encoder);
  99          $this->configurator         = $configurator;
 100          $this->hintGenerator        = new HintGenerator;
 101          $this->stylesheetCompressor = new StylesheetCompressor;
 102      }
 103  
 104      /**
 105      * Return the cached instance of Minifier (creates one if necessary)
 106      *
 107      * @return Minifier
 108      */
 109  	public function getMinifier()
 110      {
 111          if (!isset($this->minifier))
 112          {
 113              $this->minifier = new Noop;
 114          }
 115  
 116          return $this->minifier;
 117      }
 118  
 119      /**
 120      * Get a JavaScript parser
 121      *
 122      * @param  array  $config Config array returned by the configurator
 123      * @return string         JavaScript parser
 124      */
 125  	public function getParser(array $config = null)
 126      {
 127          $this->configOptimizer->reset();
 128  
 129          // Get the stylesheet used for rendering
 130          $xslt      = new XSLT;
 131          $xslt->normalizer->remove('RemoveLivePreviewAttributes');
 132          $this->xsl = $xslt->getXSL($this->configurator->rendering);
 133  
 134          // Prepare the parser's config
 135          $this->config = (isset($config)) ? $config : $this->configurator->asConfig();
 136          $this->config = ConfigHelper::filterConfig($this->config, 'JS');
 137          $this->config = $this->callbackGenerator->replaceCallbacks($this->config);
 138  
 139          // Get the parser's source and inject its config
 140          $src = $this->getHints() . $this->injectConfig($this->getSource());
 141  
 142          // Export the public API
 143          $src .= "if (!window['s9e']) window['s9e'] = {};\n" . $this->getExports();
 144  
 145          // Minify the source
 146          $src = $this->getMinifier()->get($src);
 147  
 148          // Wrap the source in a function to protect the global scope
 149          $src = '(function(){' . $src . '})();';
 150  
 151          return $src;
 152      }
 153  
 154      /**
 155      * Set the cached instance of Minifier
 156      *
 157      * Extra arguments will be passed to the minifier's constructor
 158      *
 159      * @param  string|Minifier $minifier Name of a supported minifier, or an instance of Minifier
 160      * @return Minifier                  The new minifier
 161      */
 162  	public function setMinifier($minifier)
 163      {
 164          if (is_string($minifier))
 165          {
 166              $className = __NAMESPACE__ . '\\JavaScript\\Minifiers\\' . $minifier;
 167  
 168              // Pass the extra argument to the constructor, if applicable
 169              $args = array_slice(func_get_args(), 1);
 170              if (!empty($args))
 171              {
 172                  $reflection = new ReflectionClass($className);
 173                  $minifier   = $reflection->newInstanceArgs($args);
 174              }
 175              else
 176              {
 177                  $minifier = new $className;
 178              }
 179          }
 180  
 181          $this->minifier = $minifier;
 182  
 183          return $minifier;
 184      }
 185  
 186      //==========================================================================
 187      // Internal
 188      //==========================================================================
 189  
 190      /**
 191      * Encode a PHP value into an equivalent JavaScript representation
 192      *
 193      * @param  mixed  $value Original value
 194      * @return string        JavaScript representation
 195      */
 196  	protected function encode($value)
 197      {
 198          return $this->encoder->encode($value);
 199      }
 200  
 201      /**
 202      * Generate and return the public API
 203      *
 204      * @return string JavaScript Code
 205      */
 206  	protected function getExports()
 207      {
 208          if (empty($this->exports))
 209          {
 210              return '';
 211          }
 212  
 213          $exports = [];
 214          foreach ($this->exports as $export)
 215          {
 216              $exports[] = "'" . $export . "':" . $export;
 217          }
 218          sort($exports);
 219  
 220          return "window['s9e']['TextFormatter'] = {" . implode(',', $exports) . '};';
 221      }
 222  
 223      /**
 224      * Generate a HINT object that contains informations about the configuration
 225      *
 226      * @return string JavaScript Code
 227      */
 228  	protected function getHints()
 229      {
 230          $this->hintGenerator->setConfig($this->config);
 231          $this->hintGenerator->setPlugins($this->configurator->plugins);
 232          $this->hintGenerator->setXSL($this->xsl);
 233  
 234          return $this->hintGenerator->getHints();
 235      }
 236  
 237      /**
 238      * Return the plugins' config
 239      *
 240      * @return Dictionary
 241      */
 242  	protected function getPluginsConfig()
 243      {
 244          $plugins = new Dictionary;
 245  
 246          foreach ($this->config['plugins'] as $pluginName => $pluginConfig)
 247          {
 248              if (!isset($pluginConfig['js']))
 249              {
 250                  // Skip this plugin
 251                  continue;
 252              }
 253              $js = $pluginConfig['js'];
 254              unset($pluginConfig['js']);
 255  
 256              // Not needed in JavaScript
 257              unset($pluginConfig['className']);
 258  
 259              // Ensure that quickMatch is UTF-8 if present
 260              if (isset($pluginConfig['quickMatch']))
 261              {
 262                  // Well-formed UTF-8 sequences
 263                  $valid = [
 264                      '[[:ascii:]]',
 265                      // [1100 0000-1101 1111] [1000 0000-1011 1111]
 266                      '[\\xC0-\\xDF][\\x80-\\xBF]',
 267                      // [1110 0000-1110 1111] [1000 0000-1011 1111]{2}
 268                      '[\\xE0-\\xEF][\\x80-\\xBF]{2}',
 269                      // [1111 0000-1111 0111] [1000 0000-1011 1111]{3}
 270                      '[\\xF0-\\xF7][\\x80-\\xBF]{3}'
 271                  ];
 272  
 273                  $regexp = '#(?>' . implode('|', $valid) . ')+#';
 274  
 275                  // Keep only the first valid sequence of UTF-8, or unset quickMatch if none is found
 276                  if (preg_match($regexp, $pluginConfig['quickMatch'], $m))
 277                  {
 278                      $pluginConfig['quickMatch'] = $m[0];
 279                  }
 280                  else
 281                  {
 282                      unset($pluginConfig['quickMatch']);
 283                  }
 284              }
 285  
 286              /**
 287              * @var array Keys of elements that are kept in the global scope. Everything else will be
 288              *            moved into the plugin's parser
 289              */
 290              $globalKeys = [
 291                  'quickMatch'  => 1,
 292                  'regexp'      => 1,
 293                  'regexpLimit' => 1
 294              ];
 295  
 296              $globalConfig = array_intersect_key($pluginConfig, $globalKeys);
 297              $localConfig  = array_diff_key($pluginConfig, $globalKeys);
 298  
 299              if (isset($globalConfig['regexp']) && !($globalConfig['regexp'] instanceof Code))
 300              {
 301                  $globalConfig['regexp'] = new Code(RegexpConvertor::toJS($globalConfig['regexp'], true));
 302              }
 303  
 304              $globalConfig['parser'] = new Code(
 305                  '/**
 306                  * @param {string}          text
 307                  * @param {!Array.<!Array>} matches
 308                  */
 309                  function(text, matches)
 310                  {
 311                      /** @const */
 312                      var config=' . $this->encode($localConfig) . ';
 313                      ' . $js . '
 314                  }'
 315              );
 316  
 317              $plugins[$pluginName] = $globalConfig;
 318          }
 319  
 320          return $plugins;
 321      }
 322  
 323      /**
 324      * Return the registeredVars config
 325      *
 326      * @return Dictionary
 327      */
 328  	protected function getRegisteredVarsConfig()
 329      {
 330          $registeredVars = $this->config['registeredVars'];
 331  
 332          // Remove cacheDir from the registered vars. Not only it is useless in JavaScript, it could
 333          // leak some informations about the server
 334          unset($registeredVars['cacheDir']);
 335  
 336          return new Dictionary($registeredVars);
 337      }
 338  
 339      /**
 340      * Return the root context config
 341      *
 342      * @return array
 343      */
 344  	protected function getRootContext()
 345      {
 346          return $this->config['rootContext'];
 347      }
 348  
 349      /**
 350      * Return the parser's source
 351      *
 352      * @return string
 353      */
 354  	protected function getSource()
 355      {
 356          $rootDir = __DIR__ . '/..';
 357          $src     = '';
 358  
 359          // If getLogger() is not exported we use a dummy Logger that can be optimized away
 360          $logger = (in_array('getLogger', $this->exports)) ? 'Logger.js' : 'NullLogger.js';
 361  
 362          // Prepare the list of files
 363          $files   = glob($rootDir . '/Parser/AttributeFilters/*.js');
 364          $files[] = $rootDir . '/Parser/utils.js';
 365          $files[] = $rootDir . '/Parser/FilterProcessing.js';
 366          $files[] = $rootDir . '/Parser/' . $logger;
 367          $files[] = $rootDir . '/Parser/Tag.js';
 368          $files[] = $rootDir . '/Parser.js';
 369  
 370          // Append render.js if we export the preview method
 371          if (in_array('preview', $this->exports, true))
 372          {
 373              $files[] = $rootDir . '/render.js';
 374              $src .= '/** @const */ var xsl=' . $this->getStylesheet() . ";\n";
 375          }
 376  
 377          $src .= implode("\n", array_map('file_get_contents', $files));
 378  
 379          return $src;
 380      }
 381  
 382      /**
 383      * Return the JavaScript representation of the stylesheet
 384      *
 385      * @return string
 386      */
 387  	protected function getStylesheet()
 388      {
 389          return $this->stylesheetCompressor->encode($this->xsl);
 390      }
 391  
 392      /**
 393      * Return the tags' config
 394      *
 395      * @return Dictionary
 396      */
 397  	protected function getTagsConfig()
 398      {
 399          // Prepare a Dictionary that will preserve tags' names
 400          $tags = new Dictionary;
 401          foreach ($this->config['tags'] as $tagName => $tagConfig)
 402          {
 403              if (isset($tagConfig['attributes']))
 404              {
 405                  // Make the attributes array a Dictionary, to preserve the attributes' names
 406                  $tagConfig['attributes'] = new Dictionary($tagConfig['attributes']);
 407              }
 408  
 409              $tags[$tagName] = $tagConfig;
 410          }
 411  
 412          return $tags;
 413      }
 414  
 415      /**
 416      * Inject the parser config into given source
 417      *
 418      * @param  string $src Parser's source
 419      * @return string      Modified source
 420      */
 421  	protected function injectConfig($src)
 422      {
 423          $config = array_map(
 424              [$this, 'encode'],
 425              $this->configOptimizer->optimize(
 426                  [
 427                      'plugins'        => $this->getPluginsConfig(),
 428                      'registeredVars' => $this->getRegisteredVarsConfig(),
 429                      'rootContext'    => $this->getRootContext(),
 430                      'tagsConfig'     => $this->getTagsConfig()
 431                  ]
 432              )
 433          );
 434  
 435          $src = preg_replace_callback(
 436              '/(\\nvar (' . implode('|', array_keys($config)) . '))(;)/',
 437              function ($m) use ($config)
 438              {
 439                  return $m[1] . '=' . $config[$m[2]] . $m[3];
 440              },
 441              $src
 442          );
 443  
 444          // Prepend the deduplicated objects
 445          $src = $this->configOptimizer->getVarDeclarations() . $src;
 446  
 447          return $src;
 448      }
 449  }


Generated: Sun Feb 14 20:08:31 2021 Cross-referenced by PHPXref 0.7.1