├── .gitignore ├── Benchmarks ├── LICENCE ├── graphs │ ├── chrome.png │ ├── firefox.png │ ├── opera.png │ └── safari.png ├── implementations │ ├── elm-0.17-opt-prepack │ │ ├── elm-prepacked.js │ │ ├── elm-prepacked.min.js │ │ ├── index.html │ │ └── style.css │ ├── elm-0.17-optimized │ │ ├── Todo.elm │ │ ├── elm-package.json │ │ ├── elm.js │ │ ├── elm.min.js │ │ ├── index.html │ │ └── style.css │ ├── elm-0.17-prepack │ │ ├── elm-prepacked.js │ │ ├── index.html │ │ └── style.css │ └── elm-0.17 │ │ ├── Todo.elm │ │ ├── elm-package.json │ │ ├── elm.js │ │ ├── index.html │ │ └── style.css ├── index.html ├── readme.md └── src │ ├── Picker.elm │ ├── add-complete-delete-batched.js │ ├── add-complete-delete.js │ ├── elm-package.json │ ├── picker.js │ ├── runner.js │ └── theme.css ├── HelloWorld ├── elm-package.json ├── index.html ├── main.elm ├── main.js ├── main.uglify.js ├── main.uglify.js.gz ├── prepacked.js ├── prepacked.uglify.js └── prepacked.uglify.js.gz ├── README.md ├── RandomGifHttp ├── elm-package.json ├── index.html ├── main.elm ├── main.js ├── main.uglify.js ├── main.uglify.js.gz ├── prepacked.js ├── prepacked.uglify.js ├── prepacked.uglify.js.gz └── waiting.gif └── RealWorldSPA ├── README.md ├── assets └── images │ └── error.jpg ├── elm.js └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | # elm-package generated files 2 | elm-stuff/ 3 | # elm-repl generated files 4 | repl-temp-* 5 | -------------------------------------------------------------------------------- /Benchmarks/LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Evan Czaplicki 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the {organization} nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Benchmarks/graphs/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkaterDad/elm-prepack-experiments/af09ed73d61f4932b8cfbc562a3285a617a482fc/Benchmarks/graphs/chrome.png -------------------------------------------------------------------------------- /Benchmarks/graphs/firefox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkaterDad/elm-prepack-experiments/af09ed73d61f4932b8cfbc562a3285a617a482fc/Benchmarks/graphs/firefox.png -------------------------------------------------------------------------------- /Benchmarks/graphs/opera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkaterDad/elm-prepack-experiments/af09ed73d61f4932b8cfbc562a3285a617a482fc/Benchmarks/graphs/opera.png -------------------------------------------------------------------------------- /Benchmarks/graphs/safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkaterDad/elm-prepack-experiments/af09ed73d61f4932b8cfbc562a3285a617a482fc/Benchmarks/graphs/safari.png -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17-opt-prepack/elm-prepacked.min.js: -------------------------------------------------------------------------------- 1 | (function(){"use strict";function n(){return e("[]")}function e(n){return{ctor:n}}function r(n,e){return function(r){return n(e,r)}}function t(n,e){return function(r){return function(t){return n(e,r,t)}}}function a(n,e){return function(r){return function(t){return function(a){return n(e,r,t,a)}}}}function u(n,e){return function(r){return function(t){return function(a){return function(u){return n(e,r,t,a,u)}}}}}function i(n,e){return function(r){return function(t){return function(a){return function(u){return function(i){return n(e,r,t,a,u,i)}}}}}}function o(n,e){return function(r){return function(t){return function(a){return function(u){return function(i){return function(o){return n(e,r,t,a,u,i,o)}}}}}}}function c(n,e){return function(r){return function(t){return function(a){return function(u){return function(i){return function(o){return function(c){return n(e,r,t,a,u,i,o,c)}}}}}}}}function l(n,e){return function(r){return function(t){return function(a){return function(u){return function(i){return function(o){return function(c){return function(l){return n(e,r,t,a,u,i,o,c,l)}}}}}}}}}function f(n,e,r){return pr(n,e,r)}function d(n){return s(ca.undefined,n,!1)}function s(n,e,r){try{var t=h("Todo",ci);return r||(t.renderer=jt),Mt("Todo",t,n,e)}catch(e){throw n.innerHTML=ua(e.message),e}}function h(n,e){var r=e.main;if(void 0===r.init){var t=v(ka.Nil),a=_a.Tuple2(_a.Tuple0,t);return cn({init:function(){return a},view:function(){return r},update:rn(function(){return a}),subscriptions:function(){return t}})}var u=e.flags;return cn({init:u?sr(n,r.init,u):hr(n,r.init),view:r.view,update:r.update,subscriptions:r.subscriptions})}function v(n){return{type:"node",branches:n}}function g(n,e){return{ctor:"::",_0:n,_1:e}}function p(n){return r.call(this,U,n)}function b(n){return r.call(this,en,n)}function y(n){return r.call(this,F,n)}function m(n){return r.call(this,pn,n)}function _(n){return r.call(this,mr,n)}function A(n){return r.call(this,Mr,n)}function k(n){return r.call(this,Fr,n)}function T(n){return r.call(this,Rr,n)}function w(n){return r.call(this,gt,n)}function E(n){return r.call(this,Et,n)}function x(n){for(var e=[];"[]"!==n.ctor;)e.push(n._0),n=n._1;return e}function C(n){for(var e=la,r=n.length;r--;)e=g(n[r],e);return e}function N(n,e){var r=la;if(n<=e)do{r=g(e,r)}while(e-- >n);return r}function S(n){return t.call(this,At,n)}function O(n,e,r){for(var t=x(r),a=e,u=t.length;u--;)a=j(n,t[u],a);return a}function j(n,e,r){return 2===n.arity?n.func(e,r):n(e)(r)}function M(n,e,r){for(var t=[];"[]"!==e.ctor&&"[]"!==r.ctor;)t.push(j(n,e._0,r._0)),e=e._1,r=r._1;return C(t)}function D(n,e,r,t){for(var a=[];"[]"!==e.ctor&&"[]"!==r.ctor&&"[]"!==t.ctor;)a.push(L(n,e._0,r._0,t._0)),e=e._1,r=r._1,t=t._1;return C(a)}function L(n,e,r,t){return 3===n.arity?n.func(e,r,t):n(e)(r)(t)}function B(n,e,r,t,a){for(var u=[];"[]"!==e.ctor&&"[]"!==r.ctor&&"[]"!==t.ctor&&"[]"!==a.ctor;)u.push(q(n,e._0,r._0,t._0,a._0)),e=e._1,r=r._1,t=t._1,a=a._1;return C(u)}function q(n,e,r,t,a){return 4===n.arity?n.func(e,r,t,a):n(e)(r)(t)(a)}function R(n,e,r,t,a,u){for(var i=[];"[]"!==e.ctor&&"[]"!==r.ctor&&"[]"!==t.ctor&&"[]"!==a.ctor&&"[]"!==u.ctor;)i.push(P(n,e._0,r._0,t._0,a._0,u._0)),e=e._1,r=r._1,t=t._1,a=a._1,u=u._1;return C(i)}function P(n,e,r,t,a,u){return 5===n.arity?n.func(e,r,t,a,u):n(e)(r)(t)(a)(u)}function F(n,e){return C(x(e).sort(function(e,r){return _a.cmp(n(e),n(r))}))}function J(n,e){for(var r=[{x:n,y:e}];r.length>0;){var t=r.pop(),a=t.x,u=t.y;if(a!==u){if("object"!=typeof a){if("function"==typeof a)throw new ca.Error("Equality error: general function equality is undecidable, and therefore, unsupported");return!1}var i=0;for(var o in a){if(++i,!(o in u))return!1;"ctor"!==o&&r.push({x:a[o],y:u[o]})}if("ctor"in a&&r.push({x:a.ctor,y:u.ctor}),i!==ca.Object.keys(u).length)return!1}}return!0}function V(n,e){var r;if("object"!=typeof n)return n===e?0:n=1){if(0!==(r=V(n._0,e._0)))return r;if(u>=2){if(0!==(r=V(n._1,e._1)))return r;if(u>=3){if(0!==(r=V(n._2,e._2)))return r;if(u>=4){if(0!==(r=V(n._3,e._3)))return r;if(u>=5){if(0!==(r=V(n._4,e._4)))return r;if(u>=6){if(0!==(r=V(n._5,e._5)))return r;if(u>=7)throw new ca.Error("Comparison error: cannot compare tuples with more than 6 elements.")}}}}}}return 0}throw new ca.Error("Comparison error: comparison is only defined on ints, floats, times, chars, strings, lists of comparable values, and tuples of comparable values.")}for(;;){if("[]"===n.ctor&&"[]"===e.ctor)return 0;if(n.ctor!==e.ctor)return"[]"===n.ctor?-1:1;if(0!==(r=V(n._0,e._0)))return r;n=n._1,e=e._1}}function I(n,e){return{ctor:"_Tuple2",_0:n,_1:e}}function Y(n){return new ca.String(n)}function W(n,e){var r={};for(var t in n){var a=t in e?e[t]:n[t];r[t]=a}return r}function z(n){return pa++}function U(n,e){if("string"==typeof n)return n+e;if("[]"===n.ctor)return e;var r=K(n._0,ba),t=r;for(n=n._1;"[]"!==n.ctor;)t._1=K(n._0,ba),n=n._1,t=t._1;return t._1=e,r}function K(n,e){return{ctor:"::",_0:n,_1:e}}function G(n,e){return function(r){throw new ca.Error("Ran into a `Debug.crash` in module `"+n+"` "+H(e)+"\nThe message provided by the code author is:\n\n "+r)}}function H(n){return n.start.line==n.end.line?"on line "+n.start.line:"between lines "+n.start.line+" and "+n.end.line}function Q(n,e,r){return function(t){throw new ca.Error("Ran into a `Debug.crash` in module `"+n+"`\n\nThis was caused by the `case` expression "+H(e)+".\nOne of the branches ended with a crash and the following value got through:\n\n "+X(r)+"\n\nThe message provided by the code author is:\n\n "+t)}}function X(n){var e=typeof n;if("function"===e)return"";if("boolean"===e)return n?"True":"False";if("number"===e)return n+"";if(n instanceof ca.String)return"'"+Z(n,!0)+"'";if("string"===e)return'"'+Z(n,!1)+'"';if(null===n)return"null";if("object"===e&&"ctor"in n){var r=n.ctor.substring(0,5);if("_Tupl"===r){l=[];for(var t in n)"ctor"!==t&&l.push(X(n[t]));return"("+l.join(",")+")"}if("_Task"===r)return"";if("_Array"===n.ctor)return"Array.fromList "+X(u=$(n));if(""===n.ctor)return"";if("_Process"===n.ctor)return"";if("::"===n.ctor){l="["+X(n._0);for(n=n._1;"::"===n.ctor;)l+=","+X(n._0),n=n._1;return l+"]"}if("[]"===n.ctor)return"[]";if("RBNode_elm_builtin"===n.ctor||"RBEmpty_elm_builtin"===n.ctor||"Set_elm_builtin"===n.ctor){var a,u;return"Set_elm_builtin"===n.ctor?(a="Set",u=j(b,function(n){return n._0},tn(n._0))):(a="Dict",u=tn(n)),a+".fromList "+X(u)}l="";for(var i in n)if("ctor"!==i){var o=X(n[i]),c=o[0];l+=" "+("{"===c||"("===c||"<"===c||'"'===c||o.indexOf(" ")<0?o:"("+o+")")}return n.ctor+l}if("object"===e){var l=[];for(var t in n)l.push(t+" = "+X(n[t]));return 0===l.length?"{}":"{ "+l.join(", ")+" }"}return""}function Z(n,e){var r=n.replace(/\\/g,"\\\\").replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(/\r/g,"\\r").replace(/\v/g,"\\v").replace(/\0/g,"\\0");return e?r.replace(/\'/g,"\\'"):r.replace(/\"/g,'\\"')}function $(n){return nn(ka.Nil,n)}function nn(n,e){for(var r=e.table.length-1;r>=0;r--)n=0===e.height?ka.Cons(e.table[r],n):nn(n,e.table[r]);return n}function en(n,e){return L(da,rn(function(e,r){return j(ya["::"],n(e),r)}),ka.fromArray([]),e)}function rn(n){function e(e){return function(r){return n(e,r)}}return e.arity=2,e.func=n,e}function tn(n){return L(ma,un(function(n,e,r){return j(ya["::"],{ctor:"_Tuple2",_0:n,_1:e},r)}),ka.fromArray([]),n)}function an(n,e,r){for(;;){var t=r;if("RBEmpty_elm_builtin"===t.ctor)return e;var a=n,u=L(n,t._1,t._2,L(ma,n,e,t._4));n=a,e=u,r=t._3}}function un(n){function e(e){return function(r){return function(t){return n(e,r,t)}}}return e.arity=3,e.func=n,e}function on(n,e){return C(x(e).sort(function(e,r){var t=n(e)(r).ctor;return"EQ"===t?0:"LT"===t?-1:1}))}function cn(n){return{init:n.init,update:n.update,subscriptions:n.subscriptions,view:n.view,renderer:ln}}function ln(n,e,r){function t(n){var e=Qe(i,n);u=ir(u,i,e,a),i=n}var a={tagger:e,parent:ca.undefined},u=fn(r,a);n.appendChild(u);var i=r;return{update:t}}function fn(n,e){switch(n.type){case"thunk":return n.node||(n.node=n.thunk()),fn(n.node,e);case"tagger":for(var r=n.node,t=n.tagger;"tagger"===r.type;)"object"!=typeof t?t=[t,r.tagger]:t.push(r.tagger),r=r.node;var a={tagger:t,parent:e};return(o=fn(r,a)).elm_event_node_ref=a,o;case"text":return ca.document.createTextNode(n.text);case"node":dn(o=n.namespace?ca.document.createElementNS(n.namespace,n.tag):ca.document.createElement(n.tag),e,n.facts);for(var u=n.children,i=0;i0){var a={ctor:"_Array",height:0,table:e.splice(0,t)};wn(a,r)}for(var u=0;u0&&wn(r[u],r);var i=r[r.length-1];return i.height>0&&1===i.table.length?i.table[0]:i}function wn(n,e){var r=n.height;if(e.length===r){var t={ctor:"_Array",height:r+1,table:[],lengths:[]};e.push(t)}e[r].table.push(n);var a=En(n);e[r].lengths.length>0&&(a+=e[r].lengths[e[r].lengths.length-1]),e[r].lengths.push(a),32===e[r].table.length&&(wn(e[r],e),e[r]={ctor:"_Array",height:r+1,table:[],lengths:[]})}function En(n){return 0===n.height?n.table.length:n.lengths[n.lengths.length-1]}function xn(n,e){return n<=0?wa:Cn(e,ca.Math.floor(ca.Math.log(n)/ca.Math.log(32)),0,n)}function Cn(n,e,r,t){if(0===e){for(var a=new ca.Array((t-r)%33),u=0;u0?o[u-1]:0);return{ctor:"_Array",height:e,table:a,lengths:o}}function Nn(n,e){if(0===n.table.length)return e;if(0===e.table.length)return n;var r=Sn(n,e);if(r[0].table.length+r[1].table.length<=32){if(0===r[0].table.length)return r[1];if(0===r[1].table.length)return r[0];if(r[0].table=r[0].table.concat(r[1].table),r[0].height>0){for(var t=En(r[0]),a=0;a0){var u=qn(n,e);u>2&&(r=Rn(r[0],r[1],u))}return In(r[0],r[1])}function Sn(n,e){if(0===n.height&&0===e.height)return[n,e];if(1!==n.height||1!==e.height)if(n.height===e.height)n=On(n),e=On(e),Dn(n,(r=Sn(jn(n),Mn(e)))[1]),Ln(e,r[0]);else if(n.height>e.height)Dn(n=On(n),(r=Sn(jn(n),e))[0]),e=Bn(r[1],r[1].height+1);else{var r=Sn(n,Mn(e=On(e))),t=0===r[0].table.length?0:1,a=0===t?1:0;Ln(e,r[t]),n=Bn(r[a],r[a].height+1)}if(0===n.table.length||0===e.table.length)return[n,e];var u=qn(n,e);return u<=2?[n,e]:Rn(n,e,u)}function On(n){var e={ctor:"_Array",height:n.height,table:n.table.slice()};return n.height>0&&(e.lengths=n.lengths.slice()),e}function jn(n){return n.table[n.table.length-1]}function Mn(n){return n.table[0]}function Dn(n,e){var r=n.table.length-1;n.table[r]=e,n.lengths[r]=En(e),n.lengths[r]+=r>0?n.lengths[r-1]:0}function Ln(n,e){if(e.table.length>0){n.table[0]=e,n.lengths[0]=En(e);for(var r=En(n.table[0]),t=1;t0?1:0)0)for(var d=o.lengths.length,s=d;s0?o.lengths[s-1]:0;c+=f,l.table.length<=f&&(u++,c=0),32===o.table.length&&(Vn(t,a,i,o),o=Pn(n.height-1,0),i++)}for(o.table.length>0&&(Vn(t,a,i,o),i++);u0&&(r.lengths=new ca.Array(e)),r}function Fn(n,e,r){return r0?e.lengths[r-1]:0),e.table[r]);if(r===e.table.length-1)return t;var a={ctor:"_Array",height:e.height,table:e.table.slice(r,e.table.length+1),lengths:new ca.Array(e.table.length-r)};a.table[0]=t;for(var u=0,i=0;i>5*e.height;e.lengths[r]<=n;)r++;return r}function Hn(n,e){if(n===En(e))return e;if(0===e.height)return(a={ctor:"_Array",height:0}).table=e.table.slice(0,n),a;var r=Gn(n,e),t=Hn(n-(r>0?e.lengths[r-1]:0),e.table[r]);if(0===r)return t;var a={ctor:"_Array",height:e.height,table:e.table.slice(0,r),lengths:e.lengths.slice(0,r)};return t.table.length>0&&(a.table[r]=t,a.lengths[r]=En(t)+(r>0?a.lengths[r-1]:0)),a}function Qn(n,e){if(n<0||n>=En(e))throw new ca.Error("Index "+n+" is out of range. Check the length of your array first or use getMaybe or getWithDefault.");return Xn(n,e)}function Xn(n,e){for(var r=e.height;r>0;r--){for(var t=n>>5*r;e.lengths[t]<=n;)t++;t>0&&(n-=e.lengths[t-1]),e=e.table[t]}return e.table[n]}function Zn(n,e,r){return n<0||En(r)<=n?r:$n(n,e,r)}function $n(n,e,r){if(0===(r=On(r)).height)r.table[n]=e;else{var t=Gn(n,r);t>0&&(n-=r.lengths[t-1]),r.table[t]=$n(n,e,r.table[t])}return r}function ne(n,e){var r={ctor:"_Array",height:e.height,table:new ca.Array(e.table.length)};e.height>0&&(r.lengths=e.lengths);for(var t=0;t0&&(t.lengths=e.lengths);for(var a=0;a0?i[o-1]:0);return{ctor:"_Array",height:e,table:u,lengths:i}}function le(n){return{ctor:"Just",_0:n}}function fe(n,e){return{tag:"field",field:n,rest:e}}function de(n){return{tag:"oneOf",problems:n}}function se(n){return{tag:"fail",msg:n}}function he(n){return{ctor:"Ok",_0:n}}function ve(n){for(var e="_";n;)switch(n.tag){case"primitive":return"Expecting "+n.type+("_"===e?"":" at "+e)+" but instead got: "+ge(n.value);case"index":e+="["+n.index+"]",n=n.rest;break;case"field":e+="."+n.field,n=n.rest;break;case"oneOf":for(var r=n.problems,t=0;t",tag:"null",value:n}}function be(n){return{ctor:"",tag:n}}function ye(n,e){return{ctor:"",tag:n,decoder:e}}function me(n,e){return{ctor:"",tag:"field",field:n,decoder:e}}function _e(n,e){return Ae(n,[e])}function Ae(n,e){return{ctor:"",tag:"map-many",func:n,decoders:e}}function ke(n,e,r){return Ae(n,[e,r])}function Te(n,e,r,t){return Ae(n,[e,r,t])}function we(n,e,r,t,a){return Ae(n,[e,r,t,a])}function Ee(n,e,r,t,a,u){return Ae(n,[e,r,t,a,u])}function xe(n,e,r,t,a,u,i){return Ae(n,[e,r,t,a,u,i])}function Ce(n,e,r,t,a,u,i,o){return Ae(n,[e,r,t,a,u,i,o])}function Ne(n,e,r,t,a,u,i,o,c){return Ae(n,[e,r,t,a,u,i,o,c])}function Se(n){return{ctor:"",tag:"key-value",decoder:n}}function Oe(n,e){return je(n,[e])}function je(n,e){return{ctor:"",tag:"tuple",func:n,decoders:e}}function Me(n,e,r){return je(n,[e,r])}function De(n,e,r,t){return je(n,[e,r,t])}function Le(n,e,r,t,a){return je(n,[e,r,t,a])}function Be(n,e,r,t,a,u){return je(n,[e,r,t,a,u])}function qe(n,e,r,t,a,u,i){return je(n,[e,r,t,a,u,i])}function Re(n,e,r,t,a,u,i,o){return je(n,[e,r,t,a,u,i,o])}function Pe(n,e,r,t,a,u,i,o,c){return je(n,[e,r,t,a,u,i,o,c])}function Fe(n,e){return{ctor:"",tag:"andThen",decoder:n,callback:e}}function Je(n,e){return{ctor:"",tag:"customAndThen",decoder:n,callback:e}}function Ve(n){return{ctor:"",tag:"fail",msg:n}}function Ie(n){return{ctor:"",tag:"succeed",msg:n}}function Ye(n){return{ctor:"",tag:"oneOf",decoders:n}}function We(n){return n}function ze(n){for(var e={};"[]"!==n.ctor;){var r=n._0;e[r._0]=r._1,n=n._1}return e}function Ue(n,e){if(n===e)return!0;if(n.tag!==e.tag)return!1;switch(n.tag){case"succeed":case"fail":return n.msg===e.msg;case"bool":case"int":case"float":case"string":case"value":return!0;case"null":return n.value===e.value;case"list":case"array":case"maybe":case"key-value":return Ue(n.decoder,e.decoder);case"field":return n.field===e.field&&Ue(n.decoder,e.decoder);case"map-many":case"tuple":return n.func===e.func&&Ke(n.decoders,e.decoders);case"andThen":case"customAndThen":return n.callback===e.callback&&Ue(n.decoder,e.decoder);case"oneOf":return Ke(n.decoders,e.decoders)}}function Ke(n,e){var r=n.length;if(r!==e.length)return!1;for(var t=0;t0&&r.push(Ze("p-thunk",t,f)));case"tagger":for(var d=n.tagger,s=e.tagger,h=!1,v=n.node;"tagger"===v.type;)h=!0,"object"!=typeof d?d=[d,v.tagger]:d.push(v.tagger),v=v.node;for(var g=e.node;"tagger"===g.type;)h=!0,"object"!=typeof s?s=[s,g.tagger]:s.push(g.tagger),g=g.node;return h&&d.length!==s.length?void r.push(Ze("p-redraw",t,e)):((h?$e(d,s):d===s)||r.push(Ze("p-tagger",t,s)),void Xe(v,g,r,t+1));case"text":if(n.text!==e.text)return void r.push(Ze("p-text",t,e.text));return;case"node":return n.tag!==e.tag||n.namespace!==e.namespace?void r.push(Ze("p-redraw",t,e)):(void 0!==(p=nr(n.facts,e.facts))&&r.push(Ze("p-facts",t,p)),void rr(n,e,r,t));case"keyed-node":return n.tag!==e.tag||n.namespace!==e.namespace?void r.push(Ze("p-redraw",t,e)):(void 0!==(p=nr(n.facts,e.facts))&&r.push(Ze("p-facts",t,p)),void tr(n,e,r,t));case"custom":if(n.impl!==e.impl)return void r.push(Ze("p-redraw",t,e));var p=nr(n.facts,e.facts);void 0!==p&&r.push(Ze("p-facts",t,p));var b=e.impl.diff(n,e);if(b)return void r.push(Ze("p-custom",t,b));return}}}function Ze(n,e,r){return{index:e,type:n,data:r,domNode:ca.undefined,eventNode:ca.undefined}}function $e(n,e){for(var r=0;ro?r.push(Ze("p-remove-last",t,i-o)):i0||i.length>0||void 0!==O)&&r.push(Ze("p-reorder",t,{patches:a,inserts:i,endInserts:O}))}function ar(n,e,r,t,a,u){var i=n[r];if(void 0===i)return i={tag:"insert",vnode:t,index:a,data:ca.undefined},u.push({index:a,entry:i}),void(n[r]=i);if("remove"===i.tag){u.push({index:a,entry:i}),i.tag="move";var o=[];return Xe(i.vnode,t,o,i.index),i.index=a,void(i.data.data={patches:o,entry:i})}ar(n,e,r+"_elmW6BL",t,a,u)}function ur(n,e,r,t,a){var u=n[r];if(void 0===u){o=Ze("p-remove",a,ca.undefined);return e.push(o),void(n[r]={tag:"remove",vnode:t,index:a,data:o})}if("insert"===u.tag){u.tag="move";var i=[];Xe(t,u.vnode,i,a);var o=Ze("p-remove",a,{patches:i,entry:u});return void e.push(o)}ur(n,e,r+"_elmW6BL",t,a)}function ir(n,e,r,t){return 0===r.length?n:(or(n,e,r,t),lr(n,r))}function or(n,e,r,t){cr(n,e,r,0,0,e.descendantsCount,t)}function cr(n,e,r,t,a,u,i){for(var o=r[t],c=o.index;c===a;){var l=o.type;if("p-thunk"===l)or(n,e.node,o.data,i);else if("p-reorder"===l)o.domNode=n,o.eventNode=i,(d=o.data.patches).length>0&&cr(n,e,d,0,a,u,i);else if("p-remove"===l){o.domNode=n,o.eventNode=i;var f=o.data;if(void 0!==f){f.entry.data=n;var d=f.patches;d.length>0&&cr(n,e,d,0,a,u,i)}}else o.domNode=n,o.eventNode=i;if(t++,!(o=r[t])||(c=o.index)>u)return t}switch(e.type){case"tagger":for(var s=e.node;"tagger"===s.type;)s=s.node;return cr(n,s,r,t,a+1,u,n.elm_event_node_ref);case"node":for(var h=e.children,v=n.childNodes,g=0;gu))return t;a=b}return t;case"keyed-node":for(var h=e.children,v=n.childNodes,g=0;gu))return t;a=b}return t;case"text":case"thunk":throw new ca.Error("should never traverse `text` or `thunk` nodes like this")}}function lr(n,e){for(var r=0;r0?n>=0?r:r+e:-dt(-n,-e);return t===e?0:t}function st(n,e){return n/e|0}function ht(n,e){return{ctor:"UpdateEntry",_0:n,_1:e}}function vt(n){return j(A,"blur",Ie(n))}function gt(n,e){var r=pt(j(ju,function(n){return n.completed},e)),t=pt(e)-r;return j(Uu,ka.fromArray([yr("footer"),yt(Pr(e))]),ka.fromArray([j(fu,mt,t),j(fu,_t,n),j(fu,wt,r)]))}function pt(n){return L(Wu,rn(function(n,e){return e+1}),0,n)}function bt(n,e,r){for(;;){var t=r;if("[]"===t.ctor)return e;var a=n,u=j(n,t._0,e);n=a,e=u,r=t._1}}function yt(n){return j(bu,"hidden",n)}function mt(n){var e=_a.eq(n,1)?" item":" items";return j(Gu,ka.fromArray([yr("todo-count")]),ka.fromArray([j(Qu,ka.fromArray([]),ka.fromArray([Er(X(n))])),Er(j(Iu["++"],e," left"))]))}function _t(n){return j(Zu,ka.fromArray([yr("filters")]),ka.fromArray([L(S,"#/","All",n),Er(" "),L(S,"#/active","Active",n),Er(" "),L(S,"#/completed","Completed",n)]))}function At(n,e,r){return j(Su,ka.fromArray([Wr(kt(e))]),ka.fromArray([j(ni,ka.fromArray([Tt(n),Xr(ka.fromArray([{ctor:"_Tuple2",_0:"selected",_1:_a.eq(e,r)}]))]),ka.fromArray([Er(e)]))]))}function kt(n){return{ctor:"ChangeVisibility",_0:n}}function Tt(n){return j(_,"href",n)}function wt(n){return j(Bu,ka.fromArray([yr("clear-completed"),yt(_a.eq(n,0)),Wr(ei)]),ka.fromArray([Er(j(Iu["++"],"Clear completed (",j(Iu["++"],X(n),")")))]))}function Et(n,e){var r=n;switch(r.ctor){case"NoOp":return j(ai["!"],e,ka.fromArray([]));case"Add":return j(ai["!"],_a.update(e,{uid:e.uid+1,field:"",entries:Ct(e.field)?e.entries:j(Iu["++"],e.entries,ka.fromArray([j(ui,e.field,e.uid)]))}),ka.fromArray([]));case"UpdateField":return j(ai["!"],_a.update(e,{field:r._0}),ka.fromArray([]));case"EditingEntry":var t=r._0,a=function(n){return _a.eq(n.id,t)?_a.update(n,{editing:r._1}):n};return j(ai["!"],_a.update(e,{entries:j(b,a,e.entries)}),ka.fromArray([St(j(Iu["++"],"#todo-",X(t)))]));case"UpdateEntry":a=function(n){return _a.eq(n.id,r._0)?_a.update(n,{description:r._1}):n};return j(ai["!"],_a.update(e,{entries:j(b,a,e.entries)}),ka.fromArray([]));case"Delete":return j(ai["!"],_a.update(e,{entries:j(ju,function(n){return!_a.eq(n.id,r._0)},e.entries)}),ka.fromArray([]));case"DeleteComplete":return j(ai["!"],_a.update(e,{entries:j(ju,function(n){return Jr(function(n){return n.completed}(n))},e.entries)}),ka.fromArray([]));case"Check":a=function(n){return _a.eq(n.id,r._0)?_a.update(n,{completed:r._1}):n};return j(ai["!"],_a.update(e,{entries:j(b,a,e.entries)}),ka.fromArray([]));case"CheckAll":a=function(n){return _a.update(n,{completed:r._0})};return j(ai["!"],_a.update(e,{entries:j(b,a,e.entries)}),ka.fromArray([]));default:return j(ai["!"],_a.update(e,{visibility:r._0}),ka.fromArray([]))}}function xt(n,e){return{ctor:"_Tuple2",_0:n,_1:v(e)}}function Ct(n){return 0===n.length}function Nt(n,e){return{description:n,completed:!1,editing:!1,id:e}}function St(n){return{type:"leaf",home:"focus",value:n}}function Ot(n){return ii}function jt(){return{update:function(){}}}function Mt(n,e,r,t){function a(n,e){return vi.nativeBinding(function(r){var t=j(c,n,e);e=t._0,i.update(f(e));var a=t._1,u=l(e);Gt(s,a,u),r(vi.succeed(e))})}function u(n){vi.rawSend(h,n)}var i,o=e.init,c=e.update,l=e.subscriptions,f=e.view,d=e.renderer,s={},h=na(vi.nativeBinding(function(n){var e=o(t),a=e._0;i=d(r,u,f(a));var c=e._1,h=l(a);Gt(s,c,h),n(vi.succeed(a))}),a),v=ea(s,u);return v?{ports:v}:{}}function Dt(n){return{ctor:"_Task_succeed",value:n}}function Lt(n){return{ctor:"_Task_fail",value:n}}function Bt(n){return{ctor:"_Task_nativeBinding",callback:n,cancel:null}}function qt(n,e){return{ctor:"_Task_andThen",task:n,callback:e}}function Rt(n,e){return{ctor:"_Task_onError",task:n,callback:e}}function Pt(n){return{ctor:"_Task_receive",callback:n}}function Ft(n){return Bt(function(e){e(Dt(Jt(n)))})}function Jt(n){var e={ctor:"_Process",id:_a.guid(),root:n,stack:null,mailbox:[]};return Vt(e),e}function Vt(n){di.push(n),si||(ca.setTimeout(It,0),si=!0)}function It(){for(var n,e=0;e<1e4&&(n=di.shift());)e=Yt(e,n);if(!n)return void(si=!1);ca.setTimeout(It,0)}function Yt(n,e){for(;n<1e4;){var r=e.root.ctor;if("_Task_succeed"!==r)if("_Task_fail"!==r)if("_Task_andThen"!==r)if("_Task_onError"!==r){if("_Task_nativeBinding"===r){e.root.cancel=e.root.callback(function(n){e.root=n,Vt(e)});break}if("_Task_receive"!==r)throw new ca.Error(r);var t=e.mailbox;if(0===t.length)break;e.root=e.root.callback(t.shift()),++n}else e.stack={ctor:"_Task_onError",callback:e.root.callback,rest:e.stack},e.root=e.root.task,++n;else e.stack={ctor:"_Task_andThen",callback:e.root.callback,rest:e.stack},e.root=e.root.task,++n;else{for(;e.stack&&"_Task_andThen"===e.stack.ctor;)e.stack=e.stack.rest;if(null===e.stack)break;e.root=e.stack.callback(e.root.value),e.stack=e.stack.rest,++n}else{for(;e.stack&&"_Task_onError"===e.stack.ctor;)e.stack=e.stack.rest;if(null===e.stack)break;e.root=e.stack.callback(e.root.value),e.stack=e.stack.rest,++n}}return n<1e4?n+1:(Vt(e),n)}function Wt(n){return Bt(function(e){var r=n.root;"_Task_nativeBinding"===r.ctor&&r.cancel&&r.cancel(),n.root=null,e(Dt(_a.Tuple0))})}function zt(n){return Bt(function(e){var r=ca.setTimeout(function(){e(Dt(_a.Tuple0))},n);return function(){ca.clearTimeout(r)}})}function Ut(n,e){return Bt(function(r){Kt(n,e),r(Dt(_a.Tuple0))})}function Kt(n,e){n.mailbox.push(e),Vt(n)}function Gt(n,e,r){var t={};Ht(!0,e,t,null),Ht(!1,r,t,null);for(var a in n){var u=a in t?t[a]:{cmds:ka.Nil,subs:ka.Nil};vi.rawSend(n[a],{ctor:"fx",_0:u})}}function Ht(n,e,r,t){switch(e.type){case"leaf":var a=e.home,u=Qt(n,a,t,e.value);return void(r[a]=$t(n,u,r[a]));case"node":for(var i=e.branches;"[]"!==i.ctor;)Ht(n,i._0,r,t),i=i._1;return;case"map":return void Ht(n,e.tree,r,{tagger:e.tagger,rest:t})}}function Qt(n,e,r,t){function a(n){for(var e=r;e;)n=e.tagger(n),e=e.rest;return n}return j(n?pi[e].cmdMap:pi[e].subMap,a,t)}function Xt(n,e){return e}function Zt(n){return n}function $t(n,e,r){return r=r||{cmds:ka.Nil,subs:ka.Nil},n?(r.cmds=ka.Cons(e,r.cmds),r):(r.subs=ka.Cons(e,r.subs),r)}function na(n,e){function r(n){var a=vi.receive(function(r){return e(r,n)});return j(t,a,r)}var t=vi.andThen,a=j(t,n,r);return vi.rawSpawn(a)}function ea(n,e){var r;for(var t in pi){var a=pi[t];a.isForeign&&((r=r||{})[t]="cmd"===a.tag?ra(t):ta(t,e)),n[t]=aa(a,e)}return r}function ra(n){function e(n,e,r){for(;"[]"!==e.ctor;){for(var t=u(e._0),o=0;o=0&&a.splice(e,1)}var a=[],u=pi[n].converter,i=vi.succeed(null);return pi[n].init=i,pi[n].onEffects=un(e),{subscribe:r,unsubscribe:t}}function ta(n,e){function r(n,e,r){return a=e,i}function t(r){var t=j(Ra,u,r);if("Err"===t.ctor)throw new ca.Error("Trying to send an unexpected type of value through port `"+n+"`:\n"+t._0);for(var r=t._0,i=a;"[]"!==i.ctor;)e(i._0(r)),i=i._1}var a=ka.Nil,u=pi[n].converter,i=vi.succeed(null);return pi[n].init=i,pi[n].onEffects=un(r),{send:t}}function aa(n,e){function r(n,e){if("self"===n.ctor)return L(i,t,n._0,e);var r=n._0;switch(a){case"cmd":return L(u,t,r.cmds,e);case"sub":return L(u,t,r.subs,e);case"fx":return q(u,t,r.cmds,r.subs,e)}}var t={main:e,self:ca.undefined},a=n.tag,u=n.onEffects,i=n.onSelfMsg,o=na(n.init,r);return t.self=o,o}function ua(n){return'

