mustache_templates-8.x-1.0-beta4/modules/mustache_magic/src/Plugin/mustache/Magic/Javascript.php
modules/mustache_magic/src/Plugin/mustache/Magic/Javascript.php
<?php
namespace Drupal\mustache_magic\Plugin\mustache\Magic;
use Drupal\Component\Utility\Html;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\RenderContext;
use Drupal\mustache\Helpers\MustacheRenderTemplate;
use Drupal\mustache\Render\Markup;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Write aggregateable Javascript.
*
* Usage:
*
* Use the {{#js.<name>}} section variable to define an aggregateable JS block.
* You can refer to this aggregated asset using {{><name>.js}}.
* The code will be automatically wrapped by a closure, to prevent naming
* collisions with other global variables.
*
* Available variables:
* - "sync": The global mustacheSync object that holds registered
* synchronization items, data providers and templates.
* - "Drupal": The global Drupal object.
* - "settings": The global drupalSettings object.
*
* @code
* {{#js.myalert}}alert('Hello!');{{/js.myalert}}
* // You may reuse this asset like this:
* {{>myalert.js}}
* @endcode
*
* @MustacheMagic(
* id = "js",
* label = @Translation("JS (Javascript)"),
* description = @Translation("Use the {{#js.<b><name></b>}} section variable to define an aggregateable JS block. You can refer to this aggregated asset using {{><b><name></b>.js}}. The code will be automatically wrapped by a closure, to prevent naming collisions with other global variables. Available variables:<ul><li><b>sync</b>: The global mustacheSync object that holds registered synchronization items, data providers and templates.</li><li><b>Drupal</b>: The global Drupal object.</li><li><b>settings</b>: The global drupalSettings object.</li></ul>Example:<p>{{#js.myalert}}alert('Hello!');{{/js.myalert}}<br/>// You may reuse this asset like this:<br/>{{>myalert.js}}</p>")
* )
*/
class Javascript extends Template {
/**
* The asset type extension.
*
* @var string
*/
public static $assetType = 'js';
/**
* A list of keywords to exclude as possible name.
*
* @var array
*/
public static $excludes = ['id', 'name', 'content', 'js', 'css', 'asset'];
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
$instance->renderer = $container->get('renderer');
return $instance;
}
/**
* {@inheritdoc}
*/
public function __isset($name): bool {
return !is_null($name) && is_scalar($name) && (trim((string) $name) !== '') && !in_array($name, static::$excludes);
}
/**
* {@inheritdoc}
*/
public function __invoke($template_content = NULL, $render = NULL) {
if (!isset($template_content, $render)) {
return;
}
if (!isset($this->templateName)) {
return $render($template_content);
}
$renderer = $this->renderer;
$bubbleable_metadata = BubbleableMetadata::createFromRenderArray($this->element);
$template_name = $this->templateName ?? hash('md4', $template_content);
$template_name .= '.' . static::$assetType;
// Use the magic JS template as a wrapper to embed the user-defined script.
$build = MustacheRenderTemplate::build('mustache_magic_' . static::$assetType);
$build->usingData($this->templateData($template_name, $template_content));
$build_render_array = &$build->toRenderArray();
$template_content = (string) $renderer->executeInRenderContext(new RenderContext(), function () use ($renderer, &$build_render_array) {
return $renderer->render($build_render_array);
});
$bubbleable_metadata
->addCacheableDependency(BubbleableMetadata::createFromRenderArray($build_render_array))
->applyTo($this->element);
unset($build_render_array);
// Now that we have wrapped the script, register it as template.
$this->templateStorage->registerTemplate($template_name, $template_content);
// Load the user-defined script as sync item.
$build = MustacheRenderTemplate::build($template_name);
$build->withPlaceholder(['#markup' => ''])
->usingData([static::$assetType => TRUE])
->exposeData(TRUE)
->withClientSynchronization()
->executesInnerScripts(TRUE)
->once();
$build_render_array = &$build->toRenderArray();
$build_render_array['#sync']['wrapper_tag'] = NULL;
$sync_item = $renderer->executeInRenderContext(new RenderContext(), function () use ($renderer, &$build_render_array) {
return $renderer->render($build_render_array);
});
$bubbleable_metadata
->addCacheableDependency(BubbleableMetadata::createFromRenderArray($build_render_array))
->applyTo($this->element);
// We need to replace the opening curly brackets, so that Mustache.php's
// lambda helper cannot chime in and would try to replace Mustache
// variables within the inline template with current context values.
return Markup::create(str_replace('{{', '\{\{', (string) $sync_item));
}
/**
* Builds up the array that is passed as data to the magic wrapper template.
*
* @param string $name
* The template name.
* @param string $content
* The template content.
*/
protected function templateData($name, $content) {
return [
'id' => Html::getUniqueId($name),
'name' => $name,
'content' => $content,
];
}
}
