/** * */ package xapi.elemental.impl; import elemental.client.Browser; import elemental.dom.Element; import elemental.html.StyleElement; import elemental.html.TextAreaElement; import xapi.annotation.inject.SingletonDefault; import xapi.elemental.api.ElementalService; import xapi.elemental.api.PotentialNode; import xapi.elemental.api.StyleCacheService; import xapi.inject.X_Inject; import xapi.source.api.Lexer; import xapi.ui.html.X_Html; import xapi.ui.html.impl.StyleServiceDefault; import xapi.util.api.ConvertsValue; import com.google.gwt.core.shared.GwtIncompatible; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import javax.inject.Provider; /** * @author "James X. Nelson (james@wetheinter.net)" */ @SingletonDefault(implFor=ElementalService.class) public class ElementalServiceDefault extends StyleServiceDefault<ElementalService> implements ElementalService { private static final Provider<StyleCacheService> styleCache = X_Inject.singletonLazy(StyleCacheService.class); private Lexer lexer; private static final TextAreaElement escaper = Browser.getDocument().createTextAreaElement(); @Override public <T, E extends Element> E toElement(Class<? super T> cls, T obj) { return this.<T, E>toElementBuilder(cls).convert(obj).getElement(); } @Override public <T, E extends Element> E toElement(Class<? super T> cls, Class<?> template, T obj) { return this.<T, E>toElementBuilder(cls, template).convert(obj).getElement(); } public <T, E extends Element> ConvertsValue<T, PotentialNode<E>> toElementBuilder(final Class<? super T> cls) { return toElementBuilder(cls, cls); } static class UnsupportedConverter <T, E> implements ConvertsValue<T, E> { @Override public E convert(T from) { throw new UnsupportedOperationException(); } } @Override public <T, E extends Element> ConvertsValue<T, PotentialNode<E>> toElementBuilder(final Class<? super T> cls, Class<?> template) { return new UnsupportedConverter<T, PotentialNode<E>>() { @SuppressWarnings({ "unchecked", "rawtypes" } ) @Override @GwtIncompatible public PotentialNode<E> convert(T from) { Class c = cls;// Erase generics; gwt doesn't like them much return new PotentialNode<E>(X_Html.toHtml(template, c, from, ElementalServiceDefault.this)); } }; } @Override public String enhanceMarkup(String markup) { if (lexer == null) { return markup; } try { return lexer.lex(markup).toString(); } finally { lexer.clear(); } } /** * @return the lexer */ public Lexer getLexer() { return lexer; } /** * @param lexer the lexer to set */ @Override public void setLexer(Lexer lexer) { this.lexer = lexer; } @Override public <E extends Element> PotentialNode<E> newNode(String tagname) { return new PotentialNode<E>(tagname); } @Override public <E extends Element> PotentialNode<E> newNode(E node) { return new PotentialNode<E>(node); } @Override public <E extends Element> PotentialNode<E> newNode() { return new PotentialNode<E>(); } @Override public <E extends Element> E initialize(E element) { return element; } @Override public <E extends Element> ConvertsValue<E, E> asConverter() { return new ConvertsValue<E, E>() { @Override public E convert(E from) { return initialize(from); } }; } @Override public native Element getShadowRoot(Element element) /*-{ if (element.shadowRoot) { return element.shadowRoot; } if (element.createShadowRoot) { return element.createShadowRoot(); } return element; }-*/; @Override public StyleElement injectStyle( Class<? extends ClientBundle> bundle, Class<? extends CssResource> ... styles ) { return styleCache.get().injectStyle(bundle, styles); } @Override public ElementalService registerStyle( Class<? extends ClientBundle> bundle, String css, Class<? extends CssResource>... styles ) { styleCache.get().registerStyle(bundle, css, styles); return this; } @Override public String escapeHTML(String html) { escaper.setInnerText(html); return escaper.getInnerHTML(); } @Override public String unescapeHTML(String html) { // extremely fast, and appears to be secure, // as both < and < result in < character being output escaper.setInnerHTML(html); return escaper.getTextContent(); } /** From angular JS: (use an equivalent of this for jvm elemental service SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, // Match everything outside of normal chars and " (quote character) NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; function decodeEntities(value) { if (!value) { return ''; } hiddenPre.innerHTML = value.replace(/</g,"<"); // innerText depends on styling as it doesn't display hidden elements. // Therefore, it's better to use textContent not to cause unnecessary reflows. return hiddenPre.textContent; } function encodeEntities(value) { return value. replace(/&/g, '&'). replace(SURROGATE_PAIR_REGEXP, function(value) { var hi = value.charCodeAt(0); var low = value.charCodeAt(1); return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'; }). replace(NON_ALPHANUMERIC_REGEXP, function(value) { return '&#' + value.charCodeAt(0) + ';'; }). replace(/</g, '<'). replace(/>/g, '>'); } * */ }