Oops! Something went wrong when starting your Elm program.

'+n+"
"}function ia(n,e){return s(n,e,!0)}function oa(n){return s(ca.document.body,n,!0)}var ca=this,la=n(),fa=r.bind(null,g);fa.arity=2,fa.func=g;var da=t.bind(null,O);da.arity=3,da.func=O;var sa=t.bind(null,M);sa.arity=3,sa.func=M;var ha=a.bind(null,D);ha.arity=4,ha.func=D;var va=u.bind(null,B);va.arity=5,va.func=B;var ga=i.bind(null,R);ga.arity=6,ga.func=R;var pa=0,ba=n();p.arity=2,p.func=U;var ya={"::":fa};b.arity=2,b.func=en;var ma=t.bind(null,an);ma.arity=3,ma.func=an;var _a={eq:J,cmp:V,Tuple0:{ctor:"_Tuple0"},Tuple2:I,chr:Y,update:W,guid:z,append:p,crash:G,crashCase:Q,toString:X};y.arity=2,y.func=F;var Aa=r.bind(null,on);Aa.arity=2,Aa.func=on;var ka={Nil:la,Cons:g,cons:fa,toArray:x,fromArray:C,range:N,foldr:da,map2:sa,map3:ha,map4:va,map5:ga,sortBy:y,sortWith:Aa},Ta=r.bind(null,gn);Ta.arity=2,Ta.func=gn;var wa={ctor:"_Array",height:0,table:[]},Ea=r.bind(null,xn);Ea.arity=2,Ea.func=xn;var xa=r.bind(null,Nn);xa.arity=2,xa.func=Nn;var Ca=r.bind(null,Yn);Ca.arity=2,Ca.func=Yn;var Na=t.bind(null,Un);Na.arity=3,Na.func=Un;var Sa=r.bind(null,Qn);Sa.arity=2,Sa.func=Qn;var Oa=t.bind(null,Zn);Oa.arity=3,Oa.func=Zn;var ja=r.bind(null,ne);ja.arity=2,ja.func=ne;var Ma=r.bind(null,ee);Ma.arity=2,Ma.func=ee;var Da=t.bind(null,te);Da.arity=3,Da.func=te;var La=t.bind(null,ae);La.arity=3,La.func=ae;var Ba={empty:wa,fromList:Tn,toList:$,initialize:Ea,append:xa,push:Ca,slice:Na,get:Sa,set:Oa,map:ja,indexedMap:Ma,foldl:Da,foldr:La,length:En,toJSArray:ue,fromJSArray:oe},qa=e("Nothing");m.arity=2,m.func=pn;var Ra=r.bind(null,yn);Ra.arity=2,Ra.func=yn;var Pa=r.bind(null,ye);Pa.arity=2,Pa.func=ye;var Fa=r.bind(null,me);Fa.arity=2,Fa.func=me;var Ja=r.bind(null,_e);Ja.arity=2,Ja.func=_e;var Va=t.bind(null,ke);Va.arity=3,Va.func=ke;var Ia=a.bind(null,Te);Ia.arity=4,Ia.func=Te;var Ya=u.bind(null,we);Ya.arity=5,Ya.func=we;var Wa=i.bind(null,Ee);Wa.arity=6,Wa.func=Ee;var za=o.bind(null,xe);za.arity=7,za.func=xe;var Ua=c.bind(null,Ce);Ua.arity=8,Ua.func=Ce;var Ka=l.bind(null,Ne);Ka.arity=9,Ka.func=Ne;var Ga=r.bind(null,Oe);Ga.arity=2,Ga.func=Oe;var Ha=t.bind(null,Me);Ha.arity=3,Ha.func=Me;var Qa=a.bind(null,De);Qa.arity=4,Qa.func=De;var Xa=u.bind(null,Le);Xa.arity=5,Xa.func=Le;var Za=i.bind(null,Be);Za.arity=6,Za.func=Be;var $a=o.bind(null,qe);$a.arity=7,$a.func=qe;var nu=c.bind(null,Re);nu.arity=8,nu.func=Re;var eu=l.bind(null,Pe);eu.arity=9,eu.func=Pe;var ru=r.bind(null,Fe);ru.arity=2,ru.func=Fe;var tu=r.bind(null,Je);tu.arity=2,tu.func=Je;var au={encode:Ta,runOnString:m,run:Ra,decodeNull:pe,decodePrimitive:be,decodeContainer:Pa,decodeField:Fa,decodeObject1:Ja,decodeObject2:Va,decodeObject3:Ia,decodeObject4:Ya,decodeObject5:Wa,decodeObject6:za,decodeObject7:Ua,decodeObject8:Ka,decodeKeyValuePairs:Se,decodeTuple1:Ga,decodeTuple2:Ha,decodeTuple3:Qa,decodeTuple4:Xa,decodeTuple5:Za,decodeTuple6:$a,decodeTuple7:nu,decodeTuple8:eu,andThen:ru,customAndThen:tu,fail:Ve,succeed:Ie,oneOf:Ye,identity:We,encodeNull:null,encodeArray:ue,encodeList:x,encodeObject:ze,equality:Ue},uu=f.bind(null,"div"),iu=r.bind(null,uu);iu.arity=2,iu.func=uu;var ou=r.bind(null,_r);ou.arity=2,ou.func=_r,_.arity=2,_.func=mr;var cu=f.bind(null,"section"),lu=r.bind(null,cu);lu.arity=2,lu.func=cu;var fu=r.bind(null,kr);fu.arity=2,fu.func=kr;var du=f.bind(null,"header"),su=r.bind(null,du);su.arity=2,su.func=du;var hu=f.bind(null,"h1"),vu=r.bind(null,hu);vu.arity=2,vu.func=hu;var gu=f.bind(null,"input"),pu=r.bind(null,gu);pu.arity=2,pu.func=gu;var bu=r.bind(null,Nr);bu.arity=2,bu.func=Nr;var yu=t.bind(null,Dr);yu.arity=3,yu.func=Dr;var mu={stopPropagation:!1,preventDefault:!1};A.arity=2,A.func=Mr;var _u={ctor:"",tag:"field",field:"target",decoder:{ctor:"",tag:"field",field:"value",decoder:{ctor:"",tag:"string"}}},Au=e("NoOp"),ku={ctor:"",tag:"field",field:"keyCode",decoder:{ctor:"",tag:"int"}},Tu=e("Add"),wu=t.bind(null,qr);wu.arity=3,wu.func=qr;var Eu=r.bind(null,Vr);Eu.arity=2,Eu.func=Vr,k.arity=2,k.func=Fr;var xu=f.bind(null,"label"),Cu=r.bind(null,xu);Cu.arity=2,Cu.func=xu;var Nu=f.bind(null,"li"),Su=r.bind(null,Nu);Su.arity=2,Su.func=Nu;var Ou=r.bind(null,Zr);Ou.arity=2,Ou.func=Zr;var ju=r.bind(null,nt);ju.arity=2,ju.func=nt;var Mu=r.bind(null,rt);Mu.arity=2,Mu.func=rt;var Du=r.bind(null,at);Du.arity=2,Du.func=at;var Lu=f.bind(null,"button"),Bu=r.bind(null,Lu);Bu.arity=2,Bu.func=Lu;var qu=r.bind(null,ot);qu.arity=2,qu.func=ot;var Ru=r.bind(null,ct);Ru.arity=2,Ru.func=ct;var Pu=t.bind(null,lt);Pu.arity=3,Pu.func=lt;var Fu=t.bind(null,ft);Fu.arity=3,Fu.func=ft;var Ju=r.bind(null,dt);Ju.arity=2,Ju.func=dt;var Vu=r.bind(null,st);Vu.arity=2,Vu.func=st;var Iu={"<|":qu,"|>":Ru,">>":Pu,"<<":Fu,"++":p,"||":void 0,"&&":void 0,">=":void 0,"<=":void 0,">":void 0,"<":void 0,"/=":void 0,"==":void 0,"^":void 0,"%":Ju,"//":Vu,"/":void 0,"*":void 0,"-":void 0,"+":void 0},Yu=r.bind(null,ht);Yu.arity=2,Yu.func=ht,T.arity=2,T.func=Rr;var Wu=t.bind(null,bt);Wu.arity=3,Wu.func=bt;var zu=f.bind(null,"footer"),Uu=r.bind(null,zu);Uu.arity=2,Uu.func=zu;var Ku=f.bind(null,"span"),Gu=r.bind(null,Ku);Gu.arity=2,Gu.func=Ku;var Hu=f.bind(null,"strong"),Qu=r.bind(null,Hu);Qu.arity=2,Qu.func=Hu;var Xu=f.bind(null,"ul"),Zu=r.bind(null,Xu);Zu.arity=2,Zu.func=Xu;var $u=f.bind(null,"a"),ni=r.bind(null,$u);ni.arity=2,ni.func=$u,S.arity=3,S.func=At;var ei=e("DeleteComplete");w.arity=2,w.func=gt;var ri={type:"node",tag:"footer",facts:{className:"info"},children:[{type:"node",tag:"p",facts:{},children:[{type:"text",text:"Double-click to edit a todo"}],namespace:void 0,descendantsCount:1},{type:"node",tag:"p",facts:{},children:[{type:"text",text:"Written by "},{type:"node",tag:"a",facts:{href:"https://github.com/evancz"},children:[{type:"text",text:"Evan Czaplicki"}],namespace:void 0,descendantsCount:1}],namespace:void 0,descendantsCount:3},{type:"node",tag:"p",facts:{},children:[{type:"text",text:"Part of "},{type:"node",tag:"a",facts:{href:"http://todomvc.com"},children:[{type:"text",text:"TodoMVC"}],namespace:void 0,descendantsCount:1}],namespace:void 0,descendantsCount:3}],namespace:void 0,descendantsCount:10},ti=r.bind(null,xt);ti.arity=2,ti.func=xt;var ai={"!":ti},ui=r.bind(null,Nt);ui.arity=2,ui.func=Nt,E.arity=2,E.func=Et;var ii={type:"node",branches:la},oi={init:{ctor:"_Tuple2",_0:{entries:la,visibility:"All",field:"",uid:0},_1:{type:"node",branches:la}},view:gr,update:E,subscriptions:Ot},ci={main:{init:vr,update:E,subscriptions:Ot,view:gr,renderer:ln}},li=r.bind(null,qt);li.arity=2,li.func=qt;var fi=r.bind(null,Rt);fi.arity=2,fi.func=Rt;var di=[],si=!1,hi=r.bind(null,Ut);hi.arity=2,hi.func=Ut;var vi={succeed:Dt,fail:Lt,nativeBinding:Bt,andThen:li,onError:fi,receive:Pt,spawn:Ft,kill:Wt,sleep:zt,send:hi,rawSpawn:Jt,rawSend:Kt},gi=r.bind(null,Xt);gi.arity=2,gi.func=Xt;var pi={focus:{tag:"cmd",cmdMap:gi,converter:Zt,isForeign:!0}};window.Elm={Todo:{worker:d,embed:ia,fullscreen:oa}}}).call(this); -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17-opt-prepack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Elm • TodoMVC 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17-opt-prepack/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | .todomvc-wrapper { 8 | visibility: visible !important; 9 | } 10 | 11 | button { 12 | margin: 0; 13 | padding: 0; 14 | border: 0; 15 | background: none; 16 | font-size: 100%; 17 | vertical-align: baseline; 18 | font-family: inherit; 19 | font-weight: inherit; 20 | color: inherit; 21 | -webkit-appearance: none; 22 | appearance: none; 23 | -webkit-font-smoothing: antialiased; 24 | -moz-font-smoothing: antialiased; 25 | font-smoothing: antialiased; 26 | } 27 | 28 | body { 29 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 30 | line-height: 1.4em; 31 | background: #f5f5f5; 32 | color: #4d4d4d; 33 | min-width: 230px; 34 | max-width: 550px; 35 | margin: 0 auto; 36 | -webkit-font-smoothing: antialiased; 37 | -moz-font-smoothing: antialiased; 38 | font-smoothing: antialiased; 39 | font-weight: 300; 40 | } 41 | 42 | button, 43 | input[type="checkbox"] { 44 | outline: none; 45 | } 46 | 47 | .hidden { 48 | display: none; 49 | } 50 | 51 | .todoapp { 52 | background: #fff; 53 | margin: 130px 0 40px 0; 54 | position: relative; 55 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 56 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 57 | } 58 | 59 | .todoapp input::-webkit-input-placeholder { 60 | font-style: italic; 61 | font-weight: 300; 62 | color: #e6e6e6; 63 | } 64 | 65 | .todoapp input::-moz-placeholder { 66 | font-style: italic; 67 | font-weight: 300; 68 | color: #e6e6e6; 69 | } 70 | 71 | .todoapp input::input-placeholder { 72 | font-style: italic; 73 | font-weight: 300; 74 | color: #e6e6e6; 75 | } 76 | 77 | .todoapp h1 { 78 | position: absolute; 79 | top: -155px; 80 | width: 100%; 81 | font-size: 100px; 82 | font-weight: 100; 83 | text-align: center; 84 | color: rgba(175, 47, 47, 0.15); 85 | -webkit-text-rendering: optimizeLegibility; 86 | -moz-text-rendering: optimizeLegibility; 87 | text-rendering: optimizeLegibility; 88 | } 89 | 90 | .new-todo, 91 | .edit { 92 | position: relative; 93 | margin: 0; 94 | width: 100%; 95 | font-size: 24px; 96 | font-family: inherit; 97 | font-weight: inherit; 98 | line-height: 1.4em; 99 | border: 0; 100 | outline: none; 101 | color: inherit; 102 | padding: 6px; 103 | border: 1px solid #999; 104 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 105 | box-sizing: border-box; 106 | -webkit-font-smoothing: antialiased; 107 | -moz-font-smoothing: antialiased; 108 | font-smoothing: antialiased; 109 | } 110 | 111 | .new-todo { 112 | padding: 16px 16px 16px 60px; 113 | border: none; 114 | background: rgba(0, 0, 0, 0.003); 115 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 116 | } 117 | 118 | .main { 119 | position: relative; 120 | z-index: 2; 121 | border-top: 1px solid #e6e6e6; 122 | } 123 | 124 | label[for='toggle-all'] { 125 | display: none; 126 | } 127 | 128 | .toggle-all { 129 | position: absolute; 130 | top: -55px; 131 | left: -12px; 132 | width: 60px; 133 | height: 34px; 134 | text-align: center; 135 | border: none; /* Mobile Safari */ 136 | } 137 | 138 | .toggle-all:before { 139 | content: '❯'; 140 | font-size: 22px; 141 | color: #e6e6e6; 142 | padding: 10px 27px 10px 27px; 143 | } 144 | 145 | .toggle-all:checked:before { 146 | color: #737373; 147 | } 148 | 149 | .todo-list { 150 | margin: 0; 151 | padding: 0; 152 | list-style: none; 153 | } 154 | 155 | .todo-list li { 156 | position: relative; 157 | font-size: 24px; 158 | border-bottom: 1px solid #ededed; 159 | } 160 | 161 | .todo-list li:last-child { 162 | border-bottom: none; 163 | } 164 | 165 | .todo-list li.editing { 166 | border-bottom: none; 167 | padding: 0; 168 | } 169 | 170 | .todo-list li.editing .edit { 171 | display: block; 172 | width: 506px; 173 | padding: 13px 17px 12px 17px; 174 | margin: 0 0 0 43px; 175 | } 176 | 177 | .todo-list li.editing .view { 178 | display: none; 179 | } 180 | 181 | .todo-list li .toggle { 182 | text-align: center; 183 | width: 40px; 184 | /* auto, since non-WebKit browsers doesn't support input styling */ 185 | height: auto; 186 | position: absolute; 187 | top: 0; 188 | bottom: 0; 189 | margin: auto 0; 190 | border: none; /* Mobile Safari */ 191 | -webkit-appearance: none; 192 | appearance: none; 193 | } 194 | 195 | .todo-list li .toggle:after { 196 | content: url('data:image/svg+xml;utf8,'); 197 | } 198 | 199 | .todo-list li .toggle:checked:after { 200 | content: url('data:image/svg+xml;utf8,'); 201 | } 202 | 203 | .todo-list li label { 204 | white-space: pre-line; 205 | word-break: break-all; 206 | padding: 15px 60px 15px 15px; 207 | margin-left: 45px; 208 | display: block; 209 | line-height: 1.2; 210 | transition: color 0.4s; 211 | } 212 | 213 | .todo-list li.completed label { 214 | color: #d9d9d9; 215 | text-decoration: line-through; 216 | } 217 | 218 | .todo-list li .destroy { 219 | display: none; 220 | position: absolute; 221 | top: 0; 222 | right: 10px; 223 | bottom: 0; 224 | width: 40px; 225 | height: 40px; 226 | margin: auto 0; 227 | font-size: 30px; 228 | color: #cc9a9a; 229 | margin-bottom: 11px; 230 | transition: color 0.2s ease-out; 231 | } 232 | 233 | .todo-list li .destroy:hover { 234 | color: #af5b5e; 235 | } 236 | 237 | .todo-list li .destroy:after { 238 | content: '×'; 239 | } 240 | 241 | .todo-list li:hover .destroy { 242 | display: block; 243 | } 244 | 245 | .todo-list li .edit { 246 | display: none; 247 | } 248 | 249 | .todo-list li.editing:last-child { 250 | margin-bottom: -1px; 251 | } 252 | 253 | .footer { 254 | color: #777; 255 | padding: 10px 15px; 256 | height: 20px; 257 | text-align: center; 258 | border-top: 1px solid #e6e6e6; 259 | } 260 | 261 | .footer:before { 262 | content: ''; 263 | position: absolute; 264 | right: 0; 265 | bottom: 0; 266 | left: 0; 267 | height: 50px; 268 | overflow: hidden; 269 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 270 | 0 8px 0 -3px #f6f6f6, 271 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 272 | 0 16px 0 -6px #f6f6f6, 273 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 274 | } 275 | 276 | .todo-count { 277 | float: left; 278 | text-align: left; 279 | } 280 | 281 | .todo-count strong { 282 | font-weight: 300; 283 | } 284 | 285 | .filters { 286 | margin: 0; 287 | padding: 0; 288 | list-style: none; 289 | position: absolute; 290 | right: 0; 291 | left: 0; 292 | } 293 | 294 | .filters li { 295 | display: inline; 296 | } 297 | 298 | .filters li a { 299 | color: inherit; 300 | margin: 3px; 301 | padding: 3px 7px; 302 | text-decoration: none; 303 | border: 1px solid transparent; 304 | border-radius: 3px; 305 | } 306 | 307 | .filters li a.selected, 308 | .filters li a:hover { 309 | border-color: rgba(175, 47, 47, 0.1); 310 | } 311 | 312 | .filters li a.selected { 313 | border-color: rgba(175, 47, 47, 0.2); 314 | } 315 | 316 | .clear-completed, 317 | html .clear-completed:active { 318 | float: right; 319 | position: relative; 320 | line-height: 20px; 321 | text-decoration: none; 322 | cursor: pointer; 323 | position: relative; 324 | } 325 | 326 | .clear-completed:hover { 327 | text-decoration: underline; 328 | } 329 | 330 | .info { 331 | margin: 65px auto 0; 332 | color: #bfbfbf; 333 | font-size: 10px; 334 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 335 | text-align: center; 336 | } 337 | 338 | .info p { 339 | line-height: 1; 340 | } 341 | 342 | .info a { 343 | color: inherit; 344 | text-decoration: none; 345 | font-weight: 400; 346 | } 347 | 348 | .info a:hover { 349 | text-decoration: underline; 350 | } 351 | 352 | /* 353 | Hack to remove background from Mobile Safari. 354 | Can't use it globally since it destroys checkboxes in Firefox 355 | */ 356 | @media screen and (-webkit-min-device-pixel-ratio:0) { 357 | .toggle-all, 358 | .todo-list li .toggle { 359 | background: none; 360 | } 361 | 362 | .todo-list li .toggle { 363 | height: 40px; 364 | } 365 | 366 | .toggle-all { 367 | -webkit-transform: rotate(90deg); 368 | transform: rotate(90deg); 369 | -webkit-appearance: none; 370 | appearance: none; 371 | } 372 | } 373 | 374 | @media (max-width: 430px) { 375 | .footer { 376 | height: 50px; 377 | } 378 | 379 | .filters { 380 | bottom: 10px; 381 | } 382 | } -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17-optimized/Todo.elm: -------------------------------------------------------------------------------- 1 | port module Todo exposing (main) 2 | {-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering. 3 | 4 | This application is broken up into three key parts: 5 | 6 | 1. Model - a full definition of the application's state 7 | 2. Update - a way to step the application state forward 8 | 3. View - a way to visualize our application state with HTML 9 | 10 | This clean division of concerns is a core part of Elm. You can read more about 11 | this in 12 | -} 13 | 14 | import Html exposing (..) 15 | import Html.App as App 16 | import Html.Attributes exposing (..) 17 | import Html.Events exposing (..) 18 | import Html.Keyed as Keyed 19 | import Html.Lazy exposing (lazy, lazy2) 20 | import Json.Decode as Json 21 | import String 22 | 23 | 24 | 25 | main : Program Never 26 | main = 27 | App.program 28 | { init = init 29 | , view = view 30 | , update = update 31 | , subscriptions = \_ -> Sub.none 32 | } 33 | 34 | 35 | port focus : String -> Cmd msg 36 | 37 | 38 | 39 | -- MODEL 40 | 41 | 42 | -- The full application state of our todo app. 43 | type alias Model = 44 | { entries : List Entry 45 | , field : String 46 | , uid : Int 47 | , visibility : String 48 | } 49 | 50 | 51 | type alias Entry = 52 | { description : String 53 | , completed : Bool 54 | , editing : Bool 55 | , id : Int 56 | } 57 | 58 | 59 | emptyModel : Model 60 | emptyModel = 61 | { entries = [] 62 | , visibility = "All" 63 | , field = "" 64 | , uid = 0 65 | } 66 | 67 | 68 | newEntry : String -> Int -> Entry 69 | newEntry desc id = 70 | { description = desc 71 | , completed = False 72 | , editing = False 73 | , id = id 74 | } 75 | 76 | 77 | init : ( Model, Cmd Msg ) 78 | init = 79 | emptyModel ! [] 80 | 81 | 82 | 83 | -- UPDATE 84 | 85 | 86 | {-| Users of our app can trigger messages by clicking and typing. These 87 | messages are fed into the `update` function as they occur, letting us react 88 | to them. 89 | -} 90 | type Msg 91 | = NoOp 92 | | UpdateField String 93 | | EditingEntry Int Bool 94 | | UpdateEntry Int String 95 | | Add 96 | | Delete Int 97 | | DeleteComplete 98 | | Check Int Bool 99 | | CheckAll Bool 100 | | ChangeVisibility String 101 | 102 | 103 | -- How we update our Model on a given Msg? 104 | update : Msg -> Model -> ( Model, Cmd Msg ) 105 | update msg model = 106 | case msg of 107 | NoOp -> 108 | model ! [] 109 | 110 | Add -> 111 | { model 112 | | uid = model.uid + 1 113 | , field = "" 114 | , entries = 115 | if String.isEmpty model.field then 116 | model.entries 117 | else 118 | model.entries ++ [newEntry model.field model.uid] 119 | } 120 | ! [] 121 | 122 | UpdateField str -> 123 | { model | field = str } 124 | ! [] 125 | 126 | EditingEntry id isEditing -> 127 | let 128 | updateEntry t = 129 | if t.id == id then { t | editing = isEditing } else t 130 | in 131 | { model | entries = List.map updateEntry model.entries } 132 | ! [ focus ("#todo-" ++ toString id) ] 133 | 134 | UpdateEntry id task -> 135 | let 136 | updateEntry t = 137 | if t.id == id then { t | description = task } else t 138 | in 139 | { model | entries = List.map updateEntry model.entries } 140 | ! [] 141 | 142 | Delete id -> 143 | { model | entries = List.filter (\t -> t.id /= id) model.entries } 144 | ! [] 145 | 146 | DeleteComplete -> 147 | { model | entries = List.filter (not << .completed) model.entries } 148 | ! [] 149 | 150 | Check id isCompleted -> 151 | let 152 | updateEntry t = 153 | if t.id == id then { t | completed = isCompleted } else t 154 | in 155 | { model | entries = List.map updateEntry model.entries } 156 | ! [] 157 | 158 | CheckAll isCompleted -> 159 | let 160 | updateEntry t = 161 | { t | completed = isCompleted } 162 | in 163 | { model | entries = List.map updateEntry model.entries } 164 | ! [] 165 | 166 | ChangeVisibility visibility -> 167 | { model | visibility = visibility } 168 | ! [] 169 | 170 | 171 | 172 | -- VIEW 173 | 174 | 175 | view : Model -> Html Msg 176 | view model = 177 | div 178 | [ class "todomvc-wrapper" 179 | , style [ ("visibility", "hidden") ] 180 | ] 181 | [ section 182 | [ class "todoapp" ] 183 | [ lazy viewInput model.field 184 | , lazy2 viewEntries model.visibility model.entries 185 | , lazy2 viewControls model.visibility model.entries 186 | ] 187 | , infoFooter 188 | ] 189 | 190 | 191 | viewInput : String -> Html Msg 192 | viewInput task = 193 | header 194 | [ class "header" ] 195 | [ h1 [] [ text "todos" ] 196 | , input 197 | [ class "new-todo" 198 | , placeholder "What needs to be done?" 199 | , autofocus True 200 | , value task 201 | , name "newTodo" 202 | , onInput UpdateField 203 | , onEnter Add 204 | ] 205 | [] 206 | ] 207 | 208 | 209 | onEnter : Msg -> Attribute Msg 210 | onEnter msg = 211 | let 212 | tagger code = 213 | if code == 13 then msg else NoOp 214 | in 215 | on "keydown" (Json.map tagger keyCode) 216 | 217 | 218 | 219 | -- VIEW ALL ENTRIES 220 | 221 | 222 | viewEntries : String -> List Entry -> Html Msg 223 | viewEntries visibility entries = 224 | let 225 | isVisible todo = 226 | case visibility of 227 | "Completed" -> todo.completed 228 | "Active" -> not todo.completed 229 | _ -> True 230 | 231 | allCompleted = 232 | List.all .completed entries 233 | 234 | cssVisibility = 235 | if List.isEmpty entries then "hidden" else "visible" 236 | in 237 | section 238 | [ class "main" 239 | , style [ ("visibility", cssVisibility) ] 240 | ] 241 | [ input 242 | [ class "toggle-all" 243 | , type' "checkbox" 244 | , name "toggle" 245 | , checked allCompleted 246 | , onClick (CheckAll (not allCompleted)) 247 | ] 248 | [] 249 | , label 250 | [ for "toggle-all" ] 251 | [ text "Mark all as complete" ] 252 | , Keyed.ul [ class "todo-list" ] <| 253 | List.map viewKeyedEntry (List.filter isVisible entries) 254 | ] 255 | 256 | 257 | 258 | -- VIEW INDIVIDUAL ENTRIES 259 | 260 | 261 | viewKeyedEntry : Entry -> (String, Html Msg) 262 | viewKeyedEntry todo = 263 | ( toString todo.id, lazy viewEntry todo ) 264 | 265 | 266 | viewEntry : Entry -> Html Msg 267 | viewEntry todo = 268 | li 269 | [ classList [ ("completed", todo.completed), ("editing", todo.editing) ] ] 270 | [ div 271 | [ class "view" ] 272 | [ input 273 | [ class "toggle" 274 | , type' "checkbox" 275 | , checked todo.completed 276 | , onClick (Check todo.id (not todo.completed)) 277 | ] 278 | [] 279 | , label 280 | [ onDoubleClick (EditingEntry todo.id True) ] 281 | [ text todo.description ] 282 | , button 283 | [ class "destroy" 284 | , onClick (Delete todo.id) 285 | ] 286 | [] 287 | ] 288 | , input 289 | [ class "edit" 290 | , value todo.description 291 | , name "title" 292 | , id ("todo-" ++ toString todo.id) 293 | , onInput (UpdateEntry todo.id) 294 | , onBlur (EditingEntry todo.id False) 295 | , onEnter (EditingEntry todo.id False) 296 | ] 297 | [] 298 | ] 299 | 300 | 301 | 302 | -- VIEW CONTROLS AND FOOTER 303 | 304 | 305 | viewControls : String -> List Entry -> Html Msg 306 | viewControls visibility entries = 307 | let 308 | entriesCompleted = 309 | List.length (List.filter .completed entries) 310 | 311 | entriesLeft = 312 | List.length entries - entriesCompleted 313 | in 314 | footer 315 | [ class "footer" 316 | , hidden (List.isEmpty entries) 317 | ] 318 | [ lazy viewControlsCount entriesLeft 319 | , lazy viewControlsFilters visibility 320 | , lazy viewControlsClear entriesCompleted 321 | ] 322 | 323 | 324 | viewControlsCount : Int -> Html Msg 325 | viewControlsCount entriesLeft = 326 | let 327 | item_ = 328 | if entriesLeft == 1 then " item" else " items" 329 | in 330 | span 331 | [ class "todo-count" ] 332 | [ strong [] [ text (toString entriesLeft) ] 333 | , text (item_ ++ " left") 334 | ] 335 | 336 | 337 | viewControlsFilters : String -> Html Msg 338 | viewControlsFilters visibility = 339 | ul 340 | [ class "filters" ] 341 | [ visibilitySwap "#/" "All" visibility 342 | , text " " 343 | , visibilitySwap "#/active" "Active" visibility 344 | , text " " 345 | , visibilitySwap "#/completed" "Completed" visibility 346 | ] 347 | 348 | 349 | visibilitySwap : String -> String -> String -> Html Msg 350 | visibilitySwap uri visibility actualVisibility = 351 | li 352 | [ onClick (ChangeVisibility visibility) ] 353 | [ a [ href uri, classList [("selected", visibility == actualVisibility)] ] 354 | [ text visibility ] 355 | ] 356 | 357 | 358 | viewControlsClear : Int -> Html Msg 359 | viewControlsClear entriesCompleted = 360 | button 361 | [ class "clear-completed" 362 | , hidden (entriesCompleted == 0) 363 | , onClick DeleteComplete 364 | ] 365 | [ text ("Clear completed (" ++ toString entriesCompleted ++ ")") 366 | ] 367 | 368 | 369 | infoFooter : Html msg 370 | infoFooter = 371 | footer [ class "info" ] 372 | [ p [] [ text "Double-click to edit a todo" ] 373 | , p [] 374 | [ text "Written by " 375 | , a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ] 376 | ] 377 | , p [] 378 | [ text "Part of " 379 | , a [ href "http://todomvc.com" ] [ text "TodoMVC" ] 380 | ] 381 | ] 382 | -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17-optimized/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "TodoMVC created with Elm and elm-html", 4 | "repository": "https://github.com/evancz/elm-todomvc.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "4.0.0 <= v < 5.0.0", 12 | "elm-lang/html": "1.0.0 <= v < 2.0.0" 13 | }, 14 | "elm-version": "0.17.0 <= v < 0.18.0" 15 | } 16 | -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17-optimized/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Elm • TodoMVC 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17-optimized/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | .todomvc-wrapper { 8 | visibility: visible !important; 9 | } 10 | 11 | button { 12 | margin: 0; 13 | padding: 0; 14 | border: 0; 15 | background: none; 16 | font-size: 100%; 17 | vertical-align: baseline; 18 | font-family: inherit; 19 | font-weight: inherit; 20 | color: inherit; 21 | -webkit-appearance: none; 22 | appearance: none; 23 | -webkit-font-smoothing: antialiased; 24 | -moz-font-smoothing: antialiased; 25 | font-smoothing: antialiased; 26 | } 27 | 28 | body { 29 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 30 | line-height: 1.4em; 31 | background: #f5f5f5; 32 | color: #4d4d4d; 33 | min-width: 230px; 34 | max-width: 550px; 35 | margin: 0 auto; 36 | -webkit-font-smoothing: antialiased; 37 | -moz-font-smoothing: antialiased; 38 | font-smoothing: antialiased; 39 | font-weight: 300; 40 | } 41 | 42 | button, 43 | input[type="checkbox"] { 44 | outline: none; 45 | } 46 | 47 | .hidden { 48 | display: none; 49 | } 50 | 51 | .todoapp { 52 | background: #fff; 53 | margin: 130px 0 40px 0; 54 | position: relative; 55 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 56 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 57 | } 58 | 59 | .todoapp input::-webkit-input-placeholder { 60 | font-style: italic; 61 | font-weight: 300; 62 | color: #e6e6e6; 63 | } 64 | 65 | .todoapp input::-moz-placeholder { 66 | font-style: italic; 67 | font-weight: 300; 68 | color: #e6e6e6; 69 | } 70 | 71 | .todoapp input::input-placeholder { 72 | font-style: italic; 73 | font-weight: 300; 74 | color: #e6e6e6; 75 | } 76 | 77 | .todoapp h1 { 78 | position: absolute; 79 | top: -155px; 80 | width: 100%; 81 | font-size: 100px; 82 | font-weight: 100; 83 | text-align: center; 84 | color: rgba(175, 47, 47, 0.15); 85 | -webkit-text-rendering: optimizeLegibility; 86 | -moz-text-rendering: optimizeLegibility; 87 | text-rendering: optimizeLegibility; 88 | } 89 | 90 | .new-todo, 91 | .edit { 92 | position: relative; 93 | margin: 0; 94 | width: 100%; 95 | font-size: 24px; 96 | font-family: inherit; 97 | font-weight: inherit; 98 | line-height: 1.4em; 99 | border: 0; 100 | outline: none; 101 | color: inherit; 102 | padding: 6px; 103 | border: 1px solid #999; 104 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 105 | box-sizing: border-box; 106 | -webkit-font-smoothing: antialiased; 107 | -moz-font-smoothing: antialiased; 108 | font-smoothing: antialiased; 109 | } 110 | 111 | .new-todo { 112 | padding: 16px 16px 16px 60px; 113 | border: none; 114 | background: rgba(0, 0, 0, 0.003); 115 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 116 | } 117 | 118 | .main { 119 | position: relative; 120 | z-index: 2; 121 | border-top: 1px solid #e6e6e6; 122 | } 123 | 124 | label[for='toggle-all'] { 125 | display: none; 126 | } 127 | 128 | .toggle-all { 129 | position: absolute; 130 | top: -55px; 131 | left: -12px; 132 | width: 60px; 133 | height: 34px; 134 | text-align: center; 135 | border: none; /* Mobile Safari */ 136 | } 137 | 138 | .toggle-all:before { 139 | content: '❯'; 140 | font-size: 22px; 141 | color: #e6e6e6; 142 | padding: 10px 27px 10px 27px; 143 | } 144 | 145 | .toggle-all:checked:before { 146 | color: #737373; 147 | } 148 | 149 | .todo-list { 150 | margin: 0; 151 | padding: 0; 152 | list-style: none; 153 | } 154 | 155 | .todo-list li { 156 | position: relative; 157 | font-size: 24px; 158 | border-bottom: 1px solid #ededed; 159 | } 160 | 161 | .todo-list li:last-child { 162 | border-bottom: none; 163 | } 164 | 165 | .todo-list li.editing { 166 | border-bottom: none; 167 | padding: 0; 168 | } 169 | 170 | .todo-list li.editing .edit { 171 | display: block; 172 | width: 506px; 173 | padding: 13px 17px 12px 17px; 174 | margin: 0 0 0 43px; 175 | } 176 | 177 | .todo-list li.editing .view { 178 | display: none; 179 | } 180 | 181 | .todo-list li .toggle { 182 | text-align: center; 183 | width: 40px; 184 | /* auto, since non-WebKit browsers doesn't support input styling */ 185 | height: auto; 186 | position: absolute; 187 | top: 0; 188 | bottom: 0; 189 | margin: auto 0; 190 | border: none; /* Mobile Safari */ 191 | -webkit-appearance: none; 192 | appearance: none; 193 | } 194 | 195 | .todo-list li .toggle:after { 196 | content: url('data:image/svg+xml;utf8,'); 197 | } 198 | 199 | .todo-list li .toggle:checked:after { 200 | content: url('data:image/svg+xml;utf8,'); 201 | } 202 | 203 | .todo-list li label { 204 | white-space: pre-line; 205 | word-break: break-all; 206 | padding: 15px 60px 15px 15px; 207 | margin-left: 45px; 208 | display: block; 209 | line-height: 1.2; 210 | transition: color 0.4s; 211 | } 212 | 213 | .todo-list li.completed label { 214 | color: #d9d9d9; 215 | text-decoration: line-through; 216 | } 217 | 218 | .todo-list li .destroy { 219 | display: none; 220 | position: absolute; 221 | top: 0; 222 | right: 10px; 223 | bottom: 0; 224 | width: 40px; 225 | height: 40px; 226 | margin: auto 0; 227 | font-size: 30px; 228 | color: #cc9a9a; 229 | margin-bottom: 11px; 230 | transition: color 0.2s ease-out; 231 | } 232 | 233 | .todo-list li .destroy:hover { 234 | color: #af5b5e; 235 | } 236 | 237 | .todo-list li .destroy:after { 238 | content: '×'; 239 | } 240 | 241 | .todo-list li:hover .destroy { 242 | display: block; 243 | } 244 | 245 | .todo-list li .edit { 246 | display: none; 247 | } 248 | 249 | .todo-list li.editing:last-child { 250 | margin-bottom: -1px; 251 | } 252 | 253 | .footer { 254 | color: #777; 255 | padding: 10px 15px; 256 | height: 20px; 257 | text-align: center; 258 | border-top: 1px solid #e6e6e6; 259 | } 260 | 261 | .footer:before { 262 | content: ''; 263 | position: absolute; 264 | right: 0; 265 | bottom: 0; 266 | left: 0; 267 | height: 50px; 268 | overflow: hidden; 269 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 270 | 0 8px 0 -3px #f6f6f6, 271 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 272 | 0 16px 0 -6px #f6f6f6, 273 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 274 | } 275 | 276 | .todo-count { 277 | float: left; 278 | text-align: left; 279 | } 280 | 281 | .todo-count strong { 282 | font-weight: 300; 283 | } 284 | 285 | .filters { 286 | margin: 0; 287 | padding: 0; 288 | list-style: none; 289 | position: absolute; 290 | right: 0; 291 | left: 0; 292 | } 293 | 294 | .filters li { 295 | display: inline; 296 | } 297 | 298 | .filters li a { 299 | color: inherit; 300 | margin: 3px; 301 | padding: 3px 7px; 302 | text-decoration: none; 303 | border: 1px solid transparent; 304 | border-radius: 3px; 305 | } 306 | 307 | .filters li a.selected, 308 | .filters li a:hover { 309 | border-color: rgba(175, 47, 47, 0.1); 310 | } 311 | 312 | .filters li a.selected { 313 | border-color: rgba(175, 47, 47, 0.2); 314 | } 315 | 316 | .clear-completed, 317 | html .clear-completed:active { 318 | float: right; 319 | position: relative; 320 | line-height: 20px; 321 | text-decoration: none; 322 | cursor: pointer; 323 | position: relative; 324 | } 325 | 326 | .clear-completed:hover { 327 | text-decoration: underline; 328 | } 329 | 330 | .info { 331 | margin: 65px auto 0; 332 | color: #bfbfbf; 333 | font-size: 10px; 334 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 335 | text-align: center; 336 | } 337 | 338 | .info p { 339 | line-height: 1; 340 | } 341 | 342 | .info a { 343 | color: inherit; 344 | text-decoration: none; 345 | font-weight: 400; 346 | } 347 | 348 | .info a:hover { 349 | text-decoration: underline; 350 | } 351 | 352 | /* 353 | Hack to remove background from Mobile Safari. 354 | Can't use it globally since it destroys checkboxes in Firefox 355 | */ 356 | @media screen and (-webkit-min-device-pixel-ratio:0) { 357 | .toggle-all, 358 | .todo-list li .toggle { 359 | background: none; 360 | } 361 | 362 | .todo-list li .toggle { 363 | height: 40px; 364 | } 365 | 366 | .toggle-all { 367 | -webkit-transform: rotate(90deg); 368 | transform: rotate(90deg); 369 | -webkit-appearance: none; 370 | appearance: none; 371 | } 372 | } 373 | 374 | @media (max-width: 430px) { 375 | .footer { 376 | height: 50px; 377 | } 378 | 379 | .filters { 380 | bottom: 10px; 381 | } 382 | } -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17-prepack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Elm • TodoMVC 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17-prepack/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | .todomvc-wrapper { 8 | visibility: visible !important; 9 | } 10 | 11 | button { 12 | margin: 0; 13 | padding: 0; 14 | border: 0; 15 | background: none; 16 | font-size: 100%; 17 | vertical-align: baseline; 18 | font-family: inherit; 19 | font-weight: inherit; 20 | color: inherit; 21 | -webkit-appearance: none; 22 | appearance: none; 23 | -webkit-font-smoothing: antialiased; 24 | -moz-font-smoothing: antialiased; 25 | font-smoothing: antialiased; 26 | } 27 | 28 | body { 29 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 30 | line-height: 1.4em; 31 | background: #f5f5f5; 32 | color: #4d4d4d; 33 | min-width: 230px; 34 | max-width: 550px; 35 | margin: 0 auto; 36 | -webkit-font-smoothing: antialiased; 37 | -moz-font-smoothing: antialiased; 38 | font-smoothing: antialiased; 39 | font-weight: 300; 40 | } 41 | 42 | button, 43 | input[type="checkbox"] { 44 | outline: none; 45 | } 46 | 47 | .hidden { 48 | display: none; 49 | } 50 | 51 | .todoapp { 52 | background: #fff; 53 | margin: 130px 0 40px 0; 54 | position: relative; 55 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 56 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 57 | } 58 | 59 | .todoapp input::-webkit-input-placeholder { 60 | font-style: italic; 61 | font-weight: 300; 62 | color: #e6e6e6; 63 | } 64 | 65 | .todoapp input::-moz-placeholder { 66 | font-style: italic; 67 | font-weight: 300; 68 | color: #e6e6e6; 69 | } 70 | 71 | .todoapp input::input-placeholder { 72 | font-style: italic; 73 | font-weight: 300; 74 | color: #e6e6e6; 75 | } 76 | 77 | .todoapp h1 { 78 | position: absolute; 79 | top: -155px; 80 | width: 100%; 81 | font-size: 100px; 82 | font-weight: 100; 83 | text-align: center; 84 | color: rgba(175, 47, 47, 0.15); 85 | -webkit-text-rendering: optimizeLegibility; 86 | -moz-text-rendering: optimizeLegibility; 87 | text-rendering: optimizeLegibility; 88 | } 89 | 90 | .new-todo, 91 | .edit { 92 | position: relative; 93 | margin: 0; 94 | width: 100%; 95 | font-size: 24px; 96 | font-family: inherit; 97 | font-weight: inherit; 98 | line-height: 1.4em; 99 | border: 0; 100 | outline: none; 101 | color: inherit; 102 | padding: 6px; 103 | border: 1px solid #999; 104 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 105 | box-sizing: border-box; 106 | -webkit-font-smoothing: antialiased; 107 | -moz-font-smoothing: antialiased; 108 | font-smoothing: antialiased; 109 | } 110 | 111 | .new-todo { 112 | padding: 16px 16px 16px 60px; 113 | border: none; 114 | background: rgba(0, 0, 0, 0.003); 115 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 116 | } 117 | 118 | .main { 119 | position: relative; 120 | z-index: 2; 121 | border-top: 1px solid #e6e6e6; 122 | } 123 | 124 | label[for='toggle-all'] { 125 | display: none; 126 | } 127 | 128 | .toggle-all { 129 | position: absolute; 130 | top: -55px; 131 | left: -12px; 132 | width: 60px; 133 | height: 34px; 134 | text-align: center; 135 | border: none; /* Mobile Safari */ 136 | } 137 | 138 | .toggle-all:before { 139 | content: '❯'; 140 | font-size: 22px; 141 | color: #e6e6e6; 142 | padding: 10px 27px 10px 27px; 143 | } 144 | 145 | .toggle-all:checked:before { 146 | color: #737373; 147 | } 148 | 149 | .todo-list { 150 | margin: 0; 151 | padding: 0; 152 | list-style: none; 153 | } 154 | 155 | .todo-list li { 156 | position: relative; 157 | font-size: 24px; 158 | border-bottom: 1px solid #ededed; 159 | } 160 | 161 | .todo-list li:last-child { 162 | border-bottom: none; 163 | } 164 | 165 | .todo-list li.editing { 166 | border-bottom: none; 167 | padding: 0; 168 | } 169 | 170 | .todo-list li.editing .edit { 171 | display: block; 172 | width: 506px; 173 | padding: 13px 17px 12px 17px; 174 | margin: 0 0 0 43px; 175 | } 176 | 177 | .todo-list li.editing .view { 178 | display: none; 179 | } 180 | 181 | .todo-list li .toggle { 182 | text-align: center; 183 | width: 40px; 184 | /* auto, since non-WebKit browsers doesn't support input styling */ 185 | height: auto; 186 | position: absolute; 187 | top: 0; 188 | bottom: 0; 189 | margin: auto 0; 190 | border: none; /* Mobile Safari */ 191 | -webkit-appearance: none; 192 | appearance: none; 193 | } 194 | 195 | .todo-list li .toggle:after { 196 | content: url('data:image/svg+xml;utf8,'); 197 | } 198 | 199 | .todo-list li .toggle:checked:after { 200 | content: url('data:image/svg+xml;utf8,'); 201 | } 202 | 203 | .todo-list li label { 204 | white-space: pre-line; 205 | word-break: break-all; 206 | padding: 15px 60px 15px 15px; 207 | margin-left: 45px; 208 | display: block; 209 | line-height: 1.2; 210 | transition: color 0.4s; 211 | } 212 | 213 | .todo-list li.completed label { 214 | color: #d9d9d9; 215 | text-decoration: line-through; 216 | } 217 | 218 | .todo-list li .destroy { 219 | display: none; 220 | position: absolute; 221 | top: 0; 222 | right: 10px; 223 | bottom: 0; 224 | width: 40px; 225 | height: 40px; 226 | margin: auto 0; 227 | font-size: 30px; 228 | color: #cc9a9a; 229 | margin-bottom: 11px; 230 | transition: color 0.2s ease-out; 231 | } 232 | 233 | .todo-list li .destroy:hover { 234 | color: #af5b5e; 235 | } 236 | 237 | .todo-list li .destroy:after { 238 | content: '×'; 239 | } 240 | 241 | .todo-list li:hover .destroy { 242 | display: block; 243 | } 244 | 245 | .todo-list li .edit { 246 | display: none; 247 | } 248 | 249 | .todo-list li.editing:last-child { 250 | margin-bottom: -1px; 251 | } 252 | 253 | .footer { 254 | color: #777; 255 | padding: 10px 15px; 256 | height: 20px; 257 | text-align: center; 258 | border-top: 1px solid #e6e6e6; 259 | } 260 | 261 | .footer:before { 262 | content: ''; 263 | position: absolute; 264 | right: 0; 265 | bottom: 0; 266 | left: 0; 267 | height: 50px; 268 | overflow: hidden; 269 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 270 | 0 8px 0 -3px #f6f6f6, 271 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 272 | 0 16px 0 -6px #f6f6f6, 273 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 274 | } 275 | 276 | .todo-count { 277 | float: left; 278 | text-align: left; 279 | } 280 | 281 | .todo-count strong { 282 | font-weight: 300; 283 | } 284 | 285 | .filters { 286 | margin: 0; 287 | padding: 0; 288 | list-style: none; 289 | position: absolute; 290 | right: 0; 291 | left: 0; 292 | } 293 | 294 | .filters li { 295 | display: inline; 296 | } 297 | 298 | .filters li a { 299 | color: inherit; 300 | margin: 3px; 301 | padding: 3px 7px; 302 | text-decoration: none; 303 | border: 1px solid transparent; 304 | border-radius: 3px; 305 | } 306 | 307 | .filters li a.selected, 308 | .filters li a:hover { 309 | border-color: rgba(175, 47, 47, 0.1); 310 | } 311 | 312 | .filters li a.selected { 313 | border-color: rgba(175, 47, 47, 0.2); 314 | } 315 | 316 | .clear-completed, 317 | html .clear-completed:active { 318 | float: right; 319 | position: relative; 320 | line-height: 20px; 321 | text-decoration: none; 322 | cursor: pointer; 323 | position: relative; 324 | } 325 | 326 | .clear-completed:hover { 327 | text-decoration: underline; 328 | } 329 | 330 | .info { 331 | margin: 65px auto 0; 332 | color: #bfbfbf; 333 | font-size: 10px; 334 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 335 | text-align: center; 336 | } 337 | 338 | .info p { 339 | line-height: 1; 340 | } 341 | 342 | .info a { 343 | color: inherit; 344 | text-decoration: none; 345 | font-weight: 400; 346 | } 347 | 348 | .info a:hover { 349 | text-decoration: underline; 350 | } 351 | 352 | /* 353 | Hack to remove background from Mobile Safari. 354 | Can't use it globally since it destroys checkboxes in Firefox 355 | */ 356 | @media screen and (-webkit-min-device-pixel-ratio:0) { 357 | .toggle-all, 358 | .todo-list li .toggle { 359 | background: none; 360 | } 361 | 362 | .todo-list li .toggle { 363 | height: 40px; 364 | } 365 | 366 | .toggle-all { 367 | -webkit-transform: rotate(90deg); 368 | transform: rotate(90deg); 369 | -webkit-appearance: none; 370 | appearance: none; 371 | } 372 | } 373 | 374 | @media (max-width: 430px) { 375 | .footer { 376 | height: 50px; 377 | } 378 | 379 | .filters { 380 | bottom: 10px; 381 | } 382 | } -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17/Todo.elm: -------------------------------------------------------------------------------- 1 | port module Todo exposing (main) 2 | {-| TodoMVC implemented in Elm, using plain HTML and CSS for rendering. 3 | 4 | This application is broken up into three key parts: 5 | 6 | 1. Model - a full definition of the application's state 7 | 2. Update - a way to step the application state forward 8 | 3. View - a way to visualize our application state with HTML 9 | 10 | This clean division of concerns is a core part of Elm. You can read more about 11 | this in 12 | -} 13 | 14 | import Html exposing (..) 15 | import Html.App as App 16 | import Html.Attributes exposing (..) 17 | import Html.Events exposing (..) 18 | import Json.Decode as Json 19 | import String 20 | 21 | 22 | 23 | main : Program Never 24 | main = 25 | App.program 26 | { init = init 27 | , view = view 28 | , update = update 29 | , subscriptions = \_ -> Sub.none 30 | } 31 | 32 | 33 | port focus : String -> Cmd msg 34 | 35 | 36 | 37 | -- MODEL 38 | 39 | 40 | -- The full application state of our todo app. 41 | type alias Model = 42 | { entries : List Entry 43 | , field : String 44 | , uid : Int 45 | , visibility : String 46 | } 47 | 48 | 49 | type alias Entry = 50 | { description : String 51 | , completed : Bool 52 | , editing : Bool 53 | , id : Int 54 | } 55 | 56 | 57 | emptyModel : Model 58 | emptyModel = 59 | { entries = [] 60 | , visibility = "All" 61 | , field = "" 62 | , uid = 0 63 | } 64 | 65 | 66 | newEntry : String -> Int -> Entry 67 | newEntry desc id = 68 | { description = desc 69 | , completed = False 70 | , editing = False 71 | , id = id 72 | } 73 | 74 | 75 | init : ( Model, Cmd Msg ) 76 | init = 77 | emptyModel ! [] 78 | 79 | 80 | 81 | -- UPDATE 82 | 83 | 84 | {-| Users of our app can trigger messages by clicking and typing. These 85 | messages are fed into the `update` function as they occur, letting us react 86 | to them. 87 | -} 88 | type Msg 89 | = NoOp 90 | | UpdateField String 91 | | EditingEntry Int Bool 92 | | UpdateEntry Int String 93 | | Add 94 | | Delete Int 95 | | DeleteComplete 96 | | Check Int Bool 97 | | CheckAll Bool 98 | | ChangeVisibility String 99 | 100 | 101 | -- How we update our Model on a given Msg? 102 | update : Msg -> Model -> ( Model, Cmd Msg ) 103 | update msg model = 104 | case msg of 105 | NoOp -> 106 | model ! [] 107 | 108 | Add -> 109 | { model 110 | | uid = model.uid + 1 111 | , field = "" 112 | , entries = 113 | if String.isEmpty model.field then 114 | model.entries 115 | else 116 | model.entries ++ [newEntry model.field model.uid] 117 | } 118 | ! [] 119 | 120 | UpdateField str -> 121 | { model | field = str } 122 | ! [] 123 | 124 | EditingEntry id isEditing -> 125 | let 126 | updateEntry t = 127 | if t.id == id then { t | editing = isEditing } else t 128 | in 129 | { model | entries = List.map updateEntry model.entries } 130 | ! [ focus ("#todo-" ++ toString id) ] 131 | 132 | UpdateEntry id task -> 133 | let 134 | updateEntry t = 135 | if t.id == id then { t | description = task } else t 136 | in 137 | { model | entries = List.map updateEntry model.entries } 138 | ! [] 139 | 140 | Delete id -> 141 | { model | entries = List.filter (\t -> t.id /= id) model.entries } 142 | ! [] 143 | 144 | DeleteComplete -> 145 | { model | entries = List.filter (not << .completed) model.entries } 146 | ! [] 147 | 148 | Check id isCompleted -> 149 | let 150 | updateEntry t = 151 | if t.id == id then { t | completed = isCompleted } else t 152 | in 153 | { model | entries = List.map updateEntry model.entries } 154 | ! [] 155 | 156 | CheckAll isCompleted -> 157 | let 158 | updateEntry t = 159 | { t | completed = isCompleted } 160 | in 161 | { model | entries = List.map updateEntry model.entries } 162 | ! [] 163 | 164 | ChangeVisibility visibility -> 165 | { model | visibility = visibility } 166 | ! [] 167 | 168 | 169 | 170 | -- VIEW 171 | 172 | 173 | view : Model -> Html Msg 174 | view model = 175 | div 176 | [ class "todomvc-wrapper" 177 | , style [ ("visibility", "hidden") ] 178 | ] 179 | [ section 180 | [ class "todoapp" ] 181 | [ viewInput model.field 182 | , viewEntries model.visibility model.entries 183 | , viewControls model.visibility model.entries 184 | ] 185 | , infoFooter 186 | ] 187 | 188 | 189 | viewInput : String -> Html Msg 190 | viewInput task = 191 | header 192 | [ class "header" ] 193 | [ h1 [] [ text "todos" ] 194 | , input 195 | [ class "new-todo" 196 | , placeholder "What needs to be done?" 197 | , autofocus True 198 | , value task 199 | , name "newTodo" 200 | , onInput UpdateField 201 | , onEnter Add 202 | ] 203 | [] 204 | ] 205 | 206 | 207 | onEnter : Msg -> Attribute Msg 208 | onEnter msg = 209 | let 210 | tagger code = 211 | if code == 13 then msg else NoOp 212 | in 213 | on "keydown" (Json.map tagger keyCode) 214 | 215 | 216 | 217 | -- VIEW ALL ENTRIES 218 | 219 | 220 | viewEntries : String -> List Entry -> Html Msg 221 | viewEntries visibility entries = 222 | let 223 | isVisible todo = 224 | case visibility of 225 | "Completed" -> todo.completed 226 | "Active" -> not todo.completed 227 | _ -> True 228 | 229 | allCompleted = 230 | List.all .completed entries 231 | 232 | cssVisibility = 233 | if List.isEmpty entries then "hidden" else "visible" 234 | in 235 | section 236 | [ class "main" 237 | , style [ ("visibility", cssVisibility) ] 238 | ] 239 | [ input 240 | [ class "toggle-all" 241 | , type' "checkbox" 242 | , name "toggle" 243 | , checked allCompleted 244 | , onClick (CheckAll (not allCompleted)) 245 | ] 246 | [] 247 | , label 248 | [ for "toggle-all" ] 249 | [ text "Mark all as complete" ] 250 | , ul [ class "todo-list" ] <| 251 | List.map viewEntry (List.filter isVisible entries) 252 | ] 253 | 254 | 255 | 256 | -- VIEW INDIVIDUAL ENTRIES 257 | 258 | 259 | viewEntry : Entry -> Html Msg 260 | viewEntry todo = 261 | li 262 | [ classList [ ("completed", todo.completed), ("editing", todo.editing) ] ] 263 | [ div 264 | [ class "view" ] 265 | [ input 266 | [ class "toggle" 267 | , type' "checkbox" 268 | , checked todo.completed 269 | , onClick (Check todo.id (not todo.completed)) 270 | ] 271 | [] 272 | , label 273 | [ onDoubleClick (EditingEntry todo.id True) ] 274 | [ text todo.description ] 275 | , button 276 | [ class "destroy" 277 | , onClick (Delete todo.id) 278 | ] 279 | [] 280 | ] 281 | , input 282 | [ class "edit" 283 | , value todo.description 284 | , name "title" 285 | , id ("todo-" ++ toString todo.id) 286 | , onInput (UpdateEntry todo.id) 287 | , onBlur (EditingEntry todo.id False) 288 | , onEnter (EditingEntry todo.id False) 289 | ] 290 | [] 291 | ] 292 | 293 | 294 | 295 | -- VIEW CONTROLS AND FOOTER 296 | 297 | 298 | viewControls : String -> List Entry -> Html Msg 299 | viewControls visibility entries = 300 | let 301 | entriesCompleted = 302 | List.length (List.filter .completed entries) 303 | 304 | entriesLeft = 305 | List.length entries - entriesCompleted 306 | in 307 | footer 308 | [ class "footer" 309 | , hidden (List.isEmpty entries) 310 | ] 311 | [ viewControlsCount entriesLeft 312 | , viewControlsFilters visibility 313 | , viewControlsClear entriesCompleted 314 | ] 315 | 316 | 317 | viewControlsCount : Int -> Html Msg 318 | viewControlsCount entriesLeft = 319 | let 320 | item_ = 321 | if entriesLeft == 1 then " item" else " items" 322 | in 323 | span 324 | [ class "todo-count" ] 325 | [ strong [] [ text (toString entriesLeft) ] 326 | , text (item_ ++ " left") 327 | ] 328 | 329 | 330 | viewControlsFilters : String -> Html Msg 331 | viewControlsFilters visibility = 332 | ul 333 | [ class "filters" ] 334 | [ visibilitySwap "#/" "All" visibility 335 | , text " " 336 | , visibilitySwap "#/active" "Active" visibility 337 | , text " " 338 | , visibilitySwap "#/completed" "Completed" visibility 339 | ] 340 | 341 | 342 | visibilitySwap : String -> String -> String -> Html Msg 343 | visibilitySwap uri visibility actualVisibility = 344 | li 345 | [ onClick (ChangeVisibility visibility) ] 346 | [ a [ href uri, classList [("selected", visibility == actualVisibility)] ] 347 | [ text visibility ] 348 | ] 349 | 350 | 351 | viewControlsClear : Int -> Html Msg 352 | viewControlsClear entriesCompleted = 353 | button 354 | [ class "clear-completed" 355 | , hidden (entriesCompleted == 0) 356 | , onClick DeleteComplete 357 | ] 358 | [ text ("Clear completed (" ++ toString entriesCompleted ++ ")") 359 | ] 360 | 361 | 362 | infoFooter : Html msg 363 | infoFooter = 364 | footer [ class "info" ] 365 | [ p [] [ text "Double-click to edit a todo" ] 366 | , p [] 367 | [ text "Written by " 368 | , a [ href "https://github.com/evancz" ] [ text "Evan Czaplicki" ] 369 | ] 370 | , p [] 371 | [ text "Part of " 372 | , a [ href "http://todomvc.com" ] [ text "TodoMVC" ] 373 | ] 374 | ] 375 | -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "TodoMVC created with Elm and elm-html", 4 | "repository": "https://github.com/evancz/elm-todomvc.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "4.0.0 <= v < 5.0.0", 12 | "elm-lang/html": "1.0.0 <= v < 2.0.0" 13 | }, 14 | "elm-version": "0.17.0 <= v < 0.18.0" 15 | } 16 | -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Elm • TodoMVC 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Benchmarks/implementations/elm-0.17/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | .todomvc-wrapper { 8 | visibility: visible !important; 9 | } 10 | 11 | button { 12 | margin: 0; 13 | padding: 0; 14 | border: 0; 15 | background: none; 16 | font-size: 100%; 17 | vertical-align: baseline; 18 | font-family: inherit; 19 | font-weight: inherit; 20 | color: inherit; 21 | -webkit-appearance: none; 22 | appearance: none; 23 | -webkit-font-smoothing: antialiased; 24 | -moz-font-smoothing: antialiased; 25 | font-smoothing: antialiased; 26 | } 27 | 28 | body { 29 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 30 | line-height: 1.4em; 31 | background: #f5f5f5; 32 | color: #4d4d4d; 33 | min-width: 230px; 34 | max-width: 550px; 35 | margin: 0 auto; 36 | -webkit-font-smoothing: antialiased; 37 | -moz-font-smoothing: antialiased; 38 | font-smoothing: antialiased; 39 | font-weight: 300; 40 | } 41 | 42 | button, 43 | input[type="checkbox"] { 44 | outline: none; 45 | } 46 | 47 | .hidden { 48 | display: none; 49 | } 50 | 51 | .todoapp { 52 | background: #fff; 53 | margin: 130px 0 40px 0; 54 | position: relative; 55 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 56 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 57 | } 58 | 59 | .todoapp input::-webkit-input-placeholder { 60 | font-style: italic; 61 | font-weight: 300; 62 | color: #e6e6e6; 63 | } 64 | 65 | .todoapp input::-moz-placeholder { 66 | font-style: italic; 67 | font-weight: 300; 68 | color: #e6e6e6; 69 | } 70 | 71 | .todoapp input::input-placeholder { 72 | font-style: italic; 73 | font-weight: 300; 74 | color: #e6e6e6; 75 | } 76 | 77 | .todoapp h1 { 78 | position: absolute; 79 | top: -155px; 80 | width: 100%; 81 | font-size: 100px; 82 | font-weight: 100; 83 | text-align: center; 84 | color: rgba(175, 47, 47, 0.15); 85 | -webkit-text-rendering: optimizeLegibility; 86 | -moz-text-rendering: optimizeLegibility; 87 | text-rendering: optimizeLegibility; 88 | } 89 | 90 | .new-todo, 91 | .edit { 92 | position: relative; 93 | margin: 0; 94 | width: 100%; 95 | font-size: 24px; 96 | font-family: inherit; 97 | font-weight: inherit; 98 | line-height: 1.4em; 99 | border: 0; 100 | outline: none; 101 | color: inherit; 102 | padding: 6px; 103 | border: 1px solid #999; 104 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 105 | box-sizing: border-box; 106 | -webkit-font-smoothing: antialiased; 107 | -moz-font-smoothing: antialiased; 108 | font-smoothing: antialiased; 109 | } 110 | 111 | .new-todo { 112 | padding: 16px 16px 16px 60px; 113 | border: none; 114 | background: rgba(0, 0, 0, 0.003); 115 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 116 | } 117 | 118 | .main { 119 | position: relative; 120 | z-index: 2; 121 | border-top: 1px solid #e6e6e6; 122 | } 123 | 124 | label[for='toggle-all'] { 125 | display: none; 126 | } 127 | 128 | .toggle-all { 129 | position: absolute; 130 | top: -55px; 131 | left: -12px; 132 | width: 60px; 133 | height: 34px; 134 | text-align: center; 135 | border: none; /* Mobile Safari */ 136 | } 137 | 138 | .toggle-all:before { 139 | content: '❯'; 140 | font-size: 22px; 141 | color: #e6e6e6; 142 | padding: 10px 27px 10px 27px; 143 | } 144 | 145 | .toggle-all:checked:before { 146 | color: #737373; 147 | } 148 | 149 | .todo-list { 150 | margin: 0; 151 | padding: 0; 152 | list-style: none; 153 | } 154 | 155 | .todo-list li { 156 | position: relative; 157 | font-size: 24px; 158 | border-bottom: 1px solid #ededed; 159 | } 160 | 161 | .todo-list li:last-child { 162 | border-bottom: none; 163 | } 164 | 165 | .todo-list li.editing { 166 | border-bottom: none; 167 | padding: 0; 168 | } 169 | 170 | .todo-list li.editing .edit { 171 | display: block; 172 | width: 506px; 173 | padding: 13px 17px 12px 17px; 174 | margin: 0 0 0 43px; 175 | } 176 | 177 | .todo-list li.editing .view { 178 | display: none; 179 | } 180 | 181 | .todo-list li .toggle { 182 | text-align: center; 183 | width: 40px; 184 | /* auto, since non-WebKit browsers doesn't support input styling */ 185 | height: auto; 186 | position: absolute; 187 | top: 0; 188 | bottom: 0; 189 | margin: auto 0; 190 | border: none; /* Mobile Safari */ 191 | -webkit-appearance: none; 192 | appearance: none; 193 | } 194 | 195 | .todo-list li .toggle:after { 196 | content: url('data:image/svg+xml;utf8,'); 197 | } 198 | 199 | .todo-list li .toggle:checked:after { 200 | content: url('data:image/svg+xml;utf8,'); 201 | } 202 | 203 | .todo-list li label { 204 | white-space: pre-line; 205 | word-break: break-all; 206 | padding: 15px 60px 15px 15px; 207 | margin-left: 45px; 208 | display: block; 209 | line-height: 1.2; 210 | transition: color 0.4s; 211 | } 212 | 213 | .todo-list li.completed label { 214 | color: #d9d9d9; 215 | text-decoration: line-through; 216 | } 217 | 218 | .todo-list li .destroy { 219 | display: none; 220 | position: absolute; 221 | top: 0; 222 | right: 10px; 223 | bottom: 0; 224 | width: 40px; 225 | height: 40px; 226 | margin: auto 0; 227 | font-size: 30px; 228 | color: #cc9a9a; 229 | margin-bottom: 11px; 230 | transition: color 0.2s ease-out; 231 | } 232 | 233 | .todo-list li .destroy:hover { 234 | color: #af5b5e; 235 | } 236 | 237 | .todo-list li .destroy:after { 238 | content: '×'; 239 | } 240 | 241 | .todo-list li:hover .destroy { 242 | display: block; 243 | } 244 | 245 | .todo-list li .edit { 246 | display: none; 247 | } 248 | 249 | .todo-list li.editing:last-child { 250 | margin-bottom: -1px; 251 | } 252 | 253 | .footer { 254 | color: #777; 255 | padding: 10px 15px; 256 | height: 20px; 257 | text-align: center; 258 | border-top: 1px solid #e6e6e6; 259 | } 260 | 261 | .footer:before { 262 | content: ''; 263 | position: absolute; 264 | right: 0; 265 | bottom: 0; 266 | left: 0; 267 | height: 50px; 268 | overflow: hidden; 269 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 270 | 0 8px 0 -3px #f6f6f6, 271 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 272 | 0 16px 0 -6px #f6f6f6, 273 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 274 | } 275 | 276 | .todo-count { 277 | float: left; 278 | text-align: left; 279 | } 280 | 281 | .todo-count strong { 282 | font-weight: 300; 283 | } 284 | 285 | .filters { 286 | margin: 0; 287 | padding: 0; 288 | list-style: none; 289 | position: absolute; 290 | right: 0; 291 | left: 0; 292 | } 293 | 294 | .filters li { 295 | display: inline; 296 | } 297 | 298 | .filters li a { 299 | color: inherit; 300 | margin: 3px; 301 | padding: 3px 7px; 302 | text-decoration: none; 303 | border: 1px solid transparent; 304 | border-radius: 3px; 305 | } 306 | 307 | .filters li a.selected, 308 | .filters li a:hover { 309 | border-color: rgba(175, 47, 47, 0.1); 310 | } 311 | 312 | .filters li a.selected { 313 | border-color: rgba(175, 47, 47, 0.2); 314 | } 315 | 316 | .clear-completed, 317 | html .clear-completed:active { 318 | float: right; 319 | position: relative; 320 | line-height: 20px; 321 | text-decoration: none; 322 | cursor: pointer; 323 | position: relative; 324 | } 325 | 326 | .clear-completed:hover { 327 | text-decoration: underline; 328 | } 329 | 330 | .info { 331 | margin: 65px auto 0; 332 | color: #bfbfbf; 333 | font-size: 10px; 334 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 335 | text-align: center; 336 | } 337 | 338 | .info p { 339 | line-height: 1; 340 | } 341 | 342 | .info a { 343 | color: inherit; 344 | text-decoration: none; 345 | font-weight: 400; 346 | } 347 | 348 | .info a:hover { 349 | text-decoration: underline; 350 | } 351 | 352 | /* 353 | Hack to remove background from Mobile Safari. 354 | Can't use it globally since it destroys checkboxes in Firefox 355 | */ 356 | @media screen and (-webkit-min-device-pixel-ratio:0) { 357 | .toggle-all, 358 | .todo-list li .toggle { 359 | background: none; 360 | } 361 | 362 | .todo-list li .toggle { 363 | height: 40px; 364 | } 365 | 366 | .toggle-all { 367 | -webkit-transform: rotate(90deg); 368 | transform: rotate(90deg); 369 | -webkit-appearance: none; 370 | appearance: none; 371 | } 372 | } 373 | 374 | @media (max-width: 430px) { 375 | .footer { 376 | height: 50px; 377 | } 378 | 379 | .filters { 380 | bottom: 10px; 381 | } 382 | } -------------------------------------------------------------------------------- /Benchmarks/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Performance Comparison 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

