package er.extensions.components.javascript; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.webobjects.appserver.WOActionResults; import com.webobjects.appserver.WOApplication; import com.webobjects.appserver.WOAssociation; import com.webobjects.appserver.WOComponent; import com.webobjects.appserver.WOContext; import com.webobjects.appserver.WODirectAction; import com.webobjects.appserver.WOElement; import com.webobjects.appserver.WORequest; import com.webobjects.appserver.WOResponse; import com.webobjects.appserver.WOSession; import com.webobjects.appserver._private.WODynamicElementCreationException; import com.webobjects.appserver._private.WOHTMLDynamicElement; import com.webobjects.appserver._private.WOStaticURLUtilities; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation._NSStringUtilities; import er.extensions.appserver.ERXApplication; import er.extensions.appserver.ERXResourceManager; import er.extensions.appserver.ERXResponse; import er.extensions.appserver.ERXResponseRewriter; import er.extensions.foundation.ERXExpiringCache; import er.extensions.foundation.ERXProperties; /** * Modern version of a javascript component. * <ul> * <li> HTML 4 compliant ("script" and attributes lowercased) * <li> hideInComment is ON by default if there is content * <li> can contain script text * <li> can have the script render to an external DA url that is cached in the session * <li> you can specify which framework the script comes from. * </ul> * @binding scriptSource SRC attribute, either a full URL or the filename of the script * @binding filename (same as scriptSource, but matches ERXStyleSheet) * @binding scriptFile the filename of the script when it should be * included in the page (only for compatibility, simply use the content) * @binding scriptFramework name of the framework for the script * @binding framework (same as scriptFramework, but matches ERXStyleSheet) * @binding scriptString the script text when it should be * included in the page (only for compatibility, simply use the content) * @binding scriptKey if set, the content will get rendered into an external script src * @binding hideInComment boolean that specifies if the script content should * be included in HTML comments, true by default of the script tag contains a script * * @property er.extensions.ERXJavaScript.hideInComment sets globally if the script * content should be included within HTML comments, defaults to <code>true</code> */ public class ERXJavaScript extends WOHTMLDynamicElement { @SuppressWarnings("unchecked") private static ERXExpiringCache<Object, WOResponse> cache(WOSession session) { ERXExpiringCache<Object, WOResponse> cache = (ERXExpiringCache<Object, WOResponse>) session.objectForKey("ERXJavaScript.cache"); if(cache == null) { cache = new ERXExpiringCache<>(60); session.setObjectForKey(cache, "ERXJavaScript.cache"); } return cache; } public static class Script extends WODirectAction { public Script(WORequest worequest) { super(worequest); } @Override public WOActionResults performActionNamed(String name) { WOResponse response = ERXJavaScript.cache(session()).objectForKey(name); return response; } } private static final Logger log = LoggerFactory.getLogger(ERXJavaScript.class); WOAssociation _framework; WOAssociation _scriptFramework; WOAssociation _filename; WOAssociation _scriptFile; WOAssociation _scriptString; WOAssociation _scriptSource; WOAssociation _scriptKey; WOAssociation _hideInComment; WOAssociation _language; public ERXJavaScript(String s, NSDictionary<String, WOAssociation> nsdictionary, WOElement woelement) { super("script", nsdictionary, woelement); _scriptFile = _associations.removeObjectForKey("scriptFile"); _scriptString = _associations.removeObjectForKey("scriptString"); _scriptSource = _associations.removeObjectForKey("scriptSource"); _filename = _associations.removeObjectForKey("filename"); _language = _associations.removeObjectForKey("language"); _scriptKey = _associations.removeObjectForKey("scriptKey"); _hideInComment = _associations.removeObjectForKey("hideInComment"); _scriptFramework = _associations.removeObjectForKey("scriptFramework"); _framework = _associations.removeObjectForKey("framework"); if((_scriptFile != null && _scriptString != null) || (_scriptFile != null && (_scriptSource != null || _filename != null)) || (_scriptString != null && (_scriptSource != null || _filename != null))) { throw new WODynamicElementCreationException("<" + getClass().getName() + "> Only one of 'scriptFile' or 'scriptString' or 'scriptSource/filename' attributes can be specified."); } if (_scriptFramework != null && _framework != null) { throw new WODynamicElementCreationException("<" + getClass().getName() + "> Only one of 'scriptFramework' or 'framework' can be specified."); } if (_scriptSource != null && _filename != null) { throw new WODynamicElementCreationException("<" + getClass().getName() + "> Only one of 'scriptFile' or 'filename' can be specified."); } } @Override public void appendAttributesToResponse(WOResponse woresponse, WOContext wocontext) { WOComponent wocomponent = wocontext.component(); woresponse._appendContentAsciiString(" type=\"text/javascript\""); String framework = null; String scriptName = null; String src = null; if(_scriptSource != null || _filename != null) { String srcFromBindings; if (_scriptSource != null) { srcFromBindings = (String)_scriptSource.valueInComponent(wocomponent); } else { srcFromBindings = (String) _filename.valueInComponent(wocomponent); } if(srcFromBindings != null) { if(!WOStaticURLUtilities.isRelativeURL(srcFromBindings)) { src = srcFromBindings; } else { if(!WOStaticURLUtilities.isFragmentURL(srcFromBindings)) { if(_scriptFramework != null) { framework = (String) _scriptFramework.valueInComponent(wocomponent); } else if (_framework != null) { framework = (String) _framework.valueInComponent(wocomponent); } scriptName = srcFromBindings; src = wocontext._urlForResourceNamed(srcFromBindings, framework, true); if(src == null) { src = wocomponent.baseURL() + "/" + srcFromBindings; } else if (ERXResourceManager._shouldGenerateCompleteResourceURL(wocontext)) { src = ERXResourceManager._completeURLForResource(src, null, wocontext); } } else { log.warn("relative fragment URL {}", srcFromBindings); } } } } Object key = null; if(src == null && _scriptKey != null) { key = _scriptKey.valueInComponent(wocomponent); if(key != null) { ERXExpiringCache<Object, WOResponse> cache = ERXJavaScript.cache(wocontext.session()); boolean render = cache.isStale(key); render |= ERXApplication.isDevelopmentModeSafe(); if(render) { WOResponse newresponse = new ERXResponse(); super.appendChildrenToResponse(newresponse, wocontext); newresponse.setHeader("application/x-javascript", "content-type"); cache.setObjectForKey(newresponse, key); } src = wocontext.directActionURLForActionNamed(Script.class.getName() + "/" + key, null); } } if(src != null) { woresponse._appendContentAsciiString(" src=\""); woresponse.appendContentString(src); woresponse.appendContentCharacter('"'); } super.appendAttributesToResponse(woresponse, wocontext); if (scriptName != null) { ERXResponseRewriter.resourceAddedToHead(wocontext, framework, scriptName); } } @Override public void appendChildrenToResponse(WOResponse woresponse, WOContext wocontext) { String script = ""; boolean hideInComment = ERXProperties.booleanForKeyWithDefault("er.extensions.ERXJavaScript.hideInComment", true); WOComponent wocomponent = wocontext.component(); if(_hideInComment != null) { hideInComment = _hideInComment.booleanValueInComponent(wocomponent); } if(hideInComment) { woresponse._appendContentAsciiString("<!--"); } woresponse.appendContentCharacter('\n'); if(_scriptFile != null) { String filename = (String) _scriptFile.valueInComponent(wocomponent); if(filename != null) { String framework = null; if(_scriptFramework != null) { framework = (String) _scriptFramework.valueInComponent(wocomponent); } else if (_framework != null) { framework = (String) _framework.valueInComponent(wocomponent); } java.net.URL url = WOApplication.application().resourceManager().pathURLForResourceNamed(filename, framework, wocontext._languages()); if(url == null) { url = wocontext.component()._componentDefinition().pathURLForResourceNamed(filename, framework, wocontext._languages()); } if(url == null) { throw new WODynamicElementCreationException("<" + getClass().getName() + "> : cannot find script file '" + filename + "'"); } script = _NSStringUtilities.stringFromPathURL(url); if (ERXResourceManager._shouldGenerateCompleteResourceURL(wocontext)) { script = ERXResourceManager._completeURLForResource(script, null, wocontext); } } woresponse.appendContentString(script); } else if(_scriptString != null) { Object obj1 = _scriptString.valueInComponent(wocomponent); if(obj1 != null) { script = obj1.toString(); } woresponse.appendContentString(script); } else { super.appendChildrenToResponse(woresponse, wocontext); } woresponse.appendContentCharacter('\n'); if(hideInComment) { woresponse._appendContentAsciiString("//-->"); } } @Override public void appendToResponse(WOResponse woresponse, WOContext wocontext) { if(wocontext == null || woresponse == null) { return; } String s = elementName(); if(s != null) { _appendOpenTagToResponse(woresponse, wocontext); } if(_scriptSource == null && _filename == null && ( hasChildrenElements() || _scriptString != null) && _scriptKey == null) { appendChildrenToResponse(woresponse, wocontext); } if(s != null) { _appendCloseTagToResponse(woresponse, wocontext); } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('<'); sb.append(getClass().getName()); sb.append(" scriptFile=" + _scriptFile); sb.append(" scriptString=" + _scriptString); sb.append(" scriptFramework=" + _scriptFramework); sb.append(" framework=" + _framework); sb.append(" scriptSource=" + _scriptSource); sb.append(" filename=" + _filename); sb.append(" hideInComment=" + _hideInComment); sb.append(" language=" + _language); sb.append('>'); return sb.toString(); } }