* Injects javascript libraries needed to satisfy feature dependencies.
*/
private void injectFeatureLibraries(Gadget gadget, Node headTag) throws GadgetException {
// TODO: If there isn't any js in the document, we can skip this. Unfortunately, that means
// both script tags (easy to detect) and event handlers (much more complex).
GadgetContext context = gadget.getContext();
String forcedLibs = context.getParameter("libs");
// List of forced libraries we need
Set<String> forced;
// gather the libraries we'll need to generate the forced libs
if (forcedLibs == null || forcedLibs.length() == 0) {
// Don't bother making a mutable copy if the list is empty
forced = (defaultForcedLibs.isEmpty()) ? defaultForcedLibs : Sets.newTreeSet(defaultForcedLibs);
} else {
forced = Sets.newTreeSet(Arrays.asList(forcedLibs.split(":")));
}
if (!forced.isEmpty()) {
String jsUrl = urlGenerator.getBundledJsUrl(forced, context);
Element libsTag = headTag.getOwnerDocument().createElement("script");
libsTag.setAttribute("src", jsUrl);
headTag.appendChild(libsTag);
// Forced transitive deps need to be added as well so that they don't get pulled in twice.
// Without this, a shared dependency between forced and non-forced libs would get pulled into
// both the external forced script and the inlined script.
// TODO: Figure out a clean way to avoid having to call getFeatures twice.
for (GadgetFeature dep : featureRegistry.getFeatures(forced)) {
forced.add(dep.getName());
}
}
// Make this read-only
forced = ImmutableSet.copyOf(forced);
// Inline any libs that weren't forced. The ugly context switch between inline and external
// Js is needed to allow both inline and external scripts declared in feature.xml.
String container = context.getContainer();
Collection<GadgetFeature> features = getFeatures(gadget, forced);
// Precalculate the maximum length in order to avoid excessive garbage generation.
int size = 0;
for (GadgetFeature feature : features) {
for (JsLibrary library : feature.getJsLibraries(RenderingContext.GADGET, container)) {
if (library.getType().equals(JsLibrary.Type.URL)) {
size += library.getContent().length();
}
}
}
// Really inexact.
StringBuilder inlineJs = new StringBuilder(size);
for (GadgetFeature feature : features) {
for (JsLibrary library : feature.getJsLibraries(RenderingContext.GADGET, container)) {
if (library.getType().equals(JsLibrary.Type.URL)) {
if (inlineJs.length() > 0) {
Element inlineTag = headTag.getOwnerDocument().createElement("script");
headTag.appendChild(inlineTag);
inlineTag.appendChild(headTag.getOwnerDocument().createTextNode(inlineJs.toString()));
inlineJs.setLength(0);
}
Element referenceTag = headTag.getOwnerDocument().createElement("script");
referenceTag.setAttribute("src", library.getContent());
headTag.appendChild(referenceTag);
} else {
if (!forced.contains(feature.getName())) {
// already pulled this file in from the shared contents.
if (context.getDebug()) {
inlineJs.append(library.getDebugContent());
} else {
inlineJs.append(library.getContent());
}
inlineJs.append(";\n");