Performance Comparison of Elm, React, Angular, and Ember

19 |

This is the benchmark behind Blazing Fast HTML, Round Two, so you can try it out for yourself. 20 |

21 |

Controls are on the right. Pick which implementations you want to race and press “Run”. 22 |

23 |

Between the full writeup and the repo for this runner, there is a bunch more information on the methodology and results of these benchmarks. Check them out! 24 |

25 |
26 | 27 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Benchmarks/readme.md: -------------------------------------------------------------------------------- 1 | # Comparing performance of Elm, React, Ember, and Angular 2 | 3 | This is a benchmark that tries to compare the performance of Elm, React, Ember, and Angular in a fair way. [I highly recommend reading the full analysis][blog], but the very short summary is that Elm is the fastest. 4 | 5 | [blog]: http://elm-lang.org/blog/blazing-fast-html-round-two 6 | 7 | ![Performance Comparison][graph] 8 | 9 | [graph]: graphs/chrome.png 10 | 11 | I collected graphs for a bunch of different browsers [here](/graphs), so you can see how the numbers vary across different JS virtual machines. 12 | 13 | That is just on my computer though, so I wanted everyone to be able to run this in their own browsers and see the results for themselves. You can do that [here](https://evancz.github.io/react-angular-ember-elm-performance-comparison/). 14 | 15 |
16 | 17 | ## Methodology 18 | 19 | My goal with these benchmarks was to compare renderer performance in a realistic scenario. This means rendering each frame in full, exactly like you would if a real user was interacting with the TodoMVC app. I acheived this by making two major rules: 20 | 21 | 22 | ### No Batching Events 23 | 24 | To run these benchmarks, I need to simulate user input. The naive way to do that is to generate a ton of events on a `for` loop all at once. That results in the following graph: 25 | 26 | ![Performance comparison with Batched Events][batched-graph] 27 | 28 | [batched-graph]: http://elm-lang.org/assets/blog/virtual-dom-charts/batched.png 29 | 30 | Holy smokes, it looks like Elm is 3x to 8x faster than some of the competitors! But everyone is going way faster in the first place too... What is going on here? 31 | 32 | If you run this benchmark for yourself, you will see that each implementation displays exactly four frames: no entries, one-hundred entries, one-hundred *complete* entries, and then no entries again. By generating all the events in a single `for` loop, we ensure that they all end up in a big contiguous block on JavaScript’s event queue. So when Elm or React finally gets a chance to run, they just churn through all these events. JavaScript is single threaded, so while this work is happening, we are unable to refresh the view for the user. So the browser is just not painting any frames at all. Instead of painting 100 frames, we paint just one! 33 | 34 | So this is bad for an obvious reason: it is impossible to make the event queue look like this in practice. When a user creates an event, Elm or React or Angular will *always* get woken up before another user event comes in. A user simply cannot generate a contiguous block of events on the event queue. 35 | 36 | So this is not measuring reality, but it still has some interesting information. **This graph measures the core virtual DOM implementation more directly.** This approach seems to strip out all the time spent by the browser turning DOM into pixels. Turns out, this accounts for an overwhelming majority of the time in the fair graphs. If these numbers are even vaguely decent estimates for time spent in the virtual DOM implementation, Elm is doing really well! 37 | 38 | > **Note:** In the fair approach, we generate a step for every event, [like this][fair], and measure each step individually. In the batched approach, there are only [three steps][batched1] that generate all the events at once, [like this][batched2]. 39 | > 40 | > Also, I left Angular 2 out of this graph because they are doing some trick I do not fully understand. Instead of showing 4 frames, they only show one: no entries. I suspect they are using `setTimeout` or something to delay rendering, and in this particular case, that can mean you only diff the first state against the last state. It just so happens that those are empty todo lists, so you essentially build nothing and diff nothing. I would need to know a lot more about their implementation to sort out exactly what is going on there. 41 | 42 | [fair]: https://github.com/evancz/ui-perf/blob/master/src/add-complete-delete.js#L18-L36 43 | [batched1]: https://github.com/evancz/ui-perf/blob/master/src/add-complete-delete-batched.js#L18-L31 44 | [batched2]: https://github.com/evancz/ui-perf/blob/master/src/add-complete-delete-batched.js#L40-L51 45 | 46 | 47 | ### No `requestAnimationFrame` in Elm 48 | 49 | Browsers typically repaint their content at most 60 times per second. So if you are writing JavaScript that has it changing the content 120 times per second, half of that work is wasted. Furthermore, you want your repaints to be at very even intervals so that the 60 repaints align with the 60hz of most monitors. To get the smoothest animations possible, you need to sync up with this. 50 | 51 | So `requestAnimationFrame` was introduced. It lets you say “here is a function that modifies the DOM, but I want the browser to make the changes whenever *it* thinks it is a good idea.” That means the repaints get smoothed out to 60 FPS no matter how crazy your JS happens to be. 52 | 53 | Say you get events coming in extremely quickly, and you get four events within a single frame. A naive use of `requestAnimationFrame` would just schedule them all to happen in sequence. So you would build all four virtual DOMs, diff them against each other in sequence, and show the final result to the user. We can do better though! We can just skip three of those frames entirely. Instead diff the current virtual DOM against the latest virtual DOM. The end result is exactly the same (the user sees the final result) but we skipped 75% of the work! 54 | 55 | Elm does this optimization by default. If you are using Elm, you already have this enabled and your animations are just way smoother out of the box. None of the other frameworks (Angular, Ember, or React) do this by default, and it is not their fault. If you are writing code in JavaScript (or TypeScript) this optimization is not safe at all. This optimization is all about rescheduling and skipping work, and in JavaScript, that work may have some observable effect on the rest of your program. Say you mutate your some state from the `view`. Simple. Common. There are two ways this can go wrong: 56 | 57 | 1. Using `requestAnimationFrame` means this mutation happens *later* than you expected. In the meantime, you may need to do other work that depends on that mutation having already happened. So if another event comes in *before* `requestAnimationFrame` you now have a very sneaky timing bug. 58 | 59 | 2. If you have `requestAnimationFrame` skip frames, the mutation may just *never* happen. Your application state just does not get updated correctly. This kind of bug would be truly awful to hunt down. You need a specific sequence of events to come in, one of them causing a mutation. You need them to come in so fast that they all happen within a single frame. You also need them to come in a specific order such that the event that causes mutation is one of the ones that gets dropped. This could be the definition of a [Heisenbug](https://en.wikipedia.org/wiki/Heisenbug) and I do not think I could create a more difficult bug on purpose. 60 | 61 | In both cases, the fundamental problem is that mutation is possible in your `view` code in JavaScript or TypeScript. A programmer *can* mutate something, and nothing that the React or Angular team does will change this fact. Given that, I personally think it would be crazy for them to have this kind of optimization turned on by default. In that world, using React or Angular would almost guarantee that you see these bugs in practice. 62 | 63 | To bring it back to these benchmarks, the simulated user input comes in really fast, so if I let Elm use `requestAnimationFrame` like it normally would, it would end up skipping tons of frames. That would look good, but if those events were created by a real human being, I doubt *any* of them would happen within a single frame. So in the more realistic scenario, this optimization is not going to have an impact on events that are as slow as human beings. So yes, it is really nice that Elm has this by default, and it definitely makes sense to take that into account when deciding if you want to use Elm, but it would not be fair for this benchmark. 64 | 65 | 66 | ## Building it Yourself 67 | 68 | If you want to fork this repo and try things out, the easiest way is probably to just switch to the `gh-pages` branch where the necessary assets are already checked in. Otherwise, you need to run something like this: 69 | 70 | ```bash 71 | cd src 72 | elm-make Picker.elm --output=picker.js 73 | ``` 74 | 75 | And then navigate into `implementations/*/readme.md` and follow the build instructions for the various projects. 76 | -------------------------------------------------------------------------------- /Benchmarks/src/Picker.elm: -------------------------------------------------------------------------------- 1 | port module Picker exposing (main) 2 | 3 | import Html exposing (..) 4 | import Html.App as App 5 | import Html.Attributes exposing (..) 6 | import Html.Events exposing (..) 7 | 8 | 9 | 10 | main = 11 | App.programWithFlags 12 | { init = init 13 | , view = view 14 | , update = update 15 | , subscriptions = subscriptions 16 | } 17 | 18 | 19 | 20 | -- MODEL 21 | 22 | 23 | type alias Model = 24 | { running : Bool 25 | , entries : List Entry 26 | } 27 | 28 | 29 | type alias Entry = 30 | { selected : Bool 31 | , id : Int 32 | , impl : Impl 33 | } 34 | 35 | 36 | type alias Impl = 37 | { name : String 38 | , version : String 39 | , url : String 40 | , optimized : Bool 41 | } 42 | 43 | 44 | init : List Impl -> ( Model, Cmd msg ) 45 | init impls = 46 | { running = False 47 | , entries = List.indexedMap (Entry False) impls 48 | } 49 | ! [] 50 | 51 | 52 | 53 | -- UPDATE 54 | 55 | 56 | type Msg 57 | = Toggle Int 58 | | Start 59 | | End 60 | 61 | 62 | update : Msg -> Model -> ( Model, Cmd msg ) 63 | update msg model = 64 | case msg of 65 | Toggle id -> 66 | { model | entries = toggle id model.entries } 67 | ! [] 68 | 69 | Start -> 70 | { model | running = True } 71 | ! [ startSelected model.entries ] 72 | 73 | End -> 74 | { model | running = False } 75 | ! [] 76 | 77 | 78 | toggle : Int -> List Entry -> List Entry 79 | toggle id entries = 80 | case entries of 81 | [] -> 82 | [] 83 | 84 | entry :: rest -> 85 | if entry.id == id then 86 | { entry | selected = not entry.selected } :: rest 87 | 88 | else 89 | entry :: toggle id rest 90 | 91 | 92 | port start : List Impl -> Cmd msg 93 | 94 | 95 | startSelected : List Entry -> Cmd msg 96 | startSelected entries = 97 | start (List.map .impl (List.filter .selected entries)) 98 | 99 | 100 | 101 | -- SUBSCRIPTIONS 102 | 103 | 104 | port end : (() -> msg) -> Sub msg 105 | 106 | 107 | subscriptions : Model -> Sub Msg 108 | subscriptions model = 109 | end (always End) 110 | 111 | 112 | 113 | -- VIEW 114 | 115 | 116 | view : Model -> Html Msg 117 | view { running, entries } = 118 | div [] 119 | [ ul 120 | (if running then [ style [("color", "#aaa")] ] else []) 121 | (List.map (viewEntry running) entries) 122 | , button 123 | [ style [("width","100%")] 124 | , disabled running 125 | , onClick Start 126 | ] 127 | [ text "Run" ] 128 | ] 129 | 130 | 131 | viewEntry : Bool -> Entry -> Html Msg 132 | viewEntry running { id, selected, impl } = 133 | li 134 | (if running then [ pointer ] else [ pointer, onClick (Toggle id) ]) 135 | [ input [ type' "checkbox", checked selected, disabled running ] [] 136 | , text (" " ++ impl.name ++ " " ++ impl.version) 137 | , span 138 | [ style [("color","#aaa")] 139 | ] 140 | [ text (if impl.optimized then " (optimized)" else "") 141 | ] 142 | ] 143 | 144 | 145 | pointer : Attribute msg 146 | pointer = 147 | style [ ("cursor", "pointer") ] -------------------------------------------------------------------------------- /Benchmarks/src/add-complete-delete-batched.js: -------------------------------------------------------------------------------- 1 | 2 | var suite = function() { 3 | 4 | 5 | // FACTS 6 | 7 | function getFacts(doc) 8 | { 9 | var input = doc.getElementsByClassName('new-todo')[0]; 10 | return input ? { doc: doc, input: input } : undefined; 11 | } 12 | 13 | 14 | // STEPS 15 | 16 | function addCompleteDeleteSteps(numItems) 17 | { 18 | return [ 19 | { 20 | name: 'Adding ' + numItems + ' Items', 21 | work: add(numItems) 22 | }, 23 | { 24 | name: 'Completing All Items', 25 | work: clickAll('.toggle') 26 | }, 27 | { 28 | name: 'Deleting All Items', 29 | work: clickAll('.destroy') 30 | } 31 | ]; 32 | } 33 | 34 | function add(numItems) 35 | { 36 | return function(facts) 37 | { 38 | var node = facts.input; 39 | 40 | for (var i = 0; i < numItems; i++) 41 | { 42 | var inputEvent = document.createEvent('Event'); 43 | inputEvent.initEvent('input', true, true); 44 | node.value = 'Do task ' + i; 45 | node.dispatchEvent(inputEvent); 46 | 47 | var keydownEvent = document.createEvent('Event'); 48 | keydownEvent.initEvent('keydown', true, true); 49 | keydownEvent.keyCode = 13; 50 | node.dispatchEvent(keydownEvent); 51 | } 52 | }; 53 | } 54 | 55 | function clickAll(selector) 56 | { 57 | return function(facts) 58 | { 59 | var checkboxes = facts.doc.querySelectorAll(selector); 60 | for (var i = 0; i < checkboxes.length; i++) 61 | { 62 | checkboxes[i].click(); 63 | } 64 | }; 65 | } 66 | 67 | 68 | // SUITE 69 | 70 | return { 71 | getFacts: getFacts, 72 | steps: addCompleteDeleteSteps(100) 73 | }; 74 | 75 | 76 | }(); -------------------------------------------------------------------------------- /Benchmarks/src/add-complete-delete.js: -------------------------------------------------------------------------------- 1 | 2 | var suite = function() { 3 | 4 | 5 | // FACTS 6 | 7 | function getFacts(doc) 8 | { 9 | var input = doc.getElementsByClassName('new-todo')[0]; 10 | return input ? { doc: doc, input: input } : undefined; 11 | } 12 | 13 | 14 | // STEPS 15 | 16 | function addCompleteDeleteSteps(numItems) 17 | { 18 | var steps = []; 19 | 20 | for (var i = 0; i < numItems; i++) 21 | { 22 | steps.push({ name: 'Inputing ' + i, work: inputTodo(i) }); 23 | steps.push({ name: 'Entering ' + i, work: pressEnter }); 24 | } 25 | 26 | for (var i = 0; i < numItems; i++) 27 | { 28 | steps.push({ name: 'Checking ' + i, work: click('toggle', i) }); 29 | } 30 | 31 | for (var i = 0; i < numItems; i++) 32 | { 33 | steps.push({ name: 'Removing ' + i, work: click('destroy', 0) }); 34 | } 35 | 36 | return steps; 37 | } 38 | 39 | function inputTodo(number) 40 | { 41 | return function(facts) 42 | { 43 | var node = facts.input; 44 | 45 | var inputEvent = document.createEvent('Event'); 46 | inputEvent.initEvent('input', true, true); 47 | node.value = 'Do task ' + number; 48 | node.dispatchEvent(inputEvent); 49 | }; 50 | } 51 | 52 | function pressEnter(facts) 53 | { 54 | var event = document.createEvent('Event'); 55 | event.initEvent('keydown', true, true); 56 | event.key = 'Enter'; 57 | event.keyCode = 13; 58 | event.which = 13; 59 | facts.input.dispatchEvent(event); 60 | } 61 | 62 | function click(className, index) 63 | { 64 | return function(facts) 65 | { 66 | facts.doc.getElementsByClassName(className)[index].click(); 67 | }; 68 | } 69 | 70 | 71 | // SUITE 72 | 73 | return { 74 | getFacts: getFacts, 75 | steps: addCompleteDeleteSteps(100) 76 | }; 77 | 78 | 79 | }(); -------------------------------------------------------------------------------- /Benchmarks/src/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "4.0.3 <= v < 5.0.0", 12 | "elm-lang/html": "1.1.0 <= v < 2.0.0" 13 | }, 14 | "elm-version": "0.17.1 <= v < 0.18.0" 15 | } 16 | -------------------------------------------------------------------------------- /Benchmarks/src/runner.js: -------------------------------------------------------------------------------- 1 | 2 | // SETUP 3 | 4 | if (!window.performance || !window.performance.now) 5 | { 6 | throw new Error('These tests use performance.now() which is not supported by your browser.'); 7 | } 8 | 9 | 10 | 11 | // RUNNER 12 | 13 | 14 | function runBenchmarks(impls, suite, callback) 15 | { 16 | var frame = document.getElementById('benchmark-frame'); 17 | var results = document.getElementById('benchmark-results'); 18 | 19 | frame.style.display = 'block'; 20 | results.style.visibility = 'hidden'; 21 | while (results.lastChild) { 22 | results.removeChild(results.lastChild); 23 | } 24 | 25 | runImplementations(impls, suite, 0, function() { 26 | var canvas = document.createElement('canvas'); 27 | results.appendChild(canvas); 28 | updateChart(canvas, impls); 29 | frame.style.display = 'none'; 30 | results.style.visibility = 'visible'; 31 | callback(); 32 | }); 33 | } 34 | 35 | 36 | 37 | // RUN IMPLEMENTATIONS 38 | 39 | 40 | function runImplementations(impls, suite, index, done) 41 | { 42 | var impl = impls[index]; 43 | var frame = document.getElementById('benchmark-frame'); 44 | frame.onload = function() 45 | { 46 | withFacts(0, frame.contentDocument, suite.getFacts, function(facts) 47 | { 48 | runSteps(facts, suite.steps, index, 0, [], function(results) 49 | { 50 | impl.results = results; 51 | impl.time = getTotalTime(results); 52 | console.log( 53 | impl.name + ' ' + impl.version 54 | + (impl.optimized ? ' (optimized)' : '') 55 | + ' = ' + trunc(impl.time) + ' ms' 56 | ); 57 | 58 | ++index; 59 | 60 | return (index < impls.length) 61 | ? runImplementations(impls, suite, index, done) 62 | : done(); 63 | }); 64 | }); 65 | } 66 | 67 | frame.src = impl.url; 68 | } 69 | 70 | 71 | function getTotalTime(results) 72 | { 73 | var total = 0; 74 | for (var i = 0; i < results.length; i++) 75 | { 76 | total += results[i].sync; 77 | total += results[i].async; 78 | } 79 | return total; 80 | } 81 | 82 | 83 | function withFacts(tries, doc, getFacts, callback) 84 | { 85 | if (tries > 5) 86 | { 87 | throw new Error('Could not get facts for this implementation.'); 88 | } 89 | 90 | setTimeout(function() { 91 | var facts = getFacts(doc); 92 | typeof facts === 'undefined' 93 | ? withFacts(tries + 1, doc, getFacts, callback) 94 | : callback(facts); 95 | }, 16 * Math.pow(2, tries)); 96 | } 97 | 98 | 99 | 100 | /* RUN STEPS ***/ 101 | 102 | 103 | function runSteps(facts, steps, implIndex, index, results, done) 104 | { 105 | timedStep(steps[index].work, facts, function(syncTime, asyncTime) 106 | { 107 | results.push({ 108 | name: steps[index].name, 109 | sync: syncTime, 110 | async: asyncTime 111 | }); 112 | 113 | ++index; 114 | 115 | if (index < steps.length) 116 | { 117 | return runSteps(facts, steps, implIndex, index, results, done) 118 | } 119 | 120 | return done(results); 121 | }); 122 | } 123 | 124 | 125 | function trunc(time) 126 | { 127 | return Math.round(time); 128 | } 129 | 130 | 131 | function timedStep(work, facts, callback) 132 | { 133 | // time all synchronous work 134 | var start = performance.now(); 135 | work(facts); 136 | var end = performance.now(); 137 | var syncTime = end - start; 138 | 139 | // time ONE round of asynchronous work 140 | var asyncStart = performance.now(); 141 | setTimeout(function() { 142 | var asyncEnd = performance.now(); 143 | callback(syncTime, asyncEnd - asyncStart); 144 | }, 0); 145 | 146 | // if anyone does more than one round, we do not capture it! 147 | } 148 | 149 | 150 | 151 | /* SETUP WORK LIST *********/ 152 | 153 | 154 | function setupWorklist(suite) 155 | { 156 | var impls = suite.impls; 157 | var steps = suite.steps; 158 | 159 | var workList = document.getElementById('work-list'); 160 | 161 | while (workList.lastChild) 162 | { 163 | workList.removeChild(workList.lastChild); 164 | } 165 | 166 | for (var i = 0; i < impls.length; i++) 167 | { 168 | var impl = document.createElement('li'); 169 | var title = document.createTextNode(impls[i].name); 170 | impl.appendChild(title); 171 | workList.appendChild(impl); 172 | } 173 | 174 | var sidebar = document.getElementById('sidebar'); 175 | sidebar.appendChild(workList); 176 | } 177 | 178 | 179 | 180 | /* DRAW CHARTS *************/ 181 | 182 | 183 | function updateChart(canvas, impls) 184 | { 185 | new Chart(canvas, { 186 | type: 'bar', 187 | data: { 188 | labels: impls.map(toLabel), 189 | datasets: [{ 190 | label: 'ms', 191 | data: impls.map(function(impl) { return trunc(impl.time); }), 192 | backgroundColor: impls.map(toColor) 193 | }] 194 | }, 195 | options: { 196 | defaultFontFamily: 'Source Sans Pro', 197 | title: { 198 | display: true, 199 | text: 'Benchmark Results', 200 | fontSize: 20 201 | }, 202 | legend: { 203 | display: false 204 | }, 205 | scales: { 206 | yAxes: [{ 207 | scaleLabel: { 208 | display: true, 209 | labelString: 'Milliseconds (lower is better)', 210 | fontSize: 16 211 | }, 212 | ticks: { 213 | beginAtZero: true 214 | } 215 | }] 216 | } 217 | } 218 | }); 219 | } 220 | 221 | function toLabel(impl) 222 | { 223 | return impl.name + ' ' + impl.version; 224 | } 225 | 226 | function toColor(impl) 227 | { 228 | return impl.optimized 229 | ? 'rgba(200, 12, 192, 0.5)' 230 | : 'rgba(75, 192, 192, 0.5)'; 231 | } -------------------------------------------------------------------------------- /Benchmarks/src/theme.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | background-color: rgb(253, 253, 253); 6 | font-family: 'Trebuchet MS', 'Lucida Grande', 'Bitstream Vera Sans', 'Helvetica Neue', sans-serif; 7 | } 8 | 9 | #benchmark-frame { 10 | width: 800px; 11 | height: 600px; 12 | border: 1px solid black; 13 | margin: 20px; 14 | display: none; 15 | } 16 | 17 | #sidebar { 18 | top: 20px; 19 | right: 20px; 20 | position: absolute; 21 | padding: 10px; 22 | background-color: rgb(240, 240, 240); 23 | border-radius: 4px; 24 | } 25 | 26 | #sidebar ul { 27 | list-style-type: none; 28 | padding: 2px; 29 | margin: 2px; 30 | } 31 | 32 | #benchmark-results { 33 | width: 800px; 34 | margin: 20px; 35 | border-radius: 4px; 36 | display: block; 37 | } -------------------------------------------------------------------------------- /HelloWorld/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/user/project.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-lang/core": "5.1.1 <= v < 6.0.0", 12 | "elm-lang/html": "2.0.0 <= v < 3.0.0" 13 | }, 14 | "elm-version": "0.18.0 <= v < 0.19.0" 15 | } 16 | -------------------------------------------------------------------------------- /HelloWorld/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Elm "Hello World" Prepack Test 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /HelloWorld/main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import Html exposing (Html, text) 4 | 5 | main : Html a 6 | main = 7 | text "Hello, World!" 8 | -------------------------------------------------------------------------------- /HelloWorld/main.uglify.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkaterDad/elm-prepack-experiments/af09ed73d61f4932b8cfbc562a3285a617a482fc/HelloWorld/main.uglify.js.gz -------------------------------------------------------------------------------- /HelloWorld/prepacked.uglify.js: -------------------------------------------------------------------------------- 1 | (function(){"use strict";function e(e,n){return function(t){return e(n,t)}}function n(e,n){return function(t){return function(r){return e(n,t,r)}}}function t(e,n){return function(t){return function(r){return function(a){return e(n,t,r,a)}}}}function r(e,n){return function(t){return function(r){return function(a){return function(o){return e(n,t,r,a,o)}}}}}function a(e,n){return function(t){return function(r){return function(a){return function(o){return function(i){return e(n,t,r,a,o,i)}}}}}}function o(e,n,t){return function(r){return function(a,o,i){var u=e(r,o);void 0===i?Ht(t,a,o,u):Zt(D(n,i,t),a,o,u)}}}function i(e,n){for(;e.lastChild;)e.removeChild(e.lastChild);return Wa.initialize(_r($a.init,n,e),$a.update,$a.subscriptions,Xt(e,$a.view))}function u(n){return e.call(this,Me,n)}function c(n){return e.call(this,Qe,n)}function l(n){return e.call(this,an,n)}function f(n){return e.call(this,d,n)}function s(n){return e.call(this,Yt,n)}function d(e,n){return Ea.nativeBinding(function(t){e.main(n),t(Ea.succeed(Xr.Tuple0))})}function h(e){return{ctor:"_Task_succeed",value:e}}function g(e){return{ctor:"_Task_fail",value:e}}function v(e){return{ctor:"_Task_nativeBinding",callback:e,cancel:null}}function p(e,n){return{ctor:"_Task_andThen",callback:e,task:n}}function b(e,n){return{ctor:"_Task_onError",callback:e,task:n}}function m(e){return{ctor:"_Task_receive",callback:e}}function y(e){return v(function(n){n(h(_(e)))})}function _(e){var n={ctor:"_Process",id:Xr.guid(),root:e,stack:null,mailbox:[]};return Fe(n),n}function w(e,n){for(var t,r=[],a=k(e,n,0,r);a&&(t=r.pop());)a=k(t.x,t.y,0,r);return a}function k(e,n,t,r){if(t>100)return r.push({x:e,y:n}),!0;if(e===n)return!0;if("object"!=typeof e){if("function"==typeof e)throw new kr.Error('Trying to use `(==)` on functions. There is no way to know if functions are "the same" in the Elm sense. Read more about this at http://package.elm-lang.org/packages/elm-lang/core/latest/Basics#== which describes why it is this way and what the better version will look like.');return!1}if(null===e||null===n)return!1;if(e instanceof kr.Date)return e.getTime()===n.getTime();if(!("ctor"in e)){for(var a in e)if(!k(e[a],n[a],t+1,r))return!1;return!0}if("RBNode_elm_builtin"!==e.ctor&&"RBEmpty_elm_builtin"!==e.ctor||(e=T(e),n=T(n)),"Set_elm_builtin"===e.ctor&&(e=_elm_lang$core$Set$toList(e),n=_elm_lang$core$Set$toList(n)),"::"===e.ctor){for(var o=e,i=n;"::"===o.ctor&&"::"===i.ctor;){if(!k(o._0,i._0,t+1,r))return!1;o=o._1,i=i._1}return o.ctor===i.ctor}if("_Array"===e.ctor){var u=Yr.toJSArray(e),c=Yr.toJSArray(n);if(u.length!==c.length)return!1;for(var l=0;l0){var a={ctor:"_Array",height:0,table:n.splice(0,r)};C(a,t)}for(var o=0;o0&&C(t[o],t);var i=t[t.length-1];return i.height>0&&1===i.table.length?i.table[0]:i}function C(e,n){var t=e.height;if(n.length===t){var r={ctor:"_Array",height:t+1,table:[],lengths:[]};n.push(r)}n[t].table.push(e);var a=S(e);n[t].lengths.length>0&&(a+=n[t].lengths[n[t].lengths.length-1]),n[t].lengths.push(a),32===n[t].table.length&&(C(n[t],n),n[t]={ctor:"_Array",height:t+1,table:[],lengths:[]})}function S(e){return 0===e.height?e.table.length:e.lengths[e.lengths.length-1]}function O(e){return R(Ir.Nil,e)}function R(e,n){for(var t=n.table.length-1;t>=0;t--)e=0===n.height?Ir.Cons(n.table[t],e):R(e,n.table[t]);return e}function L(e,n){return{ctor:"::",_0:e,_1:n}}function M(e){for(var n=[];"[]"!==e.ctor;)n.push(e._0),e=e._1;return n}function B(e){for(var n=Cr,t=e.length;t--;)n=L(e[t],n);return n}function P(e,n,t){for(var r=M(t),a=n,o=r.length;o--;)a=D(e,r[o],a);return a}function D(e,n,t){return 2===e.arity?e.func(n,t):e(n)(t)}function I(e,n,t){for(var r=[];"[]"!==n.ctor&&"[]"!==t.ctor;)r.push(D(e,n._0,t._0)),n=n._1,t=t._1;return B(r)}function j(e,n,t,r){for(var a=[];"[]"!==n.ctor&&"[]"!==t.ctor&&"[]"!==r.ctor;)a.push(E(e,n._0,t._0,r._0)),n=n._1,t=t._1,r=r._1;return B(a)}function z(e,n,t,r,a){for(var o=[];"[]"!==n.ctor&&"[]"!==t.ctor&&"[]"!==r.ctor&&"[]"!==a.ctor;)o.push(F(e,n._0,t._0,r._0,a._0)),n=n._1,t=t._1,r=r._1,a=a._1;return B(o)}function F(e,n,t,r,a){return 4===e.arity?e.func(n,t,r,a):e(n)(t)(r)(a)}function U(e,n,t,r,a,o){for(var i=[];"[]"!==n.ctor&&"[]"!==t.ctor&&"[]"!==r.ctor&&"[]"!==a.ctor&&"[]"!==o.ctor;)i.push(J(e,n._0,t._0,r._0,a._0,o._0)),n=n._1,t=t._1,r=r._1,a=a._1,o=o._1;return B(i)}function J(e,n,t,r,a,o){return 5===e.arity?e.func(n,t,r,a,o):e(n)(t)(r)(a)(o)}function Q(e,n){return B(M(n).sort(function(n,t){return Xr.cmp(e(n),e(t))}))}function W(e,n){return B(M(n).sort(function(n,t){var r=e(n)(t).ctor;return"EQ"===r?0:"LT"===r?-1:1}))}function $(e,n){return e<=0?xr:K(n,kr.Math.floor(kr.Math.log(e)/kr.Math.log(32)),0,e)}function K(e,n,t,r){if(0===n){for(var a=new kr.Array((r-t)%33),o=0;o0?u[o-1]:0);return{ctor:"_Array",height:n,table:a,lengths:u}}function V(e,n){if(0===e.table.length)return n;if(0===n.table.length)return e;var t=Y(e,n);if(t[0].table.length+t[1].table.length<=32){if(0===t[0].table.length)return t[1];if(0===t[1].table.length)return t[0];if(t[0].table=t[0].table.concat(t[1].table),t[0].height>0){for(var r=S(t[0]),a=0;a0){var o=ne(e,n);o>2&&(t=te(t[0],t[1],o))}return ue(t[0],t[1])}function Y(e,n){if(0===e.height&&0===n.height)return[e,n];if(1!==e.height||1!==n.height)if(e.height===n.height)e=q(e),n=q(n),X(e,(t=Y(G(e),H(n)))[1]),Z(n,t[0]);else if(e.height>n.height)X(e=q(e),(t=Y(G(e),n))[0]),n=ee(t[1],t[1].height+1);else{var t=Y(e,H(n=q(n))),r=0===t[0].table.length?0:1,a=0===r?1:0;Z(n,t[r]),e=ee(t[a],t[a].height+1)}if(0===e.table.length||0===n.table.length)return[e,n];var o=ne(e,n);return o<=2?[e,n]:te(e,n,o)}function q(e){var n={ctor:"_Array",height:e.height,table:e.table.slice()};return e.height>0&&(n.lengths=e.lengths.slice()),n}function G(e){return e.table[e.table.length-1]}function H(e){return e.table[0]}function X(e,n){var t=e.table.length-1;e.table[t]=n,e.lengths[t]=S(n),e.lengths[t]+=t>0?e.lengths[t-1]:0}function Z(e,n){if(n.table.length>0){e.table[0]=n,e.lengths[0]=S(n);for(var t=S(e.table[0]),r=1;r0?1:0)0)for(var s=u.lengths.length,d=s;d0?u.lengths[d-1]:0;c+=f,l.table.length<=f&&(o++,c=0),32===u.table.length&&(ie(r,a,i,u),u=re(e.height-1,0),i++)}for(u.table.length>0&&(ie(r,a,i,u),i++);o0&&(t.lengths=new kr.Array(n)),t}function ae(e,n,t){return t0?n.lengths[t-1]:0),n.table[t]);if(t===n.table.length-1)return r;var a={ctor:"_Array",height:n.height,table:n.table.slice(t,n.table.length+1),lengths:new kr.Array(n.table.length-t)};a.table[0]=r;for(var o=0,i=0;i>5*n.height;n.lengths[t]<=e;)t++;return t}function ge(e,n){if(e===S(n))return n;if(0===n.height)return(a={ctor:"_Array",height:0}).table=n.table.slice(0,e),a;var t=he(e,n),r=ge(e-(t>0?n.lengths[t-1]:0),n.table[t]);if(0===t)return r;var a={ctor:"_Array",height:n.height,table:n.table.slice(0,t),lengths:n.lengths.slice(0,t)};return r.table.length>0&&(a.table[t]=r,a.lengths[t]=S(r)+(t>0?a.lengths[t-1]:0)),a}function ve(e,n){if(e<0||e>=S(n))throw new kr.Error("Index "+e+" is out of range. Check the length of your array first or use getMaybe or getWithDefault.");return pe(e,n)}function pe(e,n){for(var t=n.height;t>0;t--){for(var r=e>>5*t;n.lengths[r]<=e;)r++;r>0&&(e-=n.lengths[r-1]),n=n.table[r]}return n.table[e]}function be(e,n,t){return e<0||S(t)<=e?t:me(e,n,t)}function me(e,n,t){if(0===(t=q(t)).height)t.table[e]=n;else{var r=he(e,t);r>0&&(e-=t.lengths[r-1]),t.table[r]=me(e,n,t.table[r])}return t}function ye(e,n){var t={ctor:"_Array",height:n.height,table:new kr.Array(n.table.length)};n.height>0&&(t.lengths=n.lengths);for(var r=0;r0&&(r.lengths=n.lengths);for(var a=0;a0?i[u-1]:0);return{ctor:"_Array",height:n,table:o,lengths:i}}function Ce(e,n){if("object"!=typeof e)return e===n?0:e=1){if(0!==(a=Ce(e._0,n._0)))return a;if(o>=2){if(0!==(a=Ce(e._1,n._1)))return a;if(o>=3){if(0!==(a=Ce(e._2,n._2)))return a;if(o>=4){if(0!==(a=Ce(e._3,n._3)))return a;if(o>=5){if(0!==(a=Ce(e._4,n._4)))return a;if(o>=6){if(0!==(a=Ce(e._5,n._5)))return a;if(o>=7)throw new kr.Error("Comparison error: cannot compare tuples with more than 6 elements.")}}}}}}return 0}throw new kr.Error("Comparison error: comparison is only defined on ints, floats, times, chars, strings, lists of comparable values, and tuples of comparable values.")}function Se(e,n){return{ctor:"_Tuple2",_0:e,_1:n}}function Oe(e){return new kr.String(e)}function Re(e,n){var t={};for(var r in e)t[r]=e[r];for(var r in n)t[r]=n[r];return t}function Le(e){return Gr++}function Me(e,n){if("string"==typeof e)return e+n;if("[]"===e.ctor)return n;var t=Be(e._0,Hr),r=t;for(e=e._1;"[]"!==e.ctor;)r._1=Be(e._0,Hr),e=e._1,r=r._1;return r._1=n,t}function Be(e,n){return{ctor:"::",_0:e,_1:n}}function Pe(e,n){return function(t){throw new kr.Error("Ran into a `Debug.crash` in module `"+e+"` "+De(n)+"\nThe message provided by the code author is:\n\n "+t)}}function De(e){return e.start.line==e.end.line?"on line "+e.start.line:"between lines "+e.start.line+" and "+e.end.line}function Ie(e,n,t){return function(r){throw new kr.Error("Ran into a `Debug.crash` in module `"+e+"`\n\nThis was caused by the `case` expression "+De(n)+".\nOne of the branches ended with a crash and the following value got through:\n\n "+je(t)+"\n\nThe message provided by the code author is:\n\n "+r)}}function je(e){var n=typeof e;if("function"===n)return"";if("boolean"===n)return e?"True":"False";if("number"===n)return e+"";if(e instanceof kr.String)return"'"+ze(e,!0)+"'";if("string"===n)return'"'+ze(e,!1)+'"';if(null===e)return"null";if("object"===n&&"ctor"in e){var t=e.ctor.substring(0,5);if("_Tupl"===t){u=[];for(var r in e)"ctor"!==r&&u.push(je(e[r]));return"("+u.join(",")+")"}if("_Task"===t)return"";if("_Array"===e.ctor)return"Array.fromList "+je(O(e));if(""===e.ctor)return"";if("_Process"===e.ctor)return"";if("::"===e.ctor){u="["+je(e._0);for(e=e._1;"::"===e.ctor;)u+=","+je(e._0),e=e._1;return u+"]"}if("[]"===e.ctor)return"[]";if("Set_elm_builtin"===e.ctor)return"Set.fromList "+je(_elm_lang$core$Set$toList(e));if("RBNode_elm_builtin"===e.ctor||"RBEmpty_elm_builtin"===e.ctor)return"Dict.fromList "+je(T(e));u="";for(var a in e)if("ctor"!==a){var o=je(e[a]),i=o[0];u+=" "+("{"===i||"("===i||"<"===i||'"'===i||o.indexOf(" ")<0?o:"("+o+")")}return e.ctor+u}if("object"===n){if(e instanceof kr.Date)return"<"+e.toString()+">";if(e.elm_web_socket)return"";var u=[];for(var r in e)u.push(r+" = "+je(e[r]));return 0===u.length?"{}":"{ "+u.join(", ")+" }"}return""}function ze(e,n){var t=e.replace(/\\/g,"\\\\").replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(/\r/g,"\\r").replace(/\v/g,"\\v").replace(/\0/g,"\\0");return n?t.replace(/\'/g,"\\'"):t.replace(/\"/g,'\\"')}function Fe(e){wa.push(e),ka||(kr.setTimeout(gt,0),ka=!0)}function Ue(e){var n=Ea.receive(function(n){return Je(n,e)});return D(Er,Ue,n)}function Je(e,n){return Ea.nativeBinding(function(t){var r=D(c,e,n);n=r._0,Ka(n);var a=r._1,o=We(n);$e(ra,a,o),t(Ea.succeed(n))})}function Qe(){return ea}function We(){return na}function $e(e,n,t){var r={};Ke(!0,n,r,null),Ke(!1,t,r,null);for(var a in e){var o=a in r?r[a]:{cmds:Ir.Nil,subs:Ir.Nil};Ea.rawSend(e[a],{ctor:"fx",_0:o})}}function Ke(e,n,t,r){switch(n.type){case"leaf":var a=n.home,o=Ve(e,a,r,n.value);return void(t[a]=Ye(e,o,t[a]));case"node":for(var i=n.branches;"[]"!==i.ctor;)Ke(e,i._0,t,r),i=i._1;return;case"map":return void Ke(e,n.tree,t,{tagger:n.tagger,rest:r})}}function Ve(e,n,t,r){function a(e){for(var n=t;n;)e=n.tagger(e),n=n.rest;return e}return D(e?ta[n].cmdMap:ta[n].subMap,a,r)}function Ye(e,n,t){return t=t||{cmds:Ir.Nil,subs:Ir.Nil},e?(t.cmds=Ir.Cons(n,t.cmds),t):(t.subs=Ir.Cons(n,t.subs),t)}function qe(e){var n=ea._0;Ka=Ge(ht,n);var t=ea._1,r=We(n);$e(ra,t,r),e(Ea.succeed(n))}function Ge(e,n){var t={tagger:e,parent:kr.undefined},r=He(),a=Xe(r,t);return document.body.appendChild(a),Vn(a,He,r,t)}function He(){return aa}function Xe(e,n){switch(e.type){case"thunk":return e.node||(e.node=e.thunk()),Xe(e.node,n);case"tagger":for(var t=e.node,r=e.tagger;"tagger"===t.type;)"object"!=typeof r?r=[r,t.tagger]:r.push(t.tagger),t=t.node;var a={tagger:r,parent:n};return(u=Xe(t,a)).elm_event_node_ref=a,u;case"text":return ya.createTextNode(e.text);case"node":Ze(u=e.namespace?ya.createElementNS(e.namespace,e.tag):ya.createElement(e.tag),n,e.facts);for(var o=e.children,i=0;i=n.length?fn("a longer array. Need index "+u+" but there are only "+n.length+" entries",n):"ok"===(v=cn(e.decoder,n[u])).tag?v:sn(u,v):fn("an array",n);case"key-value":if("object"!=typeof n||null===n||n instanceof kr.Array)return fn("an object",n);var c=Ir.Nil;for(var l in n){if("ok"!==(v=cn(e.decoder,n[l])).tag)return hn(l,v);var f=Xr.Tuple2(l,v.value);c=Ir.Cons(f,c)}return ln(c);case"map-many":for(var s=e.func,d=e.decoders,r=0;r",tag:"null",value:e}}function _n(e){return{ctor:"",tag:e}}function wn(e,n){return{ctor:"",tag:e,decoder:n}}function kn(e,n){return{ctor:"",tag:"field",field:e,decoder:n}}function Tn(e,n){return{ctor:"",tag:"index",index:e,decoder:n}}function En(e,n){return Nn(e,[n])}function Nn(e,n){return{ctor:"",tag:"map-many",func:e,decoders:n}}function An(e,n,t){return Nn(e,[n,t])}function xn(e,n,t,r){return Nn(e,[n,t,r])}function Cn(e,n,t,r,a){return Nn(e,[n,t,r,a])}function Sn(e,n,t,r,a,o){return Nn(e,[n,t,r,a,o])}function On(e){return function(n){return function(t){return function(r){return function(a){return function(o){return function(i){return Rn(e,n,t,r,a,o,i)}}}}}}}function Rn(e,n,t,r,a,o,i){return Nn(e,[n,t,r,a,o,i])}function Ln(e){return function(n){return function(t){return function(r){return function(a){return function(o){return function(i){return function(u){return Mn(e,n,t,r,a,o,i,u)}}}}}}}}function Mn(e,n,t,r,a,o,i,u){return Nn(e,[n,t,r,a,o,i,u])}function Bn(e){return function(n){return function(t){return function(r){return function(a){return function(o){return function(i){return function(u){return function(c){return Pn(e,n,t,r,a,o,i,u,c)}}}}}}}}}function Pn(e,n,t,r,a,o,i,u,c){return Nn(e,[n,t,r,a,o,i,u,c])}function Dn(e){return{ctor:"",tag:"key-value",decoder:e}}function In(e,n){return{ctor:"",tag:"andThen",decoder:n,callback:e}}function jn(e){return{ctor:"",tag:"fail",msg:e}}function zn(e){return{ctor:"",tag:"succeed",msg:e}}function Fn(e){return{ctor:"",tag:"oneOf",decoders:e}}function Un(e){return e}function Jn(e){for(var n={};"[]"!==e.ctor;){var t=e._0;n[t._0]=t._1,e=e._1}return n}function Qn(e,n){if(e===n)return!0;if(e.tag!==n.tag)return!1;switch(e.tag){case"succeed":case"fail":return e.msg===n.msg;case"bool":case"int":case"float":case"string":case"value":return!0;case"null":return e.value===n.value;case"list":case"array":case"maybe":case"key-value":return Qn(e.decoder,n.decoder);case"field":return e.field===n.field&&Qn(e.decoder,n.decoder);case"index":return e.index===n.index&&Qn(e.decoder,n.decoder);case"map-many":return e.func===n.func&&Wn(e.decoders,n.decoders);case"andThen":return e.callback===n.callback&&Qn(e.decoder,n.decoder);case"oneOf":return Wn(e.decoders,n.decoders)}}function Wn(e,n){var t=e.length;if(t!==n.length)return!1;for(var r=0;r.");case"PENDING_REQUEST":Yn(a),i="EXTRA_REQUEST";var t=n(o),c=qn(u,t);return e=ot(e,u,c,r),void(u=t);case"EXTRA_REQUEST":return void(i="NO_REQUEST")}}var o,i="NO_REQUEST",u=t;return function(e){"NO_REQUEST"===i&&Yn(a),i="PENDING_REQUEST",o=e}}function Yn(e){kr.setTimeout(e,1e3/60)}function qn(e,n){var t=[];return Gn(e,n,t,0),t}function Gn(e,n,t,r){if(e!==n){var a=e.type,o=n.type;if(a!==o)return void t.push(Hn("p-redraw",r,n));switch(o){case"thunk":for(var i=e.args,u=n.args,c=i.length,l=e.func===n.func&&c===u.length;l&&c--;)l=i[c]===u[c];if(l)return void(n.node=e.node);n.node=n.thunk();var f=[];return Gn(e.node,n.node,f,0),void(f.length>0&&t.push(Hn("p-thunk",r,f)));case"tagger":for(var s=e.tagger,d=n.tagger,h=!1,g=e.node;"tagger"===g.type;)h=!0,"object"!=typeof s?s=[s,g.tagger]:s.push(g.tagger),g=g.node;for(var v=n.node;"tagger"===v.type;)h=!0,"object"!=typeof d?d=[d,v.tagger]:d.push(v.tagger),v=v.node;return h&&s.length!==d.length?void t.push(Hn("p-redraw",r,n)):((h?Xn(s,d):s===d)||t.push(Hn("p-tagger",r,d)),void Gn(g,v,t,r+1));case"text":if(e.text!==n.text)return void t.push(Hn("p-text",r,n.text));return;case"node":return e.tag!==n.tag||e.namespace!==n.namespace?void t.push(Hn("p-redraw",r,n)):(void 0!==(p=Zn(e.facts,n.facts))&&t.push(Hn("p-facts",r,p)),void nt(e,n,t,r));case"keyed-node":return e.tag!==n.tag||e.namespace!==n.namespace?void t.push(Hn("p-redraw",r,n)):(void 0!==(p=Zn(e.facts,n.facts))&&t.push(Hn("p-facts",r,p)),void tt(e,n,t,r));case"custom":if(e.impl!==n.impl)return void t.push(Hn("p-redraw",r,n));var p=Zn(e.facts,n.facts);void 0!==p&&t.push(Hn("p-facts",r,p));var b=n.impl.diff(e,n);if(b)return void t.push(Hn("p-custom",r,b));return}}}function Hn(e,n,t){return{index:n,type:e,data:t,domNode:kr.undefined,eventNode:kr.undefined}}function Xn(e,n){for(var t=0;tu?t.push(Hn("p-remove-last",r,i-u)):i0||i.length>0||void 0!==O)&&t.push(Hn("p-reorder",r,{patches:a,inserts:i,endInserts:O}))}function rt(e,n,t,r,a,o){var i=e[t];if(void 0===i)return i={tag:"insert",vnode:r,index:a,data:kr.undefined},o.push({index:a,entry:i}),void(e[t]=i);if("remove"===i.tag){o.push({index:a,entry:i}),i.tag="move";var u=[];return Gn(i.vnode,r,u,i.index),i.index=a,void(i.data.data={patches:u,entry:i})}rt(e,n,t+"_elmW6BL",r,a,o)}function at(e,n,t,r,a){var o=e[t];if(void 0===o){u=Hn("p-remove",a,kr.undefined);return n.push(u),void(e[t]={tag:"remove",vnode:r,index:a,data:u})}if("insert"===o.tag){o.tag="move";var i=[];Gn(r,o.vnode,i,a);var u=Hn("p-remove",a,{patches:i,entry:o});return void n.push(u)}at(e,n,t+"_elmW6BL",r,a)}function ot(e,n,t,r){return 0===t.length?e:(it(e,n,t,r),ct(e,t))}function it(e,n,t,r){ut(e,n,t,0,0,n.descendantsCount,r)}function ut(e,n,t,r,a,o,i){for(var u=t[r],c=u.index;c===a;){var l=u.type;if("p-thunk"===l)it(e,n.node,u.data,i);else if("p-reorder"===l)u.domNode=e,u.eventNode=i,(s=u.data.patches).length>0&&ut(e,n,s,0,a,o,i);else if("p-remove"===l){u.domNode=e,u.eventNode=i;var f=u.data;if(void 0!==f){f.entry.data=e;var s=f.patches;s.length>0&&ut(e,n,s,0,a,o,i)}}else u.domNode=e,u.eventNode=i;if(r++,!(u=t[r])||(c=u.index)>o)return r}switch(n.type){case"tagger":for(var d=n.node;"tagger"===d.type;)d=d.node;return ut(e,d,t,r,a+1,o,e.elm_event_node_ref);case"node":for(var h=n.children,g=e.childNodes,v=0;vo))return r;a=b}return r;case"keyed-node":for(var h=n.children,g=e.childNodes,v=0;vo))return r;a=b}return r;case"text":case"thunk":throw new kr.Error("should never traverse `text` or `thunk` nodes like this")}}function ct(e,n){for(var t=0;t"),new kr.Error(e)}function Ht(e,n,t,r){n.embed=function(n,t){for(;n.lastChild;)n.removeChild(n.lastChild);return Wa.initialize(r(e.init,t,n),e.update,e.subscriptions,Xt(n,e.view))},n.fullscreen=function(n){return Wa.initialize(r(e.init,n,kr.document.body),e.update,e.subscriptions,Xt(kr.document.body,e.view))}}function Xt(e,n){return function(t,r){var a={tagger:t,parent:kr.undefined},o=n(r),i=Xe(o,a);return e.appendChild(i),Vn(i,n,o,a)}}function Zt(e,n,t,r){n.fullscreen=function(n){var a={doc:kr.undefined};return Wa.initialize(r(e.init,n,kr.document.body),e.update(er(a)),e.subscriptions,nr(t,kr.document.body,a,e.view,e.viewIn,e.viewOut))},n.embed=function(n,a){var o={doc:kr.undefined};return Wa.initialize(r(e.init,a,n),e.update(er(o)),e.subscriptions,nr(t,n,o,e.view,e.viewIn,e.viewOut))}}function er(e){return Ea.nativeBinding(function(n){var t=e.doc;if(t){var r=t.getElementsByClassName("debugger-sidebar-messages")[0];r&&(r.scrollTop=r.scrollHeight)}n(Ea.succeed(Xr.Tuple0))})}function nr(e,n,t,r,a,o){return function(i,u){var c={tagger:i,parent:kr.undefined},l={tagger:i,parent:kr.undefined},f=r(u),s=Xe(f,c);n.appendChild(s);var d=Vn(s,r,f,c),h=a(u)._1,g=Xe(h,l);n.appendChild(g);var v=Vn(g,tr(c,g,a),h,l),p=ir(u,o,l,n,e,t);return function(e){d(e),v(e),p(e)}}}function tr(e,n,t){var r,a=rr(n),o="Normal",i=e.tagger,u=function(){};return function(n){var c=t(n),l=c._0.ctor;return e.tagger="Normal"===l?i:u,o!==l&&(ar("removeEventListener",a,o),ar("addEventListener",a,l),"Normal"===o&&(r=kr.document.body.style.overflow,kr.document.body.style.overflow="hidden"),"Normal"===l&&(kr.document.body.style.overflow=r),o=l),c._1}}function rr(e){return function(n){if("keydown"!==n.type||!n.metaKey||82!==n.which){for(var t="scroll"===n.type||"wheel"===n.type,r=n.target;null!==r;){if("elm-overlay-message-details"===r.className&&t)return;if(r===e&&!t)return;r=r.parentNode}n.stopPropagation(),n.preventDefault()}}}function ar(e,n,t){switch(t){case"Normal":return;case"Pause":return or(e,n,ja);case"Message":return or(e,n,za)}}function or(e,n,t){for(var r=0;r=0&&a.splice(n,1)}var a=[],o=ta[e].converter,i=Ea.succeed(null);return ta[e].init=i,ta[e].onEffects=A(n),{subscribe:t,unsubscribe:r}}function vr(e,n){function t(e,n,t){for(var a=r(e,n,t),o=0;o 2 | 3 | 4 | Elm "Http Random Gif" Prepack Test 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RandomGifHttp/main.elm: -------------------------------------------------------------------------------- 1 | import Html exposing (..) 2 | import Html.Attributes exposing (..) 3 | import Html.Events exposing (..) 4 | import Http 5 | import Json.Decode as Decode 6 | 7 | 8 | 9 | main = 10 | Html.program 11 | { init = init "cats" 12 | , view = view 13 | , update = update 14 | , subscriptions = subscriptions 15 | } 16 | 17 | 18 | 19 | -- MODEL 20 | 21 | 22 | type alias Model = 23 | { topic : String 24 | , gifUrl : String 25 | } 26 | 27 | 28 | init : String -> (Model, Cmd Msg) 29 | init topic = 30 | ( Model topic "waiting.gif" 31 | , getRandomGif topic 32 | ) 33 | 34 | 35 | 36 | -- UPDATE 37 | 38 | 39 | type Msg 40 | = MorePlease 41 | | NewGif (Result Http.Error String) 42 | 43 | 44 | update : Msg -> Model -> (Model, Cmd Msg) 45 | update msg model = 46 | case msg of 47 | MorePlease -> 48 | (model, getRandomGif model.topic) 49 | 50 | NewGif (Ok newUrl) -> 51 | (Model model.topic newUrl, Cmd.none) 52 | 53 | NewGif (Err _) -> 54 | (model, Cmd.none) 55 | 56 | 57 | 58 | -- VIEW 59 | 60 | 61 | view : Model -> Html Msg 62 | view model = 63 | div [] 64 | [ h2 [] [text model.topic] 65 | , button [ onClick MorePlease ] [ text "More Please!" ] 66 | , br [] [] 67 | , img [src model.gifUrl] [] 68 | ] 69 | 70 | 71 | 72 | -- SUBSCRIPTIONS 73 | 74 | 75 | subscriptions : Model -> Sub Msg 76 | subscriptions model = 77 | Sub.none 78 | 79 | 80 | 81 | -- HTTP 82 | 83 | 84 | getRandomGif : String -> Cmd Msg 85 | getRandomGif topic = 86 | let 87 | url = 88 | "https://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=" ++ topic 89 | in 90 | Http.send NewGif (Http.get url decodeGifUrl) 91 | 92 | 93 | decodeGifUrl : Decode.Decoder String 94 | decodeGifUrl = 95 | Decode.at ["data", "image_url"] Decode.string 96 | -------------------------------------------------------------------------------- /RandomGifHttp/main.uglify.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkaterDad/elm-prepack-experiments/af09ed73d61f4932b8cfbc562a3285a617a482fc/RandomGifHttp/main.uglify.js.gz -------------------------------------------------------------------------------- /RandomGifHttp/prepacked.uglify.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkaterDad/elm-prepack-experiments/af09ed73d61f4932b8cfbc562a3285a617a482fc/RandomGifHttp/prepacked.uglify.js.gz -------------------------------------------------------------------------------- /RandomGifHttp/waiting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkaterDad/elm-prepack-experiments/af09ed73d61f4932b8cfbc562a3285a617a482fc/RandomGifHttp/waiting.gif -------------------------------------------------------------------------------- /RealWorldSPA/README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | This folder contains the built elm code from [Richard Feldman's Elm-SPA-Example repo](https://github.com/rtfeldman/elm-spa-example). 3 | 4 | Since it is intended to mimic a real world application, it should give the most interesting results. 5 | 6 | The SPA routing is done with hashes, so it can be run by opening index.html directly. 7 | 8 | A prepacked version will be here eventually, I hope! 9 | 10 | # Issues 11 | prepack 0.2.2 fails on `elm.js`. 12 | 13 | ``` 14 | SyntaxError: Invalid regular expression: /(?!/: Unterminated group 15 | ``` 16 | -------------------------------------------------------------------------------- /RealWorldSPA/assets/images/error.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SkaterDad/elm-prepack-experiments/af09ed73d61f4932b8cfbc562a3285a617a482fc/RealWorldSPA/assets/images/error.jpg -------------------------------------------------------------------------------- /RealWorldSPA/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Conduit 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 32 | 33 | 34 | --------------------------------------------------------------------------------