package com.asayama.gwt.angular.client; import java.util.logging.Level; import java.util.logging.Logger; import com.asayama.gwt.angular.client.Directive.Restrict; import com.asayama.gwt.jquery.client.JQElement; import com.asayama.gwt.jsni.client.Closure; import com.asayama.gwt.jsni.client.Function; import com.asayama.gwt.jsni.client.JSClosure; import com.asayama.gwt.jsni.client.JSFunction; import com.asayama.gwt.jsni.client.JSON; import com.google.gwt.core.client.GWT; import com.google.gwt.resources.client.TextResource; /** * TODO This file needs some serious review and clean-up. * * @author kyoken74 */ public interface Directive { public static enum Restrict { Attribute('A'), Class('C'), Element('E'), Comment('M'); final char code; Restrict(char code) { this.code = code; } } String getName(); boolean getTransclude(); void setName(String name); Restrict[] getRestrict(); TextResource getTemplate(); String getTemplateUrl(); NGScope scope(); /** * In AngularJS, compile method returns linker function. In this * implementation, however, we do not return any values. This is because * {@link DefaultDirectiveFactory} wraps the call to * {@link #link(NGScope, JQElement, JSON)} into a JavaScript function, and * returns that from the JavaScript wrapping of the compile function call. * <p> * This means that, unlike AngularJS, we currently do not support compile * dependent linker option (i.e. the outcome of compile function cannot * return different linker functions). * </p> */ void compile(JQElement element, JSON attrs); void link(NGScope scope, JQElement element, JSON attrs); } class DefaultDirectiveFactory<D extends Directive> implements Function<NGDirective> { private static final String CLASS = DefaultDirectiveFactory.class.getName(); private static final Logger LOG = Logger.getLogger(CLASS); protected final String name; protected final Class<D> klass; DefaultDirectiveFactory(String name, Class<D> klass) { this.name = name; this.klass = klass; } @Override public NGDirective call(Object... args) { String m = "creating NGDirective for " + name; NGDirective ngo = NGDirective.create(); try { m = "creating directive " + name; DirectiveCreator creator = GWT.create(DirectiveCreator.class); final Directive directive = creator.create(klass); directive.setName(name); m = "determining directive restriction for " + name; Restrict[] rs = directive.getRestrict(); if (rs != null && rs.length > 0) { StringBuilder sb = new StringBuilder(); for (Restrict r : rs) { sb.append(r.code); } ngo.setRestrict(sb.toString()); } // TODO jso.setTransclude(directive.getTransclude()); m = "setting directive template for " + name; TextResource template = directive.getTemplate(); if (template != null) { ngo.setTemplate(template.getText()); } m = "setting directive templateUrl for " + name; String templateUrl = directive.getTemplateUrl(); if (templateUrl != null) { ngo.setTemplateUrl(templateUrl); } m = "setting directive compile for " + name; ngo.setCompile(JSFunction.create(new Function<JSClosure>() { @Override public JSClosure call(Object... args) { try { LOG.finest("calling " + name + ".compile()"); JQElement element = (JQElement) args[0]; JSON attrs = (JSON) args[1]; directive.compile(element, attrs); return JSClosure.create(new Closure() { @Override public void exec(Object... args) { try { LOG.finest("calling " + name + ".link()"); NGScope scope = (NGScope) args[0]; JQElement element = (JQElement) args[1]; JSON attrs = (JSON) args[2]; directive.link(scope, element, attrs); } catch (Exception e) { LOG.log(Level.WARNING, "Exception while calling " + name + ".link()", e); } } }); } catch (Exception e) { LOG.log(Level.WARNING, "Exception while calling " + name + ".compile()", e); return null; } } })); m = "getting directive scope for " + name; NGScope scope = directive.scope(); m = "setting directive scope for " + name; ngo.setScope(scope); m = "creating binder for " + name; DirectiveBinderFactory binderFactory = GWT.create(DirectiveBinderFactory.class); JSClosure binder = binderFactory.create(directive); m = "applying binder to " + name; if (binder != null) { binder.apply(args); } m = "returning NGDirective"; return ngo; } catch (Exception e) { LOG.log(Level.WARNING, "Exception while " + m, e); return ngo; } } } /** * https://docs.angularjs.org/api/ng/service/$compile * * @author kyoken74 */ class NGDirective extends JSON { protected NGDirective() { } final void setRestrict(String restrict) { put("restrict", restrict); } final void setTemplate(String template) { put("template", template); } final void setTemplateUrl(String templateUrl) { put("templateUrl", templateUrl); } final void setTransclude(boolean transclude) { put("transclude", transclude); } final void setCompile(JSFunction<JSClosure> compile) { put("compile", compile); } /** * If scope argument is null, this sets the scope to true, which implies * that a local scope is created inheriting the parent scope. If scope * value is not null, then a local isolate scope is created. */ final native void setScope(NGScope scope) /*-{ this.scope = scope ? scope : true; }-*/; }