// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gwtexpui.linker.server;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;

/** A single permutation of the compiled GWT application. */
public class Permutation {
  private final PermutationSelector selector;
  private final String cacheHTML;
  private final String[] values;

  Permutation(PermutationSelector sel, String cacheHTML, String[] values) {
    this.selector = sel;
    this.cacheHTML = cacheHTML;
    this.values = values;
  }

  boolean matches(String[] r) {
    return Arrays.equals(values, r);
  }

  /**
   * Append GWT bootstrap for this permutation onto the end of the body.
   * <p>
   * The GWT bootstrap for this particular permutation is appended onto the end
   * of the {@code body} element of the passed host page.
   * <p>
   * To keep the bootstrap code small and simple, not all GWT features are
   * actually supported. The {@code gwt:property}, {@code gwt:onPropertyErrorFn}
   * and {@code gwt:onLoadErrorFn} meta tags are ignored and not handled.
   * <p>
   * Load order may differ from the standard GWT {@code nocache.js}. The browser
   * is asked to load the iframe immediately, rather than after the body has
   * finished loading.
   *
   * @param dom host page HTML document.
   */
  public void inject(Document dom) {
    String moduleName = selector.getModuleName();

    StringBuilder s = new StringBuilder();
    s.append("\n");
    s.append("function ").append(moduleName).append("(){");
    s.append("var s,l,t");
    s.append(",w=window");
    s.append(",d=document");
    s.append(",n='").append(moduleName).append("'");
    s.append(",f=d.createElement('iframe')");
    s.append(";");

    // Callback to execute the module once both s and l are true.
    //
    s.append("function m(){");
    s.append("if(s&&l){");
    // Base path needs to be absolute. There isn't an easy way to do this
    // other than forcing an image to load and then pulling the URL back.
    //
    s.append("var b,i=d.createElement('img');");
    s.append("i.src=n+'/clear.cache.gif';");
    s.append("b=i.src;");
    s.append("b=b.substring(0,b.lastIndexOf('/')+1);");
    s.append(moduleName).append("=null;"); // allow us to GC
    s.append("f.contentWindow.gwtOnLoad(undefined,n,b);");
    s.append("}");
    s.append("}");

    // Set s true when the module script has finished loading. The
    // exact name here is known to the IFrameLinker and is called by
    // the code in the iframe.
    //
    s.append(moduleName).append(".onScriptLoad=function(){");
    s.append("s=1;m();");
    s.append("};");

    // Set l true when the browser has finished processing the iframe
    // tag, and everything else on the page.
    //
    s.append(moduleName).append(".r=function(){");
    s.append("l=1;m();");
    s.append("};");

    // Prevents mixed mode security in IE6/7.
    s.append("f.src=\"javascript:''\";");
    s.append("f.id=n;");
    s.append("f.style.cssText)")
     .append("='position:absolute;width:0;height:0;border:none';");
    s.append("f.tabIndex=-1;");
    s.append("d.body.appendChild(f);");

    // The src has to be set after the iframe is attached to the DOM to avoid
    // refresh quirks in Safari. We have to use the location.replace trick to
    // avoid FF2 refresh quirks.
    //
    s.append("f.contentWindow.location.replace(n+'/").append(cacheHTML).append("');");

    // defer attribute here is to workaround IE running immediately.
    //
    s.append("d.write('<script defer=\"defer\">").append(moduleName).append(".r()</'+'script>');");
    s.append("}");
    s.append(moduleName).append("();");
    s.append("\n//");

    final Element html = dom.getDocumentElement();
    final Element head = (Element) html.getElementsByTagName("head").item(0);
    final Element body = (Element) html.getElementsByTagName("body").item(0);

    for (String css : selector.getCSS()) {
      if (isRelativeURL(css)) {
        css = moduleName + '/' + css;
      }

      final Element link = dom.createElement("link");
      link.setAttribute("rel", "stylesheet");
      link.setAttribute("href", css);
      head.appendChild(link);
    }

    final Element script = dom.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.setAttribute("language", "javascript");
    script.appendChild(dom.createComment(s.toString()));
    body.appendChild(script);
  }

  private static boolean isRelativeURL(String src) {
    if (src.startsWith("/")) {
      return false;
    }

    try {
      // If it parses as a URL, assume it is not relative.
      //
      new URL(src);
      return false;
    } catch (MalformedURLException e) {
    }

    return true;
  }
}

@@ -3620,17 +3620,17 @@
 Text
-)
 %22)
+;
 %0A    
- 
+s
 .app

Fix JS syntax error