/atlassian-plugins-webresource/src/main/java/com/atlassian/plugin/webresource/assembler/html/InteractiveHtmlTagWriter.java
Java | 95 lines | 55 code | 10 blank | 30 comment | 0 complexity | bea0262ba8d637f48604acf3add0f3cb MD5 | raw file
- package com.atlassian.plugin.webresource.assembler.html;
- import com.atlassian.plugin.webresource.ResourceUrl;
- import com.atlassian.plugin.webresource.assembler.ResourceUrls;
- import com.atlassian.plugin.webresource.impl.RequestState;
- import com.atlassian.plugin.webresource.impl.snapshot.resource.Resource;
- import com.atlassian.plugin.webresource.models.Requestable;
- import com.atlassian.webresource.api.UrlMode;
- import com.atlassian.webresource.api.assembler.resource.PluginUrlResource;
- import com.atlassian.webresource.api.assembler.resource.ResourcePhase;
- import com.google.common.collect.Maps;
- import javax.annotation.Nonnull;
- import java.io.Writer;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.LinkedHashMap;
- import java.util.List;
- import static java.util.Collections.emptyList;
- import static java.util.Objects.requireNonNull;
- import static java.util.stream.Collectors.toList;
- /**
- * Generates HTML to load CSS and JavaScript resources with an {@link ResourcePhase#INTERACTION} phase.
- * <p>
- * This strategy intentionally makes it the client-side's responsibility to request and load all interactive resources.
- * In the long run, it might not be a great idea. However, for the moment, it helps us work around more rigid internals
- * in the WRM.
- *
- * @since 5.0.0
- */
- final class InteractiveHtmlTagWriter extends HtmlTagWriter {
- InteractiveHtmlTagWriter(@Nonnull RequestState requestState, @Nonnull Writer writer, @Nonnull UrlMode urlMode) {
- // @todo: if should render prefetch links, add prefetch tag writers to the formatter
- super(requestState, writer, emptyList());
- }
- @Override
- public void writeHtmlTag(@Nonnull Collection<ResourceUrls> resources) {
- // Reduce the ResourceUrls that were split by content type. Keep them in order.
- LinkedHashMap<String, ResourceUrls> reducedResources = Maps.newLinkedHashMap();
- resources.forEach(resource -> reducedResources.put(resource.getResourceUrl().getKey(), resource));
- // Render a single script tag to make a call for multiple `ResourceUrl` objects, since it's more efficient
- // in the browser than having dozens of separate script tags.
- writeHtmlTag(generateRequireLazyScriptTag(reducedResources.values()));
- // @todo: if should render prefetch links, use super to do so
- // super.writeHtmlTag(resources);
- }
- @Nonnull
- @Override
- String generateHtmlTag(@Nonnull ResourceUrls resourceUrls, @Nonnull HtmlTagFormatter formatter) {
- requireNonNull(resourceUrls, "The resource urls are mandatory for the creation of the script tag.");
- requireNonNull(formatter, "The formatter is mandory for generating the tags.");
- return formatter.format(resourceUrls);
- }
- /**
- * A hackish approach to making the client request resources lazily.
- * <p>
- * This strategy reverts URLs for CSS/JS batches back in to the various requested keys that created them,
- * then uses those to write some <code>WRM.require(requestedKeys)</code> JavaScript calls.
- */
- private String generateRequireLazyScriptTag(Collection<ResourceUrls> resources) {
- final List<String> formattedKeys = resources.stream()
- .map(ResourceUrls::getResourceUrl)
- .map(this::generateRequireLazyArguments)
- .flatMap(Collection::stream)
- .collect(toList());
- return formattedKeys.size() > 0 ?
- // The `defer` attribute is added in order to support defer-loading the WRM client, otherwise
- // we'd potentially make calls to it before it's loaded.
- String.format("<script defer>WRM.requireLazily([%s])</script>", String.join(",", formattedKeys)) :
- "";
- }
- /**
- * Reverts URLs for CSS/JS batches back in to the various requested keys that created them.
- *
- * @param batchedResources an abstraction over one or more {@link Resource} objects, addressable by URL.
- * @return loosely typed {@link Requestable} keys a user would need to request in order to retrieve the content.
- */
- private List<String> generateRequireLazyArguments(ResourceUrl batchedResources) {
- final String prefix = batchedResources.getBatchType().equals(PluginUrlResource.BatchType.CONTEXT) ? "wrc!" : "wr!";
- final String[] split = batchedResources.getKey().split(",");
- return Arrays.stream(split)
- // Remove exclusions; the client-side will handle that by inspecting `data-wrm-` attributes in HTML
- .filter(key -> !key.startsWith("-"))
- .map(key -> "\"" + prefix + key + "\"")
- .collect(toList());
- }
- }