diff --git a/pom.xml b/pom.xml index cb91e354c..3c262e655 100644 --- a/pom.xml +++ b/pom.xml @@ -76,6 +76,7 @@ teavm-dom teavm-jso teavm-html4j + teavm-samples diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java index 57d446888..56b0803ea 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -15,6 +15,7 @@ */ package org.teavm.classlib.impl; +import org.teavm.classlib.impl.unicode.CLDRHelper; import org.teavm.classlib.java.util.LocaleSettingsNativeGenerator; import org.teavm.javascript.ni.Generator; import org.teavm.model.MethodDescriptor; @@ -47,7 +48,10 @@ public class JCLPlugin implements TeaVMPlugin { host.add(new MethodReference("java.util.Locale", "readLanguagesFromCLDR", ValueType.VOID), localeGen); host.add(new MethodReference("java.util.Locale", "readCountriesFromCLDR", ValueType.VOID), localeGen); host.add(new MethodReference("java.util.Calendar", "readWeeksFromCLDR", ValueType.VOID), localeGen); + host.add(new MethodReference(CLDRHelper.class.getName(), "readLikelySubtagsFromCLDR", ValueType.VOID), + localeGen); host.add(new MethodReference("java.util.Locale", "getDefaultLocale", ValueType.object("java.lang.String")), localeGen); + host.add(new MethodReference("java.util.Locale", "readAvailableLocales", ValueType.VOID), localeGen); } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java index 63dcf7c29..c68a9d690 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java @@ -15,6 +15,9 @@ */ package org.teavm.classlib.impl.unicode; +import org.teavm.dependency.PluggableDependency; +import org.teavm.javascript.ni.GeneratedBy; + /** * * @author Alexey Andreev @@ -33,6 +36,7 @@ public class CLDRHelper { // Defined by JCLPlugin private static native void readLikelySubtagsFromCLDR(); - // TODO: implement using CLDR + @GeneratedBy(CLDRHelperNativeGenerator.class) + @PluggableDependency(CLDRHelperNativeGenerator.class) private static native String getLikelySubtagsImpl(String localeCode); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java index e52f47b24..9e7ab52a3 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleNativeGenerator.java @@ -56,7 +56,8 @@ public class LocaleNativeGenerator implements Generator, DependencyPlugin { } private void generateAvailableLocales(SourceWriter writer) throws IOException { - writer.append("var locales = Object.keys(").appendClass("java.util.Locale").append(".$CLDR);").softNewLine(); + writer.append("var locales = ").appendClass("java.util.Locale").append(".$CLDR.availableLocales;") + .softNewLine(); writer.append("var array = $rt_createArray(").appendClass("java.lang.String").append(", locales);") .softNewLine(); writer.append("for (var i = 0; i < locales.length; ++i) {").indent().softNewLine(); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java index 6a5c3b664..fba284a24 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java @@ -40,6 +40,7 @@ public class LocaleSettingsNativeGenerator implements Generator { private Map knownLocales = new LinkedHashMap<>(); private Map minDaysMap = new LinkedHashMap<>(); private Map firstDayMap = new LinkedHashMap<>(); + private Map likelySubtags = new LinkedHashMap<>(); private Set availableLocales = new LinkedHashSet<>(); private Set availableLanguages = new LinkedHashSet<>(); private Set availableCountries = new LinkedHashSet<>(); @@ -60,7 +61,7 @@ public class LocaleSettingsNativeGenerator implements Generator { private void findAvailableLocales() { String availableLocalesString = properties.getProperty("java.util.Locale.available", "en_EN").trim(); - for (String locale : Arrays.asList(availableLocalesString.split(" *, +"))) { + for (String locale : Arrays.asList(availableLocalesString.split(" *, *"))) { int countryIndex = locale.indexOf('_'); if (countryIndex > 0) { String language = locale.substring(0, countryIndex); @@ -90,6 +91,8 @@ public class LocaleSettingsNativeGenerator implements Generator { if (entry.getName().equals("supplemental/weekData.json")) { readWeekData(input); continue; + } else if (entry.getName().equals("supplemental/likelySubtags.json")) { + readLikelySubtags(input); } int objectIndex = entry.getName().lastIndexOf('/'); String objectName = entry.getName().substring(objectIndex + 1); @@ -156,6 +159,15 @@ public class LocaleSettingsNativeGenerator implements Generator { } } + private void readLikelySubtags(InputStream input) { + JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input)); + JsonObject likelySubtagsJson = root.get("supplemental").getAsJsonObject().get("likelySubtags") + .getAsJsonObject(); + for (Map.Entry property : likelySubtagsJson.entrySet()) { + likelySubtags.put(property.getKey(), property.getValue().getAsString()); + } + } + private int getNumericDay(String day) { switch (day) { case "sun": @@ -190,6 +202,12 @@ public class LocaleSettingsNativeGenerator implements Generator { case "readWeeksFromCLDR": generateReadWeeksFromCDLR(writer); break; + case "readLikelySubtagsFromCLDR": + generateReadLikelySubtagsFromCLDR(writer); + break; + case "readAvailableLocales": + generateReadAvailableLocales(writer); + break; case "getDefaultLocale": generateGetDefaultLocale(writer); break; @@ -203,6 +221,20 @@ public class LocaleSettingsNativeGenerator implements Generator { writer.outdent().append("}").softNewLine(); } + private void generateReadAvailableLocales(SourceWriter writer) throws IOException { + generateDefender(writer, "availableLocales"); + writer.appendClass("java.util.Locale").append(".$CLDR.availableLocales = ["); + boolean first = true; + for (String locale : availableLocales) { + if (!first) { + writer.append(',').ws(); + } + first = false; + writer.append('"').append(Renderer.escapeString(locale)).append('"'); + } + writer.append("];").softNewLine(); + } + private void generateReadLanguagesFromCLDR(SourceWriter writer) throws IOException { generateDefender(writer, "languages"); writer.appendClass("java.util.Locale").append(".$CLDR.languages = {").indent().softNewLine(); @@ -226,7 +258,7 @@ public class LocaleSettingsNativeGenerator implements Generator { } writer.outdent().append('}'); } - writer.outdent().append("}").softNewLine(); + writer.outdent().append("};").softNewLine(); } private void generateReadCountriesFromCLDR(SourceWriter writer) throws IOException { @@ -253,34 +285,49 @@ public class LocaleSettingsNativeGenerator implements Generator { writer.outdent().append('}'); } - writer.outdent().append("}").softNewLine(); + writer.outdent().append("};").softNewLine(); } private void generateReadWeeksFromCDLR(SourceWriter writer) throws IOException { generateDefender(writer, "minDays"); writer.appendClass("java.util.Locale").append(".$CLDR.minDays = {").indent().softNewLine(); - boolean firstLocale = true; + boolean first = true; for (Map.Entry entry : minDaysMap.entrySet()) { - if (!firstLocale) { + if (!first) { writer.append(",").softNewLine(); } - firstLocale = false; + first = false; writer.append('"').append(Renderer.escapeString(entry.getKey())).append('"').ws().append(':') - .ws().append('"').append(entry.getValue()).append('"'); + .ws().append(entry.getValue()); } - writer.outdent().append("}").softNewLine(); + writer.outdent().append("};").softNewLine(); writer.appendClass("java.util.Locale").append(".$CLDR.firstDay = {").indent().softNewLine(); - firstLocale = true; + first = true; for (Map.Entry entry : firstDayMap.entrySet()) { - if (!firstLocale) { + if (!first) { writer.append(",").softNewLine(); } - firstLocale = false; + first = false; writer.append('"').append(Renderer.escapeString(entry.getKey())).append('"').ws().append(':') .ws().append('"').append(entry.getValue()).append('"'); } - writer.outdent().append("}").softNewLine(); + writer.outdent().append("};").softNewLine(); + } + + private void generateReadLikelySubtagsFromCLDR(SourceWriter writer) throws IOException { + generateDefender(writer, "likelySubtags"); + writer.append("java.util.Locale").append(".$CLDR.likelySubtags = {").indent().softNewLine(); + boolean first = true; + for (Map.Entry entry : likelySubtags.entrySet()) { + if (!first) { + writer.append(",").softNewLine(); + } + first = false; + writer.append('"').append(Renderer.escapeString(entry.getKey())).append('"').ws().append(':') + .ws().append('"').append(Renderer.escapeString(entry.getValue())).append('"'); + } + writer.outdent().append("};").softNewLine(); } private void generateGetDefaultLocale(SourceWriter writer) throws IOException { diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java index 17ef12eaa..07c5365ac 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TLocale.java @@ -88,6 +88,9 @@ public final class TLocale implements TCloneable, TSerializable { // Redefined by JCLPlugin private static native void readLanguagesFromCLDR(); + // Redefined by JCLPlugin + private static native void readAvailableLocales(); + public TLocale(String language) { this(language, "", ""); } @@ -134,6 +137,7 @@ public final class TLocale implements TCloneable, TSerializable { } public static TLocale[] getAvailableLocales() { + readAvailableLocales(); if (availableLocales == null) { String[] strings = getAvailableLocaleStrings(); availableLocales = new TLocale[strings.length]; @@ -142,7 +146,7 @@ public final class TLocale implements TCloneable, TSerializable { int countryIndex = string.indexOf('-'); if (countryIndex > 0) { availableLocales[i] = new TLocale(string.substring(0, countryIndex), - string.substring(countryIndex)); + string.substring(countryIndex + 1)); } else { availableLocales[i] = new TLocale(string); } diff --git a/teavm-dom/src/main/java/org/teavm/dom/events/EventTarget.java b/teavm-dom/src/main/java/org/teavm/dom/events/EventTarget.java index a9d833adf..0a6e229cf 100644 --- a/teavm-dom/src/main/java/org/teavm/dom/events/EventTarget.java +++ b/teavm-dom/src/main/java/org/teavm/dom/events/EventTarget.java @@ -24,7 +24,11 @@ import org.teavm.jso.JSObject; public interface EventTarget extends JSObject { void addEventListener(String type, EventListener listener, boolean useCapture); + void addEventListener(String type, EventListener listener); + void removeEventListener(String type, EventListener listener, boolean useCapture); + void removeEventListener(String type, EventListener listener); + boolean dispatchEvent(Event evt); } diff --git a/teavm-dom/src/main/java/org/teavm/dom/html/HTMLCollection.java b/teavm-dom/src/main/java/org/teavm/dom/html/HTMLCollection.java new file mode 100644 index 000000000..65caf1490 --- /dev/null +++ b/teavm-dom/src/main/java/org/teavm/dom/html/HTMLCollection.java @@ -0,0 +1,29 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.dom.html; + +import org.teavm.dom.core.Element; +import org.teavm.jso.JSArrayReader; + +/** + * + * @author Alexey Andreev + */ +public interface HTMLCollection extends JSArrayReader { + Element item(int index); + + Element namedItem(String name); +} diff --git a/teavm-dom/src/main/java/org/teavm/dom/html/HTMLInputElement.java b/teavm-dom/src/main/java/org/teavm/dom/html/HTMLInputElement.java new file mode 100644 index 000000000..bfe5798f0 --- /dev/null +++ b/teavm-dom/src/main/java/org/teavm/dom/html/HTMLInputElement.java @@ -0,0 +1,72 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.dom.html; + +import org.teavm.jso.JSProperty; + +/** + * + * @author Alexey Andreev + */ +public interface HTMLInputElement extends HTMLElement { + @JSProperty + boolean isChecked(); + + @JSProperty + void setChecked(boolean checked); + + @JSProperty + boolean isDisabled(); + + @JSProperty + void setDisabled(boolean disabled); + + @JSProperty + int getMaxLength(); + + @JSProperty + void setMaxLength(int maxLength); + + @JSProperty + String getName(); + + @JSProperty + void setName(String name); + + @JSProperty + boolean isReadOnly(); + + @JSProperty + void setReadOnly(boolean readOnly); + + @JSProperty + int getSize(); + + @JSProperty + void setSize(int size); + + @JSProperty + String getType(); + + @JSProperty + void setType(String type); + + @JSProperty + String getValue(); + + @JSProperty + void setValue(String value); +} diff --git a/teavm-dom/src/main/java/org/teavm/dom/html/HTMLOptionElement.java b/teavm-dom/src/main/java/org/teavm/dom/html/HTMLOptionElement.java new file mode 100644 index 000000000..16f60946b --- /dev/null +++ b/teavm-dom/src/main/java/org/teavm/dom/html/HTMLOptionElement.java @@ -0,0 +1,63 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.dom.html; + +import org.teavm.jso.JSProperty; + +/** + * + * @author Alexey Andreev + */ +public interface HTMLOptionElement extends HTMLElement { + @JSProperty + boolean isDisabled(); + + @JSProperty + void setDisabled(boolean disabled); + + @JSProperty + String getLabel(); + + @JSProperty + void setLabel(String label); + + @JSProperty + boolean isDefaultSelected(); + + @JSProperty + void setDefaultSelected(boolean defaultSelected); + + @JSProperty + boolean isSelected(); + + @JSProperty + void setSelected(boolean selected); + + @JSProperty + String getValue(); + + @JSProperty + void setValue(String value); + + @JSProperty + String getText(); + + @JSProperty + void setText(String text); + + @JSProperty + int getIndex(); +} diff --git a/teavm-dom/src/main/java/org/teavm/dom/html/HTMLOptionsCollection.java b/teavm-dom/src/main/java/org/teavm/dom/html/HTMLOptionsCollection.java new file mode 100644 index 000000000..f94d87e73 --- /dev/null +++ b/teavm-dom/src/main/java/org/teavm/dom/html/HTMLOptionsCollection.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.dom.html; + +import org.teavm.jso.JSIndexer; +import org.teavm.jso.JSProperty; + +/** + * + * @author Alexey Andreev + */ +public interface HTMLOptionsCollection extends HTMLCollection { + @Override + HTMLOptionElement item(int index); + + @Override + HTMLOptionElement namedItem(String name); + + @JSIndexer + void set(int index, HTMLOptionElement element); + + void add(HTMLOptionElement element, HTMLElement before); + + void add(HTMLOptionElement element, int before); + + void add(HTMLOptionElement element); + + void remove(int index); + + @JSProperty + int getSelectedIndex(); + + @JSProperty + void setSelectedIndex(int selectedIndex); +} diff --git a/teavm-dom/src/main/java/org/teavm/dom/html/HTMLSelectElement.java b/teavm-dom/src/main/java/org/teavm/dom/html/HTMLSelectElement.java new file mode 100644 index 000000000..9821049e2 --- /dev/null +++ b/teavm-dom/src/main/java/org/teavm/dom/html/HTMLSelectElement.java @@ -0,0 +1,63 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.dom.html; + +import org.teavm.jso.JSProperty; + +/** + * + * @author Alexey Andreev + */ +public interface HTMLSelectElement extends HTMLElement { + @JSProperty + boolean isDisabled(); + + @JSProperty + void setDisabled(boolean disabled); + + @JSProperty + boolean isMultiple(); + + @JSProperty + void setMultiple(boolean multiple); + + @JSProperty + HTMLOptionsCollection getOptions(); + + @JSProperty + String getName(); + + @JSProperty + void setName(String name); + + @JSProperty + int getSize(); + + @JSProperty + void setSize(int size); + + @JSProperty + int getSelectedIndex(); + + @JSProperty + void setSelectedIndex(int selectedIndex); + + @JSProperty + String getValue(); + + @JSProperty + void setValue(String value); +} diff --git a/teavm-samples/pom.xml b/teavm-samples/pom.xml index 4a5306614..1707768f3 100644 --- a/teavm-samples/pom.xml +++ b/teavm-samples/pom.xml @@ -76,6 +76,39 @@ ${project.build.directory}/javascript/matrix + + generate-datetime + + build-javascript + + process-classes + + false + org.teavm.samples.DateTime + ${project.build.directory}/javascript/datetime + + en_US,en_GB,ru_RU,ru_UA,nl_NL,nl_BE + + + + + + + maven-assembly-plugin + 2.4 + + + src/main/assembly/teavm-demos.xml + + + + + make-assembly + package + + single + + diff --git a/teavm-samples/src/main/assembly/teavm-demos.xml b/teavm-samples/src/main/assembly/teavm-demos.xml new file mode 100644 index 000000000..bb618e19a --- /dev/null +++ b/teavm-samples/src/main/assembly/teavm-demos.xml @@ -0,0 +1,24 @@ + + teavm-demos + + zip + + + + src/main/resources/datetime.html + / + + + ${project.build.directory}/javascript/datetime/classes.js + / + datetime.js + + + ${project.build.directory}/javascript/datetime/runtime.js + / + + + \ No newline at end of file diff --git a/teavm-samples/src/main/java/org/teavm/samples/DateTime.java b/teavm-samples/src/main/java/org/teavm/samples/DateTime.java new file mode 100644 index 000000000..e6bf055a0 --- /dev/null +++ b/teavm-samples/src/main/java/org/teavm/samples/DateTime.java @@ -0,0 +1,77 @@ +/* + * Copyright 2014 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.samples; + +import java.util.Date; +import java.util.Locale; +import org.teavm.dom.browser.TimerHandler; +import org.teavm.dom.browser.Window; +import org.teavm.dom.events.Event; +import org.teavm.dom.events.EventListener; +import org.teavm.dom.html.HTMLDocument; +import org.teavm.dom.html.HTMLInputElement; +import org.teavm.dom.html.HTMLOptionElement; +import org.teavm.dom.html.HTMLSelectElement; +import org.teavm.jso.JS; + +/** + * + * @author Alexey Andreev + */ +public class DateTime { + private static Window window = (Window)JS.getGlobal(); + private static HTMLDocument document = window.getDocument(); + private static Date currentDate; + + public static void main(String[] args) { + fillLocales(); + window.setInterval(new TimerHandler() { + @Override + public void onTimer() { + updateCurrentTime(); + } + }, 250); + } + + private static void fillLocales() { + final HTMLSelectElement localeElem = (HTMLSelectElement)document.getElementById("locale"); + for (Locale locale : Locale.getAvailableLocales()) { + HTMLOptionElement option = (HTMLOptionElement)document.createElement("option"); + option.setValue(locale.toString()); + option.setLabel(locale.getDisplayName(Locale.getDefault())); + localeElem.getOptions().add(option); + } + localeElem.addEventListener("change", new EventListener() { + @Override public void handleEvent(Event evt) { + // Don't do anything + } + }); + } + + private static void updateCurrentTime() { + setCurrentTime(new Date()); + } + + private static void setCurrentTime(Date date) { + currentDate = date; + updateCurrentTimeText(); + } + + private static void updateCurrentTimeText() { + HTMLInputElement timeElem = (HTMLInputElement)document.getElementById("current-time"); + timeElem.setValue(currentDate.toString()); + } +} diff --git a/teavm-samples/src/main/resources/datetime.html b/teavm-samples/src/main/resources/datetime.html new file mode 100644 index 000000000..4fb748678 --- /dev/null +++ b/teavm-samples/src/main/resources/datetime.html @@ -0,0 +1,42 @@ + + + + Localized date & time demo application + + + + + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + \ No newline at end of file