/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.requirejs; import com.google.gwt.core.client.Callback; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptException; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.core.client.ScriptInjector; import org.eclipse.che.ide.util.loging.Log; import org.eclipse.che.requirejs.conf.RequirejsConfig; import javax.inject.Inject; /** * Loads javascript modules with requirejs. * * @author "Mickaƫl Leduque" */ public class RequireJsLoader { private static boolean requirejsLoaded = false; private static boolean requirejsLoadFailed = false; private ModuleHolder moduleHolder; @Inject public RequireJsLoader(final ModuleHolder moduleHolder) { this.moduleHolder = moduleHolder; } public void require(final Callback<JavaScriptObject[], Throwable> callback, final String[] requiredScripts, final String[] moduleKeys) { // require with default config final RequirejsConfig defaultConfig = RequirejsConfig.create(); /** Using GWT.getModuleBaseForStaticFiles() blocks CodeMirror to run under Super Dev Mode */ defaultConfig.setBaseUrl(GWT.getModuleBaseURL()); defaultConfig.setWaitSeconds(0); require(new RequirejsCallback() { @Override public void onReady(final JsArray<RequirejsModule> modules) { final JavaScriptObject[] result = new JavaScriptObject[modules.length()]; for (int i = 0; i < modules.length(); i++) { result[i] = modules.get(i); } callback.onSuccess(result); } }, new RequirejsErrorHandler() { @Override public void onError(final RequireError error) { callback.onFailure(new JavaScriptException(error)); } }, defaultConfig, requiredScripts, moduleKeys); } public void require(final RequirejsCallback callback, final RequirejsErrorHandler errorHandler, final RequirejsConfig config, final String[] requiredScripts, final String[] moduleKeys) { if (!requirejsLoaded && !requirejsLoadFailed) { Log.debug(RequireJsLoader.class, "Loading require.js."); /** Using GWT.getModuleBaseForStaticFiles() blocks CodeMirror to run under Super Dev Mode */ ScriptInjector.fromUrl(GWT.getModuleBaseURL() + "require.js") .setWindow(ScriptInjector.TOP_WINDOW) .setCallback(new Callback<Void, Exception>() { @Override public void onSuccess(final Void result) { Log.debug(RequireJsLoader.class, "require.js loaded."); configureGlobalErrorCallback(); requireScripts(callback, errorHandler, config, requiredScripts, moduleKeys); requirejsLoaded = true; } @Override public void onFailure(final Exception e) { Log.error(RequireJsLoader.class, "Unable to load require.js", e); requirejsLoadFailed = true; } }).inject(); } else if (!requirejsLoadFailed) { requireScripts(callback, null, config, requiredScripts, moduleKeys); } else { Log.error(RequireJsLoader.class, "Require.js load failed, cannot require scripts."); } } private void requireScripts(final RequirejsCallback callback, final RequirejsErrorHandler errorHandler, final RequirejsConfig config, final String[] requiredScripts, final String[] moduleKeys) { final JsArrayString jsReqScripts = (JsArrayString)JavaScriptObject.createArray(); for (final String script : requiredScripts) { jsReqScripts.push(script); } Requirejs.config(config).require(jsReqScripts, new RequirejsCallback() { @Override public void onReady(final JsArray<RequirejsModule> result) { for (int i = 0; i < Math.min(result.length(), moduleKeys.length); i++) { String itemtoString = "null"; if (result.get(i) != null) { itemtoString = result.get(i).toString(); } if (itemtoString.length() > 30) { itemtoString = itemtoString.substring(0, 27) + "..."; } Log.debug(RequireJsLoader.class, "Add module reference - name=" + moduleKeys[i] + " object=" + itemtoString); moduleHolder.setModule(moduleKeys[i], result.get(i)); } callback.onReady(result); } }, errorHandler); } protected static void configureGlobalErrorCallback() { Requirejs.get().setOnError(new RequirejsErrorHandler() { @Override public void onError(final RequireError err) { final String type = err.getRequireType(); if ("scripterror".equals(type)) { // leave the module as-is final JsArrayString modules = err.getRequireModules(); if (modules != null && modules.length() > 0) { final String failed = modules.get(0); String formattedMsg = ""; if (err.getMessage() != null) { formattedMsg = formattedMsg.replace("\n", "\n\t\t"); } consoleWarn("Required module '%s' load failed with script error " + "(nonexistant script or error in the loaded script)\n" + "\t- error message = '%s'\n" + "\t- original error = %o", failed, formattedMsg, err); } else { consoleWarn("Unexpected requirejs of type 'scripterror' without requireModules property: %o", err); throw new RuntimeException(err.toString()); } } else if ("timeout".equals(type)) { // we'll retry next time final JsArrayString modules = err.getRequireModules(); if (modules != null && modules.length() > 0) { final String failed = modules.get(0); consoleWarn("Required module '%s' load failed on timeout.", failed); Requirejs.get().undef(failed); } else { consoleWarn("Unexpected requirejs of type 'timeout' without requireModules property: %o", err); throw new RuntimeException(err.toString()); } } else { throw new RuntimeException(err.toString()); } } }); } private static final native void consoleWarn(String base, Object param1) /*-{ $wnd.console.warn(base, param1); }-*/; private static final native void consoleWarn(String base, Object param1, Object param2, Object param3) /*-{ $wnd.console.warn(base, param1, param2, param3); }-*/; public void require(final Callback<JavaScriptObject[], Throwable> callback, final String[] requiredScripts) { require(callback, requiredScripts, new String[0]); } }