First real commit, mostly functional.
Showing
11 changed files
with
535 additions
and
0 deletions
.gitignore
0 → 100644
build.xml
0 → 100644
1 | <?xml version="1.0"?> | ||
2 | <!-- | ||
3 | Licensed to the Apache Software Foundation (ASF) under one | ||
4 | or more contributor license agreements. See the NOTICE file | ||
5 | distributed with this work for additional information | ||
6 | regarding copyright ownership. The ASF licenses this file | ||
7 | to you under the Apache License, Version 2.0 (the | ||
8 | "License"); you may not use this file except in compliance | ||
9 | with the License. You may obtain a copy of the License at | ||
10 | |||
11 | http://www.apache.org/licenses/LICENSE-2.0 | ||
12 | |||
13 | Unless required by applicable law or agreed to in writing, | ||
14 | software distributed under the License is distributed on an | ||
15 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
16 | KIND, either express or implied. See the License for the | ||
17 | specific language governing permissions and limitations | ||
18 | under the License. | ||
19 | --> | ||
20 | |||
21 | <project name="ofbiz-rhino" default="jar" basedir="."> | ||
22 | <first id="possible.ofbiz.home.common.xml"> | ||
23 | <resources> | ||
24 | <fileset dir=".."> | ||
25 | <include name="ofbiz/common.xml"/> | ||
26 | </fileset> | ||
27 | <fileset dir="/"> | ||
28 | <include name="srv/ofbiz-upstream/common.xml"/> | ||
29 | <include name="opt/ofbiz-upstream/common.xml"/> | ||
30 | <include name="job/ofbiz-upstream/common.xml"/> | ||
31 | </fileset> | ||
32 | </resources> | ||
33 | </first> | ||
34 | <dirname property="possible.ofbiz.home.dir" file="${toString:possible.ofbiz.home.common.xml}"/> | ||
35 | <property name="ofbiz.home.dir" value="${possible.ofbiz.home.dir}"/> | ||
36 | <import file="${ofbiz.home.dir}/common.xml"/> | ||
37 | |||
38 | <!-- ================================================================== --> | ||
39 | <!-- Initialization of all property settings --> | ||
40 | <!-- ================================================================== --> | ||
41 | |||
42 | <property name="desc" value="ofbiz-rhino"/> | ||
43 | <property name="name" value="ofbiz-rhino"/> | ||
44 | |||
45 | <path id="local.class.path"> | ||
46 | <fileset dir="lib" includes="*.jar"/> | ||
47 | <fileset dir="${ofbiz.home.dir}/framework/base/lib" includes="*.jar"/> | ||
48 | <fileset dir="${ofbiz.home.dir}/framework/base/lib/commons" includes="*.jar"/> | ||
49 | <fileset dir="${ofbiz.home.dir}/framework/base/lib/scripting" includes="*.jar"/> | ||
50 | <fileset dir="${ofbiz.home.dir}/framework/base/lib/j2eespecs" includes="*.jar"/> | ||
51 | <fileset dir="${ofbiz.home.dir}/framework/base/build/lib" includes="*.jar"/> | ||
52 | |||
53 | </path> | ||
54 | </project> |
lib/js.jar
0 → 100644
No preview for this file type
lib/r.js
0 → 100755
This diff could not be displayed because it is too large.
ofbiz-component.xml
0 → 100644
1 | <?xml version="1.0" encoding="UTF-8"?> | ||
2 | <ofbiz-component name="ofbiz-rhino" | ||
3 | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
4 | xsi:noNamespaceSchemaLocation="http://www.ofbiz.org/dtds/ofbiz-component.xsd"> | ||
5 | <resource-loader name="main" type="component"/> | ||
6 | <classpath type="jar" location="lib/*"/> | ||
7 | <classpath type="jar" location="build/lib/*"/> | ||
8 | |||
9 | </ofbiz-component> | ||
10 |
1 | package com.brainfood.ofbiz.rhino; | ||
2 | |||
3 | import java.io.File; | ||
4 | import java.io.FileNotFoundException; | ||
5 | import java.io.FileReader; | ||
6 | import java.io.IOException; | ||
7 | import java.util.Arrays; | ||
8 | import java.util.HashMap; | ||
9 | import java.util.Map; | ||
10 | |||
11 | import com.brainfood.rhino.Console; | ||
12 | import com.brainfood.rhino.HelperObjectProvider; | ||
13 | import com.brainfood.rhino.InContext; | ||
14 | import com.brainfood.rhino.Require; | ||
15 | import com.brainfood.rhino.RhinoContainer; | ||
16 | |||
17 | import org.mozilla.javascript.Context; | ||
18 | import org.mozilla.javascript.Function; | ||
19 | import org.mozilla.javascript.NativeObject; | ||
20 | import org.mozilla.javascript.Scriptable; | ||
21 | import org.mozilla.javascript.ScriptableObject; | ||
22 | |||
23 | import org.ofbiz.base.location.FlexibleLocation; | ||
24 | import org.ofbiz.base.util.Debug; | ||
25 | import org.ofbiz.base.util.UtilValidate; | ||
26 | import org.ofbiz.base.util.cache.UtilCache; | ||
27 | |||
28 | public class OfbizRhinoContainer extends RhinoContainer<OfbizRhinoContainer> { | ||
29 | public static final String module = OfbizRhinoContainer.class.getName(); | ||
30 | private final File base; | ||
31 | |||
32 | public OfbizRhinoContainer(File base) { | ||
33 | super(new OfbizHelperObjectProvider()); | ||
34 | this.base = base; | ||
35 | } | ||
36 | |||
37 | public static class OfbizHelperObjectProvider implements HelperObjectProvider<OfbizRhinoContainer> { | ||
38 | public Console newConsole(OfbizRhinoContainer container) { | ||
39 | return new OfbizConsole(container); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | public static class OfbizConsole extends Console { | ||
44 | public OfbizConsole(OfbizRhinoContainer container) { | ||
45 | super(container); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public String getClassName() { | ||
50 | return "OfbizConsole"; | ||
51 | } | ||
52 | |||
53 | public void log(Context cx, Object[] args, Function funObj) { | ||
54 | Object[] sargs = Console.getSArgs(args); | ||
55 | StringBuilder format = new StringBuilder(); | ||
56 | for (int i = 0; i < args.length; i++) { | ||
57 | if (i != 0) { | ||
58 | format.append(", "); | ||
59 | } | ||
60 | format.append("%s"); | ||
61 | } | ||
62 | Debug.logInfo(format.toString(), module, sargs); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | public void print(Context cx, Scriptable thisObj, Object[] args, Function funObj) { | ||
67 | Debug.logInfo("print:%s", module, Arrays.asList(args)); | ||
68 | } | ||
69 | |||
70 | public void processSource(Context cx, Scriptable thisObj, String filename) throws FileNotFoundException, IOException { | ||
71 | while (filename.length() > 0 && filename.charAt(0) == '/') { | ||
72 | filename = filename.substring(1); | ||
73 | } | ||
74 | cx.evaluateReader(thisObj, new FileReader(new File(base, filename)), filename, 1, null); | ||
75 | } | ||
76 | |||
77 | private static final UtilCache<String, Require> cache = UtilCache.getOrCreateUtilCache("OfbizRhinoContainer.Require", 0, 5, 10000, false, false); | ||
78 | |||
79 | public static Require getOrCreateRequire(String appLocation, String configPath, String... deps) throws Exception { | ||
80 | Require require = cache.get(appLocation); | ||
81 | if (require == null) { | ||
82 | File base = new File(FlexibleLocation.resolveLocation(appLocation).getPath()); | ||
83 | OfbizRhinoContainer container = new OfbizRhinoContainer(base); | ||
84 | File rLocation = new File(FlexibleLocation.resolveLocation("component://ofbiz-rhino/lib/r.js").getPath()); | ||
85 | require = new Require(container, rLocation, configPath); | ||
86 | for (String dep: deps) { | ||
87 | require.require(dep); | ||
88 | } | ||
89 | require = cache.putIfAbsentAndGet(appLocation, require); | ||
90 | } | ||
91 | return require; | ||
92 | } | ||
93 | } |
src/com/brainfood/rhino/Console.java
0 → 100644
1 | package com.brainfood.rhino; | ||
2 | |||
3 | import org.mozilla.javascript.Context; | ||
4 | import org.mozilla.javascript.Function; | ||
5 | import org.mozilla.javascript.Scriptable; | ||
6 | import org.mozilla.javascript.ScriptableObject; | ||
7 | |||
8 | public abstract class Console<C extends RhinoContainer> extends ScriptableObject { | ||
9 | public static final String module = Console.class.getName(); | ||
10 | |||
11 | private final C container; | ||
12 | |||
13 | public Console(C container) { | ||
14 | this.container = container; | ||
15 | this.defineFunctionProperties(new String[] {"log"}, Interface.class, ScriptableObject.DONTENUM); | ||
16 | } | ||
17 | |||
18 | public C getContainer() { | ||
19 | return container; | ||
20 | } | ||
21 | |||
22 | public static Object[] getSArgs(Object[] args) { | ||
23 | Object[] sargs = new Object[args.length]; | ||
24 | for (int i = 0; i < args.length; i++) { | ||
25 | sargs[i] = Context.toString(args[i]); | ||
26 | } | ||
27 | return sargs; | ||
28 | } | ||
29 | |||
30 | public static class Interface { | ||
31 | public static void log(Context cx, Scriptable thisObj, Object[] args, Function funObj) { | ||
32 | ((Console) thisObj).log(cx, args, funObj); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | public abstract void log(Context cx, Object[] args, Function funObj); | ||
37 | } |
src/com/brainfood/rhino/InContext.java
0 → 100644
1 | package com.brainfood.rhino; | ||
2 | |||
3 | import org.mozilla.javascript.Context; | ||
4 | import org.mozilla.javascript.Scriptable; | ||
5 | import org.mozilla.javascript.ScriptableObject; | ||
6 | import org.mozilla.javascript.WrapFactory; | ||
7 | |||
8 | public abstract class InContext<T> extends ScriptableObject { | ||
9 | public abstract T run(Context cx, RhinoContainer<?> container) throws Exception; | ||
10 | } |
src/com/brainfood/rhino/Require.java
0 → 100644
1 | package com.brainfood.rhino; | ||
2 | |||
3 | import java.io.File; | ||
4 | import java.io.FileNotFoundException; | ||
5 | import java.io.FileReader; | ||
6 | import java.io.IOException; | ||
7 | import java.util.Arrays; | ||
8 | import java.util.HashMap; | ||
9 | import java.util.Map; | ||
10 | |||
11 | import org.mozilla.javascript.Context; | ||
12 | import org.mozilla.javascript.Function; | ||
13 | import org.mozilla.javascript.NativeObject; | ||
14 | import org.mozilla.javascript.Scriptable; | ||
15 | import org.mozilla.javascript.ScriptableObject; | ||
16 | |||
17 | public class Require { | ||
18 | private final RhinoContainer<?> container; | ||
19 | private final Loader loader; | ||
20 | private final Function require; | ||
21 | |||
22 | public Require(RhinoContainer<?> container, File rLocation, String configPath) throws Exception { | ||
23 | this.container = container; | ||
24 | this.loader = new Loader(container, rLocation, configPath); | ||
25 | this.require = container.run(loader); | ||
26 | } | ||
27 | |||
28 | public RhinoContainer<?> getContainer() { | ||
29 | return container; | ||
30 | } | ||
31 | |||
32 | public <T> T require(final String path) throws Exception { | ||
33 | container.run(new InContext<Object>() { | ||
34 | @Override | ||
35 | public String getClassName() { | ||
36 | return "SetupRunner"; | ||
37 | } | ||
38 | |||
39 | public Object run(Context cx, RhinoContainer container) throws FileNotFoundException, IOException { | ||
40 | Object[] arguments = { | ||
41 | cx.newArray(loader, new Object[] {path}), | ||
42 | }; | ||
43 | return require.call(cx, loader, null, arguments); | ||
44 | } | ||
45 | }); | ||
46 | return container.run(new InContext<T>() { | ||
47 | @Override | ||
48 | public String getClassName() { | ||
49 | return "RequireRunner"; | ||
50 | } | ||
51 | |||
52 | public T run(Context cx, RhinoContainer container) throws FileNotFoundException, IOException { | ||
53 | return (T) require.call(cx, loader, null, new Object[] {path}); | ||
54 | } | ||
55 | }); | ||
56 | } | ||
57 | |||
58 | public Map<String, Object> validateWithBackbone(String moduleId, String preInitEval, Map<String, Object> data, Map<String, Object> extraOptions) throws Exception { | ||
59 | Function modelClass = require(moduleId); | ||
60 | Scriptable modelInstance = container.newObject(modelClass); | ||
61 | container.eval(loader, preInitEval, modelInstance); | ||
62 | Map<String, Object> setOptions = new HashMap<String, Object>(); | ||
63 | if (extraOptions != null) { | ||
64 | setOptions.putAll(extraOptions); | ||
65 | } | ||
66 | setOptions.put("validate", false); | ||
67 | container.call(modelInstance, "set", container.javaToJS(data), container.javaToJS(setOptions)); | ||
68 | NativeObject r = container.call(modelInstance, "toJSON", container.javaToJS(extraOptions)); | ||
69 | System.err.printf("r[%s]=%s\n", moduleId, container.toMap(r)); | ||
70 | NativeObject errors = RhinoContainer.allowUndefined(container.call(modelInstance, "validate")); | ||
71 | return new HashMap<String, Object>(errors); | ||
72 | } | ||
73 | |||
74 | public static class Loader extends InContext<Function> { | ||
75 | private final RhinoContainer<?> container; | ||
76 | private final File rLocation; | ||
77 | private final String configPath; | ||
78 | |||
79 | public Loader(RhinoContainer<?> container, File rLocation, String configPath) { | ||
80 | this.container = container; | ||
81 | this.rLocation = rLocation; | ||
82 | this.configPath = configPath; | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public String getClassName() { | ||
87 | return "Loader"; | ||
88 | } | ||
89 | |||
90 | public Function run(Context cx, RhinoContainer<?> container) throws FileNotFoundException, IOException { | ||
91 | container.configureTopScriptable(cx, this); | ||
92 | defineFunctionProperties(new String[] {"load", "print"}, Loader.class, ScriptableObject.DONTENUM); | ||
93 | |||
94 | Scriptable argsObj = cx.newArray(this, new Object[] {}); | ||
95 | defineProperty("arguments", argsObj, ScriptableObject.DONTENUM); | ||
96 | |||
97 | if (configPath != null && configPath.length() > 0) { | ||
98 | container.processSource(cx, this, configPath); | ||
99 | } | ||
100 | cx.evaluateReader(this, new FileReader(rLocation.getPath()), "r.js", 1, null); | ||
101 | return (Function) cx.evaluateString(this, "require", null, -1, null); | ||
102 | } | ||
103 | |||
104 | public static void print(Context cx, Scriptable thisObj, Object[] args, Function funObj) { | ||
105 | Loader loader = (Loader) getTopLevelScope(thisObj); | ||
106 | loader.container.print(cx, thisObj, args, funObj); | ||
107 | } | ||
108 | |||
109 | public static void load(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws FileNotFoundException, IOException { | ||
110 | Loader loader = (Loader) getTopLevelScope(thisObj); | ||
111 | for (int i = 0; i < args.length; i++) { | ||
112 | String path = Context.toString(args[i]); | ||
113 | if (path.endsWith("/jquery.js")) { | ||
114 | loader.container.print(cx, thisObj, new Object[] {"Skipping file " + path}, funObj); | ||
115 | continue; | ||
116 | } | ||
117 | loader.container.print(cx, thisObj, new Object[] {"Loading file " + path}, funObj); | ||
118 | loader.container.processSource(cx, thisObj, path); | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | } |
src/com/brainfood/rhino/RhinoContainer.java
0 → 100644
1 | package com.brainfood.rhino; | ||
2 | |||
3 | import java.io.IOException; | ||
4 | import java.io.FileNotFoundException; | ||
5 | import java.util.ArrayList; | ||
6 | import java.util.HashMap; | ||
7 | import java.util.Iterator; | ||
8 | import java.util.LinkedHashMap; | ||
9 | import java.util.List; | ||
10 | import java.util.Map; | ||
11 | import java.util.TreeMap; | ||
12 | |||
13 | import org.mozilla.javascript.Context; | ||
14 | import org.mozilla.javascript.ContextFactory; | ||
15 | import org.mozilla.javascript.Function; | ||
16 | import org.mozilla.javascript.FunctionObject; | ||
17 | import org.mozilla.javascript.NativeObject; | ||
18 | import org.mozilla.javascript.Scriptable; | ||
19 | import org.mozilla.javascript.ScriptableObject; | ||
20 | import org.mozilla.javascript.WrapFactory; | ||
21 | import org.mozilla.javascript.Undefined; | ||
22 | |||
23 | public abstract class RhinoContainer<C extends RhinoContainer<C>> extends ScriptableObject { | ||
24 | private final HelperObjectProvider<C> objectProvider; | ||
25 | private final Console console; | ||
26 | |||
27 | private final ContextFactory contextFactory = new ContextFactory(); | ||
28 | private final WrapFactory wrapFactory = new WrapFactory() { | ||
29 | public Scriptable wrapAsJavaObject(Context cx, Scriptable scope, Object javaObject, Class<?> staticType) { | ||
30 | System.err.printf("wrapAsJavaObject(%s, %s, %s, %s)\n", cx, scope, javaObject, staticType); | ||
31 | if (javaObject instanceof Map) { | ||
32 | NativeObject no = new NativeObject(); | ||
33 | for (Map.Entry<String, Object> entry: ((Map<String, Object>) javaObject).entrySet()) { | ||
34 | no.defineProperty(entry.getKey(), RhinoContainer.this.javaToJS(entry.getValue()), NativeObject.READONLY); | ||
35 | } | ||
36 | return no; | ||
37 | } | ||
38 | return super.wrapAsJavaObject(cx, scope, javaObject, staticType); | ||
39 | } | ||
40 | }; | ||
41 | |||
42 | public RhinoContainer(HelperObjectProvider<C> objectProvider) { | ||
43 | this.objectProvider = objectProvider; | ||
44 | console = objectProvider.newConsole((C) this); | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public String getClassName() { | ||
49 | return "RhinoContainer"; | ||
50 | } | ||
51 | |||
52 | public Context enterContext() { | ||
53 | Context cx = contextFactory.enterContext(); | ||
54 | cx.setWrapFactory(wrapFactory); | ||
55 | return cx; | ||
56 | } | ||
57 | |||
58 | public void configureTopScriptable(Context cx, ScriptableObject s) { | ||
59 | cx.initStandardObjects(s, false); | ||
60 | s.defineProperty("console", console, ScriptableObject.DONTENUM); | ||
61 | s.defineFunctionProperties(new String[] {"setTimeout"}, RhinoContainer.class, ScriptableObject.DONTENUM); | ||
62 | } | ||
63 | |||
64 | public <T> T eval(ScriptableObject s, String evalString, Object... args) { | ||
65 | Context cx = enterContext(); | ||
66 | try { | ||
67 | if (evalString != null && evalString.length() > 0) { | ||
68 | Function f = (Function) cx.evaluateString(s, "(function() {" + evalString + "});", null, -1, null); | ||
69 | return (T) f.call(cx, this, null, args); | ||
70 | } else { | ||
71 | return null; | ||
72 | } | ||
73 | } finally { | ||
74 | applyTimers(cx); | ||
75 | Context.exit(); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | public <T> T run(InContext<T> inContext) throws Exception { | ||
80 | Context cx = enterContext(); | ||
81 | try { | ||
82 | return inContext.run(cx, this); | ||
83 | } finally { | ||
84 | applyTimers(cx); | ||
85 | Context.exit(); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | public <T> T newObject(Function f, Object... args) { | ||
90 | Context cx = enterContext(); | ||
91 | try { | ||
92 | return (T) f.construct(cx, this, wrapArgs(args)); | ||
93 | } finally { | ||
94 | applyTimers(cx); | ||
95 | Context.exit(); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | public <T> T call(Scriptable s, String methodName, Object... args) { | ||
100 | Context cx = enterContext(); | ||
101 | try { | ||
102 | return (T) ScriptableObject.callMethod(cx, s, methodName, wrapArgs(args)); | ||
103 | } finally { | ||
104 | applyTimers(cx); | ||
105 | Context.exit(); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | public Object[] wrapArgs(Object... args) { | ||
110 | Object[] result = new Object[args.length]; | ||
111 | for (int i = 0; i < result.length; i++) { | ||
112 | result[i] = javaToJS(args[i]); | ||
113 | } | ||
114 | return result; | ||
115 | } | ||
116 | |||
117 | public Object javaToJS(Object value) { | ||
118 | Context cx = enterContext(); | ||
119 | try { | ||
120 | return Context.javaToJS(value, this); | ||
121 | } finally { | ||
122 | applyTimers(cx); | ||
123 | Context.exit(); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | public Object jsToJava(Object value) { | ||
128 | Context cx = enterContext(); | ||
129 | try { | ||
130 | return Context.jsToJava(value, null); | ||
131 | } finally { | ||
132 | applyTimers(cx); | ||
133 | Context.exit(); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | public Map<String, Object> toMap(Object value) { | ||
138 | value = allowUndefined(value); | ||
139 | if (value == null) { | ||
140 | return null; | ||
141 | } | ||
142 | Map<String, Object> result = new LinkedHashMap(); | ||
143 | for (Map.Entry<Object, Object> entry: ((NativeObject) value).entrySet()) { | ||
144 | Object subValue = entry.getValue(); | ||
145 | if (subValue instanceof NativeObject) { | ||
146 | subValue = toMap(subValue); | ||
147 | } | ||
148 | result.put((String) entry.getKey(), subValue); | ||
149 | } | ||
150 | return result; | ||
151 | } | ||
152 | |||
153 | private void applyTimers(Context cx) { | ||
154 | Map<Long, List<Function>> timers = (Map<Long, List<Function>>) cx.getThreadLocal(RhinoContainer.class.getName() +":timers"); | ||
155 | if (timers == null) { | ||
156 | return; | ||
157 | } | ||
158 | long currentTime = System.currentTimeMillis(); | ||
159 | |||
160 | while (!timers.isEmpty()) { | ||
161 | Iterator<Map.Entry<Long, List<Function>>> it = timers.entrySet().iterator(); | ||
162 | Map.Entry<Long, List<Function>> entry = it.next(); | ||
163 | it.remove(); | ||
164 | Long delay = entry.getKey(); | ||
165 | List<Function> functions = entry.getValue(); | ||
166 | if (delay != 0) { | ||
167 | System.err.printf("Unhandled delay: %s -> %s\n", delay, functions); | ||
168 | } | ||
169 | while (!functions.isEmpty()) { | ||
170 | Function function = functions.remove(0); | ||
171 | function.call(cx, this, null, new Object[0]); | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | public abstract void print(Context cx, Scriptable thisObj, Object[] args, Function funObj); | ||
177 | public abstract void processSource(Context cx, Scriptable thisObj, String filename) throws FileNotFoundException, IOException; | ||
178 | |||
179 | public static void setTimeout(Context cx, Scriptable thisObj, Object[] args, Function funObj) { | ||
180 | Function toCall = (Function) args[0]; | ||
181 | Long delay = args.length > 1 ? ((Number) args[1]).longValue() : 0; | ||
182 | Map<Long, List<Function>> timers = (Map<Long, List<Function>>) cx.getThreadLocal(RhinoContainer.class.getName() +":timers"); | ||
183 | if (timers == null) { | ||
184 | timers = new TreeMap<Long, List<Function>>(); | ||
185 | cx.putThreadLocal(RhinoContainer.class.getName() +":timers", timers); | ||
186 | } | ||
187 | List<Function> functions = timers.get(delay); | ||
188 | if (functions == null) { | ||
189 | functions = new ArrayList<Function>(); | ||
190 | timers.put(delay, functions); | ||
191 | } | ||
192 | functions.add(toCall); | ||
193 | } | ||
194 | |||
195 | public static <T> T allowUndefined(Object value) { | ||
196 | if (value instanceof Undefined) { | ||
197 | return null; | ||
198 | } else { | ||
199 | return (T) value; | ||
200 | } | ||
201 | } | ||
202 | } |
-
Please register or sign in to post a comment