on master help sign in
Merge remote-tracking branch 'evan/dev' into lint
acd2b61Colin Curtin
compiler/Gen/GenerateHtml.hs+2−2
@@ -44,7 +44,7 @@ generateHtml libLoc title source =
44 case buildFromSource True source of
44 case buildFromSource True source of
45 Right modul -> modulesToHtml Readable title libLoc [] True [modul]
45 Right modul -> modulesToHtml Readable title libLoc [] True [modul]
46 Left err -> createHtml Readable libLoc title (Right $ showErr err)
46 Left err -> createHtml Readable libLoc title (Right $ showErr err)
47− (H.noscript "") "Elm.Main"
47+ (H.noscript "") "Main"
48
48
49
49
50modulesToHtml jsStyle title libLoc jss nscrpt modules =
50modulesToHtml jsStyle title libLoc jss nscrpt modules =
@@ -71,5 +71,5 @@ createHtml jsStyle libLoc title js noscript moduleToLoad =
71 makeScript Readable (Left libLoc)
71 makeScript Readable (Left libLoc)
72 makeScript jsStyle js
72 makeScript jsStyle js
73 H.body $ do
73 H.body $ do
74− H.script ! A.type_ "text/javascript" $ preEscapedToMarkup ("Elm.init(Elm." ++ moduleToLoad ++ ")" :: String)
74+ H.script ! A.type_ "text/javascript" $ preEscapedToMarkup ("Elm.fullscreen(Elm." ++ moduleToLoad ++ ")" :: String)
75 H.noscript $ preEscapedToMarkup noscript
75 H.noscript $ preEscapedToMarkup noscript
libraries/Graphics/Collage.elm+6
@@ -46,6 +46,7 @@ defaultLine = {
46 miterLimit = 10
46 miterLimit = 10
47 }
47 }
48
48
49+-- default LineStyles
49solid : Color -> LineStyle
50solid : Color -> LineStyle
50solid clr = { defaultLine | color <- clr }
51solid clr = { defaultLine | color <- clr }
51dashed : Color -> LineStyle
52dashed : Color -> LineStyle
@@ -79,6 +80,11 @@ outlined style shape = form (FShape (Left style) shape)
79traced : LineStyle -> Path -> Form
80traced : LineStyle -> Path -> Form
80traced style path = form (FPath style path)
81traced style path = form (FPath style path)
81
82
83+-- Draw a sprite
84+-- w width
85+-- h height
86+-- pos (px,py) coordinates into the sprite map
87+-- src The location of the image
82sprite : Int -> Int -> (Int,Int) -> String -> Form
88sprite : Int -> Int -> (Int,Int) -> String -> Form
83sprite w h pos src = form (FImage w h pos src)
89sprite w h pos src = form (FImage w h pos src)
84
90
libraries/Json.elm+2−2
@@ -38,12 +38,12 @@ toJSString : Value -> JSString
38toJSString v = Native.toPrettyJSString "" v
38toJSString v = Native.toPrettyJSString "" v
39
39
40-- Parse a string representation of a proper JSON object into
40-- Parse a string representation of a proper JSON object into
41−-- its Elm's representation.
41+-- its Elm representation.
42fromString : String -> Maybe Value
42fromString : String -> Maybe Value
43fromString s = Native.fromJSString (JS.fromString s)
43fromString s = Native.fromJSString (JS.fromString s)
44
44
45-- Parse a JavaScript string representation of a proper JSON object into
45-- Parse a JavaScript string representation of a proper JSON object into
46−-- its Elm's representation.
46+-- its Elm representation.
47fromJSString : JSString -> Maybe Value
47fromJSString : JSString -> Maybe Value
48
48
49
49
libraries/Native/Show.js−19
@@ -13,24 +13,6 @@ Elm.Native.Show = function(elm) {
13 var Json = Elm.Json(elm);
13 var Json = Elm.Json(elm);
14 var Tuple2 = Elm.Native.Utils(elm).Tuple2;
14 var Tuple2 = Elm.Native.Utils(elm).Tuple2;
15
15
16− elm.node.addEventListener('log', function(e) { console.log(e.value); });
17− elm.node.addEventListener('title', function(e) {document.title = e.value;});
18− elm.node.addEventListener('redirect', function(e) {
19− if (e.value.length > 0) { window.location = e.value; }
20− });
21− elm.node.addEventListener('viewport', function(e) {
22− var node = document.getElementById('elm_viewport');
23− if (!node) {
24− node = document.createElement('meta');
25− node.id = 'elm_viewport';
26− node.name = 'viewport';
27− document.head.appendChild(node);
28− }
29− node.content = e.value;
30− Dispatcher.notify(elm.Window.dimensions.id,
31− Tuple2(elm.node.clientWidth, elm.node.clientHeight));
32− });
33−
34 var toString = function(v) {
16 var toString = function(v) {
35 if (typeof v === "function") {
17 if (typeof v === "function") {
36 var name = v.func ? v.func.name : v.name;
18 var name = v.func ? v.func.name : v.name;
@@ -44,7 +26,6 @@ Elm.Native.Show = function(elm) {
44 } else if (typeof v === "object" && '_' in v) {
26 } else if (typeof v === "object" && '_' in v) {
45 var output = [];
27 var output = [];
46 for (var k in v._) {
28 for (var k in v._) {
47− console.log(k,v._[k]);
48 for (var i = v._[k].length; i--; ) {
29 for (var i = v._[k].length; i--; ) {
49 output.push(k + " = " + toString(v._[k][i]));
30 output.push(k + " = " + toString(v._[k][i]));
50 }
31 }
libraries/Native/Signal/Keyboard.js+8−8
@@ -14,27 +14,27 @@ Elm.Native.Keyboard = function(elm) {
14 if (NList.member(e.keyCode)(keysDown.value)) return;
14 if (NList.member(e.keyCode)(keysDown.value)) return;
15 var list = NList.Cons(e.keyCode, keysDown.value);
15 var list = NList.Cons(e.keyCode, keysDown.value);
16 var hasListener = elm.notify(keysDown.id, list);
16 var hasListener = elm.notify(keysDown.id, list);
17− if (!hasListener) elm.node.removeEventListener('keydown', down);
17+ if (!hasListener) document.removeEventListener('keydown', down);
18 }
18 }
19 function up(e) {
19 function up(e) {
20 function notEq(kc) { return kc !== e.keyCode; }
20 function notEq(kc) { return kc !== e.keyCode; }
21 var codes = NList.filter(notEq)(keysDown.value);
21 var codes = NList.filter(notEq)(keysDown.value);
22 var hasListener = elm.notify(keysDown.id, codes);
22 var hasListener = elm.notify(keysDown.id, codes);
23− if (!hasListener) elm.node.removeEventListener('keyup', up);
23+ if (!hasListener) document.removeEventListener('keyup', up);
24 }
24 }
25 function blur(e) {
25 function blur(e) {
26 var hasListener = elm.notify(keysDown.id, NList.Nil);
26 var hasListener = elm.notify(keysDown.id, NList.Nil);
27− if (!hasListener) elm.node.removeEventListener('blur', blur);
27+ if (!hasListener) document.removeEventListener('blur', blur);
28 }
28 }
29 function press(e) {
29 function press(e) {
30 var hasListener = elm.notify(lastKey.id, e.charCode || e.keyCode);
30 var hasListener = elm.notify(lastKey.id, e.charCode || e.keyCode);
31− if (!hasListener) elm.node.removeEventListener('keypress', press);
31+ if (!hasListener) document.removeEventListener('keypress', press);
32 }
32 }
33
33
34− elm.node.addEventListener('keydown' , down );
34+ document.addEventListener('keydown' , down );
35− elm.node.addEventListener('keyup' , up );
35+ document.addEventListener('keyup' , up );
36− elm.node.addEventListener('blur' , blur );
36+ document.addEventListener('blur' , blur );
37− elm.node.addEventListener('keypress', press);
37+ document.addEventListener('keypress', press);
38
38
39 function keySignal(f) {
39 function keySignal(f) {
40 var signal = A2( Signal.lift, f, keysDown );
40 var signal = A2( Signal.lift, f, keysDown );
libraries/Native/Signal/Mouse.js+3−3
@@ -21,7 +21,7 @@ Elm.Native.Mouse = function(elm) {
21 var isDown = Signal.constant(false);
21 var isDown = Signal.constant(false);
22 var isClicked = Signal.constant(false);
22 var isClicked = Signal.constant(false);
23 var clicks = Signal.constant(Utils.Tuple0);
23 var clicks = Signal.constant(Utils.Tuple0);
24−
24+
25 function getXY(e) {
25 function getXY(e) {
26 var posx = 0;
26 var posx = 0;
27 var posy = 0;
27 var posy = 0;
@@ -35,10 +35,10 @@ Elm.Native.Mouse = function(elm) {
35 posy = e.clientY + document.body.scrollTop +
35 posy = e.clientY + document.body.scrollTop +
36 document.documentElement.scrollTop;
36 document.documentElement.scrollTop;
37 }
37 }
38− return Utils.Tuple2(posx, posy);
38+ return Utils.Tuple2(posx-elm.node.offsetX, posy-elm.node.offsetY);
39 }
39 }
40
40
41− var node = elm.node.tagName === "BODY" ? document : elm.node;
41+ var node = elm.display === ElmRuntime.Display.FULLSCREEN ? document : elm.node;
42
42
43 function click(e) {
43 function click(e) {
44 var hasListener1 = elm.notify(isClicked.id, true);
44 var hasListener1 = elm.notify(isClicked.id, true);
libraries/Native/Signal/Touch.js+12−8
@@ -7,6 +7,7 @@ Elm.Native.Touch = function(elm) {
7
7
8 var Signal = Elm.Signal(elm);
8 var Signal = Elm.Signal(elm);
9 var JS = Elm.JavaScript(elm);
9 var JS = Elm.JavaScript(elm);
10+ var _ = Elm.Native.Utils(elm);
10
11
11 function Dict() {
12 function Dict() {
12 this.keys = [];
13 this.keys = [];
@@ -44,24 +45,27 @@ Elm.Native.Touch = function(elm) {
44 var r = dict.lookup(t.identifier);
45 var r = dict.lookup(t.identifier);
45 return {_ : {},
46 return {_ : {},
46 id: t.identifier,
47 id: t.identifier,
47− x : t.pageX,
48+ x : t.pageX - elm.node.offsetX,
48− y : t.pageY,
49+ y : t.pageY - elm.node.offsetY,
49 x0: r.x,
50 x0: r.x,
50 y0: r.y,
51 y0: r.y,
51 t0: r.t
52 t0: r.t
52 };
53 };
53 }
54 }
54
55
55− var node = elm.node === document.body ? document : elm.node;
56+ var node = elm.display === ElmRuntime.Display.FULLSCREEN ? document : elm.node;
56
57
57 function start(e) {
58 function start(e) {
58− dict.insert(e.identifier,{x:e.pageX,y:e.pageY,t:Date.now()});
59+ dict.insert(e.identifier,
60+ {x: e.pageX - elm.node.offsetX,
61+ y: e.pageY - elm.node.offsetY,
62+ t: Date.now()});
59 }
63 }
60 function end(e) {
64 function end(e) {
61 var t = dict.remove(e.identifier);
65 var t = dict.remove(e.identifier);
62 if (Date.now() - t.t < tapTime) {
66 if (Date.now() - t.t < tapTime) {
63− hasTap = true;
67+ hasTap = true;
64− tap = {_:{}, x:t.x, y:t.y};
68+ tap = {_:{}, x:t.x, y:t.y};
65 }
69 }
66 }
70 }
67
71
@@ -87,8 +91,8 @@ Elm.Native.Touch = function(elm) {
87 function move(e) {
91 function move(e) {
88 for (var i = root.value.length; i--; ) {
92 for (var i = root.value.length; i--; ) {
89 if (root.value[i].id === mouseID) {
93 if (root.value[i].id === mouseID) {
90− root.value[i].x = e.pageX;
94+ root.value[i].x = e.pageX - elm.node.offsetX;
91− root.value[i].y = e.pageY;
95+ root.value[i].y = e.pageY - elm.node.offsetY;
92 elm.notify(root.id, root.value);
96 elm.notify(root.id, root.value);
93 break;
97 break;
94 }
98 }
libraries/Native/Signal/Window.js+9−5
@@ -10,7 +10,10 @@ Elm.Native.Window = function(elm) {
10
10
11 function getWidth() { return elm.node.clientWidth; }
11 function getWidth() { return elm.node.clientWidth; }
12 function getHeight() {
12 function getHeight() {
13+ if (elm.display === ElmRuntime.Display.FULLSCREEN) {
14+ return window.innerHeight;
15+ }
13− return document.body === elm.node ? window.innerHeight : elm.node.clientHeight;
16+ return elm.node.clientHeight;
14 }
17 }
15
18
16 var dimensions = Signal.constant(Tuple2(getWidth(), getHeight()));
19 var dimensions = Signal.constant(Tuple2(getWidth(), getHeight()));
@@ -24,19 +27,20 @@ Elm.Native.Window = function(elm) {
24 var height = A2(Signal.lift, function(p){return p._1;}, dimensions);
27 var height = A2(Signal.lift, function(p){return p._1;}, dimensions);
25 height.defaultNumberOfKids = 0;
28 height.defaultNumberOfKids = 0;
26
29
27− function resize(e) {
30+ function resizeIfNeeded() {
28 var w = getWidth();
31 var w = getWidth();
29 var h = getHeight();
32 var h = getHeight();
30 if (dimensions.value._0 === w && dimensions.value._1 === h) return;
33 if (dimensions.value._0 === w && dimensions.value._1 === h) return;
31 var hasListener = elm.notify(dimensions.id, Tuple2(w,h));
34 var hasListener = elm.notify(dimensions.id, Tuple2(w,h));
32− if (!hasListener) window.removeEventListener('resize', resize);
35+ if (!hasListener) window.removeEventListener('resize', resizeIfNeeded);
33 }
36 }
34− window.addEventListener('resize', resize);
37+ window.addEventListener('resize', resizeIfNeeded);
35
38
36 return elm.Native.Window = {
39 return elm.Native.Window = {
37 dimensions:dimensions,
40 dimensions:dimensions,
38 width:width,
41 width:width,
39− height:height
42+ height:height,
43+ resizeIfNeeded:resizeIfNeeded
40 };
44 };
41
45
42};
46};
libraries/Native/Utils.js+17
@@ -113,6 +113,23 @@ Elm.Native.Utils = function(elm) {
113 return Tuple2(w,h);
113 return Tuple2(w,h);
114 }
114 }
115
115
116+ function adjustOffset() {
117+ var node = elm.node;
118+ var offsetX = offsetY = 0;
119+ if (node.offsetParent) {
120+ do {
121+ offsetX += node.offsetLeft;
122+ offsetY += node.offsetTop;
123+ } while (node = node.offsetParent);
124+ }
125+ node.offsetX = offsetX;
126+ node.offsetY = offsetY;
127+ }
128+
129+ if (elm.display === ElmRuntime.Display.COMPONENT) {
130+ elm.node.addEventListener('mouseover', adjustOffset);
131+ }
132+
116 return elm.Native.Utils = {
133 return elm.Native.Utils = {
117 eq:eq,
134 eq:eq,
118 cmp:compare,
135 cmp:compare,
libraries/Signal.elm+5−4
@@ -3,7 +3,7 @@
3-- working with time (e.g. setting FPS) and combining signals and time (e.g.
3-- working with time (e.g. setting FPS) and combining signals and time (e.g.
4-- delaying updates, getting timestamps) can be found in the
4-- delaying updates, getting timestamps) can be found in the
5-- [`Time`](/docs/Signal/Time.elm) library.
5-- [`Time`](/docs/Signal/Time.elm) library.
6−--
6+--
7-- Note: There are lift functions up to `lift8`.
7-- Note: There are lift functions up to `lift8`.
8module Signal where
8module Signal where
9
9
@@ -29,7 +29,7 @@ lift8 : (a -> b -> c -> d -> e -> f -> g -> h -> i) -> Signal a -> Signal b -> S
29
29
30-- Create a past-dependent signal. Each value given on the input signal will
30-- Create a past-dependent signal. Each value given on the input signal will
31-- be accumulated, producing a new output value.
31-- be accumulated, producing a new output value.
32−--
32+--
33-- For instance, `(foldp (\\t acc -> acc + 1) 0 (Time.every second))` increments every second.
33-- For instance, `(foldp (\\t acc -> acc + 1) 0 (Time.every second))` increments every second.
34foldp : (a -> b -> b) -> b -> Signal a -> Signal b
34foldp : (a -> b -> b) -> b -> Signal a -> Signal b
35
35
@@ -41,6 +41,7 @@ merge : Signal a -> Signal a -> Signal a
41-- signals update simultaneously.
41-- signals update simultaneously.
42merges : [Signal a] -> Signal a
42merges : [Signal a] -> Signal a
43
43
44+-- Combine many signals into one, with an array of results.
44combine : [Signal a] -> Signal [a]
45combine : [Signal a] -> Signal [a]
45combine = L.foldr (S.lift2 (::)) (S.constant [])
46combine = L.foldr (S.lift2 (::)) (S.constant [])
46
47
@@ -104,9 +105,9 @@ delay : Time -> Signal a -> Signal a
104
105
105-- Signal application. This takes two signals, holding a function and
106-- Signal application. This takes two signals, holding a function and
106-- a value. It applies the current function to the current value.
107-- a value. It applies the current function to the current value.
107−--
108+--
108-- So the following expressions are equivalent:
109-- So the following expressions are equivalent:
109−--
110+--
110-- scene <~ Mouse.x ~ Mouse.y
111-- scene <~ Mouse.x ~ Mouse.y
111-- lift2 scene Mouse.x Mouse.y
112-- lift2 scene Mouse.x Mouse.y
112(~) : Signal (a -> b) -> Signal a -> Signal b
113(~) : Signal (a -> b) -> Signal a -> Signal b
libraries/Time.elm+1−1
@@ -39,7 +39,7 @@ inHours t = t / hour
39fps : Number a -> Signal Time
39fps : Number a -> Signal Time
40
40
41-- Same as the fps function, but you can turn it on and off. Allows you
41-- Same as the fps function, but you can turn it on and off. Allows you
42−-- to do brief animations based on user input without major ineffeciencies.
42+-- to do brief animations based on user input without major inefficiencies.
43-- The first time delta after a pause is always zero, no matter how long
43-- The first time delta after a pause is always zero, no matter how long
44-- the pause was. This way summing the deltas will actually give the amount
44-- the pause was. This way summing the deltas will actually give the amount
45-- of time that the output signal has been running.
45-- of time that the output signal has been running.
runtime/Init.js+48−38
@@ -1,7 +1,28 @@
1
1
2−Elm.init = function(module, baseNode) {
2+(function() {
3− 'use strict';
3+'use strict';
4+
5+Elm.fullscreen = function(module) {
6+ var style = document.createElement('style');
7+ style.type = 'text/css';
8+ style.innerHTML = "html,head,body { padding:0; margin:0; }" +
9+ "body { font-family: calibri, helvetica, arial, sans-serif; }";
10+ document.head.appendChild(style);
11+ return init(ElmRuntime.Display.FULLSCREEN, document.body, module);
12+};
13+
14+Elm.node = function(width, height, module) {
15+ var container = document.createElement('div');
16+ container.style.width = width + 'px';
17+ container.style.height = height + 'px';
18+ return init(ElmRuntime.Display.COMPONENT, container, module);
19+};
4
20
21+Elm.worker = function(module) {
22+ return init(ElmRuntime.Display.NONE, {}, module);
23+};
24+
25+function init(display, container, module) {
5 // defining state needed for an instance of the Elm RTS
26 // defining state needed for an instance of the Elm RTS
6 var signalGraph = null;
27 var signalGraph = null;
7 var inputs = [];
28 var inputs = [];
@@ -18,19 +39,17 @@ Elm.init = function(module, baseNode) {
18 return hasListener;
39 return hasListener;
19 }
40 }
20
41
21− // ensure that baseNode exists and is properly formatted.
42+ container.offsetX = 0;
22− if (typeof baseNode === 'undefined') {
43+ container.offsetY = 0;
23− baseNode = document.body;
24− var style = document.createElement('style');
25− style.type = 'text/css';
26− style.innerHTML = "html,head,body { padding:0; margin:0; }" +
27− "body { font-family: calibri, helvetica, arial, sans-serif; }";
28− document.head.appendChild(style);
29− }
30
44
31 // create the actual RTS. Any impure modules will attach themselves to this
45 // create the actual RTS. Any impure modules will attach themselves to this
32 // object. This permits many Elm programs to be embedded per document.
46 // object. This permits many Elm programs to be embedded per document.
33− var elm = {notify: notify, node: baseNode, id: ElmRuntime.guid(), inputs: inputs};
47+ var elm = { notify:notify,
48+ node:container,
49+ display:display,
50+ id:ElmRuntime.guid(),
51+ inputs:inputs
52+ };
34
53
35 // Set up methods to communicate with Elm program from JS.
54 // Set up methods to communicate with Elm program from JS.
36 function send(name, value) {
55 function send(name, value) {
@@ -44,15 +63,16 @@ Elm.init = function(module, baseNode) {
44 document.addEventListener(name + '_' + elm.id, handler);
63 document.addEventListener(name + '_' + elm.id, handler);
45 }
64 }
46
65
47− recv('elm_log', function(e) {console.log(e.value);});
66+ recv('log', function(e) {console.log(e.value)});
48− recv('elm_title', function(e) {document.title = e.value;});
67+ recv('title', function(e) {document.title = e.value});
49− recv('elm_redirect', function(e) {
68+ recv('redirect', function(e) {
50 if (e.value.length > 0) { window.location = e.value; }
69 if (e.value.length > 0) { window.location = e.value; }
51 });
70 });
52
71
53 // If graphics are not enabled, escape early, skip over setting up DOM stuff.
72 // If graphics are not enabled, escape early, skip over setting up DOM stuff.
73+ if (display === ElmRuntime.Display.NONE) {
54− if (baseNode === null) return { send : send, recv : recv };
74+ return { send : send, recv : recv };
55−
75+ }
56
76
57 var Render = ElmRuntime.use(ElmRuntime.Render.Element);
77 var Render = ElmRuntime.use(ElmRuntime.Render.Element);
58
78
@@ -65,36 +85,26 @@ Elm.init = function(module, baseNode) {
65 if (!('recv' in signalGraph)) signalGraph = Signal.constant(signalGraph);
85 if (!('recv' in signalGraph)) signalGraph = Signal.constant(signalGraph);
66 visualModel = signalGraph.value;
86 visualModel = signalGraph.value;
67 inputs = ElmRuntime.filterDeadInputs(inputs);
87 inputs = ElmRuntime.filterDeadInputs(inputs);
68−
88+
69− var tuple2 = Elm.Native.Utils(elm).Tuple2;
70− function adjustWindow() {
71− if ('Window' in elm) {
72− var w = baseNode.clientWidth;
73− if (w !== elm.Window.dimensions.value._0) {
74− notify(elm.Window.dimensions.id,
75− tuple2(w, document.body === baseNode ?
76− window.innerHeight : baseNode.clientHeight));
77− }
78− }
79− }
80−
81− // Add the visualModel to the DOM
89+ // Add the visualModel to the DOM
82− var renderNode = Render.render(visualModel);
90+ var renderNode = Render.render(visualModel)
83− baseNode.appendChild(renderNode);
91+ container.appendChild(renderNode);
84− adjustWindow();
92+ elm.Native.Window.resizeIfNeeded();
85−
93+
86 // set up updates so that the DOM is adjusted as necessary.
94 // set up updates so that the DOM is adjusted as necessary.
87 var update = Render.update;
95 var update = Render.update;
88 function domUpdate(value) {
96 function domUpdate(value) {
89 ElmRuntime.draw(function(_) {
97 ElmRuntime.draw(function(_) {
90 update(renderNode, visualModel, value);
98 update(renderNode, visualModel, value);
91 visualModel = value;
99 visualModel = value;
92− adjustWindow();
100+ elm.Native.Window.resizeIfNeeded();
93 });
101 });
94 return value;
102 return value;
95 }
103 }
96
104
97 signalGraph = A2(Signal.lift, domUpdate, signalGraph);
105 signalGraph = A2(Signal.lift, domUpdate, signalGraph);
98−
106+
99− return { send : send, recv : recv };
107+ return { send : send, recv : recv, node : container };
100};
108};
109+
110+}());
runtime/Render/Collage.js+185−81
@@ -3,8 +3,9 @@ ElmRuntime.Render.Collage = function() {
3'use strict';
3'use strict';
4
4
5var Render = ElmRuntime.use(ElmRuntime.Render.Element);
5var Render = ElmRuntime.use(ElmRuntime.Render.Element);
6+var Matrix = Elm.Matrix2D({});
6var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
7var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
7−var newElement = Utils.newElement, addTo = Utils.addTo,
8+var newElement = Utils.newElement,
8 extract = Utils.extract, fromList = Utils.fromList,
9 extract = Utils.extract, fromList = Utils.fromList,
9 fromString = Utils.fromString, addTransform = Utils.addTransform;
10 fromString = Utils.fromString, addTransform = Utils.addTransform;
10
11
@@ -79,20 +80,23 @@ function texture(redo, ctx, src) {
79}
80}
80
81
81function gradient(ctx, grad) {
82function gradient(ctx, grad) {
82− var g, p1, p2;
83+ var g;
84+ var stops = [];
83− if (grad.ctor === 'Linear') {
85+ if (grad.ctor === 'Linear') {
84− p1 = grad._1; p2 = grad._2;
86+ var p0 = grad._0, p1 = grad._1;
85− g = ctx.createLinearGradient(p1._0, p1._1, p2._0, p2._1);
87+ g = ctx.createLinearGradient(p0._0, -p0._1, p1._0, -p1._1);
88+ stops = fromList(grad._2);
86− } else {
89+ } else {
87− p1 = grad._1; p2 = grad._3;
90+ var p0 = grad._0, p2 = grad._2;
88− g = ctx.createRadialGradient(p1._0, p1._1, grad._2, p2._0, p2._1, grad._4);
91+ g = ctx.createRadialGradient(p0._0, -p0._1, grad._1, p2._0, -p2._1, grad._3);
92+ stops = fromList(grad._4);
89− }
93+ }
90− var stops = fromList(grad._0);
91− for (var i = stops.length; i--; ) {
94+ var len = stops.length;
95+ for (var i = 0; i < len; ++i) {
92− var stop = stops[i];
96+ var stop = stops[i];
93− g.addColorStop(stop._0, extract(stop._1));
97+ g.addColorStop(stop._0, extract(stop._1));
94− }
98+ }
95− return g;
99+ return g;
96}
100}
97
101
98function drawShape(redo, ctx, style, path) {
102function drawShape(redo, ctx, style, path) {
@@ -123,7 +127,7 @@ function drawImage(redo, ctx, form) {
123 ctx.drawImage(img, srcX, srcY, srcW, srcH, destX, destY, destW, destH);
127 ctx.drawImage(img, srcX, srcY, srcW, srcH, destX, destY, destW, destH);
124}
128}
125
129
126−function renderForm(stack, redo, ctx, form) {
130+function renderForm(redo, ctx, form) {
127 ctx.save();
131 ctx.save();
128 var x = form.x, y = form.y, theta = form.theta, scale = form.scale;
132 var x = form.x, y = form.y, theta = form.theta, scale = form.scale;
129 if (x !== 0 || y !== 0) ctx.translate(x, -y);
133 if (x !== 0 || y !== 0) ctx.translate(x, -y);
@@ -132,94 +136,194 @@ function renderForm(stack, redo, ctx, form) {
132 ctx.beginPath();
136 ctx.beginPath();
133 var f = form.form;
137 var f = form.form;
134 switch(f.ctor) {
138 switch(f.ctor) {
135− case 'FPath' : drawLine(ctx, f._0, f._1); break;
139+ case 'FPath' : drawLine(ctx, f._0, f._1); break;
140+ case 'FImage': drawImage(redo, ctx, f); break;
136− case 'FShape':
141+ case 'FShape':
137− if (f._0.ctor === 'Left') {
142+ if (f._0.ctor === 'Left') {
138− f._1.closed = true;
143+ f._1.closed = true;
139− drawLine(ctx, f._0._0, f._1);
144+ drawLine(ctx, f._0._0, f._1);
140− } else {
145+ } else {
141− drawShape(redo, ctx, f._0._0, f._1);
146+ drawShape(redo, ctx, f._0._0, f._1);
142− }
147+ }
143− break;
144− case 'FImage': drawImage(redo, ctx, f); break;
145− case 'FGroup':
146− ctx.save();
147− var m = f._0;
148− // stack.push(makeTransform(x, y, theta, scale, m));
149− ctx.transform(m[0], m[3], m[1], m[4], m[2], m[5]);
150− renderForms(stack, redo, ctx, f._1);
151− // stack.pop();
152− ctx.restore();
153− break;
148+ break;
154 }
149 }
155 ctx.restore();
150 ctx.restore();
156}
151}
157
152
158−function makeTransform(x, y, theta, scale, matrix) {
153+function makeTransform(w, h, form, matrices) {
159− return function(m) {
154+ var props = form.form._0.props;
155+ var m = A6( Matrix.matrix, 1, 0, 0, 1,
156+ (w - props.width)/2,
157+ (props.height - h)/2 );
158+ var len = matrices.length;
159+ for (var i = 0; i < len; ++i) { m = A2( Matrix.multiply, m, matrices[i] ); }
160+
161+ var x = form.x, y = form.y, theta = form.theta, scale = form.scale;
160− if (x !== 0 || y !== 0) m = A3( Matrix.move, x, y, m );
162+ if (x !== 0 || y !== 0) m = A3( Matrix.move, x, y, m );
161− if (theta !== 0) m = A2( Matrix.rotate, theta, m );
163+ if (theta !== 0) m = A2( Matrix.rotate, theta, m );
162− if (scale !== 1) m = Matrix.scale( scale );
164+ if (scale !== 1) m = Matrix.scale( scale );
163− return Matrix.multiply( m, matrix );
165+ return 'matrix(' + m[0] + ',' + m[3] + ',' + m[1] + ',' +
166+ m[4] + ',' + m[2] + ',' + (-m[5]) + ')';
167+}
168+
169+function stepperHelp(list) {
170+ var arr = fromList(list);
171+ var i = 0;
172+ function peekNext() {
173+ return i < arr.length ? arr[i].form.ctor : '';
174+ }
175+ // assumes that there is a next element
176+ function next() {
177+ var out = arr[i];
178+ ++i;
179+ return out;
164− };
180+ }
181+ return { peekNext:peekNext, next:next };
165}
182}
166
183
167−function renderForms(stack, redo, ctx, forms) {
184+function stepper(forms) {
168− var fs = fromList(forms);
185+ var ps = [stepperHelp(forms)];
186+ var matrices = [];
187+ function peekNext() {
169− for (var i = 0, len = fs.length; i < len; ++i) {
188+ var len = ps.length;
170− renderForm(stack, redo, ctx, fs[i]);
189+ var formType = '';
190+ for (var i = 0; i < len; ++i ) {
191+ if (formType = ps[i].peekNext()) return formType;
192+ }
193+ return '';
194+ }
195+ // assumes that there is a next element
196+ function next(ctx) {
197+ while (!ps[0].peekNext()) { ps.shift(); matrices.pop(); ctx.restore(); }
198+ var out = ps[0].next();
199+ var f = out.form;
200+ if (f.ctor === 'FGroup') {
201+ ps.unshift(stepperHelp(f._1));
202+ var m = f._0;
203+ matrices.push(m);
204+ var x = out.x, y = out.y, theta = out.theta, scale = out.scale;
205+ if (x !== 0 || y !== 0) ctx.translate(x, -y);
206+ if (theta !== 0) ctx.rotate(theta);
207+ if (scale !== 1) ctx.scale(scale, scale);
208+ ctx.save();
209+ ctx.transform(m[0], m[3], m[1], m[4], m[2], -m[5]);
210+ }
211+ return out;
171 }
212 }
213+ function transforms() { return matrices; }
214+ return { peekNext:peekNext, next:next, transforms:transforms };
172}
215}
173
216
174−function collageForms(w,h,forms) {
217+function makeCanvas(w,h) {
175 var canvas = newElement('canvas');
218 var canvas = newElement('canvas');
176 canvas.style.width = w + 'px';
219 canvas.style.width = w + 'px';
177 canvas.style.height = h + 'px';
220 canvas.style.height = h + 'px';
178 canvas.style.display = "block";
221 canvas.style.display = "block";
222+ canvas.style.position = "absolute";
179 canvas.width = w;
223 canvas.width = w;
180 canvas.height = h;
224 canvas.height = h;
181− function redo() { renderForms([], this, ctx, forms); }
182− if (canvas.getContext) {
183− var ctx = canvas.getContext('2d');
184− ctx.translate(w/2, h/2);
185− renderForms([], redo, ctx, forms);
186− return canvas;
187− }
188− canvas.innerHTML = "Your browser does not support canvas.";
189 return canvas;
225 return canvas;
190}
226}
191
227
192−function collageElement(stack, width, height, elem) {
228+function render(model) {
193− var e = Render.render(elem),
194− w = elem.props.width,
195− h = elem.props.height,
196− m = Matrix.matrix(1,0,0,1,width/2,height/2),
197− len = stack.length;
198− for (var i = 0; i < len; ++i) { m = stack[i](m); }
199− var mtrx = 'matrix(' + m[0] + ',' + m[3] + ',' + m[1] + ',' +
200− m[4] + ',' + m[2] + ',' + m[5] + ')';
201− addTransform(e.style, 'translate(' + (-w/2) + 'px,'+ (-h/2) + 'px) ' + mtrx);
202− var div = newElement('div');
229+ var div = newElement('div');
203− addTo(div,e);
230+ update(div, model, model);
204− div.style.width = width + 'px';
205− div.style.height = height + 'px';
206− div.style.overflow = 'hidden';
207− return div;
231+ return div;
208}
232}
209
233
210−function render(model) {
234+function updateTracker(w,h,div) {
211− return collageForms(model.w, model.h, model.forms);
235+ var kids = div.childNodes;
236+ var i = 0;
237+ function transform(transforms, ctx) {
238+ ctx.translate(w/2, h/2);
239+ var len = transforms.length;
240+ for (var i = 0; i < len; ++i) {
241+ var m = transforms[i];
242+ ctx.save();
243+ ctx.transform(m[0], m[3], m[1], m[4], m[2], m[5]);
244+ }
245+ return ctx;
246+ }
247+ function getContext(transforms) {
248+ while (i < kids.length) {
249+ var node = kids[i++];
250+ if (node.getContext) {
251+ node.width = w;
252+ node.height = h;
253+ node.style.width = w + 'px';
254+ node.style.height = h + 'px';
255+ return transform(transforms, node.getContext('2d'));
256+ }
257+ div.removeChild(node);
258+ }
259+ var canvas = makeCanvas(w,h);
260+ div.appendChild(canvas);
261+ // we have added a new node, so we must step our position
262+ ++i;
263+ return transform(transforms, canvas.getContext('2d'));
264+ }
265+ function element(matrices, form) {
266+ var container = kids[i];
267+ if (!container) {
268+ container = newElement('div');
269+ container.style.overflow = 'hidden';
270+ container.style.position = 'absolute';
271+ div.appendChild(container);
272+ } else if (container.getContext) {
273+ container = newElement('div');
274+ container.style.overflow = 'hidden';
275+ container.style.position = 'absolute';
276+ div.insertBefore(container, kids[i]);
277+ }
278+ // we have added a new node, so we must step our position
279+ ++i;
280+
281+ container.style.width = w + 'px';
282+ container.style.height = h + 'px';
283+
284+ var elem = form.form._0;
285+ var node = container.firstChild;
286+ if (node) {
287+ Render.update(node, node.oldElement, elem);
288+ } else {
289+ node = Render.render(elem);
290+ container.appendChild(node);
291+ }
292+ node.oldElement = elem;
293+ addTransform(node.style, makeTransform(w, h, form, matrices));
294+ }
295+ return { getContext:getContext, element:element };
212}
296}
213
297
298+
214−function update(node, oldModel, newModel) {
299+function update(div, _, model) {
300+ var w = model.w;
301+ var h = model.h;
302+ div.style.width = w + 'px';
303+ div.style.height = h + 'px';
304+ if (model.forms.ctor === 'Nil') {
305+ while (div.hasChildNodes()) {
306+ div.removeChild(div.lastChild);
307+ }
308+ }
309+ var stpr = stepper(model.forms);
310+ var tracker = updateTracker(w,h,div);
311+ var ctx = null;
312+ var formType = '';
313+
314+ while (formType = stpr.peekNext()) {
315+ if (ctx === null && formType !== 'FElement') {
215− if (!node.getContext) {
316+ ctx = tracker.getContext(stpr.transforms());
216− return node.parentNode.replaceChild(render(newModel), node);
317+ }
318+ var form = stpr.next(ctx);
319+ if (formType === 'FElement') {
320+ tracker.element(stpr.transforms(), form);
321+ ctx = null;
322+ } else if (formType !== 'FGroup') {
323+ renderForm(function() { update(div, model, model); }, ctx, form);
324+ }
217 }
325 }
218− var ctx = node.getContext('2d');
326+ return div;
219− var w = newModel.w, h = newModel.h;
220− ctx.clearRect(-w/2, -h/2, w, h);
221− var redo = function(){ renderForms( [], this, ctx, newModel.forms ); };
222− renderForms( [], redo, ctx, newModel.forms );
223}
327}
224
328
225return { render:render, update:update };
329return { render:render, update:update };
runtime/Render/Element.js+7−7
@@ -3,7 +3,7 @@ ElmRuntime.Render.Element = function() {
3'use strict';
3'use strict';
4
4
5var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
5var Utils = ElmRuntime.use(ElmRuntime.Render.Utils);
6−var newElement = Utils.newElement, addTo = Utils.addTo, extract = Utils.extract,
6+var newElement = Utils.newElement, extract = Utils.extract,
7 addTransform = Utils.addTransform, removeTransform = Utils.removeTransform,
7 addTransform = Utils.addTransform, removeTransform = Utils.removeTransform,
8 fromList = Utils.fromList, eq = Utils.eq;
8 fromList = Utils.fromList, eq = Utils.eq;
9
9
@@ -18,7 +18,7 @@ function setProps(props, e) {
18 if (props.href !== '') {
18 if (props.href !== '') {
19 var a = newElement('a');
19 var a = newElement('a');
20 a.href = props.href;
20 a.href = props.href;
21− addTo(a,e);
21+ a.appendChild(e);
22 return a;
22 return a;
23 }
23 }
24 return e;
24 return e;
@@ -63,7 +63,7 @@ function fittedImage(w, h, src) {
63 };
63 };
64 img.src = src;
64 img.src = src;
65 img.name = src;
65 img.name = src;
66− addTo(e,img);
66+ e.appendChild(img);
67 return e;
67 return e;
68}
68}
69
69
@@ -85,7 +85,7 @@ function croppedImage(elem, w, h, src) {
85 };
85 };
86 img.src = src;
86 img.src = src;
87 img.name = src;
87 img.name = src;
88− addTo(e,img);
88+ e.appendChild(img);
89 return e;
89 return e;
90}
90}
91
91
@@ -95,7 +95,7 @@ function goRight(e) { e.style.styleFloat = e.style.cssFloat = "left"; return e;
95function flowWith(f, array) {
95function flowWith(f, array) {
96 var container = newElement('div');
96 var container = newElement('div');
97 for (var i = array.length; i--; ) {
97 for (var i = array.length; i--; ) {
98− addTo(container, f(render(array[i])));
98+ container.appendChild(f(render(array[i])));
99 }
99 }
100 return container;
100 return container;
101}
101}
@@ -143,7 +143,7 @@ function container(pos,elem) {
143 var div = newElement('div');
143 var div = newElement('div');
144 div.style.position = 'relative';
144 div.style.position = 'relative';
145 div.style.overflow = 'hidden';
145 div.style.overflow = 'hidden';
146− addTo(div,e);
146+ div.appendChild(e);
147 return div;
147 return div;
148}
148}
149
149
@@ -256,7 +256,7 @@ function updateProps(node, curr, next) {
256 if (currP.href === '') {
256 if (currP.href === '') {
257 var a = newElement('a');
257 var a = newElement('a');
258 a.href = props.href;
258 a.href = props.href;
259− addTo(a,e);
259+ a.appendChild(e);
260 e.parentNode.replaceChild(a,e);
260 e.parentNode.replaceChild(a,e);
261 } else {
261 } else {
262 node.parentNode.href = props.href;
262 node.parentNode.href = props.href;
runtime/Utils.js+2
@@ -2,6 +2,8 @@
2(function() {
2(function() {
3'use strict';
3'use strict';
4
4
5+ElmRuntime.Display = { FULLSCREEN: 0, COMPONENT: 1, NONE: 2 };
6+
5ElmRuntime.counter = 0;
7ElmRuntime.counter = 0;
6ElmRuntime.guid = function() { return ElmRuntime.counter++; }
8ElmRuntime.guid = function() { return ElmRuntime.counter++; }
7
9