├── MIT-LICENSE
├── Makefile-gcc
├── NOTES
├── README
├── TODO
├── config-gcc.mk
├── dom
├── core.js
├── events.js
├── graphics.js
├── html.js
└── svg.js
├── gezira
├── Makefile-gcc
├── generate-bindings
├── html.js
├── spidermonkey.head.c
├── stubs.js
└── svg.js
├── mico.js
├── sdl
├── Makefile-gcc
├── spidermonkey.c
├── stubs.js
└── window.js
└── system
├── Makefile-gcc
├── browser.js
├── shell.js
└── spidermonkey.c
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008 Dan Amelang (dan@amelang.net)
2 |
3 | Permission is hereby granted, free of charge, to any person
4 | obtaining a copy of this software and associated documentation
5 | files (the "Software"), to deal in the Software without
6 | restriction, including without limitation the rights to use,
7 | copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the
9 | Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be
13 | included in all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/Makefile-gcc:
--------------------------------------------------------------------------------
1 | include config-gcc.mk
2 |
3 | default: system/mico-$(ENGINE)
4 | @for p in $(PACKAGES_TO_COMPILE); do \
5 | $(MAKE) -C $$p -f Makefile-gcc $(ENGINE)$(COMPILED_PACKAGE_EXT); \
6 | done
7 |
8 | system/mico-$(ENGINE): .FORCE
9 | $(MAKE) -C system -f Makefile-gcc mico-$(ENGINE)
10 |
11 | clean:
12 | $(MAKE) -C system -f Makefile-gcc clean
13 | @for p in $(PACKAGES_TO_COMPILE); do \
14 | $(MAKE) -C $$p -f Makefile-gcc clean; \
15 | done
16 |
17 | .FORCE:
18 |
--------------------------------------------------------------------------------
/NOTES:
--------------------------------------------------------------------------------
1 | LESSONS LEARNED
2 | ---
3 |
4 | - command for making (spider|trace)monkey:
5 |
6 | export BUILD_OPT=1 && export JS_DIST=.. && make -f Makefile.ref all export
7 |
8 | - command for cleaning (spider|trace)monkey:
9 |
10 | export BUILD_OPT=1 && make -f Makefile.ref clean clobber
11 |
12 | (but you still have to manually delete the editline build directory!)
13 |
14 | - to get embedded tracemonkey to work, I had to manually
15 | copy jsutil.h from the src/ to include/ directory
16 |
17 | - (DOM) where should the actual _value reside? In the element,
18 | or in the attr?
19 | - A: in the attr. Because attr can be created outside
20 | without any ownerElement, but must store a _value.
21 | (FF stores a value in both places and keeps them syncronized)
22 | My way means that when an attribute node is replaced
23 | (or even its value is set using the attr API), the
24 | _value is regenerated, meaning that held references
25 | to the old one are now bad. I can live with that.
26 | - (DOM) where should the parsing logic reside?
27 | - A: in the element. Attr can be created independent
28 | of an ownerElement. If/when attached to an element,
29 | the element can then parse the _value and convert
30 | it to a more specific datatype.
31 | When attr have ownerElements, it should use the element's
32 | parser for setting the _value.
33 | - (DOM) what about attributes
34 | - with no accessors?
35 | - handled (I don't know of any that LK uses)
36 | - with accessors?
37 | - handled
38 | - with read-only accessors?
39 | - handled
40 | - that are #IMPLIED?
41 | - use the defaultValue in the attributeSpec
42 | - that are of string type?
43 | - no parser needed
44 | - that are of float type?
45 | - parser in attributeSpec is parseFloat()
46 | - that are of int type?
47 | - parser is parseInt()
48 | - that are of complex type? (e.g., SVGAnimatedLength)
49 | - parser is SVGAnimatedLength.fromString
50 | - (DOM) What about tagName for (HTML) elements? Should they be a
51 | prototype method for known elements? That way we don't have
52 | _tagName defined individually for each element?
53 | Only define it (when elements are created) if the element doesn't
54 | already have it defined (via the prototype)
55 | No, we can't do this. This is becase the tagName may contain
56 | a prefix, of which the corresponding Element prototype is
57 | unaware.
58 | - we must keep local references for parentNode and ownerElement.
59 | This is because nodes that belong to a document aren't necessarily
60 | reachable from the documentElement.
61 | - what about valueAsString? Does that mean that we aren't
62 | expected to take a string as a setter value for value()?
63 | - A: No, it's that valueAsString requires that we parse the
64 | unit out of the string
65 | - why is translate(0) for textselectionmorph showing up
66 | as an unknown transform?
67 | - A: I don't think that it is unknown, I think that they just called
68 | consolidate and we turned a single translation into a matrix transform.
69 | I just have to make consolidate not do anything if there is only one
70 | transform.
71 | - changing a DOM element's gezira fragment will mess up the parent's gezira
72 | fragment's reference to the child's fragment
73 | - A: I won't have to update the parent's child/children references as long
74 | as the child doesn't change his top (or "geziraBegin") gezira object.
75 | - we can't use the built-in JS inheritance very much at all because we
76 | need multiple inheritance to support the DOM specs. This is why we use
77 | the "fake" copy inheritance of mico.extend
78 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Mico is an experimental Javascript programming environment. It provides
2 | structure and functionality on top the Javascript engine of your choice.
3 | To this aim, Mico strives to use Javascript code instead of native,
4 | engine-specific code wherever possible.
5 |
6 | Mico currently provides a fairly complete DOM Level 2 Core implementation
7 | written in pure Javascript, with very limited Level 2 HTML and SVG support.
8 | It also provides incomplete Javascript bindings to SDL and a pure Javascript
9 | bare-bones implementation of the web browser "window" object. Rendering of
10 | DOM nodes happens via an AOP-inspired bridge between DOM elements and the
11 | underlying graphics library of your choice.
12 |
13 | Mico introduces a "system" object for interfacing with the underlying
14 | Javascript engine. This object allows for loading script files
15 | (via system.load()) and compiled bindings to C libraries
16 | (via system.dlload()).
17 |
18 | Scripts in Mico can declare dependencies on other "packages." At runtime,
19 | these dependencies are resolved automatically through a simple package
20 | location mechanism.
21 |
22 | As of this writing, Mico has been used for very little, mostly as a testbed for
23 | novel web browser implementations.
24 |
25 | Initial development of Mico was funded by Viewpoints Research Institute,
26 | NSF Grant No. 0639876 and Sun Microsystems.
27 |
28 | - Dan Amelang (dan@amelang.net)
29 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | TODO
2 | ---
3 |
4 | - fix glyph server hack
5 | - fix window title hack
6 | - get cloning to work
7 | - positioning is wrong
8 | - get rotation to work (alt click)
9 | - rotates wildly, some calculation is wrong
10 |
11 | - get to run again on the n800
12 | - SVG ellipse is not elliptic enough at large sizes. The
13 | radius should be factored into the bezier calculation
14 | (which is trivial to so)
15 | - change gezira real streams contructors to take varargs instead of an array
16 | - create getters for gezira streams attributes (e.g., rotation.angle)
17 | - make shift key work (for events), and change the shift state from
18 | a parameter to a function call on the sdl object
19 | - Fix getTransformToElement
20 | - need to rewrite setAttributeNS to reuse the attr node, not
21 | create a whole new one each time
22 | - I should look at the "fill-opacity" attribute to grab
23 | alpha of fill color.
24 | - fix "copy" inheritance problem (see dom/events.js) by using
25 | true JS inheritance just for the Node clone family? This might also
26 | fix the fragile ordering of loaded files/packages
27 | - make all gezira parents (groups, too?) point to a default
28 | gezira (blank) object upon creation? This "null" node could be
29 | used in several places (instead of just a gezira.Node with no children)
30 | - ideally I'd have a object mapping to and from dom and gezira objects.
31 | This would get rid of the domObject._graphics stuff, and allow for the
32 | hit testing to work. Expose a hash table object? Or a special purpose
33 | function?
34 | - Fix memory leaks of gezira objects. Must install a finalizer gezira
35 | JS objects.
36 | - expose a window.height window.width getters (and setters?)
37 | - an element may be added to the dom render queue multiple times because
38 | multiple attributes of that element are changed in a given phase. Should
39 | we do a uniq() on that queue then? How much of a win does this get
40 | me? Can either == or === get me a fast object id-like comparison?
41 | - write glue code for other JS engines, window/events libraries and
42 | graphics renderers
43 | - v8
44 | - nitro
45 | - rhino
46 | - plug in an (X)HTML parser (see OMeta/JS)
47 | - To make our (X)HTML output the same as FF, we need to:
48 | - support doctype
49 | - the script tags, plus the "1" child text node in each
50 | - don't emit for elements that don't (can't?) have
51 | children (e.g., )
52 | - don't emit implied args for transforms (e.g., translate(0 0))
53 | - don't output default implied values (or keep track if they have been
54 | changed (specified))
55 | - no newlines for text nodes
56 | - spaces/commas for lists
57 | - same order of the attributes
58 | - network functionality. Bindings to libcurl (or something like it)
59 | - CSS
60 |
--------------------------------------------------------------------------------
/config-gcc.mk:
--------------------------------------------------------------------------------
1 | ENGINE = spidermonkey
2 | PACKAGES_TO_COMPILE = sdl gezira gezira_glyphserver
3 | COMPILED_PACKAGE_EXT = .jsm
4 | GEZIRA_REAL_TYPE = GEZIRA_REAL_FLOAT
5 |
6 | CFLAGS = \
7 | -g -Wall -DXP_UNIX \
8 | -D$(GEZIRA_REAL_TYPE) \
9 | -DCOMPILED_PACKAGE_EXT=\"$(COMPILED_PACKAGE_EXT)\" \
10 | -I../../tracemonkey/js/include \
11 | -L../../tracemonkey/js/lib \
12 | -I../../gezira \
13 | -L../../gezira \
14 | -I/usr/include/freetype2 \
15 | -I../../gezira_glyphserver \
16 | -L../../gezira_glyphserver
17 |
--------------------------------------------------------------------------------
/dom/core.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | var dom = {};
7 |
8 | // NodeList
9 |
10 | dom.NodeList = function() { this._nodes = []; };
11 |
12 | mico.extend(dom.NodeList.prototype, {
13 | item: function(index) { return this._nodes[index]; },
14 | get length() { return this._nodes.length; }
15 | });
16 |
17 | // NamedNodeMap
18 |
19 | dom.NamedNodeMap = function() { this._nodes = []; }
20 |
21 | mico.extend(dom.NamedNodeMap.prototype, {
22 | getNamedItem: function(name) { return this.getNamedItemNS(null, name); },
23 | setNamedItem: function(arg) { return this.setNamedItemNS(null, arg); },
24 | removeNamedItem: function(name) { return this.removeNamedItemNS(null, name); },
25 | item: function(index) { return this._nodes[index]; },
26 | get length() { return this._nodes.length; },
27 |
28 | getNamedItemNS: function(namespaceURI, localName) {
29 | for (var i = 0; i < this._nodes.length; i++) {
30 | if (this._nodes[i].localName == localName &&
31 | this._nodes[i].namespaceURI == namespaceURI)
32 | return this._nodes[i];
33 | }
34 | return null;
35 | },
36 |
37 | setNamedItemNS: function(arg) {
38 | var node = this.getNamedItemNS(arg.namespaceURI, arg.localName);
39 | if (node)
40 | this._nodes[this._nodes.indexOf(node)] = arg;
41 | else
42 | this._nodes.push(arg);
43 | return node;
44 | },
45 |
46 | removeNamedItemNS: function(namespaceURI, localName) {
47 | var node = this.getNamedItemNS(namespaceURI, localName);
48 | if (node)
49 | this._nodes.splice(this._nodes.indexOf(node), 1);
50 | return node;
51 | }
52 | });
53 |
54 | // Node
55 |
56 | dom.Node = function() {};
57 |
58 | mico.extend(dom.Node, {
59 | ELEMENT_NODE: 1,
60 | ATTRIBUTE_NODE: 2,
61 | TEXT_NODE: 3,
62 | CDATA_SECTION_NODE: 4,
63 | ENTITY_REFERENCE_NODE: 5,
64 | ENTITY_NODE: 6,
65 | PROCESSING_INSTRUCTION_NODE: 7,
66 | COMMENT_NODE: 8,
67 | DOCUMENT_NODE: 9,
68 | DOCUMENT_TYPE_NODE: 10,
69 | DOCUMENT_FRAGMENT_NODE: 11,
70 | NOTATION_NODE: 12,
71 |
72 | find: function(node, predicate) {
73 | if (predicate(node))
74 | return node;
75 | for (var i = 0; i < node.childNodes.length; i++) {
76 | result = this.find(node.childNodes.item(i), predicate);
77 | if (result)
78 | return result;
79 | }
80 | return null;
81 | },
82 |
83 | findAncestor: function(node, predicate) {
84 | if (!node.parentNode)
85 | return null;
86 | else if (predicate(node.parentNode))
87 | return node.parentNode;
88 | else
89 | return this.findAncestor(node.parentNode, predicate);
90 | },
91 |
92 | forEach: function(node, action) {
93 | action(node);
94 | for (var i = 0; i < node.childNodes.length; i++)
95 | this.forEach(node.childNodes.item(i), action);
96 | }
97 | });
98 |
99 | mico.extend(dom.Node.prototype, {
100 | get nodeName() { return null; },
101 | get nodeValue() { return null; },
102 | set nodeValue(value) { },
103 | get nodeType() { return 0; },
104 | get parentNode() { return this._parentNode; },
105 | get childNodes() { return this._childNodes ? this._childNodes :
106 | new dom.NodeList; },
107 | get firstChild() { return this.childNodes.item(0); },
108 | get lastChild() { return this.childNodes.item(this.childNodes.length - 1); },
109 |
110 | get previousSibling() {
111 | if (!this.parentNode)
112 | return null;
113 | var siblings = this.parentNode.childNodes;
114 | return siblings.item(siblings._nodes.indexOf(this) - 1);
115 | },
116 |
117 | get nextSibling() {
118 | if (!this.parentNode)
119 | return null;
120 | var siblings = this.parentNode.childNodes;
121 | return siblings.item(siblings._nodes.indexOf(this) + 1);
122 | },
123 |
124 | get attributes() { return this._attributes; },
125 | get ownerDocument() { return this._ownerDocument; },
126 |
127 | insertBefore: function(newChild, refChild) {
128 | if (!this._childNodes)
129 | return;
130 | this.removeChild(newChild);
131 | newChild._parentNode = this;
132 | if (refChild) {
133 | var index = this.childNodes._nodes.indexOf(refChild);
134 | this.childNodes._nodes.splice(index, 0, newChild);
135 | }
136 | else
137 | this.childNodes._nodes.push(newChild);
138 | return newChild;
139 | },
140 |
141 | replaceChild: function(newChild, oldChild) {
142 | this.insertBefore(newChild, oldChild);
143 | return this.removeChild(oldChild);
144 | },
145 |
146 | removeChild: function(oldChild) {
147 | if (!this._childNodes)
148 | return;
149 | var index = this.childNodes._nodes.indexOf(oldChild);
150 | if (index != -1) {
151 | oldChild._parentNode = null;
152 | this.childNodes._nodes.splice(index, 1);
153 | }
154 | return oldChild;
155 | },
156 |
157 | appendChild: function(newChild) {
158 | if (!this._childNodes)
159 | return;
160 | this.removeChild(newChild);
161 | newChild._parentNode = this;
162 | this.childNodes._nodes.push(newChild);
163 | return newChild;
164 | },
165 |
166 | hasChildNodes: function() { return this.childNodes.length > 0; },
167 |
168 | deepClone: function() {
169 | var clone = mico.deepClone(this,
170 | '_ownerDocument', '_parentNode', '_ownerElement');
171 | if (this._ownerDocument)
172 | clone._ownerDocument = this._ownerDocument;
173 | if (this.childNodes)
174 | for (var i = 0; i < this.childNodes.length; i++)
175 | clone.childNodes.item(i)._parentNode = clone;
176 | if (this.attributes)
177 | for (var i = 0; i < this.attributes.length; i++)
178 | clone.attributes.item(i)._ownerElement = clone;
179 | return clone;
180 | },
181 |
182 | cloneNode: function(deep) {
183 | var clone;
184 | if (deep)
185 | clone = this.deepClone();
186 | else {
187 | clone = mico.extend({}, this);
188 | clone._parentNode = null;
189 | if (this._childNodes)
190 | clone._childNodes = new dom.NodeList;
191 | if (this._ownerElement)
192 | clone._ownerElement = null;
193 | // deep clone the attributes
194 | if (this._attributes) {
195 | clone._attributes = mico.deepClone(this._attributes);
196 | for (var i = 0; i < this._attributes.length; i++)
197 | clone._attributes.item(i)._ownerElement = clone;
198 | }
199 | }
200 | return clone;
201 | },
202 |
203 | normalize: function() { mico.TODO(); },
204 | isSupported: function(feature, version) { mico.TODO(); },
205 | get namespaceURI() { return this._namespaceURI; },
206 | get prefix() { return this._prefix; },
207 | // TODO according to the spec, this is supposed to have strange side effects
208 | set prefix(value) { this._prefix = value; },
209 | get localName() { return this._localName; },
210 |
211 | hasAttributes: function() {
212 | return this.attributes && this.attributes.length > 0;
213 | }
214 | });
215 |
216 | // Attr
217 |
218 | dom.Attr = function() { dom.Node.call(this); };
219 |
220 | mico.extend(dom.Attr.prototype, dom.Node.prototype);
221 | mico.extend(dom.Attr.prototype, {
222 | get nodeName() { return this.name; },
223 | get nodeValue() { return this.value; },
224 | set nodeValue(value) { this.value = value; },
225 | get nodeType() { return dom.Node.ATTRIBUTE_NODE; },
226 | get parentNode() { return null; },
227 |
228 | get childNodes() {
229 | var nodes = new dom.NodeList;
230 | if (this.value != '')
231 | nodes._nodes = [this.ownerDocument.createTextNode(this.value)];
232 | return nodes;
233 | },
234 |
235 | get name() { return this._name; },
236 | get specified() { return true; },
237 | get value() { return String(this._value); },
238 |
239 | set value(value) {
240 | this._value = String(value == null ? '' : value);
241 | var owner = this.ownerElement;
242 | var specs = owner && owner.constructor.attributeSpecs;
243 | var spec = specs && specs[this.name];
244 | if (this._value == '' && spec && spec.defaultValue)
245 | this._value = spec.defaultValue;
246 | if (spec && spec.parser)
247 | this._value = spec.parser(this._value);
248 | },
249 |
250 | get ownerElement() { return this._ownerElement; },
251 |
252 | toString: function() { return this.name + '="' + this.value + '"'; }
253 | });
254 |
255 | // Element
256 |
257 | dom.Element = function() {
258 | dom.Node.call(this);
259 | this._childNodes = new dom.NodeList;
260 | this._attributes = new dom.NamedNodeMap;
261 | };
262 |
263 | mico.extend(dom.Element, {
264 | factories: {},
265 |
266 | // attributeSpec can have name, parser, type(.fromString), defaultValue and xmlName
267 | defineAttribute: function(element, attributeSpec) {
268 | attributeSpec = attributeSpec.name ? attributeSpec : {name:attributeSpec};
269 | var xmlName = attributeSpec.xmlName || attributeSpec.name;
270 | attributeSpec.parser = attributeSpec.parser ||
271 | (attributeSpec.type && attributeSpec.type.fromString) || attributeSpec.type;
272 | element.attributeSpecs = element.attributeSpecs || {};
273 | element.attributeSpecs[xmlName] = attributeSpec;
274 |
275 | // TODO we should not assume that all attributes have a getter
276 | element.prototype.__defineGetter__(attributeSpec.name, function() {
277 | var node = this.getAttributeNode(xmlName);
278 | if (!node) {
279 | node = this.ownerDocument.createAttribute(xmlName);
280 | this.setAttributeNode(node);
281 | }
282 | return node._value;
283 | });
284 |
285 | if (!attributeSpec.readonly) {
286 | element.prototype.__defineSetter__(attributeSpec.name, function(value) {
287 | var node = this.getAttributeNode(xmlName);
288 | if (!node) {
289 | node = this.ownerDocument.createAttribute(xmlName);
290 | this.setAttributeNode(node);
291 | }
292 | node.value = value;
293 | });
294 | }
295 | },
296 |
297 | defineAttributes: function(element) {
298 | for (var i = 1; i < arguments.length; i++)
299 | this.defineAttribute(element, arguments[i]);
300 | }
301 | });
302 |
303 | mico.extend(dom.Element.prototype, dom.Node.prototype);
304 | mico.extend(dom.Element.prototype, {
305 | get nodeName() { return this.tagName; },
306 | get nodeType() { return dom.Node.ELEMENT_NODE; },
307 | get tagName() { return this._tagName; },
308 |
309 | getAttribute: function(name) {
310 | return this.getAttributeNS(null, name);
311 | },
312 |
313 | setAttribute: function(name, value) {
314 | return this.setAttributeNS(null, name, value);
315 | },
316 |
317 | removeAttribute: function(name) {
318 | return this.removeAttributeNS(null, name);
319 | },
320 |
321 | getAttributeNode: function(name) {
322 | return this.getAttributeNodeNS(null, name);
323 | },
324 |
325 | setAttributeNode: function(newAttr) {
326 | return this.setAttributeNodeNS(newAttr);
327 | },
328 |
329 | removeAttributeNode: function(oldAttr) {
330 | this.attributes.removeNamedItemNS(oldAttr.namespaceURI, oldAttr.localName);
331 | oldAttr._ownerElement = null;
332 | return oldAttr;
333 | },
334 |
335 | getElementsByTagName: function(name) {
336 | return this.getElementsByTagNameNS('*', name);
337 | },
338 |
339 | getAttributeNS: function(namespaceURI, localName) {
340 | var node = this.getAttributeNodeNS(namespaceURI, localName);
341 | return node ? node.value : null;
342 | },
343 |
344 | setAttributeNS: function(namespaceURI, qualifiedName, value) {
345 | // TODO need to reuse the existing attribute node if there already is
346 | // one with this namespaceURI and local name
347 | var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
348 | this.setAttributeNodeNS(attr);
349 | attr.value = value;
350 | },
351 |
352 | removeAttributeNS: function(namespaceURI, localName) {
353 | var oldAttr = this.attributes.removeNamedItemNS(namespaceURI, localName);
354 | oldAttr && (oldAttr._ownerElement = null);
355 | },
356 |
357 | getAttributeNodeNS: function(namespaceURI, localName) {
358 | return this.attributes.getNamedItemNS(namespaceURI, localName);
359 | },
360 |
361 | setAttributeNodeNS: function(newAttr) {
362 | var oldAttr = this.attributes.setNamedItemNS(newAttr);
363 | oldAttr && (oldAttr._ownerElement = null);
364 | newAttr._ownerElement = this;
365 | newAttr.value = newAttr.value; // Force the value to be parsed
366 | return oldAttr;
367 | },
368 |
369 | getElementsByTagNameNS: function(namespaceURI, localName) {
370 | var elements = new dom.NodeList;
371 | dom.Node.forEach(this, function(node) {
372 | if (node.nodeType == dom.Node.ELEMENT_NODE &&
373 | (localName == '*' || node.localName == localName) &&
374 | (namespaceURI == '*' || node.namespaceURI == namespaceURI))
375 | elements._nodes.push(node);
376 | });
377 | // TODO this list is supposed to be 'live'. We could use something like
378 | // a version number on the document that increments whenever the structure
379 | // changes (insertBefore called on a node) which has the effect of
380 | // invalidating the contents of the list (forcing a requery when
381 | // item or length is called on the list).
382 | return elements;
383 | },
384 |
385 | hasAttribute: function(name) {
386 | return this.hasAttributeNS(null, name);
387 | },
388 |
389 | hasAttributeNS: function(namespaceURI, localName) {
390 | return this.getAttributeNodeNS(namespaceURI, localName) != null;
391 | },
392 |
393 | // FIXME some elements don't have closing tags
394 | toString: function(deep) {
395 | var result = '<' + this.nodeName;
396 | for (var i = 0; i < this.attributes.length; i++)
397 | result += ' ' + this.attributes.item(i).toString();
398 | result += '>\n';
399 | for (var i = 0; deep && i < this.childNodes.length; i++)
400 | result += this.childNodes.item(i).toString(deep) + '\n';
401 | result += '' + this.tagName + '>';
402 | return result;
403 | }
404 | });
405 |
406 | // CharacterData
407 |
408 | dom.CharacterData = function() { dom.Node.call(this); };
409 |
410 | mico.extend(dom.CharacterData.prototype, dom.Node.prototype);
411 | mico.extend(dom.CharacterData.prototype, {
412 | get data() { return this._data; },
413 | set data(value) { this._data = value; },
414 | get length() { return this.data ? this.data.length : 0; },
415 |
416 | substringData: function(offset, count) {
417 | return this.data.substr(offset, count);
418 | },
419 |
420 | appendData: function(arg) {
421 | this.data = this.data.concat(arg);
422 | },
423 |
424 | insertData: function(offset, arg) {
425 | this.data = this.data.substr(0, offset).concat(arg).
426 | concat(this.data.substr(offset));
427 | },
428 |
429 | deleteData: function(offset, count) {
430 | this.data = this.data.substr(0, offset).
431 | concat(this.data.substr(offset + count));
432 | },
433 |
434 | replaceData: function(offset, count, arg) {
435 | this.data = this.data.substr(0, offset).concat(arg).
436 | concat(this.data.substr(offset + count));
437 | }
438 | });
439 |
440 | // Text
441 |
442 | dom.Text = function() { dom.CharacterData.call(this); };
443 |
444 | mico.extend(dom.Text.prototype, dom.CharacterData.prototype);
445 | mico.extend(dom.Text.prototype, {
446 | get nodeName() { return '#text'; },
447 | get nodeValue() { return this.data; },
448 | set nodeValue(value) { this.data = value; },
449 | get nodeType() { return dom.Node.TEXT_NODE; },
450 |
451 | splitText: function(offset) {
452 | var text = new dom.Text;
453 | text.data = this.data.substr(offset);
454 | this.data = this.data.substr(0, offset);
455 | if (this.parentNode) {
456 | var sibling = this.nextSibling;
457 | if (sibling)
458 | this.parentNode.insertBefore(text, sibling);
459 | else
460 | this.parentNode.appendChild(text);
461 | }
462 | return text;
463 | },
464 |
465 | toString: function() { return this.data; }
466 | });
467 |
468 | // CDATASection
469 |
470 | dom.CDATASection = function() { dom.Text.call(this); };
471 |
472 | mico.extend(dom.CDATASection.prototype, dom.Text.prototype);
473 | dom.CDATASection.prototype.toString = function() {
474 | return '';
475 | };
476 |
477 | // Document
478 |
479 | dom.Document = function() {
480 | dom.Node.call(this);
481 | this._childNodes = new dom.NodeList;
482 | };
483 |
484 | mico.extend(dom.Document.prototype, dom.Node.prototype);
485 | mico.extend(dom.Document.prototype, {
486 | get nodeName() { return '#document;'; },
487 | get nodeType() { return dom.Node.DOCUMENT_NODE; },
488 | get doctype() { mico.TODO(); },
489 | get implementation() { mico.TODO(); },
490 |
491 | get documentElement() {
492 | return dom.Node.find(this, function(e) {
493 | return e.nodeType == dom.Node.ELEMENT_NODE;
494 | });
495 | },
496 |
497 | createElement: function(tagName) {
498 | return this.createElementNS(null, tagName);
499 | },
500 |
501 | createDocumentFragment: function() { mico.TODO(); },
502 |
503 | createTextNode: function(data) {
504 | var text = new dom.Text;
505 | text._ownerDocument = this;
506 | text.data = data;
507 | return text;
508 | },
509 |
510 | createComment: function(data) { mico.TODO(); },
511 |
512 | createCDATASection: function(data) {
513 | var cdata = new dom.CDATASection;
514 | cdata._ownerDocument = this;
515 | cdata.data = data;
516 | return cdata;
517 | },
518 |
519 | createProcessingInstruction: function(target, data) { mico.TODO(); },
520 |
521 | createAttribute: function(name) {
522 | return this.createAttributeNS(null, name);
523 | },
524 |
525 | createEntityReference: function(name) { mico.TODO(); },
526 |
527 | getElementsByTagName: function(tagName) {
528 | return this.getElementsByTagNameNS('*', tagName);
529 | },
530 |
531 | importNode: function(importedNode, deep) { mico.TODO(); },
532 |
533 | createElementNS: function(namespaceURI, qualifiedName) {
534 | var match = qualifiedName.match(/(\w*):(\w*)/);
535 | var localName = (match && match[2]) || qualifiedName;
536 | var factory = dom.Element.factories[namespaceURI];
537 | var element = new ((factory && factory[localName]) || dom.Element);
538 | element._ownerDocument = this;
539 | element._tagName = qualifiedName;
540 | element._namespaceURI = namespaceURI;
541 | element._prefix = match && match[1];
542 | element._localName = localName;
543 | return element;
544 | },
545 |
546 | createAttributeNS: function(namespaceURI, qualifiedName) {
547 | var attr = new dom.Attr;
548 | var match = qualifiedName.match(/(\w*):(\w*)/);
549 | attr._ownerDocument = this;
550 | attr._name = qualifiedName;
551 | attr._namespaceURI = namespaceURI;
552 | attr._prefix = match && match[1];
553 | attr._localName = (match && match[2]) || qualifiedName;
554 | attr.value = '';
555 | return attr;
556 | },
557 |
558 | getElementsByTagNameNS: function(namespaceURI, localName) {
559 | return this.documentElement.getElementsByTagNameNS(namespaceURI, localName);
560 | },
561 |
562 | getElementById: function(elementId) { return null; }
563 | });
564 |
--------------------------------------------------------------------------------
/dom/events.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | mico.require('dom.core');
7 |
8 | // FIXME this is a temporary hack, we need to properly implement
9 | // the event prototypes soon.
10 | dom.MouseEvent = function() {};
11 | mico.extend(dom.MouseEvent.prototype, {
12 | get currentTarget() { return this._currentTarget; },
13 | get shiftKey() { return this._shiftKey; },
14 | get altKey() { return this._altKey; },
15 | get type() { return this._type; },
16 | get clientX() { return this._clientX; },
17 | get clientY() { return this._clientY; },
18 | stopPropagation: function() { this._propagationStopped = true; }
19 | });
20 |
21 | dom.EventTarget = {
22 | // TODO go over the spec again
23 | addEventListener: function(type, listener, useCapture) {
24 | this._eventListeners = this._eventListeners || {};
25 | this._eventListeners[type] = this._eventListeners[type] || [];
26 | this._eventListeners[type].push(listener);
27 | },
28 |
29 | // TODO go over the spec again
30 | removeEventListener: function(type, listener, useCapture) {
31 | /*
32 | this._eventListeners = this._eventListeners || {};
33 | if (this._eventListeners[type] && this._eventListeners[type].indexOf(listener)
34 | */
35 | mico.TODO();
36 | },
37 |
38 | dispatchEvent: function(evt) {
39 | var listeners = this._eventListeners && this._eventListeners[evt.type];
40 | evt._currentTarget = this;
41 |
42 | if (listeners)
43 | listeners.forEach(function(l) { l.handleEvent(evt); });
44 |
45 | this.childNodes._nodes.forEach(function(c) {
46 | if (!evt._propagationStopped)
47 | c.dispatchEvent(evt);
48 | });
49 | },
50 | };
51 |
52 | /* TODO we really should only have to put this in Node, but because we use
53 | * "copy" inheritance, we have to mix EventTarget into all the children in
54 | * the clone family.
55 | */
56 | mico.extend(dom.Node.prototype, dom.EventTarget);
57 | mico.extend(dom.Attr.prototype, dom.EventTarget);
58 | mico.extend(dom.Element.prototype, dom.EventTarget);
59 | mico.extend(dom.CharacterData.prototype, dom.EventTarget);
60 | mico.extend(dom.Text.prototype, dom.EventTarget);
61 | mico.extend(dom.CDATASection.prototype, dom.EventTarget);
62 | mico.extend(dom.Document.prototype, dom.EventTarget);
63 |
64 | /* TODO the below needs to be done to all in the Node clone family,
65 | * as we do with mixing in EventTarget above. Perhaps we should use
66 | * "true" JS inheritance for the Node clone family, and copy
67 | * inheritance for the rest (see HTML and SVG elements) ?
68 | */
69 | mico.around(dom.Element.prototype,
70 | ['deepClone', 'cloneNode'],
71 | function(proceed, args) {
72 | var listeners = this._eventListeners;
73 | delete this._eventListeners;
74 | var clone = proceed.apply(this, args);
75 | listeners && (this._eventListeners = listeners);
76 | return clone;
77 | });
78 |
--------------------------------------------------------------------------------
/dom/graphics.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | mico.require('dom.core');
7 |
8 | dom.graphics = {
9 | queue: [],
10 | renderers: {},
11 |
12 | render: function(element) {
13 | var renderer = this.renderers[element.constructor.tagName];
14 | renderer = renderer || this.renderers[null];
15 | return renderer && renderer(element);
16 | },
17 |
18 | update: function() {
19 | // TODO get rid of multiple occurances of the same element in queue?
20 | while (this.queue.length)
21 | this.render(this.queue.pop());
22 | }
23 | };
24 |
25 | mico.around(dom.Attr.prototype, 'value=',
26 | function(proceed, args) {
27 | proceed.apply(this, args);
28 | if (this.ownerElement && this.ownerElement._graphics)
29 | dom.graphics.queue.push(this.ownerElement);
30 | });
31 |
32 | // FIXME I'm uneasy about depending on DOM implementation-specific details here
33 | mico.around(dom.Element.prototype,
34 | ['removeChild', 'removeAttributeNode', 'removeAttributeNS'],
35 | function(proceed, args) {
36 | if (this._graphics)
37 | dom.graphics.queue.push(this);
38 | return proceed.apply(this, args);
39 | });
40 |
41 | mico.around(dom.Element.prototype, ['deepClone', 'cloneNode'],
42 | function(proceed, args) {
43 | var _graphics = this._graphics;
44 | delete this._graphics;
45 | var clone = proceed.apply(this, args);
46 | _graphics && (this._graphics = _graphics);
47 | return clone;
48 | });
49 |
--------------------------------------------------------------------------------
/dom/html.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | mico.require('dom.core');
7 |
8 | // TODO deal with case-sensitivity for HTML tagnames and such
9 |
10 | // HTMLDocument
11 |
12 | dom.HTMLDocument = function () { dom.Document.call(this); };
13 | mico.extend(dom.HTMLDocument.prototype, dom.Document.prototype);
14 |
15 | mico.extend(dom.HTMLDocument.prototype, {
16 | get title() { return this._title; },
17 | set title(value) { this._title = value; },
18 | get referrer() { return this._referrer; },
19 | get domain() { return this._domain; },
20 | get URL() { return this._URL; },
21 | get body() { mico.TODO(); },
22 | set body(value) { mico.TODO(); },
23 | get images() { mico.TODO(); },
24 | get applets() { mico.TODO(); },
25 | get links() { mico.TODO(); },
26 | get forms() { mico.TODO(); },
27 | get anchors() { mico.TODO(); },
28 | get cookie() { return this._cookie; },
29 | set cookie(value) { this._cookie = value; },
30 | open: function() { mico.TODO(); },
31 | close: function() { mico.TODO(); },
32 | write: function(text) { mico.TODO(); },
33 | writeln: function(text) { mico.TODO(); },
34 | getElementsByName: function(elementName) { mico.TODO(); },
35 |
36 | getElementById: function(id) {
37 | return dom.Node.find(this.documentElement, function(node) {
38 | // TODO case-sensitivity?
39 | return (node.nodeType == dom.Node.ELEMENT_NODE &&
40 | node.getAttribute('id') == id);
41 | });
42 | }
43 | });
44 |
45 | // HTMLElement
46 |
47 | dom.HTMLElement = function() {
48 | dom.Element.call(this);
49 | // TODO we should actually implement the CSS object, and this should be
50 | // an attribute node, see interface ElementCSSInlineStyle, which HTMLElements
51 | // should mix in.
52 | this.style = {};
53 | };
54 | mico.extend(dom.HTMLElement.prototype, dom.Element.prototype);
55 | dom.Element.factories['http://www.w3.org/1999/xhtml'] =
56 | dom.HTMLElement.factory = {};
57 |
58 | dom.HTMLElement.defineElement = function(name) {
59 | var element = function() { dom.HTMLElement.call(this); };
60 | mico.extend(element.prototype, dom.HTMLElement.prototype);
61 | element.attributeSpecs = {};
62 | mico.extend(element.attributeSpecs, dom.HTMLElement.attributeSpecs);
63 | dom.Element.defineAttributes.apply(dom.Element,
64 | [element].concat(Array.prototype.slice.call(arguments, 1)));
65 | this.factory[name] = element;
66 | element.tagName = 'http://www.w3.org/1999/xhtml:' + name;
67 | return element;
68 | };
69 |
70 | dom.Element.defineAttributes(dom.HTMLElement, 'id', 'title', 'lang', 'dir',
71 | {name:'className', xmlName:'class'});
72 |
73 | // TODO should define tagName for each (using defineElement)
74 | ['sub', 'sup', 'span', 'bdo', 'tt', 'i', 'b', 'u', 's', 'strike', 'big',
75 | 'small', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd', 'var', 'cite',
76 | 'acronym', 'abbr', 'dd', 'dt', 'noframes', 'noscript', 'address', 'center'].
77 | forEach(function(name) { dom.HTMLElement.factory[name] = dom.HTMLElement; });
78 |
79 | dom.HTMLHtmlElement = dom.HTMLElement.defineElement('html', 'version');
80 | dom.HTMLHeadElement = dom.HTMLElement.defineElement('head', 'profile');
81 | dom.HTMLBodyElement = dom.HTMLElement.defineElement('body',
82 | 'aLink', 'background', 'bgColor', 'link', 'text', 'vLink');
83 | dom.HTMLDivElement = dom.HTMLElement.defineElement('div', 'align');
84 |
--------------------------------------------------------------------------------
/dom/svg.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | mico.require('dom.core');
7 |
8 | // TODO this should go in a separate file dedicated to CSS
9 | dom.CSSStyleDeclaration = function() {};
10 | dom.CSSStyleDeclaration.fromString = function(s) { mico.TODO(); };
11 |
12 | // TODO missing interfaces (among many others)
13 | // SVGUnitTypes
14 | // SVGURIReference
15 | // css::ViewCSS
16 | // css::DocumentCSS
17 | // events::DocumentEvent
18 | // SVGZoomAndPan
19 | // SVGFitToViewBox
20 | // SVGTests
21 | // SVGLangSpace
22 | // SVGExternalResourcesRequired
23 |
24 | // SVGNumber
25 |
26 | dom.SVGNumber = function() {};
27 |
28 | dom.SVGNumber.fromString = function(s) {
29 | var object = new dom.SVGNumber;
30 | object.value = s;
31 | return object;
32 | };
33 |
34 | mico.extend(dom.SVGNumber.prototype, {
35 | get value() { return this._value; },
36 | set value(value) { this._value = parseFloat(String(value)) || 0; },
37 | toString: function() { return String(this.value); }
38 | });
39 |
40 | // SVGLength
41 |
42 | dom.SVGLength = function() {};
43 |
44 | mico.extend(dom.SVGLength, {
45 | SVG_LENGTHTYPE_UNKNOWN: 0,
46 | SVG_LENGTHTYPE_NUMBER: 1,
47 | SVG_LENGTHTYPE_PERCENTAGE: 2,
48 | SVG_LENGTHTYPE_EMS: 3,
49 | SVG_LENGTHTYPE_EXS: 4,
50 | SVG_LENGTHTYPE_PX: 5,
51 | SVG_LENGTHTYPE_CM: 6,
52 | SVG_LENGTHTYPE_MM: 7,
53 | SVG_LENGTHTYPE_IN: 8,
54 | SVG_LENGTHTYPE_PT: 9,
55 | SVG_LENGTHTYPE_PC: 10,
56 | unitTypeToString: ['', '', '%', 'em', 'ex', 'px', 'cm', 'mm', 'in', 'pt', 'pc']
57 | });
58 |
59 | dom.SVGLength.fromString = function(s) {
60 | var object = new dom.SVGLength;
61 | object.valueAsString = s;
62 | return object;
63 | };
64 |
65 | mico.extend(dom.SVGLength.prototype, {
66 | get unitType() { return this._unitType; },
67 | get value() { return this._value; },
68 | set value(value) { this._value = parseFloat(String(value)) || 0; },
69 | get valueInSpecifiedUnits() { mico.TODO(); },
70 | set valueInSpecifiedUnits(value) { mico.TODO(); },
71 | get valueAsString() {
72 | var value = this._value;
73 | // TODO must denormalize other unit types, too
74 | if (this._unitType == dom.SVGLength.SVG_LENGTHTYPE_PERCENTAGE)
75 | value *= 100;
76 | return value + dom.SVGLength.unitTypeToString[this._unitType];
77 | },
78 | set valueAsString(value) {
79 | var match = String(value).match(/(\d*\.?\d*)(%|\w*)/);
80 | this.value = match && match[1];
81 | this._unitType = dom.SVGLength.unitTypeToString.
82 | lastIndexOf(match && match[2] || '');
83 | // TODO must normalize other unit types, too
84 | if (this._unitType == dom.SVGLength.SVG_LENGTHTYPE_PERCENTAGE)
85 | this._value /= 100;
86 | },
87 | newValueSpecifiedUnits:
88 | function(unitType, valueInSpecifiedUnits) { mico.TODO(); },
89 | convertToSpecifiedUnits: function(unitType) { mico.TODO(); },
90 | toString: function() { return this.valueAsString; }
91 | });
92 |
93 | // SVGPoint
94 |
95 | dom.SVGPoint = function() {};
96 |
97 | dom.SVGPoint.fromString = function(s) {
98 | var point = new dom.SVGPoint;
99 | var coors = s.split(/(?:\s|,)+/);
100 | point.x = parseFloat(coors[0]) || 0;
101 | point.y = parseFloat(coors[1]) || 0;
102 | return point;
103 | };
104 |
105 | mico.extend(dom.SVGPoint.prototype, {
106 | get x() { return this._x; },
107 | set x(value) { this._x = value; },
108 | get y() { return this._y; },
109 | set y(value) { this._y = value; },
110 |
111 | matrixTransform: function(matrix) {
112 | var point = new dom.SVGPoint;
113 | point.x = this.x * matrix.a + this.y * matrix.c + matrix.e;
114 | point.y = this.x * matrix.b + this.y * matrix.d + matrix.f;
115 | return point;
116 | },
117 |
118 | toString: function() {
119 | return this.x + ',' + this.y;
120 | }
121 | });
122 |
123 | // SVGMatrix
124 |
125 | dom.SVGMatrix = function() {
126 | this._a = 1.0; this._c = 0.0; this._e = 0.0;
127 | this._b = 0.0; this._d = 1.0; this._f = 0.0;
128 | };
129 |
130 | mico.extend(dom.SVGMatrix.prototype, {
131 | get a() { return this._a; },
132 | set a(value) { this._a = value; },
133 | get b() { return this._b; },
134 | set b(value) { this._b = value; },
135 | get c() { return this._c; },
136 | set c(value) { this._c = value; },
137 | get d() { return this._d; },
138 | set d(value) { this._d = value; },
139 | get e() { return this._e; },
140 | set e(value) { this._e = value; },
141 | get f() { return this._f; },
142 | set f(value) { this._f = value; },
143 |
144 | multiply: function(b) {
145 | var a = this;
146 | var ab = new dom.SVGMatrix;
147 | ab.a = a.a * b.a + a.c * b.b; ab.b = a.b * b.a + a.d * b.b;
148 | ab.c = a.a * b.c + a.c * b.d; ab.d = a.b * b.c + a.d * b.d;
149 | ab.e = a.a * b.e + a.c * b.f + a.e; ab.f = a.b * b.e + a.d * b.f + a.f;
150 | return ab;
151 | },
152 |
153 | inverse: function() {
154 | var a = this;
155 | var b = new dom.SVGMatrix;
156 | var d = 1 / (a.a * a.d - a.b * a.c);
157 | b.a = a.d * d; b.c = -a.c * d; b.e = -a.e * b.a - a.f * b.c;
158 | b.b = -a.b * d; b.d = a.a * d; b.f = -a.e * b.b - a.f * b.d;
159 | return b;
160 | },
161 |
162 | translate: function(x, y) {
163 | var matrix = new dom.SVGMatrix;
164 | matrix.e = x; matrix.f = y
165 | return matrix.multiply(this);
166 | },
167 |
168 | scale: function(scaleFactor) {
169 | return this.scaleNonUniform(scaleFactor, scaleFactor);
170 | },
171 |
172 | scaleNonUniform: function(scaleFactorX, scaleFactorY) {
173 | var matrix = new dom.SVGMatrix;
174 | matrix.a = scaleFactorX;
175 | matrix.d = scaleFactorY;
176 | return matrix.multiply(this);
177 | },
178 |
179 | rotate: function(angle) {
180 | var matrix = new dom.SVGMatrix;
181 | matrix.a = Math.cos(angle);
182 | matrix.b = Math.sin(angle);
183 | matrix.c = -matrix.b;
184 | matrix.d = matrix.a;
185 | return matrix.multiply(this);
186 | },
187 |
188 | rotateFromVector: function(x, y) { mico.TODO(); },
189 | flipX: function() { mico.TODO(); },
190 | flipY: function() { mico.TODO(); },
191 |
192 | skewX: function(angle) {
193 | var matrix = new dom.SVGMatrix;
194 | matrix.c = Math.tan(angle);
195 | return matrix.multiply(this);
196 | },
197 |
198 | skewY: function(angle) {
199 | var matrix = new dom.SVGMatrix;
200 | matrix.b = Math.tan(angle);
201 | return matrix.multiply(this);
202 | }
203 | });
204 |
205 | // SVGTransform
206 |
207 | dom.SVGTransform = function() {
208 | this._type = dom.SVGTransform.SVG_TRANSFORM_UNKNOWN;
209 | };
210 |
211 | mico.extend(dom.SVGTransform, {
212 | SVG_TRANSFORM_UNKNOWN: 0,
213 | SVG_TRANSFORM_MATRIX: 1,
214 | SVG_TRANSFORM_TRANSLATE: 2,
215 | SVG_TRANSFORM_SCALE: 3,
216 | SVG_TRANSFORM_ROTATE: 4,
217 | SVG_TRANSFORM_SKEWX: 5,
218 | SVG_TRANSFORM_SKEWY: 6,
219 | typeToString:
220 | ['unknown', 'matrix', 'translate', 'scale', 'rotate', 'skewX', 'skewY']
221 | });
222 |
223 | dom.SVGTransform.fromString = function(s) {
224 | var transform = new dom.SVGTransform;
225 | var match = s.match(/(\w+)\s*\((.*)\)/);
226 | if (match) {
227 | var args = match[2].split(/(?:\s|,)+/).
228 | map(function(n) { return parseFloat(n) || 0; });
229 | switch (match[1]) {
230 | case 'matrix':
231 | var matrix = new dom.SVGMatrix;
232 | matrix.a = args[0]; matrix.b = args[1];
233 | matrix.c = args[2]; matrix.d = args[3];
234 | matrix.e = args[4]; matrix.f = args[5];
235 | transform.setMatrix(matrix);
236 | break;
237 | case 'translate':
238 | transform.setTranslate(args[0], args[1]);
239 | break;
240 | case 'scale':
241 | transform.setScale(args[0], args[1]);
242 | break;
243 | case 'rotate':
244 | transform.setRotate(args[0], args[1], args[2]);
245 | break;
246 | case 'skewX':
247 | transform.setSkewX(args[0]);
248 | break;
249 | case 'skewY':
250 | transform.setSkewY(args[0]);
251 | break;
252 | }
253 | }
254 | return transform;
255 | };
256 |
257 | mico.extend(dom.SVGTransform.prototype, {
258 | get type() { return this._type; },
259 | get matrix() { return this._matrix; },
260 | get angle() { return this._angle; },
261 |
262 | setMatrix: function(matrix) {
263 | this._type = dom.SVGTransform.SVG_TRANSFORM_MATRIX;
264 | this._angle = 0;
265 | this._matrix = new dom.SVGMatrix;
266 | this._matrix.a = matrix.a; this._matrix.b = matrix.b;
267 | this._matrix.c = matrix.c; this._matrix.d = matrix.d;
268 | this._matrix.e = matrix.e; this._matrix.f = matrix.f;
269 | },
270 |
271 | setTranslate: function(tx, ty) {
272 | this._type = dom.SVGTransform.SVG_TRANSFORM_TRANSLATE;
273 | this._angle = 0;
274 | this._matrix = (new dom.SVGMatrix).translate(tx, ty || 0);
275 | },
276 |
277 | setScale: function(sx, sy) {
278 | this._type = dom.SVGTransform.SVG_TRANSFORM_SCALE;
279 | this._angle = 0;
280 | this._matrix = (new dom.SVGMatrix).scaleNonUniform(sx, sy || sx);
281 | },
282 |
283 | setRotate: function(angle, cx, cy) {
284 | cx && mico.TODO(); // We don't handle the optional cx cy yet
285 | this._type = dom.SVGTransform.SVG_TRANSFORM_ROTATE;
286 | this._angle = angle;
287 | this._matrix = (new dom.SVGMatrix).rotate(angle);
288 | },
289 |
290 | setSkewX: function(angle) {
291 | this._type = dom.SVGTransform.SVG_TRANSFORM_SKEWX;
292 | this._angle = angle;
293 | this._matrix = (new dom.SVGMatrix).skewX(angle);
294 | },
295 |
296 | setSkewY: function(angle) {
297 | this._type = dom.SVGTransform.SVG_TRANSFORM_SKEWY;
298 | this._angle = angle;
299 | this._matrix = (new dom.SVGMatrix).skewY(angle);
300 | },
301 |
302 | // TODO what about the optional cx cy for rotate?
303 | toString: function() {
304 | var args = [];
305 | with (dom.SVGTransform)
306 | switch (this.type) {
307 | case SVG_TRANSFORM_MATRIX: args = [this.matrix.a, this.matrix.b,
308 | this.matrix.c, this.matrix.d,
309 | this.matrix.e, this.matrix.f]; break;
310 | case SVG_TRANSFORM_TRANSLATE: args = [this.matrix.e, this.matrix.f]; break;
311 | case SVG_TRANSFORM_SCALE: args = [this.matrix.a, this.matrix.d]; break;
312 | case SVG_TRANSFORM_ROTATE: args = [this.angle]; break;
313 | case SVG_TRANSFORM_SKEWX: args = [this.angle]; break;
314 | case SVG_TRANSFORM_SKEWY: args = [this.angle]; break;
315 | }
316 | return dom.SVGTransform.typeToString[this.type] + '(' + args.join(' ') + ')';
317 | }
318 | });
319 |
320 | // SVGList (used for SVGStringList, SVGPointList, etc.)
321 |
322 | dom.SVGList = function() { this._items = []; };
323 |
324 | mico.extend(dom.SVGList.prototype, {
325 | get numberOfItems() { return this._items.length; },
326 | clear: function() { this._items.length = 0; },
327 |
328 | initialize: function(newItem) {
329 | this.clear();
330 | return this.appendItem(newItem);
331 | },
332 |
333 | getItem: function(index) { return this._items[index]; },
334 | insertItemBefore: function(newItem, index) { mico.TODO(); },
335 | replaceItem: function(newItem, index) { mico.TODO(); },
336 | removeItem: function(index) { mico.TODO(); },
337 |
338 | appendItem: function(newItem) {
339 | this._items.push(newItem);
340 | return newItem;
341 | },
342 |
343 | toString: function() {
344 | return this._items.join(' ');
345 | }
346 | });
347 |
348 | // SVGPointList
349 |
350 | dom.SVGPointList = function() { dom.SVGList.call(this); };
351 | mico.extend(dom.SVGPointList.prototype, dom.SVGList.prototype);
352 | mico.extend(dom.SVGPointList, {
353 | fromString: function(s) {
354 | var list = new dom.SVGPointList;
355 | var items = s.split(/(?:\s|,)+/);
356 | for (var i = 0; i < items.length - 1; i += 2)
357 | list.appendItem(dom.SVGPoint.fromString(items[i] + ',' + items[i + 1]));
358 | return list;
359 | }
360 | });
361 |
362 | // SVGNumberList
363 |
364 | dom.SVGNumberList = function() { dom.SVGList.call(this); };
365 | mico.extend(dom.SVGNumberList.prototype, dom.SVGList.prototype);
366 | mico.extend(dom.SVGNumberList, {
367 | fromString: function(s) {
368 | var list = new dom.SVGNumberList;
369 | var items = s.split(/(?:\s|,)+/);
370 | for (var i = 0; i < items.length; i++)
371 | list.appendItem(dom.SVGNumber.fromString(items[i]));
372 | return list;
373 | }
374 | });
375 |
376 | // SVGLengthList
377 |
378 | dom.SVGLengthList = function() { dom.SVGList.call(this); };
379 | mico.extend(dom.SVGLengthList.prototype, dom.SVGList.prototype);
380 | mico.extend(dom.SVGLengthList, {
381 | fromString: function(s) {
382 | var list = new dom.SVGLengthList;
383 | var items = s.split(/(?:\s|,)+/);
384 | for (var i = 0; i < items.length; i++)
385 | list.appendItem(dom.SVGLength.fromString(items[i]));
386 | return list;
387 | }
388 | });
389 |
390 | // SVGTransformList
391 |
392 | dom.SVGTransformList = function() { dom.SVGList.call(this); };
393 | mico.extend(dom.SVGTransformList.prototype, dom.SVGList.prototype);
394 | mico.extend(dom.SVGTransformList, {
395 | fromString: function(s) {
396 | var list = new dom.SVGTransformList;
397 | var items = s.split(/\)\s*,*\s*/);
398 | for (var i = 0; i < items.length - 1; i++)
399 | list.appendItem(dom.SVGTransform.fromString(items[i] + ')'));
400 | return list;
401 | }
402 | });
403 |
404 | mico.extend(dom.SVGTransformList.prototype, {
405 | createSVGTransformFromMatrix: function(matrix) {
406 | var transform = new dom.SVGTransform;
407 | transform.setMatrix(matrix);
408 | return transform;
409 | },
410 |
411 | consolidate: function() {
412 | if (this.numberOfItems == 0)
413 | return null;
414 | if (this.numberOfItems == 1)
415 | return this.getItem(0);
416 | var matrix = new dom.SVGMatrix;
417 | for (var i = 0; i < this.numberOfItems; i++)
418 | matrix = this.getItem(i).matrix.multiply(matrix);
419 | this.clear();
420 | return this.appendItem(this.createSVGTransformFromMatrix(matrix));
421 | }
422 | });
423 |
424 | // SVGAnimated (used for SVGAnimatedBoolean, etc.)
425 |
426 | dom.SVGAnimated = function() {};
427 |
428 | dom.SVGAnimated.defineAnimated = function(classToAnimate, readonly) {
429 | var fromString = classToAnimate.fromString || classToAnimate;
430 | var animatedClass = function() {};
431 | mico.extend(animatedClass.prototype, dom.SVGAnimated.prototype);
432 | if (!readonly)
433 | animatedClass.prototype.__defineSetter__('baseVal',
434 | function(value) { this._baseVal = value; });
435 | animatedClass.fromString = function(s) {
436 | var object = new animatedClass;
437 | object._baseVal = fromString(s);
438 | return object;
439 | };
440 | return animatedClass;
441 | };
442 |
443 | mico.extend(dom.SVGAnimated.prototype, {
444 | get baseVal() { return this._baseVal; },
445 | // TODO this isn't correct...
446 | get animVal() { return this._baseVal; },
447 | toString: function() { return this._baseVal.toString(); }
448 | });
449 |
450 | // TODO will Boolean correctly parse the value strings? Probably not...
451 | dom.SVGAnimatedBoolean = dom.SVGAnimated.defineAnimated(Boolean);
452 | dom.SVGAnimatedNumber = dom.SVGAnimated.defineAnimated(dom.SVGNumber);
453 | dom.SVGAnimatedEnumeration = dom.SVGAnimated.defineAnimated(parseInt);
454 | dom.SVGAnimatedLength = dom.SVGAnimated.defineAnimated(dom.SVGLength, true);
455 | dom.SVGAnimatedString = dom.SVGAnimated.defineAnimated(String);
456 | dom.SVGAnimatedNumberList =
457 | dom.SVGAnimated.defineAnimated(dom.SVGNumberList, true);
458 | dom.SVGAnimatedLengthList =
459 | dom.SVGAnimated.defineAnimated(dom.SVGLengthList, true);
460 | dom.SVGAnimatedTransformList =
461 | dom.SVGAnimated.defineAnimated(dom.SVGTransformList, true);
462 |
463 | // SVGLocatable
464 |
465 | dom.SVGLocatable = function() {};
466 | mico.extend(dom.SVGLocatable.prototype, {
467 | get nearestViewportElement() { mico.TODO(); },
468 | get farthestViewportElement() { mico.TODO(); },
469 | getBBox: function() { mico.TODO(); },
470 | getCTM: function() { mico.TODO(); },
471 | getScreenCTM: function() { mico.TODO(); },
472 |
473 | // FIXME this is broken, I'm pretty sure
474 | getTransformToElement: function(element) {
475 | var matrix;
476 |
477 | if (this === element)
478 | return new dom.SVGMatrix;
479 | if (this.parentNode && this.parentNode.getTransformToElement)
480 | matrix = this.parentNode.getTransformToElement(element);
481 | else
482 | matrix = new dom.SVGMatrix;
483 |
484 | if (this.hasAttribute('transform') &&
485 | this.transform.baseVal.numberOfItems) {
486 | var list = new dom.SVGTransformList;
487 | list._items = this.transform.baseVal._items.concat();
488 | // TODO which is right?
489 | matrix = matrix.multiply(list.consolidate().matrix.inverse());
490 | //matrix = list.consolidate().matrix.inverse().multiply(matrix);
491 | }
492 |
493 | return matrix;
494 | },
495 | });
496 |
497 | // dom.SVGTransformable
498 |
499 | dom.SVGTransformable = function() { dom.SVGLocatable.call(this); };
500 | mico.extend(dom.SVGTransformable.prototype, dom.SVGLocatable.prototype);
501 | dom.Element.defineAttributes(dom.SVGTransformable,
502 | {name:'transform', type:dom.SVGAnimatedTransformList, readonly:true});
503 |
504 | // SVGStylable
505 |
506 | dom.SVGStylable = function() {};
507 | dom.Element.defineAttributes(dom.SVGStylable,
508 | {name:'className', type:dom.SVGAnimatedString, readonly:true, xmlName:'class'},
509 | {name:'style', type:dom.CSSStyleDeclaration, readonly:true});
510 | dom.SVGStylable.prototype.getPresentationAttribute = function(name) { mico.TODO(); };
511 |
512 | // SVGAnimatedPoints
513 |
514 | dom.SVGAnimatedPoints = function() {};
515 | dom.Element.defineAttributes(dom.SVGAnimatedPoints,
516 | {name:'points', type:dom.SVGPointList, readonly:true});
517 |
518 | mico.extend(dom.SVGAnimatedPoints.prototype, {
519 | get animatedPoints() { return this.points; } // TODO not correct...
520 | });
521 |
522 | // SVGElement
523 |
524 | dom.SVGElement = function() { dom.Element.call(this); };
525 | mico.extend(dom.SVGElement.prototype, dom.Element.prototype);
526 | dom.Element.factories['http://www.w3.org/2000/svg'] =
527 | dom.SVGElement.factory = {};
528 |
529 | dom.SVGElement.defineElement = function(name, parents) {
530 | (parents = parents || []).unshift(this);
531 | var element = function() {
532 | var _this = this;
533 | parents.forEach(function(parent) { parent.call(_this); });
534 | };
535 | element.attributeSpecs = {};
536 | parents.forEach(function(parent) {
537 | mico.extend(element.attributeSpecs, parent.attributeSpecs);
538 | mico.extend(element.prototype, parent.prototype);
539 | });
540 | dom.Element.defineAttributes.apply(dom.Element,
541 | [element].concat(Array.prototype.slice.call(arguments, 2)));
542 | if (name) {
543 | this.factory[name] = element;
544 | element.tagName = 'http://www.w3.org/2000/svg:' + name;
545 | }
546 | return element;
547 | };
548 |
549 | dom.Element.defineAttributes(dom.SVGElement, 'id', 'xmlbase');
550 |
551 | mico.extend(dom.SVGElement.prototype, {
552 | get ownerSVGElement() {
553 | return dom.Node.findAncestor(this, function(p) {
554 | return p.nodeName == 'svg';
555 | });
556 | },
557 | get viewpointElement() { mico.TODO(); },
558 | });
559 |
560 | // SVGSVGElement
561 |
562 | dom.SVGSVGElement = dom.SVGElement.defineElement('svg',
563 | [dom.SVGLocatable, dom.SVGStylable],
564 | {name:'x', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'0'},
565 | {name:'y', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'0'},
566 | {name:'width', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'100%'},
567 | {name:'height', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'100%'});
568 |
569 | mico.extend(dom.SVGSVGElement.prototype, {
570 | createSVGMatrix: function() { return new dom.SVGMatrix; },
571 | createSVGTransform: function() { return new dom.SVGTransform; }
572 | });
573 |
574 | // SVGDefsElement
575 |
576 | dom.SVGDefsElement = dom.SVGElement.defineElement('defs',
577 | [dom.SVGTransformable, dom.SVGStylable]);
578 |
579 | // SVGEllipseElement
580 |
581 | dom.SVGEllipseElement = dom.SVGElement.defineElement('ellipse',
582 | [dom.SVGTransformable, dom.SVGStylable],
583 | {name:'cx', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'0'},
584 | {name:'cy', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'0'},
585 | {name:'rx', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'0'},
586 | {name:'ry', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'0'});
587 |
588 | // SVGGElement
589 |
590 | dom.SVGGElement = dom.SVGElement.defineElement('g',
591 | [dom.SVGTransformable, dom.SVGStylable]);
592 |
593 | // SVGGradientElement
594 |
595 | dom.SVGGradientElement = dom.SVGElement.defineElement(null,
596 | [dom.SVGStylable],
597 | {name:'gradientUnits', type:dom.SVGAnimatedEnumeration, readonly:true},
598 | {name:'gradientTransform', type:dom.SVGAnimatedTransformList, readonly:true},
599 | {name:'spreadMethod', type:dom.SVGAnimatedEnumeration, readonly:true});
600 |
601 | mico.extend(dom.SVGGradientElement, {
602 | SVG_SPREADMETHOD_UNKNOWN: 0,
603 | SVG_SPREADMETHOD_PAD: 1,
604 | SVG_SPREADMETHOD_REFLECT: 2,
605 | SVG_SPREADMETHOD_REPEAT: 3
606 | });
607 |
608 | // SVGLinearGradientElement
609 |
610 | dom.SVGLinearGradientElement = dom.SVGElement.defineElement('linearGradient',
611 | [dom.SVGGradientElement],
612 | {name:'x1', type:dom.SVGAnimatedLength, readonly:true, defaultValue: '0%'},
613 | {name:'y1', type:dom.SVGAnimatedLength, readonly:true, defaultValue: '0%'},
614 | {name:'x2', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'100%'},
615 | {name:'y2', type:dom.SVGAnimatedLength, readonly:true, defaultValue: '0%'});
616 |
617 | // SVGRadialGradientElement
618 |
619 | dom.SVGRadialGradientElement = dom.SVGElement.defineElement('radialGradient',
620 | [dom.SVGGradientElement],
621 | {name:'cx', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'50%'},
622 | {name:'cy', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'50%'},
623 | {name: 'r', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'50%'},
624 | {name:'fx', type:dom.SVGAnimatedLength, readonly:true},
625 | {name:'fy', type:dom.SVGAnimatedLength, readonly:true});
626 |
627 | // SVGPolygonElement
628 |
629 | dom.SVGPolygonElement = dom.SVGElement.defineElement('polygon',
630 | [dom.SVGAnimatedPoints, dom.SVGTransformable, dom.SVGStylable]);
631 |
632 | // SVGPolylineElement
633 |
634 | dom.SVGPolylineElement = dom.SVGElement.defineElement('polyline',
635 | [dom.SVGAnimatedPoints, dom.SVGTransformable, dom.SVGStylable]);
636 |
637 | // SVGRectElement
638 |
639 | dom.SVGRectElement = dom.SVGElement.defineElement('rect',
640 | [dom.SVGTransformable, dom.SVGStylable],
641 | {name: 'x', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'0'},
642 | {name: 'y', type:dom.SVGAnimatedLength, readonly:true, defaultValue:'0'},
643 | {name: 'width', type:dom.SVGAnimatedLength, readonly:true},
644 | {name:'height', type:dom.SVGAnimatedLength, readonly:true},
645 | {name: 'rx', type:dom.SVGAnimatedLength, readonly:true},
646 | {name: 'ry', type:dom.SVGAnimatedLength, readonly:true});
647 |
648 | // SVGStopElement
649 |
650 | dom.SVGStopElement = dom.SVGElement.defineElement('stop',
651 | [dom.SVGStylable],
652 | {name:'offset', type:dom.SVGAnimatedNumber, readonly:true});
653 |
654 | // SVGTextContentElement
655 |
656 | dom.SVGTextContentElement = dom.SVGElement.defineElement(null,
657 | [dom.SVGStylable],
658 | {name:'textLength', type:dom.SVGAnimatedLength, readonly:true},
659 | {name:'lengthAdjust', type:dom.SVGAnimatedEnumeration, readonly:true});
660 |
661 | mico.extend(dom.SVGTextContentElement, {
662 | LENGTHADJUST_UNKNOWN: 0,
663 | LENGTHADJUST_SPACING: 1,
664 | LENGTHADJUST_SPACINGANDGLYPHS: 2
665 | });
666 |
667 | mico.extend(dom.SVGTextContentElement.prototype, {
668 | getNumberOfChars: function () { mico.TODO(); },
669 | getComputedTextLength: function() { mico.TODO(); },
670 | getSubStringLength: function(charnum, nchars) { mico.TODO(); },
671 | getStartPositionOfChar: function(charnum) { mico.TODO(); },
672 | getEndPositionOfChar: function(charnum) { mico.TODO(); },
673 | getExtentOfChar: function(charnum) { mico.TODO(); },
674 | getRotationOfChar: function(charnum) { mico.TODO(); },
675 | getCharNumAtPosition: function(pot) { mico.TODO(); },
676 | selectSubString: function(charnum, nchars) { mico.TODO(); },
677 | });
678 |
679 | // SVGTextPositioningElement
680 |
681 | dom.SVGTextPositioningElement = dom.SVGElement.defineElement(null,
682 | [dom.SVGTextContentElement],
683 | {name:'x', type:dom.SVGAnimatedLengthList, readonly:true},
684 | {name:'y', type:dom.SVGAnimatedLengthList, readonly:true},
685 | {name:'dx', type:dom.SVGAnimatedLengthList, readonly:true},
686 | {name:'dy', type:dom.SVGAnimatedLengthList, readonly:true},
687 | {name:'rotate', type:dom.SVGAnimatedNumberList, readonly:true});
688 |
689 | // SVGTextElement
690 |
691 | dom.SVGTextElement = dom.SVGElement.defineElement('text',
692 | [dom.SVGTextPositioningElement, dom.SVGTransformable]);
693 |
694 | // SVGTSpanElement
695 |
696 | dom.SVGTSpanElement = dom.SVGElement.defineElement('tspan',
697 | [dom.SVGTextPositioningElement]);
698 |
--------------------------------------------------------------------------------
/gezira/Makefile-gcc:
--------------------------------------------------------------------------------
1 | include ../config-gcc.mk
2 |
3 | spidermonkey$(COMPILED_PACKAGE_EXT): spidermonkey.generated.c $(MAKEFILE_LIST)
4 | gcc $< $(CFLAGS) -shared -lgezira -o $@
5 |
6 | spidermonkey.generated.c: generate-bindings spidermonkey.head.c
7 | ./generate-bindings > $@
8 |
9 | clean:
10 | /bin/rm -f *$(COMPILED_PACKAGE_EXT) spidermonkey.generated.c
11 |
--------------------------------------------------------------------------------
/gezira/generate-bindings:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # This file is part of the Mico project, available under the MIT license:
3 | #
4 | # http://www.opensource.org/licenses/mit-license.php
5 | #
6 |
7 | class String
8 | def camelize
9 | to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.
10 | gsub(/(?:^|_)(.)/) { $1.upcase }
11 | end
12 | end
13 |
14 | module Gezira; end
15 |
16 | class Gezira::StreamClass
17 | attr_reader :js_name
18 |
19 | def initialize(name, attributes)
20 | @name = name
21 | @attributes = attributes
22 | @qualified_name = 'gezira_' + name
23 | @js_name = name.camelize
24 | end
25 |
26 | def generate_binding
27 | generate_constructor
28 | generate_setter_function
29 | generate_js_class
30 | generate_property_specs
31 | generate_functions
32 | end
33 |
34 | def generate_constructor
35 | puts <<-END
36 | static JSBool
37 | #{@js_name}_constructor (JSContext *cxt, JSObject *obj, uintN argc,
38 | jsval *argv, jsval *rval)
39 | {
40 | double d;
41 | #{@qualified_name} *stream =
42 | (#{@qualified_name} *) malloc (sizeof (#{@qualified_name}));
43 | stream->type = #{@qualified_name}_type;
44 | stream->n = 0;
45 | stream->elements = 0;
46 | JS_SetPrivate (cxt, obj, stream);
47 | END
48 |
49 | generate_constructor_body
50 |
51 | puts <<-END
52 | *rval = OBJECT_TO_JSVAL (obj);
53 | return JS_TRUE;
54 | }
55 | END
56 | end
57 |
58 | def generate_setter_function
59 | return if @attributes.empty?
60 |
61 | puts <<-END
62 | static JSBool
63 | #{@js_name}_setter (JSContext *cxt, JSObject *obj,
64 | jsval id, jsval *vp)
65 | {
66 | double d;
67 | #{@qualified_name} *stream =
68 | (#{@qualified_name} *) JS_GetPrivate (cxt, obj);
69 | JS_ValueToNumber (cxt, *vp, &d);
70 | switch (JSVAL_TO_INT (id)) {
71 | END
72 |
73 | @attributes.each_with_index do |a, i|
74 | puts "case #{i}: stream->#{a} = GEZIRA_REAL (d); break;"
75 | end
76 |
77 | puts <<-END
78 | }
79 | return JS_TRUE;
80 | }
81 | END
82 | end
83 |
84 | def generate_js_class
85 | puts <<-END
86 | static JSClass #{@js_name}_class = {
87 | "#{@js_name}", JSCLASS_HAS_PRIVATE,
88 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
89 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
90 | JSCLASS_NO_OPTIONAL_MEMBERS
91 | };
92 | END
93 | end
94 |
95 | def generate_property_specs
96 | puts "static JSPropertySpec #{@js_name}_property_specs[] = {"
97 | @attributes.each_with_index do |a, i|
98 | puts "{\"#{a}\", #{i}, 0, NULL, #{@js_name}_setter},"
99 | end
100 | puts "{0, 0, 0, 0, 0}};"
101 | end
102 |
103 | def generate_functions
104 | puts <<-END
105 | static JSFunctionSpec #{@js_name}_functions[] = {
106 | {NULL, NULL, 0, 0, 0}
107 | };
108 | END
109 | end
110 | end
111 |
112 | class Gezira::NodeClass < Gezira::StreamClass
113 | def generate_constructor_body
114 | @attributes.each_with_index do |a, i|
115 | puts <<-END
116 | JS_ValueToNumber (cxt, argv[#{i}], &d);
117 | stream->#{a} = GEZIRA_REAL (d);
118 | END
119 | end
120 | end
121 |
122 | def generate_functions
123 | puts <<-END
124 | static JSFunctionSpec #{@js_name}_functions[] = {
125 | {"children", Node_set_children, 0, 0, 0},
126 | {NULL, NULL, 0, 0, 0}
127 | };
128 | END
129 | end
130 | def constructor_argc; @attributes.size end
131 | end
132 |
133 | class Gezira::RealStreamClass < Gezira::StreamClass
134 | def constructor_argc; 1 end
135 | def generate_constructor_body
136 | puts "create_real_elements (cxt, argv[0], &stream->elements, &stream->n);"
137 | end
138 | end
139 |
140 | print File::read('spidermonkey.head.c')
141 |
142 | CLASS_DECLS = [
143 | [Gezira::RealStreamClass, 'line_stream'],
144 | [Gezira::RealStreamClass, 'bezier3_stream'],
145 | [Gezira::NodeClass, 'node'],
146 | [Gezira::NodeClass, 'paint_node'],
147 | [Gezira::NodeClass, 'color_over_node',
148 | 'a', 'r', 'g', 'b'],
149 | [Gezira::NodeClass, 'clip_node',
150 | 'xmin', 'ymin', 'xmax', 'ymax'],
151 | [Gezira::NodeClass, 'color_background_node',
152 | 'r', 'g', 'b'],
153 | [Gezira::NodeClass, 'transformation_node',
154 | 'a', 'b', 'c', 'd', 'e', 'f'],
155 | [Gezira::NodeClass, 'translation_node',
156 | 'x', 'y'],
157 | [Gezira::NodeClass, 'rotation_node',
158 | 'angle'],
159 | [Gezira::NodeClass, 'scale_node',
160 | 'x', 'y']
161 | ]
162 |
163 | classes = CLASS_DECLS.map do |c|
164 | c[0].new(c[1], c[2..-1])
165 | end
166 |
167 | classes.each do |c| c.generate_binding end
168 |
169 | # TODO must keep argc in sync! Major bug warning!
170 | classes << Struct.new(:js_name, :constructor_argc).new('Renderer', 5)
171 |
172 | puts <<-END
173 | void
174 | initialize (JSContext *cxt)
175 | {
176 | JSObject *gezira;
177 |
178 | gezira = JS_DefineObject (cxt, JS_GetGlobalObject (cxt),
179 | "gezira", NULL, NULL, 0);
180 | END
181 |
182 | classes.each do |c|
183 | puts <<-END
184 | JS_InitClass (cxt, gezira, NULL, {c.js_name}_class,
185 | #{c.js_name}_constructor, #{c.constructor_argc},
186 | #{c.js_name}_property_specs, #{c.js_name}_functions,
187 | NULL, NULL);
188 | END
189 | end
190 |
191 | puts "\n}"
192 |
--------------------------------------------------------------------------------
/gezira/html.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | // TODO move HTML stuff out of svg.js into here
7 |
--------------------------------------------------------------------------------
/gezira/spidermonkey.head.c:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | #include
7 | #include "jsapi.h"
8 | #include "gezira.h"
9 |
10 | static JSBool
11 | Renderer_constructor (JSContext *cxt, JSObject *obj, uintN argc,
12 | jsval *argv, jsval *rval)
13 | {
14 | double d;
15 | gezira_renderer *renderer =
16 | (gezira_renderer *) malloc (sizeof (gezira_renderer));
17 | JS_SetPrivate (cxt, obj, renderer);
18 | renderer->router = gezira_renderer_router;
19 | /* FIXME hackety hack hack */
20 | JS_ValueToNumber (cxt, argv[0], &d);
21 | renderer->pixmap.pixels = *((char **) (char *) &d);
22 | renderer->pixmap.width = JSVAL_TO_INT (argv[1]);
23 | renderer->pixmap.height = JSVAL_TO_INT (argv[2]);
24 | renderer->pixmap.stride = JSVAL_TO_INT (argv[3]);
25 | renderer->pixmap.bytes_per_pixel = JSVAL_TO_INT (argv[4]);
26 |
27 | *rval = OBJECT_TO_JSVAL (obj);
28 | return JS_TRUE;
29 | }
30 |
31 | static JSBool
32 | Renderer_render (JSContext *cxt, JSObject *obj, uintN argc,
33 | jsval *argv, jsval *rval)
34 | {
35 | gezira_renderer *renderer =
36 | (gezira_renderer *) JS_GetPrivate (cxt, obj);
37 | gezira_stream *stream =
38 | (gezira_stream *) JS_GetPrivate (cxt, JSVAL_TO_OBJECT (argv[0]));
39 |
40 | gezira_render (renderer, stream);
41 |
42 | return JS_TRUE;
43 | }
44 |
45 | static JSClass Renderer_class = {
46 | "Renderer", JSCLASS_HAS_PRIVATE,
47 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
48 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
49 | JSCLASS_NO_OPTIONAL_MEMBERS
50 | };
51 |
52 | static JSPropertySpec Renderer_property_specs[] = {{0, 0, 0, 0, 0}};
53 | static JSFunctionSpec Renderer_functions[] = {
54 | {"render", Renderer_render, 1, 0, 0},
55 | {NULL, NULL, 0, 0, 0}
56 | };
57 |
58 | static JSBool
59 | Node_set_children (JSContext *cxt, JSObject *obj, uintN argc,
60 | jsval *argv, jsval *rval)
61 | {
62 | int i;
63 | jsval val;
64 | gezira_node *node = (gezira_node *) JS_GetPrivate (cxt, obj);
65 | JSObject *array;
66 |
67 | if (argc && JS_IsArrayObject (cxt, JSVAL_TO_OBJECT (argv[0])))
68 | array = JSVAL_TO_OBJECT (argv[0]);
69 | else
70 | array = JS_NewArrayObject (cxt, argc, argv);
71 |
72 | JS_GetArrayLength (cxt, array, &node->n);
73 | free (node->elements);
74 | node->elements = (gezira_stream **)
75 | malloc (node->n * sizeof (gezira_stream *));
76 |
77 | for (i = 0; i < node->n; i++) {
78 | JS_GetElement (cxt, array, i, &val);
79 | node->elements[i] = (gezira_stream *)
80 | JS_GetPrivate (cxt, JSVAL_TO_OBJECT (val));
81 | }
82 |
83 | /* Need references to children to keep GC happy */
84 | val = OBJECT_TO_JSVAL (array);
85 | JS_SetProperty (cxt, obj, "_children", &val);
86 |
87 | *rval = OBJECT_TO_JSVAL (obj);
88 | return JS_TRUE;
89 | }
90 |
91 | static void
92 | create_real_elements (JSContext *cxt, jsval val, gezira_real **elements, int *n)
93 | {
94 | int i;
95 | JSObject *array = JSVAL_TO_OBJECT (val);
96 |
97 | JS_GetArrayLength (cxt, array, (jsuint *) n);
98 | *elements = (gezira_real *) malloc (*n * sizeof (gezira_real));
99 | //printf ("at creation, elements is %p\n", *elements);
100 |
101 | for (i = 0; i < *n; i++) {
102 | double d;
103 | jsval val;
104 |
105 | JS_GetElement (cxt, array, i, &val);
106 | JS_ValueToNumber (cxt, val, &d);
107 | (*elements)[i] = GEZIRA_REAL (d);
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/gezira/stubs.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | // FIXME this file is out of date
7 |
8 | var gezira = {};
9 |
10 | (function(stub) {
11 | ['paint', 'free', 'Object', 'Parent', 'Background', 'ColorOver', 'Composite',
12 | 'Group', 'Lines', 'Bezier3s', 'Translation', 'Scale',
13 | 'Group.prototype.children', 'Parent.prototype.child'].forEach(function(n) {
14 | eval('gezira.' + n + ' = stub;');
15 | });
16 | })(function(){});
17 |
--------------------------------------------------------------------------------
/gezira/svg.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | mico.require('dom.html');
7 | mico.require('dom.svg');
8 | mico.require('dom.graphics');
9 | mico.require('gezira');
10 | mico.require('gezira_glyphserver');
11 |
12 | dom.graphics.renderers[null] = function(element) {
13 | if (!element._graphics)
14 | element._graphics = new gezira.Node;
15 | return element._graphics;
16 | };
17 |
18 | dom.graphics.renderers[dom.HTMLHtmlElement.tagName] =
19 | dom.graphics.renderers[dom.HTMLBodyElement.tagName] =
20 | dom.graphics.renderers[dom.SVGSVGElement.tagName] =
21 | dom.graphics.renderers[dom.SVGGElement.tagName] = function(element) {
22 | if (!element._graphics)
23 | element._graphics = new gezira.Node;
24 | var gobj = (new gezira.Node).children(
25 | element.childNodes._nodes.map(function(child) {
26 | return child._graphics || dom.graphics.render(child);
27 | })
28 | );
29 | if (element.hasAttribute('transform')) {
30 | var list = element.transform.baseVal;
31 | for (var i = list.numberOfItems - 1; i >= 0; i--) {
32 | var gtransform;
33 | var transform = list.getItem(i);
34 | if (transform.type == dom.SVGTransform.SVG_TRANSFORM_TRANSLATE)
35 | gtransform = new gezira.TranslationNode(transform.matrix.e, transform.matrix.f);
36 | else if (transform.type == dom.SVGTransform.SVG_TRANSFORM_SCALE)
37 | gtransform = new gezira.ScaleNode(transform.matrix.a, transform.matrix.d);
38 | else if (transform.type == dom.SVGTransform.SVG_TRANSFORM_ROTATE)
39 | gtransform = new gezira.RotationNode(transform.angle);
40 | else
41 | continue;
42 | gobj = gtransform.children(gobj);
43 | }
44 | }
45 | return element._graphics.children(gobj);
46 | };
47 |
48 | dom.graphics.renderers[dom.SVGRectElement.tagName] = function(element) {
49 | if (!element._graphics)
50 | element._graphics = new gezira.Node;
51 | var fill = element.getAttribute('fill');
52 | var match = fill && fill.match(/rgb\((\d+),(\d+),(\d+)\)/);
53 | if (match) {
54 | var r = Number(match[1]) / 255;
55 | var g = Number(match[2]) / 255;
56 | var b = Number(match[3]) / 255;
57 | var x1 = element.x.baseVal.value;
58 | var y1 = element.y.baseVal.value;
59 | var x2 = x1 + element.width.baseVal.value
60 | var y2 = y1 + element.height.baseVal.value
61 | element._graphics.children(
62 | (new gezira.ColorOverNode(1, r, g, b)).children(
63 | (new gezira.PaintNode).children(
64 | new gezira.LineStream([x1, y1, x1, y2, x1, y2, x2, y2,
65 | x2, y2, x2, y1, x2, y1, x1, y1]))));
66 | }
67 | else
68 | element._graphics.children(new gezira.Node);
69 |
70 | return element._graphics;
71 | };
72 |
73 | dom.graphics.renderers[dom.SVGPolygonElement.tagName] = function(element) {
74 | if (!element._graphics)
75 | element._graphics = new gezira.Node;
76 | var fill = element.getAttribute('fill');
77 | var match = fill && fill.match(/rgb\((\d+),(\d+),(\d+)\)/);
78 | if (match) {
79 | var r = Number(match[1]) / 255;
80 | var g = Number(match[2]) / 255;
81 | var b = Number(match[3]) / 255;
82 | var points = element.points;
83 | var lines = new Array(points.numberOfItems * 4);
84 | lines[0] = lines[lines.length - 2] = points.getItem(0).x;
85 | lines[1] = lines[lines.length - 1] = points.getItem(0).y;
86 | for (var p_i = 1, l_i = 2; p_i < points.numberOfItems; p_i++, l_i += 4) {
87 | lines[l_i ] = lines[l_i + 2] = points.getItem(p_i).x;
88 | lines[l_i + 1] = lines[l_i + 3] = points.getItem(p_i).y;
89 | }
90 | element._graphics.children(
91 | (new gezira.ColorOverNode(1, r, g, b)).children(
92 | (new gezira.PaintNode).children(
93 | new gezira.LineStream(lines))));
94 | }
95 | else
96 | element._graphics.children(new gezira.Node);
97 |
98 | return element._graphics;
99 | };
100 |
101 | dom.graphics.renderers[dom.SVGEllipseElement.tagName] = function(element) {
102 | if (!element._graphics)
103 | element._graphics = new gezira.Node;
104 | var fill = element.getAttribute('fill');
105 | var match = fill && fill.match(/rgb\((\d+),(\d+),(\d+)\)/);
106 | if (match) {
107 | var r = Number(match[1]) / 255;
108 | var g = Number(match[2]) / 255;
109 | var b = Number(match[3]) / 255;
110 | var cx = element.cx.baseVal.value;
111 | var cy = element.cy.baseVal.value;
112 | var rx = element.rx.baseVal.value;
113 | var ry = element.ry.baseVal.value;
114 | var a = 0.552284749830794;
115 | // TODO put radius into coords instead of using gezira.ScaleNode
116 | var beziers = [0, 1, a, 1, 1, a, 1, 0, 1, 0, 1, -a, a, -1, 0, -1, 0, -1,
117 | -a, -1, -1, -a, -1, 0, -1, 0, -1, a, -a, 1, 0, 1];
118 | element._graphics.children(
119 | (new gezira.TranslationNode(cx, cy)).children(
120 | (new gezira.ScaleNode(rx, ry).children(
121 | (new gezira.ColorOverNode(1, r, g, b)).children(
122 | (new gezira.PaintNode).children(
123 | new gezira.Bezier3Stream(beziers)))))));
124 | }
125 | else {
126 | element._graphics.children(new gezira.Node);
127 | }
128 |
129 | return element._graphics;
130 | };
131 |
132 | dom.graphics.renderers[dom.SVGTextElement.tagName] = function(element) {
133 | if (!element._graphics)
134 | element._graphics = new gezira.Node;
135 | var fill = element.getAttribute('fill');
136 | var match = fill && fill.match(/rgb\((\d+),(\d+),(\d+)\)/);
137 | if (match) {
138 | var r = Number(match[1]) / 255;
139 | var g = Number(match[2]) / 255;
140 | var b = Number(match[3]) / 255;
141 | element._graphics.children(
142 | (new gezira.ColorOverNode(1, r, g, b)).children(
143 | element.childNodes._nodes.map(function(child) {
144 | return child._graphics || dom.graphics.render(child);
145 | })));
146 | }
147 | else {
148 | element._graphics.children(new gezira.Node);
149 | }
150 |
151 | return element._graphics;
152 | };
153 |
154 | gezira._glyphserver = new gezira.Glyphserver;
155 |
156 | dom.graphics.renderers[dom.SVGTSpanElement.tagName] = function(element) {
157 | if (!element._graphics)
158 | element._graphics = new gezira.Node;
159 | var x = Number(element.getAttribute('x'));
160 | var y = Number(element.getAttribute('y'));
161 | element._graphics.children(
162 | (new gezira.TranslationNode(x, y)).children(
163 | (new gezira.ScaleNode(1, -1)).children(
164 | gezira._glyphserver.glyphs(element.firstChild.data))));
165 | return element._graphics;
166 | };
167 |
--------------------------------------------------------------------------------
/mico.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | var mico = {
7 | TODO: function() {
8 | var e = new Error;
9 | e.message = 'Not implemented yet:\n' + e.stack;
10 | throw e;
11 | },
12 |
13 | // FIXME In some JS environs (FF 3.0?) we have to delete the getter/setter
14 | // before we overwrite it (I think).
15 | extend: function(destination, source) {
16 | for (var property in source) {
17 | var getter = source.__lookupGetter__(property);
18 | if (getter)
19 | destination.__defineGetter__(property, getter);
20 | var setter = source.__lookupSetter__(property);
21 | if (setter)
22 | destination.__defineSetter__(property, setter);
23 | if (!getter && !setter)
24 | destination[property] = source[property];
25 | }
26 | return destination;
27 | },
28 |
29 | // TODO needs testing (and likely some fixing!)
30 | deepClone: function(original /*, blacklist */) {
31 | var clone;
32 | var blacklist = Array.prototype.slice.call(arguments, 1);
33 |
34 | if (original && typeof original == 'object') {
35 | if (original instanceof Array) {
36 | clone = new Array(original.length);
37 | for (var i = 0; i < original.length; i++)
38 | clone[i] = (original[i] && original[i].deepClone) ?
39 | original[i].deepClone() : this.deepClone(original[i]);
40 | }
41 | else {
42 | clone = {};
43 | clone.constructor = original.constructor;
44 | for (var p in original) {
45 | if (blacklist.indexOf(p) != -1)
46 | continue;
47 | var getter = original.__lookupGetter__(p);
48 | if (getter)
49 | clone.__defineGetter__(p, getter);
50 | var setter = original.__lookupSetter__(p);
51 | if (setter)
52 | clone.__defineSetter__(p, setter);
53 | if (!getter && !setter)
54 | clone[p] = (original[p] && original[p].deepClone) ?
55 | original[p].deepClone() : this.deepClone(original[p]);
56 | }
57 | }
58 | } else
59 | clone = original;
60 |
61 | return clone;
62 | },
63 |
64 | around: function(prototype, functions, advice) {
65 | [].concat(functions).forEach(function(f) {
66 | if (f[0] === '=') {
67 | f = f.slice(1);
68 | var proceed = prototype.__lookupGetter__(f);
69 | prototype.__defineGetter__(f, function() {
70 | return advice.call(this, proceed, arguments);
71 | });
72 | } else if (f[f.length - 1] === '=') {
73 | f = f.slice(0, f.length - 1);
74 | var proceed = prototype.__lookupSetter__(f);
75 | prototype.__defineSetter__(f, function() {
76 | return advice.call(this, proceed, arguments);
77 | });
78 | } else {
79 | var proceed = prototype[f];
80 | prototype[f] = function() {
81 | return advice.call(this, proceed, arguments);
82 | }
83 | }
84 | });
85 | },
86 |
87 | _loadedPackages: {},
88 | packageLocations: {},
89 |
90 | require: function() {
91 | Array.prototype.slice.call(arguments).forEach(function(name) {
92 | if (mico._loadedPackages[name])
93 | return;
94 |
95 | var location = mico.packageLocations[name];
96 | if (location) {
97 | if (!system.dlload(location) && !system.load(location))
98 | throw 'Unable to find package: ' + name + ' at location: ' + location;
99 | }
100 | else {
101 | // TODO add the concept of paths (or repositories) here
102 | // FIXME we're not platform independent here
103 | var absName = name.replace('.', '/');
104 | if (!system.dlload(absName + '/' + system.engine) &&
105 | !system.load(absName + '.js'))
106 | throw 'Unable to find package: ' + name;
107 | }
108 |
109 | mico._loadedPackages[name] = true;
110 | });
111 | }
112 | };
113 |
--------------------------------------------------------------------------------
/sdl/Makefile-gcc:
--------------------------------------------------------------------------------
1 | include ../config-gcc.mk
2 |
3 | spidermonkey$(COMPILED_PACKAGE_EXT): spidermonkey.c $(MAKEFILE_LIST)
4 | gcc $< $(CFLAGS) -shared -lSDL -o $@
5 |
6 | clean:
7 | /bin/rm -f *$(COMPILED_PACKAGE_EXT)
8 |
--------------------------------------------------------------------------------
/sdl/spidermonkey.c:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | #include "jsapi.h"
7 | #include "SDL/SDL.h"
8 |
9 | #define DEFAULT_WIDTH 600
10 | #define DEFAULT_HEIGHT 600
11 |
12 | #define DIE(s, ...) \
13 | do { \
14 | fprintf (stderr, "(%s:%4d) " s "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
15 | exit (1); \
16 | } while (0)
17 |
18 | #define ERROR_CHECK(f) \
19 | if (!(f)) DIE ("JS engine error")
20 |
21 | static unsigned int
22 | timer_callback (unsigned int interval, void *param)
23 | {
24 | SDL_Event event;
25 | event.type = SDL_USEREVENT;
26 | event.user.data1 = param;
27 | SDL_PushEvent (&event);
28 |
29 | return 0;
30 | }
31 |
32 | static JSBool
33 | sdl_addTimer (JSContext *context, JSObject *object, uintN argc,
34 | jsval *argv, jsval *rval)
35 | {
36 | int delay;
37 |
38 | if (!JS_ValueToInt32 (context, argv[0], &delay))
39 | return JS_FALSE;
40 | *rval = INT_TO_JSVAL (
41 | SDL_AddTimer (delay, timer_callback, (void *) argv[1]));
42 |
43 | return JS_TRUE;
44 | }
45 |
46 | static JSBool
47 | sdl_loop (JSContext *context, JSObject *object, uintN argc,
48 | jsval *argv, jsval *rval)
49 | {
50 | SDL_Event event;
51 | for (;;) {
52 | jsval callback, rval;
53 |
54 | SDL_WaitEvent (&event);
55 | /* TODO change this to a callback too? In that case we have
56 | * to provide a way (in JS) to break out of the loop.
57 | */
58 | if (event.type == SDL_QUIT)
59 | break;
60 | else if (event.type == SDL_USEREVENT) {
61 | JS_GetProperty (context, object, "ontimer", &callback);
62 | if (!JSVAL_IS_VOID (callback)) {
63 | JS_CallFunctionValue (context, object, callback, 1,
64 | (jsval *) (char *) &event.user.data1, &rval);
65 | }
66 | }
67 | /* TODO I think the shift mode should be queried, using a javascript
68 | * version of SDL_GetModState (with slightly different functionality)
69 | */
70 | else if (event.type == SDL_MOUSEMOTION) {
71 | void *mark;
72 | jsval *argv;
73 |
74 | JS_GetProperty (context, object, "onmousemove", &callback);
75 | if (!JSVAL_IS_VOID (callback)) {
76 | argv = JS_PushArguments (context, &mark, "cc",
77 | event.motion.x, event.motion.y);
78 | JS_CallFunctionValue (context, object, callback, 2, argv, &rval);
79 | JS_PopArguments (context, mark);
80 | }
81 | }
82 | else if (event.type == SDL_MOUSEBUTTONDOWN) {
83 | void *mark;
84 | jsval *argv;
85 |
86 | JS_GetProperty (context, object, "onmousedown", &callback);
87 | if (!JSVAL_IS_VOID (callback)) {
88 | argv = JS_PushArguments (context, &mark, "cc",
89 | event.button.x, event.button.y);
90 | JS_CallFunctionValue (context, object, callback, 2, argv, &rval);
91 | JS_PopArguments (context, mark);
92 | }
93 | }
94 | else if (event.type == SDL_MOUSEBUTTONUP) {
95 | void *mark;
96 | jsval *argv;
97 |
98 | JS_GetProperty (context, object, "onmouseup", &callback);
99 | if (!JSVAL_IS_VOID (callback)) {
100 | argv = JS_PushArguments (context, &mark, "cc",
101 | event.button.x, event.button.y);
102 | JS_CallFunctionValue (context, object, callback, 2, argv, &rval);
103 | JS_PopArguments (context, mark);
104 | }
105 | }
106 | }
107 | return JS_TRUE;
108 | }
109 |
110 | static JSBool
111 | sdl_altKeyDown (JSContext *context, JSObject *object,
112 | uintN argc, jsval *argv, jsval *rval)
113 | {
114 | *rval = SDL_GetModState () & KMOD_ALT ? JSVAL_TRUE : JSVAL_FALSE;
115 |
116 | return JS_TRUE;
117 | }
118 |
119 | static JSBool
120 | sdl_shiftKeyDown (JSContext *context, JSObject *object,
121 | uintN argc, jsval *argv, jsval *rval)
122 | {
123 | *rval = SDL_GetModState () & KMOD_SHIFT ? JSVAL_TRUE : JSVAL_FALSE;
124 |
125 | return JS_TRUE;
126 | }
127 |
128 | /* TODO this should probably go in a 'finalize' function */
129 | static JSBool
130 | sdl_quit (JSContext *context, JSObject *object,
131 | uintN argc, jsval *argv, jsval *rval)
132 | {
133 | SDL_Quit ();
134 |
135 | return JS_TRUE;
136 | }
137 |
138 | static JSBool
139 | sdl_videoSurface_get_width(JSContext *context, JSObject *object,
140 | jsval id, jsval *vp)
141 | {
142 | *vp = INT_TO_JSVAL (SDL_GetVideoSurface ()->w);
143 | return JS_TRUE;
144 | }
145 |
146 | static JSBool
147 | sdl_videoSurface_get_height(JSContext *context, JSObject *object,
148 | jsval id, jsval *vp)
149 | {
150 | *vp = INT_TO_JSVAL (SDL_GetVideoSurface ()->h);
151 | return JS_TRUE;
152 | }
153 |
154 | static JSBool
155 | sdl_videoSurface_get_bytesPerPixel(JSContext *context, JSObject *object,
156 | jsval id, jsval *vp)
157 | {
158 | *vp = INT_TO_JSVAL (SDL_GetVideoSurface ()->format->BytesPerPixel);
159 | return JS_TRUE;
160 | }
161 |
162 | static JSBool
163 | sdl_videoSurface_get_pitch(JSContext *context, JSObject *object,
164 | jsval id, jsval *vp)
165 | {
166 | *vp = INT_TO_JSVAL (SDL_GetVideoSurface ()->pitch);
167 | return JS_TRUE;
168 | }
169 |
170 | static JSBool
171 | sdl_videoSurface_get_pixels(JSContext *context, JSObject *object,
172 | jsval id, jsval *vp)
173 | {
174 | void *pixels = SDL_GetVideoSurface ()->pixels;
175 | /* FIXME hackety hack hack */
176 | JS_NewNumberValue (context, *((double *) (char *) &pixels), vp);
177 | return JS_TRUE;
178 | }
179 |
180 | static JSBool
181 | sdl_videoSurface_update (JSContext *context, JSObject *object,
182 | uintN argc, jsval *argv, jsval *rval)
183 | {
184 | SDL_UpdateRect (SDL_GetVideoSurface (), 0, 0, 0, 0);
185 | return JS_TRUE;
186 | }
187 |
188 | void
189 | initialize (JSContext *context)
190 | {
191 | JSObject *sdl, *videoSurface;
192 |
193 | static JSFunctionSpec sdl_functions[] = {
194 | {"quit", sdl_quit, 0, 0, 0},
195 | {"addTimer", sdl_addTimer, 2, 0, 0},
196 | {"loop", sdl_loop, 0, 0, 0},
197 | {"altKeyDown", sdl_altKeyDown, 0, 0, 0},
198 | {"shiftKeyDown", sdl_shiftKeyDown, 0, 0, 0},
199 | {NULL, NULL, 0, 0, 0}
200 | };
201 |
202 | if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1)
203 | DIE ("SDL_Init failed: %s", SDL_GetError ());
204 | if (!SDL_SetVideoMode (DEFAULT_WIDTH, DEFAULT_HEIGHT, 0,
205 | SDL_HWSURFACE | SDL_ANYFORMAT))
206 | DIE ("SDL_SetVideoMode failed: %s", SDL_GetError ());
207 | ERROR_CHECK(sdl = JS_DefineObject (context, JS_GetGlobalObject (context),
208 | "sdl", NULL, NULL, 0));
209 | ERROR_CHECK (JS_DefineFunctions (context, sdl, sdl_functions));
210 | ERROR_CHECK (videoSurface = JS_DefineObject (context, sdl, "videoSurface",
211 | NULL, NULL, 0));
212 |
213 | #define DEFINE_READ_ONLY_PROPERTY(object, name) \
214 | ERROR_CHECK ( \
215 | JS_DefineProperty (context, object, #name, JSVAL_NULL, \
216 | sdl_##object##_get_##name, NULL, \
217 | JSPROP_READONLY | JSPROP_PERMANENT))
218 |
219 | DEFINE_READ_ONLY_PROPERTY (videoSurface, width);
220 | DEFINE_READ_ONLY_PROPERTY (videoSurface, height);
221 | DEFINE_READ_ONLY_PROPERTY (videoSurface, bytesPerPixel);
222 | DEFINE_READ_ONLY_PROPERTY (videoSurface, pitch);
223 | DEFINE_READ_ONLY_PROPERTY (videoSurface, pixels);
224 | JS_DefineFunction (context, videoSurface, "update",
225 | sdl_videoSurface_update, 0, 0);
226 | /* FIXME hack for demo! Should pull out of title element... */
227 | SDL_WM_SetCaption ("Mico - Sun Labs Lively Kernel", "Mico");
228 | }
229 |
--------------------------------------------------------------------------------
/sdl/stubs.js:
--------------------------------------------------------------------------------
1 | var sdl = {
2 | addTimer: function(delay, i) {
3 | /* TODO add some recursion detection and allow for returns
4 | * back to the top level, then trigger timers again...?
5 | */
6 | sdl.ontimer(i);
7 | },
8 | loop: function() {}
9 | };
10 |
11 | sdl.videoSurface = {
12 | update: function() {}
13 | };
14 |
--------------------------------------------------------------------------------
/sdl/window.js:
--------------------------------------------------------------------------------
1 | mico.require('sdl');
2 | mico.require('dom.graphics');
3 | mico.require('dom.events');
4 |
5 | var window = this;
6 |
7 | mico.extend(window, {
8 | parent: window,
9 | alert: system.print,
10 | console: {log: system.print},
11 | navigator: {userAgent: "Mico"},
12 | _timers: [],
13 |
14 | setTimeout: function(action, delay) {
15 | var i = 0;
16 | while (window._timers[i])
17 | i++;
18 | var timer = window._timers[i] = {action: action};
19 | // NOTE timer may fire before the assignment occurs
20 | timer.id = sdl.addTimer(delay, i);
21 | return i;
22 | },
23 |
24 | dispatchEvent: function(event) {
25 | window.document && window.document.documentElement.dispatchEvent(event);
26 | window.refresh();
27 | },
28 |
29 | refresh: function() {
30 | dom.graphics.update();
31 | /* FIXME ugly hack */
32 | if (window._graphics)
33 | window._graphics.render(window.document._graphics);
34 | sdl.videoSurface.update();
35 | }
36 | });
37 |
38 | sdl.ontimer = function(i) {
39 | var timer = window._timers[i];
40 | if (timer) {
41 | window._timers[i] = null;
42 | timer.action.apply(window);
43 | window.refresh();
44 | }
45 | };
46 |
47 | sdl.onmousemove = function(x, y) {
48 | var event = new dom.MouseEvent;
49 | event._type = 'mousemove';
50 | event._shiftKey = sdl.shiftKeyDown ();
51 | event._altKey = sdl.altKeyDown ();
52 | event._clientX = x;
53 | event._clientY = y;
54 | window.dispatchEvent(event);
55 | };
56 |
57 | sdl.onmousedown = function(x, y) {
58 | var event = new dom.MouseEvent;
59 | event._type = 'mousedown';
60 | event._shiftKey = sdl.shiftKeyDown();
61 | event._altKey = sdl.altKeyDown();
62 | event._clientX = x;
63 | event._clientY = y;
64 | window.dispatchEvent(event);
65 | };
66 |
67 | sdl.onmouseup = function(x, y) {
68 | var event = new dom.MouseEvent;
69 | event._type = 'mouseup';
70 | event._shiftKey = sdl.shiftKeyDown();
71 | event._altKey = sdl.altKeyDown();
72 | event._clientX = x;
73 | event._clientY = y;
74 | window.dispatchEvent(event);
75 | };
76 |
--------------------------------------------------------------------------------
/system/Makefile-gcc:
--------------------------------------------------------------------------------
1 | include ../config-gcc.mk
2 |
3 | mico-spidermonkey: spidermonkey.c $(MAKEFILE_LIST)
4 | g++ $< $(CFLAGS) -rdynamic -Wl,-Bstatic -ljs -Wl,-Bdynamic -ldl -lm -o $@
5 |
6 | clean:
7 | /bin/rm -f mico-spidermonkey
8 |
--------------------------------------------------------------------------------
/system/browser.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | var system = {
7 | engine: 'browser',
8 |
9 | // TODO this goes away after open() gets implemented
10 | print: function(s) { console.log(s); },
11 |
12 | open: function(filename, action) {
13 | // TODO maybe do an XHR here?
14 | },
15 |
16 | dlload: function(filename) {},
17 |
18 | load: function(filename) {
19 | // TODO use script tag trick?
20 | }
21 | };
22 |
--------------------------------------------------------------------------------
/system/shell.js:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | var system = {
7 | engine: 'shell',
8 |
9 | // TODO this goes away after open() gets implemented
10 | print: function(s) { print(s); },
11 |
12 | open: function(filename, action) {
13 | // TODO readFile() or some common shell API?
14 | },
15 |
16 | dlload: function(filename) {},
17 |
18 | load: function() {
19 | Array.prototype.forEach.call(arguments, function (filename) {
20 | load(filename);
21 | });
22 | return true;
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/system/spidermonkey.c:
--------------------------------------------------------------------------------
1 | /* This file is part of the Mico project, available under the MIT license:
2 | *
3 | * http://www.opensource.org/licenses/mit-license.php
4 | */
5 |
6 | #include
7 | #include
8 | #include
9 | #include "jsapi.h"
10 |
11 | #define HEAP_SIZE_LIMIT (8 * 1024 * 1024)
12 | #define STACK_CHUNK_SIZE (8192)
13 | #define NCALLBACKS_BEFORE_WE_MAYBEGC (1000)
14 |
15 | #define DIE(s, ...) \
16 | do { \
17 | fprintf (stderr, "(%s:%4d) " s "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
18 | exit (1); \
19 | } while (0)
20 |
21 | #define ERROR_CHECK(f) \
22 | if (!(f)) DIE ("JS engine error")
23 |
24 | static JSBool
25 | branch_callback (JSContext *context, JSScript *script)
26 | {
27 | static int ncallbacks;
28 |
29 | if (ncallbacks == NCALLBACKS_BEFORE_WE_MAYBEGC) {
30 | JS_MaybeGC (context);
31 | ncallbacks = 0;
32 | }
33 | else
34 | ncallbacks++;
35 |
36 | return JS_TRUE;
37 | }
38 |
39 | static void
40 | error_reporter (JSContext *context, const char *message, JSErrorReport *report)
41 | {
42 | fprintf (stderr, "%s:%u:%s\n",
43 | report->filename ? report->filename : "",
44 | (unsigned int) report->lineno, message);
45 |
46 | exit (1);
47 | }
48 |
49 | static JSBool
50 | system_open (JSContext *context, JSObject *object,
51 | uintN argc, jsval *argv, jsval *rval)
52 | {
53 | /* TODO */
54 | return JS_TRUE;
55 | }
56 |
57 | /* TODO replace this (or implement it in JS) with system.open(1, ) */
58 | static JSBool
59 | system_print (JSContext *context, JSObject *object,
60 | uintN argc, jsval *argv, jsval *rval)
61 | {
62 | JSString *string;
63 |
64 | if (!(string = JS_ValueToString (context, argv[0])))
65 | return JS_FALSE;
66 | printf ("%s\n", JS_GetStringBytes (string));
67 |
68 | return JS_TRUE;
69 | }
70 |
71 | static JSBool
72 | system_dlload (JSContext *context, JSObject *object,
73 | uintN argc, jsval *argv, jsval *rval)
74 | {
75 | void *library;
76 | char *filename, *filename_with_ext;
77 | void (*initialize) (JSContext *context);
78 | JSString *string;
79 | unsigned int i;
80 |
81 | for (i = 0; i < argc; i++) {
82 | string = JS_ValueToString (context, argv[i]);
83 | if (!string)
84 | return JS_FALSE;
85 | filename = JS_GetStringBytes (string);
86 | filename_with_ext = (char *) malloc (strlen (filename) +
87 | strlen (COMPILED_PACKAGE_EXT) + 1);
88 | sprintf (filename_with_ext, "%s%s", filename, COMPILED_PACKAGE_EXT);
89 |
90 | /* TODO don't even try if file doesn't exist */
91 | library = dlopen (filename_with_ext, RTLD_LAZY | RTLD_GLOBAL);
92 | if (!library) {
93 | //printf ("dlopen error: %s\n", dlerror ());
94 | free (filename_with_ext);
95 | *rval = JSVAL_NULL;
96 | return JS_TRUE;
97 | }
98 | free (filename_with_ext);
99 |
100 | initialize = (void (*) (JSContext *)) dlsym (library, "initialize");
101 | if (!initialize) {
102 | *rval = JSVAL_NULL;
103 | return JS_TRUE;
104 | }
105 |
106 | initialize (context);
107 | }
108 |
109 | *rval = JSVAL_TRUE;
110 | return JS_TRUE;
111 | }
112 |
113 | static JSBool
114 | system_load (JSContext *context, JSObject *object,
115 | uintN argc, jsval *argv, jsval *rval)
116 | {
117 | JSScript *script;
118 | jsval val;
119 | JSObject *global;
120 | JSString *string;
121 | char *filename;
122 | FILE *f;
123 | unsigned int i;
124 |
125 | *rval = JSVAL_TRUE;
126 | global = JS_GetGlobalObject (context);
127 |
128 | for (i = 0; i < argc; i++) {
129 | string = JS_ValueToString (context, argv[i]);
130 | if (!string)
131 | return JS_FALSE;
132 | filename = JS_GetStringBytes (string);
133 |
134 | f = fopen (filename, "r");
135 | if (!f) {
136 | *rval = JSVAL_FALSE;
137 | break;
138 | }
139 | fclose (f);
140 |
141 | if (!(script = JS_CompileFile (context, global, filename))) {
142 | if (JS_IsExceptionPending (context)) {
143 | JS_ReportPendingException (context);
144 | /*
145 | jsval val;
146 | JS_GetPendingException (context, &val);
147 | JS_ThrowReportedError (context, "system.load failed",
148 | JS_ErrorFromException (context, val));
149 | */
150 | }
151 | *rval = JSVAL_FALSE;
152 | break;
153 | }
154 | if (!JS_ExecuteScript (context, global, script, &val) &&
155 | JS_IsExceptionPending (context))
156 | JS_ReportPendingException (context);
157 | JS_DestroyScript (context, script);
158 | }
159 |
160 | return JS_TRUE;
161 | }
162 |
163 | int
164 | main (int argc, const char *argv[])
165 | {
166 | JSRuntime *runtime;
167 | JSContext *context;
168 | JSObject *global, *system;
169 | JSScript *script;
170 | jsval val;
171 |
172 | JSClass global_class = {
173 | "global", JSCLASS_GLOBAL_FLAGS,
174 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
175 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
176 | JSCLASS_NO_OPTIONAL_MEMBERS
177 | };
178 |
179 | JSFunctionSpec system_functions[] = {
180 | {"print", system_print, 1, 0, 0},
181 | {"open", system_open, 2, 0, 0},
182 | {"dlload", system_dlload, 1, 0, 0},
183 | {"load", system_load, 1, 0, 0},
184 | {NULL, NULL, 0, 0, 0}
185 | };
186 |
187 | ERROR_CHECK (runtime = JS_NewRuntime (HEAP_SIZE_LIMIT));
188 | ERROR_CHECK (context = JS_NewContext (runtime, STACK_CHUNK_SIZE));
189 | JS_SetOptions (context, JSOPTION_VAROBJFIX);
190 | #ifdef JSOPTION_JIT
191 | JS_SetOptions (context, JSOPTION_JIT);
192 | #endif
193 | ERROR_CHECK (global = JS_NewObject (context, &global_class, NULL, NULL));
194 | ERROR_CHECK (JS_InitStandardClasses (context, global));
195 | ERROR_CHECK (system = JS_DefineObject (context, global, "system",
196 | NULL, NULL, 0));
197 | val = STRING_TO_JSVAL (JS_NewStringCopyZ (context, "spidermonkey"));
198 | ERROR_CHECK (JS_SetProperty (context, system, "engine", &val));
199 | ERROR_CHECK (JS_DefineFunctions (context, system, system_functions));
200 |
201 | JS_SetErrorReporter (context, error_reporter);
202 | JS_SetBranchCallback (context, branch_callback);
203 | if (argc > 1) {
204 | ERROR_CHECK (script = JS_CompileFile (context, global, argv[1]));
205 | ERROR_CHECK (JS_ExecuteScript (context, global, script, &val));
206 | JS_DestroyScript (context, script);
207 | }
208 |
209 | JS_DestroyContext (context);
210 | JS_DestroyRuntime (runtime);
211 | JS_ShutDown ();
212 |
213 | return 0;
214 | }
215 |
--------------------------------------------------------------------------------