diff --git a/server/assets/bundle.js b/server/assets/bundle.js index e483880..019d367 100644 --- a/server/assets/bundle.js +++ b/server/assets/bundle.js @@ -1 +1,6637 @@ -!function(){"use strict";function e(e){return Mn.test(e)}function t(e){return typeof e===Sn}function n(e){return e&&typeof e===Tn}function r(e){return typeof e===En}function i(e){return typeof e===_n}function o(e){return r(e)||null===e||""===e}function a(e){return Array.isArray(e)||e instanceof Array}function u(e,t){var n=Object.getOwnPropertyDescriptor(e,t);return r(e[t])||n&&n.writable}function s(e){return Jn.test(e)}function c(e,t){return Array.prototype.slice.call((t||document).querySelectorAll(e))}function f(e,t){return(t||document).querySelector(e)}function p(){return document.createDocumentFragment()}function l(){return document.createTextNode("")}function h(e){return!!e.ownerSVGElement}function d(e){return"svg"===e?document.createElementNS(kn,e):document.createElement(e)}function y(e,t){if(r(e.innerHTML)){var n=(new DOMParser).parseFromString(t,"application/xml"),i=e.ownerDocument.importNode(n.documentElement,!0);e.appendChild(i)}else e.innerHTML=t}function m(e,t){e.style.display=t?"":"none",e.hidden=!t}function v(e,t){e.removeAttribute(t)}function g(e){return Object.keys(e).reduce(function(t,n){return t+" "+n+": "+e[n]+";"},"")}function x(e,t){return e.getAttribute(t)}function b(e,t,n){var r=Nn.exec(t);r&&r[1]?e.setAttributeNS(On,r[1],n):e.setAttribute(t,n)}function w(e,t,n){e.insertBefore(t,n.parentNode&&n)}function _(e,t){if(e)for(var n;n=Fn.exec(e);)t(n[1].toLowerCase(),n[2]||n[3]||n[4])}function T(e,t,n){if(e){var r,i=t(e,n);if(!1===i)return;for(e=e.firstChild;e;)r=e.nextSibling,T(e,t,i),e=r}}function E(e,t){for(var n=e?e.length:0,r=0;rr;)n--,R.apply(t[n],[t,n])}function R(e,t){e.splice(t,1),this.unmount(),he(this.parent,this,this.__.tagName,!0)}function U(e){var t=this;E(Object.keys(this.tags),function(n){ae.apply(t.tags[n],[n,e])})}function B(e,t,n){n?ve.apply(this,[e,t]):w(e,this.root,t.root)}function P(e,t,n){n?me.apply(this,[e,t]):w(e,this.root,t.root)}function z(e,t){t?me.call(this,e):e.appendChild(this.root)}function V(e,t,n){v(e,vn);var r,o=typeof x(e,gn)!==_n||v(e,gn),u=fe(e),s=pn[u],c=e.parentNode,f=l(),h=ie(e),d=x(e,mn),y=[],m=[],g=!pn[u],b="VIRTUAL"===e.tagName;return n=Hn.loopKeys(n),n.isLoop=!0,d&&v(e,mn),c.insertBefore(f,e),c.removeChild(e),n.update=function(){n.value=Hn(n.val,t);var c=p(),l=n.value,v=!a(l)&&!i(l),x=f.parentNode;x&&(v?l=(r=l||!1)?Object.keys(l).map(function(e){return M(n,l[e],e)}):[]:r=!1,d&&(l=l.filter(function(e,r){return n.key&&!v?!!Hn(d,M(n,e,r,t)):!!Hn(d,L(Object.create(t),e))})),E(l,function(i,a){var f=o&&typeof i===Tn&&!r,p=m.indexOf(i),d=-1===p,v=!d&&f?p:a,w=y[v],_=a>=m.length,T=f&&d||!f&&!w;i=!r&&n.key?M(n,i,a):i,T?((w=new re(s,{parent:t,isLoop:!0,isAnonymous:g,tagName:u,root:e.cloneNode(g),item:i,index:a},e.innerHTML)).mount(),_?z.apply(w,[c||x,b]):P.apply(w,[x,y[a],b]),_||m.splice(a,0,i),y.splice(a,0,w),h&&le(t.tags,u,w,!0)):v!==a&&f&&(S(l,m[v])&&(B.apply(w,[x,y[a],b]),y.splice(a,0,y.splice(v,1)[0]),m.splice(a,0,m.splice(v,1)[0])),n.pos&&(w[n.pos]=a),!h&&w.tags&&U.call(w,a)),w.__.item=i,w.__.index=a,w.__.parent=t,T||w.update(i)}),D(l,y),m=l.slice(),x.insertBefore(c,f))},n.unmount=function(){E(y,function(e){e.unmount()})},n}function $(e,t,n){var r=this;T(e,function(t,i){var o,a,u,s=t.nodeType,c=i.parent;if(!n&&t===e)return{parent:c};if(3===s&&"STYLE"!==t.parentNode.tagName&&Hn.hasExpr(t.nodeValue)&&c.children.push({dom:t,expr:t.nodeValue}),1!==s)return i;var f="VIRTUAL"===t.tagName;if(o=x(t,vn))return f&&b(t,"loopVirtual",!0),c.children.push(V(t,r,o)),!1;if(o=x(t,mn))return c.children.push(Object.create(Zn).init(t,r,o)),!1;if((a=x(t,yn))&&Hn.hasExpr(a))return c.children.push({isRtag:!0,expr:a,dom:t,attrs:[].slice.call(t.attributes)}),!1;if(u=ie(t),f&&(x(t,"virtualized")&&t.parentElement.removeChild(t),u||x(t,"virtualized")||x(t,"loopVirtual")||(u={tmpl:t.outerHTML})),u&&(t!==e||n)){if(!f||x(t,yn)){var p={root:t,parent:r,hasImpl:!0};return c.children.push(ue(u,p,t.innerHTML,r)),!1}b(t,"virtualized",!0);var l=new re({tmpl:t.outerHTML},{root:t,parent:r},t.innerHTML);c.children.push(l)}return q.apply(r,[t,t.attributes,function(e,t){t&&c.children.push(t)}]),{parent:c}},{parent:{children:t}})}function q(t,n,r){var i=this;E(n,function(n){if(!n)return!1;var o,a=n.name,u=e(a);S(dn,a)?o=Object.create(Qn).init(t,i,a,n.value):Hn.hasExpr(n.value)&&(o={dom:t,expr:n.value,attr:a,bool:u}),r(n,o)})}function H(e,t,n){var r="o"===n[0],i=r?"select>":"table>";if(e.innerHTML="<"+i+t.trim()+"t?-1:0}function Qe(e){return vr.groupBy(e.sort(),vr.isEqual)}function Xe(e){var t={};return e.map(function(e){t[e[0]]=e[1]}),t}function Ye(e,t){var n=vr.clone(e);return n.push.apply(n,t),n}function et(e){(e=vr.filter(e,function(e){return e.length>0})).sort(Ze);var t=e.reduce(function(e,t){return!t||t.length<1?"":e+"("+RegExp.escape(t)+")|"},""),n=new RegExp(t);return function(e){var t=n.exec(e);return!(vr.isNull(t)||!t[0])&&t[0]}}function tt(e){console.log("%j\n",e)}function nt(e){if(!e)return!1;var t=e.charCodeAt();return!isNaN(t)&&(4732}function ot(e){var t=e.charCodeAt();return!isNaN(t)&&t>=65&&t<=90}function at(e,t,n){var r=[],i=e[0].charCodeAt(),o=!1,a=0;for(43===i||45===i?(r.push(e[0]),e=e.substr(1),a++):46===i&&(e=e.substr(1),a++,t++,r.push("0"),r.push("."),o=!0);nt(e[0])&&0!==e.length;)r.push(e[0]),e=e.substr(1),t++,a++;if("."===e[0]&&nt(e[1])){for(r.push("."),r.push(e[1]),e=e.substr(2),t++,t++,a++,a++;nt(e[0])&&0!==e.length;)r.push(e[0]),e=e.substr(1),a++,t++;return[a,["float",parseFloat(r.join(""),10),t,n]]}return o?[a,["float",parseFloat(r.join(""),10),t,n]]:[a,["integer",parseInt(r.join(""),10),t,n]]}function ut(e,t,n,r){for(var i=[],o=0;!rt(e[0])&&it(e[0])&&!t(e);)i.push(e[0]),e=e.substr(1),o++,n++;return i=i.join(""),[[o,["identifier",i,n,r]]]}function st(e,t,n,r){var i=ut(e,t,n,r);return i[0][1][0]="constructor",i}function ct(e,t,n){var r=[],i=1,o=t;for(e=e.substr(1);34!==e[0].charCodeAt();)if(r.push(e[0]),e=e.substr(1),i++,o++,e.length<1)throw yr.JSyntaxError(n,t,"Error: missing quotation mark");return i++,[i,["stringlit",r.join(""),o,n]]}function ft(e,t,n){if(e.length<4)return!1;var r=e.substr(0,4);return"then"===r?["thenexp","then"]:"true"===r&&["truelit","true"]}function pt(e,t,n,r,i){var o=n.length;return!(e.length1&&kt(r)&&n(kt(r)))for(;n(Nt(r))&&n(kt(r))&&(u.push(e(r)),t(kt(u).exprType));){if(!kt(r))throw yr.JSyntaxError(o,i,"Unexpected end of source");if(kt(r)[0],r.length<=1)break}if(!kt(r))throw yr.JSyntaxError(kt(r)[3],kt(r)[2],"unexpected end of source");return n(kt(r))&&u.push(e(r)),u}function Ft(e,t,n,r,i){var o,a,u=Zt(n);if(!e(u))throw yr.JSyntaxError(kt(n)[2],kt(n)[3],"Unexpected token: ``"+kt(n)[0]+"''");if(o=[u],n.length>1&&kt(n)[0]===t){for(;kt(n)[0]===t;){if(n.pop(),a=Zt(n),!kt(n))throw yr.JSyntaxError(kt(n)[3],kt(n)[2],"Missing terminator: "+t);o.push(a)}return o}return o}function Ct(e,t,n){var r;if("right_square"===kt(e)[0]?r=[]:"comma"===kt(e)[0]?(e.pop(),r=[]):r=Ft(function(e){return!0},"comma",e,kt(e)[3],kt(e)[2]),!kt(e)||"right_square"!==kt(e)[0])throw yr.JSyntaxError(kt(e)[3],kt(e)[2],"list must be terminated by ]");return e.pop(),Ot(new br.ListT(r),e,t,n)}function Mt(e,t,n){var r,i,o=Zt(e);if("Name"!==o.exprType)throw yr.JSyntaxError(t,n,"Expected an identifier in function definition");if(r="right_paren"===kt(e)[0]?[]:Jt(Zt,Ar,Ht,e,n,t),!e||"right_paren"!==kt(e)[0])throw yr.JSyntaxError(t,n,"Formal parameters must be followed by )");return e.pop(),i=Zt(e),Ot(new br.DefFunc(o,r,i),e,i.linenum,i.charnum)}function Dt(e,t,n){var r,i;if(kt(e)||yr.JSyntaxError(t,n,"Unexpected end of source"),r=Jt(Bt,Nr,Lr,e,n,t),kt(e)&&"right_brace"!==kt(e)[0])throw yr.JSyntaxError(kt(e)[2],kt(e)[3],"let/def form must have a closing }");if(!kt(e))throw yr.JSyntaxError(vr.last(r).linenum,vr.last(r).charnum,"Unexpected end of source");if(e.pop(),e.length<=0)throw yr.JSyntaxError(vr.last(r).linenum,vr.last(r).charnum,"let/def form must have a body");if("Definition"===(i=Zt(e)).exprType||"FunctionDefinition"===i.exprType)throw yr.JSyntaxError(i.linenum,i.charnum,"Body of a let/def expression cannot be a definition");return Ot(new br.LetExp(r,i),e,i.linenum,i.charnum)}function Rt(e,t,n){var r,i,o=Zt(e);if("Name"!=o.exprType)throw yr.JSyntaxError(kt(e)[3],kt(e)[2],"Expected an identifier in function definition");if(r="right_paren"===kt(e)[0]?[]:Jt(Zt,Ar,Ht,e,n,t),"right_paren"!==kt(e)[0])throw yr.JSyntaxError(t,n,"Formal parameters must be followed by )");if(e.pop(),"->"!==kt(e)[1])throw yr.JSyntaxError(kt(e)[3],kt(e)[2],"Function parameters in let/def form must be followed by ->");return e.pop(),i=Zt(e),Ot(new br.DefFunc(o,r,i),e,i.linenum,i.charnum)}function Ut(e,t,n){var r,i=Zt(e);if("Name"!=i.exprType)throw yr.JSyntaxError(i.linenum,i.charnum,"Expected an identifier in let/def binding");if(!kt(e)||"="!==kt(e)[1])throw yr.JSyntaxError(i.linenum,i.charnum,"An identifier in a let/def binding must be followed by ``=''");if(e.pop(),!Lt(e,["comma","arrow","right_brace","right_square"],i.linenum,i.charnum))throw yr.JSyntaxError(i.linenum,i.charnum,"The binding of "+identifier.val+" must not be followed by "+kt(e)[0]);if("Definition"===(r=Zt(e)).exprType||"FunctionDefinition"===r.exprType)throw yr.JSyntaxError(r.linenum,r.charnum,"A definition cannot be the value of a binding");return Ot(new br.Def(i,r),e,r.linenum,r.charnum)}function Bt(e){var t=kt(e)[3],n=kt(e)[2];return kt(e)&&"left_paren"===kt(e)[0]?(e.pop(),Rt(e,t,n)):Ut(e,t,n)}function Pt(e,t,n){var r,i=Zt(e,t,n);if("TypeOperator"!==i.exprType)throw yr.JSyntaxError(i.linenum,i.charnum,"Expected a type operator in data type definition");if("right_paren"!==kt(e)[0])o=Jt(Zt,Ar,Ht,e,n,t);else var o=[];if(!e||"right_paren"!==kt(e)[0])throw yr.JSyntaxError(vr.last(o).linenum,vr.last(o).charnum,"Data type parameters must be followed by )");return e.pop(),r=Zt(e),Ot(new br.DataType(i,o,r),e,r.linenum,r.charnum)}function zt(e,t,n){var r,i;if(e.length<2)throw yr.JSyntaxError(t,n,"Unexpected end of source");if("left_paren"===kt(e)[0])return e.pop(),Pt(e,t,n);if(Lt(e,["constructor"],t,n))throw yr.JSyntaxError(t,n,"deftype must be followed by a single constructor if it is not a data type definition with type variables");if(i=Zt(e,t,n),!e)throw yr.JSyntaxError(i.linenum,i.charnum,"Unexpected end of source");if("TypeOperator"!==i.exprType)throw yr.JSyntaxError(i.linenum,i.charnum,"left-hand side of type alias was not a type operator");if("Application"!==(r=Zt(e,t,n)).exprType&&"TypeOperator"!==r.exprType)throw yr.JSyntaxError(r.linenum,r.charnum,"was expecting an application or type operator on the right-hand side of a type alias");return Ot(new br.DefType(i,r),e,r.linenum,r.charnum)}function Vt(e,t,n){var r,i;if(e.length<2)throw yr.JSyntaxError(t,n,"Unexpected end of source");if("left_paren"===kt(e)[0])return e.pop(),Mt(e,t,n);if("left_brace"===kt(e)[0])return e.pop(),Dt(e,kt(e)[3],kt(e)[2]);if(Lt(e,["identifier"],t,n))throw yr.JSyntaxError(t,n,"def must be followed by identifier, not "+kt(e)[0]);if(r=Zt(e),!kt(e))throw yr.JSyntaxError(r.linenum,r.charnum,"Unexpected end of source");if(!Lt(e,["comma","arrow","right_brace","right_square"],r.linenum,r.charnum))throw yr.JSyntaxError(r.linenum,r.charnum,"def "+r.val+" must not be followed by "+kt(e)[0]);if("Definition"===(i=Zt(e)).exprType||"FunctionDefinition"===i.exprType)throw yr.JSyntaxError(i.linenum,i.charnum,"A definition cannot be the value of a binding");return Ot(new br.Def(r,i),e,i.linenum,i.charnum)}function $t(e,t,n){var r,i;if("integer"!==kt(e)[0]||kt(e)[1]<1)throw yr.JSyntaxError(t,n,"defop must be followed by integer precedence >= 1");if(e.pop(),"Left"!==kt(e)[1]&&"Right"!==kt(e)[1])throw yr.JSyntaxError(t,n,"defop must be followed by precedence and then either Left or Right");if(e.pop(),"left_paren"!==kt(e)[0])throw yr.JSyntaxError(t,n,"defop arguments must start with (");if(e.pop(),!e.slice(e.length-3,e.length).every(function(e){return"identifier"===e[0]}))throw yr.JSyntaxError(t,n,"defop must be surrounded by exactly 3 identifiers");if(i=e.slice(e.length-3,e.length),e.pop(),e.pop(),e.pop(),"right_paren"!==kt(e)[0])throw yr.JSyntaxError(t,n,"defop pattern must be terminated with )");return e.pop(),(r=[new br.Name(i[1][1]),new br.Name(i[0][1]),new br.Name(i[2][1])]).map(function(e){return e.linenum=t,e.charnum=n,e}),Ot(new br.DefFunc(r[0],r.slice(1,3),Zt(e)),e,vr.last(r).linenum,vr.last(r).charnum)}function qt(e,t,n){var r,i,o;if(Lt(e,["def","comma","lambda"],t,n)){if(r=Zt(e),kt(e)&&"thenexp"===kt(e)[0]){if(e.pop(),i=Zt(e),kt(e)&&"elsexp"===kt(e)[0]){if(e.pop(),vr.size(e)<1)throw yr.JSyntaxError(i.linenum,i.charnum,"Unexpected end of source");return o=Zt(e),Ot(new br.If(r,i,o),e,o.linenum,o.charnum)}throw yr.JSyntaxError(i.linenum,i.charnum,"If expression must include an else variant")}throw yr.JSyntaxError(r.linenum,r.charnum,"if ``exp'' must be folowed by ``then'' exp, not "+Nt(e)[0])}throw yr.JSyntaxError(t,n,"``if'' cannot be followed by "+kt(e)[0])}function Ht(e){return"identifier"===e[0]&&"->"!==e[1]}function Kt(e,t,n){var r=Jt(Zt,Ar,Ht,e,n,t);if("->"!==kt(e)[1])throw yr.JSyntaxError(vr.last(r).linenum,vr.last(r).charnum,"arrow must follow parameters in lambda, not "+kt(e)[0]);e.pop();var i=Zt(e);return Ot(new br.FuncT(r,i),e,i.linenum,i.charnum)}function Wt(e,t,n){var r,i,o,a=Zt(e);if(!kt(e))throw yr.JSyntaxError(a.linenum,a.charnum,"Unexpected end of source");if(r=kt(e),br.OPInfo[r[1]]){if(i=Gt(e,1,a,a.linenum,a.charnum),kt(e)&&"right_paren"===kt(e)[0])return e.pop(),i;throw yr.JSyntaxError(a.linenum,a.charnum,"Mismatched parentheses or missing parenthesis on right-hand side")}if(o="right_paren"!==kt(e)[0]?Jt(Zt,Jr,Ir,e,kt(e)[2],kt(e)[3]):[],kt(e)&&"right_paren"===kt(e)[0])return e.pop(),Ot(br.makeApp(a,o),e,n,t);throw yr.JSyntaxError(kt(e)[3],kt(e)[2],"Mismatched parentheses or missing parenthesis on right-hand side")}function Gt(e,t,n,r,i){for(n||(n=Zt(e));;){var o=kt(e);if(!o)throw yr.JSyntaxError(r,i,"Unexpected end of source");var a=br.OPInfo[o[1]];if(!a||a[0]0;)t=kr.annotate_fvs(Or.desugar(Zt(e),r)),n.push(t);return{ast:n,types:r}}catch(e){void 0!==e.stxerror?(e.stxerror(),console.error("Tokenization error")):console.log(e.errormessage)}}function Xt(e,t){var n={};n.name=e,n.bindings={};for(var r=0;r=0&&/\s/.test(e[t]););return t}function t(t,u){var s=/.*/g,c=s.lastIndex=u++,f=s.exec(t)[0].match(o);if(f){var p=c+f[0].length,l=t[c=e(t,c)];if(c<0||~n.indexOf(l))return p;if("."===l)"."===t[c-1]&&(u=p);else if("+"===l||"-"===l)(t[--c]!==l||(c=e(t,c))<0||!a.test(t[c]))&&(u=p);else if(~i.indexOf(l)){for(var h=c+1;--c>=0&&a.test(t[c]););~r.indexOf(t.slice(c+1,h))&&(u=p)}}return u}var n="[{(,;:?=|&!^~>%*/",r=["case","default","do","else","in","instanceof","prefix","return","typeof","void","yield"],i=r.reduce(function(e,t){return e+t.slice(-1)},""),o=/^\/(?=[^*>/])[^[/\\]*(?:(?:\\.|\[(?:\\.|[^\]\\]*)*\])[^[\\/]*)*?\/[gimuy]*/,a=/[$\w]/;return t}(),qn=function(e){function t(e){return e}function n(e,t){return t||(t=b),new RegExp(e.source.replace(/{/g,t[2]).replace(/}/g,t[3]),e.global?c:"")}function r(e){if(e===v)return g;var t=e.split(" ");if(2!==t.length||h.test(e))throw new Error('Unsupported brackets "'+e+'"');return t=t.concat(e.replace(d,"\\").split(" ")),t[4]=n(t[1].length>1?/{[\S\s]*?}/:g[4],t),t[5]=n(e.length>3?/\\({|})/g:g[5],t),t[6]=n(g[6],t),t[7]=RegExp("\\\\("+t[3]+")|([[({])|("+t[3]+")|"+y,c),t[8]=e,t}function i(e){return e instanceof RegExp?u(e):b[e]}function o(e){(e||(e=v))!==b[8]&&(b=r(e),u=e===v?t:n,b[9]=u(g[9])),x=e}function a(e){var t;t=(e=e||{}).brackets,Object.defineProperty(e,"brackets",{set:o,get:function(){return x},enumerable:!0}),s=e,o(t)}var u,s,c="g",f=/\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//g,p=/"[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|`[^`\\]*(?:\\[\S\s][^`\\]*)*`/g,l=p.source+"|"+/(?:\breturn\s+|(?:[$\w\)\]]|\+\+|--)\s*(\/)(?![*\/]))/.source+"|"+/\/(?=[^*\/])[^[\/\\]*(?:(?:\[(?:\\.|[^\]\\]*)*\]|\\.)[^[\/\\]*)*?([^<]\/)[gim]*/.source,h=RegExp("[\\x00-\\x1F<>a-zA-Z0-9'\",;\\\\]"),d=/(?=[[\]()*+?.^$|])/g,y=p.source+"|"+/(\/)(?![*\/])/.source,m={"(":RegExp("([()])|"+y,c),"[":RegExp("([[\\]])|"+y,c),"{":RegExp("([{}])|"+y,c)},v="{ }",g=["{","}","{","}",/{[^}]*}/,/\\([{}])/g,/\\({)|{/g,RegExp("\\\\(})|([[({])|(})|"+y,c),v,/^\s*{\^?\s*([$\w]+)(?:\s*,\s*(\S+))?\s+in\s+(\S.*)\s*}/,/(^|[^\\]){=[\S\s]*?}/],x=void 0,b=[];return i.split=function(e,t,n){function r(e){d&&(e=d+e,d=""),t||a?p.push(e&&e.replace(n[5],"$1")):p.push(e)}function i(n,r,i){return i&&(r=$n(e,n)),t&&r>n+2&&(c="⁗"+h.length+"~",h.push(e.slice(n,r)),d+=e.slice(u,n)+c,u=r),r}n||(n=b);var o,a,u,s,c,f,p=[],l=n[6],h=[],d="";for(a=u=l.lastIndex=0;o=l.exec(e);){if(f=l.lastIndex,s=o.index,a){if(o[2]){var y=o[2],v=m[y],g=1;for(v.lastIndex=f;o=v.exec(e);)if(o[1]){if(o[1]===y)++g;else if(!--g)break}else v.lastIndex=i(o.index,v.lastIndex,o[2]);l.lastIndex=g?e.length:v.lastIndex;continue}if(!o[3]){l.lastIndex=i(s,f,o[4]);continue}}o[1]||(r(e.slice(u,s)),u=l.lastIndex,(l=n[6+(a^=1)]).lastIndex=u)}return e&&u %s",t.riotData.tagName||"Unknown tag",this.tmpl),console.log(this.data))}function n(e){var t=r(e);return"try{return "!==t.slice(0,11)&&(t="return "+t),new Function("E",t+";")}function r(e){var t,n=qn.split(e.replace(u,'"'),1),r=n.qblocks;if(n.length>2||n[0]){var o,a,c=[];for(o=a=0;o1?"["+i.join(",")+'].join(" ").trim()':i[0]:o(e,t)}return e}function o(e,t,n){var r;return e=e.replace(l,function(e,t,n,i,o){return n&&(i=r?0:i+e.length,"this"!==n&&"global"!==n&&"window"!==n?(e=t+'("'+n+p+n,i&&(r="."===(o=o[i])||"("===o||"["===o)):i&&(r=!h.test(o.slice(i)))),e}),r&&(e="try{return "+e+"}catch(e){E(e,this)}"),n?e=(r?"function(){"+e+"}.call(this)":"("+e+")")+'?"'+n+'":""':t&&(e="function(v){"+(r?e.replace("return ","v="):"v=("+e+")")+';return v||v===0?v:""}.call(this)'),e}var a={};e.hasExpr=qn.hasExpr,e.loopKeys=qn.loopKeys,e.clearCache=function(){a={}},e.errorHandler=null;var u=/\u2057/g,s=/\u2057(\d+)~/g,c=/^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\u2057(\d+)~):/,f={"(":/[()]/g,"[":/[[\]]/g,"{":/[{}]/g},p='"in this?this:'+("object"!=typeof window?"global":"window")+").",l=/[,{][\$\w]+(?=:)|(^ *|[^$\w\.{])(?!(?:typeof|true|false|null|undefined|in|instanceof|is(?:Finite|NaN)|void|NaN|new|Date|RegExp|Math)(?![$\w]))([$_A-Za-z][$\w]*)/g,h=/^(?=(\.[$\w]+))\1(?:[^.[(]|$)/;return e.version=qn.version="v3.0.8",e}(),Kn=function(e){e=e||{};var t={},n=Array.prototype.slice;return Object.defineProperties(e,{on:{value:function(n,r){return"function"==typeof r&&(t[n]=t[n]||[]).push(r),e},enumerable:!1,writable:!1,configurable:!1},off:{value:function(n,r){if("*"!=n||r)if(r)for(var i,o=t[n],a=0;i=o&&o[a];++a)i==r&&o.splice(a--,1);else delete t[n];else t={};return e},enumerable:!1,writable:!1,configurable:!1},one:{value:function(t,n){function r(){e.off(t,r),n.apply(e,arguments)}return e.on(t,r)},enumerable:!1,writable:!1,configurable:!1},trigger:{value:function(r){var i,o,a,u=arguments,s=arguments.length-1,c=new Array(s);for(a=0;a|>([\S\s]*?)<\/yield\s*>|>)/gi,er=/]*)['"]\s*>([\S\s]*?)<\/yield\s*>/gi,tr=/|>([\S\s]*?)<\/yield\s*>)/gi,nr={tr:"tbody",th:"tr",td:"tr",col:"colgroup"},rr=Dn&&Dn<10?An:jn,ir="div",or="svg",ar={},ur=ar[ln]={},sr=0,cr=Object.freeze({Tag:G,tag:Z,tag2:Q,mount:X,mixin:Y,update:ee,unregister:te,version:"WIP"}),fr=0,pr=Object.freeze({getTag:ie,inheritFrom:oe,moveChildTag:ae,initChildTag:ue,getImmediateCustomParentTag:se,unmountAll:ce,getTagName:fe,cleanUpData:pe,arrayishAdd:le,arrayishRemove:he,mountTo:de,makeReplaceVirtual:ye,makeVirtual:me,moveVirtual:ve,selectTags:ge}),lr=Gn,hr={tmpl:Hn,brackets:qn,styleManager:Vn,vdom:fn,styleNode:Vn.styleNode,dom:Un,check:Rn,misc:Wn,tags:pr},dr=L({},cr,{observable:Kn,settings:lr,util:hr}),yr={JSyntaxError:xe,JTypeError:be,JInternalError:we},mr="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},vr=function(e,t){return t={exports:{}},e(t,t.exports),t.exports}(function(e,t){(function(){function n(e){function t(t,n,r,i,o,a){for(;o>=0&&o0?0:u-1;return arguments.length<3&&(i=n[a?a[s]:s],s+=e),t(n,r,i,a,s,u)}}function r(e){return function(t,n,r){n=_(n,r);for(var i=k(t),o=e>0?0:i-1;o>=0&&o0?a=o>=0?o:Math.max(o+u,a):u=o>=0?Math.min(o+1,u):o+u+1;else if(n&&o&&u)return o=n(r,i),r[o]===i?o:-1;if(i!==i)return o=t(l.call(r,a,u),b.isNaN),o>=0?o+a:-1;for(o=e>0?a:u-1;o>=0&&o=0&&t<=O};b.each=b.forEach=function(e,t,n){t=w(t,n);var r,i;if(N(e))for(r=0,i=e.length;r=0},b.invoke=function(e,t){var n=l.call(arguments,2),r=b.isFunction(t);return b.map(e,function(e){var i=r?t:e[t];return null==i?i:i.apply(e,n)})},b.pluck=function(e,t){return b.map(e,b.property(t))},b.where=function(e,t){return b.filter(e,b.matcher(t))},b.findWhere=function(e,t){return b.find(e,b.matcher(t))},b.max=function(e,t,n){var r,i,o=-1/0,a=-1/0;if(null==t&&null!=e)for(var u=0,s=(e=N(e)?e:b.values(e)).length;uo&&(o=r);else t=_(t,n),b.each(e,function(e,n,r){((i=t(e,n,r))>a||i===-1/0&&o===-1/0)&&(o=e,a=i)});return o},b.min=function(e,t,n){var r,i,o=1/0,a=1/0;if(null==t&&null!=e)for(var u=0,s=(e=N(e)?e:b.values(e)).length;ur||void 0===n)return 1;if(nt?(a&&(clearTimeout(a),a=null),u=c,o=e.apply(r,i),a||(r=i=null)):a||!1===n.trailing||(a=setTimeout(s,f)),o}},b.debounce=function(e,t,n){var r,i,o,a,u,s=function(){var c=b.now()-a;c=0?r=setTimeout(s,t-c):(r=null,n||(u=e.apply(o,i),r||(o=i=null)))};return function(){o=this,i=arguments,a=b.now();var c=n&&!r;return r||(r=setTimeout(s,t)),c&&(u=e.apply(o,i),o=i=null),u}},b.wrap=function(e,t){return b.partial(t,e)},b.negate=function(e){return function(){return!e.apply(this,arguments)}},b.compose=function(){var e=arguments,t=e.length-1;return function(){for(var n=this,r=t,i=e[t].apply(this,arguments);r--;)i=e[r].call(n,i);return i}},b.after=function(e,t){return function(){if(--e<1)return t.apply(this,arguments)}},b.before=function(e,t){var n;return function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=null),n}},b.once=b.partial(b.before,2);var I=!{toString:null}.propertyIsEnumerable("toString"),J=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];b.keys=function(e){if(!b.isObject(e))return[];if(m)return m(e);var t=[];for(var n in e)b.has(e,n)&&t.push(n);return I&&o(e,t),t},b.allKeys=function(e){if(!b.isObject(e))return[];var t=[];for(var n in e)t.push(n);return I&&o(e,t),t},b.values=function(e){for(var t=b.keys(e),n=t.length,r=Array(n),i=0;i":">",'"':""","'":"'","`":"`"},M=b.invert(C),D=function(e){var t=function(t){return e[t]},n="(?:"+b.keys(e).join("|")+")",r=RegExp(n),i=RegExp(n,"g");return function(e){return e=null==e?"":""+e,r.test(e)?e.replace(i,t):e}};b.escape=D(C),b.unescape=D(M),b.result=function(e,t,n){var r=null==e?void 0:e[t];return void 0===r&&(r=n),b.isFunction(r)?r.call(e):r};var R=0;b.uniqueId=function(e){var t=++R+"";return e?e+t:t},b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var U=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},P=/\\|'|\r|\n|\u2028|\u2029/g,z=function(e){return"\\"+B[e]};b.template=function(e,t,n){!t&&n&&(t=n),t=b.defaults({},t,b.templateSettings);var r=RegExp([(t.escape||U).source,(t.interpolate||U).source,(t.evaluate||U).source].join("|")+"|$","g"),i=0,o="__p+='";e.replace(r,function(t,n,r,a,u){return o+=e.slice(i,u).replace(P,z),i=u+t.length,n?o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'":r?o+="'+\n((__t=("+r+"))==null?'':__t)+\n'":a&&(o+="';\n"+a+"\n__p+='"),t}),o+="';\n",t.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{var a=new Function(t.variable||"obj","_",o)}catch(e){throw e.source=o,e}var u=function(e){return a.call(this,e,b)},s=t.variable||"obj";return u.source="function("+s+"){\n"+o+"}",u},b.chain=function(e){var t=b(e);return t._chain=!0,t};var V=function(e,t){return e._chain?b(t).chain():t};b.mixin=function(e){b.each(b.functions(e),function(t){var n=b[t]=e[t];b.prototype[t]=function(){var e=[this._wrapped];return p.apply(e,arguments),V(this,n.apply(b,e))}})},b.mixin(b),b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=s[e];b.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),"shift"!==e&&"splice"!==e||0!==n.length||delete n[0],V(this,n)}}),b.each(["concat","join","slice"],function(e){var t=s[e];b.prototype[e]=function(){return V(this,t.apply(this._wrapped,arguments))}}),b.prototype.value=function(){return this._wrapped},b.prototype.valueOf=b.prototype.toJSON=b.prototype.value,b.prototype.toString=function(){return""+this._wrapped}}).call(mr)}),gr={display:function(){return this.exprType+" "+this.val},type:function(){return this.exprType},linenum:0,charnum:0},xr={unify:function(e){if(this.expr===e.expr)return e.expr;console.log("Could not unify "+this.expr+" with "+e.expr)},isTypeExpr:!0,linenum:0,charnum:0};Se.prototype=gr,Oe.prototype=gr,ke.prototype=gr,Ne.prototype=gr,Le.prototype=gr,Ae.prototype=gr,je.prototype=gr,Ie.prototype=gr,Je.prototype=gr,Fe.prototype=gr,Ce.prototype=gr,Me.prototype=gr,De.prototype=gr,Re.prototype=gr,Ue.prototype=gr,Be.prototype=xr,Pe.prototype=xr,Ve.prototype=xr,$e.prototype=gr;var br={IntT:ke,FloatT:Ne,StrT:Le,BoolT:Ae,ListT:je,FuncT:Je,App:Ce,Name:Me,Def:De,OpT:Fe,OPInfo:{"::":[2,"Left"],",":[1,"Left"],"->":[1,"Right"]},makeApp:Ke,If:Ue,DefFunc:Re,UnaryOp:Oe,Nil:Ie,LetExp:Se,gensym:function(){var e=0;return function(){var t="G"+e;return e+=1,t}}(),TypeVar:Be,TypeOp:Pe,TypeDecl:Ve,Closure:Ee,isTypeExpr:Te,DefType:$e,DataType:He};RegExp.escape=function(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")};var wr={not:Ge,groupOps:Qe,opMatch:et,dict:Xe,extend:Ye,empty:We,debugPrint:tt},_r={src:"\n;; This file declares the various types used by intrinsic/prelude definitions\n;; It is sort of special in that it doesn't care whether there are any associated definitions\n;; just that there are type definitions for that particular binding's name\n\n\n;; Type definitions\ndeftype String (Vector Char)\n\ndeftype (Int) Intrinsic\n\ndeftype (Float) Intrinsic\n\ndeftype (Char) Intrinsic\n\ndeftype (Byte) Intrinsic\n\ndeftype (Void) Intrinsic\n\ndeftype (IO a) Intrinsic\n\ndeftype (Vector a) Intrinsic\n\ndeftype (List a)\n (Empty |\n (Cons a (List a)))\n\ndeftype (Bottom)\n Undefined\n\ndeftype (Maybe a)\n (Nothing |\n (Just a))\n\ndeftype (Either a b)\n ((Left a) |\n (Right b))\n\n;; List functions\n\n(: :: (a -> (List a) -> (List a)))\n\n(map :: ((a -> b) -> (List a) -> (List b)))\n\n(head :: ((List a) -> a))\n\n(tail :: ((List a) -> (List a)))\n\n(!! :: (Int -> (List a) -> a))\n\n(take :: (Int -> (List a) -> (Maybe (List a))))\n\n(drop :: (Int -> (List a) -> (Maybe (List a))))\n\n;; Optional functions\n\n(maybe :: (b -> (a -> b) -> (Maybe a) -> b))\n\n(either :: ((b -> c) -> (b -> c) -> (Either a b) -> c))\n\n;; I/O functions\n\n(print :: (String -> (IO Void)))\n\n;; Operator definitions\n\ndefop 1 Left (a + b)\n (add a b)\n\ndefop 1 Left (a - b)\n (minus a b)\n\ndefop 2 Left (a * b)\n (mul a b)\n\ndefop 2 Left (a / b)\n (div a b)\n\ndefop 2 Right (a ^ b)\n (pow a b)\n\ndefop 3 Left (a ++ b)\n (listConcat a b)\n\ndefop 3 Left (a == b)\n (eq a b)\n\ndefop 3 Left (a > b)\n (gt a b)\n\ndefop 3 Left (a >= b)\n (gte a b)\n\ndefop 3 Left (a < b)\n (lt a b)\n\ndefop 3 Left (a <= b)\n (lte a b)\n\ndefop 3 Left (a && b)\n (and a b)\n\ndefop 3 Left (a || b)\n (or a b)\n\ndefop 4 Left (x : xs)\n (cons x xs)\n\ndefop 5 Left (f $ x)\n (fapply f x)\n\ndefop 5 Left (f . g)\n (compose f g)\n\ndefop 3 Left (a | b)\n (bitwiseOr a b)\n\ndefop 3 Left (a & b)\n (bitwiseAnd a b)"},Tr=Object.keys(br.OPInfo),Er=["defop","integer","constructor","left_paren","identifier","identifier","identifier","right_paren"],Sr={tokenize:yt,isIdentifier:it},Or={desugar:wt},kr=(vr.compose(wr.not,vr.partial(vr.equal,[])),{annotate_fvs:Et}),Nr=At(["Definition","FunctionDefinition"].map(It)),Lr=vr.compose(wr.not,At(["right_brace"].map(jt))),Ar=At(["Name"].map(It)),jr=["def","comma","right_paren","right_square","right_brace","left_brace","right_brace"].map(jt),Ir=vr.compose(wr.not,At(jr)),Jr=vr.compose(wr.not,At(["Definition"].map(It))),Fr={parse:function(e){return Qt(Sr.tokenize(e))},tokenize:Sr.tokenize,parseFull:Qt},Cr={lookup:Yt,makeEnv:Xt}.makeEnv("toplevel",[["len",function(e){return e.length}],["+",function(e){return function(t){return e+t}}],["*",function(e){return function(t){return e*t}}],["-",function(e){return function(t){return e-t}}],["/",function(e){return function(t){return e/t}}],[":",en],["a",2],["b",3]]),Mr={evaluateString:nn};dr.tag2("test",'

{v}

',"","",function(e){var t=this;t.outputs=[],t.default="",this.evaluate=function(e){e.preventDefault();var n=t.refs.input;t.outputs.push(JSON.stringify(Mr.evaluateString(n.value))),t.refs.input.value=t.default,t.update()}.bind(this)}),dr.mount("test")}(); +(function () { +'use strict'; + +var __TAGS_CACHE = []; +var __TAG_IMPL = {}; +var GLOBAL_MIXIN = '__global_mixin'; +var ATTRS_PREFIX = 'riot-'; +var REF_DIRECTIVES = ['ref', 'data-ref']; +var IS_DIRECTIVE = 'data-is'; +var CONDITIONAL_DIRECTIVE = 'if'; +var LOOP_DIRECTIVE = 'each'; +var LOOP_NO_REORDER_DIRECTIVE = 'no-reorder'; +var SHOW_DIRECTIVE = 'show'; +var HIDE_DIRECTIVE = 'hide'; +var RIOT_EVENTS_KEY = '__riot-events__'; +var T_STRING = 'string'; +var T_OBJECT = 'object'; +var T_UNDEF = 'undefined'; +var T_FUNCTION = 'function'; +var XLINK_NS = 'http://www.w3.org/1999/xlink'; +var SVG_NS = 'http://www.w3.org/2000/svg'; +var XLINK_REGEX = /^xlink:(\w+)/; +var WIN = typeof window === T_UNDEF ? undefined : window; +var RE_SPECIAL_TAGS = /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?|opt(?:ion|group))$/; +var RE_SPECIAL_TAGS_NO_OPTION = /^(?:t(?:body|head|foot|[rhd])|caption|col(?:group)?)$/; +var RE_EVENTS_PREFIX = /^on/; +var RE_RESERVED_NAMES = /^(?:_(?:item|id|parent)|update|root|(?:un)?mount|mixin|is(?:Mounted|Loop)|tags|refs|parent|opts|trigger|o(?:n|ff|ne))$/; +var RE_HTML_ATTRS = /([-\w]+) ?= ?(?:"([^"]*)|'([^']*)|({[^}]*}))/g; +var CASE_SENSITIVE_ATTRIBUTES = { 'viewbox': 'viewBox' }; +var RE_BOOL_ATTRS = /^(?:disabled|checked|readonly|required|allowfullscreen|auto(?:focus|play)|compact|controls|default|formnovalidate|hidden|ismap|itemscope|loop|multiple|muted|no(?:resize|shade|validate|wrap)?|open|reversed|seamless|selected|sortable|truespeed|typemustmatch)$/; +var IE_VERSION = (WIN && WIN.document || {}).documentMode | 0; + +/** + * Check Check if the passed argument is undefined + * @param { String } value - + * @returns { Boolean } - + */ +function isBoolAttr(value) { + return RE_BOOL_ATTRS.test(value) +} + +/** + * Check if passed argument is a function + * @param { * } value - + * @returns { Boolean } - + */ +function isFunction(value) { + return typeof value === T_FUNCTION +} + +/** + * Check if passed argument is an object, exclude null + * NOTE: use isObject(x) && !isArray(x) to excludes arrays. + * @param { * } value - + * @returns { Boolean } - + */ +function isObject(value) { + return value && typeof value === T_OBJECT // typeof null is 'object' +} + +/** + * Check if passed argument is undefined + * @param { * } value - + * @returns { Boolean } - + */ +function isUndefined(value) { + return typeof value === T_UNDEF +} + +/** + * Check if passed argument is a string + * @param { * } value - + * @returns { Boolean } - + */ +function isString(value) { + return typeof value === T_STRING +} + +/** + * Check if passed argument is empty. Different from falsy, because we dont consider 0 or false to be blank + * @param { * } value - + * @returns { Boolean } - + */ +function isBlank(value) { + return isUndefined(value) || value === null || value === '' +} + +/** + * Check if passed argument is a kind of array + * @param { * } value - + * @returns { Boolean } - + */ +function isArray(value) { + return Array.isArray(value) || value instanceof Array +} + +/** + * Check whether object's property could be overridden + * @param { Object } obj - source object + * @param { String } key - object property + * @returns { Boolean } - + */ +function isWritable(obj, key) { + var descriptor = Object.getOwnPropertyDescriptor(obj, key); + return isUndefined(obj[key]) || descriptor && descriptor.writable +} + +/** + * Check if passed argument is a reserved name + * @param { String } value - + * @returns { Boolean } - + */ +function isReservedName(value) { + return RE_RESERVED_NAMES.test(value) +} + +var check = Object.freeze({ + isBoolAttr: isBoolAttr, + isFunction: isFunction, + isObject: isObject, + isUndefined: isUndefined, + isString: isString, + isBlank: isBlank, + isArray: isArray, + isWritable: isWritable, + isReservedName: isReservedName +}); + +/** + * Shorter and fast way to select multiple nodes in the DOM + * @param { String } selector - DOM selector + * @param { Object } ctx - DOM node where the targets of our search will is located + * @returns { Object } dom nodes found + */ +function $$(selector, ctx) { + return Array.prototype.slice.call((ctx || document).querySelectorAll(selector)) +} + +/** + * Shorter and fast way to select a single node in the DOM + * @param { String } selector - unique dom selector + * @param { Object } ctx - DOM node where the target of our search will is located + * @returns { Object } dom node found + */ +function $(selector, ctx) { + return (ctx || document).querySelector(selector) +} + +/** + * Create a document fragment + * @returns { Object } document fragment + */ +function createFrag() { + return document.createDocumentFragment() +} + +/** + * Create a document text node + * @returns { Object } create a text node to use as placeholder + */ +function createDOMPlaceholder() { + return document.createTextNode('') +} + +/** + * Check if a DOM node is an svg tag + * @param { HTMLElement } el - node we want to test + * @returns {Boolean} true if it's an svg node + */ +function isSvg(el) { + return !!el.ownerSVGElement +} + +/** + * Create a generic DOM node + * @param { String } name - name of the DOM node we want to create + * @param { Boolean } isSvg - true if we need to use an svg node + * @returns { Object } DOM node just created + */ +function mkEl(name) { + return name === 'svg' ? document.createElementNS(SVG_NS, name) : document.createElement(name) +} + +/** + * Set the inner html of any DOM node SVGs included + * @param { Object } container - DOM node where we'll inject new html + * @param { String } html - html to inject + */ +/* istanbul ignore next */ +function setInnerHTML(container, html) { + if (!isUndefined(container.innerHTML)) + { container.innerHTML = html; } + // some browsers do not support innerHTML on the SVGs tags + else { + var doc = new DOMParser().parseFromString(html, 'application/xml'); + var node = container.ownerDocument.importNode(doc.documentElement, true); + container.appendChild(node); + } +} + +/** + * Toggle the visibility of any DOM node + * @param { Object } dom - DOM node we want to hide + * @param { Boolean } show - do we want to show it? + */ + +function toggleVisibility(dom, show) { + dom.style.display = show ? '' : 'none'; + dom['hidden'] = show ? false : true; +} + +/** + * Remove any DOM attribute from a node + * @param { Object } dom - DOM node we want to update + * @param { String } name - name of the property we want to remove + */ +function remAttr(dom, name) { + dom.removeAttribute(name); +} + +/** + * Convert a style object to a string + * @param { Object } style - style object we need to parse + * @returns { String } resulting css string + * @example + * styleObjectToString({ color: 'red', height: '10px'}) // => 'color: red; height: 10px' + */ +function styleObjectToString(style) { + return Object.keys(style).reduce(function (acc, prop) { + return (acc + " " + prop + ": " + (style[prop]) + ";") + }, '') +} + +/** + * Get the value of any DOM attribute on a node + * @param { Object } dom - DOM node we want to parse + * @param { String } name - name of the attribute we want to get + * @returns { String | undefined } name of the node attribute whether it exists + */ +function getAttr(dom, name) { + return dom.getAttribute(name) +} + +/** + * Set any DOM attribute + * @param { Object } dom - DOM node we want to update + * @param { String } name - name of the property we want to set + * @param { String } val - value of the property we want to set + */ +function setAttr(dom, name, val) { + var xlink = XLINK_REGEX.exec(name); + if (xlink && xlink[1]) + { dom.setAttributeNS(XLINK_NS, xlink[1], val); } + else + { dom.setAttribute(name, val); } +} + +/** + * Insert safely a tag to fix #1962 #1649 + * @param { HTMLElement } root - children container + * @param { HTMLElement } curr - node to insert + * @param { HTMLElement } next - node that should preceed the current node inserted + */ +function safeInsert(root, curr, next) { + root.insertBefore(curr, next.parentNode && next); +} + +/** + * Minimize risk: only zero or one _space_ between attr & value + * @param { String } html - html string we want to parse + * @param { Function } fn - callback function to apply on any attribute found + */ +function walkAttrs(html, fn) { + if (!html) + { return } + var m; + while (m = RE_HTML_ATTRS.exec(html)) + { fn(m[1].toLowerCase(), m[2] || m[3] || m[4]); } +} + +/** + * Walk down recursively all the children tags starting dom node + * @param { Object } dom - starting node where we will start the recursion + * @param { Function } fn - callback to transform the child node just found + * @param { Object } context - fn can optionally return an object, which is passed to children + */ +function walkNodes(dom, fn, context) { + if (dom) { + var res = fn(dom, context); + var next; + // stop the recursion + if (res === false) { return } + + dom = dom.firstChild; + + while (dom) { + next = dom.nextSibling; + walkNodes(dom, fn, res); + dom = next; + } + } +} + +var dom = Object.freeze({ + $$: $$, + $: $, + createFrag: createFrag, + createDOMPlaceholder: createDOMPlaceholder, + isSvg: isSvg, + mkEl: mkEl, + setInnerHTML: setInnerHTML, + toggleVisibility: toggleVisibility, + remAttr: remAttr, + styleObjectToString: styleObjectToString, + getAttr: getAttr, + setAttr: setAttr, + safeInsert: safeInsert, + walkAttrs: walkAttrs, + walkNodes: walkNodes +}); + +var styleNode; +var cssTextProp; +var byName = {}; +var remainder = []; +var needsInject = false; + +// skip the following code on the server +if (WIN) { + styleNode = (function () { + // create a new style element with the correct type + var newNode = mkEl('style'); + setAttr(newNode, 'type', 'text/css'); + + // replace any user node or insert the new one into the head + var userNode = $('style[type=riot]'); + /* istanbul ignore next */ + if (userNode) { + if (userNode.id) { newNode.id = userNode.id; } + userNode.parentNode.replaceChild(newNode, userNode); + } + else { document.getElementsByTagName('head')[0].appendChild(newNode); } + + return newNode + })(); + cssTextProp = styleNode.styleSheet; +} + +/** + * Object that will be used to inject and manage the css of every tag instance + */ +var styleManager = { + styleNode: styleNode, + /** + * Save a tag style to be later injected into DOM + * @param { String } css - css string + * @param { String } name - if it's passed we will map the css to a tagname + */ + add: function add(css, name) { + if (name) { byName[name] = css; } + else { remainder.push(css); } + needsInject = true; + }, + /** + * Inject all previously saved tag styles into DOM + * innerHTML seems slow: http://jsperf.com/riot-insert-style + */ + inject: function inject() { + if (!WIN || !needsInject) { return } + needsInject = false; + var style = Object.keys(byName) + .map(function(k) { return byName[k] }) + .concat(remainder).join('\n'); + /* istanbul ignore next */ + if (cssTextProp) { cssTextProp.cssText = style; } + else { styleNode.innerHTML = style; } + } +}; + +/** + * The riot template engine + * @version v3.0.8 + */ + +var skipRegex = (function () { //eslint-disable-line no-unused-vars + + var beforeReChars = '[{(,;:?=|&!^~>%*/'; + + var beforeReWords = [ + 'case', + 'default', + 'do', + 'else', + 'in', + 'instanceof', + 'prefix', + 'return', + 'typeof', + 'void', + 'yield' + ]; + + var wordsLastChar = beforeReWords.reduce(function (s, w) { + return s + w.slice(-1) + }, ''); + + var RE_REGEX = /^\/(?=[^*>/])[^[/\\]*(?:(?:\\.|\[(?:\\.|[^\]\\]*)*\])[^[\\/]*)*?\/[gimuy]*/; + var RE_VN_CHAR = /[$\w]/; + + function prev (code, pos) { + while (--pos >= 0 && /\s/.test(code[pos])){ } + return pos + } + + function _skipRegex (code, start) { + + var re = /.*/g; + var pos = re.lastIndex = start++; + var match = re.exec(code)[0].match(RE_REGEX); + + if (match) { + var next = pos + match[0].length; + + pos = prev(code, pos); + var c = code[pos]; + + if (pos < 0 || ~beforeReChars.indexOf(c)) { + return next + } + + if (c === '.') { + + if (code[pos - 1] === '.') { + start = next; + } + + } else if (c === '+' || c === '-') { + + if (code[--pos] !== c || + (pos = prev(code, pos)) < 0 || + !RE_VN_CHAR.test(code[pos])) { + start = next; + } + + } else if (~wordsLastChar.indexOf(c)) { + + var end = pos + 1; + + while (--pos >= 0 && RE_VN_CHAR.test(code[pos])){ } + if (~beforeReWords.indexOf(code.slice(pos + 1, end))) { + start = next; + } + } + } + + return start + } + + return _skipRegex + +})(); + +/** + * riot.util.brackets + * + * - `brackets ` - Returns a string or regex based on its parameter + * - `brackets.set` - Change the current riot brackets + * + * @module + */ + +/* global riot */ + +var brackets = (function (UNDEF) { + + var + REGLOB = 'g', + + R_MLCOMMS = /\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//g, + + R_STRINGS = /"[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|`[^`\\]*(?:\\[\S\s][^`\\]*)*`/g, + + S_QBLOCKS = R_STRINGS.source + '|' + + /(?:\breturn\s+|(?:[$\w\)\]]|\+\+|--)\s*(\/)(?![*\/]))/.source + '|' + + /\/(?=[^*\/])[^[\/\\]*(?:(?:\[(?:\\.|[^\]\\]*)*\]|\\.)[^[\/\\]*)*?([^<]\/)[gim]*/.source, + + UNSUPPORTED = RegExp('[\\' + 'x00-\\x1F<>a-zA-Z0-9\'",;\\\\]'), + + NEED_ESCAPE = /(?=[[\]()*+?.^$|])/g, + + S_QBLOCK2 = R_STRINGS.source + '|' + /(\/)(?![*\/])/.source, + + FINDBRACES = { + '(': RegExp('([()])|' + S_QBLOCK2, REGLOB), + '[': RegExp('([[\\]])|' + S_QBLOCK2, REGLOB), + '{': RegExp('([{}])|' + S_QBLOCK2, REGLOB) + }, + + DEFAULT = '{ }'; + + var _pairs = [ + '{', '}', + '{', '}', + /{[^}]*}/, + /\\([{}])/g, + /\\({)|{/g, + RegExp('\\\\(})|([[({])|(})|' + S_QBLOCK2, REGLOB), + DEFAULT, + /^\s*{\^?\s*([$\w]+)(?:\s*,\s*(\S+))?\s+in\s+(\S.*)\s*}/, + /(^|[^\\]){=[\S\s]*?}/ + ]; + + var + cachedBrackets = UNDEF, + _regex, + _cache = [], + _settings; + + function _loopback (re) { return re } + + function _rewrite (re, bp) { + if (!bp) { bp = _cache; } + return new RegExp( + re.source.replace(/{/g, bp[2]).replace(/}/g, bp[3]), re.global ? REGLOB : '' + ) + } + + function _create (pair) { + if (pair === DEFAULT) { return _pairs } + + var arr = pair.split(' '); + + if (arr.length !== 2 || UNSUPPORTED.test(pair)) { + throw new Error('Unsupported brackets "' + pair + '"') + } + arr = arr.concat(pair.replace(NEED_ESCAPE, '\\').split(' ')); + + arr[4] = _rewrite(arr[1].length > 1 ? /{[\S\s]*?}/ : _pairs[4], arr); + arr[5] = _rewrite(pair.length > 3 ? /\\({|})/g : _pairs[5], arr); + arr[6] = _rewrite(_pairs[6], arr); + arr[7] = RegExp('\\\\(' + arr[3] + ')|([[({])|(' + arr[3] + ')|' + S_QBLOCK2, REGLOB); + arr[8] = pair; + return arr + } + + function _brackets (reOrIdx) { + return reOrIdx instanceof RegExp ? _regex(reOrIdx) : _cache[reOrIdx] + } + + _brackets.split = function split (str, tmpl, _bp) { + // istanbul ignore next: _bp is for the compiler + if (!_bp) { _bp = _cache; } + + var + parts = [], + match, + isexpr, + start, + pos, + re = _bp[6]; + + var qblocks = []; + var prevStr = ''; + var mark, lastIndex; + + isexpr = start = re.lastIndex = 0; + + while ((match = re.exec(str))) { + + lastIndex = re.lastIndex; + pos = match.index; + + if (isexpr) { + + if (match[2]) { + + var ch = match[2]; + var rech = FINDBRACES[ch]; + var ix = 1; + + rech.lastIndex = lastIndex; + while ((match = rech.exec(str))) { + if (match[1]) { + if (match[1] === ch) { ++ix; } + else if (!--ix) { break } + } else { + rech.lastIndex = pushQBlock(match.index, rech.lastIndex, match[2]); + } + } + re.lastIndex = ix ? str.length : rech.lastIndex; + continue + } + + if (!match[3]) { + re.lastIndex = pushQBlock(pos, lastIndex, match[4]); + continue + } + } + + if (!match[1]) { + unescapeStr(str.slice(start, pos)); + start = re.lastIndex; + re = _bp[6 + (isexpr ^= 1)]; + re.lastIndex = start; + } + } + + if (str && start < str.length) { + unescapeStr(str.slice(start)); + } + + parts.qblocks = qblocks; + + return parts + + function unescapeStr (s) { + if (prevStr) { + s = prevStr + s; + prevStr = ''; + } + if (tmpl || isexpr) { + parts.push(s && s.replace(_bp[5], '$1')); + } else { + parts.push(s); + } + } + + function pushQBlock(_pos, _lastIndex, slash) { //eslint-disable-line + if (slash) { + _lastIndex = skipRegex(str, _pos); + } + + if (tmpl && _lastIndex > _pos + 2) { + mark = '\u2057' + qblocks.length + '~'; + qblocks.push(str.slice(_pos, _lastIndex)); + prevStr += str.slice(start, _pos) + mark; + start = _lastIndex; + } + return _lastIndex + } + }; + + _brackets.hasExpr = function hasExpr (str) { + return _cache[4].test(str) + }; + + _brackets.loopKeys = function loopKeys (expr) { + var m = expr.match(_cache[9]); + + return m + ? { key: m[1], pos: m[2], val: _cache[0] + m[3].trim() + _cache[1] } + : { val: expr.trim() } + }; + + _brackets.array = function array (pair) { + return pair ? _create(pair) : _cache + }; + + function _reset (pair) { + if ((pair || (pair = DEFAULT)) !== _cache[8]) { + _cache = _create(pair); + _regex = pair === DEFAULT ? _loopback : _rewrite; + _cache[9] = _regex(_pairs[9]); + } + cachedBrackets = pair; + } + + function _setSettings (o) { + var b; + + o = o || {}; + b = o.brackets; + Object.defineProperty(o, 'brackets', { + set: _reset, + get: function () { return cachedBrackets }, + enumerable: true + }); + _settings = o; + _reset(b); + } + + Object.defineProperty(_brackets, 'settings', { + set: _setSettings, + get: function () { return _settings } + }); + + /* istanbul ignore next: in the browser riot is always in the scope */ + _brackets.settings = typeof riot !== 'undefined' && riot.settings || {}; + _brackets.set = _reset; + _brackets.skipRegex = skipRegex; + + _brackets.R_STRINGS = R_STRINGS; + _brackets.R_MLCOMMS = R_MLCOMMS; + _brackets.S_QBLOCKS = S_QBLOCKS; + _brackets.S_QBLOCK2 = S_QBLOCK2; + + return _brackets + +})(); + +/** + * @module tmpl + * + * tmpl - Root function, returns the template value, render with data + * tmpl.hasExpr - Test the existence of a expression inside a string + * tmpl.loopKeys - Get the keys for an 'each' loop (used by `_each`) + */ + +var tmpl = (function () { + + var _cache = {}; + + function _tmpl (str, data) { + if (!str) { return str } + + return (_cache[str] || (_cache[str] = _create(str))).call( + data, _logErr.bind({ + data: data, + tmpl: str + }) + ) + } + + _tmpl.hasExpr = brackets.hasExpr; + + _tmpl.loopKeys = brackets.loopKeys; + + // istanbul ignore next + _tmpl.clearCache = function () { _cache = {}; }; + + _tmpl.errorHandler = null; + + function _logErr (err, ctx) { + + err.riotData = { + tagName: ctx && ctx.__ && ctx.__.tagName, + _riot_id: ctx && ctx._riot_id //eslint-disable-line camelcase + }; + + if (_tmpl.errorHandler) { _tmpl.errorHandler(err); } + else if ( + typeof console !== 'undefined' && + typeof console.error === 'function' + ) { + console.error(err.message); + console.log('<%s> %s', err.riotData.tagName || 'Unknown tag', this.tmpl); // eslint-disable-line + console.log(this.data); // eslint-disable-line + } + } + + function _create (str) { + var expr = _getTmpl(str); + + if (expr.slice(0, 11) !== 'try{return ') { expr = 'return ' + expr; } + + return new Function('E', expr + ';') // eslint-disable-line no-new-func + } + + var RE_DQUOTE = /\u2057/g; + var RE_QBMARK = /\u2057(\d+)~/g; + + function _getTmpl (str) { + var parts = brackets.split(str.replace(RE_DQUOTE, '"'), 1); + var qstr = parts.qblocks; + var expr; + + if (parts.length > 2 || parts[0]) { + var i, j, list = []; + + for (i = j = 0; i < parts.length; ++i) { + + expr = parts[i]; + + if (expr && (expr = i & 1 + + ? _parseExpr(expr, 1, qstr) + + : '"' + expr + .replace(/\\/g, '\\\\') + .replace(/\r\n?|\n/g, '\\n') + .replace(/"/g, '\\"') + + '"' + + )) { list[j++] = expr; } + + } + + expr = j < 2 ? list[0] + : '[' + list.join(',') + '].join("")'; + + } else { + + expr = _parseExpr(parts[1], 0, qstr); + } + + if (qstr.length) { + expr = expr.replace(RE_QBMARK, function (_, pos) { + return qstr[pos] + .replace(/\r/g, '\\r') + .replace(/\n/g, '\\n') + }); + } + return expr + } + + var RE_CSNAME = /^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\u2057(\d+)~):/; + var + RE_BREND = { + '(': /[()]/g, + '[': /[[\]]/g, + '{': /[{}]/g + }; + + function _parseExpr (expr, asText, qstr) { + + expr = expr + .replace(/\s+/g, ' ').trim() + .replace(/\ ?([[\({},?\.:])\ ?/g, '$1'); + + if (expr) { + var + list = [], + cnt = 0, + match; + + while (expr && + (match = expr.match(RE_CSNAME)) && + !match.index + ) { + var + key, + jsb, + re = /,|([[{(])|$/g; + + expr = RegExp.rightContext; + key = match[2] ? qstr[match[2]].slice(1, -1).trim().replace(/\s+/g, ' ') : match[1]; + + while (jsb = (match = re.exec(expr))[1]) { skipBraces(jsb, re); } + + jsb = expr.slice(0, match.index); + expr = RegExp.rightContext; + + list[cnt++] = _wrapExpr(jsb, 1, key); + } + + expr = !cnt ? _wrapExpr(expr, asText) + : cnt > 1 ? '[' + list.join(',') + '].join(" ").trim()' : list[0]; + } + return expr + + function skipBraces (ch, re) { + var + mm, + lv = 1, + ir = RE_BREND[ch]; + + ir.lastIndex = re.lastIndex; + while (mm = ir.exec(expr)) { + if (mm[0] === ch) { ++lv; } + else if (!--lv) { break } + } + re.lastIndex = lv ? expr.length : ir.lastIndex; + } + } + + // istanbul ignore next: not both + var // eslint-disable-next-line max-len + JS_CONTEXT = '"in this?this:' + (typeof window !== 'object' ? 'global' : 'window') + ').', + JS_VARNAME = /[,{][\$\w]+(?=:)|(^ *|[^$\w\.{])(?!(?:typeof|true|false|null|undefined|in|instanceof|is(?:Finite|NaN)|void|NaN|new|Date|RegExp|Math)(?![$\w]))([$_A-Za-z][$\w]*)/g, + JS_NOPROPS = /^(?=(\.[$\w]+))\1(?:[^.[(]|$)/; + + function _wrapExpr (expr, asText, key) { + var tb; + + expr = expr.replace(JS_VARNAME, function (match, p, mvar, pos, s) { + if (mvar) { + pos = tb ? 0 : pos + match.length; + + if (mvar !== 'this' && mvar !== 'global' && mvar !== 'window') { + match = p + '("' + mvar + JS_CONTEXT + mvar; + if (pos) { tb = (s = s[pos]) === '.' || s === '(' || s === '['; } + } else if (pos) { + tb = !JS_NOPROPS.test(s.slice(pos)); + } + } + return match + }); + + if (tb) { + expr = 'try{return ' + expr + '}catch(e){E(e,this)}'; + } + + if (key) { + + expr = (tb + ? 'function(){' + expr + '}.call(this)' : '(' + expr + ')' + ) + '?"' + key + '":""'; + + } else if (asText) { + + expr = 'function(v){' + (tb + ? expr.replace('return ', 'v=') : 'v=(' + expr + ')' + ) + ';return v||v===0?v:""}.call(this)'; + } + + return expr + } + + _tmpl.version = brackets.version = 'v3.0.8'; + + return _tmpl + +})(); + +var observable$1 = function(el) { + + /** + * Extend the original object or create a new empty one + * @type { Object } + */ + + el = el || {}; + + /** + * Private variables + */ + var callbacks = {}, + slice = Array.prototype.slice; + + /** + * Public Api + */ + + // extend the el object adding the observable methods + Object.defineProperties(el, { + /** + * Listen to the given `event` ands + * execute the `callback` each time an event is triggered. + * @param { String } event - event id + * @param { Function } fn - callback function + * @returns { Object } el + */ + on: { + value: function(event, fn) { + if (typeof fn == 'function') + { (callbacks[event] = callbacks[event] || []).push(fn); } + return el + }, + enumerable: false, + writable: false, + configurable: false + }, + + /** + * Removes the given `event` listeners + * @param { String } event - event id + * @param { Function } fn - callback function + * @returns { Object } el + */ + off: { + value: function(event, fn) { + if (event == '*' && !fn) { callbacks = {}; } + else { + if (fn) { + var arr = callbacks[event]; + for (var i = 0, cb; cb = arr && arr[i]; ++i) { + if (cb == fn) { arr.splice(i--, 1); } + } + } else { delete callbacks[event]; } + } + return el + }, + enumerable: false, + writable: false, + configurable: false + }, + + /** + * Listen to the given `event` and + * execute the `callback` at most once + * @param { String } event - event id + * @param { Function } fn - callback function + * @returns { Object } el + */ + one: { + value: function(event, fn) { + function on() { + el.off(event, on); + fn.apply(el, arguments); + } + return el.on(event, on) + }, + enumerable: false, + writable: false, + configurable: false + }, + + /** + * Execute all callback functions that listen to + * the given `event` + * @param { String } event - event id + * @returns { Object } el + */ + trigger: { + value: function(event) { + var arguments$1 = arguments; + + + // getting the arguments + var arglen = arguments.length - 1, + args = new Array(arglen), + fns, + fn, + i; + + for (i = 0; i < arglen; i++) { + args[i] = arguments$1[i + 1]; // skip first argument + } + + fns = slice.call(callbacks[event] || [], 0); + + for (i = 0; fn = fns[i]; ++i) { + fn.apply(el, args); + } + + if (callbacks['*'] && event != '*') + { el.trigger.apply(el, ['*', event].concat(args)); } + + return el + }, + enumerable: false, + writable: false, + configurable: false + } + }); + + return el + +}; + +/** + * Specialized function for looping an array-like collection with `each={}` + * @param { Array } list - collection of items + * @param {Function} fn - callback function + * @returns { Array } the array looped + */ +function each(list, fn) { + var len = list ? list.length : 0; + var i = 0; + for (; i < len; ++i) { + fn(list[i], i); + } + return list +} + +/** + * Check whether an array contains an item + * @param { Array } array - target array + * @param { * } item - item to test + * @returns { Boolean } - + */ +function contains(array, item) { + return array.indexOf(item) !== -1 +} + +/** + * Convert a string containing dashes to camel case + * @param { String } str - input string + * @returns { String } my-string -> myString + */ +function toCamel(str) { + return str.replace(/-(\w)/g, function (_, c) { return c.toUpperCase(); }) +} + +/** + * Faster String startsWith alternative + * @param { String } str - source string + * @param { String } value - test string + * @returns { Boolean } - + */ +function startsWith(str, value) { + return str.slice(0, value.length) === value +} + +/** + * Helper function to set an immutable property + * @param { Object } el - object where the new property will be set + * @param { String } key - object key where the new property will be stored + * @param { * } value - value of the new property + * @param { Object } options - set the propery overriding the default options + * @returns { Object } - the initial object + */ +function defineProperty(el, key, value, options) { + Object.defineProperty(el, key, extend({ + value: value, + enumerable: false, + writable: false, + configurable: true + }, options)); + return el +} + +/** + * Extend any object with other properties + * @param { Object } src - source object + * @returns { Object } the resulting extended object + * + * var obj = { foo: 'baz' } + * extend(obj, {bar: 'bar', foo: 'bar'}) + * console.log(obj) => {bar: 'bar', foo: 'bar'} + * + */ +function extend(src) { + var obj, args = arguments; + for (var i = 1; i < args.length; ++i) { + if (obj = args[i]) { + for (var key in obj) { + // check if this property of the source object could be overridden + if (isWritable(src, key)) + { src[key] = obj[key]; } + } + } + } + return src +} + +var misc = Object.freeze({ + each: each, + contains: contains, + toCamel: toCamel, + startsWith: startsWith, + defineProperty: defineProperty, + extend: extend +}); + +var settings$1 = extend(Object.create(brackets.settings), { + skipAnonymousTags: true, + // handle the auto updates on any DOM event + autoUpdate: true +}); + +/** + * Trigger DOM events + * @param { HTMLElement } dom - dom element target of the event + * @param { Function } handler - user function + * @param { Object } e - event object + */ +function handleEvent(dom, handler, e) { + var ptag = this.__.parent, + item = this.__.item; + + if (!item) + { while (ptag && !item) { + item = ptag.__.item; + ptag = ptag.__.parent; + } } + + // override the event properties + /* istanbul ignore next */ + if (isWritable(e, 'currentTarget')) { e.currentTarget = dom; } + /* istanbul ignore next */ + if (isWritable(e, 'target')) { e.target = e.srcElement; } + /* istanbul ignore next */ + if (isWritable(e, 'which')) { e.which = e.charCode || e.keyCode; } + + e.item = item; + + handler.call(this, e); + + // avoid auto updates + if (!settings$1.autoUpdate) { return } + + if (!e.preventUpdate) { + var p = getImmediateCustomParentTag(this); + // fixes #2083 + if (p.isMounted) { p.update(); } + } +} + +/** + * Attach an event to a DOM node + * @param { String } name - event name + * @param { Function } handler - event callback + * @param { Object } dom - dom node + * @param { Tag } tag - tag instance + */ +function setEventHandler(name, handler, dom, tag) { + var eventName, + cb = handleEvent.bind(tag, dom, handler); + + // avoid to bind twice the same event + // possible fix for #2332 + dom[name] = null; + + // normalize event name + eventName = name.replace(RE_EVENTS_PREFIX, ''); + + // cache the listener into the listeners array + if (!contains(tag.__.listeners, dom)) { tag.__.listeners.push(dom); } + if (!dom[RIOT_EVENTS_KEY]) { dom[RIOT_EVENTS_KEY] = {}; } + if (dom[RIOT_EVENTS_KEY][name]) { dom.removeEventListener(eventName, dom[RIOT_EVENTS_KEY][name]); } + + dom[RIOT_EVENTS_KEY][name] = cb; + dom.addEventListener(eventName, cb, false); +} + +/** + * Update dynamically created data-is tags with changing expressions + * @param { Object } expr - expression tag and expression info + * @param { Tag } parent - parent for tag creation + * @param { String } tagName - tag implementation we want to use + */ +function updateDataIs(expr, parent, tagName) { + var conf, isVirtual, head, ref; + + if (expr.tag && expr.tagName === tagName) { + expr.tag.update(); + return + } + + isVirtual = expr.dom.tagName === 'VIRTUAL'; + // sync _parent to accommodate changing tagnames + if (expr.tag) { + // need placeholder before unmount + if(isVirtual) { + head = expr.tag.__.head; + ref = createDOMPlaceholder(); + head.parentNode.insertBefore(ref, head); + } + + expr.tag.unmount(true); + } + + if (!isString(tagName)) { return } + + expr.impl = __TAG_IMPL[tagName]; + conf = {root: expr.dom, parent: parent, hasImpl: true, tagName: tagName}; + expr.tag = initChildTag(expr.impl, conf, expr.dom.innerHTML, parent); + each(expr.attrs, function (a) { return setAttr(expr.tag.root, a.name, a.value); }); + expr.tagName = tagName; + expr.tag.mount(); + if (isVirtual) + { makeReplaceVirtual(expr.tag, ref || expr.tag.root); } // root exist first time, after use placeholder + + // parent is the placeholder tag, not the dynamic tag so clean up + parent.__.onUnmount = function() { + var delName = expr.tag.opts.dataIs, + tags = expr.tag.parent.tags, + _tags = expr.tag.__.parent.tags; + arrayishRemove(tags, delName, expr.tag); + arrayishRemove(_tags, delName, expr.tag); + expr.tag.unmount(); + }; +} + +/** + * Nomalize any attribute removing the "riot-" prefix + * @param { String } attrName - original attribute name + * @returns { String } valid html attribute name + */ +function normalizeAttrName(attrName) { + if (!attrName) { return null } + attrName = attrName.replace(ATTRS_PREFIX, ''); + if (CASE_SENSITIVE_ATTRIBUTES[attrName]) { attrName = CASE_SENSITIVE_ATTRIBUTES[attrName]; } + return attrName +} + +/** + * Update on single tag expression + * @this Tag + * @param { Object } expr - expression logic + * @returns { undefined } + */ +function updateExpression(expr) { + if (this.root && getAttr(this.root,'virtualized')) { return } + + var dom = expr.dom, + // remove the riot- prefix + attrName = normalizeAttrName(expr.attr), + isToggle = contains([SHOW_DIRECTIVE, HIDE_DIRECTIVE], attrName), + isVirtual = expr.root && expr.root.tagName === 'VIRTUAL', + parent = dom && (expr.parent || dom.parentNode), + // detect the style attributes + isStyleAttr = attrName === 'style', + isClassAttr = attrName === 'class', + hasValue, + isObj, + value; + + // if it's a tag we could totally skip the rest + if (expr._riot_id) { + if (expr.isMounted) { + expr.update(); + // if it hasn't been mounted yet, do that now. + } else { + expr.mount(); + if (isVirtual) { + makeReplaceVirtual(expr, expr.root); + } + } + return + } + // if this expression has the update method it means it can handle the DOM changes by itself + if (expr.update) { return expr.update() } + + // ...it seems to be a simple expression so we try to calculat its value + value = tmpl(expr.expr, isToggle ? extend({}, Object.create(this.parent), this) : this); + hasValue = !isBlank(value); + isObj = isObject(value); + + // convert the style/class objects to strings + if (isObj) { + isObj = !isClassAttr && !isStyleAttr; + if (isClassAttr) { + value = tmpl(JSON.stringify(value), this); + } else if (isStyleAttr) { + value = styleObjectToString(value); + } + } + + // remove original attribute + if (expr.attr && (!expr.isAttrRemoved || !hasValue || value === false)) { + remAttr(dom, expr.attr); + expr.isAttrRemoved = true; + } + + // for the boolean attributes we don't need the value + // we can convert it to checked=true to checked=checked + if (expr.bool) { value = value ? attrName : false; } + if (expr.isRtag) { return updateDataIs(expr, this, value) } + if (expr.wasParsedOnce && expr.value === value) { return } + + // update the expression value + expr.value = value; + expr.wasParsedOnce = true; + + // if the value is an object we can not do much more with it + if (isObj && !isToggle) { return } + // avoid to render undefined/null values + if (isBlank(value)) { value = ''; } + + // textarea and text nodes have no attribute name + if (!attrName) { + // about #815 w/o replace: the browser converts the value to a string, + // the comparison by "==" does too, but not in the server + value += ''; + // test for parent avoids error with invalid assignment to nodeValue + if (parent) { + // cache the parent node because somehow it will become null on IE + // on the next iteration + expr.parent = parent; + if (parent.tagName === 'TEXTAREA') { + parent.value = value; // #1113 + if (!IE_VERSION) { dom.nodeValue = value; } // #1625 IE throws here, nodeValue + } // will be available on 'updated' + else { dom.nodeValue = value; } + } + return + } + + + // event handler + if (isFunction(value)) { + setEventHandler(attrName, value, dom, this); + // show / hide + } else if (isToggle) { + toggleVisibility(dom, attrName === HIDE_DIRECTIVE ? !value : value); + // handle attributes + } else { + if (expr.bool) { + dom[attrName] = value; + } + + if (attrName === 'value' && dom.value !== value) { + dom.value = value; + } + + if (hasValue && value !== false) { + setAttr(dom, attrName, value); + } + + // make sure that in case of style changes + // the element stays hidden + if (isStyleAttr && dom.hidden) { toggleVisibility(dom, false); } + } +} + +/** + * Update all the expressions in a Tag instance + * @this Tag + * @param { Array } expressions - expression that must be re evaluated + */ +function updateAllExpressions(expressions) { + each(expressions, updateExpression.bind(this)); +} + +var IfExpr = { + init: function init(dom, tag, expr) { + remAttr(dom, CONDITIONAL_DIRECTIVE); + this.tag = tag; + this.expr = expr; + this.stub = createDOMPlaceholder(); + this.pristine = dom; + + var p = dom.parentNode; + p.insertBefore(this.stub, dom); + p.removeChild(dom); + + return this + }, + update: function update() { + this.value = tmpl(this.expr, this.tag); + + if (this.value && !this.current) { // insert + this.current = this.pristine.cloneNode(true); + this.stub.parentNode.insertBefore(this.current, this.stub); + this.expressions = []; + parseExpressions.apply(this.tag, [this.current, this.expressions, true]); + } else if (!this.value && this.current) { // remove + unmountAll(this.expressions); + if (this.current._tag) { + this.current._tag.unmount(); + } else if (this.current.parentNode) { + this.current.parentNode.removeChild(this.current); + } + this.current = null; + this.expressions = []; + } + + if (this.value) { updateAllExpressions.call(this.tag, this.expressions); } + }, + unmount: function unmount() { + unmountAll(this.expressions || []); + } +}; + +var RefExpr = { + init: function init(dom, parent, attrName, attrValue) { + this.dom = dom; + this.attr = attrName; + this.rawValue = attrValue; + this.parent = parent; + this.hasExp = tmpl.hasExpr(attrValue); + return this + }, + update: function update() { + var old = this.value; + var customParent = this.parent && getImmediateCustomParentTag(this.parent); + // if the referenced element is a custom tag, then we set the tag itself, rather than DOM + var tagOrDom = this.dom.__ref || this.tag || this.dom; + + this.value = this.hasExp ? tmpl(this.rawValue, this.parent) : this.rawValue; + + // the name changed, so we need to remove it from the old key (if present) + if (!isBlank(old) && customParent) { arrayishRemove(customParent.refs, old, tagOrDom); } + if (!isBlank(this.value) && isString(this.value)) { + // add it to the refs of parent tag (this behavior was changed >=3.0) + if (customParent) { arrayishAdd( + customParent.refs, + this.value, + tagOrDom, + // use an array if it's a looped node and the ref is not an expression + null, + this.parent.__.index + ); } + + if (this.value !== old) { + setAttr(this.dom, this.attr, this.value); + } + } else { + remAttr(this.dom, this.attr); + } + + // cache the ref bound to this dom node + // to reuse it in future (see also #2329) + if (!this.dom.__ref) { this.dom.__ref = tagOrDom; } + }, + unmount: function unmount() { + var tagOrDom = this.tag || this.dom; + var customParent = this.parent && getImmediateCustomParentTag(this.parent); + if (!isBlank(this.value) && customParent) + { arrayishRemove(customParent.refs, this.value, tagOrDom); } + } +}; + +/** + * Convert the item looped into an object used to extend the child tag properties + * @param { Object } expr - object containing the keys used to extend the children tags + * @param { * } key - value to assign to the new object returned + * @param { * } val - value containing the position of the item in the array + * @param { Object } base - prototype object for the new item + * @returns { Object } - new object containing the values of the original item + * + * The variables 'key' and 'val' are arbitrary. + * They depend on the collection type looped (Array, Object) + * and on the expression used on the each tag + * + */ +function mkitem(expr, key, val, base) { + var item = base ? Object.create(base) : {}; + item[expr.key] = key; + if (expr.pos) { item[expr.pos] = val; } + return item +} + +/** + * Unmount the redundant tags + * @param { Array } items - array containing the current items to loop + * @param { Array } tags - array containing all the children tags + */ +function unmountRedundant(items, tags) { + var i = tags.length, + j = items.length; + + while (i > j) { + i--; + remove.apply(tags[i], [tags, i]); + } +} + + +/** + * Remove a child tag + * @this Tag + * @param { Array } tags - tags collection + * @param { Number } i - index of the tag to remove + */ +function remove(tags, i) { + tags.splice(i, 1); + this.unmount(); + arrayishRemove(this.parent, this, this.__.tagName, true); +} + +/** + * Move the nested custom tags in non custom loop tags + * @this Tag + * @param { Number } i - current position of the loop tag + */ +function moveNestedTags(i) { + var this$1 = this; + + each(Object.keys(this.tags), function (tagName) { + moveChildTag.apply(this$1.tags[tagName], [tagName, i]); + }); +} + +/** + * Move a child tag + * @this Tag + * @param { HTMLElement } root - dom node containing all the loop children + * @param { Tag } nextTag - instance of the next tag preceding the one we want to move + * @param { Boolean } isVirtual - is it a virtual tag? + */ +function move(root, nextTag, isVirtual) { + if (isVirtual) + { moveVirtual.apply(this, [root, nextTag]); } + else + { safeInsert(root, this.root, nextTag.root); } +} + +/** + * Insert and mount a child tag + * @this Tag + * @param { HTMLElement } root - dom node containing all the loop children + * @param { Tag } nextTag - instance of the next tag preceding the one we want to insert + * @param { Boolean } isVirtual - is it a virtual tag? + */ +function insert(root, nextTag, isVirtual) { + if (isVirtual) + { makeVirtual.apply(this, [root, nextTag]); } + else + { safeInsert(root, this.root, nextTag.root); } +} + +/** + * Append a new tag into the DOM + * @this Tag + * @param { HTMLElement } root - dom node containing all the loop children + * @param { Boolean } isVirtual - is it a virtual tag? + */ +function append(root, isVirtual) { + if (isVirtual) + { makeVirtual.call(this, root); } + else + { root.appendChild(this.root); } +} + +/** + * Manage tags having the 'each' + * @param { HTMLElement } dom - DOM node we need to loop + * @param { Tag } parent - parent tag instance where the dom node is contained + * @param { String } expr - string contained in the 'each' attribute + * @returns { Object } expression object for this each loop + */ +function _each(dom, parent, expr) { + + // remove the each property from the original tag + remAttr(dom, LOOP_DIRECTIVE); + + var mustReorder = typeof getAttr(dom, LOOP_NO_REORDER_DIRECTIVE) !== T_STRING || remAttr(dom, LOOP_NO_REORDER_DIRECTIVE), + tagName = getTagName(dom), + impl = __TAG_IMPL[tagName], + parentNode = dom.parentNode, + placeholder = createDOMPlaceholder(), + child = getTag(dom), + ifExpr = getAttr(dom, CONDITIONAL_DIRECTIVE), + tags = [], + oldItems = [], + hasKeys, + isLoop = true, + isAnonymous = !__TAG_IMPL[tagName], + isVirtual = dom.tagName === 'VIRTUAL'; + + // parse the each expression + expr = tmpl.loopKeys(expr); + expr.isLoop = true; + + if (ifExpr) { remAttr(dom, CONDITIONAL_DIRECTIVE); } + + // insert a marked where the loop tags will be injected + parentNode.insertBefore(placeholder, dom); + parentNode.removeChild(dom); + + expr.update = function updateEach() { + // get the new items collection + expr.value = tmpl(expr.val, parent); + + var frag = createFrag(), + items = expr.value, + isObject$$1 = !isArray(items) && !isString(items), + root = placeholder.parentNode; + + // if this DOM was removed the update here is useless + // this condition fixes also a weird async issue on IE in our unit test + if (!root) { return } + + // object loop. any changes cause full redraw + if (isObject$$1) { + hasKeys = items || false; + items = hasKeys ? + Object.keys(items).map(function (key) { + return mkitem(expr, items[key], key) + }) : []; + } else { + hasKeys = false; + } + + if (ifExpr) { + items = items.filter(function(item, i) { + if (expr.key && !isObject$$1) + { return !!tmpl(ifExpr, mkitem(expr, item, i, parent)) } + + return !!tmpl(ifExpr, extend(Object.create(parent), item)) + }); + } + + // loop all the new items + each(items, function(item, i) { + // reorder only if the items are objects + var + doReorder = mustReorder && typeof item === T_OBJECT && !hasKeys, + oldPos = oldItems.indexOf(item), + isNew = oldPos === -1, + pos = !isNew && doReorder ? oldPos : i, + // does a tag exist in this position? + tag = tags[pos], + mustAppend = i >= oldItems.length, + mustCreate = doReorder && isNew || !doReorder && !tag; + + item = !hasKeys && expr.key ? mkitem(expr, item, i) : item; + + // new tag + if (mustCreate) { + tag = new Tag$1(impl, { + parent: parent, + isLoop: isLoop, + isAnonymous: isAnonymous, + tagName: tagName, + root: dom.cloneNode(isAnonymous), + item: item, + index: i, + }, dom.innerHTML); + + // mount the tag + tag.mount(); + + if (mustAppend) + { append.apply(tag, [frag || root, isVirtual]); } + else + { insert.apply(tag, [root, tags[i], isVirtual]); } + + if (!mustAppend) { oldItems.splice(i, 0, item); } + tags.splice(i, 0, tag); + if (child) { arrayishAdd(parent.tags, tagName, tag, true); } + } else if (pos !== i && doReorder) { + // move + if (contains(items, oldItems[pos])) { + move.apply(tag, [root, tags[i], isVirtual]); + // move the old tag instance + tags.splice(i, 0, tags.splice(pos, 1)[0]); + // move the old item + oldItems.splice(i, 0, oldItems.splice(pos, 1)[0]); + } + + // update the position attribute if it exists + if (expr.pos) { tag[expr.pos] = i; } + + // if the loop tags are not custom + // we need to move all their custom tags into the right position + if (!child && tag.tags) { moveNestedTags.call(tag, i); } + } + + // cache the original item to use it in the events bound to this node + // and its children + tag.__.item = item; + tag.__.index = i; + tag.__.parent = parent; + + if (!mustCreate) { tag.update(item); } + }); + + // remove the redundant tags + unmountRedundant(items, tags); + + // clone the items array + oldItems = items.slice(); + + // this condition is weird u + root.insertBefore(frag, placeholder); + }; + + expr.unmount = function() { + each(tags, function(t) { t.unmount(); }); + }; + + return expr +} + +/** + * Walk the tag DOM to detect the expressions to evaluate + * @this Tag + * @param { HTMLElement } root - root tag where we will start digging the expressions + * @param { Array } expressions - empty array where the expressions will be added + * @param { Boolean } mustIncludeRoot - flag to decide whether the root must be parsed as well + * @returns { Object } an object containing the root noode and the dom tree + */ +function parseExpressions(root, expressions, mustIncludeRoot) { + var this$1 = this; + + var tree = {parent: {children: expressions}}; + + walkNodes(root, function (dom, ctx) { + var type = dom.nodeType, parent = ctx.parent, attr, expr, tagImpl; + if (!mustIncludeRoot && dom === root) { return {parent: parent} } + + // text node + if (type === 3 && dom.parentNode.tagName !== 'STYLE' && tmpl.hasExpr(dom.nodeValue)) + { parent.children.push({dom: dom, expr: dom.nodeValue}); } + + if (type !== 1) { return ctx } // not an element + + var isVirtual = dom.tagName === 'VIRTUAL'; + + // loop. each does it's own thing (for now) + if (attr = getAttr(dom, LOOP_DIRECTIVE)) { + if(isVirtual) { setAttr(dom, 'loopVirtual', true); } // ignore here, handled in _each + parent.children.push(_each(dom, this$1, attr)); + return false + } + + // if-attrs become the new parent. Any following expressions (either on the current + // element, or below it) become children of this expression. + if (attr = getAttr(dom, CONDITIONAL_DIRECTIVE)) { + parent.children.push(Object.create(IfExpr).init(dom, this$1, attr)); + return false + } + + if (expr = getAttr(dom, IS_DIRECTIVE)) { + if (tmpl.hasExpr(expr)) { + parent.children.push({isRtag: true, expr: expr, dom: dom, attrs: [].slice.call(dom.attributes)}); + return false + } + } + + // if this is a tag, stop traversing here. + // we ignore the root, since parseExpressions is called while we're mounting that root + tagImpl = getTag(dom); + if(isVirtual) { + if(getAttr(dom, 'virtualized')) {dom.parentElement.removeChild(dom); } // tag created, remove from dom + if(!tagImpl && !getAttr(dom, 'virtualized') && !getAttr(dom, 'loopVirtual')) // ok to create virtual tag + { tagImpl = { tmpl: dom.outerHTML }; } + } + + if (tagImpl && (dom !== root || mustIncludeRoot)) { + if(isVirtual && !getAttr(dom, IS_DIRECTIVE)) { // handled in update + // can not remove attribute like directives + // so flag for removal after creation to prevent maximum stack error + setAttr(dom, 'virtualized', true); + + var tag = new Tag$1({ tmpl: dom.outerHTML }, + {root: dom, parent: this$1}, + dom.innerHTML); + parent.children.push(tag); // no return, anonymous tag, keep parsing + } else { + var conf = {root: dom, parent: this$1, hasImpl: true}; + parent.children.push(initChildTag(tagImpl, conf, dom.innerHTML, this$1)); + return false + } + } + + // attribute expressions + parseAttributes.apply(this$1, [dom, dom.attributes, function(attr, expr) { + if (!expr) { return } + parent.children.push(expr); + }]); + + // whatever the parent is, all child elements get the same parent. + // If this element had an if-attr, that's the parent for all child elements + return {parent: parent} + }, tree); +} + +/** + * Calls `fn` for every attribute on an element. If that attr has an expression, + * it is also passed to fn. + * @this Tag + * @param { HTMLElement } dom - dom node to parse + * @param { Array } attrs - array of attributes + * @param { Function } fn - callback to exec on any iteration + */ +function parseAttributes(dom, attrs, fn) { + var this$1 = this; + + each(attrs, function (attr) { + if (!attr) { return false } + + var name = attr.name, bool = isBoolAttr(name), expr; + + if (contains(REF_DIRECTIVES, name)) { + expr = Object.create(RefExpr).init(dom, this$1, name, attr.value); + } else if (tmpl.hasExpr(attr.value)) { + expr = {dom: dom, expr: attr.value, attr: name, bool: bool}; + } + + fn(attr, expr); + }); +} + +/* + Includes hacks needed for the Internet Explorer version 9 and below + See: http://kangax.github.io/compat-table/es5/#ie8 + http://codeplanet.io/dropping-ie8/ +*/ + +var reHasYield = /|>([\S\s]*?)<\/yield\s*>|>)/ig; +var reYieldSrc = /]*)['"]\s*>([\S\s]*?)<\/yield\s*>/ig; +var reYieldDest = /|>([\S\s]*?)<\/yield\s*>)/ig; +var rootEls = { tr: 'tbody', th: 'tr', td: 'tr', col: 'colgroup' }; +var tblTags = IE_VERSION && IE_VERSION < 10 ? RE_SPECIAL_TAGS : RE_SPECIAL_TAGS_NO_OPTION; +var GENERIC = 'div'; +var SVG = 'svg'; + + +/* + Creates the root element for table or select child elements: + tr/th/td/thead/tfoot/tbody/caption/col/colgroup/option/optgroup +*/ +function specialTags(el, tmpl, tagName) { + + var + select = tagName[0] === 'o', + parent = select ? 'select>' : 'table>'; + + // trim() is important here, this ensures we don't have artifacts, + // so we can check if we have only one element inside the parent + el.innerHTML = '<' + parent + tmpl.trim() + '= 0 && length <= MAX_ARRAY_INDEX; + }; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles raw objects in addition to array-likes. Treats all + // sparse array-likes as if they were dense. + _.each = _.forEach = function(obj, iteratee, context) { + iteratee = optimizeCb(iteratee, context); + var i, length; + if (isArrayLike(obj)) { + for (i = 0, length = obj.length; i < length; i++) { + iteratee(obj[i], i, obj); + } + } else { + var keys = _.keys(obj); + for (i = 0, length = keys.length; i < length; i++) { + iteratee(obj[keys[i]], keys[i], obj); + } + } + return obj; + }; + + // Return the results of applying the iteratee to each element. + _.map = _.collect = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length, + results = Array(length); + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + results[index] = iteratee(obj[currentKey], currentKey, obj); + } + return results; + }; + + // Create a reducing function iterating left or right. + function createReduce(dir) { + // Optimized iterator function as using arguments.length + // in the main function will deoptimize the, see #1991. + function iterator(obj, iteratee, memo, keys, index, length) { + for (; index >= 0 && index < length; index += dir) { + var currentKey = keys ? keys[index] : index; + memo = iteratee(memo, obj[currentKey], currentKey, obj); + } + return memo; + } + + return function(obj, iteratee, memo, context) { + iteratee = optimizeCb(iteratee, context, 4); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length, + index = dir > 0 ? 0 : length - 1; + // Determine the initial value if none is provided. + if (arguments.length < 3) { + memo = obj[keys ? keys[index] : index]; + index += dir; + } + return iterator(obj, iteratee, memo, keys, index, length); + }; + } + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. + _.reduce = _.foldl = _.inject = createReduce(1); + + // The right-associative version of reduce, also known as `foldr`. + _.reduceRight = _.foldr = createReduce(-1); + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, predicate, context) { + var key; + if (isArrayLike(obj)) { + key = _.findIndex(obj, predicate, context); + } else { + key = _.findKey(obj, predicate, context); + } + if (key !== void 0 && key !== -1) { return obj[key]; } + }; + + // Return all the elements that pass a truth test. + // Aliased as `select`. + _.filter = _.select = function(obj, predicate, context) { + var results = []; + predicate = cb(predicate, context); + _.each(obj, function(value, index, list) { + if (predicate(value, index, list)) { results.push(value); } + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, predicate, context) { + return _.filter(obj, _.negate(cb(predicate)), context); + }; + + // Determine whether all of the elements match a truth test. + // Aliased as `all`. + _.every = _.all = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length; + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + if (!predicate(obj[currentKey], currentKey, obj)) { return false; } + } + return true; + }; + + // Determine if at least one element in the object matches a truth test. + // Aliased as `any`. + _.some = _.any = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = !isArrayLike(obj) && _.keys(obj), + length = (keys || obj).length; + for (var index = 0; index < length; index++) { + var currentKey = keys ? keys[index] : index; + if (predicate(obj[currentKey], currentKey, obj)) { return true; } + } + return false; + }; + + // Determine if the array or object contains a given item (using `===`). + // Aliased as `includes` and `include`. + _.contains = _.includes = _.include = function(obj, item, fromIndex, guard) { + if (!isArrayLike(obj)) { obj = _.values(obj); } + if (typeof fromIndex != 'number' || guard) { fromIndex = 0; } + return _.indexOf(obj, item, fromIndex) >= 0; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + var func = isFunc ? method : value[method]; + return func == null ? func : func.apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, _.property(key)); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs) { + return _.filter(obj, _.matcher(attrs)); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.find(obj, _.matcher(attrs)); + }; + + // Return the maximum element (or element-based computation). + _.max = function(obj, iteratee, context) { + var result = -Infinity, lastComputed = -Infinity, + value, computed; + if (iteratee == null && obj != null) { + obj = isArrayLike(obj) ? obj : _.values(obj); + for (var i = 0, length = obj.length; i < length; i++) { + value = obj[i]; + if (value > result) { + result = value; + } + } + } else { + iteratee = cb(iteratee, context); + _.each(obj, function(value, index, list) { + computed = iteratee(value, index, list); + if (computed > lastComputed || computed === -Infinity && result === -Infinity) { + result = value; + lastComputed = computed; + } + }); + } + return result; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iteratee, context) { + var result = Infinity, lastComputed = Infinity, + value, computed; + if (iteratee == null && obj != null) { + obj = isArrayLike(obj) ? obj : _.values(obj); + for (var i = 0, length = obj.length; i < length; i++) { + value = obj[i]; + if (value < result) { + result = value; + } + } + } else { + iteratee = cb(iteratee, context); + _.each(obj, function(value, index, list) { + computed = iteratee(value, index, list); + if (computed < lastComputed || computed === Infinity && result === Infinity) { + result = value; + lastComputed = computed; + } + }); + } + return result; + }; + + // Shuffle a collection, using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). + _.shuffle = function(obj) { + var set = isArrayLike(obj) ? obj : _.values(obj); + var length = set.length; + var shuffled = Array(length); + for (var index = 0, rand; index < length; index++) { + rand = _.random(0, index); + if (rand !== index) { shuffled[index] = shuffled[rand]; } + shuffled[rand] = set[index]; + } + return shuffled; + }; + + // Sample **n** random values from a collection. + // If **n** is not specified, returns a single random element. + // The internal `guard` argument allows it to work with `map`. + _.sample = function(obj, n, guard) { + if (n == null || guard) { + if (!isArrayLike(obj)) { obj = _.values(obj); } + return obj[_.random(obj.length - 1)]; + } + return _.shuffle(obj).slice(0, Math.max(0, n)); + }; + + // Sort the object's values by a criterion produced by an iteratee. + _.sortBy = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value: value, + index: index, + criteria: iteratee(value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) { return 1; } + if (a < b || b === void 0) { return -1; } + } + return left.index - right.index; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(behavior) { + return function(obj, iteratee, context) { + var result = {}; + iteratee = cb(iteratee, context); + _.each(obj, function(value, index) { + var key = iteratee(value, index, obj); + behavior(result, value, key); + }); + return result; + }; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = group(function(result, value, key) { + if (_.has(result, key)) { result[key].push(value); } else { result[key] = [value]; } + }); + + // Indexes the object's values by a criterion, similar to `groupBy`, but for + // when you know that your index values will be unique. + _.indexBy = group(function(result, value, key) { + result[key] = value; + }); + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = group(function(result, value, key) { + if (_.has(result, key)) { result[key]++; } else { result[key] = 1; } + }); + + // Safely create a real, live array from anything iterable. + _.toArray = function(obj) { + if (!obj) { return []; } + if (_.isArray(obj)) { return slice.call(obj); } + if (isArrayLike(obj)) { return _.map(obj, _.identity); } + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) { return 0; } + return isArrayLike(obj) ? obj.length : _.keys(obj).length; + }; + + // Split a collection into two arrays: one whose elements all satisfy the given + // predicate, and one whose elements all do not satisfy the predicate. + _.partition = function(obj, predicate, context) { + predicate = cb(predicate, context); + var pass = [], fail = []; + _.each(obj, function(value, key, obj) { + (predicate(value, key, obj) ? pass : fail).push(value); + }); + return [pass, fail]; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) { return void 0; } + if (n == null || guard) { return array[0]; } + return _.initial(array, array.length - n); + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. + _.initial = function(array, n, guard) { + return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. + _.last = function(array, n, guard) { + if (array == null) { return void 0; } + if (n == null || guard) { return array[array.length - 1]; } + return _.rest(array, Math.max(0, array.length - n)); + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, n == null || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, strict, startIndex) { + var output = [], idx = 0; + for (var i = startIndex || 0, length = getLength(input); i < length; i++) { + var value = input[i]; + if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { + //flatten current level of array or arguments object + if (!shallow) { value = flatten(value, shallow, strict); } + var j = 0, len = value.length; + output.length += len; + while (j < len) { + output[idx++] = value[j++]; + } + } else if (!strict) { + output[idx++] = value; + } + } + return output; + }; + + // Flatten out an array, either recursively (by default), or just one level. + _.flatten = function(array, shallow) { + return flatten(array, shallow, false); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iteratee, context) { + if (!_.isBoolean(isSorted)) { + context = iteratee; + iteratee = isSorted; + isSorted = false; + } + if (iteratee != null) { iteratee = cb(iteratee, context); } + var result = []; + var seen = []; + for (var i = 0, length = getLength(array); i < length; i++) { + var value = array[i], + computed = iteratee ? iteratee(value, i, array) : value; + if (isSorted) { + if (!i || seen !== computed) { result.push(value); } + seen = computed; + } else if (iteratee) { + if (!_.contains(seen, computed)) { + seen.push(computed); + result.push(value); + } + } else if (!_.contains(result, value)) { + result.push(value); + } + } + return result; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(flatten(arguments, true, true)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var arguments$1 = arguments; + + var result = []; + var argsLength = arguments.length; + for (var i = 0, length = getLength(array); i < length; i++) { + var item = array[i]; + if (_.contains(result, item)) { continue; } + for (var j = 1; j < argsLength; j++) { + if (!_.contains(arguments$1[j], item)) { break; } + } + if (j === argsLength) { result.push(item); } + } + return result; + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = flatten(arguments, true, true, 1); + return _.filter(array, function(value){ + return !_.contains(rest, value); + }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + return _.unzip(arguments); + }; + + // Complement of _.zip. Unzip accepts an array of arrays and groups + // each array's elements on shared indices + _.unzip = function(array) { + var length = array && _.max(array, getLength).length || 0; + var result = Array(length); + + for (var index = 0; index < length; index++) { + result[index] = _.pluck(array, index); + } + return result; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + var result = {}; + for (var i = 0, length = getLength(list); i < length; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // Generator function to create the findIndex and findLastIndex functions + function createPredicateIndexFinder(dir) { + return function(array, predicate, context) { + predicate = cb(predicate, context); + var length = getLength(array); + var index = dir > 0 ? 0 : length - 1; + for (; index >= 0 && index < length; index += dir) { + if (predicate(array[index], index, array)) { return index; } + } + return -1; + }; + } + + // Returns the first index on an array-like that passes a predicate test + _.findIndex = createPredicateIndexFinder(1); + _.findLastIndex = createPredicateIndexFinder(-1); + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iteratee, context) { + iteratee = cb(iteratee, context, 1); + var value = iteratee(obj); + var low = 0, high = getLength(array); + while (low < high) { + var mid = Math.floor((low + high) / 2); + if (iteratee(array[mid]) < value) { low = mid + 1; } else { high = mid; } + } + return low; + }; + + // Generator function to create the indexOf and lastIndexOf functions + function createIndexFinder(dir, predicateFind, sortedIndex) { + return function(array, item, idx) { + var i = 0, length = getLength(array); + if (typeof idx == 'number') { + if (dir > 0) { + i = idx >= 0 ? idx : Math.max(idx + length, i); + } else { + length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1; + } + } else if (sortedIndex && idx && length) { + idx = sortedIndex(array, item); + return array[idx] === item ? idx : -1; + } + if (item !== item) { + idx = predicateFind(slice.call(array, i, length), _.isNaN); + return idx >= 0 ? idx + i : -1; + } + for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) { + if (array[idx] === item) { return idx; } + } + return -1; + }; + } + + // Return the position of the first occurrence of an item in an array, + // or -1 if the item is not included in the array. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex); + _.lastIndexOf = createIndexFinder(-1, _.findLastIndex); + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (stop == null) { + stop = start || 0; + start = 0; + } + step = step || 1; + + var length = Math.max(Math.ceil((stop - start) / step), 0); + var range = Array(length); + + for (var idx = 0; idx < length; idx++, start += step) { + range[idx] = start; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Determines whether to execute a function as a constructor + // or a normal function with the provided arguments + var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) { + if (!(callingContext instanceof boundFunc)) { return sourceFunc.apply(context, args); } + var self = baseCreate(sourceFunc.prototype); + var result = sourceFunc.apply(self, args); + if (_.isObject(result)) { return result; } + return self; + }; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + if (nativeBind && func.bind === nativeBind) { return nativeBind.apply(func, slice.call(arguments, 1)); } + if (!_.isFunction(func)) { throw new TypeError('Bind must be called on a function'); } + var args = slice.call(arguments, 2); + var bound = function() { + return executeBound(func, bound, context, this, args.concat(slice.call(arguments))); + }; + return bound; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. _ acts + // as a placeholder, allowing any combination of arguments to be pre-filled. + _.partial = function(func) { + var boundArgs = slice.call(arguments, 1); + var bound = function() { + var arguments$1 = arguments; + + var position = 0, length = boundArgs.length; + var args = Array(length); + for (var i = 0; i < length; i++) { + args[i] = boundArgs[i] === _ ? arguments$1[position++] : boundArgs[i]; + } + while (position < arguments.length) { args.push(arguments$1[position++]); } + return executeBound(func, bound, this, this, args); + }; + return bound; + }; + + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. + _.bindAll = function(obj) { + var arguments$1 = arguments; + + var i, length = arguments.length, key; + if (length <= 1) { throw new Error('bindAll must be passed function names'); } + for (i = 1; i < length; i++) { + key = arguments$1[i]; + obj[key] = _.bind(obj[key], obj); + } + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memoize = function(key) { + var cache = memoize.cache; + var address = '' + (hasher ? hasher.apply(this, arguments) : key); + if (!_.has(cache, address)) { cache[address] = func.apply(this, arguments); } + return cache[address]; + }; + memoize.cache = {}; + return memoize; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ + return func.apply(null, args); + }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = _.partial(_.delay, _, 1); + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + if (!options) { options = {}; } + var later = function() { + previous = options.leading === false ? 0 : _.now(); + timeout = null; + result = func.apply(context, args); + if (!timeout) { context = args = null; } + }; + return function() { + var now = _.now(); + if (!previous && options.leading === false) { previous = now; } + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0 || remaining > wait) { + if (timeout) { + clearTimeout(timeout); + timeout = null; + } + previous = now; + result = func.apply(context, args); + if (!timeout) { context = args = null; } + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, args, context, timestamp, result; + + var later = function() { + var last = _.now() - timestamp; + + if (last < wait && last >= 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + if (!timeout) { context = args = null; } + } + } + }; + + return function() { + context = this; + args = arguments; + timestamp = _.now(); + var callNow = immediate && !timeout; + if (!timeout) { timeout = setTimeout(later, wait); } + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return _.partial(wrapper, func); + }; + + // Returns a negated version of the passed-in predicate. + _.negate = function(predicate) { + return function() { + return !predicate.apply(this, arguments); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var args = arguments; + var start = args.length - 1; + return function() { + var this$1 = this; + + var i = start; + var result = args[start].apply(this, arguments); + while (i--) { result = args[i].call(this$1, result); } + return result; + }; + }; + + // Returns a function that will only be executed on and after the Nth call. + _.after = function(times, func) { + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Returns a function that will only be executed up to (but not including) the Nth call. + _.before = function(times, func) { + var memo; + return function() { + if (--times > 0) { + memo = func.apply(this, arguments); + } + if (times <= 1) { func = null; } + return memo; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = _.partial(_.before, 2); + + // Object Functions + // ---------------- + + // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed. + var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString'); + var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString', + 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString']; + + function collectNonEnumProps(obj, keys) { + var nonEnumIdx = nonEnumerableProps.length; + var constructor = obj.constructor; + var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto; + + // Constructor is a special case. + var prop = 'constructor'; + if (_.has(obj, prop) && !_.contains(keys, prop)) { keys.push(prop); } + + while (nonEnumIdx--) { + prop = nonEnumerableProps[nonEnumIdx]; + if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) { + keys.push(prop); + } + } + } + + // Retrieve the names of an object's own properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = function(obj) { + if (!_.isObject(obj)) { return []; } + if (nativeKeys) { return nativeKeys(obj); } + var keys = []; + for (var key in obj) { if (_.has(obj, key)) { keys.push(key); } } + // Ahem, IE < 9. + if (hasEnumBug) { collectNonEnumProps(obj, keys); } + return keys; + }; + + // Retrieve all the property names of an object. + _.allKeys = function(obj) { + if (!_.isObject(obj)) { return []; } + var keys = []; + for (var key in obj) { keys.push(key); } + // Ahem, IE < 9. + if (hasEnumBug) { collectNonEnumProps(obj, keys); } + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var values = Array(length); + for (var i = 0; i < length; i++) { + values[i] = obj[keys[i]]; + } + return values; + }; + + // Returns the results of applying the iteratee to each element of the object + // In contrast to _.map it returns an object + _.mapObject = function(obj, iteratee, context) { + iteratee = cb(iteratee, context); + var keys = _.keys(obj), + length = keys.length, + results = {}, + currentKey; + for (var index = 0; index < length; index++) { + currentKey = keys[index]; + results[currentKey] = iteratee(obj[currentKey], currentKey, obj); + } + return results; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var pairs = Array(length); + for (var i = 0; i < length; i++) { + pairs[i] = [keys[i], obj[keys[i]]]; + } + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + result[obj[keys[i]]] = keys[i]; + } + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) { names.push(key); } + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = createAssigner(_.allKeys); + + // Assigns a given object with all the own properties in the passed-in object(s) + // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) + _.extendOwn = _.assign = createAssigner(_.keys); + + // Returns the first key on an object that passes a predicate test + _.findKey = function(obj, predicate, context) { + predicate = cb(predicate, context); + var keys = _.keys(obj), key; + for (var i = 0, length = keys.length; i < length; i++) { + key = keys[i]; + if (predicate(obj[key], key, obj)) { return key; } + } + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(object, oiteratee, context) { + var result = {}, obj = object, iteratee, keys; + if (obj == null) { return result; } + if (_.isFunction(oiteratee)) { + keys = _.allKeys(obj); + iteratee = optimizeCb(oiteratee, context); + } else { + keys = flatten(arguments, false, false, 1); + iteratee = function(value, key, obj) { return key in obj; }; + obj = Object(obj); + } + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i]; + var value = obj[key]; + if (iteratee(value, key, obj)) { result[key] = value; } + } + return result; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj, iteratee, context) { + if (_.isFunction(iteratee)) { + iteratee = _.negate(iteratee); + } else { + var keys = _.map(flatten(arguments, false, false, 1), String); + iteratee = function(value, key) { + return !_.contains(keys, key); + }; + } + return _.pick(obj, iteratee, context); + }; + + // Fill in a given object with default properties. + _.defaults = createAssigner(_.allKeys, true); + + // Creates an object that inherits from the given prototype object. + // If additional properties are provided then they will be added to the + // created object. + _.create = function(prototype, props) { + var result = baseCreate(prototype); + if (props) { _.extendOwn(result, props); } + return result; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) { return obj; } + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Returns whether an object has a given set of `key:value` pairs. + _.isMatch = function(object, attrs) { + var keys = _.keys(attrs), length = keys.length; + if (object == null) { return !length; } + var obj = Object(object); + for (var i = 0; i < length; i++) { + var key = keys[i]; + if (attrs[key] !== obj[key] || !(key in obj)) { return false; } + } + return true; + }; + + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) { return a !== 0 || 1 / a === 1 / b; } + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) { return a === b; } + // Unwrap any wrapped objects. + if (a instanceof _) { a = a._wrapped; } + if (b instanceof _) { b = b._wrapped; } + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className !== toString.call(b)) { return false; } + switch (className) { + // Strings, numbers, regular expressions, dates, and booleans are compared by value. + case '[object RegExp]': + // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return '' + a === '' + b; + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. + // Object(NaN) is equivalent to NaN + if (+a !== +a) { return +b !== +b; } + // An `egal` comparison is performed for other numeric values. + return +a === 0 ? 1 / +a === 1 / b : +a === +b; + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a === +b; + } + + var areArrays = className === '[object Array]'; + if (!areArrays) { + if (typeof a != 'object' || typeof b != 'object') { return false; } + + // Objects with different constructors are not equivalent, but `Object`s or `Array`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor && + _.isFunction(bCtor) && bCtor instanceof bCtor) + && ('constructor' in a && 'constructor' in b)) { + return false; + } + } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + + // Initializing stack of traversed objects. + // It's done here since we only need them for objects and arrays comparison. + aStack = aStack || []; + bStack = bStack || []; + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] === a) { return bStack[length] === b; } + } + + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + + // Recursively compare objects and arrays. + if (areArrays) { + // Compare array lengths to determine if a deep comparison is necessary. + length = a.length; + if (length !== b.length) { return false; } + // Deep compare the contents, ignoring non-numeric properties. + while (length--) { + if (!eq(a[length], b[length], aStack, bStack)) { return false; } + } + } else { + // Deep compare objects. + var keys = _.keys(a), key; + length = keys.length; + // Ensure that both objects contain the same number of properties before comparing deep equality. + if (_.keys(b).length !== length) { return false; } + while (length--) { + // Deep compare each member + key = keys[length]; + if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) { return false; } + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return true; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) { return true; } + if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) { return obj.length === 0; } + return _.keys(obj).length === 0; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) === '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + var type = typeof obj; + return type === 'function' || type === 'object' && !!obj; + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError. + _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) === '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE < 9), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return _.has(obj, 'callee'); + }; + } + + // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8, + // IE 11 (#1621), and in Safari 8 (#1929). + if (typeof /./ != 'function' && typeof Int8Array != 'object') { + _.isFunction = function(obj) { + return typeof obj == 'function' || false; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj !== +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return obj != null && hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iteratees. + _.identity = function(value) { + return value; + }; + + // Predicate-generating functions. Often useful outside of Underscore. + _.constant = function(value) { + return function() { + return value; + }; + }; + + _.noop = function(){}; + + _.property = property; + + // Generates a function for a given object that returns a given property. + _.propertyOf = function(obj) { + return obj == null ? function(){} : function(key) { + return obj[key]; + }; + }; + + // Returns a predicate for checking whether an object has a given set of + // `key:value` pairs. + _.matcher = _.matches = function(attrs) { + attrs = _.extendOwn({}, attrs); + return function(obj) { + return _.isMatch(obj, attrs); + }; + }; + + // Run a function **n** times. + _.times = function(n, iteratee, context) { + var accum = Array(Math.max(0, n)); + iteratee = optimizeCb(iteratee, context, 1); + for (var i = 0; i < n; i++) { accum[i] = iteratee(i); } + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // A (possibly faster) way to get the current timestamp as an integer. + _.now = Date.now || function() { + return new Date().getTime(); + }; + + // List of HTML entities for escaping. + var escapeMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }; + var unescapeMap = _.invert(escapeMap); + + // Functions for escaping and unescaping strings to/from HTML interpolation. + var createEscaper = function(map) { + var escaper = function(match) { + return map[match]; + }; + // Regexes for identifying a key that needs to be escaped + var source = '(?:' + _.keys(map).join('|') + ')'; + var testRegexp = RegExp(source); + var replaceRegexp = RegExp(source, 'g'); + return function(string) { + string = string == null ? '' : '' + string; + return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; + }; + }; + _.escape = createEscaper(escapeMap); + _.unescape = createEscaper(unescapeMap); + + // If the value of the named `property` is a function then invoke it with the + // `object` as context; otherwise, return it. + _.result = function(object, property, fallback) { + var value = object == null ? void 0 : object[property]; + if (value === void 0) { + value = fallback; + } + return _.isFunction(value) ? value.call(object) : value; + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\u2028|\u2029/g; + + var escapeChar = function(match) { + return '\\' + escapes[match]; + }; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + // NB: `oldSettings` only exists for backwards compatibility. + _.template = function(text, settings, oldSettings) { + if (!settings && oldSettings) { settings = oldSettings; } + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset).replace(escaper, escapeChar); + index = offset + match.length; + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } else if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } else if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + + // Adobe VMs need the match returned to produce the correct offest. + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) { source = 'with(obj||{}){\n' + source + '}\n'; } + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + 'return __p;\n'; + + try { + var render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled source as a convenience for precompilation. + var argument = settings.variable || 'obj'; + template.source = 'function(' + argument + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function. Start chaining a wrapped Underscore object. + _.chain = function(obj) { + var instance = _(obj); + instance._chain = true; + return instance; + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(instance, obj) { + return instance._chain ? _(obj).chain() : obj; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + _.each(_.functions(obj), function(name) { + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result(this, func.apply(_, args)); + }; + }); + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name === 'shift' || name === 'splice') && obj.length === 0) { delete obj[0]; } + return result(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + _.each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result(this, method.apply(this._wrapped, arguments)); + }; + }); + + // Extracts the result from a wrapped and chained object. + _.prototype.value = function() { + return this._wrapped; + }; + + // Provide unwrapping proxy for some methods used in engine operations + // such as arithmetic and JSON stringification. + _.prototype.valueOf = _.prototype.toJSON = _.prototype.value; + + _.prototype.toString = function() { + return '' + this._wrapped; + }; + + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof undefined === 'function' && undefined.amd) { + undefined('underscore', [], function() { + return _; + }); + } +}.call(commonjsGlobal)); +}); + +var Expression = { + display : + function() { + return this.exprType + " " + this.val; + }, + type : + function () { + return this.exprType; + }, + linenum : 0, + charnum : 0 +}; + +var TypeExpression = { + unify : + function (t) { + if (this.expr === t.expr) { + return t.expr; + } + else { + console.log("Could not unify " + this.expr + " with " + t.expr); + } + }, + isTypeExpr : true, + linenum : 0, + charnum : 0 +}; + +function flattenTypeDecl(stx) { + if (isTypeExpr(stx)) { + return true; + } + if (stx.exprType === "Application") { + /* it might be a type application so recursively check it */ + if (stx.p !== undefined) { + return underscore.flatten([flattenTypeDecl(stx.p), flattenTypeDecl(stx.func)]); + } + else { + return underscore.flatten([flattenTypeDecl(stx.func)]); + } + } + if (stx.exprType === "Name") { + /* + * Either it is a type operator + * or we assume it is a type variable + * since it was not capitalized + */ + return true; + } + return { + failed : true, + stx : stx + }; +} + + +function isTypeExprRec(stx) { + var flattened = flattenTypeDecl(stx); + for(var i = 0; i < flattened.length; i++) { + if (flattened[i].failed !== undefined && + flattened[i].failed) { + return flattened[i]; + } + } + return true; +} + +function Closure(bound_vars, free_vars, body, env) { + this.bound_vars = bound_vars; + this.free_vars = free_vars; + this.body = body; + this.env = env; + this.exprType = "Closure"; + return this; +} + +function LetExp(pairs, body) { + if (!pairs.every(function(x) { + return (x.exprType === "Definition" || + x.exprType === "FunctionDefinition"); + })) { + throw errors.JInternalError( + "let can only be used to bind things to names or functions" + ); + } + this.exprType = "Let"; + this.val = [pairs, body]; + this.pairs = pairs; + this.body = body; + return this; +} +LetExp.prototype = Expression; + +function UnaryOp(op, v) { + this.exprType = "Unary"; + this.val = v; + this.op = op; + return this; +} +UnaryOp.prototype = Expression; + +function IntT(v) { + this.exprType = "Integer"; + this.val = parseInt(v, 10); + return this; +} +IntT.prototype = Expression; + +function FloatT(v) { + this.exprType = "Float"; + this.val = parseFloat(v, 10); + return this; +} + +FloatT.prototype = Expression; + +function StrT(v) { + this.exprType = "String"; + this.val = v; + return this; +} + +StrT.prototype = Expression; + +function BoolT(b) { + if (b === "true") { + this.val = true; + } + else { + this.val = false; + } + this.exprType = "Bool"; + return this; +} + +BoolT.prototype = Expression; + +function ListT(xs) { + this.xs = xs; + this.val = xs; + this.exprType = "List"; + return this; +} + +ListT.prototype = Expression; + +function Nil() { + this.exprType = "Nil"; + return this; +} + +Nil.prototype = Expression; + + +function FuncT(p, body) { + this.p = p; + this.body = body; + this.val = [p, body]; + this.exprType = "Function"; + return this; +} + +FuncT.prototype = Expression; + +//Wrapper for function objects +function OpT(operator) { + this.op = operator; + this.val = this.op; + this.exprType = "Function"; + return this; +} + +OpT.prototype = Expression; + +// Applications separate from other types +function App(func, p) { + this.func = func; + this.exprType = "Application"; + if (p) + { this.p = p; } + return this; +} + +App.prototype = Expression; + +// Names are not types +function Name(identifier) { + this.ident = identifier; + this.val = this.ident; + this.exprType = "Name"; + return this; +} + +Name.prototype = Expression; + +function Def(ident, exp) { + this.ident = ident; + this.val = exp; + this.exprType = "Definition"; + return this; +} + +Def.prototype = Expression; + +function DefFunc(ident, params, body) { + this.ident = ident; + this.val = this.ident; + this.params = params; + this.body = body; + this.exprType = "FunctionDefinition"; + return this; +} + +DefFunc.prototype = Expression; + +function If(condition, thenexp, elseexp) { + this.condition = condition; + this.thenexp = thenexp; + this.elseexp = elseexp; + this.exprType = "If"; + return this; +} + +If.prototype = Expression; + +function TypeVar(name) { + this.exprtype = "TypeVar"; + this.name = name; + this.exprType = "TypeVar"; + return this; +} + +TypeVar.prototype = TypeExpression; + +function TypeOp(name) { + this.name = name; + this.val = name; + this.exprType = "TypeOperator"; + return this; +} + +TypeOp.prototype = TypeExpression; + +function isTypeExpr(expr) { + if (!expr.exprType) { + throw errors.JInternalError(expr); + } + return ((expr.exprType === "TypeOperator") || + (expr.exprType === "TypeVar") || + (expr.exprType === "TypeDeclaration")); +} + +function TypeDecl(expression, type) { + if (isTypeExprRec(expression) && + expression.exprType !== "Name") { + throw errors.JSyntaxError( + expression.linenum, + expression.charnum, + "Left-hand-side of type declaration must not be in the type language" + ); + } + if (isTypeExprRec(type).failed) { + throw errors.JInternalError( + "Right-hand-side of type declaration must be a type expression" + ); + } + this.expression = expression; + this.type = type; + this.exprType = "TypeDeclaration"; + return this; +} + +TypeDecl.prototype = TypeExpression; + +function DefType(lhs, rhs) { + /* Both rhs and lhs are expected + * to be fully desugared already + */ + if (isTypeExprRec(rhs).failed || + !isTypeExpr(lhs)) { + throw errors.JSyntaxError( + rhs.linenum, + rhs.charnum, + "Illegal type definition, both sides must be valid type expressions"); + } + this.rhs = rhs; + this.lhs = lhs; + this.exprType = "TypeDefinition"; + return this; +} + +DefType.prototype = Expression; + +function checkName(exp) { + if (exp.exprType !== "Name") { + throw errors.JSyntaxError( + exp.linenum, + exp.charnum, + "Expected a type variable (an identifier starting with a lowercase character), got " + exp.val); + } +} + +function DataType(name, params, type) { + /* Params is a list of type variables + * type is a type expression + */ + if (name.exprType !== "TypeOperator") { + throw errors.JSyntaxError( + name.linenum, + name.charnum, + "First element in a data type definition must be its name " + + "which is a type operator"); + } + underscore.each(params, checkName); + if (isTypeExprRec(type).failed) { + throw errors.JSyntaxError( + type.linenum, + type.charnum, + "Body of a type definition must be a valid type expression"); + } + this.name = name; + this.params = params; + this.type = type; + this.exprType = "TypeFuncDefinition"; + return this; +} + + +/* Applies the function ``name'' to the list of parameters */ +function makeApp(name, parameters) { + if (parameters) { + return parameters.slice(1).reduce(function(f, ident) { + return new App(f, ident); + }, new App(name, parameters[0])); + } + else { + return new App(name); + } +} + +function makeGensym() { + var n = 0; + return function() { + var x = "G"+n; + n = n + 1; + return x; + }; +} + +var gensym = makeGensym(); + +var OPInfo = { + "::" : [2, "Left"], + "," : [1, "Left"], + "->" : [1, "Right"] + }; + +var rep = { + IntT : IntT, + FloatT : FloatT, + StrT : StrT, + BoolT : BoolT, + ListT : ListT, + FuncT : FuncT, + App : App, + Name : Name, + Def : Def, + OpT : OpT, + OPInfo : OPInfo, + makeApp : makeApp, + If : If, + DefFunc : DefFunc, + UnaryOp : UnaryOp, + Nil : Nil, + LetExp : LetExp, + gensym : gensym, + TypeVar : TypeVar, + TypeOp : TypeOp, + TypeDecl : TypeDecl, + Closure : Closure, + isTypeExpr : isTypeExprRec, + DefType : DefType, + DataType : DataType + }; + +function empty(xs) { + return underscore.size(xs) < 1; +} + +function not(x) { + return !x; +} + +function min(a, b) { + if (a < b) { + return 1; + } + else if (a > b) { + return -1; + } + else { + return 0; + } +} + +function groupOps(ops) { + return underscore.groupBy(ops.sort(), underscore.isEqual); +} + +function dict(pairs) { + var o = {}; + pairs.map(function(p) { + o[p[0]] = p[1]; + }); + return o; +} + +function extend$2(xs, ys) { + var result = underscore.clone(xs); + result.push.apply(result, ys); + return result; +} + +RegExp.escape= function(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); +}; + +function operatorMatch(ops) { + ops = underscore.filter(ops, + function (op) { + return op.length > 0; + }); + ops.sort(min); + var rstring = ops.reduce( + function(acc, x) { + if (!x || x.length < 1) { + return ""; + } + return acc + "(" + RegExp.escape(x) + ")|"; + }, ""); + var reg = new RegExp(rstring); + return function(x) { + var matched = reg.exec(x); + if ((!(underscore.isNull(matched))) && matched[0]) { + return matched[0]; + } + else { + return false; + } + }; +} + +function debugPrint(stx) { + console.log("%j\n", stx); +} + + +var $$1 = { + not : not, + groupOps : groupOps, + opMatch : operatorMatch, + dict: dict, + extend : extend$2, + empty : empty, + debugPrint : debugPrint +}; + +var src = "\n;; This file declares the various types used by intrinsic/prelude definitions\n;; It is sort of special in that it doesn't care whether there are any associated definitions\n;; just that there are type definitions for that particular binding's name\n\n\n;; Type definitions\ndeftype String (Vector Char)\n\ndeftype (Int) Intrinsic\n\ndeftype (Float) Intrinsic\n\ndeftype (Char) Intrinsic\n\ndeftype (Byte) Intrinsic\n\ndeftype (Void) Intrinsic\n\ndeftype (IO a) Intrinsic\n\ndeftype (Vector a) Intrinsic\n\ndeftype (List a)\n (Empty |\n (Cons a (List a)))\n\ndeftype (Bottom)\n Undefined\n\ndeftype (Maybe a)\n (Nothing |\n (Just a))\n\ndeftype (Either a b)\n ((Left a) |\n (Right b))\n\n;; List functions\n\n(: :: (a -> (List a) -> (List a)))\n\n(map :: ((a -> b) -> (List a) -> (List b)))\n\n(head :: ((List a) -> a))\n\n(tail :: ((List a) -> (List a)))\n\n(!! :: (Int -> (List a) -> a))\n\n(take :: (Int -> (List a) -> (Maybe (List a))))\n\n(drop :: (Int -> (List a) -> (Maybe (List a))))\n\n;; Optional functions\n\n(maybe :: (b -> (a -> b) -> (Maybe a) -> b))\n\n(either :: ((b -> c) -> (b -> c) -> (Either a b) -> c))\n\n;; I/O functions\n\n(print :: (String -> (IO Void)))\n\n;; Operator definitions\n\ndefop 1 Left (a + b)\n (add a b)\n\ndefop 1 Left (a - b)\n (minus a b)\n\ndefop 2 Left (a * b)\n (mul a b)\n\ndefop 2 Left (a / b)\n (div a b)\n\ndefop 2 Right (a ^ b)\n (pow a b)\n\ndefop 3 Left (a ++ b)\n (listConcat a b)\n\ndefop 3 Left (a == b)\n (eq a b)\n\ndefop 3 Left (a > b)\n (gt a b)\n\ndefop 3 Left (a >= b)\n (gte a b)\n\ndefop 3 Left (a < b)\n (lt a b)\n\ndefop 3 Left (a <= b)\n (lte a b)\n\ndefop 3 Left (a && b)\n (and a b)\n\ndefop 3 Left (a || b)\n (or a b)\n\ndefop 4 Left (x : xs)\n (cons x xs)\n\ndefop 5 Left (f $ x)\n (fapply f x)\n\ndefop 5 Left (f . g)\n (compose f g)\n\ndefop 3 Left (a | b)\n (bitwiseOr a b)\n\ndefop 3 Left (a & b)\n (bitwiseAnd a b)"; + +var prelude = { + "src" : src +}; + +var operators = Object.keys(rep.OPInfo); + +function isDigit(c) { + if (!c) + { return false; } + var code = c.charCodeAt(); + if (isNaN(code)) { + return false; + } + return (47 < code) && (code < 58) +} + +function isWhitespace(c) { + if (!c) + { return true; } + + var code = c.charCodeAt(); + if (isNaN(code)) { + return true; + } + return (code === 9 || + code === 32 || + code === 10 || + code === 13 || + code === 11); +} + +function isIdentifier(c) { + var code = c.charCodeAt(); + return (!isNaN(code) && + code !== 41 && + code !== 40 && + code !== 125 && + code !== 123 && + code !== 93 && + code !== 91 && + code !== 44 && + code !== 34 && + code > 32); +} + +function isUpper(c) { + var code = c.charCodeAt(); + return (!isNaN(code) && + (code >= 65) && + (code <= 90)); +} + +function tokenizeNum(tokstream, charnum, linenum) { + var number = []; + var code = tokstream[0].charCodeAt(); + var isFloat = false; + var n = 0; + // + - + // might want to remove this since it probably won't ever get run? + if (code === 43 || code === 45) { // + or - + number.push(tokstream[0]); + tokstream = tokstream.substr(1); + n++; + } + else if (code === 46) { // . + tokstream = tokstream.substr(1); + n++; + charnum++; + number.push('0'); + number.push('.'); + isFloat = true; + } + + while (isDigit(tokstream[0]) && tokstream.length !== 0) { + number.push(tokstream[0]); + tokstream = tokstream.substr(1); + charnum++; + n++; + } + if (tokstream[0] === '.' && isDigit(tokstream[1])) { + number.push('.'); + number.push(tokstream[1]); + tokstream = tokstream.substr(2); + charnum++; charnum++; + n++; n++; + while (isDigit(tokstream[0]) && tokstream.length !== 0) { + number.push(tokstream[0]); + tokstream = tokstream.substr(1); + n++; + charnum++; + } + return [n, ["float", parseFloat(number.join(''), 10), charnum, linenum]]; + } + if (!isFloat) + { return [n, ["integer", parseInt(number.join(''), 10), charnum, linenum]]; } + else + { return [n, ["float", parseFloat(number.join(''), 10), charnum, linenum]]; } +} + +/* Split up the tokenized identifier if an operator appears in it + * prefer longer identifiers that start with the same character(s) as shorter ones + * e.g. ++ over + + * Everything after the operator goes back on to the token stream + */ + +function tokenizeIdent(tokstream, + matchop, + charnum, + linenum) { + var identifier = []; + var n = 0; + while ((!isWhitespace(tokstream[0])) && isIdentifier(tokstream[0]) && !matchop(tokstream)) { + identifier.push(tokstream[0]); + tokstream = tokstream.substr(1); + n++; + charnum++; + } + identifier = identifier.join(''); + + return [[n, ["identifier", identifier, charnum, linenum]]]; +} + +function tokenizeCtor(tokstream, + matchop, + charnum, + linenum) { + var ident = tokenizeIdent(tokstream, + matchop, + charnum, + linenum); + ident[0][1][0] = "constructor"; + return ident; +} + +function tokenizeStr(tokstream, charnum, linenum) { + var stringlit = []; + var n = 1; + var new_charnum = charnum; + tokstream = tokstream.substr(1); + while (tokstream[0].charCodeAt() !== 34) { + stringlit.push(tokstream[0]); + tokstream = tokstream.substr(1); + n++; + new_charnum++; + if (tokstream.length < 1) { + throw errors.JSyntaxError(linenum, charnum, "Error: missing quotation mark"); + } + } + n++; + return [n, ["stringlit", stringlit.join(''), new_charnum, linenum]]; + +} + +function tokenizeT(tokstream, charnum, linenum) { + if (tokstream.length < 4) + { return false; } + var next4 = tokstream.substr(0,4); + if (next4 === "then") + { return ["thenexp", "then"]; } + else if (next4 === "true") + { return ["truelit", "true"]; } + return false; +} + +function peek(tokstream, toktype, word, charnum, linenum) { + var n = word.length; + if (tokstream.length < n) + { return false; } + var nextN = tokstream.substr(0, n); + if (nextN == word) { + return [toktype, word]; + } + return false; +} + + +function tokenize(tokstream, matchop) { + var tokens = []; + var charnum = 1; + var linenum = 1; + var i, result, lambda, num, comment; + + while (tokstream) { + switch (tokstream[0].charCodeAt()) { + /* falls through */ + case 59: // ; + while (tokstream[0].charCodeAt() !== 10) { + tokstream = tokstream.substr(1); + } + break; + case 9: // '\t' + charnum++; + tokens.push(["whitespace", '\t', charnum, linenum]); + tokstream = tokstream.substr(1); + break; + /* falls through */ + case 32: // ' ' + charnum++; + tokens.push(["whitespace", ' ', charnum, linenum]); + tokstream = tokstream.substr(1); + break; + /* falls through */ + case 10: // '\n' + linenum++; + charnum = 1; /* Reset the character number for each line to 1 */ + tokens.push(["whitespace", '\n', charnum, linenum]); + tokstream = tokstream.substr(1); + break; + /* falls through */ + case 44: // ',' + charnum++; + tokens.push(["comma", ",", charnum, linenum]); + tokstream = tokstream.substr(1); + break; + /* falls through */ + case 40: // '(' + charnum++; + tokens.push(["left_paren", '(', charnum, linenum]); + tokstream = tokstream.substr(1); + + break; + /* falls through */ + case 41: // ')' + charnum++; + tokens.push(["right_paren", ')', charnum, linenum]); + tokstream = tokstream.substr(1); + break; + /* falls through */ + case 123: // '{' + charnum++; + tokens.push(["left_brace", '{', charnum, linenum]); + tokstream = tokstream.substr(1); + break; + /* falls through */ + case 125: // '}' + charnum++; + tokens.push(["right_brace", '}', charnum, linenum]); + tokstream = tokstream.substr(1); + break; + /* falls through */ + case 91: // '[' + charnum++; + tokens.push(["left_square", '[', charnum, linenum]); + tokstream = tokstream.substr(1); + break; + /* falls through */ + case 93: // ']' + charnum++; + tokens.push(["right_square", ']', charnum, linenum]); + tokstream = tokstream.substr(1); + break; + /* falls through */ + case 34: // '"' + result = tokenizeStr(tokstream, charnum, linenum); + var str = result[1]; + i = result[0]; + tokens.push(str); + tokstream = tokstream.substr(i); + break; + + /* falls through */ + case 46: // '.' + if (isDigit(tokstream[1])) { + result = tokenizeNum(tokstream, charnum, linenum); + num = result[1]; + i = result[0]; + if (!isNaN(num[1])) { + tokens.push(num); + } + tokstream = tokstream.substr(i); + break; + } + /* falls through */ + case 116: // 't' + result = tokenizeT(tokstream); + if (result) { + tokens.push($$1.extend(result, [charnum, linenum])); + tokstream = tokstream.substr(4); // 4 = length of either token + break; + } + /* falls through */ + case 105: // 'i' + var ifexp = peek(tokstream, "ifexp", "if"); + if (ifexp) { + tokens.push($$1.extend(ifexp, [charnum, linenum])); + tokstream = tokstream.substr(2); + break; + } + var inkeyword = peek(tokstream, "in", "in "); + if (inkeyword) { + tokens.push($$1.extend(inkeyword, [charnum, linenum])); + tokstream = tokstream.substr(3); + break; + } + + /* falls through */ + case 100: // 'd' + var defop = peek(tokstream, "defop", "defop"); + if (defop) { + tokens.push(["defop", "defop", charnum, linenum]); + tokstream = tokstream.substr(5); + break; + } + var deftype = peek(tokstream, "deftype", "deftype"); + if (deftype) { + tokens.push(["deftype", "deftype", charnum, linenum]); + tokstream = tokstream.substr(7); + break; + } + var def = peek(tokstream, "def", "def"); + if (def) { + tokens.push(["def", "def", charnum, linenum]); + tokstream = tokstream.substr(3); + break; + } + /* falls through */ + case 101: // e + result = peek(tokstream, "elsexp", "else"); + if (result) { + tokens.push($$1.extend(result, [charnum, linenum])); + tokstream = tokstream.substr(4); + break; + } + /* falls through */ + case 102: // f + result = peek(tokstream, "falselit", "false"); + if (result) { + tokens.push($$1.extend(result, [charnum, linenum])); + tokstream = tokstream.substr(5); + break; + } + /* falls through */ + case 108: // l + lambda = peek(tokstream, "lambda", "lambda"); + if (lambda) { + tokens.push($$1.extend(lambda, [charnum, linenum])); + tokstream = tokstream.substr(6); + break; + } + var letexp = peek(tokstream, "let", "let"); + if (letexp) { + tokens.push($$1.extend(letexp, [charnum, linenum])); + tokstream = tokstream.substr(3); + break; + } + + /* falls through */ + default: + if (isDigit(tokstream[0])) { + result = tokenizeNum(tokstream, charnum, linenum); + num = result[1]; + i = result[0]; + if (!isNaN(num[1])) { + tokens.push(num); + } + tokstream = tokstream.substr(i); + break; + } + var op = matchop(tokstream); + if (op) { + var l = op.length; + charnum = charnum + l; + tokstream = tokstream.substr(l); + tokens.push(["identifier", op, charnum, linenum]); + } + else { + if (isUpper(tokstream[0])) { + result = tokenizeCtor(tokstream, matchop, charnum, linenum); + } + else { + result = tokenizeIdent(tokstream, matchop, charnum, linenum); + } + for(var index = 0; index < result.length; index++) { + charnum++; + tokens.push(result[index][1]); + tokstream = tokstream.substr(result[index][0]); + } + } + } + } + return tokens; +} + +function tokenizeHelp(input, matchop, strip_whitespace) { + return tokenize(input, matchop).reverse().filter(function(x) { + if (strip_whitespace) { + return x[0] !== "whitespace"; + } + else { + return true; + } + }); +} + +var defop_pattern = ["defop", "integer", "constructor", + "left_paren", "identifier", + "identifier", "identifier", "right_paren"]; + +function checkPattern(x, i) { + return x === defop_pattern[i]; +} + +function tokenizeFull(input) { + var preludeSrc = prelude.src; + var matchop; + input = [preludeSrc, input].join(""); + var initialPass = tokenizeHelp(input, underscore.constant(false), true).reverse(); + + for (var i = 0; i < initialPass.length; i++) { + if (initialPass.slice(i, i+8). + map(underscore.first). + every(checkPattern)) { + rep.OPInfo[initialPass[i+5][1]] = + [parseInt(initialPass[i+1][1], 10), + initialPass[i+2][1]]; + } + } + operators = Object.keys(rep.OPInfo); + matchop = $$1.opMatch(operators); + return tokenizeHelp(input, matchop, true); +} + +var tokenizer = {tokenize : tokenizeFull, + isIdentifier : isIdentifier}; + +/* + * This module takes a parse tree in a surface format + * and transforms it into the "core" language which is + * much simpler and easier to type-check, optimize, and evaluate + */ + +function isAtomicNumber(stx) { + return stx.exprType == "Integer" || stx.exprType == "Float"; +} + +// Lists get desugared to nested function calls +// i.e. (cons (cons (cons ...))) +function desugarList(lst) { + if (lst.xs.length <= 0) { + return new rep.Nil(); + } + else { + var x = desugar(lst.xs[0]); + var rest = lst.xs.slice(1); + return new rep.App(new rep.App(new rep.Name(":"), x), desugarList(new rep.ListT(rest))); + } +} + +function curryFunc(ps, body) { + var result; + if (underscore.isEmpty(ps)) { + return desugar(body); + } + else { + result = new rep.FuncT(desugar(underscore.first(ps)), + curryFunc(underscore.rest(ps), body)); + result.charnum = ps.charnum; + result.linenum = ps.linenum; + return result; + } +} + + +function desugarLet(stx) { + var values = stx.pairs.map(desugar); + return new rep.LetExp(values, desugar(stx.body)); +} + +function sugarTypeDecl(stx) { + var type; + var expression; + type = stx.p; + expression = desugar(stx.func.p); + expression.linenum = stx.linenum; + expression.charnum = stx.charnum; + return new rep.TypeDecl(expression, type); +} + +function desugarDefType(stx, typeEnv) { + var result; + var rhs = desugar(stx.rhs); + var name = stx.lhs.name; + typeEnv[name] = rhs; + + result = new rep.DefType(stx.lhs, desugar(stx.rhs)); + result.linenum = stx.linenum; + result.charnum = stx.charnum; + return result; +} + +function desugar(stx, typeEnv) { + var typeExpTest; + + switch (stx.exprType) { + case "If": + if (stx.elseexp) { + return new rep.If(desugar(stx.condition, typeEnv), desugar(stx.thenexp, typeEnv), desugar(stx.elseexp, typeEnv)); + } + return new rep.If(desugar(stx.condition, typeEnv), desugar(stx.thenexp, typeEnv)); + /* FIXME closures not yet working */ + //case "FunctionDefinition": + //return desugarDefFunc(stx); + case "Definition": + return new rep.Def(stx.ident, desugar(stx.val, typeEnv)); + case "TypeDefinition": + return desugarDefType(stx, typeEnv); + case "Name": + return stx; + case "Application": + if ((stx.func.func !== undefined ) && + (stx.func.func.ident === "::")) { + /* It's a type declaration probably (will be verified later) + * In this case we actually *add* syntax here to differentiate type declarations + * from normal function application + */ + typeExpTest = rep.isTypeExpr(stx.p); + + if (typeExpTest.failed !== undefined && + typeExpTest.failed) { + throw errors.JInternalError( + "Type declaration error near line " + stx.linenum + " at character #"+stx.charnum + + "\n"+typeExpTest.stx.exprType+" (" + typeExpTest.stx.val + ") found where a type operator or type application was expected"); + } + return sugarTypeDecl(stx); + } + + if (false && + stx.p && isAtomicNumber(stx.p)) { + return new rep.UnaryOp(desugar(stx.func, typeEnv), desugar(stx.p, typeEnv)); + } + if (stx.p) { + return new rep.App(desugar(stx.func, typeEnv), desugar(stx.p, typeEnv)); + } + return new rep.App(stx.func); + case "Function": + return curryFunc(stx.p, stx.body); + case "List": + return desugarList(stx); + case "Bool": + return stx; + case "String": + return stx; + case "Float": + return stx; + case "Integer": + return stx; + case "Let": + return desugarLet(stx); + default: + return stx; + } +} + +var desugarer = { desugar : desugar }; +//var test = typ.ListT([1,2,3]); + +//console.log(desugarList(test)); + +/* Takes an AST and converts all of the functions into "closures" + * A closure is a triple of: + * the bound variables in a function or let + * the free variables in a function or let + * a function body or let body and bound values (if it is an escaping closure only) + * The closure has the property that all of the free variables of the function or let + * are in the environment, or an exception is raised because the variable is not bound + * in the current environment. + * A free variable is simply those that are not in the list of formal parameters or bound variables if it is a let + * + * Therefore in order to call a closure one must first extract the actual function and then + * call the function with the environment associated with it. + * For the purposes of type checking it does not matter how the function gets called, the environment + * is only used for looking up the types of names. Formal parameters are given type variables. + * + * The first phase of closure conversion is not really closure conversion exactly. + * All it does is find out the free variables in scope and tie those up into a data structure with their types later. + * The second phase will be done to the CPS language and closures will actually lambda-lifted out potentially. + */ + +var notEmpty = underscore.compose($$1.not, underscore.partial(underscore.equal, [])); + +function fvs(stx) { + switch (stx.exprType) { + case "Integer": + return []; + case "Float": + return []; + case "String": + return []; + case "Function": + return []; + case "Nil": + return []; + case "Bool": + return []; + case "Let": + return []; + case "Unary": + return underscore.flatten([stx.op.ident, fvs(stx.val)]); + case "Definition": + return underscore.flatten(fvs(stx.val)); + case "Application": + var vs = underscore.flatten(fvs(stx.p)); + var f_fvs = underscore.flatten(fvs(stx.func)); + return underscore.flatten([vs, f_fvs]); + case "If": + if (stx.elseexp) { + var cond_fvs = fvs(stx.condition); + var then_fvs = fvs(stx.thenexp); + var else_fvs = fvs(stx.elseexp); + return underscore.flatten([cond_fvs, then_fvs, else_fvs]); + } + else { + return underscore.flatten([fvs(stx.condition), fvs(stx.thenexp)]); + } + break; + case "Name": + return [stx.ident]; + } +} + +function annotate_fvs(stx) { + /* Takes a stx object that is either + * a lambda + * a let + * and returns a closure wrapped around that stx object + */ + if (stx.exprType !== "Function" && + stx.exprType !== "Let") { + throw errors.JInternalError( + ["Tried to calculate the free variables of", + "something that was not a function or let.\n", + "That something was a: " + stx.exprType +"\n"].reduce( + function (a,b) { + return a+" "+b; + }, "")); + } + var variables, free_variables, bound_vars, stx_type; + + switch (stx.exprType) { + case "Let": + bound_vars = stx.pairs.map( + function (stx) { + return stx.ident.ident; + }); + var let_fvs = stx.pairs.map(fvs); + var body_fvs = fvs(stx.body); + variables = underscore.flatten(let_fvs); + $$1.extend(variables, underscore.flatten(body_fvs)); + break; + case "Function": + bound_vars = [stx.p.ident ]; + variables = fvs(stx.body); + break; + } + free_variables = underscore.difference(underscore.uniq(variables), bound_vars); + return new rep.Closure(bound_vars, free_variables, stx, []); +} + +/* + * This traverse the tree and gathers up all of the free variables of various functions/let bindings + */ +function annotate_fvs_all(stx) { + var closure; + switch (stx.exprType) { + case "Let": + closure = annotate_fvs(stx); + closure.body.pairs = closure.body.pairs.map(annotate_fvs_all); + closure.body = annotate_fvs_all(closure.body.body); + return closure; + case "Function": + closure = annotate_fvs(stx); + closure.body.body = annotate_fvs_all(closure.body.body); + return closure; + case "Unary": + stx.val = annotate_fvs_all(stx.val); + return stx; + case "Application": + stx.func = annotate_fvs_all(stx.func); + stx.p = annotate_fvs_all(stx.p); + return stx; + case "If": + if (stx.elseexp) { + stx.condition = annotate_fvs_all(stx.condition); + stx.thenexp = annotate_fvs_all(stx.thenexp); + stx.elseexp = annotate_fvs_all(stx.elseexp); + return stx; + } + else { + stx.condition = annotate_fvs_all(stx.condition); + stx.thenexp = annotate_fvs_all(stx.thenexp); + return stx; + } + break; + case "Definition": + stx.val = annotate_fvs_all(stx.val); + return stx; + default: + return stx; + } +} + + +//console.log(test("if something then if a then if b then c else d else rtrrt else some_other_thing")); +var closure = { + annotate_fvs : annotate_fvs_all +}; + +function sourcePos(tokens, linenum, charnum) { + if (!tokens || tokens.length === 0) { + return { linenum : linenum, + charnum : charnum + }; + } + return { + linenum : fst(tokens)[3], + charnum : fst(tokens)[2] + }; +} + +function addSrcPos(stx, tokens, linenum, charnum) { + var pos = sourcePos(tokens, linenum, charnum); + stx.linenum = pos.linenum; + stx.charnum = pos.charnum; + return stx; +} + +/* Gets the first token from the right side of the stack */ +function fst(ts) { + return ts[ts.length-1]; +} + +/* Gets the second token from the right side of the stack */ +function snd(ts) { + return ts[ts.length-2]; +} + +/*Checks if the next token is not followed by any of ``checks'' */ +function notFollowedBy(tokens, checks, linenum, charnum) { + if (!fst(tokens)) { + throw errors.JSyntaxError(0,0,"unexpected end of source"); + } + var nextT = fst(tokens)[0]; + if (checks.some(function (x) { + return x === nextT; + })) + { return false; } + else + { return true; } +} + +/* returns a function that takes a parameter and + checks if it is in the array ``props''*/ +function makeChecker(props) { + return function(x) { + return x && props.some(function (y) {return y(x);}); + }; +} + +function tokTypeCheck(name) { + return function(tok) { + return tok[0] === name; + }; +} + +function formTypeCheck(stxtype) { + return function(stx) { + return stx.exprType === stxtype; + }; +} + +/*Tries to parse until the prediction ``valid'' fails or the wrong type is parsed + Collects the results into an array and returns it*/ +function parseMany(parse, exprType, valid, tokens, charnum, linenum) { + if (!fst(tokens)) { + throw errors.JSyntaxError(charnum, + linenum, + "Unexpected end of source"); + } + var current = fst(tokens)[0]; + var results = []; + var parsed; + + if (valid(fst(tokens))) { + parsed = parse(tokens); + } + else { + throw errors.JSyntaxError(fst(tokens)[3], + fst(tokens)[2], + "Unexpected token: ``"+fst(tokens)[0]+"''"); + } + results.push(parsed); + + //make sure there are at least 2 tokens to parse + if (tokens.length > 1 && fst(tokens) && valid(fst(tokens))) { + while (valid(snd(tokens))) { + if (!(valid(fst(tokens)))) { + break; + } + results.push(parse(tokens)); + if (!exprType(fst(results).exprType)) { + break; + } + if (fst(tokens)) { + current = fst(tokens)[0]; + } + else { + throw errors.JSyntaxError(linenum, + charnum, + "Unexpected end of source"); + } + if (tokens.length <= 1) { + break; + } + } + } + //do the same validity check as before and in the loop + if (!fst(tokens)) { + throw errors.JSyntaxError(fst(tokens)[3], + fst(tokens)[2], + "unexpected end of source"); + } + if (valid(fst(tokens))) { + results.push(parse(tokens)); + } + return results; +} + + +/* Tries to parse exprType separated by the token between + * e.g. ,,... + */ +function parseBetween(exprType, between, tokens, charnum, linenum) { + var first = parse(tokens); + var items; + var parsed; + + if (!exprType(first)) { + throw errors.JSyntaxError(fst(tokens)[2], fst(tokens)[3], "Unexpected token: ``"+fst(tokens)[0]+"''"); + } + + items = [first]; + + if (tokens.length > 1 && fst(tokens)[0] === between) { + while (fst(tokens)[0] === between) { + tokens.pop(); + parsed = parse(tokens); + if (!fst(tokens)) + { throw errors.JSyntaxError(fst(tokens)[3], + fst(tokens)[2], + "Missing terminator: "+between); } + items.push(parsed); + } + return items; + } + return items; +} + +function parseList(tokens, linenum, charnum) { + var xs; + var result; + + if (fst(tokens)[0] === "right_square") { + xs = []; + } + else if (fst(tokens)[0] === "comma") { + tokens.pop(); + xs = []; + } + else { + xs = parseBetween(function (x) { + return true; + }, "comma", tokens, fst(tokens)[3], fst(tokens)[2]); + } + if (!fst(tokens) || fst(tokens)[0] !== "right_square") { + throw errors.JSyntaxError(fst(tokens)[3], + fst(tokens)[2], + "list must be terminated by ]"); + } + tokens.pop(); + result = addSrcPos(new rep.ListT(xs), tokens, linenum, charnum); + return result; +} + + +function parseDefFunction(tokens, linenum, charnum) { + var fname = parse(tokens); + var parameters; + var result; + var body; + + if (fname.exprType !== "Name") { + throw errors.JSyntaxError(linenum, + charnum, + "Expected an identifier in function definition"); + } + if (fst(tokens)[0] === "right_paren") { + parameters = []; + } + else { + parameters = parseMany(parse, + validName, + validFormPar, + tokens, + charnum, + linenum); + } + if (!tokens || (fst(tokens)[0]) !== "right_paren") { + throw errors.JSyntaxError(linenum, + charnum, + "Formal parameters must be followed by )"); + } + tokens.pop(); + body = parse(tokens); + result = addSrcPos(new rep.DefFunc(fname, parameters, body), tokens, body.linenum, body.charnum); + return result; +} + +var validLet = makeChecker(["Definition", "FunctionDefinition"].map(formTypeCheck)); +var letEnd = underscore.compose($$1.not, makeChecker(["right_brace"].map(tokTypeCheck))); + +function parseLetForm(tokens, linenum, charnum) { + var result; + var pairs; + var body; + + if (!fst(tokens)) { + errors.JSyntaxError(linenum, + charnum, + "Unexpected end of source"); + } + pairs = parseMany(parseLetItem, + validLet, + letEnd, + tokens, + charnum, + linenum); + if (fst(tokens) && fst(tokens)[0] !== "right_brace") { + throw errors.JSyntaxError(fst(tokens)[2], + fst(tokens)[3], + "let/def form must have a closing }"); + } + if (!fst(tokens)) { + throw errors.JSyntaxError(underscore.last(pairs).linenum, + underscore.last(pairs).charnum, + "Unexpected end of source"); + } + tokens.pop(); + if (tokens.length <= 0) { + throw errors.JSyntaxError(underscore.last(pairs).linenum, + underscore.last(pairs).charnum, + "let/def form must have a body"); + } + body = parse(tokens); + if (body.exprType === "Definition" || + body.exprType === "FunctionDefinition") { + throw errors.JSyntaxError(body.linenum, + body.charnum, + "Body of a let/def expression cannot be a definition"); + } + result = addSrcPos(new rep.LetExp(pairs, body), tokens, body.linenum, body.charnum); + return result; + +} + +function parseLetFunction(tokens, linenum, charnum) { + var fname = parse(tokens); + var parameters; + var result; + var body; + + if (fname.exprType != "Name") { + throw errors.JSyntaxError(fst(tokens)[3], + fst(tokens)[2], + "Expected an identifier in function definition"); + } + if (fst(tokens)[0] === "right_paren") { + parameters = []; + } + else { + parameters = parseMany(parse, + validName, + validFormPar, + tokens, + charnum, + linenum); + } + if ((fst(tokens)[0]) !== "right_paren") { + throw errors.JSyntaxError(linenum, + charnum, + "Formal parameters must be followed by )"); + } + tokens.pop(); + if (fst(tokens)[1] !== "->") { + throw errors.JSyntaxError(fst(tokens)[3], + fst(tokens)[2], + "Function parameters in let/def form must be followed by ->"); + } + tokens.pop(); + body = parse(tokens); + result = addSrcPos(new rep.DefFunc(fname, parameters, body), tokens, body.linenum, body.charnum); + return result; +} + +function parseLetBinding(tokens, linenum, charnum) { + var name = parse(tokens); + var result; + var bound; + + if (name.exprType != "Name") { + throw errors.JSyntaxError(name.linenum, + name.charnum, + "Expected an identifier in let/def binding"); + } + if (!fst(tokens) || fst(tokens)[1] !== "=") { + throw errors.JSyntaxError(name.linenum, + name.charnum, + "An identifier in a let/def binding must be followed by ``=''"); + } + tokens.pop(); + if (!notFollowedBy(tokens, + ["comma", "arrow", "right_brace", "right_square"], + name.linenum, + name.charnum)) { + throw errors.JSyntaxError(name.linenum, + name.charnum, + "The binding of " + identifier.val + " must not be followed by " + fst(tokens)[0]); + } + bound = parse(tokens); + if (bound.exprType === "Definition" || + bound.exprType === "FunctionDefinition") { + throw errors.JSyntaxError(bound.linenum, + bound.charnum, + "A definition cannot be the value of a binding"); + } + result = addSrcPos(new rep.Def(name, bound), tokens, bound.linenum, bound.charnum); + return result; +} + +function parseLetItem(tokens) { + var linenum = fst(tokens)[3]; + var charnum = fst(tokens)[2]; + + if (fst(tokens) && fst(tokens)[0] === "left_paren") { + tokens.pop(); + return parseLetFunction(tokens, + linenum, + charnum); + } + else { + return parseLetBinding(tokens, + linenum, + charnum); + } +} + +function parseDataType(tokens, linenum, charnum) { + var typeName = parse(tokens, linenum, charnum); + var typeParams; + var typeBody; + var result; + + if (typeName.exprType !== "TypeOperator") { + throw errors.JSyntaxError(typeName.linenum, + typeName.charnum, + "Expected a type operator in data type definition"); + } + if (fst(tokens)[0] !== "right_paren") { + var parameters = parseMany(parse, + validName, + validFormPar, + tokens, + charnum, + linenum); + } + else { + var parameters = []; + } + if (!tokens || (fst(tokens)[0]) !== "right_paren") { + throw errors.JSyntaxError(underscore.last(parameters).linenum, + underscore.last(parameters).charnum, + "Data type parameters must be followed by )"); + } + tokens.pop(); + typeBody = parse(tokens); + result = addSrcPos(new rep.DataType(typeName, parameters, typeBody), tokens, typeBody.linenum, typeBody.charnum); + + return result; +} + + + +function parseDefType(tokens, linenum, charnum) { + var result; + var rhs; + var lhs; + + if (tokens.length < 2) { + /* Minimal number of tokens required is 2 + * because it could be 'deftype Foo Blah' + */ + throw errors.JSyntaxError(linenum, + charnum, + "Unexpected end of source"); + } + if (fst(tokens)[0] === "left_paren") { + /* It's an actual data type definition + * i.e. not just an alias + */ + tokens.pop(); + return parseDataType(tokens, linenum, charnum); + } + + if (notFollowedBy(tokens, ["constructor"], linenum, charnum)) { + throw errors.JSyntaxError(linenum, + charnum, + "deftype must be followed by a single constructor" + + " if it is not a data type definition with type variables"); + } + else { + lhs = parse(tokens, linenum, charnum); + if (!tokens) { + throw errors.JSyntaxError(lhs.linenum, + lhs.charnum, + "Unexpected end of source"); + } + if (lhs.exprType !== "TypeOperator") { + throw errors.JSyntaxError(lhs.linenum, + lhs.charnum, + "left-hand side of type alias was not a type operator"); + } + rhs = parse(tokens, linenum, charnum); + + if (rhs.exprType !== "Application" && + rhs.exprType !== "TypeOperator") { + throw errors.JSyntaxError(rhs.linenum, + rhs.charnum, + "was expecting an application or type operator on the right-hand side of a type alias"); + } + result = addSrcPos(new rep.DefType(lhs, rhs), tokens, rhs.linenum, rhs.charnum); + return result; + } +} + + + + + +function parseDef(tokens, linenum, charnum) { + var result; + var identifier; + var bound; + + if (tokens.length < 2) { + /* Minimal number of tokens required is 2 + * because it could be 'def foo blah' + */ + throw errors.JSyntaxError(linenum, + charnum, + "Unexpected end of source"); + } + if (fst(tokens)[0] === "left_paren") { + /* It's a function definition */ + tokens.pop(); + return parseDefFunction(tokens, linenum, charnum); + } + + if (fst(tokens)[0] === "left_brace") { + /* It's a let/def form */ + tokens.pop(); + return parseLetForm(tokens, + fst(tokens)[3], + fst(tokens)[2]); + } + + if (notFollowedBy(tokens, ["identifier"], linenum, charnum)) { + throw errors.JSyntaxError(linenum, + charnum, + "def must be followed by identifier, not "+fst(tokens)[0]); + } + else { + identifier = parse(tokens); + if (!fst(tokens)) + { throw errors.JSyntaxError(identifier.linenum, + identifier.charnum, + "Unexpected end of source"); } + if (!notFollowedBy(tokens, + ["comma", "arrow", "right_brace", "right_square"], + identifier.linenum, + identifier.charnum)) { + throw errors.JSyntaxError(identifier.linenum, + identifier.charnum, + "def " + identifier.val + " must not be followed by " + fst(tokens)[0]); + } + bound = parse(tokens); + if (bound.exprType === "Definition" || + bound.exprType === "FunctionDefinition") { + throw errors.JSyntaxError(bound.linenum, + bound.charnum, + "A definition cannot be the value of a binding"); + } + result = addSrcPos(new rep.Def(identifier, bound), tokens, bound.linenum, bound.charnum); + return result; + } + } + +function parseDefOp(tokens, linenum, charnum) { + var result; + var names; + var pattern; + + if (fst(tokens)[0] !== "integer" || + fst(tokens)[1] < 1) { + throw errors.JSyntaxError(linenum, + charnum, + "defop must be followed by integer precedence >= 1"); + } + tokens.pop(); + + if (fst(tokens)[1] !== "Left" && fst(tokens)[1] !== "Right") { + throw errors.JSyntaxError(linenum, + charnum, + "defop must be followed by precedence and then either Left or Right"); + } + tokens.pop(); + if (fst(tokens)[0] !== "left_paren") { + throw errors.JSyntaxError(linenum, + charnum, + "defop arguments must start with ("); + } + tokens.pop(); + if (!(tokens.slice(tokens.length-3, + tokens.length).every(function(x) { + return x[0] === "identifier"; + }))) { + throw errors.JSyntaxError(linenum, + charnum, + "defop must be surrounded by exactly 3 identifiers"); + } + pattern = tokens.slice(tokens.length-3, + tokens.length); + tokens.pop(); tokens.pop(); tokens.pop(); + if (fst(tokens)[0] !== "right_paren") { + throw errors.JSyntaxError(linenum, + charnum, + "defop pattern must be terminated with )"); + } + tokens.pop(); + names = [new rep.Name(pattern[1][1]), + new rep.Name(pattern[0][1]), + new rep.Name(pattern[2][1])]; + names.map(function(name) { + name.linenum = linenum; + name.charnum = charnum; + return name; + }); + + result = addSrcPos(new rep.DefFunc(names[0], + names.slice(1,3), + parse(tokens)), + tokens, + underscore.last(names).linenum, + underscore.last(names).charnum); + return result; +} + + + +function parseIf(tokens, linenum, charnum) { + var result; + var ifC; + var thenC; + var elseC; + + if (!notFollowedBy(tokens, + ["def","comma","lambda"], + linenum, + charnum)) { + throw errors.JSyntaxError(linenum, + charnum, + "``if'' cannot be followed by "+fst(tokens)[0]) ; + } + else { + ifC = parse(tokens); + if (!fst(tokens) || fst(tokens)[0] !== "thenexp") { + throw errors.JSyntaxError(ifC.linenum, + ifC.charnum, + "if ``exp'' must be folowed by ``then'' exp, not "+snd(tokens)[0]); + } + else { + tokens.pop(); + thenC = parse(tokens); + + if (fst(tokens) && fst(tokens)[0] === "elsexp") { + tokens.pop(); + if (underscore.size(tokens) < 1) { + throw errors.JSyntaxError(thenC.linenum, + thenC.charnum, + "Unexpected end of source"); + } + else { + elseC = parse(tokens); + result = addSrcPos(new rep.If(ifC, thenC, elseC), tokens, elseC.linenum, elseC.charnum); + return result; + } + } + else { + throw errors.JSyntaxError(thenC.linenum, + thenC.charnum, + "If expression must include an else variant"); + } + } + } +} + +var validName = makeChecker(["Name"].map(formTypeCheck)); + +function validFormPar(tok) { + return tok[0] === "identifier" && + tok[1] !== "->"; +} + +function parseLambda(tokens, linenum, charnum) { + var result; + var parameters = parseMany(parse, + validName, + validFormPar, + tokens, + charnum, + linenum); + if (fst(tokens)[1] !== "->") { + throw errors.JSyntaxError(underscore.last(parameters).linenum, + underscore.last(parameters).charnum, + "arrow must follow parameters in lambda, not "+fst(tokens)[0]); + } + tokens.pop(); + var body = parse(tokens); + result = addSrcPos(new rep.FuncT(parameters, body), tokens, body.linenum, body.charnum); + return result; +} + +var invalidArguments = ["def", "comma", "right_paren", "right_square", "right_brace", "left_brace", "right_brace"].map(tokTypeCheck); +var validArgument = underscore.compose($$1.not, makeChecker(invalidArguments)); +var validArgTypes = underscore.compose($$1.not, makeChecker(["Definition"].map(formTypeCheck))); + +/* Parses function application (either infix or prefix) */ +function computeApp(tokens, charnum, linenum) { + var lhs = parse(tokens); + var next; + var result; + var parameters; + + if (fst(tokens)) { + next = fst(tokens); + } + else { + throw errors.JSyntaxError(lhs.linenum, + lhs.charnum, + "Unexpected end of source"); + } + + if (rep.OPInfo[next[1]]) { + /* it's an infix expression */ + result = parseInfix(tokens, 1, lhs, lhs.linenum, lhs.charnum); + if (!fst(tokens) || fst(tokens)[0] !== "right_paren") { + throw errors.JSyntaxError(lhs.linenum, + lhs.charnum, + "Mismatched parentheses or missing parenthesis on right-hand side"); + } + else { + tokens.pop(); + return result; + } + } + else { + /* it's a prefix application */ + if (fst(tokens)[0] !== "right_paren") { + parameters = parseMany(parse, + validArgTypes, + validArgument, + tokens, + fst(tokens)[2], + fst(tokens)[3]); + } + else { + parameters = []; + } + if ((!fst(tokens)) || fst(tokens)[0] !== "right_paren") { + throw errors.JSyntaxError(fst(tokens)[3], + fst(tokens)[2], + "Mismatched parentheses or missing parenthesis on right-hand side"); + } + else { + tokens.pop(); + return addSrcPos(rep.makeApp(lhs, parameters), tokens, linenum, charnum); + } + } +} + +/*Parses infix expressions by precedence climbing + * console.log(stx); + See this for more info and an implementation in python + http://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing/ +*/ +function parseInfix(tokens, minPrec, lhs, linenum, charnum) { + if (!lhs) { + lhs = parse(tokens); + } + while (true) { + var cur = fst(tokens); + if (!cur) { + throw errors.JSyntaxError(linenum, + charnum, + "Unexpected end of source"); + } + var opinfo = rep.OPInfo[cur[1]]; + + if (!opinfo || opinfo[0] < minPrec) + { break; } + + var op = addSrcPos(new rep.Name(cur[1]), tokens, linenum, charnum); + var prec = opinfo[0]; + var assoc = opinfo[1]; + var nextMinPrec = assoc === "Left" ? prec + 1 : prec; + tokens.pop(); + /*remove the operator token*/ + var rhs = parseInfix(tokens, nextMinPrec); + lhs = addSrcPos(rep.makeApp(op, [lhs, rhs]), tokens, rhs.linenum, rhs.charnum); + } + return lhs; +} + +function parse(tokens) { + var charnum = fst(tokens)[2]; + var linenum = fst(tokens)[3]; + var toktype; + var result; + if (fst(tokens)) { + toktype = fst(tokens)[0]; + } + else { + console.error("Tokenization error"); + } + var token = fst(tokens)[1]; + tokens.pop(); + if (toktype === "stringlit") { + result = addSrcPos(new rep.StrT(token), tokens, linenum, charnum); + return result; + } + else if (toktype === "left_square") { + return parseList(tokens, linenum, charnum); + } + else if (toktype === "lambda") { + return parseLambda(tokens, linenum, charnum); + } + else if (toktype === "integer") { + result = addSrcPos(new rep.IntT(token), tokens, linenum, charnum); + return result; + } + else if (toktype === "float") { + result = addSrcPos(new rep.FloatT(token), tokens, linenum, charnum); + return result; + } + else if (toktype === "identifier") { + result = addSrcPos(new rep.Name(token), tokens, linenum, charnum); + return result; + } + else if (toktype === "constructor") { + result = addSrcPos(new rep.TypeOp(token), tokens, linenum, charnum); + return result; + } + else if (toktype === "truelit" || toktype === "falselit") { + result = addSrcPos(new rep.BoolT(token), tokens, linenum, charnum); + return result; + } + else if (toktype === "def" || + toktype === "let") { + return parseDef(tokens, linenum, charnum); + } + else if (toktype === "deftype") { + return parseDefType(tokens, linenum, charnum); + } + else if (toktype === "defop") { + return parseDefOp(tokens, linenum, charnum); + } + else if (toktype === "ifexp") { + return parseIf(tokens, linenum, charnum); + } + else if (toktype === "left_paren") { + if (tokens.length == 0) { + throw errors.JSyntaxError(linenum, + charnum, + "Unexpected end of source"); + } + if (fst(tokens)[0] === "lambda") { + tokens.pop(); + var parsed = parseLambda(tokens, linenum, charnum); + tokens.pop(); + return parsed; + } + else + { return computeApp(tokens, linenum, charnum); } + } + else { + throw errors.JSyntaxError(linenum, + charnum, + "Unexpected token: ``" + toktype+"''"); + } +} + + +function parseFull(tokenized) { + var ast = []; + var typeBindings = {}; + var current; + while (tokenized.length > 0) { + current = closure.annotate_fvs(desugarer.desugar(parse(tokenized), typeBindings)); + ast.push(current); + } + return { + "ast" : ast, + "types" : typeBindings + }; +} + +var parser = { parse : function(str) { + return parseFull(tokenizer.tokenize(str)); + }, + tokenize : tokenizer.tokenize, + parseFull : parseFull, + }; +//var testParse = parseFull(tokenizer.tokenize(istr)); +//console.log(testParse[1]); +//console.log(testParse[0].map(pprint.pprint)); + +/* + * An environment is just an object that maps identifiers to JLambda expressions + * with a few built-in (a standard Prelude environment) + */ + +// creates a new environment initialized with the pairs in values +function makeEnv(name, values) { + var env = {}; + env.name = name; + env.bindings = {}; + for (var i = 0; i < values.length; i++) { + name = values[i][0]; + var val = values[i][1]; + env.bindings[name] = val; + } + return env; +} + +function lookup$1(name, env) { + var value = env.bindings[name]; + if (!value) { + throw errors.JUnboundError(name, env.name); + } + return value; +} + +var env = { + lookup : lookup$1, + makeEnv : makeEnv +}; + +function cons(x) { + return function(xs) { + if (xs.exprType == "Nil") { + return [x]; + } + xs.unshift(x); + return xs; + }; +} + +var testenv = env.makeEnv("toplevel", + [ + ["len", function(xs) { return xs.length; }], + ["+", function(a) { return function(b) { return a + b; } }], + ["*", function(a) { return function(b) { return a * b; } }], + ["-", function(a) { return function(b) { return a - b; } }], + ["/", function(a) { return function(b) { return a / b; } }], + [":", cons], + ["a", 2], + ["b", 3]]); + +function lookup(ident, env$$1) { + var value = env$$1.bindings[ident]; + if (value.exprType !== undefined) { + var result = evaluate(value, env$$1); + return result; + } + return value; +} + +function evaluateString(input) { + var ast = parser.parseFull(tokenizer.tokenize(input)); + return evaluateAll(ast.ast, testenv); +} + +function apply(func, p) { + return func(p); +} + +function evaluateAll(ast, environment) { + var l = ast.length; + var evaled = []; + for (var i = 0; i < l; i++) { + // should look for closures? + evaled.push(evaluate(ast[i], environment)); + } + return evaled[evaled.length-1]; +} + +function extend$1(def, env$$1) { + env$$1.bindings[def.ident.val] = evaluate(def.val, env$$1); + return; +} + +function evaluate(ast, environment) { + if (ast.exprType == "Application") { + var func = evaluate(ast.func, environment); + return apply( + func, + evaluate(ast.p, environment)); + } + else if (ast.exprType === "Unary") { + /* Unary function application */ + var func = evaluate(ast.op, environment); + return apply( + func, + evaluate(ast.val, environment)); + } + else if (ast.exprType === "Name") { + return lookup(ast.ident, environment); + } + else if (ast.exprType === "If") { + if (evaluate(ast.condition, environment)) { + return evaluate(ast.thenexp, environment); + } + else { + return evaluate(ast.elseexp, environment); + } + } + else if (ast.exprType === "Definition") { + extend$1(ast, environment); + return; + } + else if (ast.exprType === "Integer" || + ast.exprType === "Float" || + ast.exprType === "String") { + /* Return an atom */ + return ast.val; + } + else if (ast.exprType === "Closure") { + /* return evaluateClosure(ast); */ + return ast; + } + else { + return ast; + } +} + +var vm = { + evaluateString : evaluateString +}; + +riot$1.tag2('test', '

{v}

', '', '', function(opts) { + +var self = this; +self.outputs = []; +self.default = ""; + +this.evaluate = function(ev) { + ev.preventDefault(); + var input = self.refs.input; + if (!input.value) { + return; + } + else { + try { + self.outputs.push(JSON.stringify(vm.evaluateString(input.value))); + } + catch (e) { + self.outputs.push(("Error: " + (e.errormessage))); + } + } + self.refs.input.value = self.default; + self.update(); +}.bind(this); + +}); + +riot$1.mount("test"); + +}()); diff --git a/server/parse.js b/server/parse.js index bc123bc..3e1470a 100755 --- a/server/parse.js +++ b/server/parse.js @@ -789,6 +789,11 @@ function parse(tokens) { return parseIf(tokens, linenum, charnum); } else if (toktype === "left_paren") { + if (tokens.length == 0) { + throw error.JSyntaxError(linenum, + charnum, + "Unexpected end of source"); + } if (fst(tokens)[0] === "lambda") { tokens.pop(); var parsed = parseLambda(tokens, linenum, charnum); @@ -810,24 +815,14 @@ function parseFull(tokenized) { var ast = []; var typeBindings = {}; var current; - try { - while (tokenized.length > 0) { - current = closure.annotate_fvs(desugarer.desugar(parse(tokenized), typeBindings)); - ast.push(current); - } - return { - "ast" : ast, - "types" : typeBindings - }; - } catch (e) { - if (e.stxerror !== undefined) { - e.stxerror(); - console.error("Tokenization error"); - } - else { - console.log(e.errormessage); - } + while (tokenized.length > 0) { + current = closure.annotate_fvs(desugarer.desugar(parse(tokenized), typeBindings)); + ast.push(current); } + return { + "ast" : ast, + "types" : typeBindings + }; } export default { parse : function(str) { diff --git a/server/rollup.config.js b/server/rollup.config.js index da74e26..9ca6e93 100644 --- a/server/rollup.config.js +++ b/server/rollup.config.js @@ -14,8 +14,7 @@ export default { commonjs({ './vm.js' : ['evaluateString'] }), - buble(), - uglify() + buble() ], format: 'iife' } diff --git a/server/tags/test.tag b/server/tags/test.tag index 81605d8..161274a 100644 --- a/server/tags/test.tag +++ b/server/tags/test.tag @@ -25,7 +25,17 @@ self.default = ""; evaluate(ev) { ev.preventDefault(); var input = self.refs.input; - self.outputs.push(JSON.stringify(vm.evaluateString(input.value))); + if (!input.value) { + return; + } + else { + try { + self.outputs.push(JSON.stringify(vm.evaluateString(input.value))); + } + catch (e) { + self.outputs.push(`Error: ${e.errormessage}`); + } + } self.refs.input.value = self.default; self.update(); } diff --git a/server/tokenize.js b/server/tokenize.js index bb3e44c..7b1690d 100755 --- a/server/tokenize.js +++ b/server/tokenize.js @@ -384,18 +384,14 @@ function tokenize(tokstream, matchop) { } function tokenizeHelp(input, matchop, strip_whitespace) { - try { - return tokenize(input, matchop).reverse().filter(function(x) { - if (strip_whitespace) { - return x[0] !== "whitespace"; - } - else { - return true; - } - }); - } catch (e) { - console.log(e.stxerror()); - } + return tokenize(input, matchop).reverse().filter(function(x) { + if (strip_whitespace) { + return x[0] !== "whitespace"; + } + else { + return true; + } + }); } var defop_pattern = ["defop", "integer", "constructor",