Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

JavaScript.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 11.23 KiB


001  <?php
002   
003  /**
004  * @package   s9e\TextFormatter
005  * @copyright Copyright (c) 2010-2022 The s9e authors
006  * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
007  */
008  namespace s9e\TextFormatter\Configurator;
009   
010  use ReflectionClass;
011  use s9e\TextFormatter\Configurator;
012  use s9e\TextFormatter\Configurator\Helpers\AVTHelper;
013  use s9e\TextFormatter\Configurator\Helpers\ConfigHelper;
014  use s9e\TextFormatter\Configurator\JavaScript\CallbackGenerator;
015  use s9e\TextFormatter\Configurator\JavaScript\Code;
016  use s9e\TextFormatter\Configurator\JavaScript\ConfigOptimizer;
017  use s9e\TextFormatter\Configurator\JavaScript\Dictionary;
018  use s9e\TextFormatter\Configurator\JavaScript\Encoder;
019  use s9e\TextFormatter\Configurator\JavaScript\HintGenerator;
020  use s9e\TextFormatter\Configurator\JavaScript\Minifier;
021  use s9e\TextFormatter\Configurator\JavaScript\Minifiers\Noop;
022  use s9e\TextFormatter\Configurator\JavaScript\RegexpConvertor;
023  use s9e\TextFormatter\Configurator\JavaScript\StylesheetCompressor;
024  use s9e\TextFormatter\Configurator\RendererGenerators\XSLT;
025   
026  class JavaScript
027  {
028      /**
029      * @var CallbackGenerator
030      */
031      protected $callbackGenerator;
032   
033      /**
034      * @var array Configuration, filtered for JavaScript
035      */
036      protected $config;
037   
038      /**
039      * @var ConfigOptimizer
040      */
041      protected $configOptimizer;
042   
043      /**
044      * @var Configurator Configurator this instance belongs to
045      */
046      protected $configurator;
047   
048      /**
049      * @var Encoder
050      */
051      public $encoder;
052   
053      /**
054      * @var array List of methods and properties to be exported in the s9e.TextFormatter object
055      */
056      public $exports = [
057          'disablePlugin',
058          'disableTag',
059          'enablePlugin',
060          'enableTag',
061          'getLogger',
062          'parse',
063          'preview',
064          'registeredVars',
065          'setNestingLimit',
066          'setParameter',
067          'setTagLimit'
068      ];
069   
070      /**
071      * @var HintGenerator
072      */
073      protected $hintGenerator;
074   
075      /**
076      * @var Minifier Instance of Minifier used to minify the JavaScript parser
077      */
078      protected $minifier;
079   
080      /**
081      * @var StylesheetCompressor
082      */
083      protected $stylesheetCompressor;
084   
085      /**
086      * @var string Stylesheet used for rendering
087      */
088      protected $xsl;
089   
090      /**
091      * Constructor
092      *
093      * @param  Configurator $configurator Configurator
094      */
095      public function __construct(Configurator $configurator)
096      {
097          $this->encoder              = new Encoder;
098          $this->callbackGenerator    = new CallbackGenerator;
099          $this->configOptimizer      = new ConfigOptimizer($this->encoder);
100          $this->configurator         = $configurator;
101          $this->hintGenerator        = new HintGenerator;
102          $this->stylesheetCompressor = new StylesheetCompressor;
103      }
104   
105      /**
106      * Return the cached instance of Minifier (creates one if necessary)
107      *
108      * @return Minifier
109      */
110      public function getMinifier()
111      {
112          if (!isset($this->minifier))
113          {
114              $this->minifier = new Noop;
115          }
116   
117          return $this->minifier;
118      }
119   
120      /**
121      * Get a JavaScript parser
122      *
123      * @param  array  $config Config array returned by the configurator
124      * @return string         JavaScript parser
125      */
126      public function getParser(array $config = null)
127      {
128          $this->configOptimizer->reset();
129   
130          // Get the stylesheet used for rendering
131          $xslt      = new XSLT;
132          $xslt->normalizer->remove('RemoveLivePreviewAttributes');
133          $this->xsl = $xslt->getXSL($this->configurator->rendering);
134   
135          // Prepare the parser's config
136          $this->config = $config ?? $this->configurator->asConfig();
137          $this->config = ConfigHelper::filterConfig($this->config, 'JS');
138          $this->config = $this->callbackGenerator->replaceCallbacks($this->config);
139   
140          // Get the parser's source and inject its config
141          $src = $this->getHints() . $this->injectConfig($this->getSource());
142   
143          // Export the public API
144          $src .= "if (!window['s9e']) window['s9e'] = {};\n" . $this->getExports();
145   
146          // Minify the source
147          $src = $this->getMinifier()->get($src);
148   
149          // Wrap the source in a function to protect the global scope
150          $src = '(function(){' . $src . '})();';
151   
152          return $src;
153      }
154   
155      /**
156      * Set the cached instance of Minifier
157      *
158      * Extra arguments will be passed to the minifier's constructor
159      *
160      * @param  string|Minifier $minifier Name of a supported minifier, or an instance of Minifier
161      * @return Minifier                  The new minifier
162      */
163      public function setMinifier($minifier)
164      {
165          if (is_string($minifier))
166          {
167              $className = __NAMESPACE__ . '\\JavaScript\\Minifiers\\' . $minifier;
168   
169              // Pass the extra argument to the constructor, if applicable
170              $args = array_slice(func_get_args(), 1);
171              if (!empty($args))
172              {
173                  $reflection = new ReflectionClass($className);
174                  $minifier   = $reflection->newInstanceArgs($args);
175              }
176              else
177              {
178                  $minifier = new $className;
179              }
180          }
181   
182          $this->minifier = $minifier;
183   
184          return $minifier;
185      }
186   
187      //==========================================================================
188      // Internal
189      //==========================================================================
190   
191      /**
192      * Encode a PHP value into an equivalent JavaScript representation
193      *
194      * @param  mixed  $value Original value
195      * @return string        JavaScript representation
196      */
197      protected function encode($value)
198      {
199          return $this->encoder->encode($value);
200      }
201   
202      /**
203      * Generate and return the public API
204      *
205      * @return string JavaScript Code
206      */
207      protected function getExports()
208      {
209          if (empty($this->exports))
210          {
211              return '';
212          }
213   
214          $exports = [];
215          foreach ($this->exports as $export)
216          {
217              $exports[] = "'" . $export . "':" . $export;
218          }
219          sort($exports);
220   
221          return "window['s9e']['TextFormatter'] = {" . implode(',', $exports) . '};';
222      }
223   
224      /**
225      * @return string Function cache serialized as a JavaScript object
226      */
227      protected function getFunctionCache(): string
228      {
229          preg_match_all('(data-s9e-livepreview-on\\w+="([^">]++)(?=[^<>]++>))', $this->xsl, $m);
230   
231          $cache = [];
232          foreach ($m[1] as $js)
233          {
234              $avt = AVTHelper::parse($js);
235              if (count($avt) === 1 && $avt[0][0] === 'literal')
236              {
237                  $js = htmlspecialchars_decode($js);
238                  $cache[] = json_encode($js) . ':/**@this {!Element}*/function(){' . trim($js, ';') . ';}';
239              }
240          }
241   
242          return '{' . implode(',', $cache) . '}';
243      }
244   
245      /**
246      * Generate a HINT object that contains informations about the configuration
247      *
248      * @return string JavaScript Code
249      */
250      protected function getHints()
251      {
252          $this->hintGenerator->setConfig($this->config);
253          $this->hintGenerator->setPlugins($this->configurator->plugins);
254          $this->hintGenerator->setXSL($this->xsl);
255   
256          return $this->hintGenerator->getHints();
257      }
258   
259      /**
260      * Return the plugins' config
261      *
262      * @return Dictionary
263      */
264      protected function getPluginsConfig()
265      {
266          $plugins = new Dictionary;
267   
268          foreach ($this->config['plugins'] as $pluginName => $pluginConfig)
269          {
270              if (!isset($pluginConfig['js']))
271              {
272                  // Skip this plugin
273                  continue;
274              }
275              $js = $pluginConfig['js'];
276              unset($pluginConfig['js']);
277   
278              // Not needed in JavaScript
279              unset($pluginConfig['className']);
280   
281              // Ensure that quickMatch is UTF-8 if present
282              if (isset($pluginConfig['quickMatch']))
283              {
284                  // Well-formed UTF-8 sequences
285                  $valid = [
286                      '[[:ascii:]]',
287                      // [1100 0000-1101 1111] [1000 0000-1011 1111]
288                      '[\\xC0-\\xDF][\\x80-\\xBF]',
289                      // [1110 0000-1110 1111] [1000 0000-1011 1111]{2}
290                      '[\\xE0-\\xEF][\\x80-\\xBF]{2}',
291                      // [1111 0000-1111 0111] [1000 0000-1011 1111]{3}
292                      '[\\xF0-\\xF7][\\x80-\\xBF]{3}'
293                  ];
294   
295                  $regexp = '#(?>' . implode('|', $valid) . ')+#';
296   
297                  // Keep only the first valid sequence of UTF-8, or unset quickMatch if none is found
298                  if (preg_match($regexp, $pluginConfig['quickMatch'], $m))
299                  {
300                      $pluginConfig['quickMatch'] = $m[0];
301                  }
302                  else
303                  {
304                      unset($pluginConfig['quickMatch']);
305                  }
306              }
307   
308              /**
309              * @var array Keys of elements that are kept in the global scope. Everything else will be
310              *            moved into the plugin's parser
311              */
312              $globalKeys = [
313                  'quickMatch'  => 1,
314                  'regexp'      => 1,
315                  'regexpLimit' => 1
316              ];
317   
318              $globalConfig = array_intersect_key($pluginConfig, $globalKeys);
319              $localConfig  = array_diff_key($pluginConfig, $globalKeys);
320   
321              if (isset($globalConfig['regexp']) && !($globalConfig['regexp'] instanceof Code))
322              {
323                  $globalConfig['regexp'] = new Code(RegexpConvertor::toJS($globalConfig['regexp'], true));
324              }
325   
326              $globalConfig['parser'] = new Code(
327                  '/**
328                  * @param {string}          text
329                  * @param {!Array.<!Array>} matches
330                  */
331                  function(text, matches)
332                  {
333                      /** @const */
334                      var config=' . $this->encode($localConfig) . ';
335                      ' . $js . '
336                  }'
337              );
338   
339              $plugins[$pluginName] = $globalConfig;
340          }
341   
342          return $plugins;
343      }
344   
345      /**
346      * Return the registeredVars config
347      *
348      * @return Dictionary
349      */
350      protected function getRegisteredVarsConfig()
351      {
352          $registeredVars = $this->config['registeredVars'];
353   
354          // Remove cacheDir from the registered vars. Not only it is useless in JavaScript, it could
355          // leak some informations about the server
356          unset($registeredVars['cacheDir']);
357   
358          return new Dictionary($registeredVars);
359      }
360   
361      /**
362      * Return the root context config
363      *
364      * @return array
365      */
366      protected function getRootContext()
367      {
368          return $this->config['rootContext'];
369      }
370   
371      /**
372      * Return the parser's source
373      *
374      * @return string
375      */
376      protected function getSource()
377      {
378          $rootDir = __DIR__ . '/..';
379          $src     = '';
380   
381          // If getLogger() is not exported we use a dummy Logger that can be optimized away
382          $logger = (in_array('getLogger', $this->exports)) ? 'Logger.js' : 'NullLogger.js';
383   
384          // Prepare the list of files
385          $files   = glob($rootDir . '/Parser/AttributeFilters/*.js');
386          $files[] = $rootDir . '/Parser/utils.js';
387          $files[] = $rootDir . '/Parser/FilterProcessing.js';
388          $files[] = $rootDir . '/Parser/' . $logger;
389          $files[] = $rootDir . '/Parser/Tag.js';
390          $files[] = $rootDir . '/Parser.js';
391   
392          // Append render.js if we export the preview method
393          if (in_array('preview', $this->exports, true))
394          {
395              $files[] = $rootDir . '/render.js';
396              $src .= '/** @const */ var xsl=' . $this->getStylesheet() . ";\n";
397              $src .= 'var functionCache=' . $this->getFunctionCache() . ";\n";
398          }
399   
400          $src .= implode("\n", array_map('file_get_contents', $files));
401   
402          return $src;
403      }
404   
405      /**
406      * Return the JavaScript representation of the stylesheet
407      *
408      * @return string
409      */
410      protected function getStylesheet()
411      {
412          return $this->stylesheetCompressor->encode($this->xsl);
413      }
414   
415      /**
416      * Return the tags' config
417      *
418      * @return Dictionary
419      */
420      protected function getTagsConfig()
421      {
422          // Prepare a Dictionary that will preserve tags' names
423          $tags = new Dictionary;
424          foreach ($this->config['tags'] as $tagName => $tagConfig)
425          {
426              if (isset($tagConfig['attributes']))
427              {
428                  // Make the attributes array a Dictionary, to preserve the attributes' names
429                  $tagConfig['attributes'] = new Dictionary($tagConfig['attributes']);
430              }
431   
432              $tags[$tagName] = $tagConfig;
433          }
434   
435          return $tags;
436      }
437   
438      /**
439      * Inject the parser config into given source
440      *
441      * @param  string $src Parser's source
442      * @return string      Modified source
443      */
444      protected function injectConfig($src)
445      {
446          $config = array_map(
447              [$this, 'encode'],
448              $this->configOptimizer->optimize(
449                  [
450                      'plugins'        => $this->getPluginsConfig(),
451                      'registeredVars' => $this->getRegisteredVarsConfig(),
452                      'rootContext'    => $this->getRootContext(),
453                      'tagsConfig'     => $this->getTagsConfig()
454                  ]
455              )
456          );
457   
458          $src = preg_replace_callback(
459              '/(\\nvar (' . implode('|', array_keys($config)) . '))(;)/',
460              function ($m) use ($config)
461              {
462                  return $m[1] . '=' . $config[$m[2]] . $m[3];
463              },
464              $src
465          );
466   
467          // Prepend the deduplicated objects
468          $src = $this->configOptimizer->getVarDeclarations() . $src;
469   
470          return $src;
471      }
472  }