public static Uri exportJekyllPages(Context context) {
    File exportDirectory = context.getExternalFilesDir(null);

    String filename = System.currentTimeMillis() + "_jekyll.zip";

    File exportedFile = new File(exportDirectory, filename);

    try {
      FileOutputStream out = new FileOutputStream(exportedFile);

      ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(out));

      try {
        BootstrapSiteExporter.writeScriptDocumentation(
            context, zos, "embedded_website/docs/all", BootstrapSiteExporter.TEMPLATE_TYPE_JEKYLL);
        BootstrapSiteExporter.writeScriptDocumentation(
            context,
            zos,
            "embedded_website/docs/scheme",
            BootstrapSiteExporter.TEMPLATE_TYPE_JEKYLL);
        BootstrapSiteExporter.writeScriptDocumentation(
            context,
            zos,
            "embedded_website/docs/javascript",
            BootstrapSiteExporter.TEMPLATE_TYPE_JEKYLL);
      } finally {
        zos.close();
      }
    } catch (java.io.IOException e) {
      e.printStackTrace();
    }

    return Uri.fromFile(exportedFile);
  }
  private static void writeScriptDocumentation(
      Context context, ZipOutputStream out, String path, int templateType) {
    String[] components = path.split("/");

    String language = components[components.length - 1];

    byte[] indexPage =
        BootstrapSiteExporter.fetchContent(context, language, null, true, templateType);

    try {
      ZipEntry entry = new ZipEntry(path + "/index.html");
      out.putNextEntry(entry);
      out.write(indexPage);
      out.closeEntry();

      JSONArray methods = BootstrapSiteExporter.methodsForLanguage(context, language);

      HashSet<String> loaded = new HashSet<>();

      for (int i = 0; i < methods.length(); i++) {
        try {
          JSONObject methodDef = methods.getJSONObject(i);

          if (methodDef.has(BootstrapSiteExporter.METHOD_ASSET_PATH)) {
            String[] methodComponents =
                methodDef.getString(BootstrapSiteExporter.METHOD_ASSET_PATH).split("/");

            String page = methodComponents[methodComponents.length - 1];

            if (loaded.contains(page) == false) {
              String pageLanguage = language;

              if (page.startsWith("all_")) pageLanguage = "all";

              byte[] methodContent =
                  BootstrapSiteExporter.fetchContent(
                      context, pageLanguage, page, true, templateType);

              try {
                ZipEntry methodEntry = new ZipEntry(path + "/" + page);
                out.putNextEntry(methodEntry);
                out.write(methodContent);
                out.closeEntry();
              } catch (ZipException e) {
                e.printStackTrace();
              }

              loaded.add(page);
            }
          }
        } catch (JSONException e) {
          e.printStackTrace();
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  private static void writeStaticAssets(AssetManager assets, ZipOutputStream out, String path) {
    try {
      for (String item : assets.list(path)) {
        try {
          InputStream fileIn = assets.open(path + "/" + item);

          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          BufferedInputStream bin = new BufferedInputStream(fileIn);

          byte[] buffer = new byte[8192];
          int read = 0;

          while ((read = bin.read(buffer, 0, buffer.length)) != -1) {
            baos.write(buffer, 0, read);
          }

          bin.close();
          baos.close();

          ZipEntry entry = new ZipEntry(path + "/" + item);
          out.putNextEntry(entry);
          out.write(baos.toByteArray());
          out.closeEntry();
        } catch (IOException e) {
          BootstrapSiteExporter.writeStaticAssets(assets, out, path + "/" + item);
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static byte[] fetchContent(
      Context context, String language, String page, boolean readOnly, int templateType) {
    Class[] scriptingClasses = {JavaScriptEngine.class, SchemeEngine.class};

    language = language.toLowerCase();

    ArrayList<String> languages = new ArrayList<>();
    languages.add(BootstrapSiteExporter.LANGUAGE_ALL.toLowerCase());

    if (languages.contains(language) == false) languages.add(language.toLowerCase());

    if (page == null || page.trim().length() == 0) {
      try {
        JSONArray declaredMethods = BootstrapSiteExporter.methodsForLanguage(context, language);

        AssetManager am = context.getAssets();

        String template = "embedded_website/docs/scripting_template.html";

        if (readOnly && templateType == BootstrapSiteExporter.TEMPLATE_TYPE_BOOTSTRAP)
          template = "embedded_website/docs/scripting_template_readonly.html";
        else if (templateType == BootstrapSiteExporter.TEMPLATE_TYPE_JEKYLL)
          template = "embedded_website/docs/scripting_template_jekyll.html";

        InputStream in = am.open(template);

        // http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string
        Scanner s = new Scanner(in).useDelimiter("\\A");

        String content = "";

        if (s.hasNext()) content = s.next();

        content = content.replace("{{ METHOD_DEFINITIONS }}", declaredMethods.toString(2));
        content = content.replace("{{ LANGUAGE }}", language.toLowerCase());

        return content.getBytes(Charset.forName("UTF-8"));
      } catch (IOException | JSONException e) {
        e.printStackTrace();
      }

      return "404 ERROR".getBytes(Charset.forName("UTF-8"));
    } else {
      if (page.startsWith("all_")) language = "all";

      try {
        AssetManager am = context.getAssets();

        String template = "embedded_website/docs/method_template.html";

        if (readOnly && templateType == BootstrapSiteExporter.TEMPLATE_TYPE_BOOTSTRAP)
          template = "embedded_website/docs/method_template_readonly.html";
        else if (templateType == BootstrapSiteExporter.TEMPLATE_TYPE_JEKYLL)
          template = "embedded_website/docs/method_template_jekyll.html";

        InputStream in = am.open(template);

        // http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string
        Scanner s = new Scanner(in).useDelimiter("\\A");

        String content = "";

        if (s.hasNext()) content = s.next();

        s.close();

        in = am.open("embedded_website/docs/" + language.toLowerCase() + "/" + page);

        s = new Scanner(in).useDelimiter("\\A");

        String pageContent = "";

        if (s.hasNext()) pageContent = s.next();

        s.close();

        for (Class classObj : scriptingClasses) {
          Method[] methods = classObj.getMethods();

          for (Method method : methods) {
            Annotation[] annotations = method.getDeclaredAnnotations();

            for (Annotation annotation : annotations) {
              if (annotation instanceof ScriptingEngineMethod) {
                ScriptingEngineMethod scriptAnnotation = (ScriptingEngineMethod) annotation;

                if (page.equals(scriptAnnotation.assetPath())) {
                  StringBuilder args = new StringBuilder();

                  for (String argument : ((ScriptingEngineMethod) annotation).arguments()) {
                    if (args.length() > 0) args.append(", ");

                    args.append(argument);
                  }

                  content =
                      content.replace(
                          "{{ METHOD_NAME }}", method.getName() + "(" + args.toString() + ")");
                  content =
                      content.replace(
                          "{{ LANGUAGE }}",
                          ((ScriptingEngineMethod) annotation).language().toLowerCase());
                  content = content.replace("{{ PAGE }}", page);
                }
              }
            }
          }
        }

        content = content.replace("{{ METHOD_DOCUMENTATION }}", pageContent);

        return content.getBytes(Charset.forName("UTF-8"));
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    return "404 ERROR".getBytes(Charset.forName("UTF-8"));
  }