diff --git a/.gitignore b/.gitignore
index d06d7597e..ad1a37985 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,7 @@ cabal-dev
*.chi
*.chs.h
*.aes
-.DS_Store
\ No newline at end of file
+.DS_Store
+/Examples/elm-js/ChangeTitle/ChangeTitle.html
+/Examples/elm-js/ConsoleLog/Log.html
+/Examples/elm-js/Redirect/Redirect.html
\ No newline at end of file
diff --git a/elm/changelog.txt b/elm/changelog.txt
index 9eb5d012e..0e78bb472 100644
--- a/elm/changelog.txt
+++ b/elm/changelog.txt
@@ -1,3 +1,20 @@
+Release 0.3.5
+=============
+
+* Add JavaScript event interface. Allows Elm to import and export JS values
+ and events. This makes it possible to import and export Elements, so users
+ can use JS techniques and libraries if necessary.
+* Add new flags to help with JavaScript event interface.
+* Add new Signal functions:
+ count :: Signal a -> Signal Int
+ keepIf :: a -> (a -> Bool) -> Signal a -> Signal a
+ dropIf :: a -> (a -> Bool) -> Signal a -> Signal a
+ dropRepeats :: Signal a -> Signal a
+ The keep and drop functions make it possible to filter events, which
+ was not possible in prior releases.
+
+
+
Release 0.3.0
=============
diff --git a/elm/elm-runtime-0.3.5.js b/elm/elm-runtime-0.3.5.js
new file mode 100644
index 000000000..e730396be
--- /dev/null
+++ b/elm/elm-runtime-0.3.5.js
@@ -0,0 +1,2081 @@
+
+var Guid = function() {
+ var counter = 0;
+ var guid = function() { counter += 1; return counter; };
+ return {guid : guid};
+}();
+var Foreign = function() {
+ var JavaScript = function() {
+ function castJSBoolToBool(b) { return b; }
+ function castBoolToJSBool(b) { return b; }
+
+ function castJSNumberToFloat(n) { return n; }
+ function castFloatToJSNumber(n) { return n; }
+
+ function castJSNumberToInt(n) { return ~~n; }
+ function castIntToJSNumber(n) { return n; }
+
+ var castJSElementToElement = Element.jsElement;
+ function castElementToJSElement(elem) { return elem; }
+
+ function castJSArrayToList(arr) {
+ var list = ["Nil"];
+ for (var i = arr.length; i--; ) {
+ list = [ "Cons", arr[i], list ];
+ }
+ return list;
+ }
+ function castListToJSArray(list) {
+ var a = [];
+ while (list[0] === "Cons") {
+ a.push(list[1]);
+ list = list[2];
+ }
+ return a;
+ }
+
+ var castJSStringToString = castJSArrayToList;
+ function castStringToJSString(str) {
+ if (typeof str === "string") return str;
+ return castListToJSArray(str).join('');
+ }
+
+ function fromTuple(t) { return t.slice(1); }
+ function toTuple(a) { return a.unshift("Tuple" + a.length); }
+
+ return {castJSBoolToBool:castJSBoolToBool,
+ castBoolToJSBool:castBoolToJSBool,
+ castJSNumberToFloat:castJSNumberToFloat,
+ castFloatToJSNumber:castFloatToJSNumber,
+ castJSNumberToInt:castJSNumberToInt,
+ castIntToJSNumber:castIntToJSNumber,
+ castJSElementToElement:castJSElementToElement,
+ castElementToJSElement:castElementToJSElement,
+ castJSArrayToList:castJSArrayToList,
+ castListToJSArray:castListToJSArray,
+ castJSStringToString:castJSStringToString,
+ castStringToJSString:castStringToJSString,
+ castTupleToJSTuple2:fromTuple,
+ castTupleToJSTuple3:fromTuple,
+ castTupleToJSTuple4:fromTuple,
+ castTupleToJSTuple5:fromTuple,
+ castJSTupleToTuple2:toTuple,
+ castJSTupleToTuple3:toTuple,
+ castJSTupleToTuple4:toTuple,
+ castJSTupleToTuple5:toTuple
+ };
+ }();
+ return {JavaScript:JavaScript};
+}();
+var Value = function(){
+
+ var eq = function(x,y) {
+ if (typeof x === "object") {
+ if (x === y) return true;
+ if (x.length !== y.length) return false;
+ for (var i = x.length; i--; ) {
+ if (!eq(x[i],y[i])) return false;
+ }
+ return true;
+ }
+ return x === y;
+ };
+
+ var Tuple = function() {
+ var len = arguments.length;
+ var arr = new Array(len+1);
+ arr[0] = "Tuple" + arguments.length;
+ for (var i = len; i--; ) {
+ arr[i+1] = arguments[i];
+ }
+ return arr;
+ };
+
+ var listToArray = function(list) {
+ var arr = [];
+ while (list[0] === "Cons") {
+ arr.push(list[1]);
+ list = list[2];
+ }
+ return arr;
+ };
+
+ var properEscape = function(str) {
+ str.replace('"', """);
+ str.replace("&", "&");
+ str.replace("'", "'");
+ str.replace("<", "<");
+ str.replace(">", ">");
+ return str;
+ };
+
+ var toText = function(elmList) {
+ if (typeof elmList === "string") return elmList;
+ var a = [];
+ while (elmList[0] === "Cons") {
+ a.push(elmList[1]);
+ elmList = elmList[2];
+ }
+ return properEscape(a.join(''));
+ };
+
+ var toString = function(v) {
+ if (typeof v === "boolean") {
+ return v ? "True" : "False";
+ } else if (typeof v === "number") {
+ return v+"";
+ } else if (v[0]) {
+ if (v[0].substring(0,5) === "Tuple") {
+ var output = "";
+ for (var i = v.length; --i; ) {
+ output = "," + toString(v[i]) + output;
+ }
+ if (output[0] === ",") output = output.substring(1);
+ return "("+output+")";
+ } else if (v[0] === "Cons") {
+ var start = (typeof v[1] === "string") ? '"' : "[";
+ var end = (typeof v[1] === "string") ? '"' : "]";
+ var div = (typeof v[1] === "string") ? "" : ",";
+ var output = start + toString(v[1]);
+ v = v[2];
+ while (true) {
+ if (v[0] === "Cons") {
+ output += div + toString(v[1]);
+ v = v[2];
+ } else {
+ return output + end;
+ }
+ }
+ } else if (v[0] === "Nil") {
+ return "[]";
+ } else {
+ var output = "";
+ for (var i = v.length; --i; ) {
+ output = " " + toString(v[i]) + output
+ }
+ output = v[0] + output;
+ return (v.length > 1) ? "(" + output + ")" : output;
+ }
+ }
+ return v+"";
+ };
+ var show = function(v) {
+ return Text.monospace(properEscape(toString(v)));
+ };
+ var append = function(xs,ys) {
+ if (typeof xs === "string" && typeof ys === "string") {
+ return xs.concat(ys);
+ }
+ if (xs[0] === "Nil") {
+ return ys;
+ }
+ var root = ["Cons", xs[1], ["Nil"]];
+ var curr = root;
+ xs = xs[2];
+ while (xs[0]==="Cons") {
+ curr[2] = ["Cons", xs[1], ["Nil"]];
+ xs = xs[2];
+ curr = curr[2];
+ }
+ curr[2] = ys;
+ return root;
+ };
+
+ var str = function(s) {
+ var out = ["Nil"];
+ for (var i = s.length; i--; ) {
+ out = ["Cons", s[i], out];
+ }
+ return out;
+ };
+
+ return {eq:eq,
+ str:str,
+ show:show,
+ Tuple:Tuple,
+ append:append,
+ listToArray:listToArray,
+ toText : toText,
+ properEscape : properEscape};
+}();var List = function() {
+
+ var throwError = function(f) {
+ throw "Function '" + f + "' expecting a list!";
+ }
+
+ function length(xs) {
+ var out = 0;
+ while (xs[0] === "Cons") {
+ out += 1;
+ xs = xs[2];
+ }
+ return out;
+ };
+ var reverse = foldl(function(x_72) {
+ return function(y_73) {
+ return["Cons", x_72, y_73]
+ }
+ })(["Nil"]);
+ var concat = foldr(function(x_74) {
+ return function(y_75) {
+ return Value.append(x_74, y_75)
+ }
+ })(["Nil"]);
+ var and = foldl(function(x_77) {
+ return function(y_78) {
+ return x_77 && y_78
+ }
+ })(true);
+ var or = foldl(function(x_79) {
+ return function(y_80) {
+ return x_79 || y_80
+ }
+ })(false);
+ var sum = foldl(function(x_89) {
+ return function(y_90) {
+ return x_89 + y_90
+ }
+ })(0);
+ var product = foldl(function(x_91) {
+ return function(y_92) {
+ return x_91 * y_92
+ }
+ })(1);
+ var maximum = foldl1(function(x) { return function(y) { return Math.max(x,y) } });
+ var minimum = foldl1(function(x) { return function(y) { return Math.min(x,y) } });
+ function head(v) {
+ if (v[0] !== "Cons") {
+ throw "Error: 'head' only accepts lists of length greater than one.";
+ }
+ return v[1];
+ }
+ function tail(v) {
+ if (v[0] !== "Cons") {
+ throw "Error: 'tail' only accepts lists of length greater than one.";
+ }
+ return v[2];
+ }
+ function map(f) {
+ return function(xs) {
+ if (xs[0] === "Nil") { return xs; }
+ if (xs[0] !== "Cons") { throwError('map'); }
+ var root = ["Cons", f(xs[1]), ["Nil"]];
+ var curr = root;
+ xs = xs[2];
+ while (xs[0]==="Cons") {
+ curr[2] = ["Cons", f(xs[1]), ["Nil"]];
+ xs = xs[2];
+ curr = curr[2];
+ }
+ return root;
+ }
+ }
+ function foldl(f) {
+ return function(b) {
+ return function(xs) {
+ var acc = b;
+ if (xs[0] === "Nil") { return acc; }
+ if (xs[0] !== "Cons") { throwError('foldl'); }
+ while (xs[0] === "Cons") {
+ acc = f(xs[1])(acc);
+ xs = xs[2];
+ }
+ return acc;
+ }
+ }
+ }
+ function foldr(f) {
+ return function(b) {
+ return function(xs) {
+ var acc = b;
+ if (xs[0] === "Nil") { return acc; }
+ if (xs[0] !== "Cons") { throwError('foldr'); }
+ var arr = [];
+ while (xs[0] === "Cons") {
+ arr.push(xs[1]);
+ xs = xs[2];
+ }
+ for (var i = arr.length; i--; ) {
+ acc = f(arr[i])(acc);
+ }
+ return acc;
+ }
+ }
+ }
+ function foldl1(f_49) {
+ return function(_temp_50) {
+ return function(v) {
+ if("Cons" !== v[0]) {
+ return undefined
+ }else {
+ var x_51 = v[1];
+ var xs_52 = v[2];
+ return foldl(f_49)(x_51)(xs_52)
+ }
+ }(_temp_50)
+ }
+ }
+ function foldr1(f_53) {
+ return function(_temp_54) {
+ return function(v) {
+ if("Cons" !== v[0]) {
+ return undefined
+ }else {
+ var x_55 = v[1];
+ var xs_56 = v[2];
+ return foldr(f_53)(x_55)(xs_56)
+ }
+ }(_temp_54)
+ }
+ }
+ function scanl(f) {
+ return function(b) {
+ return function(xs) {
+ if (xs[0] === "Nil") { return ["Cons",b,["Nil"]]; }
+ if (xs[0] !== "Cons") { throwError('scanl'); }
+ var arr = [b];
+ while (xs[0] === "Cons") {
+ b = f(xs[1])(b);
+ arr.push(b);
+ xs = xs[2];
+ }
+ var out = ["Nil"];
+ for (var i = arr.length; i--; ) {
+ out = ["Cons", arr[i], out];
+ }
+ return out;
+ }
+ }
+ }
+ function scanl1(f) {
+ return function(xs) {
+ if (xs[0] !== "Cons") {
+ throw "Error: 'scanl1' requires a list of at least length 1.";
+ }
+ return scanl(f)(xs[1])(xs[2]);
+ }
+ }
+ function filter(pred) {
+ return function(xs) {
+ if (xs[0] === "Nil") { return xs; }
+ if (xs[0] !== "Cons") { throwError('filter'); }
+ var arr = [];
+ while (xs[0] === "Cons") {
+ if (pred(xs[1])) { arr.push(xs[1]); }
+ xs = xs[2];
+ }
+ var out = ["Nil"];
+ for (var i = arr.length; i--; ) {
+ out = ["Cons", arr[i], out];
+ }
+ return out;
+ }
+ }
+ function concatMap(f_76) {
+ return function(x) {
+ return concat(map(f_76)(x))
+ }
+ }
+ function forall(pred) {
+ return foldl(function(x) { return function(acc) {
+ return acc && pred(x);
+ };})(true);
+ }
+ function exists(pred) {
+ return foldl(function(x) { return function(acc) {
+ return acc || pred(x);
+ };})(false);
+ }
+ function partition(pred_93) {
+ return function(lst_94) {
+ return function() {
+ var v = lst_94;
+ var c = [function(v) {
+ if("Nil" !== v[0]) {
+ return undefined
+ }else {
+ return["Tuple2", ["Nil"], ["Nil"]]
+ }
+ }, function(v) {
+ if("Cons" !== v[0]) {
+ return undefined
+ }else {
+ var x_95 = v[1];
+ var xs_96 = v[2];
+ return function(v) {
+ if("Tuple2" !== v[0]) {
+ return undefined
+ }else {
+ var as_97 = v[1];
+ var bs_98 = v[2];
+ return pred_93(x_95) ? ["Tuple2", ["Cons", x_95, as_97], bs_98] : ["Tuple2", as_97, ["Cons", x_95, bs_98]]
+ }
+ }(partition(pred_93)(xs_96))
+ }
+ }];
+ for(var i = c.length;i--;) {
+ var r = c[i](v);
+ if(r !== undefined) {
+ return r
+ }
+ }
+ }()
+ }
+ }
+ function zipWith(f) {
+ return function(listA) {
+ return function(listB) {
+ if (listA[0] === "Nil" || listB[0] === "Nil") { return ["Nil"]; }
+ if (listA[0] !== "Cons" || listB[0] !== "Cons") { throwError('zipWith'); }
+ var arr = [];
+ while (listA[0] === "Cons" && listB[0] === "Cons") {
+ arr.push(f(listA[1])(listB[1]));
+ listA = listA[2];
+ listB = listB[2];
+ }
+ var out = ["Nil"];
+ for (var i = arr.length; i--; ) {
+ out = ["Cons", arr[i], out];
+ }
+ return out;
+ }
+ }
+ }
+ function zip(listA) {
+ return function(listB) {
+ if (listA[0] === "Nil" || listB[0] === "Nil") { return ["Nil"]; }
+ if (listA[0] !== "Cons" || listB[0] !== "Cons") { throwError('zip'); }
+ var arr = [];
+ while (listA[0] === "Cons" && listB[0] === "Cons") {
+ arr.push(["Tuple2", listA[1], listB[1]]);
+ listA = listA[2];
+ listB = listB[2];
+ }
+ var out = ["Nil"];
+ for (var i = arr.length; i--; ) {
+ out = ["Cons", arr[i], out];
+ }
+ return out;
+ }
+ }
+ function unzip(pairs_112) {
+ return function() {
+ var v = pairs_112;
+ var c = [function(v) {
+ if("Nil" !== v[0]) {
+ return undefined
+ }else {
+ return["Tuple2", ["Nil"], ["Nil"]]
+ }
+ }, function(v) {
+ if("Cons" !== v[0]) {
+ return undefined
+ }else {
+ var p_113 = v[1];
+ var ps_114 = v[2];
+ return function(v) {
+ if("Tuple2" !== v[0]) {
+ return undefined
+ }else {
+ if("Tuple2" !== v[1][0]) {
+ return undefined
+ }else {
+ var x_115 = v[1][1];
+ var y_116 = v[1][2];
+ if("Tuple2" !== v[2][0]) {
+ return undefined
+ }else {
+ var xs_117 = v[2][1];
+ var ys_118 = v[2][2];
+ return["Tuple2", ["Cons", x_115, xs_117], ["Cons", y_116, ys_118]]
+ }
+ }
+ }
+ }(["Tuple2", p_113, unzip(ps_114)])
+ }
+ }];
+ for(var i = c.length;i--;) {
+ var r = c[i](v);
+ if(r !== undefined) {
+ return r
+ }
+ }
+ }()
+ }
+ function intersperse(sep_119) {
+ return function(xs_120) {
+ return function() {
+ var v = xs_120;
+ var c = [function(v) {
+ if("Nil" !== v[0]) {
+ return undefined
+ }else {
+ return["Nil"]
+ }
+ }, function(v) {
+ if("Cons" !== v[0]) {
+ return undefined
+ }else {
+ var a_124 = v[1];
+ if("Nil" !== v[2][0]) {
+ return undefined
+ }else {
+ return["Cons", a_124, ["Nil"]]
+ }
+ }
+ }, function(v) {
+ if("Cons" !== v[0]) {
+ return undefined
+ }else {
+ var a_121 = v[1];
+ if("Cons" !== v[2][0]) {
+ return undefined
+ }else {
+ var b_122 = v[2][1];
+ var cs_123 = v[2][2];
+ return["Cons", a_121, ["Cons", sep_119, intersperse(sep_119)(["Cons", b_122, cs_123])]]
+ }
+ }
+ }];
+ for(var i = c.length;i--;) {
+ var r = c[i](v);
+ if(r !== undefined) {
+ return r
+ }
+ }
+ }()
+ }
+ }
+ function intercalate(sep_125) {
+ return function(xs_126) {
+ return function() {
+ var v = xs_126;
+ var c = [function(v) {
+ if("Nil" !== v[0]) {
+ return undefined
+ }else {
+ return["Nil"]
+ }
+ }, function(v) {
+ if("Cons" !== v[0]) {
+ return undefined
+ }else {
+ var a_130 = v[1];
+ if("Nil" !== v[2][0]) {
+ return undefined
+ }else {
+ return a_130
+ }
+ }
+ }, function(v) {
+ if("Cons" !== v[0]) {
+ return undefined
+ }else {
+ var a_127 = v[1];
+ if("Cons" !== v[2][0]) {
+ return undefined
+ }else {
+ var b_128 = v[2][1];
+ var cs_129 = v[2][2];
+ return Value.append(a_127, Value.append(sep_125, intercalate(sep_125)(["Cons", b_128, cs_129])))
+ }
+ }
+ }];
+ for(var i = c.length;i--;) {
+ var r = c[i](v);
+ if(r !== undefined) {
+ return r
+ }
+ }
+ }()
+ }
+ }
+ function sort(xs) {
+ if (xs[0] === "Nil") { return xs; }
+ if (xs[0] !== "Cons") { throwError('sort'); }
+ var arr = [];
+ while (xs[0] === "Cons") {
+ arr.push(xs[1]);
+ xs = xs[2];
+ }
+ arr.sort(function(a,b) { return a - b});
+ var out = ["Nil"];
+ for (var i = arr.length; i--; ) {
+ out = [ "Cons", arr[i], out ];
+ }
+ return out;
+ }
+ function take(n) { return function(xs) {
+ if (n <= 0) { return ["Nil"]; }
+ if (xs[0] === "Nil") { return xs; }
+ if (xs[0] !== "Cons") { throwError('take'); }
+ var out = [ "Cons", xs[1], ["Nil"] ];
+ var temp = out;
+ xs = xs[2];
+ --n;
+ while (xs[0] === "Cons" && n > 0) {
+ temp[2] = [ "Cons", xs[1], ["Nil"] ];
+ temp = temp[2];
+ xs = xs[2];
+ --n;
+ }
+ return out;
+ };
+ }
+ function drop(n) { return function(xs) {
+ if (xs[0] === "Nil") { return xs; }
+ if (xs[0] !== "Cons") { throwError('drop'); }
+ while (xs[0] === "Cons" && n > 0) {
+ xs = xs[2];
+ --n;
+ }
+ return xs;
+ };
+ }
+ return {head:head,
+ tail:tail,
+ map:map,
+ foldl:foldl,
+ foldr:foldr,
+ foldl1:foldl1,
+ foldr1:foldr1,
+ scanl:scanl,
+ scanl1:scanl1,
+ filter:filter,
+ length:length,
+ reverse:reverse,
+ concat:concat,
+ concatMap:concatMap,
+ and:and,
+ or:or,
+ forall:forall,
+ exists:exists,
+ sum:sum,
+ product:product,
+ maximum:maximum,
+ minimum:minimum,
+ partition:partition,
+ zipWith:zipWith,
+ zip:zip,
+ unzip:unzip,
+ intersperse:intersperse,
+ intercalate:intercalate,
+ sort:sort,
+ take:take,
+ drop:drop};
+}();
+var Data = function() {
+
+var Char = function() {
+ return {fromCode : function(c) { return String.fromCharCode(c); },
+ toCode : function(c) { return c.charCodeAt(0); },
+ toUpper : function(c) { return c.toUpperCase(); },
+ toLower : function(c) { return c.toLowerCase(); },
+ toLocaleUpper : function(c) { return c.toLocaleUpperCase(); },
+ toLocaleLower : function(c) { return c.toLocaleLowerCase(); }
+ };
+}();
+
+var Maybe = function() {
+ function consMaybe(x) { return function(xs) {
+ if (x[0] === "Just") return ["Cons", x[1], xs];
+ return xs;
+ };
+ }
+ function fromMaybe(b) { return function(m) {
+ if (m[0] === "Just") return m[1];
+ return b;
+ };
+ }
+ function mapCons(f) { return function(y) { return function(xs) {
+ var x = f(y);
+ if (x[0] === "Just") return ["Cons", x[1], xs];
+ return xs;
+ };
+ };
+ }
+ function maybe(b) { return function(f) { return function(m) {
+ if (m[0] === "Just") return f(m[1]);
+ return b;
+ };
+ };
+ }
+
+ return {Just : function(x) { return ["Just",x]; },
+ Nothing : ["Nothing"],
+ catMaybes : List.foldr(consMaybe)(["Nil"]),
+ isJust : function(m) { return m[0] === "Just"; },
+ isNothing : function(m) { return m[0] === "Nothing"; },
+ fromMaybe : fromMaybe,
+ consMaybe : consMaybe,
+ mapMaybe : function(f) { return List.foldr(mapCons(f))(["Nil"]); },
+ maybe : maybe
+ };
+}();
+
+var String = function() {
+
+ function append(s1) { return function(s2) {
+ return s1.concat(s2);
+ };
+ }
+ function map(f) { return function(s) {
+ for (var i = s.length; i--; ) { s[i] = f(s[i]); }
+ };
+ }
+
+ function intercalate(sep) { return function(ss) {
+ return Value.listToArray(ss).join(sep);
+ };
+ }
+ function intersperse(sep) { return function(s) {
+ return s.split("").join(sep);
+ };
+ }
+
+ function foldl(f) { return function(b) { return function(s) {
+ var acc = b;
+ for (var i = 0, len = s.length; i < len; ++i) { acc = f(s[i])(acc); }
+ return acc;
+ };
+ };
+ }
+ function foldr(f) { return function(b) { return function(s) {
+ var acc = b;
+ for (var i = s.length; i--; ) { acc = f(s[i])(acc); }
+ return acc;
+ };
+ };
+ }
+
+ function concatMap(f) { return function(s) {
+ var a = s.split("");
+ for (var i = a.length; i--; ) { a[i] = f(a[i]); }
+ return a.join("");
+ };
+ }
+
+ function forall(pred) { return function(s) {
+ for (var i = s.length; i--; ) { if (!pred(s[i])) {return false}; }
+ return true;
+ };
+ }
+ function exists(pred) { return function(s) {
+ for (var i = s.length; i--; ) { if (pred(s[i])) {return true}; }
+ return false;
+ };
+ }
+
+ return {cons : append,
+ snoc : append,
+ head : function(s) { return s[0]; },
+ last : function(s) { return s[s.length-1]; },
+ tail : function(s) { return s.slice(1); },
+ length : function(s) { return s.length; },
+ map : map,
+ intercalate : intercalate,
+ intersperse : intersperse,
+ reverse : function(s) { return s.split("").reverse().join(""); },
+ toLower : function(s) { return s.toLowerCase(); },
+ toUpper : function(s) { return s.toUpperCase(); },
+ foldl : foldl,
+ foldr : foldr,
+ concat : function(ss) { return Value.listToArray(ss).join(""); },
+ concatMap : concatMap,
+ forall : forall,
+ exists : exists,
+ /*
+ filter : filter,
+ take:,
+ drop:,
+ */
+ toText : Value.toText,
+ properEscape : Value.properEscape
+ };
+}();
+
+return {String: {toText:Value.toText, properEscape:Value.properEscape},
+ Char:Char,
+ Maybe:Maybe,
+ List:List
+ };
+}();
+var Color = function() {
+
+ var create = function(r,g,b,a) {
+ return { r: r,
+ g: g,
+ b: b,
+ a: a };
+ };
+
+ var extract = function(c) {
+ if (c.a === 1) {
+ return 'rgb(' + c.r + ',' + c.g + ',' + c.b + ')';
+ }
+ return 'rgba(' + c.r + ',' + c.g + ',' + c.b + ',' + c.a + ')';
+ };
+
+ var rgba = function(r) { return function(g) { return function(b) {
+ return function(a) { return create(r,g,b,a); }; }; };
+ };
+
+ var rgb = function(r) { return function(g) { return function(b) {
+ return create(r,g,b,1); }; };
+ };
+ return {black: create( 0, 0, 0,1),
+ white: create(255,255,255,1),
+ red : create(255, 0, 0,1),
+ green: create( 0,255, 0,1),
+ blue : create( 0, 0,255,1),
+
+ gray : create(128,128,128,1),
+ grey : create(128,128,128,1),
+
+ yellow : create(255,255, 0,1),
+ cyan : create( 0,255,255,1),
+ magenta : create(255, 0,255,1),
+
+ rgba : rgba,
+ rgb : rgb,
+ Internal : { extract:extract }
+ };
+}();
+var Element = function() {
+ var newElement = function(elementType) {
+ var e = document.createElement(elementType);
+ e.id = Guid.guid();
+ //e.timesAdded = 0;
+ return e;
+ };
+ var addTo = function(container, elem) {
+ container.appendChild(clone(elem));
+ //elem.timesAdded += 1;
+ };
+ var clone = function(e) {
+ return e;
+ /*if (e.timesAdded === 0) { return e; }
+ if (e.tagName === "CANVAS") {
+ var newCanvas = e.cloneNode(true);
+ var ctx = newCanvas.getContext('2d');
+ ctx.drawImage(e, 0, 0);
+ return newCanvas;
+ } else if (e.tagName === "IMG") {
+ d = image([]);
+ d.src = e.src;
+ d.name = e.name;
+ if (e.style.width !== "") { d.style.width = d.width = e.style.width; }
+ if (e.style.height !== ""){d.style.height = d.height = e.style.height;}
+ return d;
+ } else if (e.hasOwnProperty('isElmLeaf')) {
+ return e.cloneNode(true);
+ } else {
+ d = e.cloneNode(false);
+ var kids = e.childNodes;
+ var len = kids.length;
+ for (var i = 0; i < len; i++) {
+ d.appendChild(clone(kids[i]));
+ }
+ return d;
+ }*/
+ };
+ var divify = function(e) {
+ var div = newElement('div');
+ addTo(div, e);
+ return div;
+ };
+
+ var rectangle = function(w) { return function(h) {
+ var e = newElement('div');
+ e.isElmLeaf = true;
+ e.style.width = w + "px";
+ e.style.height = h + "px";
+ return e;
+ };
+ };
+
+ var makeText = function(w) { return function(pos) { return function(txt) {
+ var e = newElement('div');
+ e.isElmLeaf = true;
+ e.isElmText = true;
+ e.innerHTML = txt;
+ e.style.textAlign = pos;
+ if (w > 0) e.style.width = w + "px";
+ return e;
+ };
+ };
+ };
+ var correctTextSize = function(e) {
+ var w = e.style.width ? e.style.width.slice(0,-2) : 0;
+
+ var t = newElement('div');
+ t.innerHTML = e.innerHTML;
+ t.style.textAlign = e.style.textAlign;
+ if (w > 0) { t.style.width = w + "px"; }
+
+ t.style.visibility = "hidden";
+ t.style.styleFloat = "left";
+ t.style.cssFloat = "left";
+
+ document.body.appendChild(t);
+ var cStyle = window.getComputedStyle(t);
+ if (w <= 0) e.style.width = cStyle.getPropertyValue("width");
+ e.style.height = cStyle.getPropertyValue("height");
+ document.body.removeChild(t);
+ };
+
+ var link = function (href) { return function (e) {
+ var a = newElement('a');
+ a.href = Text.fromString(href);
+ addTo(a,e);
+ return divify(a);
+ };
+ };
+
+ var text = makeText(0)('left');
+ var plainText = function(str) {
+ return makeText(0)("left")(Data.String.toText(str)); };
+ var justifiedText = makeText(0)('justify');
+ var centeredText = makeText(0)('center');
+ var rightedText = makeText(0)('right');
+ var asText = function(v) { return makeText(0)("left")(Value.show(v)); };
+
+ var image = function(src) {
+ var img = newElement('img');
+ img.isElmLeaf = true;
+ img.onload = function() {
+ if (img.style.width === "" && this.width > 0) {
+ img.style.width = img.width = this.width + "px";
+ }
+ if (img.style.height === "" && this.height > 0) {
+ img.style.height = img.height = this.height + "px";
+ }
+ Dispatcher.adjust()
+ };
+ img.src = Data.String.toText(src);
+ img.name = img.src;
+ return img;
+ };
+ var fittedImage = function(w) { return function(h) { return function(src) {
+ var canvas = newElement('canvas');
+ canvas.style.width = w + 'px';
+ canvas.style.height = h + 'px';
+ canvas.width = w;
+ canvas.height = h;
+ canvas.innerHTML = "Your browser does not support the canvas element.";
+ canvas.isElmLeaf = true;
+
+ var img = newElement('img');
+ img.onload = function() {
+ if (canvas.getContext) {
+ var ctx = canvas.getContext('2d');
+ var sx = 0, sy = 0, sWidth = this.width, sHeight = this.height;
+ if (w / h > this.width / this.height) {
+ sHeight = this.width * h / w;
+ sy = (this.height - sHeight) / 2
+ } else {
+ sWidth = this.height * w / h;
+ sx = (this.width - sWidth) / 2
+ }
+ ctx.drawImage(img, sx, sy, sWidth, sHeight,
+ 0,0, canvas.width, canvas.height);
+ }
+ };
+ img.src = Data.String.toText(src);
+ return canvas;
+ };
+ };
+ };
+
+ var video = function(src) {
+ src = Data.String.toText(src);
+ var e = newElement('video');
+ e.controls = "controls";
+ var source = newElement('source');
+ source.src = src;
+ source.type = "video/" + src.substring(src.length - 3, src.length);
+ addTo(e, source);
+ e.isElmLeaf = true;
+ return e;
+ };
+ var audio = function(src) {
+ src = Data.String.toString(src);
+ var e = newElement('video');
+ e.controls = "controls";
+ var source = newElement('source');
+ source.src = src;
+ source.type = "audio/" + src.substring(src.length - 3, src.length);
+ addTo(e, source);
+ e.isElmLeaf = true;
+ return e;
+ };
+ var collage = function(w) { return function(h) { return function(cflist) {
+ var canvas = newElement('canvas');
+ canvas.style.width = w + 'px';
+ canvas.style.height = h + 'px';
+ canvas.width = w;
+ canvas.height = h;
+ if (canvas.getContext) {
+ var ctx = canvas.getContext('2d');
+ ctx.clearRect(0,0, canvas.width, canvas.height);
+ while (cflist[0] === "Cons") {
+ ctx = cflist[1](ctx);
+ cflist = cflist[2];
+ }
+ return canvas;
+ }
+ canvas.innerHTML = "Your browser does not support the canvas element.";
+ canvas.isElmLeaf = true;
+ return canvas;
+ };
+ };
+ };
+
+ var goDown = function(e) {
+ return e.tagName === "DIV" ? e : divify(e);
+ };
+ var goRight = function(e) {
+ e.style.styleFloat = "left";
+ e.style.cssFloat = "left";
+ return e;
+ };
+ var goIn = function(e) {
+ e.style.position = 'absolute';
+ return e;
+ };
+ var flowWith = function(dir, f, elist) {
+ var container = newElement('div');
+ for (var i = elist.length; i--; ) {
+ addTo(container, f(elist[i]));
+ }
+ container.elmFlowDirection = dir;
+ return container;
+ };
+
+ var flow = function(direction) { return function(elist) {
+ var arr = [];
+ while (elist[0] === "Cons") {
+ arr.push(elist[1]);
+ elist = elist[2];
+ }
+ if (direction >= 3) arr.reverse();
+ var f = function(x) { return x; };
+ var dir = direction % 3;
+ if (dir == 0) return flowWith("Y", goDown , arr);
+ if (dir == 1) return flowWith("X", goRight, arr);
+ if (dir == 2) return flowWith("Z", goIn , arr);
+ };
+ };
+
+ var beside = function(a) { return function(b) {
+ return flow(4)(["Cons",a,["Cons",b,["Nil"]]]); }; };
+ var above = function(a) { return function(b) {
+ return flow(3)(["Cons",a,["Cons",b,["Nil"]]]); }; };
+ var below = function(a) { return function(b) {
+ return flow(0)(["Cons",a,["Cons",b,["Nil"]]]); }; };
+
+ var box = function(pos) { return function(e) {
+ e.style.position = "absolute";
+ e.style.margin = "auto";
+ var x = (pos - 1) % 3;
+ var y = (pos - 1) / 3;
+ if (x < 2) e.style.left = 0;
+ if (x > 0) e.style.right = 0;
+ if (y < 2) e.style.top = 0;
+ if (y > 0) e.style.bottom = 0;
+ var div = newElement('div');
+ div.style.position = "relative";
+ addTo(div,e);
+ return div;
+ };
+ };
+
+ var width = function(w) { return function(e) {
+ if (e.tagName === "A") {
+ width(w)(e.firstChild);
+ return e;
+ }
+ if (e.hasOwnProperty('isElmText')) {
+ var d = makeText(w)(e.style.textAlign)(e.innerHTML);
+ e.style.height = d.style.height;
+ }
+ e.style.width = w + "px";
+ return e;
+ };
+ };
+ var height = function(h) { return function(e) {
+ (e.tagName === "A" ? e.firstChild : e).style.height = h + "px";
+ return e;
+ };
+ };
+ var size = function(w) { return function(h) { return function(e) {
+ var d = e.tagName === "A" ? e.firstChild : e;
+ d.style.width = w + "px";
+ d.style.height = h + "px";
+ return e;
+ };
+ };
+ };
+
+ var color = function(c) { return function(e) {
+ e.style.backgroundColor = Color.Internal.extract(c);
+ return e;
+ };
+ };
+ var opacity = function(value) { return function(e) {
+ e.style.opacity = value;
+ return e;
+ };
+ };
+
+ var jsElement = function(w) { return function(h) { return function(elem) {
+ var e = newElement('div');
+ e.isElmLeaf = true;
+ e.style.width = w + "px";
+ e.style.height = h + "px";
+ addTo(e,elem);
+ return e;
+ };
+ };
+ };
+
+ return {text : text,
+ image : image,
+ fittedImage : fittedImage,
+ video : video,
+ audio : audio,
+ collage : collage,
+ flow : flow,
+ layers : flow(2),
+ rectangle : rectangle,
+
+ beside : beside,
+ above : above,
+ below : below,
+ box : box,
+
+ width : width,
+ height : height,
+ size : size,
+ color : color,
+ opacity : opacity,
+
+ link : link,
+ asText : asText,
+ plainText : plainText,
+ justifiedText : justifiedText,
+ centeredText : centeredText,
+ rightedText : rightedText,
+
+ // directions
+ up : 0,
+ left : 1,
+ inward : 2,
+ down : 3,
+ right : 4,
+ outward : 5,
+
+ correctTextSize : correctTextSize,
+ jsElement : jsElement
+ };
+}();
+
+
+
+var Text = function() {
+ var fromString = function(elmList) {
+ if (typeof elmList === "string") return elmList;
+ var a = [];
+ while (elmList[0] === "Cons") {
+ a.push(elmList[1]);
+ elmList = elmList[2];
+ }
+ return Data.String.properEscape(a.join(''));
+ };
+
+ var addTag = function(tag) { return function(text) {
+ return '<' + tag + '>' + text + '' + tag + '>';
+ };
+ };
+ var addStyle = function(style, value) { return function(text) {
+ return "" + text + "";
+ };
+ };
+
+ var typeface = function(name) { return addStyle('font-family', name); };
+ var size = function(px) {
+ return addStyle('font-size', px + 'px');
+ };
+ var header = addTag('h1');
+ var height = function(h) { return addStyle('font-size', h+'em'); }
+ var italic = addStyle('font-style', 'italic');
+ var bold = addTag('b');
+ var color = function(c) {
+ return addStyle('color', Color.Internal.extract(c));
+ };
+ var underline = addStyle('text-decoration', 'underline');
+ var overline = addStyle('text-decoration', 'overline');
+ var strikeThrough = addStyle('text-decoration', 'line-through');
+ var link = function(href) { return function(text) {
+ return "" + text + "";
+ };
+ };
+
+ return {fromString : fromString,
+ toText: fromString,
+ header : header,
+ height : height,
+ italic : italic,
+ bold : bold,
+ underline : underline,
+ overline : overline,
+ strikeThrough : strikeThrough,
+ monospace : typeface('monospace'),
+ color : color,
+ link : link };
+}();
+var Shape = function() {
+
+ var create = function(center, ps, theta, s) {
+ return { center: center, points: ps, theta: theta, scale: s };
+ };
+
+ //// Construction ////
+
+ var polygon = function(ps) { return function(center) {
+ var parr = [];
+ while (ps[0] === "Cons") {
+ parr.push([ps[1][1], ps[1][2]]);
+ ps = ps[2];
+ }
+ center = [center[1], center[2]];
+ return create(center, parr, 0, 1);
+ };
+};
+ var ngon = function(n) { return function(r) { return function(center) {
+ var ps = [];
+ for (var i = n; i--;) {
+ ps.push([r * Math.cos(Math.PI * 2 * i/n),
+ r * Math.sin(Math.PI * 2 * i/n)]);
+ }
+ center = [center[1], center[2]];
+ return create(center, ps, 0, 1);
+ };
+ };
+};
+ var rect = function(w) { return function(h) { return function(center) {
+ var ps = [[- w/2, - h/2],
+ [ w/2, - h/2],
+ [ w/2, h/2],
+ [- w/2, h/2]];
+ center = [center[1], center[2]];
+ return create(center, ps, 0, 1);
+ };
+ };
+ };
+ var oval = function(w) { return function(h) { return function(center) {
+ var ps = [];
+ for (var theta = 2 * Math.PI; theta > 0; theta -= Math.PI /50) {
+ ps.push([w/2 * Math.cos(theta), h/2 * Math.sin(theta)]);
+ }
+ center = [center[1], center[2]];
+ return create(center, ps, 0, 1);
+ };
+ };
+ };
+
+//// Transforms ////
+
+ var move = function(x) { return function(y) { return function(shape) {
+ var newCenter = [x + shape.center[0],y + shape.center[1]];
+ return create(newCenter, shape.points, shape.theta, shape.scale);
+ };
+ };
+ };
+ var rotate = function(theta) { return function(shape) {
+ var newTheta = shape.theta + 2 * Math.PI * theta;
+ return create(shape.center, shape.points, newTheta, shape.scale);
+ };
+ };
+ var scale = function(s) { return function(shape) {
+ return create(shape.center, shape.points, shape.theta, shape.scale * s);
+ };
+ };
+
+
+ //// Atomize ////
+
+ var draw = function(fill) {
+ return function(color) { return function(shape) { return function(ctx) {
+ ctx.save();
+ ctx.translate(shape.center[0], shape.center[1]);
+ ctx.rotate(shape.theta);
+ ctx.scale(shape.scale, shape.scale);
+ ctx.beginPath();
+ var points = shape.points;
+ ctx.moveTo(points[0][0], points[0][1]);
+ for (var i = points.length; i--; ) {
+ ctx.lineTo(points[i][0], points[i][1]);
+ }
+ ctx.closePath();
+ if (fill) {
+ ctx.fillStyle = Color.Internal.extract(color);
+ ctx.fill();
+ } else {
+ ctx.strokeStyle = Color.Internal.extract(color);
+ ctx.stroke();
+ }
+ ctx.restore();
+ return ctx;
+ };
+ };
+ };
+ };
+
+ var customOutline = function(p) {
+ return function(c) { return function(shape) {
+ shape.points.push(shape.points[0]);
+ return Line.customLine(p)(c)(shape);
+ };
+ };
+ };
+ return {polygon:polygon, ngon:ngon, rect:rect, oval:oval,
+ move:move, rotate:rotate, scale:scale,
+ filled:draw(true), outlined:draw(false), customOutline:customOutline };
+}();
+var Line = function() {
+
+ var pair = function(a,b) { return [a,b]; };
+
+ var create = function(center, ps, theta, s) {
+ return { center: center, points: ps, theta: theta, scale: s };
+ };
+
+
+ //// Construction ////
+
+ var line = function(ps) {
+ var parr = [];
+ while (ps[0] === "Cons") {
+ parr.push(pair(ps[1][1], ps[1][2]));
+ ps = ps[2];
+ }
+ return create(pair(0,0), parr, 0, 1);
+ };
+
+ //// Atomize ////
+
+ var solid = function(color) { return function(line) { return function(ctx) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(line.center[0], line.center[1]);
+ ctx.rotate(line.theta);
+ ctx.scale(line.scale, line.scale);
+ var points = line.points;
+ var i = points.length;
+ ctx.moveTo(points[i-1][0], points[i-1][1]);
+ while (i--) {
+ ctx.lineTo(points[i][0], points[i][1]);
+ }
+ ctx.strokeStyle = Color.Internal.extract(color);
+ ctx.stroke();
+ ctx.restore();
+ return ctx;
+ };
+ };
+ };
+
+ var customLine = function(pattern) {
+ return function(color) { return function(line) {
+ if (typeof pattern[0] === "string") {
+ var temp = [];
+ while (pattern[0] === "Cons") {
+ temp.push(pattern[1]);
+ pattern = pattern[2];
+ }
+ pattern = temp;
+ }
+ if (pattern.length === 0) { pattern = [8,4]; }
+ return function(ctx) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.translate(line.center[0], line.center[1]);
+ ctx.rotate(line.theta);
+ ctx.scale(line.scale, line.scale);
+ customLineHelp(ctx, pattern, line.points);
+ ctx.strokeStyle = Color.Internal.extract(color);
+ ctx.stroke();
+ ctx.restore();
+ return ctx;
+ };
+ };
+ };
+ };
+ var customLineHelp = function(ctx, pattern, points) {
+ var i = points.length - 1;
+ var x0 = points[i][0], y0 = points[i][1];
+ var x1=0, y1=0, dx=0, dy=0, remaining=0, nx=0, ny=0;
+ var pindex = 0, plen = pattern.length;
+ var draw = true, segmentLength = pattern[0];
+ ctx.moveTo(x0,y0);
+ while (i--) {
+ x1 = points[i][0]; y1 = points[i][1];
+ dx = x1 - x0; dy = y1 - y0;
+ remaining = Math.sqrt(dx * dx + dy * dy);
+ while (segmentLength <= remaining) {
+ x0 += dx * segmentLength / remaining;
+ y0 += dy * segmentLength / remaining;
+ ctx[draw ? 'lineTo' : 'moveTo'](x0, y0);
+ // update starting position
+ dx = x1 - x0; dy = y1 - y0;
+ remaining = Math.sqrt(dx * dx + dy * dy);
+ // update pattern
+ draw = !draw;
+ pindex = (pindex + 1) % plen;
+ segmentLength = pattern[pindex];
+ }
+ if (remaining > 0) {
+ ctx[draw ? 'lineTo' : 'moveTo'](x1, y1);
+ segmentLength -= remaining;
+ }
+ x0 = x1; y0 = y1;
+ }
+ };
+
+ return {line:line, customLine:customLine, solid:solid,
+ dashed: customLine([8,4]), dotted: customLine([3,3]) };
+}();
+
+var Elm = function() {
+ var send = function(kids, timestep, changed) {
+ for (var i = kids.length; i--; ) {
+ kids[i].recv(timestep, changed);
+ }
+ };
+ var input = function(base) {
+ this.id = Guid.guid();
+ this.value = base;
+ this.kids = [];
+ this.recv = function(timestep, eid, v) {
+ var changed = eid === this.id;
+ if (changed) { this.value = v; }
+ send(this.kids, timestep, changed);
+ };
+ Dispatcher.inputs.push(this);
+ };
+ var lift = function(func,args) {
+ this.id = Guid.guid();
+ this.value = null;
+ this.kids = [];
+ this.inbox = {};
+
+ args.reverse();
+ this.recalc = function() {
+ var f = func;
+ for (var i = args.length; i--; ) {
+ f = f(args[i].value);
+ }
+ this.value = f;
+ };
+ this.recalc();
+
+ this.recv = function(timestep, changed) {
+ if (!this.inbox.hasOwnProperty(timestep)) {
+ this.inbox[timestep] = { changed: false, count: 0 };
+ }
+ var box = this.inbox[timestep];
+ box.count += 1;
+ if (changed) { box.changed = true; }
+ if (box.count == args.length) {
+ if (box.changed) { this.recalc() }
+ send(this.kids, timestep, box.changed);
+ delete this.inbox[timestep];
+ }
+ };
+ for (var i = args.length; i--; ) {
+ args[i].kids.push(this);
+ }
+ };
+ var fold = function(func,base,input) {
+ this.id = Guid.guid();
+ this.value = base;
+ this.kids = [];
+ this.recv = function(timestep, changed) {
+ if (changed) { this.value = func(input.value)(this.value); }
+ send(this.kids, timestep, changed);
+ };
+ input.kids.push(this);
+ };
+
+ var dropIf = function(pred,base,input) {
+ this.id = Guid.guid();
+ this.value = pred(input.value) ? base : input.value;
+ this.kids = [];
+ this.recv = function(timestep, changed) {
+ var chng = changed && !pred(input.value);
+ if (chng) { this.value = input.value; }
+ send(this.kids, timestep, chng);
+ };
+ input.kids.push(this);
+ };
+ var dropRepeats = function(input) {
+ this.id = Guid.guid();
+ this.value = input.value;
+ this.kids = [];
+ this.recv = function(timestep, changed) {
+ var chng = changed && !eq(this.value,input.value);
+ if (chng) { this.value = input.value; }
+ send(this.kids, timestep, chng);
+ };
+ input.kids.push(this);
+ };
+
+ var dropWhen = function(s1) { return function(b) { return function(s2) {
+ var pairs = new lift(function(x){return function(y){return [x,y];};},[s1,s2]);
+ var dropped = new dropIf(function(p){return p[0];},[true,b],pairs);
+ return new lift(function(p){return p[1];},[dropped]); }; };
+ };
+
+ return {Input: function(x) {return new input(x);},
+ Lift: function(f,xs){return new lift(f,xs);},
+ Fold: function(f,b,x){return new fold(f,b,x);},
+ keepIf : function(pred) { return function(base) { return function(sig) {
+ return new dropIf(function(x) { return !pred(x)},base,sig); }; }; },
+ dropIf : function(pred) { return function(base) { return function(sig) {
+ return new dropIf(pred,base,sig); }; }; },
+ keepWhen : function(s) { return dropWhen(new lift(function(b){return !b;},[s])); },
+ dropWhen : dropWhen,
+ dropRepeats : function(s) { return new dropRepeats(s);}
+ };
+}();
+
+var Dispatcher = function() {
+ var program = null;
+ var timestep = 0;
+ var inputs = [];
+
+ var correctSize = function(e) {
+ var kids = e.childNodes;
+ var len = kids.length;
+ if (e.hasOwnProperty('isElmLeaf')) {
+ if (e.hasOwnProperty('isElmText')) { Element.correctTextSize(e); }
+ var w = e.style.width === "" ?
+ 0 : e.style.width.slice(0,-2) - 0;
+ var h = e.style.height === "" ?
+ 0 : e.style.height.slice(0,-2) - 0;
+ return [w, h];
+ }
+ if (len === 1) {
+ var dim = correctSize(kids[0]);
+ if (e.style.width !== "") { dim[0] = e.style.width.slice(0,-2) - 0; }
+ if (e.style.height !== "") { dim[1] = e.style.height.slice(0,-2) - 0; }
+ if (dim[0] !== 0) { e.style.width = dim[0] + "px"; }
+ if (dim[1] !== 0) { e.style.height = dim[1] + "px"; }
+ return dim;
+ }
+ var wmax = 0, hmax = 0, wsum = 0, hsum = 0;
+ var hasWidth = true, hasHeight = true, dim = null;
+ while (len--) {
+ dim = correctSize(kids[len]);
+ wmax = Math.max(wmax, dim[0]);
+ hmax = Math.max(hmax, dim[1]);
+ wsum += dim[0];
+ hsum += dim[1];
+ hasWidth = hasWidth && dim[0] > 0;
+ hasHeight = hasHeight && dim[1] > 0;
+ }
+ var w = wmax, h = hmax, dir = e.elmFlowDirection;
+ if (dir === "X") { w = hasWidth ? wsum : 0; }
+ if (dir === "Y") { h = hasHeight ? hsum : 0; }
+ if (w > 0) e.style.width = w + "px";
+ if (h > 0) e.style.height = h + "px";
+ return [w,h];
+ };
+
+ var initialize = function() {
+ program = ElmCode.main();
+ if (!program.hasOwnProperty('recv')) {
+ program = Elm.Input(program);
+ }
+ var content = document.getElementById('content');
+ content.appendChild(program.value);
+ adjust();
+ var w = document.getElementById('widthChecker').offsetWidth;
+ if (w !== window.innerWidth) {
+ Dispatcher.notify(Window.dimensions.id, Value.Tuple(w, window.innerHeight));
+ }
+ program = Elm.Lift(function(value) {
+ var content = document.getElementById('content');
+ var kid = content.children[0]
+ content.replaceChild(value, kid);
+ delete kid;
+ adjust();
+ return value;
+ }, [program]);
+ };
+ var adjust = function() {
+ var content = document.getElementById('content');
+ correctSize(content.children[0]);
+ }
+ var notify = function(id, v) {
+ timestep += 1;
+ for (var i = inputs.length; i--; ) {
+ inputs[i].recv(timestep, id, v);
+ }
+ };
+ return {initialize:initialize, notify:notify, adjust:adjust, inputs:inputs};
+}();
+var Signal = function() {
+ function toElmString(str) {
+ var out = ["Nil"];
+ for (var i = str.length; i--; ) {
+ out = ["Cons", str[i], out];
+ }
+ return out;
+ }
+ var addListener = function() {
+ if(document.addEventListener) {
+ return function(element, event, handler) {
+ element.addEventListener(event, handler, false);
+ };
+ }
+ else {
+ return function(element, event, handler) {
+ element.attachEvent('on' + event, handler);
+ };
+ }
+ }();
+
+ var Mouse = function() {
+ var position = Elm.Input(Value.Tuple(0,0));
+ var isDown = Elm.Input(false);
+ var isClicked = Elm.Input(false);
+
+ function getXY(e) {
+ var posx = 0;
+ var posy = 0;
+ if (!e) var e = window.event;
+ if (e.pageX || e.pageY) {
+ posx = e.pageX;
+ posy = e.pageY;
+ } else if (e.clientX || e.clientY) {
+ posx = e.clientX + document.body.scrollLeft +
+ document.documentElement.scrollLeft;
+ posy = e.clientY + document.body.scrollTop +
+ document.documentElement.scrollTop;
+ }
+ return Value.Tuple(posx, posy);
+ }
+
+ addListener(document, 'click', function(e) {
+ Dispatcher.notify(isClicked.id, true);
+ Dispatcher.notify(isClicked.id, false);
+ });
+ addListener(document, 'mousedown', function(e) {
+ Dispatcher.notify(isDown.id, true); });
+ addListener(document, 'mouseup', function(e) {
+ Dispatcher.notify(isDown.id, false); });
+ addListener(document, 'mousemove', function(e) {
+ Dispatcher.notify(position.id, getXY(e)); });
+ var clickedOn = function(elem) {
+ var click = Elm.Input(false);
+ addListener(elem, 'click', function(e) {
+ Dispatcher.notify(click.id, true);
+ Dispatcher.notify(click.id, false);
+ });
+ return Value.Tuple(elem, click);
+ };
+ return {position: position,
+ x: Elm.Lift(function(p){return p[1];},[position]),
+ y: Elm.Lift(function(p){return p[2];},[position]),
+ isClicked: isClicked,
+ isDown: isDown,
+ clickedOn: clickedOn
+ };
+ }();
+
+ var Time = function() {
+ var every = function(t) {
+ t *= 1000;
+ var clock = Elm.Input(0);
+ var time = 0;
+ setInterval(function() {
+ time += t;
+ Dispatcher.notify(clock.id, time/1000);
+ }, t);
+ return clock;
+ };
+ var after = function(t) {
+ t *= 1000;
+ var thread = Elm.Input(false);
+ setTimeout(function() { Dispatcher.notify(thread.id, true); }, t);
+ return thread;
+ };
+ var before = function(t) {
+ t *= 1000;
+ var thread = Elm.Input(true);
+ setTimeout(function() { Dispatcher.notify(thread.id, false); }, t);
+ return thread;
+ };
+ return {every:every,after:after,before:before};
+ }();
+
+ var Window = function() {
+ var dimensions = Elm.Input(Value.Tuple(window.innerWidth,window.innerHeight));
+ addListener(window, 'resize', function(e) {
+ var w = document.getElementById('widthChecker').offsetWidth;
+ Dispatcher.notify(dimensions.id, Value.Tuple(w, window.innerHeight));
+ });
+ return {dimensions:dimensions,
+ width : Elm.Lift(function(p){return p[1];},[dimensions]),
+ height: Elm.Lift(function(p){return p[2];},[dimensions]) };
+ }();
+
+ var Keyboard = { Raw : function() {
+ var keysDown = Elm.Input(["Nil"]);
+ var charPressed = Elm.Input(["Nothing"]);
+ function remove(x,xs) {
+ if (xs[0] === "Nil") return xs;
+ if (xs[1] === x) return xs[2];
+ return ["Cons", xs[1], remove(x,xs[2])];
+ }
+ function has(x,xs) {
+ while (xs[0] !== "Nil") {
+ if (xs[1] === x) return true;
+ xs = xs[2];
+ }
+ return false;
+ }
+ addListener(document, 'keydown', function(e) {
+ if (has(e.keyCode, keysDown.value)) return;
+ Dispatcher.notify(keysDown.id, ["Cons", e.keyCode, keysDown.value]);
+ });
+ addListener(document, 'keyup', function(e) {
+ var codes = remove(e.keyCode, keysDown.value)
+ Dispatcher.notify(keysDown.id, codes);
+ });
+ addListener(window, 'blur', function(e) {
+ Dispatcher.notify(keysDown.id, ["Nil"]);
+ });
+ addListener(document, 'keypress', function(e) {
+ Dispatcher.notify(charPressed.id, ["Just",e.charCode || e.keyCode]);
+ Dispatcher.notify(charPressed.id, ["Nothing"]);
+ });
+ return {keysDown:keysDown,
+ charPressed:charPressed};
+ }()
+ };
+
+ var HTTP = function() {
+ var fetch = function(how) { return function(url) {
+ var thread = Elm.Input(["Waiting"]);
+ var request = {};
+ if (window.XMLHttpRequest) { request = new XMLHttpRequest(); }
+ else if (window.ActiveXObject) { request = new ActiveXObject("Microsoft.XMLHTTP"); }
+ request.onreadystatechange = function(e) {
+ if (request.readyState === 4) {
+ Dispatcher.notify(thread.id,
+ request.status === 200
+ ? ["Success", toElmString(request.responseText)]
+ : ["Failure", request.status, toElmString(request.statusText)]);
+ }
+ };
+ request.open(how, Data.String.toText(url), true);
+ request.send(null);
+ return thread;
+ };
+ };
+ var fetches = function(how) { return function(input) {
+ var output = Elm.Input(["Nothing"]);
+ var fetcher = Elm.Lift(update, [input]);
+ var combine = Elm.Lift(function(x) { return function(y) { return x; } }, [output,fetcher]);
+ function update(strOpt) {
+ if (strOpt[0] !== "Just") {
+ try { Dispatcher.notify(output.id, ["Nothing"]); } catch(e) {}
+ return [];
+ }
+ try {
+ Dispatcher.notify(output.id, ["Just", ["Waiting"]]);
+ } catch(e) { output.value = ["Just", ["Waiting"]]; }
+ var request = {};
+ if (window.XMLHttpRequest) { request = new XMLHttpRequest(); }
+ else if (window.ActiveXObject) { request = new ActiveXObject("Microsoft.XMLHTTP"); }
+ request.onreadystatechange = function(e) {
+ if (request.readyState === 4) {
+ Dispatcher.notify(output.id,
+ ["Just", request.status === 200
+ ? ["Success", toElmString(request.responseText)]
+ : ["Failure", request.status, toElmString(request.statusText)]]);
+ }
+ };
+ request.open(how, Data.String.toText(strOpt[1]), true);
+ request.send(null);
+ return [];
+ }
+ return combine;
+ };
+ };
+ return {get : fetch("GET"), post : fetch("POST"),
+ gets : fetches("GET"), posts : fetches("POST") };
+ }();
+ var Random = function() {
+ var inRange = function(min) { return function(max) {
+ return Elm.Input(Math.floor(Math.random() * (max-min+1)) + min);
+ };
+ };
+ var randomize = function(min) { return function(max) { return function(signal) {
+ return Elm.Lift(function(x) { return Math.floor(Math.random() * (max-min+1)) + min;},[signal]);
+ };
+ };
+ };
+ return { inRange:inRange, randomize:randomize };
+ }();
+ var Input = function() {
+ var newTextInput = function(elem, ghostText) {
+ elem.isElmLeaf = true;
+ var str = Elm.Input(["Nil"]);
+ addListener(elem, 'keyup', function(e) {
+ Dispatcher.notify(str.id, toElmString(elem.value));
+ elem.focus();
+ });
+ return Value.Tuple(elem, str);
+ };
+ var newElement = function(name) {
+ var e = document.createElement(name);
+ e.id = Guid.guid();
+ return e;
+ };
+ var textArea = function(cols) { return function(rows) {
+ var textarea = newElement('textarea');
+ textarea.rows = rows;
+ textarea.cols = cols;
+ return newTextInput(textarea, "");
+ };
+ };
+ var textField = function(ghostText) {
+ var field = newElement('input');
+ field.type = 'text';
+ return newTextInput(field, ghostText);
+ };
+ var password = function(ghostText) {
+ var field = newElement('input');
+ field.type = 'password';
+ return newTextInput(field, ghostText);
+ };
+ var checkbox = function(checked) {
+ var box = newElement('input');
+ box.type = 'checkbox';
+ box.checked = checked;
+ var status = Elm.Input(checked);
+ addListener(box, 'change', function(e) {
+ Dispatcher.notify(status.id, box.checked);
+ });
+ return Value.Tuple(box, status);
+ };
+ var dropDown = function(options) {
+ var slct = newElement('select');
+ var opts = [];
+ while (options[0] === "Cons") {
+ var opt = newElement('option');
+ var str = Text.toText(options[1][1]);
+ opt.value = str;
+ opt.innerHTML = str;
+ slct.appendChild(opt);
+ opts.push(options[1][2]);
+ options = options[2];
+ }
+ var status = Elm.Input(opts[0]);
+ addListener(slct, 'change', function(e) {
+ Dispatcher.notify(status.id, opts[slct.selectedIndex]);
+ });
+ return Value.Tuple(slct, status);
+ };
+ var stringDropDown = function(opts) {
+ return dropDown(List.map (function(x) {return Value.Tuple(x,x);}) (opts));
+ };
+ var button = function(name) {
+ var b = newElement('input');
+ b.type = "button";
+ b.value = Text.toText(name);
+ var press = Elm.Input(false);
+ addListener(b, 'click', function(e) {
+ Dispatcher.notify(press.id, true);
+ Dispatcher.notify(press.id, false);
+ });
+ return Value.Tuple(b,press);
+ };
+ return {textArea:textArea, textField:textField,
+ password:password, checkbox:checkbox,
+ dropDown:dropDown, stringDropDown:stringDropDown,
+ button:button};
+ }();
+
+ return {Mouse:Mouse,
+ Keyboard:Keyboard,
+ Time:Time,
+ Window:Window,
+ HTTP:HTTP,
+ Random:Random,
+ Input:Input,
+ constant : function(v) { return Elm.Input(v); },
+ lift : function(f){return function(e){return Elm.Lift(f,[e]);};},
+ lift2 : function(f) { return function(e1) { return function(e2) {
+ return Elm.Lift(f, [e1,e2]); }; }; },
+ lift3 : function(f) { return function(e1) { return function(e2) {
+ return function(e3){return Elm.Lift(f,[e1,e2,e3]);};};};},
+ lift4 : function(f) { return function(e1) { return function(e2) {
+ return function(e3) { return function(e4) {
+ return Elm.Lift(f, [e1,e2,e3,e4]); }; }; }; }; },
+ foldp : function(f) { return function(b) { return function(e) {
+ return Elm.Fold(f,b,e); }; }; },
+ count : function(sig){return Elm.fold(function(c){return c+1},0,sig)},
+ keepIf : Elm.keepIf,
+ dropIf : Elm.dropIf,
+ keepWhen : Elm.keepWhen,
+ dropWhen : Elm.dropWhen,
+ dropRepeats : Elm.dropRepeats
+ };
+}();
+var Prelude = function() {
+ var mod = function(x) { return function(y) {
+ var r = x % y;
+ var m = x==0 ? 0 : (y>0 ? (x>=0 ? r : r+y) : -mod(-x)(-y));
+ return m == y ? 0 : m;
+ }; };
+
+ var min = function(x) { return function(y) { return Math.min(x,y); }; };
+ var max = function(x) { return function(y) { return Math.max(x,y); }; };
+
+ var flip=function(f){return function(x){return function(y){return f(y)(x);};};};
+ var clamp = function(lo) { return function(hi) {
+ return function(x) { return Math.min(hi, Math.max(lo, x)); };
+ };
+ };
+ var curry = function(f) { return function(x) { return function(y) {
+ return f(["Tuple2",x,y]); }; };
+ };
+ var uncurry = function(f) { return function(p) {
+ if (p[0] !== "Tuple2") {
+ throw "Function was uncurry'd but was not given a pair.";
+ }
+ return f(p[1])(p[2]); };
+ };
+
+ var logBase=function(b){return function(x){return Math.log(x)/Math.log(b);};};
+
+ return {id : function(x) { return x; },
+ not : function(b) { return !b; },
+ fst : function(p) { return p[1]; },
+ snd : function(p) { return p[2]; },
+ rem : function(x) { return function(y) { return x % y; }; },
+ sqrt : Math.sqrt,
+ abs : Math.abs,
+ pi : Math.PI,
+ sin : Math.sin,
+ cos : Math.cos,
+ tan : Math.tan,
+ asin : Math.asin,
+ acos : Math.acos,
+ atan : Math.atan,
+ mod : mod,
+ min : min,
+ max : max,
+ flip : flip,
+ clamp : clamp,
+ curry : curry,
+ uncurry : uncurry,
+ logBase : logBase,
+ Just : Data.Maybe.Just,
+ Nothing : Data.Maybe.Nothing,
+ maybe : Data.Maybe.maybe,
+ map : Data.List.map,
+ filter : Data.List.filter,
+ head : Data.List.head,
+ tail : Data.List.tail,
+ length : Data.List.length,
+ reverse : Data.List.reverse,
+ foldr : Data.List.foldr,
+ foldr1 : Data.List.foldr1,
+ foldl : Data.List.foldl,
+ foldl1 : Data.List.foldl1,
+ and : Data.List.and,
+ or : Data.List.or,
+ forall : Data.List.forall,
+ exists : Data.List.exists,
+ sum : Data.List.sum,
+ product : Data.List.product,
+ concat : Data.List.concat,
+ concatMap : Data.List.concatMap,
+ maximum : Data.List.maximum,
+ minimum : Data.List.minimum,
+ scanl : Data.List.scanl,
+ scanl1 : Data.List.scanl1,
+ take : Data.List.take,
+ drop : Data.List.drop,
+ lift : Signal.lift,
+ lift2 : Signal.lift2,
+ lift3 : Signal.lift3,
+ lift4 : Signal.lift4,
+ foldp : Signal.foldp,
+ constant : Signal.constant,
+ count : Signal.count,
+ keepIf : Signal.keepIf,
+ dropIf : Signal.dropIf,
+ keepWhen : Signal.keepWhen,
+ dropWhen : Signal.dropWhen,
+ dropRepeats : Signal.dropRepeats
+ };
+}();
+var eq = Value.eq;
+
+var includeGlobal = this;
+(function() {
+ var include = function(library) {
+ for (var i in library) {
+ if (i === 'Internal') continue;
+ try {
+ includeGlobal[i] = library[i];
+ } catch (err) {
+ if (i === 'length') {
+ includeGlobal.execScript('var length;');
+ length = library[i];
+ continue;
+ }
+ }
+ }
+ };
+ var includeAs = function(name) { return function(library) {
+ includeGlobal[name] = includeGlobal[name] || {};
+ for (var i in library) {
+ if (i === 'Internal') continue;
+ includeGlobal[name][i] = library[i];
+ }
+ };
+ };
+ include (Element);
+ include (Text);
+
+ color = Element.color;
+ height = Element.height;
+ show = Value.show;
+
+ include (Color);
+ include (Shape);
+ include (Line);
+
+ includeAs ('Time') (Signal.Time);
+ includeAs ('Mouse') (Signal.Mouse);
+ includeAs ('Keyboard') (Signal.Keyboard);
+ includeAs ('Window') (Signal.Window);
+ includeAs ('HTTP') (Signal.HTTP);
+ includeAs ('Input') (Signal.Input);
+ includeAs ('Random') (Signal.Random);
+
+}());
+
+var ElmCode = {};
+ElmCode.Data = Data;
+ElmCode.Signal = Signal;
+ElmCode.Data.List = List;
+ElmCode.Foreign = Foreign;
+ElmCode.Prelude = Prelude;
\ No newline at end of file
diff --git a/elm/src/Ast.hs b/elm/src/Ast.hs
index dd5f4b49d..df32deda2 100644
--- a/elm/src/Ast.hs
+++ b/elm/src/Ast.hs
@@ -6,7 +6,7 @@ import Data.List (intercalate)
import Types
import Guid
-data Module = Module [String] Exports Imports Defs [JSFFI]
+data Module = Module [String] Exports Imports Defs JSFFI
type Exports = [String]
@@ -15,10 +15,8 @@ data ImportMethod = As String | Hiding [String] | Importing [String]
type Defs = [(String,Expr)]
-data JSFFI = ImportValue String String Type
- | ExportValue String String Type
- | ImportEvent String String Expr Type
- | ExportEvent String String Type
+type JSFFI = ( [(String, Expr, String, Type)]
+ , [(String, String, Type)] )
data Pattern = PData String [Pattern] | PVar String | PAnything
diff --git a/elm/src/Compiler.hs b/elm/src/Compiler.hs
index 38610f6b0..49d662e0a 100644
--- a/elm/src/Compiler.hs
+++ b/elm/src/Compiler.hs
@@ -20,38 +20,39 @@ data ELM =
, runtime :: Maybe FilePath
, separate_js :: Bool
, only_js :: Bool
--- , verbose_js :: Bool
+ , import_js :: [FilePath]
}
deriving (Data,Typeable,Show,Eq)
elm = ELM { make = False &= help "automatically compile dependencies."
, files = def &= args &= typ "FILES"
- , runtime = Nothing &= typ "FILE" &=
+ , runtime = Nothing &= typFile &=
help "Specify a custom location for Elm's runtime system."
, separate_js = False &= help "Compile to separate HTML and JS files."
, only_js = False &= help "Compile only to JavaScript."
--- , verbose_js = False &= help "Produce JavaScript that may be easier to debug."
+ , import_js = [] &= typFile &= help "Include a JavaScript file before the body of the Elm program. Can be used many times. Files will be included in the given order."
} &=
help "Compile Elm programs to HTML, CSS, and JavaScript." &=
- summary "The Elm Compiler v0.3.0, (c) Evan Czaplicki"
+ summary "The Elm Compiler v0.3.5, (c) Evan Czaplicki"
main = do
args <- cmdArgs elm
- mini <- getDataFileName "elm-runtime-0.3.0.js"
+ mini <- getDataFileName "elm-runtime-0.3.5.js"
compileArgs mini args
-compileArgs mini (ELM _ [] _ _ _) =
+compileArgs mini (ELM _ [] _ _ _ _) =
putStrLn "Usage: elm [OPTIONS] [FILES]\nFor more help: elm --help"
-compileArgs mini (ELM make files rtLoc split only) =
- mapM_ (fileTo get what $ fromMaybe mini rtLoc) files
+compileArgs mini (ELM make files rtLoc split only js) =
+ mapM_ (fileTo get what js $ fromMaybe mini rtLoc) files
where get = if make then getModules [] else getModule
what = if only then JS else
if split then Split else HTML
data What = JS | HTML | Split
-fileTo get what rtLoc file = do
+fileTo get what jsFiles rtLoc file = do
ems <- get file
+ jss <- concat `fmap` mapM readFile jsFiles
case ems of
Left err -> putStrLn $ "Error while compiling " ++ file ++ ":\n" ++ err
Right ms ->
@@ -59,18 +60,18 @@ fileTo get what rtLoc file = do
js = name ++ ".js"
html = name ++ ".html"
in case what of
- JS -> writeFile js $ concatMap jsModule ms
- HTML -> writeFile html . renderHtml $ modulesToHtml rtLoc ms
+ JS -> writeFile js $ jss ++ concatMap jsModule ms
+ HTML -> writeFile html . renderHtml $ modulesToHtml "" rtLoc jss ms
Split -> do
writeFile html . renderHtml $ linkedHtml rtLoc js ms
- writeFile js $ concatMap jsModule ms
+ writeFile js $ jss ++ concatMap jsModule ms
getModules :: [String] -> FilePath -> IO (Either String [Module])
getModules uses file = do
code <- readFile file
case initialize code of
Left err -> return . Left $ "Error in " ++ file ++ ":\n" ++ err
- Right modul@(Module _ _ imports _) ->
+ Right modul@(Module _ _ imports _ _) ->
let imps = filter (`notElem` builtInModules) $ map fst imports in
case intersect uses imps of
x:_ -> return . Left $ "Error: Cyclic dependency. Module " ++
diff --git a/elm/src/Gen/CompileToJS.hs b/elm/src/Gen/CompileToJS.hs
index ba12d2be0..7ac703c96 100644
--- a/elm/src/Gen/CompileToJS.hs
+++ b/elm/src/Gen/CompileToJS.hs
@@ -14,9 +14,9 @@ showErr :: String -> String
showErr err = mainEquals $ "text(monospace(" ++ msg ++ "))"
where msg = show . concatMap (++"
") . lines $ err
-parens = ("("++) . (++")")
-braces = ("{"++) . (++"}")
-jsList = ("["++) . (++"]") . intercalate ","
+parens s = "(" ++ s ++ ")"
+braces s = "{" ++ s ++ "}"
+jsList ss = "["++ intercalate "," ss ++"]"
jsFunc args body = "function(" ++ args ++ "){" ++ body ++ "}"
assign x e = "\nvar " ++ x ++ "=" ++ e ++ ";"
ret e = "\nreturn "++ e ++";"
@@ -38,14 +38,14 @@ tryBlock names e =
]
-jsModule (Module names exports imports defs (ims,exs)) =
+jsModule (Module names exports imports defs foreigns) =
tryBlock (tail modNames) $ concat
[ concatMap (\n -> globalAssign n $ n ++ " || {}") .
map (intercalate ".") . drop 2 . inits $
take (length modNames - 1) modNames
, "\nif (" ++ modName ++ ") throw \"Module name collision, '" ++
intercalate "." (tail modNames) ++ "' is already defined.\"; "
- , globalAssign modName $ jsFunc "" (includes++body++export)++"()"
+ , globalAssign modName $ jsFunc "" (includes++ims++body++exs++export)++"()"
, mainEquals $ modName ++ ".main" ]
where modNames = if null names then ["ElmCode", "Main"] else "ElmCode" : names
modName = intercalate "." modNames
@@ -57,23 +57,23 @@ jsModule (Module names exports imports defs (ims,exs)) =
getNames (x,_) =
let y = reverse . tail . dropWhile isDigit $ reverse x in
if y `elem` exps then Just $ y ++ ":" ++ x else Nothing
+ (ims,exs) = let (i,e) = foreigns in
+ (concatMap importEvent i, concatMap exportEvent e)
-ffi (ImportValue js elm _) = assign elm js
-ffi (ExportValue js elm _) = assign js elm
-ffi (ImportEvent js elm base _) =
+importEvent (js,base,elm,_) =
concat [ "var " ++ elm ++ " = Elm.Input(" ++ toJS base ++ ");"
, "Signal.addListener(document, '" ++ js
, "', function(e) { Dispatcher.notify(" ++ elm
- , ".id, e); });" ]
-ffi (ExportEvent js elm base _) =
+ , ".id, e.value); });" ]
+exportEvent (js,elm,_) =
concat [ "lift (function(v) { var e = document.createEvent('Event');"
, "e.initEvent('" ++ js ++ "', true, true);"
- , "e.elmValue = v;"
- , "document.disspatchEvent(e); return v; })(" ++ elm ++ ")"
+ , "e.value = v;"
+ , "document.dispatchEvent(e); return v; })(" ++ elm ++ ")"
]
jsImport (modul, how) =
- concat [ "\ntry{" ++ modul ++ "} catch(e) {throw \"Module '"
+ concat [ "\ntry{" ++ modul ++ " instanceof Object} catch(e) {throw \"Module '"
, drop 1 (dropWhile (/='.') modul)
, "' is missing. Compile with --make flag or load missing "
, "module in a separate JavaScript file.\";}" ] ++
@@ -96,7 +96,7 @@ toJS expr =
Number n -> show n
Var x -> x
Chr c -> show c
- Str s -> toJS . list $ map Chr s
+ Str s -> "Value.str" ++ parens (show s)
Boolean b -> if b then "true" else "false"
Range lo hi -> jsRange (toJS lo) (toJS hi)
Access e lbl -> toJS e ++ "." ++ lbl
diff --git a/elm/src/Gen/GenerateHtml.hs b/elm/src/Gen/GenerateHtml.hs
index dc61ab0ab..caab0e351 100644
--- a/elm/src/Gen/GenerateHtml.hs
+++ b/elm/src/Gen/GenerateHtml.hs
@@ -44,17 +44,21 @@ generateHtml :: String -- ^ Location of elm-min.js as expected by the browser
generateHtml libLoc title source =
case initialize source of
Left err -> createHtml libLoc title (Right $ showErr err) (H.noscript "")
- Right modul -> modulesToHtml libLoc [modul]
+ Right modul -> modulesToHtml title libLoc [] [modul]
-modulesToHtml libLoc modules = createHtml libLoc title js noscript
- where title = (\(Module names _ _ _) -> intercalate "." names) $ last modules
- js = Right $ concatMap jsModule modules
+modulesToHtml title libLoc jss modules = createHtml libLoc title' js noscript
+ where js = Right $ jss ++ concatMap jsModule modules
noscript = extract $ last modules
+ title' = if null title then altTitle else title
+ altTitle = (\(Module names _ _ _ _) -> intercalate "." names) $
+ last modules
+
linkedHtml rtLoc jsLoc modules =
createHtml rtLoc title (Left jsLoc) (H.noscript "")
- where title = (\(Module names _ _ _) -> intercalate "." names) $ last modules
+ where title = (\(Module names _ _ _ _) -> intercalate "." names) $
+ last modules
createHtml libLoc title js noscript =
diff --git a/elm/src/Initialize.hs b/elm/src/Initialize.hs
index dc7b86045..f368c7bb3 100644
--- a/elm/src/Initialize.hs
+++ b/elm/src/Initialize.hs
@@ -1,6 +1,7 @@
module Initialize (initialize) where
import Control.Monad.Error
+import Data.List (lookup)
import Ast
import Parser (parseProgram)
@@ -10,18 +11,21 @@ import Rename
import Optimize
initialize str = do
- (Module name ex im defs jsffi, tipes) <- parseProgram str
- let expr = rename . Let defs $ Var "_"
- let allHints = liftM2 (\a b -> a ++ b ++ map ffiHint jsffi) hints tipes
- subs <- unify allHints expr
- let Let defs' _ = optimize expr
+ (Module name ex im defs (ims,exs), tipes) <- parseProgram str
+ let (Let ds _) = rename . Let defs $ Var "_"
+ let dict n = maybe n id . lookup n $ zip (map fst defs) (map fst ds)
+ let allHints = liftM2 (\a b -> a ++ b ++ ffiHints (ims,exs)) hints tipes
+ subs <- unify allHints . Let ds $ checkFFI dict ims exs
+ let Let defs' _ = optimize (Let ds $ Var "_")
let im' = if any ((=="Prelude") . fst) im then im else
("Prelude", Importing []):im
- return (subs `seq` Module name ex im' defs' jsffi)
+ let exs' = map (\(js,n,t) -> (js,dict n,t)) exs
+ return (subs `seq` Module name ex im' defs' (ims,exs'))
-ffiHint x = case x of
- ImportValue _ n t -> (n,t)
- ExportValue _ n t -> (n,t)
- ImportEvent _ n _ t -> (n,t)
- ExportEvent _ n t -> (n,t)
+ffiHints (ims,exs) =
+ map (\(_,_,n,t) -> (n,t)) ims ++ map (\(_,n,t) -> (n,t)) exs
+
+checkFFI dict ims exs = list $ ims' ++ exs'
+ where ims' = map (\(_,b,n,_)-> Binop "==" (Var n) (App (Var "constant") b)) ims
+ exs' = map (\(_,n,_) -> Binop "==" (Var n) (Var $ dict n)) exs
\ No newline at end of file
diff --git a/elm/src/Parse/ParseForeign.hs b/elm/src/Parse/ParseForeign.hs
index 288463736..e3c771028 100644
--- a/elm/src/Parse/ParseForeign.hs
+++ b/elm/src/Parse/ParseForeign.hs
@@ -11,50 +11,37 @@ import ParseExpr (term)
import ParseTypes
-foreignDefs = option [] $ do
- f <- commitIf (reserved "foreign") foreign
+foreignDefs = do
+ f <- foreign --commitIf (reserved "foreign") foreign
fs <- many (commitIf (freshLine >> reserved "foreign") (freshLine >> foreign))
- return (f:fs)
+ return . partitionEithers $ f:fs
-foreign = do reserved "foreign" ; whitespace ; (jsEvent <|> jsValue)
-
-
-jsValue = do
- what <- try $ choice
- [ try (reserved "import" >> whitespace >>
- reserved "jsValue" >> whitespace >> return ImportValue)
- , try (reserved "export" >> whitespace >>
- reserved "jsValue" >> whitespace >> return ExportValue) ]
- js <- jsVar ; whitespace
- elm <- lowVar ; whitespace
- tipe <- typeExpr
- either fail (return . what js elm) (toForeignType tipe)
-
-
-jsEvent = importEvent <|> exportEvent
+foreign = do try (reserved "foreign") ; whitespace
+ Left <$> importEvent <|> Right <$> exportEvent
exportEvent = do
- try (reserved "export" >> whitespace >> reserved "jsEvent" >> whitespace)
+ try (reserved "export" >> whitespace >> reserved "jsevent" >> whitespace)
js <- jsVar ; whitespace
elm <- lowVar ; whitespace
string "::" ; whitespace
tipe <- typeExpr
case tipe of
ADTPT "Signal" [pt] ->
- either fail (return . ExportEvent js elm) (toForeignType pt)
+ either fail (return . (,,) js elm) (toForeignType pt)
_ -> fail "When exporting events, the exported value must be a Signal."
importEvent = do
- try (reserved "import" >> whitespace >> reserved "jsEvent" >> whitespace)
- js <- jsVar ; whitespace
- base <- term ; whitespace
- elm <- lowVar ; whitespace
- string "::" ; whitespace
+ try (reserved "import" >> whitespace >> reserved "jsevent" >> whitespace)
+ js <- jsVar ; whitespace
+ base <- term > "Base case for imported signal (signals cannot be undefined)"
+ whitespace
+ elm <- lowVar > "Name of imported signal"
+ whitespace ; string "::" ; whitespace
tipe <- typeExpr
case tipe of
ADTPT "Signal" [pt] ->
- either fail (return . ImportEvent js elm base) (toForeignType pt)
+ either fail (return . (,,,) js base elm) (toForeignType pt)
_ -> fail "When importing events, the imported value must be a Signal."
diff --git a/elm/src/Parse/ParseLib.hs b/elm/src/Parse/ParseLib.hs
index dcb30f68c..cafe30b0e 100644
--- a/elm/src/Parse/ParseLib.hs
+++ b/elm/src/Parse/ParseLib.hs
@@ -11,7 +11,8 @@ reserveds = [ "if", "then", "else"
, "case", "of", "data"
, "let", "in"
, "module", "where"
- , "import", "as", "hiding" ]
+ , "import", "as", "hiding"
+ , "export", "foreign" ]
expecting = flip (>)
diff --git a/elm/src/Parse/ParseTypes.hs b/elm/src/Parse/ParseTypes.hs
index 1af45405b..6f06f4cf9 100644
--- a/elm/src/Parse/ParseTypes.hs
+++ b/elm/src/Parse/ParseTypes.hs
@@ -83,14 +83,20 @@ toType pairs outType (name,args) =
_ -> ADT x []
toForeignType (LambdaPT t1 t2) =
- LambdaT <$> toForeignType t1 <*> toForeignType t2
-toForeignType (ADTPT name args) = do
- case name == "JSArray" of
- True -> ADT name <$> mapM toForeignType args
- False -> Left $ "'" ++ name ++
- "' is not an exportable type constructor. Only 'JSArray' is exportable."
+ fail $ "Elm's JavaScript event interface does not yet handle functions. " ++
+ "Only simple values can be imported and exported in this release."
+ --LambdaT <$> toForeignType t1 <*> toForeignType t2
+toForeignType (ADTPT name args)
+ | isJsStructure name = ADT name <$> mapM toForeignType args
+ | otherwise =
+ Left $ "'" ++ name ++ "' is not an exportable type " ++
+ "constructor. Only 'JSArray' and 'JSTupleN' are exportable."
+
toForeignType (VarPT x@(c:_))
| isLower c =
Left "All exported types must be concrete types (JSNumber, JSString, etc.)"
- | x `elem` [ "JSString", "JSNumber", "JSElement" ] = Right (ADT x [])
- | otherwise = Left $ "'" ++ x ++ "' is not an exportable type. JSNumber, JSString, JSElement, and (JSArray a) are exportable."
\ No newline at end of file
+ | x `elem` ["JSString","JSNumber","JSElement","JSBool"] = Right (ADT x [])
+ | otherwise = Left $ "'" ++ x ++ "' is not an exportable type. Only JSTypes are exportable."
+
+isJsStructure name = name == "JSArray" || isTuple
+ where isTuple = "JSTuple" == take 7 name && name `elem` map show [2..5]
\ No newline at end of file
diff --git a/elm/src/Parse/Parser.hs b/elm/src/Parse/Parser.hs
index 99c2b0f71..1ed85a3f0 100644
--- a/elm/src/Parse/Parser.hs
+++ b/elm/src/Parse/Parser.hs
@@ -27,11 +27,10 @@ defs = do
program = do
optional freshLine
- (names,exports) <- option ([],[]) (moduleDef `followedBy` freshLine)
+ (names,exports) <- option (["Main"],[]) (moduleDef `followedBy` freshLine)
is <- (do try (lookAhead $ reserved "import")
imports `followedBy` freshLine) <|> return []
- jsffi <- foreignDefs
- freshLine
+ jsffi <- foreignDefs `followedBy` freshLine <|> return ([],[])
(vs,es,ts) <- defs
optional freshLine ; optional spaces ; eof
return (Module names exports is (zip vs es) jsffi, zip vs `liftM` ts)
diff --git a/elm/src/Types/Hints.hs b/elm/src/Types/Hints.hs
index ad69bee44..27d00c535 100644
--- a/elm/src/Types/Hints.hs
+++ b/elm/src/Types/Hints.hs
@@ -49,6 +49,35 @@ shapes = [ "polygon" -: listOf point ==> point ==> shape
] ++ hasType (IntT ==> IntT ==> point ==> shape) ["ngon","rect","oval"]
+-------- Foreign --------
+
+casts =
+ [ "castJSBoolToBool" -: jsBool ==> BoolT
+ , "castBoolToJSBool" -: BoolT ==> jsBool
+ , "castJSNumberToInt" -: jsNumber ==> IntT
+ , "castIntToJSNumber" -: IntT ==> jsNumber
+ , "castJSElementToElement" -: jsElement ==> element
+ , "castElementToJSElement" -: element ==> jsElement
+ , "castJSStringToString" -: jsString ==> string
+ , "castStringToJSString" -: string ==> jsString
+ -- , "castJSNumberToFloat -:
+ -- , "castFloatToJSNumber -:
+ ]
+
+polyCasts = sequence
+ [ do a <- var ; "castJSArrayToList" -:: jsArray a ==> listOf a
+ , do a <- var ; "castListToJSArray" -:: listOf a ==> jsArray a
+ , do vs <- vars 2 ; "castTupleToJSTuple2" -:: tupleOf vs ==> jsTuple vs
+ , do vs <- vars 3 ; "castTupleToJSTuple3" -:: tupleOf vs ==> jsTuple vs
+ , do vs <- vars 4 ; "castTupleToJSTuple4" -:: tupleOf vs ==> jsTuple vs
+ , do vs <- vars 5 ; "castTupleToJSTuple5" -:: tupleOf vs ==> jsTuple vs
+ , do vs <- vars 2 ; "castJSTupleToTuple2" -:: jsTuple vs ==> tupleOf vs
+ , do vs <- vars 3 ; "castJSTupleToTuple3" -:: jsTuple vs ==> tupleOf vs
+ , do vs <- vars 4 ; "castJSTupleToTuple4" -:: jsTuple vs ==> tupleOf vs
+ , do vs <- vars 5 ; "castJSTupleToTuple5" -:: jsTuple vs ==> tupleOf vs
+ ]
+
+
-------- Signals --------
sig ts = fn ts ==> fn (map signalOf ts)
@@ -62,22 +91,29 @@ signals = sequence
, do ts <- vars 5 ; "lift4" -:: sig ts
, do [a,b] <- vars 2
"foldp" -:: (a ==> b ==> b) ==> b ==> signalOf a ==> signalOf b
+ , do a <- var ; "randomize" -:: IntT ==> IntT ==> signalOf a ==> signalOf IntT
]
-concreteSignals =
- [ "Mouse.position" -: signalOf point
- , "Mouse.x" -: signalOf IntT
- , "Mouse.y" -: signalOf IntT
- , "Mouse.isDown" -: signalOf BoolT
- , "Mouse.isClicked" -: signalOf BoolT
- , "Window.dimensions" -: signalOf point
- , "Window.width" -: signalOf IntT
- , "Window.height" -: signalOf IntT
- , "Input.textField" -: string ==> tupleOf [element, signalOf string]
- , "Input.password" -: string ==> tupleOf [element, signalOf string]
- , "Input.textArea" -: IntT ==> IntT ==> tupleOf [element, signalOf string]
- , "Input.stringDropDown" -: listOf string ==> tupleOf [element, signalOf string]
- ]
+concreteSignals =
+ [ "keysDown" -: signalOf (listOf IntT)
+ , "charPressed" -: signalOf (maybeOf IntT)
+ , "inRange" -: IntT ==> IntT ==> signalOf IntT
+ , "every" -: time ==> signalOf time
+ , "before" -: time ==> signalOf BoolT
+ , "after" -: time ==> signalOf BoolT
+ , "dimensions" -: signalOf point
+ , "position" -: signalOf point
+ , "x" -: signalOf IntT
+ , "y" -: signalOf IntT
+ , "isDown" -: signalOf BoolT
+ , "isClicked" -: signalOf BoolT
+ , "textField" -: string ==> tupleOf [element, signalOf string]
+ , "password" -: string ==> tupleOf [element, signalOf string]
+ , "textArea" -: IntT ==> IntT ==> tupleOf [element, signalOf string]
+ , "checkBox" -: BoolT ==> tupleOf [element, signalOf BoolT]
+ , "button" -: string ==> tupleOf [element, signalOf BoolT]
+ , "stringDropDown" -: listOf string ==> tupleOf [element, signalOf string]
+ ]
-------- Math and Binops --------
@@ -111,7 +147,7 @@ funcs = sequence
, do [a,b,c] <- vars 3 ; "." -:: (b ==> c) ==> (a ==> b) ==> (a ==> c)
, do [a,b] <- vars 2 ; "$" -:: (a ==> b) ==> a ==> b
, do a <- var ; ":" -:: a ==> listOf a ==> listOf a
- , do a <- var ; "++" -:: listOf a ==> listOf a ==> listOf a
+ , do a <- var ; "++" -:: a ==> a ==> a
, do a <- var ; "Cons" -:: a ==> listOf a ==> listOf a
, do a <- var ; "Nil" -:: listOf a
, do a <- var ; "Just" -:: a ==> ADT "Maybe" [a]
@@ -121,7 +157,7 @@ funcs = sequence
ints = map (-: (listOf IntT ==> IntT)) [ "sum","product","maximum","minimum" ]
-lists = liftM (map (first ("List."++)) . (++ints)) . sequence $
+lists = liftM (++ints) . sequence $
[ "and" -:: listOf BoolT ==> BoolT
, "or" -:: listOf BoolT ==> BoolT
, "sort" -:: listOf IntT ==> listOf IntT
@@ -136,6 +172,9 @@ lists = liftM (map (first ("List."++)) . (++ints)) . sequence $
, do a <- var ; "exists" -:: (a ==> BoolT) ==> listOf a ==> BoolT
, do a <- var ; "concat" -:: listOf (listOf a) ==> listOf a
, do a <- var ; "reverse" -:: listOf a ==> listOf a
+ , do a <- var ; "take" -:: IntT ==> listOf a ==> listOf a
+ , do a <- var ; "drop" -:: IntT ==> listOf a ==> listOf a
+ , do a <- var ; "partition" -:: (a==>BoolT)==>listOf a==>tupleOf [listOf a,listOf a]
, do a <- var ; "intersperse" -:: a ==> listOf a ==> listOf a
, do a <- var ; "intercalate" -:: listOf a ==> listOf(listOf a) ==> listOf a
, do [a,b] <- vars 2 ; "zip" -:: listOf a ==>listOf b ==>listOf(tupleOf [a,b])
@@ -152,8 +191,8 @@ lists = liftM (map (first ("List."++)) . (++ints)) . sequence $
-------- Everything --------
hints = do
- fs <- funcs ; ls <- lists ; ss <- signals
+ fs <- funcs ; ls <- lists ; ss <- signals ; pcasts <- polyCasts
return $ concat [ fs, ls, ss, math, bool, str2elem, textAttrs
, elements, directions, colors, lineTypes, shapes
- , concreteSignals
+ , concreteSignals, casts, pcasts
]
diff --git a/elm/src/Types/Types.hs b/elm/src/Types/Types.hs
index 59b3660b9..6b24e2723 100644
--- a/elm/src/Types/Types.hs
+++ b/elm/src/Types/Types.hs
@@ -30,7 +30,16 @@ point = tupleOf [IntT,IntT]
listOf t = ADT "List" [t]
signalOf t = ADT "Signal" [t]
tupleOf ts = ADT ("Tuple" ++ show (length ts)) ts
+maybeOf t = ADT "Maybe" [t]
string = listOf CharT
+time = IntT
+
+jsBool = ADT "JSBool" []
+jsNumber = ADT "JSNumber" []
+jsString = ADT "JSString" []
+jsElement = ADT "JSElement" []
+jsArray t = ADT "JSArray" [t]
+jsTuple ts = ADT ("JSTuple" ++ show (length ts)) ts
infixr ==>
t1 ==> t2 = LambdaT t1 t2